summaryrefslogtreecommitdiffstats
path: root/toolkit/components/formautofill/FormAutofillContent.sys.mjs
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/components/formautofill/FormAutofillContent.sys.mjs')
-rw-r--r--toolkit/components/formautofill/FormAutofillContent.sys.mjs367
1 files changed, 27 insertions, 340 deletions
diff --git a/toolkit/components/formautofill/FormAutofillContent.sys.mjs b/toolkit/components/formautofill/FormAutofillContent.sys.mjs
index 133e5e1d0a..c07e0d67b3 100644
--- a/toolkit/components/formautofill/FormAutofillContent.sys.mjs
+++ b/toolkit/components/formautofill/FormAutofillContent.sys.mjs
@@ -8,43 +8,18 @@
/* eslint-disable no-use-before-define */
-import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
-import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
-
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
FormAutofill: "resource://autofill/FormAutofill.sys.mjs",
- FormAutofillUtils: "resource://gre/modules/shared/FormAutofillUtils.sys.mjs",
- PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.sys.mjs",
- FormStateManager: "resource://gre/modules/shared/FormStateManager.sys.mjs",
ProfileAutocomplete:
"resource://autofill/AutofillProfileAutoComplete.sys.mjs",
- AutofillTelemetry: "resource://autofill/AutofillTelemetry.sys.mjs",
});
-XPCOMUtils.defineLazyPreferenceGetter(
- lazy,
- "DELEGATE_AUTOCOMPLETE",
- "toolkit.autocomplete.delegate",
- false
-);
-
const formFillController = Cc[
"@mozilla.org/satchel/form-fill-controller;1"
].getService(Ci.nsIFormFillController);
-function getActorFromWindow(contentWindow, name = "FormAutofill") {
- // In unit tests, contentWindow isn't a real window.
- if (!contentWindow) {
- return null;
- }
-
- return contentWindow.windowGlobalChild
- ? contentWindow.windowGlobalChild.getActor(name)
- : null;
-}
-
/**
* Handles content's interactions for the process.
*
@@ -63,12 +38,6 @@ export var FormAutofillContent = {
*/
_popupPending: false,
- /**
- * @type {boolean} Flag indicating whether the form is waiting to be
- * filled by Autofill.
- */
- _autofillPending: false,
-
init() {
this.log = lazy.FormAutofill.defineLogGetter(this, "FormAutofillContent");
this.debug("init");
@@ -76,11 +45,13 @@ export var FormAutofillContent = {
// eslint-disable-next-line mozilla/balanced-listeners
Services.cpmm.sharedData.addEventListener("change", this);
- let autofillEnabled = Services.cpmm.sharedData.get("FormAutofill:enabled");
+ const autofillEnabled = Services.cpmm.sharedData.get(
+ "FormAutofill:enabled"
+ );
// If storage hasn't be initialized yet autofillEnabled is undefined but we need to ensure
// autocomplete is registered before the focusin so register it in this case as long as the
// pref is true.
- let shouldEnableAutofill =
+ const shouldEnableAutofill =
autofillEnabled === undefined &&
(lazy.FormAutofill.isAutofillAddressesEnabled ||
lazy.FormAutofill.isAutofillCreditCardsEnabled);
@@ -88,120 +59,49 @@ export var FormAutofillContent = {
lazy.ProfileAutocomplete.ensureRegistered();
}
- /**
- * @type {FormAutofillFieldDetailsManager} handling state management of current forms and handlers.
- */
- this._fieldDetailsManager = new lazy.FormStateManager(
- this.formSubmitted.bind(this),
- this._showPopup.bind(this)
- );
+ this.activeAutofillChild = null;
},
get activeFieldDetail() {
- return this._fieldDetailsManager.activeFieldDetail;
+ return this.activeAutofillChild?.activeFieldDetail;
},
get activeFormDetails() {
- return this._fieldDetailsManager.activeFormDetails;
+ return this.activeAutofillChild?.activeFormDetails;
},
get activeInput() {
- return this._fieldDetailsManager.activeInput;
+ return this.activeAutofillChild?.activeInput;
},
get activeHandler() {
- return this._fieldDetailsManager.activeHandler;
+ return this.activeAutofillChild?.activeHandler;
},
get activeSection() {
- return this._fieldDetailsManager.activeSection;
- },
-
- /**
- * Send the profile to parent for doorhanger and storage saving/updating.
- *
- * @param {object} profile Submitted form's address/creditcard guid and record.
- * @param {object} domWin Current content window.
- */
- _onFormSubmit(profile, domWin) {
- let actor = getActorFromWindow(domWin);
- actor.sendAsyncMessage("FormAutofill:OnFormSubmit", profile);
+ return this.activeAutofillChild?.activeSection;
},
- /**
- * Handle a form submission and early return when:
- * 1. In private browsing mode.
- * 2. Could not map any autofill handler by form element.
- * 3. Number of filled fields is less than autofill threshold
- *
- * @param {HTMLElement} formElement Root element which receives submit event.
- * @param {string} formSubmissionReason Reason for invoking the form submission
- * (see options for FORM_SUBMISSION_REASON in FormAutofillUtils))
- * @param {Window} domWin Content window; passed for unit tests and when
- * invoked by the FormAutofillSection
- * @param {object} handler FormAutofillHander, if known by caller
- */
- formSubmitted(
- formElement,
- formSubmissionReason,
- domWin = formElement.ownerGlobal,
- handler = undefined
- ) {
- this.debug(`Handling form submission - infered by ${formSubmissionReason}`);
-
- // Unregister the progress listener since we detected a form submission
- // (domWin is null in unit tests)
- getActorFromWindow(domWin)?.unregisterProgressListener();
-
- lazy.AutofillTelemetry.recordFormSubmissionHeuristicCount(
- formSubmissionReason
- );
-
- if (!lazy.FormAutofill.isAutofillEnabled) {
- this.debug("Form Autofill is disabled");
- return;
- }
-
- // The `domWin` truthiness test is used by unit tests to bypass this check.
- if (domWin && lazy.PrivateBrowsingUtils.isContentWindowPrivate(domWin)) {
- this.debug("Ignoring submission in a private window");
- return;
- }
-
- handler = handler || this._fieldDetailsManager._getFormHandler(formElement);
- const records = this._fieldDetailsManager.getRecords(formElement, handler);
-
- if (!records || !handler) {
- this.debug("Form element could not map to an existing handler");
- return;
+ set autofillPending(flag) {
+ if (this.activeAutofillChild) {
+ this.activeAutofillChild.autofillPending = flag;
}
+ },
- [records.address, records.creditCard].forEach((rs, idx) => {
- lazy.AutofillTelemetry.recordSubmittedSectionCount(
- idx == 0
- ? lazy.AutofillTelemetry.ADDRESS
- : lazy.AutofillTelemetry.CREDIT_CARD,
- rs?.length
- );
-
- rs?.forEach(r => {
- lazy.AutofillTelemetry.recordFormInteractionEvent(
- "submitted",
- r.section,
- {
- record: r,
- form: handler.form,
- }
- );
- delete r.section;
- });
- });
-
- this._onFormSubmit(records, domWin);
+ updateActiveAutofillChild(autofillChild) {
+ this.activeAutofillChild = autofillChild;
},
- _showPopup() {
- formFillController.showPopup();
+ showPopup() {
+ if (Services.cpmm.sharedData.get("FormAutofill:enabled")) {
+ this.debug("updateActiveElement: opening pop up");
+ formFillController.showPopup();
+ } else {
+ this.debug(
+ "updateActiveElement: Deferring pop-up until Autofill is ready"
+ );
+ this._popupPending = true;
+ }
},
handleEvent(evt) {
@@ -215,7 +115,7 @@ export var FormAutofillContent = {
if (this._popupPending) {
this._popupPending = false;
this.debug("handleEvent: Opening deferred popup");
- this._showPopup();
+ formFillController.showPopup();
}
} else {
lazy.ProfileAutocomplete.ensureUnregistered();
@@ -224,219 +124,6 @@ export var FormAutofillContent = {
}
}
},
-
- /**
- * All active items should be updated according the active element of
- * `formFillController.focusedInput`. All of them including element,
- * handler, section, and field detail, can be retrieved by their own getters.
- *
- * @param {HTMLElement|null} element The active item should be updated based
- * on this or `formFillController.focusedInput` will be taken.
- */
- updateActiveInput(element) {
- element = element || formFillController.focusedInput;
- if (!element) {
- this.debug("updateActiveElement: no element selected");
- return;
- }
- this._fieldDetailsManager.updateActiveInput(element);
- this.debug("updateActiveElement: checking for popup-on-focus");
- // We know this element just received focus. If it's a credit card field,
- // open its popup.
- if (this._autofillPending) {
- this.debug("updateActiveElement: skipping check; autofill is imminent");
- } else if (element.value?.length !== 0) {
- this.debug(
- `updateActiveElement: Not opening popup because field is not empty.`
- );
- } else {
- this.debug(
- "updateActiveElement: checking if empty field is cc-*: ",
- this.activeFieldDetail?.fieldName
- );
-
- if (
- this.activeFieldDetail?.fieldName?.startsWith("cc-") ||
- AppConstants.platform === "android"
- ) {
- if (Services.cpmm.sharedData.get("FormAutofill:enabled")) {
- this.debug("updateActiveElement: opening pop up");
- this._showPopup();
- } else {
- this.debug(
- "updateActiveElement: Deferring pop-up until Autofill is ready"
- );
- this._popupPending = true;
- }
- }
- }
- },
-
- set autofillPending(flag) {
- this.debug("Setting autofillPending to", flag);
- this._autofillPending = flag;
- },
-
- /**
- * Identifies and marks each autofill field
- *
- * @param {HTMLElement} element
- * Element that serves as an anchor for the formautofill heuristics to retrieve
- * the root form and run the formautofill heuristics on the form elements
- * @returns {boolean}
- * whether any autofill fields were identified
- */
- identifyAutofillFields(element) {
- this.debug(
- `identifyAutofillFields: ${element.ownerDocument.location?.hostname}`
- );
-
- if (lazy.DELEGATE_AUTOCOMPLETE || !this.savedFieldNames) {
- this.debug("identifyAutofillFields: savedFieldNames are not known yet");
- let actor = getActorFromWindow(element.ownerGlobal);
- if (actor) {
- actor.sendAsyncMessage("FormAutofill:InitStorage");
- }
- }
-
- const validDetails =
- this._fieldDetailsManager.identifyAutofillFields(element);
-
- validDetails?.forEach(detail => this._markAsAutofillField(detail.element));
-
- return !!validDetails.length;
- },
-
- clearForm() {
- let focusedInput =
- this.activeInput ||
- lazy.ProfileAutocomplete._lastAutoCompleteFocusedInput;
- if (!focusedInput) {
- return;
- }
-
- this.activeSection.clearPopulatedForm();
-
- let fieldName = FormAutofillContent.activeFieldDetail?.fieldName;
- if (lazy.FormAutofillUtils.isCreditCardField(fieldName)) {
- lazy.AutofillTelemetry.recordFormInteractionEvent(
- "cleared",
- this.activeSection,
- { fieldName }
- );
- }
- },
-
- previewProfile(doc) {
- let docWin = doc.ownerGlobal;
- let selectedIndex = lazy.ProfileAutocomplete._getSelectedIndex(docWin);
- let lastAutoCompleteResult =
- lazy.ProfileAutocomplete.lastProfileAutoCompleteResult;
- let focusedInput = this.activeInput;
- let actor = getActorFromWindow(docWin);
-
- if (
- selectedIndex === -1 ||
- !focusedInput ||
- !lastAutoCompleteResult ||
- lastAutoCompleteResult.getStyleAt(selectedIndex) != "autofill-profile"
- ) {
- actor.sendAsyncMessage("FormAutofill:UpdateWarningMessage", {});
-
- lazy.ProfileAutocomplete._clearProfilePreview();
- } else {
- let focusedInputDetails = this.activeFieldDetail;
- let profile = JSON.parse(
- lastAutoCompleteResult.getCommentAt(selectedIndex)
- );
- let allFieldNames = FormAutofillContent.activeSection.allFieldNames;
- let profileFields = allFieldNames.filter(
- fieldName => !!profile[fieldName]
- );
-
- let focusedCategory = lazy.FormAutofillUtils.getCategoryFromFieldName(
- focusedInputDetails.fieldName
- );
- let categories =
- lazy.FormAutofillUtils.getCategoriesFromFieldNames(profileFields);
- actor.sendAsyncMessage("FormAutofill:UpdateWarningMessage", {
- focusedCategory,
- categories,
- });
-
- lazy.ProfileAutocomplete._previewSelectedProfile(selectedIndex);
- }
- },
-
- onPopupClosed(selectedRowStyle) {
- this.debug("Popup has closed.");
- lazy.ProfileAutocomplete._clearProfilePreview();
-
- let lastAutoCompleteResult =
- lazy.ProfileAutocomplete.lastProfileAutoCompleteResult;
- let focusedInput = FormAutofillContent.activeInput;
- if (
- lastAutoCompleteResult &&
- FormAutofillContent._keyDownEnterForInput &&
- focusedInput === FormAutofillContent._keyDownEnterForInput &&
- focusedInput ===
- lazy.ProfileAutocomplete.lastProfileAutoCompleteFocusedInput
- ) {
- if (selectedRowStyle == "autofill-footer") {
- let actor = getActorFromWindow(focusedInput.ownerGlobal);
- actor.sendAsyncMessage("FormAutofill:OpenPreferences");
- } else if (selectedRowStyle == "autofill-clear-button") {
- FormAutofillContent.clearForm();
- }
- }
- },
-
- onPopupOpened() {
- this.debug(
- "Popup has opened, automatic =",
- formFillController.passwordPopupAutomaticallyOpened
- );
-
- let fieldName = FormAutofillContent.activeFieldDetail?.fieldName;
- if (fieldName && this.activeSection) {
- lazy.AutofillTelemetry.recordFormInteractionEvent(
- "popup_shown",
- this.activeSection,
- { fieldName }
- );
- }
- },
-
- _markAsAutofillField(field) {
- // Since Form Autofill popup is only for input element, any non-Input
- // element should be excluded here.
- if (!HTMLInputElement.isInstance(field)) {
- return;
- }
-
- formFillController.markAsAutofillField(field);
- },
-
- _onKeyDown(e) {
- delete FormAutofillContent._keyDownEnterForInput;
- let lastAutoCompleteResult =
- lazy.ProfileAutocomplete.lastProfileAutoCompleteResult;
- let focusedInput = FormAutofillContent.activeInput;
- if (
- e.keyCode != e.DOM_VK_RETURN ||
- !lastAutoCompleteResult ||
- !focusedInput ||
- focusedInput !=
- lazy.ProfileAutocomplete.lastProfileAutoCompleteFocusedInput
- ) {
- return;
- }
- FormAutofillContent._keyDownEnterForInput = focusedInput;
- },
-
- didDestroy() {
- this._fieldDetailsManager.didDestroy();
- },
};
FormAutofillContent.init();