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
--- 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