From a90a5cba08fdf6c0ceb95101c275108a152a3aed Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 12 Jun 2024 07:35:37 +0200 Subject: Merging upstream version 127.0. Signed-off-by: Daniel Baumann --- .../AutofillProfileAutoComplete.sys.mjs | 334 +-------------------- 1 file changed, 10 insertions(+), 324 deletions(-) (limited to 'toolkit/components/formautofill/AutofillProfileAutoComplete.sys.mjs') diff --git a/toolkit/components/formautofill/AutofillProfileAutoComplete.sys.mjs b/toolkit/components/formautofill/AutofillProfileAutoComplete.sys.mjs index 7594fc8fcf..21282ff936 100644 --- a/toolkit/components/formautofill/AutofillProfileAutoComplete.sys.mjs +++ b/toolkit/components/formautofill/AutofillProfileAutoComplete.sys.mjs @@ -6,38 +6,21 @@ * Form Autofill content process module. */ -import { - GenericAutocompleteItem, - sendFillRequestToParent, -} from "resource://gre/modules/FillHelpers.sys.mjs"; +import { sendFillRequestToParent } from "resource://gre/modules/FillHelpers.sys.mjs"; /* eslint-disable no-use-before-define */ -const Cm = Components.manager; - const lazy = {}; ChromeUtils.defineESModuleGetters(lazy, { - AddressResult: "resource://autofill/ProfileAutoCompleteResult.sys.mjs", - ComponentUtils: "resource://gre/modules/ComponentUtils.sys.mjs", - CreditCardResult: "resource://autofill/ProfileAutoCompleteResult.sys.mjs", FormAutofill: "resource://autofill/FormAutofill.sys.mjs", - FormAutofillUtils: "resource://gre/modules/shared/FormAutofillUtils.sys.mjs", FormAutofillContent: "resource://autofill/FormAutofillContent.sys.mjs", - FormScenarios: "resource://gre/modules/FormScenarios.sys.mjs", - InsecurePasswordUtils: "resource://gre/modules/InsecurePasswordUtils.sys.mjs", }); const autocompleteController = Cc[ "@mozilla.org/autocomplete/controller;1" ].getService(Ci.nsIAutoCompleteController); -ChromeUtils.defineLazyGetter( - lazy, - "FIELD_STATES", - () => lazy.FormAutofillUtils.FIELD_STATES -); - function getActorFromWindow(contentWindow, name = "FormAutofill") { // In unit tests, contentWindow isn't a real window. if (!contentWindow) { @@ -49,257 +32,10 @@ function getActorFromWindow(contentWindow, name = "FormAutofill") { : null; } -// Register/unregister a constructor as a factory. -function AutocompleteFactory() {} -AutocompleteFactory.prototype = { - register(targetConstructor) { - let proto = targetConstructor.prototype; - this._classID = proto.classID; - - let factory = - lazy.ComponentUtils.generateSingletonFactory(targetConstructor); - this._factory = factory; - - let registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar); - registrar.registerFactory( - proto.classID, - proto.classDescription, - proto.contractID, - factory - ); - - if (proto.classID2) { - this._classID2 = proto.classID2; - registrar.registerFactory( - proto.classID2, - proto.classDescription, - proto.contractID2, - factory - ); - } - }, - - unregister() { - let registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar); - registrar.unregisterFactory(this._classID, this._factory); - if (this._classID2) { - registrar.unregisterFactory(this._classID2, this._factory); - } - this._factory = null; - }, -}; - -/** - * @class - * - * @implements {nsIAutoCompleteSearch} - */ -function AutofillProfileAutoCompleteSearch() { - this.log = lazy.FormAutofill.defineLogGetter( - this, - "AutofillProfileAutoCompleteSearch" - ); -} -AutofillProfileAutoCompleteSearch.prototype = { - classID: Components.ID("4f9f1e4c-7f2c-439e-9c9e-566b68bc187d"), - contractID: "@mozilla.org/autocomplete/search;1?name=autofill-profiles", - classDescription: "AutofillProfileAutoCompleteSearch", - QueryInterface: ChromeUtils.generateQI(["nsIAutoCompleteSearch"]), - - // Begin nsIAutoCompleteSearch implementation - - /** - * Searches for a given string and notifies a listener (either synchronously - * or asynchronously) of the result - * - * @param {string} searchString the string to search for - * @param {string} searchParam - * @param {object} previousResult a previous result to use for faster searchinig - * @param {object} listener the listener to notify when the search is complete - */ - startSearch(searchString, searchParam, previousResult, listener) { - let { - activeInput, - activeSection, - activeFieldDetail, - activeHandler, - savedFieldNames, - } = lazy.FormAutofillContent; - this.forceStop = false; - - let isAddressField = lazy.FormAutofillUtils.isAddressField( - activeFieldDetail.fieldName - ); - const isCreditCardField = lazy.FormAutofillUtils.isCreditCardField( - activeFieldDetail.fieldName - ); - let isInputAutofilled = - activeHandler.getFilledStateByElement(activeInput) == - lazy.FIELD_STATES.AUTO_FILLED; - let allFieldNames = activeSection.allFieldNames; - let filledRecordGUID = activeSection.filledRecordGUID; - - let searchPermitted = isAddressField - ? lazy.FormAutofill.isAutofillAddressesEnabled - : lazy.FormAutofill.isAutofillCreditCardsEnabled; - let AutocompleteResult = isAddressField - ? lazy.AddressResult - : lazy.CreditCardResult; - let isFormAutofillSearch = true; - let pendingSearchResult = null; - - ProfileAutocomplete.lastProfileAutoCompleteFocusedInput = activeInput; - // Fallback to form-history if ... - // - specified autofill feature is pref off. - // - no profile can fill the currently-focused input. - // - the current form has already been populated and the field is not - // an empty credit card field. - // - (address only) less than 3 inputs are covered by all saved fields in the storage. - if ( - !searchPermitted || - !savedFieldNames.has(activeFieldDetail.fieldName) || - (!isInputAutofilled && - filledRecordGUID && - !(isCreditCardField && activeInput.value === "")) || - (isAddressField && - allFieldNames.filter(field => savedFieldNames.has(field)).length < - lazy.FormAutofillUtils.AUTOFILL_FIELDS_THRESHOLD) - ) { - isFormAutofillSearch = false; - if (activeInput.autocomplete == "off") { - // Create a dummy result as an empty search result. - pendingSearchResult = new AutocompleteResult("", "", [], [], {}); - } else { - pendingSearchResult = new Promise(resolve => { - let formHistory = Cc[ - "@mozilla.org/autocomplete/search;1?name=form-history" - ].createInstance(Ci.nsIAutoCompleteSearch); - formHistory.startSearch(searchString, searchParam, previousResult, { - onSearchResult: (_, result) => resolve(result), - }); - }); - } - } else if (isInputAutofilled) { - pendingSearchResult = new AutocompleteResult(searchString, "", [], [], { - isInputAutofilled, - }); - } else { - const data = { - fieldName: activeFieldDetail.fieldName, - searchString, - }; - - pendingSearchResult = this._getRecords(activeInput, data).then( - ({ records, externalEntries }) => { - if (this.forceStop) { - return null; - } - // Sort addresses by timeLastUsed for showing the lastest used address at top. - records.sort((a, b) => b.timeLastUsed - a.timeLastUsed); - - let adaptedRecords = activeSection.getAdaptedProfiles(records); - let handler = lazy.FormAutofillContent.activeHandler; - let isSecure = lazy.InsecurePasswordUtils.isFormSecure(handler.form); - - const result = new AutocompleteResult( - searchString, - activeFieldDetail.fieldName, - allFieldNames, - adaptedRecords, - { isSecure, isInputAutofilled } - ); - - result.externalEntries.push( - ...externalEntries.map( - entry => - new GenericAutocompleteItem( - entry.image, - entry.title, - entry.subtitle, - entry.fillMessageName, - entry.fillMessageData - ) - ) - ); - - return result; - } - ); - } - - Promise.resolve(pendingSearchResult).then(result => { - if (this.forceStop) { - // If we notify the listener the search result when the search is already - // cancelled, it corrupts the internal state of the listener. So we only - // reset the controller's state in this case. - if (isFormAutofillSearch) { - autocompleteController.resetInternalState(); - } - return; - } - - listener.onSearchResult(this, result); - // Don't save cache results or reset state when returning non-autofill results such as the - // form history fallback above. - if (isFormAutofillSearch) { - ProfileAutocomplete.lastProfileAutoCompleteResult = result; - // Reset AutoCompleteController's state at the end of startSearch to ensure that - // none of form autofill result will be cached in other places and make the - // result out of sync. - autocompleteController.resetInternalState(); - } else { - // Clear the cache so that we don't try to autofill from it after falling - // back to form history. - ProfileAutocomplete.lastProfileAutoCompleteResult = null; - } - }); - }, - - /** - * Stops an asynchronous search that is in progress - */ - stopSearch() { - ProfileAutocomplete.lastProfileAutoCompleteResult = null; - this.forceStop = true; - }, - - /** - * Get the records from parent process for AutoComplete result. - * - * @private - * @param {object} input - * Input element for autocomplete. - * @param {object} data - * Parameters for querying the corresponding result. - * @param {string} data.searchString - * The typed string for filtering out the matched records. - * @param {string} data.fieldName - * The identified field name for the input - * @returns {Promise} - * Promise that resolves when addresses returned from parent process. - */ - _getRecords(input, data) { - if (!input) { - return []; - } - - let actor = getActorFromWindow(input.ownerGlobal); - return actor.sendQuery("FormAutofill:GetRecords", { - scenarioName: lazy.FormScenarios.detect({ input }).signUpForm - ? "SignUpFormScenario" - : "", - ...data, - }); - }, -}; - export const ProfileAutocomplete = { QueryInterface: ChromeUtils.generateQI(["nsIObserver"]), - lastProfileAutoCompleteResult: null, - lastProfileAutoCompleteFocusedInput: null, _registered: false, - _factory: null, ensureRegistered() { if (this._registered) { @@ -308,8 +44,6 @@ export const ProfileAutocomplete = { this.log = lazy.FormAutofill.defineLogGetter(this, "ProfileAutocomplete"); this.debug("ensureRegistered"); - this._factory = new AutocompleteFactory(); - this._factory.register(AutofillProfileAutoCompleteSearch); this._registered = true; Services.obs.addObserver(this, "autocomplete-will-enter-text"); @@ -326,10 +60,7 @@ export const ProfileAutocomplete = { } this.debug("ensureUnregistered"); - this._factory.unregister(); - this._factory = null; this._registered = false; - this._lastAutoCompleteResult = null; Services.obs.removeObserver(this, "autocomplete-will-enter-text"); }, @@ -341,27 +72,14 @@ export const ProfileAutocomplete = { // The observer notification is for autocomplete in a different process. break; } - lazy.FormAutofillContent.autofillPending = true; - Services.obs.notifyObservers(null, "autofill-fill-starting"); await this._fillFromAutocompleteRow( lazy.FormAutofillContent.activeInput ); - Services.obs.notifyObservers(null, "autofill-fill-complete"); - lazy.FormAutofillContent.autofillPending = false; break; } } }, - _getSelectedIndex(contentWindow) { - let actor = getActorFromWindow(contentWindow, "AutoComplete"); - if (!actor) { - throw new Error("Invalid autocomplete selectedIndex"); - } - - return actor.selectedIndex; - }, - async _fillFromAutocompleteRow(focusedInput) { this.debug("_fillFromAutocompleteRow:", focusedInput); let formDetails = lazy.FormAutofillContent.activeFormDetails; @@ -369,20 +87,24 @@ export const ProfileAutocomplete = { // The observer notification is for a different frame. return; } + const actor = getActorFromWindow(focusedInput.ownerGlobal, "AutoComplete"); + if (!actor) { + throw new Error("Invalid autocomplete selectedIndex"); + } - let selectedIndex = this._getSelectedIndex(focusedInput.ownerGlobal); + const selectedIndex = actor.selectedIndex; + const lastProfileAutoCompleteResult = actor.lastProfileAutoCompleteResult; const validIndex = selectedIndex >= 0 && - selectedIndex < this.lastProfileAutoCompleteResult?.matchCount; + selectedIndex < lastProfileAutoCompleteResult?.matchCount; const comment = validIndex - ? this.lastProfileAutoCompleteResult.getCommentAt(selectedIndex) + ? lastProfileAutoCompleteResult.getCommentAt(selectedIndex) : null; let profile = JSON.parse(comment); if ( selectedIndex == -1 || - !this.lastProfileAutoCompleteResult || - this.lastProfileAutoCompleteResult.getStyleAt(selectedIndex) != "autofill" + lastProfileAutoCompleteResult?.getStyleAt(selectedIndex) != "autofill" ) { if ( focusedInput && @@ -400,42 +122,6 @@ export const ProfileAutocomplete = { ); } } - return; - } - - await lazy.FormAutofillContent.activeHandler.autofillFormFields(profile); - }, - - _clearProfilePreview() { - if ( - !this.lastProfileAutoCompleteFocusedInput || - !lazy.FormAutofillContent.activeSection - ) { - return; - } - - lazy.FormAutofillContent.activeSection.clearPreviewedFormFields(); - }, - - _previewSelectedProfile(selectedIndex) { - if ( - !lazy.FormAutofillContent.activeInput || - !lazy.FormAutofillContent.activeFormDetails - ) { - // The observer notification is for a different process/frame. - return; - } - - if ( - !this.lastProfileAutoCompleteResult || - this.lastProfileAutoCompleteResult.getStyleAt(selectedIndex) != "autofill" - ) { - return; } - - let profile = JSON.parse( - this.lastProfileAutoCompleteResult.getCommentAt(selectedIndex) - ); - lazy.FormAutofillContent.activeSection.previewFormFields(profile); }, }; -- cgit v1.2.3