Bug 1367077 - 3. Move form fill event listeners out of browser.js; r=sebastian
authorJim Chen <nchen@mozilla.com>
Thu, 14 Sep 2017 17:50:57 -0400
changeset 665150 17f9dd20314af009f328e920f68008885ab8cb73
parent 665149 72e591f41777329d56c686ba778f3e3c8b0dce20
child 665151 5bb25516598f08df439fb16c69d60555b406d353
push id79943
push userbmo:tchiovoloni@mozilla.com
push dateThu, 14 Sep 2017 23:59:48 +0000
reviewerssebastian
bugs1367077
milestone57.0a1
Bug 1367077 - 3. Move form fill event listeners out of browser.js; r=sebastian Move the form fill event listeners out of browser.js and into BrowserCLH.js, and update them to support chrome windows, so we can handle form fill events for Fennec, custom tabs, and PWAs. MozReview-Commit-ID: Fb5gWmGvxfE
mobile/android/chrome/content/browser.js
mobile/android/components/BrowserCLH.js
mobile/android/modules/DelayedInit.jsm
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -41,22 +41,16 @@ XPCOMUtils.defineLazyModuleGetter(this, 
                                   "resource://gre/modules/PluralForm.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "Downloads",
                                   "resource://gre/modules/Downloads.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "UserAgentOverrides",
                                   "resource://gre/modules/UserAgentOverrides.jsm");
 
-XPCOMUtils.defineLazyModuleGetter(this, "LoginManagerContent",
-                                  "resource://gre/modules/LoginManagerContent.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "LoginManagerParent",
-                                  "resource://gre/modules/LoginManagerParent.jsm");
-
 XPCOMUtils.defineLazyModuleGetter(this, "Task", "resource://gre/modules/Task.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "SafeBrowsing",
                                   "resource://gre/modules/SafeBrowsing.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "BrowserUtils",
                                   "resource://gre/modules/BrowserUtils.jsm");
@@ -551,19 +545,16 @@ var BrowserApp = {
 
       InitLater(() => LightWeightThemeWebInstaller.init());
       InitLater(() => CastingApps.init(), window, "CastingApps");
       InitLater(() => Services.search.init(), Services, "search");
 
       // Bug 778855 - Perf regression if we do this here. To be addressed in bug 779008.
       InitLater(() => SafeBrowsing.init(), window, "SafeBrowsing");
 
-      InitLater(() => Cc["@mozilla.org/login-manager;1"].getService(Ci.nsILoginManager));
-      InitLater(() => LoginManagerParent.init(), window, "LoginManagerParent");
-
     }, {once: true});
 
     // Pass caret StateChanged events to ActionBarHandler.
     window.addEventListener("mozcaretstatechanged", e => {
       ActionBarHandler.caretStateChangedHandler(e);
     }, /* useCapture = */ true, /* wantsUntrusted = */ false);
   },
 
