tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

editDialog.mjs (6241B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 /* eslint-disable mozilla/balanced-listeners */ // Not relevant since the document gets unloaded.
      6 
      7 import {
      8  getCurrentFormData,
      9  canSubmitForm,
     10 } from "chrome://formautofill/content/addressFormLayout.mjs";
     11 
     12 const lazy = {};
     13 ChromeUtils.defineESModuleGetters(lazy, {
     14  AutofillTelemetry: "resource://gre/modules/shared/AutofillTelemetry.sys.mjs",
     15  formAutofillStorage: "resource://autofill/FormAutofillStorage.sys.mjs",
     16 });
     17 
     18 class AutofillEditDialog {
     19  constructor(subStorageName, elements, record) {
     20    this._storageInitPromise = lazy.formAutofillStorage.initialize();
     21    this._subStorageName = subStorageName;
     22    this._elements = elements;
     23    this._record = record;
     24    this.localizeDocument();
     25    window.addEventListener("load", this, { once: true });
     26  }
     27 
     28  async init() {
     29    this.updateSaveButtonState();
     30    this.attachEventListeners();
     31    // For testing only: signal to tests that the dialog is ready for testing.
     32    // This is likely no longer needed since retrieving from storage is fully
     33    // handled in manageDialog.js now.
     34    window.dispatchEvent(new CustomEvent("FormReadyForTests"));
     35  }
     36 
     37  /**
     38   * Get storage and ensure it has been initialized.
     39   *
     40   * @returns {object}
     41   */
     42  async getStorage() {
     43    await this._storageInitPromise;
     44    return lazy.formAutofillStorage[this._subStorageName];
     45  }
     46 
     47  /**
     48   * Asks FormAutofillParent to save or update an record.
     49   *
     50   * @param  {object} record
     51   * @param  {string} guid [optional]
     52   */
     53  async saveRecord(record, guid) {
     54    let storage = await this.getStorage();
     55    if (guid) {
     56      await storage.update(guid, record);
     57    } else {
     58      await storage.add(record);
     59    }
     60  }
     61 
     62  /**
     63   * Handle events
     64   *
     65   * @param  {DOMEvent} event
     66   */
     67  handleEvent(event) {
     68    switch (event.type) {
     69      case "load": {
     70        this.init();
     71        break;
     72      }
     73      case "click": {
     74        this.handleClick(event);
     75        break;
     76      }
     77      case "input": {
     78        this.handleInput(event);
     79        break;
     80      }
     81      case "keypress": {
     82        this.handleKeyPress(event);
     83        break;
     84      }
     85      case "contextmenu": {
     86        if (
     87          !HTMLInputElement.isInstance(event.target) &&
     88          !HTMLTextAreaElement.isInstance(event.target)
     89        ) {
     90          event.preventDefault();
     91        }
     92        break;
     93      }
     94    }
     95  }
     96 
     97  /**
     98   * Handle click events
     99   *
    100   * @param  {DOMEvent} event
    101   */
    102  handleClick(event) {
    103    if (event.target == this._elements.cancel) {
    104      window.close();
    105    }
    106    if (event.target == this._elements.save) {
    107      this.handleSubmit();
    108    }
    109  }
    110 
    111  /**
    112   * Handle input events
    113   */
    114  handleInput(_e) {
    115    this.updateSaveButtonState();
    116  }
    117 
    118  /**
    119   * Handle key press events
    120   *
    121   * @param  {DOMEvent} event
    122   */
    123  handleKeyPress(event) {
    124    if (event.keyCode == KeyEvent.DOM_VK_ESCAPE) {
    125      window.close();
    126    }
    127  }
    128 
    129  updateSaveButtonState() {
    130    // Toggle disabled attribute on the save button based on
    131    // whether the form is filled or empty.
    132    if (!Object.keys(this._elements.fieldContainer.buildFormObject()).length) {
    133      this._elements.save.setAttribute("disabled", true);
    134    } else {
    135      this._elements.save.removeAttribute("disabled");
    136    }
    137  }
    138 
    139  /**
    140   * Attach event listener
    141   */
    142  attachEventListeners() {
    143    window.addEventListener("keypress", this);
    144    window.addEventListener("contextmenu", this);
    145    this._elements.save.addEventListener("click", this);
    146    this._elements.cancel.addEventListener("click", this);
    147    document.addEventListener("input", this);
    148  }
    149 
    150  // An interface to be inherited.
    151  localizeDocument() {}
    152 
    153  recordFormSubmit() {
    154    let method = this._record?.guid ? "edit" : "add";
    155    lazy.AutofillTelemetry.recordManageEvent(this.telemetryType, method);
    156  }
    157 }
    158 
    159 export class EditAddressDialog extends AutofillEditDialog {
    160  telemetryType = lazy.AutofillTelemetry.ADDRESS;
    161 
    162  constructor(elements, record) {
    163    super("addresses", elements, record);
    164    if (record) {
    165      lazy.AutofillTelemetry.recordManageEvent(
    166        this.telemetryType,
    167        "show_entry"
    168      );
    169    }
    170  }
    171 
    172  localizeDocument() {
    173    if (this._record?.guid) {
    174      document.l10n.setAttributes(
    175        this._elements.title,
    176        "autofill-edit-address-title"
    177      );
    178    }
    179  }
    180 
    181  updateSaveButtonState() {
    182    // Toggle disabled attribute on the save button based on
    183    // whether the form is filled or empty.
    184    if (!canSubmitForm()) {
    185      this._elements.save.setAttribute("disabled", true);
    186    } else {
    187      this._elements.save.removeAttribute("disabled");
    188    }
    189  }
    190 
    191  async handleSubmit() {
    192    await this.saveRecord(
    193      getCurrentFormData(),
    194      this._record ? this._record.guid : null
    195    );
    196    this.recordFormSubmit();
    197 
    198    window.close();
    199  }
    200 }
    201 
    202 export class EditCreditCardDialog extends AutofillEditDialog {
    203  telemetryType = lazy.AutofillTelemetry.CREDIT_CARD;
    204 
    205  constructor(elements, record) {
    206    elements.fieldContainer._elements.billingAddress.disabled = true;
    207    super("creditCards", elements, record);
    208    elements.fieldContainer._elements.ccNumber.addEventListener(
    209      "blur",
    210      this._onCCNumberFieldBlur.bind(this)
    211    );
    212    if (record) {
    213      lazy.AutofillTelemetry.recordManageEvent(
    214        this.telemetryType,
    215        "show_entry"
    216      );
    217    }
    218  }
    219 
    220  _onCCNumberFieldBlur() {
    221    let elem = this._elements.fieldContainer._elements.ccNumber;
    222    this._elements.fieldContainer.updateCustomValidity(elem);
    223  }
    224 
    225  localizeDocument() {
    226    if (this._record?.guid) {
    227      document.l10n.setAttributes(
    228        this._elements.title,
    229        "autofill-edit-card-title2"
    230      );
    231    }
    232  }
    233 
    234  async handleSubmit() {
    235    let creditCard = this._elements.fieldContainer.buildFormObject();
    236    if (!this._elements.fieldContainer._elements.form.reportValidity()) {
    237      return;
    238    }
    239 
    240    try {
    241      await this.saveRecord(
    242        creditCard,
    243        this._record ? this._record.guid : null
    244      );
    245 
    246      this.recordFormSubmit();
    247 
    248      window.close();
    249    } catch (ex) {
    250      console.error(ex);
    251    }
    252  }
    253 }