Bug 1394580 - 2. Add GeckoViewStartup to consolidate startup tasks; r?snorp draft
authorJim Chen <nchen@mozilla.com>
Thu, 31 Aug 2017 18:12:52 -0400
changeset 656940 6baf7028427d42dfbf01ff1d68ee1b6b5b354567
parent 656939 e159f360b3489868dfcb8398af0eb24cc8089461
child 656941 e815ece51b69af0e97a27ec807cef7dfbe042a30
push id77384
push userbmo:nchen@mozilla.com
push dateThu, 31 Aug 2017 22:20:06 +0000
reviewerssnorp
bugs1394580
milestone57.0a1
Bug 1394580 - 2. Add GeckoViewStartup to consolidate startup tasks; r?snorp Add a GeckoViewStartup component to consolidate tasks performed during GeckoView startup, such as adding lazy observers and event listeners. This enables us to not load individual GeckoView modules such as GeckoViewPermission and GeckoViewPrompt until they are actually needed. MozReview-Commit-ID: IsaUGwBHKbs
mobile/android/components/geckoview/GeckoView.manifest
mobile/android/components/geckoview/GeckoViewPermission.js
mobile/android/components/geckoview/GeckoViewPrompt.js
mobile/android/components/geckoview/GeckoViewStartup.js
mobile/android/components/geckoview/moz.build
mobile/android/installer/package-manifest.in
--- a/mobile/android/components/geckoview/GeckoView.manifest
+++ b/mobile/android/components/geckoview/GeckoView.manifest
@@ -1,18 +1,21 @@
 # Stylesheets
 category agent-style-sheets browser-content-stylesheet chrome://geckoview/skin/content.css
 
+# GeckoViewStartup.js
+component {8e993c34-fdd6-432c-967e-f995d888777f} GeckoViewStartup.js
+contract @mozilla.org/geckoview/startup;1 {8e993c34-fdd6-432c-967e-f995d888777f}
+category app-startup GeckoViewStartup service,@mozilla.org/geckoview/startup;1
+category profile-after-change GeckoViewStartup @mozilla.org/geckoview/startup;1 process=main
+
 # GeckoViewPermission.js
 component {42f3c238-e8e8-4015-9ca2-148723a8afcf} GeckoViewPermission.js
 contract @mozilla.org/content-permission/prompt;1 {42f3c238-e8e8-4015-9ca2-148723a8afcf}
-category app-startup GeckoViewPermission service,@mozilla.org/content-permission/prompt;1
 
 # GeckoViewPrompt.js
 component {076ac188-23c1-4390-aa08-7ef1f78ca5d9} GeckoViewPrompt.js
 contract @mozilla.org/embedcomp/prompt-service;1 {076ac188-23c1-4390-aa08-7ef1f78ca5d9}
 contract @mozilla.org/prompter;1 {076ac188-23c1-4390-aa08-7ef1f78ca5d9}
-category app-startup GeckoViewPrompt service,@mozilla.org/prompter;1
-category profile-after-change GeckoViewPrompt @mozilla.org/prompter;1 process=main
 component {aa0dd6fc-73dd-4621-8385-c0b377e02cee} GeckoViewPrompt.js process=main
 contract @mozilla.org/colorpicker;1 {aa0dd6fc-73dd-4621-8385-c0b377e02cee} process=main
 component {e4565e36-f101-4bf5-950b-4be0887785a9} GeckoViewPrompt.js process=main
 contract @mozilla.org/filepicker;1 {e4565e36-f101-4bf5-950b-4be0887785a9} process=main
--- a/mobile/android/components/geckoview/GeckoViewPermission.js
+++ b/mobile/android/components/geckoview/GeckoViewPermission.js
@@ -1,19 +1,20 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * 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/. */
 
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
-Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
-XPCOMUtils.defineLazyModuleGetter(this, "EventDispatcher",
-                                  "resource://gre/modules/Messaging.jsm");
+XPCOMUtils.defineLazyModuleGetters(this, [
+  EventDispatcher: "resource://gre/modules/Messaging.jsm",
+  Services: "resource://gre/modules/Services.jsm",
+]);
 
 // See: http://developer.android.com/reference/android/Manifest.permission.html
 const PERM_ACCESS_FINE_LOCATION = "android.permission.ACCESS_FINE_LOCATION";
 const PERM_CAMERA = "android.permission.CAMERA";
 const PERM_RECORD_AUDIO = "android.permission.RECORD_AUDIO";
 
 function GeckoViewPermission() {
 }