@@ -3654,27 +3645,24 @@ Tab.prototype = {
                 Ci.nsIWebProgress.NOTIFY_SECURITY;
     this.filter = Cc["@mozilla.org/appshell/component/browser-status-filter;1"].createInstance(Ci.nsIWebProgress);
     this.filter.addProgressListener(this, flags)
     this.browser.addProgressListener(this.filter, flags);
     this.browser.sessionHistory.addSHistoryListener(this);
 
     this.browser.addEventListener("DOMContentLoaded", this, true);
     this.browser.addEventListener("DOMFormHasPassword", this, true);
-    this.browser.addEventListener("DOMInputPasswordAdded", this, true);
     this.browser.addEventListener("DOMLinkAdded", this, true);
     this.browser.addEventListener("DOMLinkChanged", this, true);
     this.browser.addEventListener("DOMMetaAdded", this);
     this.browser.addEventListener("DOMTitleChanged", this, true);
     this.browser.addEventListener("DOMAudioPlaybackStarted", this, true);
     this.browser.addEventListener("DOMAudioPlaybackStopped", this, true);
     this.browser.addEventListener("DOMWindowClose", this, true);
     this.browser.addEventListener("DOMWillOpenModalDialog", this, true);
-    this.browser.addEventListener("DOMAutoComplete", this, true);
-    this.browser.addEventListener("blur", this, true);
     this.browser.addEventListener("pageshow", this, true);
     this.browser.addEventListener("MozApplicationManifest", this, true);
     this.browser.addEventListener("TabPreZombify", this, true);
     this.browser.addEventListener("DOMWindowFocus", this, true);
 
     // Note that the XBL binding is untrusted
     this.browser.addEventListener("VideoBindingAttached", this, true, true);
     this.browser.addEventListener("VideoBindingCast", this, true, true);
@@ -3769,27 +3757,24 @@ Tab.prototype = {
 
     this.browser.removeProgressListener(this.filter);
     this.filter.removeProgressListener(this);
     this.filter = null;
     this.browser.sessionHistory.removeSHistoryListener(this);
 
     this.browser.removeEventListener("DOMContentLoaded", this, true);
     this.browser.removeEventListener("DOMFormHasPassword", this, true);
-    this.browser.removeEventListener("DOMInputPasswordAdded", this, true);
     this.browser.removeEventListener("DOMLinkAdded", this, true);
     this.browser.removeEventListener("DOMLinkChanged", this, true);
     this.browser.removeEventListener("DOMMetaAdded", this);
     this.browser.removeEventListener("DOMTitleChanged", this, true);
     this.browser.removeEventListener("DOMAudioPlaybackStarted", this, true);
     this.browser.removeEventListener("DOMAudioPlaybackStopped", this, true);
     this.browser.removeEventListener("DOMWindowClose", this, true);
     this.browser.removeEventListener("DOMWillOpenModalDialog", this, true);
-    this.browser.removeEventListener("DOMAutoComplete", this, true);
-    this.browser.removeEventListener("blur", this, true);
     this.browser.removeEventListener("pageshow", this, true);
     this.browser.removeEventListener("MozApplicationManifest", this, true);
     this.browser.removeEventListener("TabPreZombify", this, true);
     this.browser.removeEventListener("DOMWindowFocus", this, true);
 
     this.browser.removeEventListener("VideoBindingAttached", this, true, true);
     this.browser.removeEventListener("VideoBindingCast", this, true, true);
 
@@ -4139,39 +4124,31 @@ Tab.prototype = {
         if (!docURI.startsWith("about:")) {
           WebsiteMetadata.parseAsynchronously(this.browser.contentDocument);
         }
 
         break;
       }
 
       case "DOMFormHasPassword": {
-        LoginManagerContent.onDOMFormHasPassword(aEvent,
-                                                 this.browser.contentWindow);
-
         // Send logins for this hostname to Java.
         let hostname = aEvent.target.baseURIObject.prePath;
         let foundLogins = Services.logins.findLogins({}, hostname, "", "");
         if (foundLogins.length > 0) {
           let displayHost = IdentityHandler.getEffectiveHost();
           let title = { text: displayHost, resource: hostname };
           let selectObj = { title: title, logins: foundLogins };
           GlobalEventDispatcher.sendRequest({
             type: "Doorhanger:Logins",
             data: selectObj
           });
         }
         break;
       }
 
-      case "DOMInputPasswordAdded": {
-        LoginManagerContent.onDOMInputPasswordAdded(aEvent,
-                                                    this.browser.contentWindow);
-      }
-
       case "DOMMetaAdded":
         let target = aEvent.originalTarget;
         let browser = BrowserApp.getBrowserForDocument(target.ownerDocument);
 
         switch (target.name) {
           case "msapplication-TileImage":
             this.addMetadata("tileImage", browser.currentURI.resolve(target.content), this.METADATA_GOOD_MATCH);
             break;
@@ -4291,22 +4268,16 @@ Tab.prototype = {
 
         // We're about to open a modal dialog, make sure the opening
         // tab is brought to the front.
         let tab = BrowserApp.getTabForWindow(aEvent.target.top);
         BrowserApp.selectTab(tab);
         break;
       }
 
-      case "DOMAutoComplete":
-      case "blur": {
-        LoginManagerContent.onUsernameInput(aEvent);
-        break;
-      }
-
       case "VideoBindingAttached": {
         CastingApps.handleVideoBindingAttached(this, aEvent);
         break;
       }
 
       case "VideoBindingCast": {
         CastingApps.handleVideoBindingCast(this, aEvent);
         break;
@@ -4322,18 +4293,16 @@ Tab.prototype = {
           type: "Tab:Select",
           tabID: this.id,
           foreground: true,
         });
         break;
       }
 
       case "pageshow": {
-        LoginManagerContent.onPageShow(aEvent, this.browser.contentWindow);
-
         // The rest of this only handles pageshow for the top-level document.
         if (aEvent.originalTarget.defaultView != this.browser.contentWindow)
           return;
 
         let target = aEvent.originalTarget;
         let docURI = target.documentURI;
         if (!docURI.startsWith("about:neterror") && !this.isSearch) {
           // If this wasn't an error page and the user isn't search, don't retain the typed entry
--- a/mobile/android/components/BrowserCLH.js
+++ b/mobile/android/components/BrowserCLH.js
@@ -3,16 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetters(this, {
   AppConstants: "resource://gre/modules/AppConstants.jsm",
+  DelayedInit: "resource://gre/modules/DelayedInit.jsm",
   GeckoViewUtils: "resource://gre/modules/GeckoViewUtils.jsm",
   Services: "resource://gre/modules/Services.jsm",
 });
 
 var Strings = {};
 
 XPCOMUtils.defineLazyGetter(Strings, "brand", _ =>
         Services.strings.createBundle("chrome://branding/locale/brand.properties"));
@@ -69,16 +70,43 @@ BrowserCLH.prototype = {
         }
 
         GeckoViewUtils.addLazyGetter(this, "SelectHelper", {
           script: "chrome://browser/content/SelectHelper.js",
         });
         GeckoViewUtils.addLazyGetter(this, "InputWidgetHelper", {
           script: "chrome://browser/content/InputWidgetHelper.js",
         });
+
+        GeckoViewUtils.addLazyGetter(this, "LoginManagerParent", {
+          module: "resource://gre/modules/LoginManagerParent.jsm",
+          mm: [
+            // PLEASE KEEP THIS LIST IN SYNC WITH THE DESKTOP LIST IN nsBrowserGlue.js
+            "RemoteLogins:findLogins",
+            "RemoteLogins:findRecipes",
+            "RemoteLogins:onFormSubmit",
+            "RemoteLogins:autoCompleteLogins",
+            "RemoteLogins:removeLogin",
+            "RemoteLogins:insecureLoginFormPresent",
+            // PLEASE KEEP THIS LIST IN SYNC WITH THE DESKTOP LIST IN nsBrowserGlue.js
+          ],
+        });
+        GeckoViewUtils.addLazyGetter(this, "LoginManagerContent", {
+          module: "resource://gre/modules/LoginManagerContent.jsm",
+        });
+
+        // Once the first chrome window is loaded, schedule a list of startup
+        // tasks to be performed on idle.
+        GeckoViewUtils.addLazyGetter(this, "DelayedStartup", {
+          observers: ["chrome-document-loaded"],
+          once: true,
+          handler: _ => DelayedInit.scheduleList([
+            _ => Services.logins,
+          ], 10000 /* 10 seconds maximum wait. */),
+        });
         break;
       }
 
       case "chrome-document-global-created":
       case "content-document-global-created": {
         let win = GeckoViewUtils.getChromeWindow(subject);
         if (win !== subject) {
           // Only attach to top-level windows.
@@ -87,21 +115,59 @@ BrowserCLH.prototype = {
 
         GeckoViewUtils.addLazyEventListener(win, "click", {
           handler: _ => [this.SelectHelper, this.InputWidgetHelper],
           options: {
             capture: true,
             mozSystemGroup: true,
           },
         });
+
+        this._initLoginManagerEvents(win);
         break;
       }
     }
   },
 
+  _initLoginManagerEvents: function(aWindow) {
+    if (Services.prefs.getBoolPref("reftest.remote", false)) {
+      // XXX known incompatibility between reftest harness and form-fill.
+      return;
+    }
+
+    let options = {
+      capture: true,
+      mozSystemGroup: true,
+    };
+
+    aWindow.addEventListener("DOMFormHasPassword", event => {
+      this.LoginManagerContent.onDOMFormHasPassword(event, event.target.ownerGlobal.top);
+    }, options);
+
+    aWindow.addEventListener("DOMInputPasswordAdded", event => {
+      this.LoginManagerContent.onDOMInputPasswordAdded(event, event.target.ownerGlobal.top);
+    }, options);
+
+    aWindow.addEventListener("DOMAutoComplete", event => {
+      this.LoginManagerContent.onUsernameInput(event);
+    }, options);
+
+    aWindow.addEventListener("blur", event => {
+      if (event.target instanceof Ci.nsIDOMHTMLInputElement) {
+        this.LoginManagerContent.onUsernameInput(event);
+      }
+    }, options);
+
+    aWindow.addEventListener("pageshow", event => {
+      if (event.target instanceof Ci.nsIDOMHTMLDocument) {
+        this.LoginManagerContent.onPageShow(event, event.target.defaultView.top);
+      }
+    }, options);
+  },
+
   // QI
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
 
   // XPCOMUtils factory
   classID: Components.ID("{be623d20-d305-11de-8a39-0800200c9a66}")
 };
 
 var components = [ BrowserCLH ];
--- a/mobile/android/modules/DelayedInit.jsm
+++ b/mobile/android/modules/DelayedInit.jsm
@@ -47,16 +47,22 @@ XPCOMUtils.defineLazyServiceGetter(this,
  *   }
  *   InitLater(() => Foo.init());
  *   InitLater(() => Bar.init(), this, "Bar");
  */
 var DelayedInit = {
   schedule: function(fn, object, name, maxWait) {
     return Impl.scheduleInit(fn, object, name, maxWait);
   },
+
+  scheduleList: function(fns, maxWait) {
+    for (let fn of fns) {
+      Impl.scheduleInit(fn, null, null, maxWait);
+    }
+  },
 };
 
 // Maximum duration for each idling period. Pending inits are run until this
 // duration is exceeded; then we wait for next idling period.
 const MAX_IDLE_RUN_MS = 50;
 
 var Impl = {
   pendingInits: [],