Bug 1394580 - 2. Add GeckoViewStartup to consolidate startup tasks; r=snorp
☠☠ backed out by e2953d11ef63 ☠ ☠
authorJim Chen <nchen@mozilla.com>
Tue, 05 Sep 2017 15:27:35 -0400
changeset 428506 6c4c8b4e38cfc198f9caa1b47e91236f15ce3af2
parent 428505 b84fc40ed8c24e165204f288e8fde19a66939dce
child 428507 995d9ef9af5f6abc56c995e0b27c31033e2384f9
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