From e052d1f53776fe55a61460e5e754e0442f54ad1c Mon Sep 17 00:00:00 2001 From: Luke Chang Date: Wed, 23 Aug 2017 17:35:32 +0800 Subject: [PATCH] Bug 1392975 - [Form Autofill] Avoid registering multiple listeners of DOMContentLoaded. r=seanlee MozReview-Commit-ID: Eo3KSBoaotr --HG-- extra : rebase_source : 7ef03e929e5e2320dd5ef692326c4fb31decd719 --- .../formautofill/FormAutofillContent.jsm | 27 ++++---- .../formautofill/FormAutofillUtils.jsm | 6 +- .../content/FormAutofillFrameScript.js | 61 +++++++++++-------- 3 files changed, 50 insertions(+), 44 deletions(-) diff --git a/browser/extensions/formautofill/FormAutofillContent.jsm b/browser/extensions/formautofill/FormAutofillContent.jsm index 704518d26cef..dbffcf29e54e 100644 --- a/browser/extensions/formautofill/FormAutofillContent.jsm +++ b/browser/extensions/formautofill/FormAutofillContent.jsm @@ -480,11 +480,6 @@ var FormAutofillContent = { Services.cpmm.sendAsyncMessage("FormAutofill:InitStorage"); } - if (!FormAutofillUtils.isFieldEligibleForAutofill(element)) { - this.log.debug("Not an eligible field."); - return; - } - let formHandler = this.getFormHandler(element); if (!formHandler) { let formLike = FormLikeFactory.createFromField(element); @@ -504,17 +499,7 @@ var FormAutofillContent = { ); }, - _markAsAutofillField(field) { - // Since Form Autofill popup is only for input element, any non-Input - // element should be excluded here. - if (!field || !(field instanceof Ci.nsIDOMHTMLInputElement)) { - return; - } - - formFillController.markAsAutofillField(field); - }, - - _previewProfile(doc) { + previewProfile(doc) { let docWin = doc.ownerGlobal; let selectedIndex = ProfileAutocomplete._getSelectedIndex(docWin); let lastAutoCompleteResult = ProfileAutocomplete.getProfileAutoCompleteResult(); @@ -545,6 +530,16 @@ var FormAutofillContent = { } }, + _markAsAutofillField(field) { + // Since Form Autofill popup is only for input element, any non-Input + // element should be excluded here. + if (!field || !(field instanceof Ci.nsIDOMHTMLInputElement)) { + return; + } + + formFillController.markAsAutofillField(field); + }, + _messageManagerFromWindow(win) { return win.QueryInterface(Ci.nsIInterfaceRequestor) .getInterface(Ci.nsIWebNavigation) diff --git a/browser/extensions/formautofill/FormAutofillUtils.jsm b/browser/extensions/formautofill/FormAutofillUtils.jsm index 4a66ba40852c..4242fb0d2cb1 100644 --- a/browser/extensions/formautofill/FormAutofillUtils.jsm +++ b/browser/extensions/formautofill/FormAutofillUtils.jsm @@ -26,6 +26,7 @@ Cu.import("resource://gre/modules/Services.jsm"); this.FormAutofillUtils = { get AUTOFILL_FIELDS_THRESHOLD() { return 3; }, + get isAutofillEnabled() { return this.isAutofillAddressesEnabled || this.isAutofillCreditCardsEnabled; }, ADDRESSES_COLLECTION_NAME, CREDITCARDS_COLLECTION_NAME, @@ -141,12 +142,13 @@ this.FormAutofillUtils = { return false; } - if (element instanceof Ci.nsIDOMHTMLInputElement) { + let tagName = element.tagName; + if (tagName == "INPUT") { // `element.type` can be recognized as `text`, if it's missing or invalid. if (!this.ALLOWED_TYPES.includes(element.type)) { return false; } - } else if (!(element instanceof Ci.nsIDOMHTMLSelectElement)) { + } else if (tagName != "SELECT") { return false; } diff --git a/browser/extensions/formautofill/content/FormAutofillFrameScript.js b/browser/extensions/formautofill/content/FormAutofillFrameScript.js index 345b267338c6..364de7e95aba 100644 --- a/browser/extensions/formautofill/content/FormAutofillFrameScript.js +++ b/browser/extensions/formautofill/content/FormAutofillFrameScript.js @@ -12,8 +12,6 @@ const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; -Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://formautofill/FormAutofillContent.jsm"); Cu.import("resource://formautofill/FormAutofillUtils.jsm"); @@ -23,6 +21,24 @@ Cu.import("resource://formautofill/FormAutofillUtils.jsm"); * NOTE: Declares it by "var" to make it accessible in unit tests. */ var FormAutofillFrameScript = { + _nextHandleElement: null, + _alreadyDOMContentLoaded: false, + _hasDOMContentLoadedHandler: false, + _hasPendingTask: false, + + _doIdentifyAutofillFields() { + if (this._hasPendingTask) { + return; + } + this._hasPendingTask = true; + + setTimeout(() => { + FormAutofillContent.identifyAutofillFields(this._nextHandleElement); + this._hasPendingTask = false; + this._nextHandleElement = null; + }); + }, + init() { addEventListener("focusin", this); addMessageListener("FormAutofill:PreviewProfile", this); @@ -31,40 +47,33 @@ var FormAutofillFrameScript = { }, handleEvent(evt) { - if (!evt.isTrusted) { + if (!evt.isTrusted || !FormAutofillUtils.isAutofillEnabled) { return; } - if (!FormAutofillUtils.isAutofillAddressesEnabled && - !FormAutofillUtils.isAutofillCreditCardsEnable) { + let element = evt.target; + if (!FormAutofillUtils.isFieldEligibleForAutofill(element)) { return; } + this._nextHandleElement = element; - switch (evt.type) { - case "focusin": { - let element = evt.target; - let doc = element.ownerDocument; - - if (!FormAutofillUtils.isFieldEligibleForAutofill(element)) { - return; + if (!this._alreadyDOMContentLoaded) { + let doc = element.ownerDocument; + if (doc.readyState === "loading") { + if (!this._hasDOMContentLoadedHandler) { + this._hasDOMContentLoadedHandler = true; + doc.addEventListener("DOMContentLoaded", () => this._doIdentifyAutofillFields(), {once: true}); } - - let doIdentifyAutofillFields = - () => setTimeout(() => FormAutofillContent.identifyAutofillFields(element)); - - if (doc.readyState === "loading") { - doc.addEventListener("DOMContentLoaded", doIdentifyAutofillFields, {once: true}); - } else { - doIdentifyAutofillFields(); - } - break; + return; } + this._alreadyDOMContentLoaded = true; } + + this._doIdentifyAutofillFields(); }, receiveMessage(message) { - if (!FormAutofillUtils.isAutofillAddressesEnabled && - !FormAutofillUtils.isAutofillCreditCardsEnable) { + if (!FormAutofillUtils.isAutofillEnabled) { return; } @@ -73,11 +82,11 @@ var FormAutofillFrameScript = { switch (message.name) { case "FormAutofill:PreviewProfile": { - FormAutofillContent._previewProfile(doc); + FormAutofillContent.previewProfile(doc); break; } case "FormAutoComplete:PopupClosed": { - FormAutofillContent._previewProfile(doc); + FormAutofillContent.previewProfile(doc); chromeEventHandler.removeEventListener("keydown", FormAutofillContent._onKeyDown, {capturing: true}); break;