Bug 1309908 - support the Lightweight Themes feature set in the themes API. r?k-9
MozReview-Commit-ID: CxNmI55IZWN
--- a/browser/components/extensions/ext-theme.js
+++ b/browser/components/extensions/ext-theme.js
@@ -1,56 +1,84 @@
"use strict";
+Cu.import("resource://gre/modules/Services.jsm");
+
// WeakMap[Extension -> Theme]
let themeMap = new WeakMap();
let styleSheetService = Components.classes["@mozilla.org/content/style-sheet-service;1"]
.getService(Components.interfaces.nsIStyleSheetService);
let ioService = Components.classes["@mozilla.org/network/io-service;1"]
.getService(Components.interfaces.nsIIOService);
function Theme(manifest) {
this.userSheetURI = null;
+ this.LWTStyles = null;
this.loadThemeFromManifest(manifest);
}
Theme.prototype = {
loadThemeFromManifest(manifest) {
- if (!manifest || !manifest.theme || !manifest.theme.images) {
- return;
- }
-
// A temporary element is created to filter the CSS values that
// themes can provide.
let temp = WindowManager.topWindow.document.createElement("temp");
let cssVars = "";
- for (let image of Object.keys(manifest.theme.images)) {
+ let LWTStyles = this.LWTStyles = { headerURL: "", footerURL: "", textcolor: "", accentcolor: "" };
+
+ for (let image of Object.getOwnPropertyNames(manifest.theme.images || {})) {
+ let cssURL = 'url("' + manifest.theme.images[image].replace(/"/g, '\\"') + '")';
if (image == "theme_ntp_background") {
- temp.style.background = manifest.theme.images.theme_ntp_background;
+ temp.style.background = cssURL;
cssVars += `--page-background: ${temp.style.background} !important;`;
+ } else if (image == "theme_frame" || image == "headerURL") {
+ LWTStyles.headerURL = manifest.theme.images[image];
}
}
+
+ for (let color of Object.getOwnPropertyNames(manifest.theme.colors || {})) {
+ let val = manifest.theme.colors[color];
+ let cssColor = Array.isArray(val) ?
+ "rgb" + (val.length > 3 ? "a" : "") + "(" + val.join(",") + ")" : val;
+ switch (color) {
+ case "accentcolor":
+ case "frame":
+ LWTStyles.accentcolor = cssColor;
+ break;
+ case "tab_text":
+ case "textcolor":
+ LWTStyles.textcolor = cssColor;
+ break;
+ }
+ }
+
let aboutHomeStyles = `
@-moz-document url("about:home"),
url("chrome://browser/content/abouthome/aboutHome.xhtml") {
:root {
${cssVars}
}
}`;
let dataURL = `data:text/css,${aboutHomeStyles}`;
this.userSheetURI = ioService.newURI(dataURL, null, null);
styleSheetService.loadAndRegisterSheet(this.userSheetURI, styleSheetService.USER_SHEET);
+
+ if (LWTStyles.headerURL) {
+ Services.obs.notifyObservers(null, "lightweight-theme-styling-update", JSON.stringify(LWTStyles));
+ }
},
shutdown() {
if (this.userSheetURI) {
styleSheetService.unregisterSheet(this.userSheetURI, styleSheetService.USER_SHEET);
}
+ if (this.LWTStyles.headerURL) {
+ Services.obs.notifyObservers(null, "lightweight-theme-styling-update", null);
+ }
}
}
/* eslint-disable mozilla/balanced-listeners */
extensions.on("manifest_theme", (type, directive, extension, manifest) => {
themeMap.set(extension, new Theme(manifest));
});
--- a/browser/components/extensions/schemas/theme.json
+++ b/browser/components/extensions/schemas/theme.json
@@ -12,21 +12,51 @@
"theme": {
"type": "object",
"optional": true,
"properties": {
"images": {
"type": "object",
"optional": true,
"properties": {
+ "headerURL": {
+ "type": "string",
+ "optional": true
+ },
+ "theme_frame": {
+ "type": "string",
+ "optional": true
+ },
"theme_ntp_background": {
"type": "string",
"optional": true
}
}
+ },
+ "colors": {
+ "type": "object",
+ "optional": true,
+ "properties": {
+ "accentcolor": {
+ "type": "string",
+ "optional": true
+ },
+ "frame": {
+ "type": "array",
+ "optional": true
+ },
+ "tab_text": {
+ "type": "array",
+ "optional": true
+ },
+ "textcolor": {
+ "type": "string",
+ "optional": true
+ }
+ }
}
}
}
}
}
]
},
{
--- a/browser/components/extensions/test/browser/browser.ini
+++ b/browser/components/extensions/test/browser/browser.ini
@@ -81,16 +81,17 @@ tags = webextensions
[browser_ext_tabs_query.js]
[browser_ext_tabs_reload.js]
[browser_ext_tabs_reload_bypass_cache.js]
[browser_ext_tabs_sendMessage.js]
[browser_ext_tabs_update.js]
[browser_ext_tabs_zoom.js]
[browser_ext_tabs_update_url.js]
[browser_ext_theme_abouthomebackground.js]
+[browser_ext_theme_lwtsupport.js]
[browser_ext_topwindowid.js]
[browser_ext_webNavigation_frameId0.js]
[browser_ext_webNavigation_getFrames.js]
[browser_ext_webNavigation_urlbar_transitions.js]
[browser_ext_windows.js]
[browser_ext_windows_create.js]
tags = fullscreen
[browser_ext_windows_create_tabId.js]
--- a/browser/components/extensions/test/browser/browser_ext_theme_abouthomebackground.js
+++ b/browser/components/extensions/test/browser/browser_ext_theme_abouthomebackground.js
@@ -1,13 +1,13 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
-const kBackground = `url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==")`;
+const kBackground = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==";
add_task(function* testAboutHomeBackground() {
let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:home", true);
let extension = ExtensionTestUtils.loadExtension({
manifest: {
"theme": {
"images": {
@@ -17,17 +17,17 @@ add_task(function* testAboutHomeBackgrou
}
});
yield extension.startup();
let win = tab.linkedBrowser.contentWindow;
let docEl = win.document.documentElement;
let background = win.getComputedStyle(docEl).backgroundImage;
- is(background, kBackground, "Expected background image");
+ is(background, `url("` + kBackground + `")`, "Expected background image");
yield extension.unload();
background = win.getComputedStyle(docEl).backgroundImage;
is(background, "none", "The background image should be cleared when the extension is unloaded");
yield BrowserTestUtils.removeTab(tab);
});
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/test/browser/browser_ext_theme_lwtsupport.js
@@ -0,0 +1,98 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+"use strict";
+
+const kBackground = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==";
+
+function hexToRGB(hex) {
+ hex = parseInt((hex.indexOf("#") > -1 ? hex.substring(1) : hex), 16);
+ return [ hex >> 16, (hex & 0x00FF00) >> 8, (hex & 0x0000FF) ];
+}
+
+add_task(function* testLWTSupportNewProperties() {
+ const kFrameColor = [71, 105, 91];
+ const kTabTextColor = [207, 221, 192, .9];
+
+ let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:home", true);
+
+ let extension = ExtensionTestUtils.loadExtension({
+ manifest: {
+ "theme": {
+ "images": {
+ "theme_frame": kBackground
+ },
+ "colors": {
+ "frame": kFrameColor,
+ "tab_text": kTabTextColor
+ }
+ }
+ }
+ });
+
+ yield extension.startup();
+
+ let win = tab.ownerDocument.defaultView;
+ let docEl = tab.ownerDocument.documentElement;
+ let style = win.getComputedStyle(docEl);
+
+ Assert.ok(docEl.hasAttribute("lwtheme"), "LWT attribute should be set");
+ Assert.equal(docEl.getAttribute("lwthemetextcolor"), "bright",
+ "LWT text color attribute should be set");
+
+ Assert.equal(style.backgroundImage, 'url("' + kBackground.replace(/"/g, '\\"') + '")',
+ "Expected background image");
+ Assert.equal(style.backgroundColor, "rgb(" + kFrameColor.join(", ") + ")",
+ "Expected correct background color");
+ Assert.equal(style.color, "rgba(" + kTabTextColor.join(", ") + ")",
+ "Expected correct text color");
+
+ yield extension.unload();
+
+ Assert.ok(!docEl.hasAttribute("lwtheme"), "LWT attribute should not be set");
+
+ yield BrowserTestUtils.removeTab(tab);
+});
+
+add_task(function* testLWTSupportLegacyProperties() {
+ const kAccentColor = "#a14040";
+ const kTextColor = "#fac96e";
+
+ let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:home", true);
+
+ let extension = ExtensionTestUtils.loadExtension({
+ manifest: {
+ "theme": {
+ "images": {
+ "headerURL": kBackground
+ },
+ "colors": {
+ "accentcolor": kAccentColor,
+ "textcolor": kTextColor
+ }
+ }
+ }
+ });
+
+ yield extension.startup();
+
+ let win = tab.ownerDocument.defaultView;
+ let docEl = tab.ownerDocument.documentElement;
+ let style = win.getComputedStyle(docEl);
+
+ Assert.ok(docEl.hasAttribute("lwtheme"), "LWT attribute should be set");
+ Assert.equal(docEl.getAttribute("lwthemetextcolor"), "bright",
+ "LWT text color attribute should be set");
+
+ Assert.equal(style.backgroundImage, 'url("' + kBackground.replace(/"/g, '\\"') + '")',
+ "Expected background image");
+ Assert.equal(style.backgroundColor, "rgb(" + hexToRGB(kAccentColor).join(", ") + ")",
+ "Expected correct background color");
+ Assert.equal(style.color, "rgb(" + hexToRGB(kTextColor).join(", ") + ")",
+ "Expected correct text color");
+
+ yield extension.unload();
+
+ Assert.ok(!docEl.hasAttribute("lwtheme"), "LWT attribute should not be set");
+
+ yield BrowserTestUtils.removeTab(tab);
+});