From da4c7e7ed675c3bf405668739c3012d140856109 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 15 May 2024 05:34:42 +0200 Subject: Adding upstream version 126.0. Signed-off-by: Daniel Baumann --- .../AutofillProfileAutoComplete.sys.mjs | 95 +++------- .../formautofill/FormAutofillChild.sys.mjs | 94 +--------- .../formautofill/FormAutofillParent.sys.mjs | 103 +++-------- toolkit/components/formautofill/Helpers.ios.mjs | 8 +- .../formautofill/ProfileAutoCompleteResult.sys.mjs | 205 +++++++++++++++------ .../android/FormAutofillStorage.sys.mjs | 4 +- .../default/FormAutofillPrompter.sys.mjs | 15 +- .../formautofill/shared/AddressComponent.sys.mjs | 3 +- .../shared/FormAutofillSection.sys.mjs | 6 +- .../formautofill/shared/FormAutofillUtils.sys.mjs | 42 +++++ 10 files changed, 266 insertions(+), 309 deletions(-) (limited to 'toolkit/components/formautofill') diff --git a/toolkit/components/formautofill/AutofillProfileAutoComplete.sys.mjs b/toolkit/components/formautofill/AutofillProfileAutoComplete.sys.mjs index fc3f0454b0..7594fc8fcf 100644 --- a/toolkit/components/formautofill/AutofillProfileAutoComplete.sys.mjs +++ b/toolkit/components/formautofill/AutofillProfileAutoComplete.sys.mjs @@ -6,7 +6,10 @@ * Form Autofill content process module. */ -import { GenericAutocompleteItem } from "resource://gre/modules/FillHelpers.sys.mjs"; +import { + GenericAutocompleteItem, + sendFillRequestToParent, +} from "resource://gre/modules/FillHelpers.sys.mjs"; /* eslint-disable no-use-before-define */ @@ -29,16 +32,6 @@ const autocompleteController = Cc[ "@mozilla.org/autocomplete/controller;1" ].getService(Ci.nsIAutoCompleteController); -ChromeUtils.defineLazyGetter( - lazy, - "ADDRESSES_COLLECTION_NAME", - () => lazy.FormAutofillUtils.ADDRESSES_COLLECTION_NAME -); -ChromeUtils.defineLazyGetter( - lazy, - "CREDITCARDS_COLLECTION_NAME", - () => lazy.FormAutofillUtils.CREDITCARDS_COLLECTION_NAME -); ChromeUtils.defineLazyGetter( lazy, "FIELD_STATES", @@ -191,14 +184,8 @@ AutofillProfileAutoCompleteSearch.prototype = { isInputAutofilled, }); } else { - let infoWithoutElement = { ...activeFieldDetail }; - delete infoWithoutElement.elementWeakRef; - - let data = { - collectionName: isAddressField - ? lazy.ADDRESSES_COLLECTION_NAME - : lazy.CREDITCARDS_COLLECTION_NAME, - info: infoWithoutElement, + const data = { + fieldName: activeFieldDetail.fieldName, searchString, }; @@ -284,12 +271,10 @@ AutofillProfileAutoCompleteSearch.prototype = { * Input element for autocomplete. * @param {object} data * Parameters for querying the corresponding result. - * @param {string} data.collectionName - * The name used to specify which collection to retrieve records. * @param {string} data.searchString * The typed string for filtering out the matched records. - * @param {string} data.info - * The input autocomplete property's information. + * @param {string} data.fieldName + * The identified field name for the input * @returns {Promise} * Promise that resolves when addresses returned from parent process. */ @@ -368,44 +353,6 @@ export const ProfileAutocomplete = { } }, - fillRequestId: 0, - - async sendFillRequestToFormAutofillParent(input, comment) { - if (!comment) { - return false; - } - - if (!input || input != autocompleteController?.input.focusedInput) { - return false; - } - - const { fillMessageName, fillMessageData } = JSON.parse(comment ?? "{}"); - if (!fillMessageName) { - return false; - } - - this.fillRequestId++; - const fillRequestId = this.fillRequestId; - const actor = getActorFromWindow(input.ownerGlobal, "FormAutofill"); - const value = await actor.sendQuery(fillMessageName, fillMessageData ?? {}); - - // skip fill if another fill operation started during await - if (fillRequestId != this.fillRequestId) { - return false; - } - - if (typeof value !== "string") { - return false; - } - - // If AutoFillParent returned a string to fill, we must do it here because - // nsAutoCompleteController.cpp already finished it's work before we finished await. - input.setUserInput(value); - input.select(value.length, value.length); - - return true; - }, - _getSelectedIndex(contentWindow) { let actor = getActorFromWindow(contentWindow, "AutoComplete"); if (!actor) { @@ -431,18 +378,31 @@ export const ProfileAutocomplete = { ? this.lastProfileAutoCompleteResult.getCommentAt(selectedIndex) : null; + let profile = JSON.parse(comment); if ( selectedIndex == -1 || !this.lastProfileAutoCompleteResult || - this.lastProfileAutoCompleteResult.getStyleAt(selectedIndex) != - "autofill-profile" + this.lastProfileAutoCompleteResult.getStyleAt(selectedIndex) != "autofill" ) { - await this.sendFillRequestToFormAutofillParent(focusedInput, comment); + if ( + focusedInput && + focusedInput == autocompleteController?.input.focusedInput + ) { + if (profile?.fillMessageName == "FormAutofill:ClearForm") { + // The child can do this directly. + getActorFromWindow(focusedInput.ownerGlobal)?.clearForm(); + } else { + // Pass focusedInput as both input arguments. + await sendFillRequestToParent( + "FormAutofill", + autocompleteController.input, + comment + ); + } + } return; } - let profile = JSON.parse(comment); - await lazy.FormAutofillContent.activeHandler.autofillFormFields(profile); }, @@ -468,8 +428,7 @@ export const ProfileAutocomplete = { if ( !this.lastProfileAutoCompleteResult || - this.lastProfileAutoCompleteResult.getStyleAt(selectedIndex) != - "autofill-profile" + this.lastProfileAutoCompleteResult.getStyleAt(selectedIndex) != "autofill" ) { return; } diff --git a/toolkit/components/formautofill/FormAutofillChild.sys.mjs b/toolkit/components/formautofill/FormAutofillChild.sys.mjs index 8678a7bd45..af84459432 100644 --- a/toolkit/components/formautofill/FormAutofillChild.sys.mjs +++ b/toolkit/components/formautofill/FormAutofillChild.sys.mjs @@ -124,33 +124,18 @@ export class FormAutofillChild extends JSWindowActorChild { lazy.AutoCompleteChild.removePopupStateListener(this); } - popupStateChanged(messageName, data, _target) { - let docShell; - try { - docShell = this.docShell; - } catch (ex) { - lazy.AutoCompleteChild.removePopupStateListener(this); - return; - } - + popupStateChanged(messageName, _data, _target) { if (!lazy.FormAutofill.isAutofillEnabled) { return; } - const { chromeEventHandler } = docShell; - switch (messageName) { - case "FormAutoComplete:PopupClosed": { - this.onPopupClosed(data.selectedRowStyle); - Services.tm.dispatchToMainThread(() => { - chromeEventHandler.removeEventListener("keydown", this, true); - }); - + case "AutoComplete:PopupClosed": { + this.onPopupClosed(); break; } - case "FormAutoComplete:PopupOpened": { + case "AutoComplete:PopupOpened": { this.onPopupOpened(); - chromeEventHandler.addEventListener("keydown", this, true); break; } } @@ -385,10 +370,6 @@ export class FormAutofillChild extends JSWindowActorChild { } switch (evt.type) { - case "keydown": { - this._onKeyDown(evt); - break; - } case "focusin": { if (lazy.FormAutofill.isAutofillEnabled) { this.onFocusIn(evt); @@ -497,11 +478,9 @@ export class FormAutofillChild extends JSWindowActorChild { return; } - const doc = this.document; - switch (message.name) { case "FormAutofill:PreviewProfile": { - this.previewProfile(doc); + this.previewProfile(message.data.selectedIndex); break; } case "FormAutofill:ClearForm": { @@ -677,9 +656,7 @@ export class FormAutofillChild extends JSWindowActorChild { } } - previewProfile(doc) { - let docWin = doc.ownerGlobal; - let selectedIndex = lazy.ProfileAutocomplete._getSelectedIndex(docWin); + previewProfile(selectedIndex) { let lastAutoCompleteResult = lazy.ProfileAutocomplete.lastProfileAutoCompleteResult; let focusedInput = this.activeInput; @@ -688,55 +665,17 @@ export class FormAutofillChild extends JSWindowActorChild { selectedIndex === -1 || !focusedInput || !lastAutoCompleteResult || - lastAutoCompleteResult.getStyleAt(selectedIndex) != "autofill-profile" + lastAutoCompleteResult.getStyleAt(selectedIndex) != "autofill" ) { - this.sendAsyncMessage("FormAutofill:UpdateWarningMessage", {}); - lazy.ProfileAutocomplete._clearProfilePreview(); } else { - let focusedInputDetails = this.activeFieldDetail; - let profile = JSON.parse( - lastAutoCompleteResult.getCommentAt(selectedIndex) - ); - let allFieldNames = this.activeSection.allFieldNames; - let profileFields = allFieldNames.filter( - fieldName => !!profile[fieldName] - ); - - let focusedCategory = lazy.FormAutofillUtils.getCategoryFromFieldName( - focusedInputDetails.fieldName - ); - let categories = - lazy.FormAutofillUtils.getCategoriesFromFieldNames(profileFields); - this.sendAsyncMessage("FormAutofill:UpdateWarningMessage", { - focusedCategory, - categories, - }); - lazy.ProfileAutocomplete._previewSelectedProfile(selectedIndex); } } - onPopupClosed(selectedRowStyle) { + onPopupClosed() { this.debug("Popup has closed."); lazy.ProfileAutocomplete._clearProfilePreview(); - - let lastAutoCompleteResult = - lazy.ProfileAutocomplete.lastProfileAutoCompleteResult; - let focusedInput = this.activeInput; - if ( - lastAutoCompleteResult && - this._keyDownEnterForInput && - focusedInput === this._keyDownEnterForInput && - focusedInput === - lazy.ProfileAutocomplete.lastProfileAutoCompleteFocusedInput - ) { - if (selectedRowStyle == "autofill-footer") { - this.sendAsyncMessage("FormAutofill:OpenPreferences"); - } else if (selectedRowStyle == "autofill-clear-button") { - this.clearForm(); - } - } } onPopupOpened() { @@ -764,21 +703,4 @@ export class FormAutofillChild extends JSWindowActorChild { formFillController.markAsAutofillField(field); } - - _onKeyDown(e) { - delete this._keyDownEnterForInput; - let lastAutoCompleteResult = - lazy.ProfileAutocomplete.lastProfileAutoCompleteResult; - let focusedInput = this.activeInput; - if ( - e.keyCode != e.DOM_VK_RETURN || - !lastAutoCompleteResult || - !focusedInput || - focusedInput != - lazy.ProfileAutocomplete.lastProfileAutoCompleteFocusedInput - ) { - return; - } - this._keyDownEnterForInput = focusedInput; - } } diff --git a/toolkit/components/formautofill/FormAutofillParent.sys.mjs b/toolkit/components/formautofill/FormAutofillParent.sys.mjs index 61c4bd2943..34dac8ce15 100644 --- a/toolkit/components/formautofill/FormAutofillParent.sys.mjs +++ b/toolkit/components/formautofill/FormAutofillParent.sys.mjs @@ -84,21 +84,6 @@ export let FormAutofillStatus = { Services.prefs.addObserver(ENABLED_AUTOFILL_CREDITCARDS_PREF, this); } - // We have to use empty window type to get all opened windows here because the - // window type parameter may not be available during startup. - for (let win of Services.wm.getEnumerator("")) { - let { documentElement } = win.document; - if (documentElement?.getAttribute("windowtype") == "navigator:browser") { - this.injectElements(win.document); - } else { - // Manually call onOpenWindow for windows that are already opened but not - // yet have the window type set. This ensures we inject the elements we need - // when its docuemnt is ready. - this.onOpenWindow(win); - } - } - Services.wm.addListener(this); - Services.telemetry.setEventRecordingEnabled("creditcard", true); Services.telemetry.setEventRecordingEnabled("address", true); }, @@ -198,31 +183,6 @@ export let FormAutofillStatus = { this.updateStatus(); }, - injectElements(doc) { - Services.scriptloader.loadSubScript( - "chrome://formautofill/content/customElements.js", - doc.ownerGlobal - ); - }, - - onOpenWindow(xulWindow) { - const win = xulWindow.docShell.domWindow; - win.addEventListener( - "load", - () => { - if ( - win.document.documentElement.getAttribute("windowtype") == - "navigator:browser" - ) { - this.injectElements(win.document); - } - }, - { once: true } - ); - }, - - onCloseWindow() {}, - async observe(subject, topic, data) { lazy.log.debug("observe:", topic, "with data:", data); switch (topic) { @@ -312,7 +272,7 @@ export class FormAutofillParent extends JSWindowActorParent { scenarioName: data.scenarioName, hasInput: !!data.searchString?.length, }); - const recordsPromise = FormAutofillParent._getRecords(data); + const recordsPromise = FormAutofillParent.getRecords(data); const [records, externalEntries] = await Promise.all([ recordsPromise, relayPromise, @@ -448,42 +408,42 @@ export class FormAutofillParent extends JSWindowActorParent { * * This is static as a unit test calls this. * - * @private * @param {object} data - * @param {string} data.collectionName - * The name used to specify which collection to retrieve records. * @param {string} data.searchString * The typed string for filtering out the matched records. - * @param {string} data.info - * The input autocomplete property's information. + * @param {string} data.collectionName + * The name used to specify which collection to retrieve records. + * @param {string} data.fieldName + * The field name to search. If not specified, return all records in + * the collection */ - static async _getRecords({ collectionName, searchString, info }) { - let collection = lazy.gFormAutofillStorage[collectionName]; + static async getRecords({ searchString, collectionName, fieldName }) { + // Derive the collection name from field name if it doesn't exist + collectionName ||= + FormAutofillUtils.getCollectionNameFromFieldName(fieldName); + + const collection = lazy.gFormAutofillStorage[collectionName]; if (!collection) { return []; } - let recordsInCollection = await collection.getAll(); - if (!info || !info.fieldName || !recordsInCollection.length) { - return recordsInCollection; + const records = await collection.getAll(); + if (!fieldName || !records.length) { + return records; } - let isCC = collectionName == CREDITCARDS_COLLECTION_NAME; // We don't filter "cc-number" - if (isCC && info.fieldName == "cc-number") { - recordsInCollection = recordsInCollection.filter( - record => !!record["cc-number"] - ); - return recordsInCollection; + if (collectionName == CREDITCARDS_COLLECTION_NAME) { + if (fieldName == "cc-number") { + return records.filter(record => !!record["cc-number"]); + } } - let records = []; - let lcSearchString = searchString.toLowerCase(); - - for (let record of recordsInCollection) { - let fieldValue = record[info.fieldName]; + const lcSearchString = searchString.toLowerCase(); + return records.filter(record => { + const fieldValue = record[fieldName]; if (!fieldValue) { - continue; + return false; } if ( @@ -493,19 +453,14 @@ export class FormAutofillParent extends JSWindowActorParent { ) { // Address autofill isn't supported for the record's country so we don't // want to attempt to potentially incorrectly fill the address fields. - continue; - } - - if ( - lcSearchString && - !String(fieldValue).toLowerCase().startsWith(lcSearchString) - ) { - continue; + return false; } - records.push(record); - } - return records; + return ( + !lcSearchString || + String(fieldValue).toLowerCase().startsWith(lcSearchString) + ); + }); } async _onAddressSubmit(address, browser) { diff --git a/toolkit/components/formautofill/Helpers.ios.mjs b/toolkit/components/formautofill/Helpers.ios.mjs index 56bb49f0e9..83137331f1 100644 --- a/toolkit/components/formautofill/Helpers.ios.mjs +++ b/toolkit/components/formautofill/Helpers.ios.mjs @@ -45,12 +45,6 @@ HTMLElement.prototype.getAutocompleteInfo = function () { }; }; -// Bug 1835024. Webkit doesn't support `checkVisibility` API -// https://drafts.csswg.org/cssom-view-1/#dom-element-checkvisibility -HTMLElement.prototype.checkVisibility = function (_options) { - throw new Error(`Not implemented: WebKit doesn't support checkVisibility `); -}; - // This function helps us debug better when an error occurs because a certain mock is missing const withNotImplementedError = obj => new Proxy(obj, { @@ -106,7 +100,7 @@ export const XPCOMUtils = withNotImplementedError({ prop, pref, defaultValue = null, - onUpdate = null, + onUpdate, transform = val => val ) => { if (!Object.keys(IOSAppConstants.prefs).includes(pref)) { diff --git a/toolkit/components/formautofill/ProfileAutoCompleteResult.sys.mjs b/toolkit/components/formautofill/ProfileAutoCompleteResult.sys.mjs index 52ed8bed03..68df30f8b5 100644 --- a/toolkit/components/formautofill/ProfileAutoCompleteResult.sys.mjs +++ b/toolkit/components/formautofill/ProfileAutoCompleteResult.sys.mjs @@ -131,6 +131,12 @@ class ProfileAutoCompleteResult { if (typeof label == "string") { return label; } + + let type = this.getTypeOfIndex(index); + if (type == "clear" || type == "manage") { + return label.primary; + } + return JSON.stringify(label); } @@ -141,6 +147,16 @@ class ProfileAutoCompleteResult { * @returns {string} The comment at the specified index */ getCommentAt(index) { + let type = this.getTypeOfIndex(index); + switch (type) { + case "clear": + return '{"fillMessageName": "FormAutofill:ClearForm"}'; + case "manage": + return '{"fillMessageName": "FormAutofill:OpenPreferences"}'; + case "insecure": + return '{"noLearnMore": true }'; + } + const item = this.getAt(index); return item.comment ?? JSON.stringify(this._matchingProfiles[index]); } @@ -157,14 +173,16 @@ class ProfileAutoCompleteResult { return itemStyle; } - if (index == this._popupLabels.length - 1) { - return "autofill-footer"; - } - if (this._isInputAutofilled) { - return "autofill-clear-button"; + switch (this.getTypeOfIndex(index)) { + case "manage": + return "action"; + case "clear": + return "action"; + case "insecure": + return "insecureWarning"; + default: + return "autofill"; } - - return "autofill-profile"; } /** @@ -205,6 +223,24 @@ class ProfileAutoCompleteResult { removeValueAt(_index) { // There is no plan to support removing profiles via autocomplete. } + + /** + * Returns a type string that identifies te type of row at the given index. + * + * @param {number} index The index of the result requested + * @returns {string} The type at the specified index + */ + getTypeOfIndex(index) { + if (this._isInputAutofilled && index == 0) { + return "clear"; + } + + if (index == this._popupLabels.length - 1) { + return "manage"; + } + + return "item"; + } } export class AddressResult extends ProfileAutoCompleteResult { @@ -281,18 +317,26 @@ export class AddressResult extends ProfileAutoCompleteResult { "autofill-manage-addresses-label" ); + let footerItem = { + primary: manageLabel, + secondary: "", + }; + if (this._isInputAutofilled) { - return [ - { primary: "", secondary: "" }, // Clear button - // Footer + const clearLabel = lazy.l10n.formatValueSync("autofill-clear-form-label"); + + let labels = [ { - primary: "", - secondary: "", - manageLabel, + primary: clearLabel, }, ]; + labels.push(footerItem); + return labels; } + let focusedCategory = + lazy.FormAutofillUtils.getCategoryFromFieldName(focusedFieldName); + // Skip results without a primary label. let labels = profiles .filter(profile => { @@ -306,35 +350,88 @@ export class AddressResult extends ProfileAutoCompleteResult { ) { primaryLabel = profile["-moz-street-address-one-line"]; } + + let profileFields = allFieldNames.filter( + fieldName => !!profile[fieldName] + ); + + let categories = + lazy.FormAutofillUtils.getCategoriesFromFieldNames(profileFields); + let status = this.getStatusNote(categories, focusedCategory); + let secondary = this._getSecondaryLabel( + focusedFieldName, + allFieldNames, + profile + ); + const ariaLabel = [primaryLabel, secondary, status] + .filter(chunk => !!chunk) // Exclude empty chunks. + .join(" "); return { primary: primaryLabel, - secondary: this._getSecondaryLabel( - focusedFieldName, - allFieldNames, - profile - ), + secondary, + status, + ariaLabel, }; }); - const focusedCategory = - lazy.FormAutofillUtils.getCategoryFromFieldName(focusedFieldName); + let allCategories = + lazy.FormAutofillUtils.getCategoriesFromFieldNames(allFieldNames); + + if (allCategories && allCategories.length) { + let statusItem = { + primary: "", + secondary: "", + status: this.getStatusNote(allCategories, focusedCategory), + style: "status", + }; + labels.push(statusItem); + } - // Add an empty result entry for footer. Its content will come from - // the footer binding, so don't assign any value to it. - // The additional properties: categories and focusedCategory are required of - // the popup to generate autofill hint on the footer. - labels.push({ - primary: "", - secondary: "", - manageLabel, - categories: lazy.FormAutofillUtils.getCategoriesFromFieldNames( - this._allFieldNames - ), - focusedCategory, - }); + labels.push(footerItem); return labels; } + + getStatusNote(categories, focusedCategory) { + if (!categories || !categories.length) { + return ""; + } + + // If the length of categories is 1, that means all the fillable fields are in the same + // category. We will change the way to inform user according to this flag. When the value + // is true, we show "Also autofills ...", otherwise, show "Autofills ..." only. + let hasExtraCategories = categories.length > 1; + // Show the categories in certain order to conform with the spec. + let orderedCategoryList = [ + "address", + "name", + "organization", + "tel", + "email", + ]; + let showCategories = hasExtraCategories + ? orderedCategoryList.filter( + category => + categories.includes(category) && category != focusedCategory + ) + : [orderedCategoryList.find(category => category == focusedCategory)]; + + let formatter = new Intl.ListFormat(undefined, { + style: "narrow", + }); + + let categoriesText = showCategories.map(category => + lazy.l10n.formatValueSync("autofill-category-" + category) + ); + categoriesText = formatter.format(categoriesText); + + let statusTextTmplKey = hasExtraCategories + ? "autofill-phishing-warningmessage-extracategory" + : "autofill-phishing-warningmessage"; + return lazy.l10n.formatValueSync(statusTextTmplKey, { + categories: categoriesText, + }); + } } export class CreditCardResult extends ProfileAutoCompleteResult { @@ -401,16 +498,20 @@ export class CreditCardResult extends ProfileAutoCompleteResult { "autofill-manage-payment-methods-label" ); + let footerItem = { + primary: manageLabel, + }; + if (this._isInputAutofilled) { - return [ - { primary: "", secondary: "" }, // Clear button - // Footer + const clearLabel = lazy.l10n.formatValueSync("autofill-clear-form-label"); + + let labels = [ { - primary: "", - secondary: "", - manageLabel, + primary: clearLabel, }, ]; + labels.push(footerItem); + return labels; } // Skip results without a primary label. @@ -446,37 +547,23 @@ export class CreditCardResult extends ProfileAutoCompleteResult { .filter(chunk => !!chunk) // Exclude empty chunks. .join(" "); return { - primary, - secondary, + primary: primary.toString().replaceAll("*", "•"), + secondary: secondary.toString().replaceAll("*", "•"), ariaLabel, image, }; }); - const focusedCategory = - lazy.FormAutofillUtils.getCategoryFromFieldName(focusedFieldName); - - // Add an empty result entry for footer. - labels.push({ - primary: "", - secondary: "", - manageLabel, - focusedCategory, - }); + labels.push(footerItem); return labels; } - getStyleAt(index) { - const itemStyle = this.getAt(index).style; - if (itemStyle) { - return itemStyle; - } - + getTypeOfIndex(index) { if (!this._isSecure) { - return "autofill-insecureWarning"; + return "insecure"; } - return super.getStyleAt(index); + return super.getTypeOfIndex(index); } } diff --git a/toolkit/components/formautofill/android/FormAutofillStorage.sys.mjs b/toolkit/components/formautofill/android/FormAutofillStorage.sys.mjs index 964be31d06..17a50de7eb 100644 --- a/toolkit/components/formautofill/android/FormAutofillStorage.sys.mjs +++ b/toolkit/components/formautofill/android/FormAutofillStorage.sys.mjs @@ -68,7 +68,7 @@ class Addresses extends AddressesBase { this._initializePromise = Promise.resolve(); } - async _saveRecord(record, { sourceSync = false } = {}) { + async _saveRecord(record) { lazy.GeckoViewAutocomplete.onAddressSave(lazy.Address.fromGecko(record)); } @@ -136,7 +136,7 @@ class CreditCards extends CreditCardsBase { this._initializePromise = Promise.resolve(); } - async _saveRecord(record, { sourceSync = false } = {}) { + async _saveRecord(record) { lazy.GeckoViewAutocomplete.onCreditCardSave( lazy.CreditCard.fromGecko(record) ); diff --git a/toolkit/components/formautofill/default/FormAutofillPrompter.sys.mjs b/toolkit/components/formautofill/default/FormAutofillPrompter.sys.mjs index f166716de5..05dcf5bace 100644 --- a/toolkit/components/formautofill/default/FormAutofillPrompter.sys.mjs +++ b/toolkit/components/formautofill/default/FormAutofillPrompter.sys.mjs @@ -789,16 +789,15 @@ export class AddressEditDoorhanger extends AutofillDoorhanger { input.setAttribute("id", inputId); - const value = this.newRecord[fieldName] ?? ""; if (popup) { - const menuitem = Array.from(popup.childNodes).find( - item => - item.label.toLowerCase() === value?.toLowerCase() || - item.value.toLowerCase() === value?.toLowerCase() - ); - input.selectedItem = menuitem; + input.selectedItem = + FormAutofillUtils.findAddressSelectOptionWithMenuPopup( + popup, + this.newRecord, + fieldName + ); } else { - input.value = value; + input.value = this.newRecord[fieldName] ?? ""; } div.appendChild(input); diff --git a/toolkit/components/formautofill/shared/AddressComponent.sys.mjs b/toolkit/components/formautofill/shared/AddressComponent.sys.mjs index 40e00b66a0..e83cd22251 100644 --- a/toolkit/components/formautofill/shared/AddressComponent.sys.mjs +++ b/toolkit/components/formautofill/shared/AddressComponent.sys.mjs @@ -412,7 +412,6 @@ class State extends AddressField { const options = { merge_whitespace: true, - remove_punctuation: true, }; this.#state = lazy.FormAutofillUtils.getAbbreviatedSubregionName( this.normalizeUserValue(options), @@ -991,7 +990,7 @@ export class AddressComparison { * country, postal code, etc. The class provides a compare methods * to compare another AddressComponent against the current instance. * - * Note. This class assumes records that pass to it have already been normalized. + * Note: This class assumes records that pass to it have already been normalized. */ export class AddressComponent { /** diff --git a/toolkit/components/formautofill/shared/FormAutofillSection.sys.mjs b/toolkit/components/formautofill/shared/FormAutofillSection.sys.mjs index 7bda4c167b..1a5b3014c9 100644 --- a/toolkit/components/formautofill/shared/FormAutofillSection.sys.mjs +++ b/toolkit/components/formautofill/shared/FormAutofillSection.sys.mjs @@ -136,15 +136,15 @@ export class FormAutofillSection { * specific case. Return the original value in the default case. * @param {String} value * The original field value. - * @param {Object} fieldDetail + * @param {Object} _fieldName * A fieldDetail of the related element. - * @param {HTMLElement} element + * @param {HTMLElement} _element * A element for checking converting value. * * @returns {String} * A string of the converted value. */ - computeFillingValue(value, fieldName, element) { + computeFillingValue(value, _fieldName, _element) { return value; } diff --git a/toolkit/components/formautofill/shared/FormAutofillUtils.sys.mjs b/toolkit/components/formautofill/shared/FormAutofillUtils.sys.mjs index e86f14975c..c2b48a53a3 100644 --- a/toolkit/components/formautofill/shared/FormAutofillUtils.sys.mjs +++ b/toolkit/components/formautofill/shared/FormAutofillUtils.sys.mjs @@ -176,6 +176,12 @@ FormAutofillUtils = { return Array.from(categories); }, + getCollectionNameFromFieldName(fieldName) { + return this.isCreditCardField(fieldName) + ? CREDITCARDS_COLLECTION_NAME + : ADDRESSES_COLLECTION_NAME; + }, + getAddressSeparator() { // The separator should be based on the L10N address format, and using a // white space is a temporary solution. @@ -799,6 +805,42 @@ FormAutofillUtils = { return null; }, + /** + * Find the option element from xul menu popups, as used in address capture + * doorhanger. + * + * This is a proxy to `findAddressSelectOption`, which expects HTML select + * DOM nodes and operates on options instead of xul menuitems. + * + * NOTE: This is a temporary solution until Bug 1886949 is landed. This + * method will then be removed `findAddressSelectOption` will be used + * directly. + * + * @param {XULPopupElement} menupopup + * @param {object} address + * @param {string} fieldName + * @returns {XULElement} + */ + findAddressSelectOptionWithMenuPopup(menupopup, address, fieldName) { + class MenuitemProxy { + constructor(menuitem) { + this.menuitem = menuitem; + } + get text() { + return this.menuitem.label; + } + get value() { + return this.menuitem.value; + } + } + const selectEl = { + options: Array.from(menupopup.childNodes).map( + menuitem => new MenuitemProxy(menuitem) + ), + }; + return this.findAddressSelectOption(selectEl, address, fieldName)?.menuitem; + }, + findCreditCardSelectOption(selectEl, creditCard, fieldName) { let oneDigitMonth = creditCard["cc-exp-month"] ? creditCard["cc-exp-month"].toString() -- cgit v1.2.3