Bug 1369466 - Load RemotePageManager.jsm lazily
MozReview-Commit-ID: 3P5UQHIQZhr
--- a/browser/base/content/test/performance/browser_startup.js
+++ b/browser/base/content/test/performance/browser_startup.js
@@ -34,19 +34,16 @@ const startupPhases = {
"WebContentConverter.js", // bug 1369443
"nsSessionStartup.js", // bug 1369456
"PushComponents.js", // bug 1369436
]),
modules: new Set([
"resource://gre/modules/AppConstants.jsm",
"resource://gre/modules/XPCOMUtils.jsm",
"resource://gre/modules/Services.jsm",
-
- // Bugs to fix: Probably loaded too early, needs investigation.
- "resource://gre/modules/RemotePageManager.jsm", // bug 1369466
])
}},
// For the following phases of startup we have only a black list for now
// We are at this phase after creating the first browser window (ie. after final-ui-startup).
"before opening first browser window": {blacklist: {
modules: new Set([
@@ -71,16 +68,17 @@ const startupPhases = {
"resource:///modules/BrowserUITelemetry.jsm",
"resource:///modules/BrowserUsageTelemetry.jsm",
"resource:///modules/ContentCrashHandlers.jsm",
"resource:///modules/DirectoryLinksProvider.jsm",
"resource://gre/modules/NewTabUtils.jsm",
"resource://gre/modules/PageThumbs.jsm",
"resource://gre/modules/Promise.jsm", // imported by devtools during _delayedStartup
"resource://gre/modules/Preferences.jsm",
+ "resource://gre/modules/RemotePageManager.jsm",
]),
services: new Set([
"@mozilla.org/browser/search-service;1",
])
}},
// We are at this phase once we are ready to handle user events.
// Anything loaded at this phase or before gets in the way of the user
--- a/toolkit/content/process-content.js
+++ b/toolkit/content/process-content.js
@@ -1,23 +1,73 @@
/* 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/. */
"use strict";
var { classes: Cc, interfaces: Ci, utils: Cu } = Components;
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+const gInContentProcess = Services.appinfo.processType == Ci.nsIXULRuntime.PROCESS_TYPE_CONTENT;
+
// Creates a new PageListener for this process. This will listen for page loads
// and for those that match URLs provided by the parent process will set up
// a dedicated message port and notify the parent process.
-Cu.import("resource://gre/modules/RemotePageManager.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "RemotePageManager",
+ "resource://gre/modules/RemotePageManager.jsm");
+// Once the chrome process has loaded the RemotePageManager, trigger the load of
+// the RemotePageManager in this process immediately.
+if (gInContentProcess) {
+ if (Services.cpmm.initialProcessData["RemotePageManager:initialized"]) {
+ RemotePageManager;
+ } else {
+ Services.cpmm.addMessageListener("RemotePage:Init", function RPMInitListener() {
+ Services.cpmm.removeMessageListener("RemotePage:Init", RPMInitListener);
+ RemotePageManager;
+ });
+ }
+}
+if (!Services.cpmm.initialProcessData["RemotePageManager:initialized"]) {
+ // If the remote page manager hasn't been loaded already, wait to load it
+ // until we see a page load that it needs to observe.
+ function loadRemotePageManager(subject, topic, data) {
+ if (Services.cpmm.initialProcessData["RemotePageManager:initialized"]) {
+ // If the remote page manager is already initialized, it will already be
+ // observing this event. This additional observer is not needed.
+ Services.obs.removeObserver(loadRemotePageManager,
+ "chrome-document-global-created");
+ Services.obs.removeObserver(loadRemotePageManager,
+ "content-document-global-created");
+ return;
+ }
-const gInContentProcess = Services.appinfo.processType == Ci.nsIXULRuntime.PROCESS_TYPE_CONTENT;
+ let url = subject.document.documentURI;
+ let watchUrls = Services.ppmm.initialProcessData["RemotePageManager:urls"];
+ watchUrls = watchUrls ? watchUrls : [];
+ if (watchUrls.includes(url)) {
+ // The remote page manager needs to observe the load of this page.
+ // Forward this event to its observer. This will also result in the
+ // RemotePageManager module being loaded.
+ RemotePageManager.observe(subject, topic, data);
+ // Now that the RemotePageManager module has been loaded, it will connect
+ // its own event listeners, so this one is no longer needed.
+ Services.obs.removeObserver(loadRemotePageManager,
+ "chrome-document-global-created");
+ Services.obs.removeObserver(loadRemotePageManager,
+ "content-document-global-created");
+ }
+ }
+ Services.obs.addObserver(loadRemotePageManager,
+ "chrome-document-global-created");
+ Services.obs.addObserver(loadRemotePageManager,
+ "content-document-global-created");
+}
Services.cpmm.addMessageListener("gmp-plugin-crash", msg => {
let gmpservice = Cc["@mozilla.org/gecko-media-plugin-service;1"]
.getService(Ci.mozIGeckoMediaPluginService);
gmpservice.RunPluginCrashCallbacks(msg.data.pluginID, msg.data.pluginName);
});
--- a/toolkit/modules/RemotePageManager.jsm
+++ b/toolkit/modules/RemotePageManager.jsm
@@ -529,46 +529,58 @@ var RemotePageManagerInternal = {
let port = new ChromeMessagePort(browser, portID, url);
callback(port.publicPort);
}
};
if (Services.appinfo.processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT)
RemotePageManagerInternal.init();
-// The public API for the above object
-this.RemotePageManager = {
- addRemotePageListener: RemotePageManagerInternal.addRemotePageListener.bind(RemotePageManagerInternal),
- removeRemotePageListener: RemotePageManagerInternal.removeRemotePageListener.bind(RemotePageManagerInternal),
-};
-
// Listen for pages in any process we're loaded in
var registeredURLs = new Set(Services.cpmm.initialProcessData["RemotePageManager:urls"]);
-var observer = (window) => {
- // Strip the hash from the URL, because it's not part of the origin.
- let url = window.document.documentURI.replace(/[\#|\?].*$/, "");
- if (!registeredURLs.has(url))
- return;
+// The public API for RemotePageManagerInternal
+this.RemotePageManager = {
+ addRemotePageListener: RemotePageManagerInternal.addRemotePageListener.bind(RemotePageManagerInternal),
+ removeRemotePageListener: RemotePageManagerInternal.removeRemotePageListener.bind(RemotePageManagerInternal),
+
+ // This observer is meant to observe these two events:
+ // "chrome-document-global-created"
+ // "content-document-global-created"
+ // These events are connected to this observer by process-content.js
+ observe(window) {
+ // Strip the hash from the URL, because it's not part of the origin.
+ let url = window.document.documentURI.replace(/[\#|\?].*$/, "");
+ if (!registeredURLs.has(url))
+ return;
- // Get the frame message manager for this window so we can associate this
- // page with a browser element
- let messageManager = window.QueryInterface(Ci.nsIInterfaceRequestor)
- .getInterface(Ci.nsIDocShell)
- .QueryInterface(Ci.nsIInterfaceRequestor)
- .getInterface(Ci.nsIContentFrameMessageManager);
- // Set up the child side of the message port
- new ChildMessagePort(messageManager, window);
+ // Get the frame message manager for this window so we can associate this
+ // page with a browser element
+ let messageManager = window.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIDocShell)
+ .QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIContentFrameMessageManager);
+ // Set up the child side of the message port
+ new ChildMessagePort(messageManager, window);
+ },
};
-Services.obs.addObserver(observer, "chrome-document-global-created");
-Services.obs.addObserver(observer, "content-document-global-created");
+Services.obs.addObserver(this.RemotePageManager.observe,
+ "chrome-document-global-created");
+Services.obs.addObserver(this.RemotePageManager.observe,
+ "content-document-global-created");
// A message from chrome telling us what pages to listen for
Services.cpmm.addMessageListener("RemotePage:Register", ({ data }) => {
for (let url of data.urls)
registeredURLs.add(url);
});
// A message from chrome telling us what pages to stop listening for
Services.cpmm.addMessageListener("RemotePage:Unregister", ({ data }) => {
for (let url of data.urls)
registeredURLs.delete(url);
});
+
+if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_DEFAULT) {
+ // Let the content processes know that the chrome process has initialized.
+ Services.ppmm.broadcastAsyncMessage("RemotePage:Init");
+}
+Services.ppmm.initialProcessData["RemotePageManager:initialized"] = true;
--- a/toolkit/mozapps/extensions/AddonManager.jsm
+++ b/toolkit/mozapps/extensions/AddonManager.jsm
@@ -898,35 +898,59 @@ var AddonManagerInternal = {
}
// If this is a new profile just pretend that there were no changes
if (appChanged === undefined) {
for (let type in this.startupChanges)
delete this.startupChanges[type];
}
- // Support for remote about:plugins. Note that this module isn't loaded
- // at the top because Services.appinfo is defined late in tests.
- let { RemotePages } = Cu.import("resource://gre/modules/RemotePageManager.jsm", {});
-
- gPluginPageListener = new RemotePages("about:plugins");
- gPluginPageListener.addMessageListener("RequestPlugins", this.requestPlugins);
+ // Don't initialize the remote page listener here. Let it initialize
+ // itself, then call initRemotePageListener to interface with it.
+ if (Services.cpmm.initialProcessData["RemotePageManager:initialized"]) {
+ this.initRemotePageListener();
+ } else {
+ // Add about:plugins to the RemotePageManager:urls list. This ensures
+ // that the remote page listener will be initialized when the
+ // about:plugins page is opened (if not before).
+ if (Services.ppmm.initialProcessData["RemotePageManager:urls"]) {
+ Services.ppmm.initialProcessData["RemotePageManager:urls"].push("about:plugins");
+ } else {
+ Services.ppmm.initialProcessData["RemotePageManager:urls"] = ["about:plugins"];
+ }
+ let initRemotePageListener = this.initRemotePageListener;
+ Services.cpmm.addMessageListener("RemotePage:Init", function RPMInitListener() {
+ Services.cpmm.removeMessageListener("RemotePage:Init", RPMInitListener);
+ initRemotePageListener();
+ });
+ }
gStartupComplete = true;
this.recordTimestamp("AMI_startup_end");
} catch (e) {
logger.error("startup failed", e);
AddonManagerPrivate.recordException("AMI", "startup failed", e);
}
logger.debug("Completed startup sequence");
this.callManagerListeners("onStartup");
},
/**
+ * Initializes gPluginPageListener, which provides support for remote
+ * about:plugins. Initialization needs to be done sometime after first
+ * paint to prevent RemotePageManager.jsm from being loaded too early.
+ */
+ initRemotePageListener() {
+ let { RemotePages } = Cu.import("resource://gre/modules/RemotePageManager.jsm", {});
+ gPluginPageListener = new RemotePages("about:plugins");
+ gPluginPageListener.addMessageListener("RequestPlugins", AddonManagerInternal.requestPlugins);
+ },
+
+ /**
* Registers a new AddonProvider.
*
* @param aProvider
* The provider to register
* @param aTypes
* An optional array of add-on types
*/
registerProvider(aProvider, aTypes) {
@@ -1107,18 +1131,20 @@ var AddonManagerInternal = {
gShutdownInProgress = true;
// Clean up listeners
Services.prefs.removeObserver(PREF_EM_CHECK_COMPATIBILITY, this);
Services.prefs.removeObserver(PREF_EM_STRICT_COMPATIBILITY, this);
Services.prefs.removeObserver(PREF_EM_CHECK_UPDATE_SECURITY, this);
Services.prefs.removeObserver(PREF_EM_UPDATE_ENABLED, this);
Services.prefs.removeObserver(PREF_EM_AUTOUPDATE_DEFAULT, this);
Services.prefs.removeObserver(PREF_EM_HOTFIX_ID, this);
- gPluginPageListener.destroy();
- gPluginPageListener = null;
+ if (gPluginPageListener) {
+ gPluginPageListener.destroy();
+ gPluginPageListener = null;
+ }
let savedError = null;
// Only shut down providers if they've been started.
if (gStarted) {
try {
await gShutdownBarrier.wait();
} catch (err) {
savedError = err;