Bug 1394580 - 2. Add GeckoViewStartup to consolidate startup tasks; r=snorp
authorJim Chen <nchen@mozilla.com>
Tue, 05 Sep 2017 21:26:30 -0400
changeset 428576 0fd2ec57de053d48dcc5d98f3ce2c0c3b536a01e
parent 428575 71322aab413bf464bfdbe5ce9eed7c5db4f26297
child 428577 aeb4ccaf74b3697f43c288ceb3a594c925bbf9ae
push id7761
push userjlund@mozilla.com
push dateFri, 15 Sep 2017 00:19:52 +0000
treeherdermozilla-beta@c38455951db4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssnorp
bugs1394580
milestone57.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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,45 +1,41 @@
 /* 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() {
+  this.wrappedJSObject = this;
 }
 
 GeckoViewPermission.prototype = {
   classID: Components.ID("{42f3c238-e8e8-4015-9ca2-148723a8afcf}"),
 
   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() {
+  this.wrappedJSObject = this;
 }
 
 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.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,155 @@
+/* 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]),
+
+  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.defineLazyGetter(this, name, _ =>
+        Cc[service].getService(Ci.nsISupports).wrappedJSObject);
+    }
+
+    if (observers) {
+      let observer = (subject, topic, data) => {
+        Services.obs.removeObserver(observer, topic);
+        if (!aOptions.once) {
+          Services.obs.addObserver(this[name], topic);
+        }
+        this[name].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 => {
+        target.removeMessageListener(msg.name, listener);
+        if (!aOptions.once) {
+          target.addMessageListener(msg.name, this[name]);
+        }
+        this[name].receiveMessage(msg);
+      };
+      (ppmm || mm).forEach(msg => target.addMessageListener(msg, listener));
+    }
+  },
+
+  addLazyEventListener: function(aOptions) {
+    let {name, target, events, options} = aOptions;
+    let listener = event => {
+      if (!options || !options.once) {
+        target.removeEventListener(event.type, listener, options);
+        target.addEventListener(event.type, this[name], options);
+      }
+      this[name].handleEvent(event);
+    };
+    events.forEach(event => target.addEventListener(event, listener, options));
+  },
+
+  /* ----------  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",
+          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",
+          });
+        }
+        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",
+          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