@@ -24,22 +25,16 @@ GeckoViewPermission.prototype = {
   QueryInterface: XPCOMUtils.generateQI([
       Ci.nsIObserver, Ci.nsIContentPermissionPrompt]),
 
   _appPermissions: {},
 
   /* ----------  nsIObserver  ---------- */
   observe: function(aSubject, aTopic, aData) {
     switch (aTopic) {
-      case "app-startup": {
-        Services.obs.addObserver(this, "getUserMedia:ask-device-permission");
-        Services.obs.addObserver(this, "getUserMedia:request");
-        Services.obs.addObserver(this, "PeerConnection:request");
-        break;
-      }
       case "getUserMedia:ask-device-permission": {
         this.handleMediaAskDevicePermission(aData, aSubject);
         break;
       }
       case "getUserMedia:request": {
         this.handleMediaRequest(aSubject);
         break;
       }
--- a/mobile/android/components/geckoview/GeckoViewPrompt.js
+++ b/mobile/android/components/geckoview/GeckoViewPrompt.js
@@ -1,68 +1,34 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * 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/. */
 
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
-Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
-XPCOMUtils.defineLazyModuleGetter(this, "ContentPrefServiceParent",
-                                  "resource://gre/modules/ContentPrefServiceParent.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "EventDispatcher",
-                                  "resource://gre/modules/Messaging.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
-                                  "resource://gre/modules/FileUtils.jsm");
+XPCOMUtils.defineLazyModuleGetters(this, [
+  EventDispatcher: "resource://gre/modules/Messaging.jsm",
+  FileUtils: "resource://gre/modules/FileUtils.jsm",
+  Services: "resource://gre/modules/Services.jsm",
+]);
 
 XPCOMUtils.defineLazyServiceGetter(this, "UUIDGen",
                                    "@mozilla.org/uuid-generator;1", "nsIUUIDGenerator");
 
 function PromptFactory() {
 }
 
 PromptFactory.prototype = {
   classID: Components.ID("{076ac188-23c1-4390-aa08-7ef1f78ca5d9}"),
 
   QueryInterface: XPCOMUtils.generateQI([
-    Ci.nsIObserver, Ci.nsIPromptFactory, Ci.nsIPromptService, Ci.nsIPromptService2]),
-
-  /* ----------  nsIObserver  ---------- */
-  observe: function(aSubject, aTopic, aData) {
-    switch (aTopic) {
-      case "app-startup": {
-        Services.obs.addObserver(this, "chrome-document-global-created");
-        Services.obs.addObserver(this, "content-document-global-created");
-        break;
-      }
-      case "profile-after-change": {
-        // ContentPrefServiceParent is needed for e10s file picker.
-        ContentPrefServiceParent.init();
-        Services.mm.addMessageListener("GeckoView:Prompt", this);
-        break;
-      }
-      case "chrome-document-global-created":
-      case "content-document-global-created": {
-        let win = aSubject.QueryInterface(Ci.nsIInterfaceRequestor)
-                          .getInterface(Ci.nsIDocShell).QueryInterface(Ci.nsIDocShellTreeItem)
-                          .rootTreeItem.QueryInterface(Ci.nsIInterfaceRequestor)
-                          .getInterface(Ci.nsIDOMWindow);
-        if (win !== aSubject) {
-          // Only attach to top-level windows.
-          return;
-        }
-        win.addEventListener("click", this); // non-capture
-        win.addEventListener("contextmenu", this); // non-capture
-        break;
-      }
-    }
-  },
+    Ci.nsIDOMEventListener, Ci.nsIMessageListener,
+    Ci.nsIPromptFactory, Ci.nsIPromptService, Ci.nsIPromptService2]),
 
   handleEvent: function(aEvent) {
     switch (aEvent.type) {
       case "click":
         this._handleClick(aEvent);
         break;
       case "contextmenu":
         this._handleContextMenu(aEvent);
new file mode 100644
--- /dev/null
+++ b/mobile/android/components/geckoview/GeckoViewStartup.js
@@ -0,0 +1,167 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "Services",
+                                  "resource://gre/modules/Services.jsm");
+
+function GeckoViewStartup() {
+}
+
+GeckoViewStartup.prototype = {
+  classID: Components.ID("{8e993c34-fdd6-432c-967e-f995d888777f}"),
+
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
+
+  getInterface: function(aObject, aInterface) {
+    if (!aObject.QueryInterface) {
+      return aObject;
+    }
+    return aObject.QueryInterface(aInterface);
+  },
+
+  addLazyGetter: function(aOptions) {
+    let {name, script, service, module, observers, ppmm, mm} = aOptions;
+    if (script) {
+      XPCOMUtils.defineLazyScriptGetter(this, name, script);
+    } else if (module) {
+      XPCOMUtils.defineLazyGetter(this, name, _ => {
+        let sandbox = {};
+        Cu.import(module, sandbox);
+        if (aOptions.init) {
+          aOptions.init.call(this, sandbox[name]);
+        }
+        return sandbox[name];
+      });
+    } else if (service) {
+      XPCOMUtils.defineLazyServiceGetter(this, name, service, aOptions.interface);
+    }
+
+    if (observers) {
+      let observer = (subject, topic, data) => {
+        let obj = this.getInterface(this[name], Ci.nsIObserver);
+        Services.obs.removeObserver(observer, topic);
+        if (!aOptions.once) {
+          Services.obs.addObserver(obj, topic);
+        }
+        obj.observe(subject, topic, data); // Explicitly notify new observer
+      };
+      observers.forEach(topic => Services.obs.addObserver(observer, topic));
+    }
+
+    if (ppmm || mm) {
+      let target = ppmm ? Services.ppmm : Services.mm;
+      let listener = msg => {
+        let obj = this.getInterface(this[name], Ci.nsIMessageListener);
+        target.removeMessageListener(msg.name, listener);
+        if (!aOptions.once) {
+          target.addMessageListener(msg.name, obj);
+        }
+        obj.receiveMessage(msg);
+      };
+      (ppmm || mm).forEach(msg => target.addMessageListener(msg, listener));
+    }
+  },
+
+  addLazyEventListener: function(aOptions) {
+    let {name, target, events, options} = aOptions;
+    let listener = event => {
+      let obj = this.getInterface(this[name], Ci.nsIDOMEventListener);
+      if (!options || !options.once) {
+        target.removeEventListener(event.type, listener, options);
+        target.addEventListener(event.type, obj, options);
+      }
+      obj.handleEvent(event);
+    };
+    events.forEach(event => target.addEventListener(event, listener));
+  },
+
+  /* ----------  nsIObserver  ---------- */
+  observe: function(aSubject, aTopic, aData) {
+    switch (aTopic) {
+      case "app-startup": {
+        // Parent and content process.
+        Services.obs.addObserver(this, "chrome-document-global-created");
+        Services.obs.addObserver(this, "content-document-global-created");
+
+        this.addLazyGetter({
+          name: "GeckoViewPermission",
+          service: "@mozilla.org/content-permission/prompt;1",
+          interface: "nsIObserver",
+          observers: [
+            "getUserMedia:ask-device-permission",
+            "getUserMedia:request",
+            "PeerConnection:request",
+          ],
+        });
+
+        if (Services.appinfo.processType != Services.appinfo.PROCESS_TYPE_DEFAULT) {
+          // Content process only.
+          this.addLazyGetter({
+            name: "GeckoViewPrompt",
+            service: "@mozilla.org/prompter;1",
+            interface: "nsIDOMEventListener",
+          });
+        }
+        break;
+      }
+
+      case "profile-after-change": {
+        // Parent process only.
+        // ContentPrefServiceParent is needed for e10s file picker.
+        this.addLazyGetter({
+          name: "ContentPrefServiceParent",
+          module: "resource://gre/modules/ContentPrefServiceParent.jsm",
+          init: cpsp => cpsp.alwaysInit(),
+          ppmm: [
+            "ContentPrefs:FunctionCall",
+            "ContentPrefs:AddObserverForName",
+            "ContentPrefs:RemoveObserverForName",
+          ],
+        });
+
+        this.addLazyGetter({
+          name: "GeckoViewPrompt",
+          service: "@mozilla.org/prompter;1",
+          interface: "nsIDOMEventListener",
+          mm: [
+            "GeckoView:Prompt",
+          ],
+        });
+        break;
+      }
+
+      case "chrome-document-global-created":
+      case "content-document-global-created": {
+        let win = aSubject.QueryInterface(Ci.nsIInterfaceRequestor)
+                          .getInterface(Ci.nsIDocShell).QueryInterface(Ci.nsIDocShellTreeItem)
+                          .rootTreeItem.QueryInterface(Ci.nsIInterfaceRequestor)
+                          .getInterface(Ci.nsIDOMWindow);
+        if (win !== aSubject) {
+          // Only attach to top-level windows.
+          return;
+        }
+
+        this.addLazyEventListener({
+          name: "GeckoViewPrompt",
+          target: win,
+          events: [
+            "click",
+            "contextmenu",
+          ],
+          options: {
+            capture: false,
+            mozSystemGroup: true,
+          },
+        });
+        break;
+      }
+    }
+  },
+};
+
+this.NSGetFactory = XPCOMUtils.generateNSGetFactory([GeckoViewStartup]);
--- a/mobile/android/components/geckoview/moz.build
+++ b/mobile/android/components/geckoview/moz.build
@@ -3,9 +3,10 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # 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/.
 
 EXTRA_COMPONENTS += [
     'GeckoView.manifest',
     'GeckoViewPermission.js',
     'GeckoViewPrompt.js',
+    'GeckoViewStartup.js',
 ]
--- a/mobile/android/installer/package-manifest.in
+++ b/mobile/android/installer/package-manifest.in
@@ -521,16 +521,17 @@
 [mobile]
 @BINPATH@/chrome/geckoview@JAREXT@
 @BINPATH@/chrome/geckoview.manifest
 
 #ifdef MOZ_GECKOVIEW_JAR
 @BINPATH@/components/GeckoView.manifest
 @BINPATH@/components/GeckoViewPrompt.js
 @BINPATH@/components/GeckoViewPermission.js
+@BINPATH@/components/GeckoViewStartup.js
 #else
 @BINPATH@/chrome/chrome@JAREXT@
 @BINPATH@/chrome/chrome.manifest
 @BINPATH@/components/AboutRedirector.js
 @BINPATH@/components/AddonUpdateService.js
 @BINPATH@/components/BlocklistPrompt.js
 @BINPATH@/components/BrowserCLH.js
 @BINPATH@/components/ColorPicker.js