Bug 1349944 - Add browser.theme.getCurrent() to query the selected theme. r=jaws,mixedpuppy

MozReview-Commit-ID: Hzdm21riVlb

--HG--
extra : rebase_source : 135464563d5b6c3784cd475cfe1dbe4a7c72de7b
This commit is contained in:
Tim Nguyen 2017-10-31 01:03:15 +00:00
Родитель dba3425d42
Коммит 9056c46ef4
4 изменённых файлов: 209 добавлений и 9 удалений

Просмотреть файл

@ -28,14 +28,29 @@ class Theme {
* to show manifest warnings to the theme author.
*/
constructor(baseURI, logger) {
// A dictionary of light weight theme styles.
this.lwtStyles = {
icons: {},
};
// The base theme applied to all windows.
this.baseProperties = {};
// Window-specific theme overrides.
this.windowOverrides = new WeakMap();
this.baseURI = baseURI;
this.logger = logger;
}
/**
* Gets the current theme for a specified window
*
* @param {Object} window
* @returns {Object} The theme of the specified window
*/
getWindowTheme(window) {
if (this.windowOverrides.has(window)) {
return this.windowOverrides.get(window);
}
return this.baseProperties;
}
/**
* Loads a theme by reading the properties from the extension's manifest.
* This method will override any currently applied theme.
@ -46,6 +61,10 @@ class Theme {
* this parameter will apply the theme globally.
*/
load(details, targetWindow) {
this.lwtStyles = {
icons: {},
};
if (targetWindow) {
this.lwtStyles.window = getWinUtils(targetWindow).outerWindowID;
}
@ -70,6 +89,11 @@ class Theme {
if (this.lwtStyles.headerURL &&
this.lwtStyles.accentcolor &&
this.lwtStyles.textcolor) {
if (!targetWindow) {
this.baseProperties = details;
} else {
this.windowOverrides.set(targetWindow, details);
}
LightweightThemeManager.fallbackThemeData = this.lwtStyles;
Services.obs.notifyObservers(null,
"lightweight-theme-styling-update",
@ -245,7 +269,7 @@ class Theme {
* @param {Object} targetWindow The window the theme should be unloaded from
*/
unload(targetWindow) {
let lwtStyles = {
this.lwtStyles = {
headerURL: "",
accentcolor: "",
additionalBackgrounds: "",
@ -256,16 +280,20 @@ class Theme {
};
if (targetWindow) {
lwtStyles.window = getWinUtils(targetWindow).outerWindowID;
this.lwtStyles.window = getWinUtils(targetWindow).outerWindowID;
this.windowOverrides.set(targetWindow, {});
} else {
this.windowOverrides = new WeakMap();
this.baseProperties = {};
}
for (let icon of ICONS) {
lwtStyles.icons[`--${icon}--icon`] = "";
this.lwtStyles.icons[`--${icon}--icon`] = "";
}
LightweightThemeManager.fallbackThemeData = null;
Services.obs.notifyObservers(null,
"lightweight-theme-styling-update",
JSON.stringify(lwtStyles));
JSON.stringify(this.lwtStyles));
}
}
@ -299,6 +327,23 @@ this.theme = class extends ExtensionAPI {
return {
theme: {
getCurrent: (windowId) => {
// Return empty theme if none is applied.
if (!this.theme) {
return Promise.resolve({});
}
// Return theme applied on last focused window when no ID is supplied.
if (!windowId) {
return Promise.resolve(this.theme.getWindowTheme(windowTracker.topWindow));
}
const browserWindow = windowTracker.getWindow(windowId, context);
if (!browserWindow) {
return Promise.reject(`Invalid window ID: ${windowId}`);
}
return Promise.resolve(this.theme.getWindowTheme(browserWindow));
},
update: (windowId, details) => {
if (!gThemesEnabled) {
// Return early if themes are disabled.

Просмотреть файл

@ -458,11 +458,25 @@
"description": "The theme API allows customizing of visual elements of the browser.",
"permissions": ["theme"],
"functions": [
{
"name": "getCurrent",
"type": "function",
"async": true,
"description": "Returns the current theme for the specified window or the last focused window.",
"parameters": [
{
"type": "integer",
"name": "windowId",
"optional": true,
"description": "The window for which we want the theme."
}
]
},
{
"name": "update",
"type": "function",
"async": true,
"description": "Make complete or partial updates to the theme. Resolves when the update has completed.",
"description": "Make complete updates to the theme. Resolves when the update has completed.",
"parameters": [
{
"type": "integer",

Просмотреть файл

@ -4,6 +4,7 @@ support-files =
[browser_ext_management_themes.js]
[browser_ext_themes_chromeparity.js]
[browser_ext_themes_dynamic_getCurrent.js]
[browser_ext_themes_dynamic_updates.js]
[browser_ext_themes_lwtsupport.js]
[browser_ext_themes_multiple_backgrounds.js]

Просмотреть файл

@ -0,0 +1,140 @@
"use strict";
// This test checks whether browser.theme.getCurrent() works correctly in different
// configurations and with different parameter.
// PNG image data for a simple red dot.
const BACKGROUND_1 = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==";
// 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==";
add_task(async function test_get_current() {
let extension = ExtensionTestUtils.loadExtension({
async background() {
const ACCENT_COLOR_1 = "#a14040";
const TEXT_COLOR_1 = "#fac96e";
const ACCENT_COLOR_2 = "#03fe03";
const TEXT_COLOR_2 = "#0ef325";
const theme1 = {
"images": {
"headerURL": "image1.png",
},
"colors": {
"accentcolor": ACCENT_COLOR_1,
"textcolor": TEXT_COLOR_1,
},
};
const theme2 = {
"images": {
"headerURL": "image2.png",
},
"colors": {
"accentcolor": ACCENT_COLOR_2,
"textcolor": TEXT_COLOR_2,
},
};
function testTheme1(returnedTheme) {
browser.test.assertTrue(returnedTheme.images.headerURL.includes("image1.png"),
"Theme 1 header URL should be applied");
browser.test.assertEq(ACCENT_COLOR_1, returnedTheme.colors.accentcolor,
"Theme 1 accent color should be applied");
browser.test.assertEq(TEXT_COLOR_1, returnedTheme.colors.textcolor,
"Theme 1 text color should be applied");
}
function testTheme2(returnedTheme) {
browser.test.assertTrue(returnedTheme.images.headerURL.includes("image2.png"),
"Theme 2 header URL should be applied");
browser.test.assertEq(ACCENT_COLOR_2, returnedTheme.colors.accentcolor,
"Theme 2 accent color should be applied");
browser.test.assertEq(TEXT_COLOR_2, returnedTheme.colors.textcolor,
"Theme 2 text color should be applied");
}
function testEmptyTheme(returnedTheme) {
browser.test.assertEq(0, Object.keys(returnedTheme).length, JSON.stringify(returnedTheme, null, 2));
}
browser.test.log("Testing getCurrent() with initial unthemed window");
const firstWin = await browser.windows.getCurrent();
testEmptyTheme(await browser.theme.getCurrent());
testEmptyTheme(await browser.theme.getCurrent(firstWin.id));
browser.test.log("Testing getCurrent() with after theme.update()");
await browser.theme.update(theme1);
testTheme1(await browser.theme.getCurrent());
testTheme1(await browser.theme.getCurrent(firstWin.id));
browser.test.log("Testing getCurrent() with after theme.update(windowId)");
const secondWin = await browser.windows.create();
await browser.theme.update(secondWin.id, theme2);
testTheme2(await browser.theme.getCurrent());
testTheme1(await browser.theme.getCurrent(firstWin.id));
testTheme2(await browser.theme.getCurrent(secondWin.id));
browser.test.log("Testing getCurrent() after window focus change");
await browser.windows.update(firstWin.id, {focused: true});
testTheme1(await browser.theme.getCurrent());
testTheme1(await browser.theme.getCurrent(firstWin.id));
testTheme2(await browser.theme.getCurrent(secondWin.id));
browser.test.log("Testing getCurrent() after another window focus change");
await browser.windows.update(secondWin.id, {focused: true});
testTheme2(await browser.theme.getCurrent());
testTheme1(await browser.theme.getCurrent(firstWin.id));
testTheme2(await browser.theme.getCurrent(secondWin.id));
browser.test.log("Testing getCurrent() after theme.reset(windowId)");
await browser.theme.reset(firstWin.id);
testTheme2(await browser.theme.getCurrent());
testEmptyTheme(await browser.theme.getCurrent(firstWin.id));
testTheme2(await browser.theme.getCurrent(secondWin.id));
browser.test.log("Testing getCurrent() after reset and window focus change");
await browser.windows.update(firstWin.id, {focused: true});
testEmptyTheme(await browser.theme.getCurrent());
testEmptyTheme(await browser.theme.getCurrent(firstWin.id));
testTheme2(await browser.theme.getCurrent(secondWin.id));
browser.test.log("Testing getCurrent() after theme.update(windowId)");
await browser.theme.update(firstWin.id, theme1);
testTheme1(await browser.theme.getCurrent());
testTheme1(await browser.theme.getCurrent(firstWin.id));
testTheme2(await browser.theme.getCurrent(secondWin.id));
browser.test.log("Testing getCurrent() after theme.reset()");
await browser.theme.reset();
testEmptyTheme(await browser.theme.getCurrent());
testEmptyTheme(await browser.theme.getCurrent(firstWin.id));
testEmptyTheme(await browser.theme.getCurrent(secondWin.id));
browser.test.log("Testing getCurrent() after closing a window");
await browser.windows.remove(secondWin.id);
testEmptyTheme(await browser.theme.getCurrent());
testEmptyTheme(await browser.theme.getCurrent(firstWin.id));
browser.test.log("Testing getCurrent() with invalid window ID");
await browser.test.assertRejects(
browser.theme.getCurrent(secondWin.id),
/Invalid window/,
"Invalid window should throw",
);
browser.test.notifyPass("get_current");
},
manifest: {
permissions: ["theme"],
},
files: {
"image1.png": BACKGROUND_1,
"image2.png": BACKGROUND_2,
},
});
await extension.startup();
await extension.awaitFinish("get_current");
await extension.unload();
});