зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1330341 - Add support for dynamic updates r=mikedeboer
MozReview-Commit-ID: 8wA7J1oX2t --HG-- extra : rebase_source : e244e9803644676398d952057d416f2d9558e833
This commit is contained in:
Родитель
32d84fe236
Коммит
247590ec7d
|
@ -5,58 +5,123 @@ Cu.import("resource://gre/modules/Services.jsm");
|
|||
XPCOMUtils.defineLazyModuleGetter(this, "Preferences",
|
||||
"resource://gre/modules/Preferences.jsm");
|
||||
|
||||
/* eslint-disable mozilla/balanced-listeners */
|
||||
extensions.on("manifest_theme", (type, directive, extension, manifest) => {
|
||||
let enabled = Preferences.get("extensions.webextensions.themes.enabled");
|
||||
// WeakMap[Extension -> Theme]
|
||||
let themeMap = new WeakMap();
|
||||
|
||||
if (!enabled || !manifest || !manifest.theme) {
|
||||
return;
|
||||
}
|
||||
// Apply theme only if themes are enabled.
|
||||
let lwtStyles = {footerURL: ""};
|
||||
if (manifest.theme.colors) {
|
||||
let colors = manifest.theme.colors;
|
||||
for (let color of Object.getOwnPropertyNames(colors)) {
|
||||
let val = colors[color];
|
||||
// Since values are optional, they may be `null`.
|
||||
if (val === null) {
|
||||
continue;
|
||||
/** Class representing a theme. */
|
||||
class Theme {
|
||||
/**
|
||||
* Creates a theme instance.
|
||||
*/
|
||||
constructor() {
|
||||
// A dictionary of light weight theme styles.
|
||||
this.lwtStyles = {};
|
||||
}
|
||||
|
||||
if (color == "accentcolor") {
|
||||
lwtStyles.accentcolor = val;
|
||||
continue;
|
||||
}
|
||||
if (color == "textcolor") {
|
||||
lwtStyles.textcolor = val;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Loads a theme by reading the properties from the extension's manifest.
|
||||
* This method will override any currently applied theme.
|
||||
*
|
||||
* @param {Object} details Theme part of the manifest. Supported
|
||||
* properties can be found in the schema under ThemeType.
|
||||
*/
|
||||
load(details) {
|
||||
if (details.colors) {
|
||||
this.loadColors(details.colors);
|
||||
}
|
||||
|
||||
if (manifest.theme.images) {
|
||||
let images = manifest.theme.images;
|
||||
for (let image of Object.getOwnPropertyNames(images)) {
|
||||
let val = images[image];
|
||||
if (val === null) {
|
||||
continue;
|
||||
if (details.images) {
|
||||
this.loadImages(details.images);
|
||||
}
|
||||
|
||||
if (image == "headerURL") {
|
||||
lwtStyles.headerURL = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (lwtStyles.headerURL &&
|
||||
lwtStyles.accentcolor &&
|
||||
lwtStyles.textcolor) {
|
||||
// Lightweight themes require all properties to be defined.
|
||||
if (this.lwtStyles.headerURL &&
|
||||
this.lwtStyles.accentcolor &&
|
||||
this.lwtStyles.textcolor) {
|
||||
Services.obs.notifyObservers(null,
|
||||
"lightweight-theme-styling-update",
|
||||
JSON.stringify(lwtStyles));
|
||||
JSON.stringify(this.lwtStyles));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method for loading colors found in the extension's manifest.
|
||||
*
|
||||
* @param {Object} colors Dictionary mapping color properties to values.
|
||||
*/
|
||||
loadColors(colors) {
|
||||
let {accentcolor, textcolor} = colors;
|
||||
|
||||
if (accentcolor) {
|
||||
this.lwtStyles.accentcolor = accentcolor;
|
||||
}
|
||||
|
||||
if (textcolor) {
|
||||
this.lwtStyles.textcolor = textcolor;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method for loading images found in the extension's manifest.
|
||||
*
|
||||
* @param {Object} images Dictionary mapping image properties to values.
|
||||
*/
|
||||
loadImages(images) {
|
||||
let {headerURL} = images;
|
||||
|
||||
if (headerURL) {
|
||||
this.lwtStyles.headerURL = headerURL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unloads the currently applied theme.
|
||||
*/
|
||||
unload() {
|
||||
Services.obs.notifyObservers(null,
|
||||
"lightweight-theme-styling-update",
|
||||
null);
|
||||
}
|
||||
}
|
||||
|
||||
/* eslint-disable mozilla/balanced-listeners */
|
||||
extensions.on("manifest_theme", (type, directive, extension, manifest) => {
|
||||
if (!Preferences.get("extensions.webextensions.themes.enabled")) {
|
||||
// Return early if themes are disabled.
|
||||
return;
|
||||
}
|
||||
|
||||
let theme = new Theme();
|
||||
theme.load(manifest.theme);
|
||||
themeMap.set(extension, theme);
|
||||
});
|
||||
|
||||
extensions.on("shutdown", (type, extension) => {
|
||||
Services.obs.notifyObservers(null, "lightweight-theme-styling-update", null);
|
||||
let theme = themeMap.get(extension);
|
||||
|
||||
// We won't have a theme if theme's aren't enabled.
|
||||
if (!theme) {
|
||||
return;
|
||||
}
|
||||
|
||||
theme.unload();
|
||||
});
|
||||
/* eslint-enable mozilla/balanced-listeners */
|
||||
|
||||
extensions.registerSchemaAPI("theme", "addon_parent", context => {
|
||||
let {extension} = context;
|
||||
return {
|
||||
theme: {
|
||||
update(details) {
|
||||
let theme = themeMap.get(extension);
|
||||
|
||||
// We won't have a theme if theme's aren't enabled.
|
||||
if (!theme) {
|
||||
return;
|
||||
}
|
||||
|
||||
theme.load(details);
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
|
|
|
@ -46,5 +46,25 @@
|
|||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"namespace": "theme",
|
||||
"description": "The theme API allows customizing of visual elements of the browser.",
|
||||
"permissions": ["manifest:theme"],
|
||||
"functions": [
|
||||
{
|
||||
"name": "update",
|
||||
"type": "function",
|
||||
"async": true,
|
||||
"description": "Make complete or partial updates to the theme. Resolves when the update has completed.",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "details",
|
||||
"$ref": "manifest.ThemeType",
|
||||
"description": "The properties of the theme to update."
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
|
|
@ -107,6 +107,7 @@ support-files =
|
|||
[browser_ext_tabs_update.js]
|
||||
[browser_ext_tabs_zoom.js]
|
||||
[browser_ext_tabs_update_url.js]
|
||||
[browser_ext_themes_dynamic_updates.js]
|
||||
[browser_ext_themes_lwtsupport.js]
|
||||
[browser_ext_topwindowid.js]
|
||||
[browser_ext_url_overrides.js]
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
"use strict";
|
||||
|
||||
// PNG image data for a simple red dot.
|
||||
const BACKGROUND_1 = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==";
|
||||
const ACCENT_COLOR_1 = "#a14040";
|
||||
const TEXT_COLOR_1 = "#fac96e";
|
||||
|
||||
// PNG image data for the Mozilla dino head.
|
||||
const BACKGROUND_2 = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAHWSURBVHjaYvz//z8DJQAggJiQOe/fv2fv7Oz8rays/N+VkfG/iYnJfyD/1+rVq7ffu3dPFpsBAAHEAHIBCJ85c8bN2Nj4vwsDw/8zQLwKiO8CcRoQu0DxqlWrdsHUwzBAAIGJmTNnPgYa9j8UqhFElwPxf2MIDeIrKSn9FwSJoRkAEEAM0DD4DzMAyPi/G+QKY4hh5WAXGf8PDQ0FGwJ22d27CjADAAIIrLmjo+MXA9R2kAHvGBA2wwx6B8W7od6CeQcggKCmCEL8bgwxYCbUIGTDVkHDBia+CuotgACCueD3TDQN75D4xmAvCoK9ARMHBzAw0AECiBHkAlC0Mdy7x9ABNA3obAZXIAa6iKEcGlMVQHwWyjYuL2d4v2cPg8vZswx7gHyAAAK7AOif7SAbOqCmn4Ha3AHFsIDtgPq/vLz8P4MSkJ2W9h8ggBjevXvHDo4FQUQg/kdypqCg4H8lUIACnQ/SOBMYI8bAsAJFPcj1AAEEjwVQqLpAbXmH5BJjqI0gi9DTAAgDBBCcAVLkgmQ7yKCZxpCQxqUZhAECCJ4XgMl493ug21ZD+aDAXH0WLM4A9MZPXJkJIIAwTAR5pQMalaCABQUULttBGCCAGCnNzgABBgAMJ5THwGvJLAAAAABJRU5ErkJggg==";
|
||||
const ACCENT_COLOR_2 = "#03fe03";
|
||||
const TEXT_COLOR_2 = "#0ef325";
|
||||
|
||||
function hexToRGB(hex) {
|
||||
hex = parseInt((hex.indexOf("#") > -1 ? hex.substring(1) : hex), 16);
|
||||
return [hex >> 16, (hex & 0x00FF00) >> 8, (hex & 0x0000FF)];
|
||||
}
|
||||
|
||||
function validateTheme(backgroundImage, accentColor, textColor) {
|
||||
let docEl = window.document.documentElement;
|
||||
let style = window.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("' + backgroundImage.replace(/"/g, '\\"') + '")',
|
||||
"Expected correct background image");
|
||||
Assert.equal(style.backgroundColor, "rgb(" + hexToRGB(accentColor).join(", ") + ")",
|
||||
"Expected correct accent color");
|
||||
Assert.equal(style.color, "rgb(" + hexToRGB(textColor).join(", ") + ")",
|
||||
"Expected correct text color");
|
||||
}
|
||||
|
||||
add_task(function* setup() {
|
||||
yield SpecialPowers.pushPrefEnv({
|
||||
set: [["extensions.webextensions.themes.enabled", true]],
|
||||
});
|
||||
});
|
||||
|
||||
add_task(function* test_dynamic_theme_updates() {
|
||||
let extension = ExtensionTestUtils.loadExtension({
|
||||
manifest: {
|
||||
"theme": {
|
||||
"images": {
|
||||
"headerURL": BACKGROUND_1,
|
||||
},
|
||||
"colors": {
|
||||
"accentcolor": ACCENT_COLOR_1,
|
||||
"textcolor": TEXT_COLOR_1,
|
||||
},
|
||||
},
|
||||
},
|
||||
background() {
|
||||
browser.test.onMessage.addListener((msg, details) => {
|
||||
if (msg != "update-theme") {
|
||||
browser.test.fail("expected 'update-theme' message");
|
||||
}
|
||||
|
||||
browser.theme.update(details);
|
||||
browser.test.sendMessage("theme-updated");
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
yield extension.startup();
|
||||
|
||||
validateTheme(BACKGROUND_1, ACCENT_COLOR_1, TEXT_COLOR_1);
|
||||
|
||||
extension.sendMessage("update-theme", {
|
||||
"images": {
|
||||
"headerURL": BACKGROUND_2,
|
||||
},
|
||||
"colors": {
|
||||
"accentcolor": ACCENT_COLOR_2,
|
||||
"textcolor": TEXT_COLOR_2,
|
||||
},
|
||||
});
|
||||
|
||||
yield extension.awaitMessage("theme-updated");
|
||||
|
||||
validateTheme(BACKGROUND_2, ACCENT_COLOR_2, TEXT_COLOR_2);
|
||||
|
||||
yield extension.unload();
|
||||
|
||||
let docEl = window.document.documentElement;
|
||||
Assert.ok(!docEl.hasAttribute("lwtheme"), "LWT attribute should not be set");
|
||||
});
|
|
@ -1,8 +1,8 @@
|
|||
"use strict";
|
||||
|
||||
const kBackground = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==";
|
||||
const kAccentColor = "#a14040";
|
||||
const kTextColor = "#fac96e";
|
||||
const BACKGROUND = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==";
|
||||
const ACCENT_COLOR = "#a14040";
|
||||
const TEXT_COLOR = "#fac96e";
|
||||
|
||||
function hexToRGB(hex) {
|
||||
hex = parseInt((hex.indexOf("#") > -1 ? hex.substring(1) : hex), 16);
|
||||
|
@ -15,16 +15,16 @@ add_task(function* setup() {
|
|||
});
|
||||
});
|
||||
|
||||
add_task(function* testSupportLWTProperties() {
|
||||
add_task(function* test_support_LWT_properties() {
|
||||
let extension = ExtensionTestUtils.loadExtension({
|
||||
manifest: {
|
||||
"theme": {
|
||||
"images": {
|
||||
"headerURL": kBackground,
|
||||
"headerURL": BACKGROUND,
|
||||
},
|
||||
"colors": {
|
||||
"accentcolor": kAccentColor,
|
||||
"textcolor": kTextColor,
|
||||
"accentcolor": ACCENT_COLOR,
|
||||
"textcolor": TEXT_COLOR,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -39,11 +39,11 @@ add_task(function* testSupportLWTProperties() {
|
|||
Assert.equal(docEl.getAttribute("lwthemetextcolor"), "bright",
|
||||
"LWT text color attribute should be set");
|
||||
|
||||
Assert.equal(style.backgroundImage, 'url("' + kBackground.replace(/"/g, '\\"') + '")',
|
||||
Assert.equal(style.backgroundImage, 'url("' + BACKGROUND.replace(/"/g, '\\"') + '")',
|
||||
"Expected background image");
|
||||
Assert.equal(style.backgroundColor, "rgb(" + hexToRGB(kAccentColor).join(", ") + ")",
|
||||
Assert.equal(style.backgroundColor, "rgb(" + hexToRGB(ACCENT_COLOR).join(", ") + ")",
|
||||
"Expected correct background color");
|
||||
Assert.equal(style.color, "rgb(" + hexToRGB(kTextColor).join(", ") + ")",
|
||||
Assert.equal(style.color, "rgb(" + hexToRGB(TEXT_COLOR).join(", ") + ")",
|
||||
"Expected correct text color");
|
||||
|
||||
yield extension.unload();
|
||||
|
@ -51,12 +51,12 @@ add_task(function* testSupportLWTProperties() {
|
|||
Assert.ok(!docEl.hasAttribute("lwtheme"), "LWT attribute should not be set");
|
||||
});
|
||||
|
||||
add_task(function* testLWTRequiresAllPropertiesDefinedImageOnly() {
|
||||
add_task(function* test_LWT_requires_all_properties_defined_image_only() {
|
||||
let extension = ExtensionTestUtils.loadExtension({
|
||||
manifest: {
|
||||
"theme": {
|
||||
"images": {
|
||||
"headerURL": kBackground,
|
||||
"headerURL": BACKGROUND,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -70,13 +70,13 @@ add_task(function* testLWTRequiresAllPropertiesDefinedImageOnly() {
|
|||
Assert.ok(!docEl.hasAttribute("lwtheme"), "LWT attribute should not be set");
|
||||
});
|
||||
|
||||
add_task(function* testLWTRequiresAllPropertiesDefinedColorsOnly() {
|
||||
add_task(function* test_LWT_requires_all_properties_defined_colors_only() {
|
||||
let extension = ExtensionTestUtils.loadExtension({
|
||||
manifest: {
|
||||
"theme": {
|
||||
"colors": {
|
||||
"accentcolor": kAccentColor,
|
||||
"textcolor": kTextColor,
|
||||
"accentcolor": ACCENT_COLOR,
|
||||
"textcolor": TEXT_COLOR,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
Загрузка…
Ссылка в новой задаче