summaryrefslogtreecommitdiffstats
path: root/toolkit/components/formautofill/FormAutofillChild.sys.mjs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 01:14:29 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 01:14:29 +0000
commitfbaf0bb26397aa498eb9156f06d5a6fe34dd7dd8 (patch)
tree4c1ccaf5486d4f2009f9a338a98a83e886e29c97 /toolkit/components/formautofill/FormAutofillChild.sys.mjs
parentReleasing progress-linux version 124.0.1-1~progress7.99u1. (diff)
downloadfirefox-fbaf0bb26397aa498eb9156f06d5a6fe34dd7dd8.tar.xz
firefox-fbaf0bb26397aa498eb9156f06d5a6fe34dd7dd8.zip
Merging upstream version 125.0.1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'toolkit/components/formautofill/FormAutofillChild.sys.mjs')
-rw-r--r--toolkit/components/formautofill/FormAutofillChild.sys.mjs524
1 files changed, 418 insertions, 106 deletions
diff --git a/toolkit/components/formautofill/FormAutofillChild.sys.mjs b/toolkit/components/formautofill/FormAutofillChild.sys.mjs
index c40bfddbce..8678a7bd45 100644
--- a/toolkit/components/formautofill/FormAutofillChild.sys.mjs
+++ b/toolkit/components/formautofill/FormAutofillChild.sys.mjs
@@ -2,16 +2,36 @@
* 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/. */
+import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
+import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
+
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
AutoCompleteChild: "resource://gre/actors/AutoCompleteChild.sys.mjs",
+ AutofillTelemetry: "resource://gre/modules/shared/AutofillTelemetry.sys.mjs",
FormAutofill: "resource://autofill/FormAutofill.sys.mjs",
FormAutofillContent: "resource://autofill/FormAutofillContent.sys.mjs",
FormAutofillUtils: "resource://gre/modules/shared/FormAutofillUtils.sys.mjs",
+ FormStateManager: "resource://gre/modules/shared/FormStateManager.sys.mjs",
+ PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.sys.mjs",
+ ProfileAutocomplete:
+ "resource://autofill/AutofillProfileAutoComplete.sys.mjs",
setTimeout: "resource://gre/modules/Timer.sys.mjs",
+ FORM_SUBMISSION_REASON: "resource://gre/actors/FormHandlerChild.sys.mjs",
});
+XPCOMUtils.defineLazyPreferenceGetter(
+ lazy,
+ "DELEGATE_AUTOCOMPLETE",
+ "toolkit.autocomplete.delegate",
+ false
+);
+
+const formFillController = Cc[
+ "@mozilla.org/satchel/form-fill-controller;1"
+].getService(Ci.nsIFormFillController);
+
const observer = {
QueryInterface: ChromeUtils.generateQI([
"nsIWebProgressListener",
@@ -31,7 +51,7 @@ const observer = {
formAutofillChild.onPageNavigation();
},
- onStateChange(aWebProgress, aRequest, aStateFlags, aStatus) {
+ onStateChange(aWebProgress, aRequest, aStateFlags, _aStatus) {
if (
// if restoring a previously-rendered presentation (bfcache)
aStateFlags & Ci.nsIWebProgressListener.STATE_RESTORING &&
@@ -77,21 +97,34 @@ export class FormAutofillChild extends JSWindowActorChild {
constructor() {
super();
+ this.log = lazy.FormAutofill.defineLogGetter(this, "FormAutofillChild");
+ this.debug("init");
+
this._nextHandleElement = null;
- this._alreadyDOMContentLoaded = false;
this._hasDOMContentLoadedHandler = false;
this._hasPendingTask = false;
- this.testListener = null;
+
+ // Flag indicating whether the form is waiting to be filled by Autofill.
+ this._autofillPending = false;
+
+ /**
+ * @type {FormAutofillFieldDetailsManager} handling state management of current forms and handlers.
+ */
+ this._fieldDetailsManager = new lazy.FormStateManager(
+ this.formSubmitted.bind(this),
+ this.formAutofilled.bind(this)
+ );
lazy.AutoCompleteChild.addPopupStateListener(this);
}
didDestroy() {
+ this._fieldDetailsManager.didDestroy();
+
lazy.AutoCompleteChild.removePopupStateListener(this);
- lazy.FormAutofillContent.didDestroy();
}
- popupStateChanged(messageName, data, target) {
+ popupStateChanged(messageName, data, _target) {
let docShell;
try {
docShell = this.docShell;
@@ -108,50 +141,56 @@ export class FormAutofillChild extends JSWindowActorChild {
switch (messageName) {
case "FormAutoComplete:PopupClosed": {
- lazy.FormAutofillContent.onPopupClosed(data.selectedRowStyle);
+ this.onPopupClosed(data.selectedRowStyle);
Services.tm.dispatchToMainThread(() => {
- chromeEventHandler.removeEventListener(
- "keydown",
- lazy.FormAutofillContent._onKeyDown,
- true
- );
+ chromeEventHandler.removeEventListener("keydown", this, true);
});
break;
}
case "FormAutoComplete:PopupOpened": {
- lazy.FormAutofillContent.onPopupOpened();
- chromeEventHandler.addEventListener(
- "keydown",
- lazy.FormAutofillContent._onKeyDown,
- true
- );
+ this.onPopupOpened();
+ chromeEventHandler.addEventListener("keydown", this, true);
break;
}
}
}
/**
- * Invokes the FormAutofillContent to identify the autofill fields
- * and consider opening the dropdown menu for the focused field
- *
+ * Identifies and marks each autofill field
*/
- _doIdentifyAutofillFields() {
+ identifyAutofillFields() {
if (this._hasPendingTask) {
return;
}
this._hasPendingTask = true;
lazy.setTimeout(() => {
- const isAnyFieldIdentified =
- lazy.FormAutofillContent.identifyAutofillFields(
- this._nextHandleElement
- );
- if (isAnyFieldIdentified) {
+ const element = this._nextHandleElement;
+ this.debug(
+ `identifyAutofillFields: ${element.ownerDocument.location?.hostname}`
+ );
+
+ if (
+ lazy.DELEGATE_AUTOCOMPLETE ||
+ !lazy.FormAutofillContent.savedFieldNames
+ ) {
+ this.debug("identifyAutofillFields: savedFieldNames are not known yet");
+
+ // Init can be asynchronous because we don't need anything from the parent
+ // at this point.
+ this.sendAsyncMessage("FormAutofill:InitStorage");
+ }
+
+ const validDetails =
+ this._fieldDetailsManager.identifyAutofillFields(element);
+
+ validDetails?.forEach(detail =>
+ this._markAsAutofillField(detail.element)
+ );
+ if (validDetails.length) {
if (lazy.FormAutofill.captureOnFormRemoval) {
- this.registerDOMDocFetchSuccessEventListener(
- this._nextHandleElement.ownerDocument
- );
+ this.registerDOMDocFetchSuccessEventListener();
}
if (lazy.FormAutofill.captureOnPageNavigation) {
this.registerProgressListener();
@@ -163,7 +202,7 @@ export class FormAutofillChild extends JSWindowActorChild {
// This is for testing purpose only which sends a notification to indicate that the
// form has been identified, and ready to open popup.
this.sendAsyncMessage("FormAutofill:FieldsIdentified");
- lazy.FormAutofillContent.updateActiveInput();
+ this.updateActiveInput();
});
}
@@ -192,13 +231,18 @@ export class FormAutofillChild extends JSWindowActorChild {
/**
* After being notified of a page navigation, we check whether
* the navigated window is the active window or one of its parents
- * (active window = FormAutofillContent.activeHandler.window)
+ * (active window = activeHandler.window)
*
* @returns {boolean} whether the navigation affects the active window
*/
isActiveWindowNavigation() {
- const activeWindow = lazy.FormAutofillContent.activeHandler.window;
+ const activeWindow = lazy.FormAutofillContent.activeHandler?.window;
const navigatedWindow = this.document.defaultView;
+
+ if (!activeWindow || !navigatedWindow) {
+ return false;
+ }
+
const navigatedBrowsingContext =
BrowsingContext.getFromWindow(navigatedWindow);
@@ -218,19 +262,23 @@ export class FormAutofillChild extends JSWindowActorChild {
* Infer a form submission after document is navigated
*/
onPageNavigation() {
- const activeElement =
- lazy.FormAutofillContent.activeFieldDetail?.elementWeakRef.deref();
-
if (!this.isActiveWindowNavigation()) {
return;
}
- const formSubmissionReason =
- lazy.FormAutofillUtils.FORM_SUBMISSION_REASON.PAGE_NAVIGATION;
+ // TODO: We should not use FormAutofillContent and let the
+ // parent decides which child to notify
+ const activeChild = lazy.FormAutofillContent.activeAutofillChild;
+ const activeElement = activeChild.activeFieldDetail?.elementWeakRef.deref();
+ if (!activeElement) {
+ return;
+ }
+
+ const formSubmissionReason = lazy.FORM_SUBMISSION_REASON.PAGE_NAVIGATION;
// We only capture the form of the active field right now,
// this means that we might miss some fields (see bug 1871356)
- lazy.FormAutofillContent.formSubmitted(activeElement, formSubmissionReason);
+ activeChild.formSubmitted(activeElement, formSubmissionReason);
}
/**
@@ -267,11 +315,9 @@ export class FormAutofillChild extends JSWindowActorChild {
/**
* After a focusin event and after we identify formautofill fields,
* we set up an event listener for the DOMDocFetchSuccess event
- *
- * @param {Document} document The document we want to be notified by of a DOMDocFetchSuccess event
*/
- registerDOMDocFetchSuccessEventListener(document) {
- document.setNotifyFetchSuccess(true);
+ registerDOMDocFetchSuccessEventListener() {
+ this.document.setNotifyFetchSuccess(true);
// Is removed after a DOMDocFetchSuccess event (bug 1864855)
/* eslint-disable mozilla/balanced-listeners */
@@ -284,11 +330,9 @@ export class FormAutofillChild extends JSWindowActorChild {
/**
* After a DOMDocFetchSuccess event, we register an event listener for the DOMFormRemoved event
- *
- * @param {Document} document The document we want to be notified by of a DOMFormRemoved event
*/
- registerDOMFormRemovedEventListener(document) {
- document.setNotifyFormOrPasswordRemoved(true);
+ registerDOMFormRemovedEventListener() {
+ this.document.setNotifyFormOrPasswordRemoved(true);
// Is removed after a DOMFormRemoved event (bug 1864855)
/* eslint-disable mozilla/balanced-listeners */
@@ -301,11 +345,9 @@ export class FormAutofillChild extends JSWindowActorChild {
/**
* After a DOMDocFetchSuccess event we remove the DOMDocFetchSuccess event listener
- *
- * @param {Document} document The document we are notified by of a DOMDocFetchSuccess event
*/
- unregisterDOMDocFetchSuccessEventListener(document) {
- document.setNotifyFetchSuccess(false);
+ unregisterDOMDocFetchSuccessEventListener() {
+ this.document.setNotifyFetchSuccess(false);
this.docShell.chromeEventHandler.removeEventListener(
"DOMDocFetchSuccess",
this
@@ -314,11 +356,9 @@ export class FormAutofillChild extends JSWindowActorChild {
/**
* After a DOMFormRemoved event we remove the DOMFormRemoved event listener
- *
- * @param {Document} document The document we are notified by of a DOMFormRemoved event
*/
- unregisterDOMFormRemovedEventListener(document) {
- document.setNotifyFormOrPasswordRemoved(false);
+ unregisterDOMFormRemovedEventListener() {
+ this.document.setNotifyFormOrPasswordRemoved(false);
this.docShell.chromeEventHandler.removeEventListener(
"DOMFormRemoved",
this
@@ -327,11 +367,7 @@ export class FormAutofillChild extends JSWindowActorChild {
shouldIgnoreFormAutofillEvent(event) {
let nodePrincipal = event.target.nodePrincipal;
- return (
- nodePrincipal.isSystemPrincipal ||
- nodePrincipal.isNullPrincipal ||
- nodePrincipal.schemeIs("about")
- );
+ return nodePrincipal.isSystemPrincipal || nodePrincipal.schemeIs("about");
}
handleEvent(evt) {
@@ -342,16 +378,20 @@ export class FormAutofillChild extends JSWindowActorChild {
return;
}
+ if (!this.windowContext) {
+ // !this.windowContext must not be null, because we need the
+ // windowContext and/or docShell to (un)register form submission listeners
+ return;
+ }
+
switch (evt.type) {
- case "focusin": {
- if (lazy.FormAutofill.isAutofillEnabled) {
- this.onFocusIn(evt);
- }
+ case "keydown": {
+ this._onKeyDown(evt);
break;
}
- case "DOMFormBeforeSubmit": {
+ case "focusin": {
if (lazy.FormAutofill.isAutofillEnabled) {
- this.onDOMFormBeforeSubmit(evt);
+ this.onFocusIn(evt);
}
break;
}
@@ -360,7 +400,13 @@ export class FormAutofillChild extends JSWindowActorChild {
break;
}
case "DOMDocFetchSuccess": {
- this.onDOMDocFetchSuccess(evt);
+ this.onDOMDocFetchSuccess();
+ break;
+ }
+ case "form-submission-detected": {
+ if (lazy.FormAutofill.isAutofillEnabled) {
+ this.onFormSubmission(evt);
+ }
break;
}
@@ -371,45 +417,42 @@ export class FormAutofillChild extends JSWindowActorChild {
}
onFocusIn(evt) {
- lazy.FormAutofillContent.updateActiveInput();
+ this.updateActiveInput();
- let element = evt.target;
+ const element = evt.target;
if (!lazy.FormAutofillUtils.isCreditCardOrAddressFieldType(element)) {
return;
}
- this._nextHandleElement = element;
- if (!this._alreadyDOMContentLoaded) {
- let doc = element.ownerDocument;
- if (doc.readyState === "loading") {
- if (!this._hasDOMContentLoadedHandler) {
- this._hasDOMContentLoadedHandler = true;
- doc.addEventListener(
- "DOMContentLoaded",
- () => this._doIdentifyAutofillFields(),
- { once: true }
- );
- }
- return;
+ this._nextHandleElement = element;
+ const doc = element.ownerDocument;
+ if (doc.readyState === "loading") {
+ // For auto-focused input, we might receive focus event before document becomes ready.
+ // When this happens, run field identification after receiving `DOMContentLoaded` event
+ if (!this._hasDOMContentLoadedHandler) {
+ this._hasDOMContentLoadedHandler = true;
+ doc.addEventListener(
+ "DOMContentLoaded",
+ () => this.identifyAutofillFields(),
+ { once: true }
+ );
}
- this._alreadyDOMContentLoaded = true;
+ return;
}
- this._doIdentifyAutofillFields();
+ this.identifyAutofillFields();
}
/**
- * Handle the DOMFormBeforeSubmit event.
+ * Handle form-submission-detected event (dispatched by FormHandlerChild)
*
- * @param {Event} evt
+ * @param {CustomEvent} evt form-submission-detected event
*/
- onDOMFormBeforeSubmit(evt) {
- const formElement = evt.target;
-
- const formSubmissionReason =
- lazy.FormAutofillUtils.FORM_SUBMISSION_REASON.FORM_SUBMIT_EVENT;
+ onFormSubmission(evt) {
+ const formElement = evt.detail.form;
+ const formSubmissionReason = evt.detail.reason;
- lazy.FormAutofillContent.formSubmitted(formElement, formSubmissionReason);
+ this.formSubmitted(formElement, formSubmissionReason);
}
/**
@@ -421,14 +464,10 @@ export class FormAutofillChild extends JSWindowActorChild {
* @param {Event} evt DOMFormRemoved
*/
onDOMFormRemoved(evt) {
- const document = evt.composedTarget.ownerDocument;
-
const formSubmissionReason =
- lazy.FormAutofillUtils.FORM_SUBMISSION_REASON.FORM_REMOVAL_AFTER_FETCH;
-
- lazy.FormAutofillContent.formSubmitted(evt.target, formSubmissionReason);
+ lazy.FORM_SUBMISSION_REASON.FORM_REMOVAL_AFTER_FETCH;
- this.unregisterDOMFormRemovedEventListener(document);
+ this.formSubmitted(evt.target, formSubmissionReason);
}
/**
@@ -436,15 +475,21 @@ export class FormAutofillChild extends JSWindowActorChild {
*
* Sets up an event listener for the DOMFormRemoved event
* and unregisters the event listener for DOMDocFetchSuccess event.
- *
- * @param {Event} evt DOMDocFetchSuccess
*/
- onDOMDocFetchSuccess(evt) {
- const document = evt.target;
+ onDOMDocFetchSuccess() {
+ this.registerDOMFormRemovedEventListener();
- this.registerDOMFormRemovedEventListener(document);
+ this.unregisterDOMDocFetchSuccessEventListener();
+ }
- this.unregisterDOMDocFetchSuccessEventListener(document);
+ /**
+ * Unregister all listeners that notify of a form submission,
+ * because we just detected and acted on a form submission
+ */
+ unregisterFormSubmissionListeners() {
+ this.unregisterDOMDocFetchSuccessEventListener();
+ this.unregisterDOMFormRemovedEventListener();
+ this.unregisterProgressListener();
}
receiveMessage(message) {
@@ -456,17 +501,284 @@ export class FormAutofillChild extends JSWindowActorChild {
switch (message.name) {
case "FormAutofill:PreviewProfile": {
- lazy.FormAutofillContent.previewProfile(doc);
+ this.previewProfile(doc);
break;
}
case "FormAutofill:ClearForm": {
- lazy.FormAutofillContent.clearForm();
+ this.clearForm();
break;
}
case "FormAutofill:FillForm": {
- lazy.FormAutofillContent.activeHandler.autofillFormFields(message.data);
+ this.activeHandler.autofillFormFields(message.data);
break;
}
}
}
+
+ get activeFieldDetail() {
+ return this._fieldDetailsManager.activeFieldDetail;
+ }
+
+ get activeFormDetails() {
+ return this._fieldDetailsManager.activeFormDetails;
+ }
+
+ get activeInput() {
+ return this._fieldDetailsManager.activeInput;
+ }
+
+ get activeHandler() {
+ return this._fieldDetailsManager.activeHandler;
+ }
+
+ get activeSection() {
+ return this._fieldDetailsManager.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}`);
+
+ 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;
+ }
+
+ // Unregister the form submission listeners after handling a form submission
+ this.debug("Unregistering form submission listeners");
+ this.unregisterFormSubmissionListeners();
+
+ [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.sendAsyncMessage("FormAutofill:OnFormSubmit", records);
+ }
+
+ formAutofilled() {
+ lazy.FormAutofillContent.showPopup();
+ }
+
+ /**
+ * 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;
+ }
+ lazy.FormAutofillContent.updateActiveAutofillChild(this);
+
+ 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"
+ ) {
+ lazy.FormAutofillContent.showPopup();
+ }
+ }
+ }
+
+ set autofillPending(flag) {
+ this.debug("Setting autofillPending to", flag);
+ this._autofillPending = flag;
+ }
+
+ clearForm() {
+ let focusedInput =
+ this.activeInput ||
+ lazy.ProfileAutocomplete._lastAutoCompleteFocusedInput;
+ if (!focusedInput) {
+ return;
+ }
+
+ this.activeSection.clearPopulatedForm();
+
+ let fieldName = this.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;
+
+ if (
+ selectedIndex === -1 ||
+ !focusedInput ||
+ !lastAutoCompleteResult ||
+ lastAutoCompleteResult.getStyleAt(selectedIndex) != "autofill-profile"
+ ) {
+ 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) {
+ 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() {
+ this.debug(
+ "Popup has opened, automatic =",
+ formFillController.passwordPopupAutomaticallyOpened
+ );
+
+ let fieldName = this.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 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;
+ }
}