Bug 1367077 - 3. Move form fill event listeners out of browser.js; r=sebastian draft
authorJim Chen <nchen@mozilla.com>
Fri, 15 Sep 2017 14:44:49 -0400
changeset 665601 df4e84d73759278d796e3fdb54368d0c2f1c33a2
parent 665600 1521490e5d2c13650a951251666fe00f038b8c14
child 665602 b3f9bb6f01a4b2f5fc29ba9f61d04fcb281cc891
push id80116
push userbmo:nchen@mozilla.com
push dateFri, 15 Sep 2017 18:46:16 +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: [],