--- a/browser/base/content/contentTheme.js
+++ b/browser/base/content/contentTheme.js
@@ -41,27 +41,24 @@ const inContentVariableMap = [
*/
const ContentThemeController = {
/**
* Tell the frame script that the page supports theming, and watch for updates
* from the frame script.
*/
init() {
addEventListener("LightweightTheme:Set", this);
-
- const event = new CustomEvent("LightweightTheme:Support", {bubbles: true});
- document.dispatchEvent(event);
},
/**
* Handle theme updates from the frame script.
* @param {Object} event object containing the theme update.
*/
- handleEvent({ detail }) {
- if (detail.type == "LightweightTheme:Update") {
+ handleEvent({ type, detail }) {
+ if (type == "LightweightTheme:Set") {
let {data} = detail;
if (!data) {
data = {};
}
this._setProperties(document.body, data);
}
},
--- a/browser/base/content/tab-content.js
+++ b/browser/base/content/tab-content.js
@@ -19,18 +19,16 @@ ChromeUtils.defineModuleGetter(this, "Ut
ChromeUtils.defineModuleGetter(this, "PrivateBrowsingUtils",
"resource://gre/modules/PrivateBrowsingUtils.jsm");
ChromeUtils.defineModuleGetter(this, "AboutReader",
"resource://gre/modules/AboutReader.jsm");
ChromeUtils.defineModuleGetter(this, "ReaderMode",
"resource://gre/modules/ReaderMode.jsm");
ChromeUtils.defineModuleGetter(this, "PageStyleHandler",
"resource:///modules/PageStyleHandler.jsm");
-ChromeUtils.defineModuleGetter(this, "LightweightThemeChildListener",
- "resource:///modules/LightweightThemeChildListener.jsm");
// TabChildGlobal
var global = this;
addEventListener("MozDOMPointerLock:Entered", function(aEvent) {
sendAsyncMessage("PointerLock:Entered", {
originNoSuffix: aEvent.target.nodePrincipal.originNoSuffix
@@ -84,43 +82,31 @@ addMessageListener("Browser:Reload", fun
} catch (e) {
}
});
addMessageListener("MixedContent:ReenableProtection", function() {
docShell.mixedContentChannel = null;
});
-var LightweightThemeChildListenerStub = {
- _childListener: null,
- get childListener() {
- if (!this._childListener) {
- this._childListener = new LightweightThemeChildListener();
- }
- return this._childListener;
- },
+XPCOMUtils.defineLazyProxy(this, "LightweightThemeChildHelper",
+ "resource:///modules/LightweightThemeChildHelper.jsm");
- init() {
- addEventListener("LightweightTheme:Support", this, false, true);
- addMessageListener("LightweightTheme:Update", this);
- sendAsyncMessage("LightweightTheme:Request");
- this.init = null;
- },
+let themeablePagesWhitelist = new Set([
+ "about:home",
+ "about:newtab",
+ "about:welcome",
+]);
- handleEvent(event) {
- return this.childListener.handleEvent(event);
- },
-
- receiveMessage(msg) {
- return this.childListener.receiveMessage(msg);
- },
-};
-
-LightweightThemeChildListenerStub.init();
-
+addEventListener("pageshow", function({ originalTarget }) {
+ if (originalTarget.defaultView == content && themeablePagesWhitelist.has(content.document.documentURI)) {
+ LightweightThemeChildHelper.listen(themeablePagesWhitelist);
+ LightweightThemeChildHelper.update(chromeOuterWindowID, content);
+ }
+}, false, true);
var AboutReaderListener = {
_articlePromise: null,
_isLeavingReaderableReaderMode: false,
init() {
--- a/browser/base/content/test/performance/browser_startup_content.js
+++ b/browser/base/content/test/performance/browser_startup_content.js
@@ -51,17 +51,16 @@ const whitelist = {
// Forms and passwords
"resource://formautofill/FormAutofill.jsm",
"resource://formautofill/FormAutofillContent.jsm",
// Browser front-end
"resource:///modules/ContentLinkHandler.jsm",
"resource:///modules/ContentMetaHandler.jsm",
"resource:///modules/PageStyleHandler.jsm",
- "resource:///modules/LightweightThemeChildListener.jsm",
"resource://gre/modules/BrowserUtils.jsm",
"resource://gre/modules/E10SUtils.jsm",
"resource://gre/modules/PrivateBrowsingUtils.jsm",
"resource://gre/modules/ReaderMode.jsm",
// Pocket
"chrome://pocket/content/AboutPocket.jsm",
rename from browser/modules/LightweightThemeChildListener.jsm
rename to browser/modules/LightweightThemeChildHelper.jsm
--- a/browser/modules/LightweightThemeChildListener.jsm
+++ b/browser/modules/LightweightThemeChildHelper.jsm
@@ -1,82 +1,71 @@
/* 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"];
+
/**
- * LightweightThemeChildListener forwards theme updates from LightweightThemeConsumer to
- * the whitelisted in-content pages
+ * LightweightThemeChildHelper forwards theme data to in-content pages.
*/
-class LightweightThemeChildListener {
- constructor() {
- /**
- * The pages that will receive theme updates
- */
- this.whitelist = new Set([
- "about:home",
- "about:newtab",
- "about:welcome",
- ]);
-
- /**
- * The last theme data received from LightweightThemeConsumer
- */
- this._lastData = null;
- }
+var LightweightThemeChildHelper = {
+ listener: null,
+ whitelist: [],
/**
- * Handles theme updates from the parent process
- * @param {Object} message from the parent process.
+ * Listen to theme updates for the current process
+ * @param {Array} whitelist The pages that can receive theme updates.
*/
- receiveMessage({ name, data, target }) {
- if (name == "LightweightTheme:Update") {
- this._lastData = data;
- this._update(data, target.content);
+ 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);
}
- }
+ },
/**
- * Handles events from the content scope.
- * @param {Object} event The received event.
+ * Update the theme data for the whole process
+ * @param {Array} changedKeys The sharedData keys that were changed.
*/
- handleEvent(event) {
- const content = event.originalTarget.defaultView;
- if (content != content.top) {
- return;
+ _updateProcess(changedKeys) {
+ const windowEnumerator = Services.ww.getWindowEnumerator();
+ while (windowEnumerator.hasMoreElements()) {
+ const window = windowEnumerator.getNext().QueryInterface(Ci.nsIDOMWindow);
+ const tabChildGlobal = window.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIDocShell)
+ .sameTypeRootTreeItem
+ .QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIContentFrameMessageManager);
+ const {chromeOuterWindowID, content} = tabChildGlobal;
+ if (changedKeys.includes(`theme/${chromeOuterWindowID}`) &&
+ content && this.whitelist.has(content.document.documentURI)) {
+ this.update(chromeOuterWindowID, content);
+ }
}
-
- if (event.type == "LightweightTheme:Support") {
- this._update(this._lastData, content);
- }
- }
-
- /**
- * Checks if a given global is allowed to receive theme updates
- * @param {Object} content The global to check against.
- * @returns {boolean} Whether the global is allowed to receive updates.
- */
- _isContentWhitelisted(content) {
- return this.whitelist.has(content.document.documentURI);
- }
+ },
/**
* Forward the theme data to the page.
- * @param {Object} data The theme data to forward
+ * @param {Object} outerWindowID The outerWindowID the parent process window has.
* @param {Object} content The receiving global
*/
- _update(data, content) {
- if (this._isContentWhitelisted(content)) {
- const event = Cu.cloneInto({
- detail: {
- type: "LightweightTheme:Update",
- data,
- },
- }, content);
- content.dispatchEvent(new content.CustomEvent("LightweightTheme:Set",
- event));
- }
- }
-}
-
-var EXPORTED_SYMBOLS = ["LightweightThemeChildListener"];
+ update(outerWindowID, content) {
+ const event = Cu.cloneInto({
+ detail: {
+ data: Services.cpmm.sharedData.get(`theme/${outerWindowID}`)
+ },
+ }, content);
+ content.dispatchEvent(new content.CustomEvent("LightweightTheme:Set",
+ event));
+ },
+};
--- a/browser/modules/moz.build
+++ b/browser/modules/moz.build
@@ -59,17 +59,17 @@ with Files("ContentWebRTC.jsm"):
BUG_COMPONENT = ("Firefox", "Device Permissions")
with Files("ExtensionsUI.jsm"):
BUG_COMPONENT = ("WebExtensions", "General")
with Files("LaterRun.jsm"):
BUG_COMPONENT = ("Firefox", "Tours")
-with Files("LightweightThemeChildListener.jsm"):
+with Files("LightweightThemeChildHelper.jsm"):
BUG_COMPONENT = ("WebExtensions", "Themes")
with Files("LightWeightThemeWebInstallListener.jsm"):
BUG_COMPONENT = ("Firefox", "Theme")
with Files("OpenInTabsUtils.jsm"):
BUG_COMPONENT = ("Firefox", "Tabbed Browser")
@@ -148,17 +148,17 @@ EXTRA_JS_MODULES += [
'ContentWebRTC.jsm',
'ContextMenu.jsm',
'ExtensionsUI.jsm',
'Feeds.jsm',
'FormSubmitObserver.jsm',
'FormValidationHandler.jsm',
'HomePage.jsm',
'LaterRun.jsm',
- 'LightweightThemeChildListener.jsm',
+ 'LightweightThemeChildHelper.jsm',
'LightWeightThemeWebInstallListener.jsm',
'NetErrorContent.jsm',
'OpenInTabsUtils.jsm',
'PageActions.jsm',
'PageInfoListener.jsm',
'PageStyleHandler.jsm',
'PermissionUI.jsm',
'PingCentre.jsm',
--- a/toolkit/components/extensions/test/browser/browser_ext_themes_ntp_colors.js
+++ b/toolkit/components/extensions/test/browser/browser_ext_themes_ntp_colors.js
@@ -29,16 +29,18 @@ async function test_ntp_theme(theme, isB
return {
originalBackground: content.getComputedStyle(doc.body).backgroundColor,
originalColor: content.getComputedStyle(doc.querySelector(".outer-wrapper")).color,
};
});
await extension.startup();
+ Services.ppmm.sharedData.flush();
+
await ContentTask.spawn(browser, {
isBrightText,
background: hexToCSS(theme.colors.ntp_background),
color: hexToCSS(theme.colors.ntp_text),
}, function({isBrightText, background, color}) {
let doc = content.document;
ok(doc.body.hasAttribute("lwt-newtab"),
"New tab page should have lwt-newtab attribute");
@@ -48,16 +50,18 @@ async function test_ntp_theme(theme, isB
is(content.getComputedStyle(doc.body).backgroundColor, background,
"New tab page background should be set.");
is(content.getComputedStyle(doc.querySelector(".outer-wrapper")).color, color,
"New tab page text color should be set.");
});
await extension.unload();
+ Services.ppmm.sharedData.flush();
+
await ContentTask.spawn(browser, {
originalBackground,
originalColor,
}, function({originalBackground, originalColor}) {
let doc = content.document;
ok(!doc.body.hasAttribute("lwt-newtab"),
"New tab page should not have lwt-newtab attribute");
ok(!doc.body.hasAttribute("lwt-newtab-brighttext"),
--- a/toolkit/components/extensions/test/browser/browser_ext_themes_ntp_colors_perwindow.js
+++ b/toolkit/components/extensions/test/browser/browser_ext_themes_ntp_colors_perwindow.js
@@ -5,16 +5,17 @@
/**
* Test whether a given browser has the new tab page theme applied
* @param {Object} browser to test against
* @param {Object} theme that is applied
* @param {boolean} isBrightText whether the brighttext attribute should be set
* @returns {Promise} The task as a promise
*/
function test_ntp_theme(browser, theme, isBrightText) {
+ Services.ppmm.sharedData.flush();
return ContentTask.spawn(browser, {
isBrightText,
background: hexToCSS(theme.colors.ntp_background),
color: hexToCSS(theme.colors.ntp_text),
}, function({isBrightText, background, color}) {
let doc = content.document;
ok(doc.body.hasAttribute("lwt-newtab"),
"New tab page should have lwt-newtab attribute");
@@ -29,16 +30,17 @@ function test_ntp_theme(browser, theme,
}
/**
* Test whether a given browser has the default theme applied
* @param {Object} browser to test against
* @returns {Promise} The task as a promise
*/
function test_ntp_default_theme(browser) {
+ Services.ppmm.sharedData.flush();
return ContentTask.spawn(browser, {
background: hexToCSS("#F9F9FA"),
color: hexToCSS("#0C0C0D"),
}, function({background, color}) {
let doc = content.document;
ok(!doc.body.hasAttribute("lwt-newtab"),
"New tab page should not have lwt-newtab attribute");
ok(!doc.body.hasAttribute("lwt-newtab-brighttext"),
--- a/toolkit/modules/LightweightThemeConsumer.jsm
+++ b/toolkit/modules/LightweightThemeConsumer.jsm
@@ -105,80 +105,66 @@ ChromeUtils.defineModuleGetter(this, "Th
ChromeUtils.defineModuleGetter(this, "ThemeVariableMap",
"resource:///modules/ThemeVariableMap.jsm");
ChromeUtils.defineModuleGetter(this, "LightweightThemeImageOptimizer",
"resource://gre/modules/addons/LightweightThemeImageOptimizer.jsm");
function LightweightThemeConsumer(aDocument) {
this._doc = aDocument;
this._win = aDocument.defaultView;
+ this._winId = this._win.windowUtils.outerWindowID;
Services.obs.addObserver(this, "lightweight-theme-styling-update");
var temp = {};
ChromeUtils.import("resource://gre/modules/LightweightThemeManager.jsm", temp);
this._update(temp.LightweightThemeManager.currentThemeForDisplay);
this._win.addEventListener("resolutionchange", this);
this._win.addEventListener("unload", this, { once: true });
- this._win.addEventListener("EndSwapDocShells", this, true);
- this._win.messageManager.addMessageListener("LightweightTheme:Request", this);
let darkThemeMediaQuery = this._win.matchMedia("(-moz-system-dark-theme)");
darkThemeMediaQuery.addListener(temp.LightweightThemeManager);
temp.LightweightThemeManager.systemThemeChanged(darkThemeMediaQuery);
}
LightweightThemeConsumer.prototype = {
_lastData: null,
// Whether a lightweight theme is enabled.
_active: false,
observe(aSubject, aTopic, aData) {
if (aTopic != "lightweight-theme-styling-update")
return;
- const { outerWindowID } = this._win.windowUtils;
-
let parsedData = JSON.parse(aData);
if (!parsedData) {
parsedData = { theme: null };
}
- if (parsedData.window && parsedData.window !== outerWindowID) {
+ if (parsedData.window && parsedData.window !== this._winId) {
return;
}
this._update(parsedData.theme);
},
- receiveMessage({ name, target }) {
- if (name == "LightweightTheme:Request") {
- let contentThemeData = _getContentProperties(this._doc, this._active, this._lastData);
- target.messageManager.sendAsyncMessage("LightweightTheme:Update", contentThemeData);
- }
- },
-
handleEvent(aEvent) {
switch (aEvent.type) {
case "resolutionchange":
if (this._active) {
this._update(this._lastData);
}
break;
case "unload":
Services.obs.removeObserver(this, "lightweight-theme-styling-update");
+ Services.ppmm.sharedData.delete(`theme/${this._winId}`);
this._win.removeEventListener("resolutionchange", this);
- this._win.removeEventListener("EndSwapDocShells", this, true);
this._win = this._doc = null;
break;
- case "EndSwapDocShells":
- let contentThemeData = _getContentProperties(this._doc, this._active, this._lastData);
- aEvent.target.messageManager.sendAsyncMessage("LightweightTheme:Update", contentThemeData);
- break;
}
},
_update(aData) {
this._lastData = aData;
if (aData) {
aData = LightweightThemeImageOptimizer.optimize(aData, this._win.screen);
}
@@ -222,21 +208,17 @@ LightweightThemeConsumer.prototype = {
}
if (active && aData.footerURL)
root.setAttribute("lwthemefooter", "true");
else
root.removeAttribute("lwthemefooter");
let contentThemeData = _getContentProperties(this._doc, active, aData);
-
- let browserMessageManager = this._win.getGroupMessageManager("browsers");
- browserMessageManager.broadcastAsyncMessage(
- "LightweightTheme:Update", contentThemeData
- );
+ Services.ppmm.sharedData.set(`theme/${this._winId}`, contentThemeData);
}
};
function _getContentProperties(doc, active, data) {
if (!active) {
return {};
}
let properties = {};