diff --git a/browser/actors/LightweightThemeChild.jsm b/browser/actors/LightweightThemeChild.jsm new file mode 100644 index 000000000000..3fd3675664ff --- /dev/null +++ b/browser/actors/LightweightThemeChild.jsm @@ -0,0 +1,72 @@ +/* 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 EXPORTED_SYMBOLS = ["LightweightThemeChild"]; + +ChromeUtils.import("resource://gre/modules/ActorChild.jsm"); +ChromeUtils.import("resource://gre/modules/Services.jsm"); + +/** + * LightweightThemeChild forwards theme data to in-content pages. + */ +class LightweightThemeChild extends ActorChild { + constructor(mm) { + super(mm); + + this.init(); + } + + /** + * Initializes the actor for the current page, sending it any existing + * theme data, and adding shared data change listeners so it can + * notify the page of future updates. + * + * This is called when the actor is constructed, and any time + * ActorManagerChild receives a pageshow event for the page we're + * attached to. + */ + init() { + Services.cpmm.sharedData.addEventListener("change", this); + this.update(this.mm.chromeOuterWindowID, this.content); + } + + /** + * Cleans up any global listeners registered by the actor. + * + * This is called by ActorManagerChild any time it receives a pagehide + * event for the page we're attached to. + */ + cleanup() { + Services.cpmm.sharedData.removeEventListener("change", this); + } + + /** + * Handles "change" events on the child sharedData map, and notifies + * our content page if its theme data was among the changed keys. + */ + handleEvent(event) { + if (event.type === "change") { + if (event.changedKeys.includes(`theme/${this.mm.chromeOuterWindowID}`)) { + this.update(this.mm.chromeOuterWindowID, this.content); + } + } + } + + /** + * Forward the theme data to the page. + * @param {Object} outerWindowID The outerWindowID the parent process window has. + * @param {Object} content The receiving global + */ + update(outerWindowID, content) { + const event = Cu.cloneInto({ + detail: { + data: Services.cpmm.sharedData.get(`theme/${outerWindowID}`) + }, + }, content); + content.dispatchEvent(new content.CustomEvent("LightweightTheme:Set", + event)); + } +} diff --git a/browser/actors/moz.build b/browser/actors/moz.build index 4e241dee27e6..a9a1175915f0 100644 --- a/browser/actors/moz.build +++ b/browser/actors/moz.build @@ -7,6 +7,9 @@ with Files("**"): BUG_COMPONENT = ("Firefox", "General") +with Files("LightweightThemeChild.jsm"): + BUG_COMPONENT = ("WebExtensions", "Themes") + with Files("LightWeightThemeInstallChild.jsm"): BUG_COMPONENT = ("Firefox", "Theme") @@ -30,6 +33,7 @@ FINAL_TARGET_FILES.actors += [ 'ContentSearchChild.jsm', 'ContextMenuChild.jsm', 'DOMFullscreenChild.jsm', + 'LightweightThemeChild.jsm', 'LightWeightThemeInstallChild.jsm', 'LinkHandlerChild.jsm', 'NetErrorChild.jsm', diff --git a/browser/base/content/tab-content.js b/browser/base/content/tab-content.js index 7fdb8dbbb72e..9f67d9689462 100644 --- a/browser/base/content/tab-content.js +++ b/browser/base/content/tab-content.js @@ -22,22 +22,6 @@ ActorManagerChild.attach(this, "browsers"); // TabChildGlobal var global = this; -XPCOMUtils.defineLazyProxy(this, "LightweightThemeChildHelper", - "resource:///modules/LightweightThemeChildHelper.jsm"); - -let themeablePagesWhitelist = new Set([ - "about:home", - "about:newtab", - "about:welcome", -]); - -addEventListener("pageshow", function({ originalTarget }) { - if (originalTarget.defaultView == content && themeablePagesWhitelist.has(content.document.documentURI)) { - LightweightThemeChildHelper.listen(themeablePagesWhitelist); - LightweightThemeChildHelper.update(chromeOuterWindowID, content); - } -}, false, true); - // Keep a reference to the translation content handler to avoid it it being GC'ed. var trHandler = null; if (Services.prefs.getBoolPref("browser.translation.detectLanguage")) { diff --git a/browser/components/nsBrowserGlue.js b/browser/components/nsBrowserGlue.js index 557096ea8783..9b8b914b6cb7 100644 --- a/browser/components/nsBrowserGlue.js +++ b/browser/components/nsBrowserGlue.js @@ -131,6 +131,16 @@ let ACTORS = { }, }, + LightweightTheme: { + child: { + module: "resource:///actors/LightweightThemeChild.jsm", + matches: ["about:home", "about:newtab", "about:welcome"], + events: { + "pageshow": {mozSystemGroup: true}, + }, + }, + }, + LinkHandler: { child: { module: "resource:///actors/LinkHandlerChild.jsm", diff --git a/browser/modules/LightweightThemeChildHelper.jsm b/browser/modules/LightweightThemeChildHelper.jsm deleted file mode 100644 index 35ca2eeb3880..000000000000 --- a/browser/modules/LightweightThemeChildHelper.jsm +++ /dev/null @@ -1,68 +0,0 @@ -/* 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"; - -ChromeUtils.import("resource://gre/modules/Services.jsm"); - -var EXPORTED_SYMBOLS = ["LightweightThemeChildHelper"]; - -/** - * LightweightThemeChildHelper forwards theme data to in-content pages. - */ -var LightweightThemeChildHelper = { - listener: null, - whitelist: null, - - /** - * Listen to theme updates for the current process - * @param {Array} whitelist The pages that can receive theme updates. - */ - listen(whitelist) { - if (!this.listener) { - // Clone the whitelist to avoid leaking the global the whitelist - // originates from. - this.whitelist = new Set([...whitelist]); - this.listener = ({ changedKeys }) => { - if (changedKeys.find(change => change.startsWith("theme/"))) { - this._updateProcess(changedKeys); - } - }; - Services.cpmm.sharedData.addEventListener("change", this.listener); - } - }, - - /** - * Update the theme data for the whole process - * @param {Array} changedKeys The sharedData keys that were changed. - */ - _updateProcess(changedKeys) { - const windowEnumerator = Services.ww.getWindowEnumerator(); - while (windowEnumerator.hasMoreElements()) { - const { - chromeOuterWindowID, - content, - } = windowEnumerator.getNext().docShell.messageManager; - if (changedKeys.includes(`theme/${chromeOuterWindowID}`) && - content && this.whitelist.has(content.document.documentURI)) { - this.update(chromeOuterWindowID, content); - } - } - }, - - /** - * Forward the theme data to the page. - * @param {Object} outerWindowID The outerWindowID the parent process window has. - * @param {Object} content The receiving global - */ - update(outerWindowID, content) { - const event = Cu.cloneInto({ - detail: { - data: Services.cpmm.sharedData.get(`theme/${outerWindowID}`) - }, - }, content); - content.dispatchEvent(new content.CustomEvent("LightweightTheme:Set", - event)); - }, -}; diff --git a/browser/modules/moz.build b/browser/modules/moz.build index 40f1ef7900df..72c3dc6fc938 100644 --- a/browser/modules/moz.build +++ b/browser/modules/moz.build @@ -61,9 +61,6 @@ with Files("ExtensionsUI.jsm"): with Files("LaterRun.jsm"): BUG_COMPONENT = ("Firefox", "Tours") -with Files("LightweightThemeChildHelper.jsm"): - BUG_COMPONENT = ("WebExtensions", "Themes") - with Files("OpenInTabsUtils.jsm"): BUG_COMPONENT = ("Firefox", "Tabbed Browser") @@ -134,7 +131,6 @@ EXTRA_JS_MODULES += [ 'FormValidationHandler.jsm', 'HomePage.jsm', 'LaterRun.jsm', - 'LightweightThemeChildHelper.jsm', 'OpenInTabsUtils.jsm', 'PageActions.jsm', 'PermissionUI.jsm', diff --git a/toolkit/modules/ActorManagerChild.jsm b/toolkit/modules/ActorManagerChild.jsm index e424d4ff9f26..4c8596a21d32 100644 --- a/toolkit/modules/ActorManagerChild.jsm +++ b/toolkit/modules/ActorManagerChild.jsm @@ -116,13 +116,27 @@ class SingletonDispatcher extends Dispatcher { constructor(window, data) { super(getMessageManager(window), data); - window.addEventListener("pageshow", this); - window.addEventListener("pagehide", this); + window.addEventListener("pageshow", this, {mozSystemGroup: true}); + window.addEventListener("pagehide", this, {mozSystemGroup: true}); this.window = window; this.listeners = []; } + init() { + super.init(); + + for (let actor of this.instances.values()) { + if (typeof actor.init === "function") { + try { + actor.init(); + } catch (e) { + Cu.reportError(e); + } + } + } + } + cleanup() { super.cleanup(); @@ -132,6 +146,17 @@ class SingletonDispatcher extends Dispatcher { for (let [event, listener, options] of this.listeners) { this.window.removeEventListener(event, listener, options); } + + for (let actor of this.instances.values()) { + if (typeof actor.cleanup === "function") { + try { + actor.cleanup(); + } catch (e) { + Cu.reportError(e); + } + } + } + this.listeners = null; }