summaryrefslogtreecommitdiffstats
path: root/browser/extensions/formautofill/content/editDialog.js
diff options
context:
space:
mode:
Diffstat (limited to 'browser/extensions/formautofill/content/editDialog.js')
-rw-r--r--browser/extensions/formautofill/content/editDialog.js241
1 files changed, 241 insertions, 0 deletions
diff --git a/browser/extensions/formautofill/content/editDialog.js b/browser/extensions/formautofill/content/editDialog.js
new file mode 100644
index 0000000000..2a705dd525
--- /dev/null
+++ b/browser/extensions/formautofill/content/editDialog.js
@@ -0,0 +1,241 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* exported EditAddressDialog, EditCreditCardDialog */
+/* eslint-disable mozilla/balanced-listeners */ // Not relevant since the document gets unloaded.
+
+"use strict";
+
+ChromeUtils.defineModuleGetter(
+ this,
+ "formAutofillStorage",
+ "resource://autofill/FormAutofillStorage.jsm"
+);
+ChromeUtils.defineModuleGetter(
+ this,
+ "AutofillTelemetry",
+ "resource://autofill/AutofillTelemetry.jsm"
+);
+
+class AutofillEditDialog {
+ constructor(subStorageName, elements, record) {
+ this._storageInitPromise = formAutofillStorage.initialize();
+ this._subStorageName = subStorageName;
+ this._elements = elements;
+ this._record = record;
+ this.localizeDocument();
+ window.addEventListener("DOMContentLoaded", this, { once: true });
+ }
+
+ async init() {
+ this.updateSaveButtonState();
+ this.attachEventListeners();
+ // For testing only: signal to tests that the dialog is ready for testing.
+ // This is likely no longer needed since retrieving from storage is fully
+ // handled in manageDialog.js now.
+ window.dispatchEvent(new CustomEvent("FormReady"));
+ }
+
+ /**
+ * Get storage and ensure it has been initialized.
+ *
+ * @returns {object}
+ */
+ async getStorage() {
+ await this._storageInitPromise;
+ return formAutofillStorage[this._subStorageName];
+ }
+
+ /**
+ * Asks FormAutofillParent to save or update an record.
+ *
+ * @param {object} record
+ * @param {string} guid [optional]
+ */
+ async saveRecord(record, guid) {
+ let storage = await this.getStorage();
+ if (guid) {
+ await storage.update(guid, record);
+ } else {
+ await storage.add(record);
+ }
+ }
+
+ /**
+ * Handle events
+ *
+ * @param {DOMEvent} event
+ */
+ handleEvent(event) {
+ switch (event.type) {
+ case "DOMContentLoaded": {
+ this.init();
+ break;
+ }
+ case "click": {
+ this.handleClick(event);
+ break;
+ }
+ case "input": {
+ this.handleInput(event);
+ break;
+ }
+ case "keypress": {
+ this.handleKeyPress(event);
+ break;
+ }
+ case "contextmenu": {
+ if (
+ !HTMLInputElement.isInstance(event.target) &&
+ !HTMLTextAreaElement.isInstance(event.target)
+ ) {
+ event.preventDefault();
+ }
+ break;
+ }
+ }
+ }
+
+ /**
+ * Handle click events
+ *
+ * @param {DOMEvent} event
+ */
+ handleClick(event) {
+ if (event.target == this._elements.cancel) {
+ window.close();
+ }
+ if (event.target == this._elements.save) {
+ this.handleSubmit();
+ }
+ }
+
+ /**
+ * Handle input events
+ *
+ * @param {DOMEvent} event
+ */
+ handleInput(event) {
+ this.updateSaveButtonState();
+ }
+
+ /**
+ * Handle key press events
+ *
+ * @param {DOMEvent} event
+ */
+ handleKeyPress(event) {
+ if (event.keyCode == KeyEvent.DOM_VK_ESCAPE) {
+ window.close();
+ }
+ }
+
+ updateSaveButtonState() {
+ // Toggle disabled attribute on the save button based on
+ // whether the form is filled or empty.
+ if (!Object.keys(this._elements.fieldContainer.buildFormObject()).length) {
+ this._elements.save.setAttribute("disabled", true);
+ } else {
+ this._elements.save.removeAttribute("disabled");
+ }
+ }
+
+ /**
+ * Attach event listener
+ */
+ attachEventListeners() {
+ window.addEventListener("keypress", this);
+ window.addEventListener("contextmenu", this);
+ this._elements.controlsContainer.addEventListener("click", this);
+ document.addEventListener("input", this);
+ }
+
+ // An interface to be inherited.
+ localizeDocument() {}
+
+ recordFormSubmit() {
+ let method = this._record?.guid ? "edit" : "add";
+ AutofillTelemetry.recordManageEvent(this.telemetryType, method);
+ }
+}
+
+class EditAddressDialog extends AutofillEditDialog {
+ telemetryType = AutofillTelemetry.ADDRESS;
+
+ constructor(elements, record) {
+ super("addresses", elements, record);
+ if (record) {
+ AutofillTelemetry.recordManageEvent(this.telemetryType, "show_entry");
+ }
+ }
+
+ localizeDocument() {
+ if (this._record?.guid) {
+ document.l10n.setAttributes(
+ this._elements.title,
+ "autofill-edit-address-title"
+ );
+ }
+ }
+
+ async handleSubmit() {
+ await this.saveRecord(
+ this._elements.fieldContainer.buildFormObject(),
+ this._record ? this._record.guid : null
+ );
+ this.recordFormSubmit();
+
+ window.close();
+ }
+}
+
+class EditCreditCardDialog extends AutofillEditDialog {
+ telemetryType = AutofillTelemetry.CREDIT_CARD;
+
+ constructor(elements, record) {
+ elements.fieldContainer._elements.billingAddress.disabled = true;
+ super("creditCards", elements, record);
+ elements.fieldContainer._elements.ccNumber.addEventListener(
+ "blur",
+ this._onCCNumberFieldBlur.bind(this)
+ );
+ if (record) {
+ AutofillTelemetry.recordManageEvent(this.telemetryType, "show_entry");
+ }
+ }
+
+ _onCCNumberFieldBlur() {
+ let elem = this._elements.fieldContainer._elements.ccNumber;
+ this._elements.fieldContainer.updateCustomValidity(elem);
+ }
+
+ localizeDocument() {
+ if (this._record?.guid) {
+ document.l10n.setAttributes(
+ this._elements.title,
+ "autofill-edit-card-title"
+ );
+ }
+ }
+
+ async handleSubmit() {
+ let creditCard = this._elements.fieldContainer.buildFormObject();
+ if (!this._elements.fieldContainer._elements.form.reportValidity()) {
+ return;
+ }
+
+ try {
+ await this.saveRecord(
+ creditCard,
+ this._record ? this._record.guid : null
+ );
+
+ this.recordFormSubmit();
+
+ window.close();
+ } catch (ex) {
+ console.error(ex);
+ }
+ }
+}