diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-12 05:43:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-12 05:43:14 +0000 |
commit | 8dd16259287f58f9273002717ec4d27e97127719 (patch) | |
tree | 3863e62a53829a84037444beab3abd4ed9dfc7d0 /browser/extensions/formautofill/content/manageDialog.js | |
parent | Releasing progress-linux version 126.0.1-1~progress7.99u1. (diff) | |
download | firefox-8dd16259287f58f9273002717ec4d27e97127719.tar.xz firefox-8dd16259287f58f9273002717ec4d27e97127719.zip |
Merging upstream version 127.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'browser/extensions/formautofill/content/manageDialog.js')
-rw-r--r-- | browser/extensions/formautofill/content/manageDialog.js | 454 |
1 files changed, 0 insertions, 454 deletions
diff --git a/browser/extensions/formautofill/content/manageDialog.js b/browser/extensions/formautofill/content/manageDialog.js deleted file mode 100644 index ad5cefbb15..0000000000 --- a/browser/extensions/formautofill/content/manageDialog.js +++ /dev/null @@ -1,454 +0,0 @@ -/* 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 ManageAddresses, ManageCreditCards */ - -"use strict"; - -const EDIT_ADDRESS_URL = "chrome://formautofill/content/editAddress.xhtml"; -const EDIT_CREDIT_CARD_URL = - "chrome://formautofill/content/editCreditCard.xhtml"; - -const { AppConstants } = ChromeUtils.importESModule( - "resource://gre/modules/AppConstants.sys.mjs" -); -const { FormAutofill } = ChromeUtils.importESModule( - "resource://autofill/FormAutofill.sys.mjs" -); -const { AutofillTelemetry } = ChromeUtils.importESModule( - "resource://gre/modules/shared/AutofillTelemetry.sys.mjs" -); - -ChromeUtils.defineESModuleGetters(this, { - CreditCard: "resource://gre/modules/CreditCard.sys.mjs", - FormAutofillUtils: "resource://gre/modules/shared/FormAutofillUtils.sys.mjs", - OSKeyStore: "resource://gre/modules/OSKeyStore.sys.mjs", - formAutofillStorage: "resource://autofill/FormAutofillStorage.sys.mjs", -}); - -this.log = null; -ChromeUtils.defineLazyGetter(this, "log", () => - FormAutofill.defineLogGetter(this, "manageAddresses") -); - -class ManageRecords { - constructor(subStorageName, elements) { - this._storageInitPromise = formAutofillStorage.initialize(); - this._subStorageName = subStorageName; - this._elements = elements; - this._newRequest = false; - this._isLoadingRecords = false; - this.prefWin = window.opener; - window.addEventListener("DOMContentLoaded", this, { once: true }); - } - - async init() { - await this.loadRecords(); - this.attachEventListeners(); - // For testing only: Notify when the dialog is ready for interaction - window.dispatchEvent(new CustomEvent("FormReady")); - } - - uninit() { - log.debug("uninit"); - this.detachEventListeners(); - this._elements = null; - } - - /** - * Get the selected options on the addresses element. - * - * @returns {Array<DOMElement>} - */ - get _selectedOptions() { - return Array.from(this._elements.records.selectedOptions); - } - - /** - * Get storage and ensure it has been initialized. - * - * @returns {object} - */ - async getStorage() { - await this._storageInitPromise; - return formAutofillStorage[this._subStorageName]; - } - - /** - * Load records and render them. This function is a wrapper for _loadRecords - * to ensure any reentrant will be handled well. - */ - async loadRecords() { - // This function can be early returned when there is any reentrant happends. - // "_newRequest" needs to be set to ensure all changes will be applied. - if (this._isLoadingRecords) { - this._newRequest = true; - return; - } - this._isLoadingRecords = true; - - await this._loadRecords(); - - // _loadRecords should be invoked again if there is any multiple entrant - // during running _loadRecords(). This step ensures that the latest request - // still is applied. - while (this._newRequest) { - this._newRequest = false; - await this._loadRecords(); - } - this._isLoadingRecords = false; - - // For testing only: Notify when records are loaded - this._elements.records.dispatchEvent(new CustomEvent("RecordsLoaded")); - } - - async _loadRecords() { - let storage = await this.getStorage(); - let records = await storage.getAll(); - // Sort by last used time starting with most recent - records.sort((a, b) => { - let aLastUsed = a.timeLastUsed || a.timeLastModified; - let bLastUsed = b.timeLastUsed || b.timeLastModified; - return bLastUsed - aLastUsed; - }); - await this.renderRecordElements(records); - this.updateButtonsStates(this._selectedOptions.length); - } - - /** - * Render the records onto the page while maintaining selected options if - * they still exist. - * - * @param {Array<object>} records - */ - async renderRecordElements(records) { - let selectedGuids = this._selectedOptions.map(option => option.value); - this.clearRecordElements(); - for (let record of records) { - let { id, args, raw } = await this.getLabelInfo(record); - let option = new Option( - raw ?? "", - record.guid, - false, - selectedGuids.includes(record.guid) - ); - if (id) { - document.l10n.setAttributes(option, id, args); - } - - option.record = record; - this._elements.records.appendChild(option); - } - } - - /** - * Remove all existing record elements. - */ - clearRecordElements() { - let parent = this._elements.records; - while (parent.lastChild) { - parent.removeChild(parent.lastChild); - } - } - - /** - * Remove records by selected options. - * - * @param {Array<DOMElement>} options - */ - async removeRecords(options) { - let storage = await this.getStorage(); - // Pause listening to storage change event to avoid triggering `loadRecords` - // when removing records - Services.obs.removeObserver(this, "formautofill-storage-changed"); - - for (let option of options) { - storage.remove(option.value); - option.remove(); - } - this.updateButtonsStates(this._selectedOptions); - - // Resume listening to storage change event - Services.obs.addObserver(this, "formautofill-storage-changed"); - // For testing only: notify record(s) has been removed - this._elements.records.dispatchEvent(new CustomEvent("RecordsRemoved")); - - for (let i = 0; i < options.length; i++) { - AutofillTelemetry.recordManageEvent(this.telemetryType, "delete"); - } - } - - /** - * Enable/disable the Edit and Remove buttons based on number of selected - * options. - * - * @param {number} selectedCount - */ - updateButtonsStates(selectedCount) { - log.debug("updateButtonsStates:", selectedCount); - if (selectedCount == 0) { - this._elements.edit.setAttribute("disabled", "disabled"); - this._elements.remove.setAttribute("disabled", "disabled"); - } else if (selectedCount == 1) { - this._elements.edit.removeAttribute("disabled"); - this._elements.remove.removeAttribute("disabled"); - } else if (selectedCount > 1) { - this._elements.edit.setAttribute("disabled", "disabled"); - this._elements.remove.removeAttribute("disabled"); - } - this._elements.add.disabled = !Services.prefs.getBoolPref( - `extensions.formautofill.${this._subStorageName}.enabled` - ); - } - - /** - * Handle events - * - * @param {DOMEvent} event - */ - handleEvent(event) { - switch (event.type) { - case "DOMContentLoaded": { - this.init(); - break; - } - case "click": { - this.handleClick(event); - break; - } - case "change": { - this.updateButtonsStates(this._selectedOptions.length); - break; - } - case "unload": { - this.uninit(); - break; - } - case "keypress": { - this.handleKeyPress(event); - break; - } - case "contextmenu": { - event.preventDefault(); - break; - } - } - } - - /** - * Handle click events - * - * @param {DOMEvent} event - */ - handleClick(event) { - if (event.target == this._elements.remove) { - this.removeRecords(this._selectedOptions); - } else if (event.target == this._elements.add) { - this.openEditDialog(); - } else if ( - event.target == this._elements.edit || - (event.target.parentNode == this._elements.records && event.detail > 1) - ) { - this.openEditDialog(this._selectedOptions[0].record); - } - } - - /** - * Handle key press events - * - * @param {DOMEvent} event - */ - handleKeyPress(event) { - if (event.keyCode == KeyEvent.DOM_VK_ESCAPE) { - window.close(); - } - if (event.keyCode == KeyEvent.DOM_VK_DELETE) { - this.removeRecords(this._selectedOptions); - } - } - - observe(_subject, topic, _data) { - switch (topic) { - case "formautofill-storage-changed": { - this.loadRecords(); - } - } - } - - /** - * Attach event listener - */ - attachEventListeners() { - window.addEventListener("unload", this, { once: true }); - window.addEventListener("keypress", this); - window.addEventListener("contextmenu", this); - this._elements.records.addEventListener("change", this); - this._elements.records.addEventListener("click", this); - this._elements.controlsContainer.addEventListener("click", this); - Services.obs.addObserver(this, "formautofill-storage-changed"); - } - - /** - * Remove event listener - */ - detachEventListeners() { - window.removeEventListener("keypress", this); - window.removeEventListener("contextmenu", this); - this._elements.records.removeEventListener("change", this); - this._elements.records.removeEventListener("click", this); - this._elements.controlsContainer.removeEventListener("click", this); - Services.obs.removeObserver(this, "formautofill-storage-changed"); - } -} - -class ManageAddresses extends ManageRecords { - telemetryType = AutofillTelemetry.ADDRESS; - - constructor(elements) { - super("addresses", elements); - elements.add.setAttribute( - "search-l10n-ids", - FormAutofillUtils.EDIT_ADDRESS_L10N_IDS.join(",") - ); - AutofillTelemetry.recordManageEvent(this.telemetryType, "show"); - } - - /** - * Open the edit address dialog to create/edit an address. - * - * @param {object} address [optional] - */ - openEditDialog(address) { - this.prefWin.gSubDialog.open(EDIT_ADDRESS_URL, undefined, { - record: address, - // Don't validate in preferences since it's fine for fields to be missing - // for autofill purposes. For PaymentRequest addresses get more validation. - noValidate: true, - }); - } - - getLabelInfo(address) { - return { raw: FormAutofillUtils.getAddressLabel(address) }; - } -} - -class ManageCreditCards extends ManageRecords { - telemetryType = AutofillTelemetry.CREDIT_CARD; - - constructor(elements) { - super("creditCards", elements); - elements.add.setAttribute( - "search-l10n-ids", - FormAutofillUtils.EDIT_CREDITCARD_L10N_IDS.join(",") - ); - - this._isDecrypted = false; - AutofillTelemetry.recordManageEvent(this.telemetryType, "show"); - } - - /** - * Open the edit address dialog to create/edit a credit card. - * - * @param {object} creditCard [optional] - */ - async openEditDialog(creditCard) { - // Ask for reauth if user is trying to edit an existing credit card. - if (creditCard) { - const promptMessage = FormAutofillUtils.reauthOSPromptMessage( - "autofill-edit-payment-method-os-prompt-macos", - "autofill-edit-payment-method-os-prompt-windows", - "autofill-edit-payment-method-os-prompt-other" - ); - - const loggedIn = await FormAutofillUtils.ensureLoggedIn(promptMessage); - if (!loggedIn.authenticated) { - return; - } - } - - let decryptedCCNumObj = {}; - if (creditCard && creditCard["cc-number-encrypted"]) { - try { - decryptedCCNumObj["cc-number"] = await OSKeyStore.decrypt( - creditCard["cc-number-encrypted"] - ); - } catch (ex) { - if (ex.result == Cr.NS_ERROR_ABORT) { - // User shouldn't be ask to reauth here, but it could happen. - // Return here and skip opening the dialog. - return; - } - // We've got ourselves a real error. - // Recover from encryption error so the user gets a chance to re-enter - // unencrypted credit card number. - decryptedCCNumObj["cc-number"] = ""; - console.error(ex); - } - } - let decryptedCreditCard = Object.assign({}, creditCard, decryptedCCNumObj); - this.prefWin.gSubDialog.open( - EDIT_CREDIT_CARD_URL, - { features: "resizable=no" }, - { - record: decryptedCreditCard, - } - ); - } - - /** - * Get credit card display label. It should display masked numbers and the - * cardholder's name, separated by a comma. - * - * @param {object} creditCard - * @returns {Promise<string>} - */ - async getLabelInfo(creditCard) { - // The card type is displayed visually using an image. For a11y, we need - // to expose it as text. We do this using aria-label. However, - // aria-label overrides the text content, so we must include that also. - // Since the text content is generated by Fluent, aria-label must be - // generated by Fluent also. - const type = creditCard["cc-type"]; - const typeL10nId = CreditCard.getNetworkL10nId(type); - const typeName = typeL10nId - ? await document.l10n.formatValue(typeL10nId) - : type ?? ""; // Unknown card type - return CreditCard.getLabelInfo({ - name: creditCard["cc-name"], - number: creditCard["cc-number"], - month: creditCard["cc-exp-month"], - year: creditCard["cc-exp-year"], - type: typeName, - }); - } - - async renderRecordElements(records) { - // Revert back to encrypted form when re-rendering happens - this._isDecrypted = false; - // Display third-party card icons when possible - this._elements.records.classList.toggle( - "branded", - AppConstants.MOZILLA_OFFICIAL - ); - await super.renderRecordElements(records); - - let options = this._elements.records.options; - for (let option of options) { - let record = option.record; - if (record && record["cc-type"]) { - option.setAttribute("cc-type", record["cc-type"]); - } else { - option.removeAttribute("cc-type"); - } - } - } - - updateButtonsStates(selectedCount) { - super.updateButtonsStates(selectedCount); - } - - handleClick(event) { - super.handleClick(event); - } -} |