--- a/browser/base/content/abouthome/aboutHome.css
+++ b/browser/base/content/abouthome/aboutHome.css
@@ -1,23 +1,36 @@
%if 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/. */
%endif
:root {
- --page-background: hsl(0,0%,95%);
+ --page-background-color: hsl(0,0%,95%);
+ --page-background-image: none;
+ --page-background-position: inherit;
+ --page-background-repeat: no-repeat;
+ --text-color: #000;
+ --launchbutton-text-color: #525c66;
+ --link-color: -moz-nativehyperlinktext;
+ --link-hover-color: -moz-nativehyperlinktext;
+ --section-color: #3c3c3c;
+ --section-link-color: inherit;
+ --section-link-hover-color: inherit;
}
html {
+ color: var(--text-color);
+ background-color: var(--page-background-color);
+ background-image: var(--page-background-image);
+ background-position: var(--page-background-position);
+ background-repeat: var(--page-background-repeat);
font: message-box;
font-size: 100%;
- background: var(--page-background);
- color: #000;
height: 100%;
}
body {
margin: 0;
display: -moz-box;
-moz-box-orient: vertical;
width: 100%;
@@ -26,20 +39,24 @@ body {
input,
button {
font-size: inherit;
font-family: inherit;
}
a {
- color: -moz-nativehyperlinktext;
+ color: var(--link-color);
text-decoration: none;
}
+a:hover {
+ color: var(--link-hover-color);
+}
+
.spacer {
-moz-box-flex: 1;
}
#topSection {
text-align: center;
}
@@ -198,26 +215,34 @@ a {
#defaultSnippet2 {
background-image: url("chrome://browser/content/abouthome/snippet2.png");
}
#snippets {
display: inline-block;
text-align: start;
margin: 12px 0;
- color: #3c3c3c;
+ color: var(--section-color);
font-size: 75%;
/* 12px is the computed font size, 15px the computed line height of the snippets
with Segoe UI on a default Windows 7 setup. The 15/12 multiplier approximately
converts em from units of font-size to units of line-height. The goal is to
preset the height of a three-line snippet to avoid visual moving/flickering as
the snippets load. */
min-height: calc(15/12 * 3em);
}
+#snippets a {
+ color: var(--section-link-color);
+}
+
+#snippets a:hover {
+ color: var(--section-link-hover-color);
+}
+
#launcher {
display: -moz-box;
-moz-box-align: center;
-moz-box-pack: center;
width: 100%;
background-color: hsla(0,0%,0%,.03);
border-top: 1px solid hsla(0,0%,0%,.03);
box-shadow: 0 1px 2px hsla(0,0%,0%,.02) inset,
@@ -239,17 +264,17 @@ body[narrow] #launcher[session] {
min-width: 88px;
max-width: 176px;
max-height: 85px;
vertical-align: top;
white-space: normal;
background: transparent padding-box;
border: 1px solid transparent;
border-radius: 2px;
- color: #525c66;
+ color: var(--launchbutton-text-color);
font-size: 75%;
cursor: pointer;
transition-property: background-color, border-color, box-shadow;
transition-duration: 150ms;
}
body[narrow] #launcher[session] > .launchButton {
margin: 4px 1px;
--- a/browser/base/content/newtab/newTab.css
+++ b/browser/base/content/newtab/newTab.css
@@ -1,24 +1,37 @@
/* 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/. */
+:root {
+ --page-background-color: #F9F9F9;
+ --page-background-image: none;
+ --page-background-position: inherit;
+ --page-background-repeat: no-repeat;
+ --text-color: message-box;
+ --section-color: #F2F2F2;
+}
+
html {
width: 100%;
height: 100%;
}
body {
font: message-box;
+ color: var(--text-color);
width: 100%;
height: 100%;
padding: 0;
margin: 0;
- background-color: #F9F9F9;
+ background-color: var(--page-background-color);
+ background-image: var(--page-background-image);
+ background-position: var(--page-background-position);
+ background-repeat: var(--page-background-repeat);
display: -moz-box;
position: relative;
-moz-box-flex: 1;
-moz-user-focus: normal;
-moz-box-orient: vertical;
}
input {
@@ -177,17 +190,17 @@ input[type=button] {
.newtab-sponsored,
.newtab-title {
bottom: 0;
white-space: nowrap;
text-overflow: ellipsis;
font-size: 13px;
line-height: 30px;
vertical-align: middle;
- background-color: #F2F2F2;
+ background-color: var(--section-color);
}
.newtab-suggested {
border: 1px solid transparent;
border-radius: 2px;
font-size: 12px;
height: 17px;
line-height: 17px;
--- a/browser/components/extensions/ext-theme.js
+++ b/browser/components/extensions/ext-theme.js
@@ -17,17 +17,28 @@ let ioService = Components.classes["@moz
const kIsMac = AppConstants.platform == "macosx";
const kChromeThemeColorsVarMap = new Map([
["background_tab", ["--tab-selection-background-color", ":root"]],
["background_tab_inactive", ["--tab-background-color", ":root"]],
["background_tab_text", ["--tabs-toolbar-color", ":root"]],
["button_background", ["--chrome-nav-buttons-background", ":root"]],
["frame", ["--chrome-background-color", ":root"]],
["frame_inactive", ["--chrome-secondary-background-color", ":root"]],
+ ["frame_text", "--chrome-color"],
+ ["ntp_background", ["--page-background-color", ":root"]],
+ ["ntp_text", ["--text-color", ":root"]],
+ ["ntp_link", ["--link-color", ":root"]],
+ ["ntp_link_underline", ["--link-hover-color", ":root"]],
+ ["ntp_header", ["--launchbutton-text-color", ":root"]],
+ ["ntp_section", ["--snippet-color", ":root"]],
+ ["ntp_section_text", ["--snippet-color", ":root"]],
+ ["ntp_section_link", ["--snippet-link-color", ":root"]],
+ ["ntp_section_link_underline", ["--snippet-link-hover-color", ":root"]],
["tab_text", ["--tab-selection-color", ":root"]],
+ ["tab_background_text", ["--tabs-toolbar-color", ":root"]],
["toolbar", ["--tab-background-color", ":root"]],
["toolbar_bottom_separator", ["--chrome-nav-bar-separator-color", ":root"]],
["toolbar_button_stroke", ["--toolbarbutton-active-bordercolor", ":root", ":root toolbar:-moz-lwtheme"]],
["toolbar_button_stroke_inactive", ["--toolbarbutton-hover-bordercolor", ":root", ":root toolbar:-moz-lwtheme"]],
["toolbar_input_control", ["--url-and-searchbar-background-color", ":root"]],
["toolbar_input_control_stroke", ["--chrome-nav-bar-controls-border-color", ":root"]],
["toolbar_top_separator", ["--chrome-navigator-toolbox-separator-color", ":root"]],
["toolbar_vertical_separator", ["--urlbar-separator-color", ":root"]],
@@ -36,16 +47,19 @@ const kChromeThemeTintsVarMap = new Map(
["background_tab", ["--tab-hover-background-color", 1, ":root"]],
["buttons", ["--toolbarbutton-hover-background", .2, ":root", ":root toolbar:-moz-lwtheme"]],
]);
const kChromeThemeGradientsVarMap = new Map([
["toolbar_button", ["--toolbarbutton-hover-background", ":root", ":root toolbar:-moz-lwtheme"]],
["toolbar_button_pressed", ["--toolbarbutton-active-background", ":root", ":root toolbar:-moz-lwtheme"]],
]);
const kThemePropertiesVarMap = new Map([
+ ["ntp_background_alignment", ["--page-background-position", ":root"]],
+ ["ntp_background_repeat", ["--page-background-repeat", ":root"]],
+ ["ntp_background_tiling", ["--page-background-repeat", ":root"]],
["square_backbutton", ["--backbutton-urlbar-overlap", ":root"]],
["space_above_tabbar", ["--space-above-tabbar", ":root"]],
["square_tabs", ["--tab-curve-width", "#TabsToolbar"]],
["toolbar_button_shadow", ["--toolbarbutton-active-boxshadow", ":root"]],
["toolbar_button_shadow_inactive", ["--toolbarbutton-hover-boxshadow", ":root"]],
["toolbar_navbar_overlap", ["--tab-toolbar-navbar-overlap", ":root"]],
["toolbar_navbar_highlight_overlap", ["--navbar-tab-toolbar-highlight-overlap", ":root"]],
["toolbar_text_shadow", ["--toolbarbutton-text-shadow", ":root"]],
@@ -77,17 +91,17 @@ const kBrowserOverrideColorsVarMap = new
["frame_inactive", [
["#navigator-toolbox > toolbar:not(#TabsToolbar):not(#toolbar-menubar), .browserContainer > findbar, #browser-bottombox",
{"background-color": "--chrome-secondary-background-color", "background-image": "none"}],
]],
["space_above_tabbar", [
["#main-window[tabsintitlebar][customizing]", {"--space-above-tabbar": "--space-above-tabbar"}],
]],
["tab_text", [
- [":root", {"--chrome-color": "--tab-selection-color"}],
+ [":root", {"--chrome-color": "--tabs-toolbar-color"}],
[".tabbrowser-tab[visuallyselected]", {"color": "--tab-selection-color"}],
["#tabbrowser-tabs, #TabsToolbar, #browser-panel", {"color": "--chrome-color"}],
["#navigator-toolbox > toolbar:not(#TabsToolbar):not(#toolbar-menubar), .browserContainer > findbar, #browser-bottombox",
{"color": "--chrome-color"}],
["#navigator-toolbox .toolbarbutton-1, .browserContainer > findbar .findbar-button, #PlacesToolbar toolbarbutton.bookmark-item",
{"color": "--chrome-color"}],
// Using toolbar[brighttext] instead of important to override linux:
["toolbar[brighttext] #downloads-indicator-counter", {"color": "--chrome-color"}],
@@ -99,16 +113,26 @@ const kBrowserOverrideColorsVarMap = new
"background-image": "none",
"color": "inherit",
"border": "1px solid",
"border-color": "--chrome-nav-bar-controls-border-color",
"box-shadow": "none",
}],
]],
]);
+const kBrowserOverrideImagesVarMap = new Map([
+ ["theme_toolbar", [
+ ["#navigator-toolbox > toolbar:not(#TabsToolbar):-moz-lwtheme",
+ {"background-image": "--toolbar-background-image"}],
+ ["#navigator-toolbox > toolbar:not(#toolbar-menubar):not(#TabsToolbar)",
+ {"background-image": "--toolbar-background-image"}],
+ ["#navigator-toolbox > toolbar:not(#toolbar-menubar):not(#TabsToolbar):not(#addon-bar):-moz-lwtheme",
+ {"background-image": "--toolbar-background-image"}],
+ ]],
+]);
const kBrowserOverrideTintsVarMap = new Map([
["background_tab", [
[".tabbrowser-arrowscrollbox > .scrollbutton-down:not([disabled]):hover, " +
".tabbrowser-arrowscrollbox > .scrollbutton-up:not([disabled]):hover, .tabbrowser-tab:hover:not([visuallyselected])",
{"background-color": "--tab-hover-background-color"}],
[".tabs-newtab-button:hover", {"background-color": "--tab-hover-background-color", "background-image": "none"}],
]],
]);
@@ -125,16 +149,17 @@ const kBrowserOverridePropertiesVarMap =
]],
["space_above_tabbar", [
// Give some space to drag the window around while customizing (normal space
// to left and right of tabs doesn't work in this case)
["#main-window[tabsintitlebar][customizing]", {"--space-above-tabbar": "9px"}],
]],
]);
const kTransparentGif = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
+const kVideoElementID = "__WebExtThemeVideoElement__";
function hueToRgb(p, q, t) {
if (t < 0) {
t += 1;
}
if (t > 1) {
t -= 1;
}
@@ -199,23 +224,80 @@ function addToBrowserOverrides(browserSt
value = value.replace(/\$\{val(:?ue)?\}/g, propValue);
}
}
browserStyleOverrides[selector].push(`${style}: ${value}`);
}
}
}
+var gContentScriptLoaded = false;
+var gContentScript = `function() {
+ var Theme = {
+ init() {
+ addEventListener("DOMContentLoaded", this);
+ addEventListener("pagehide", this);
+ addEventListener("pageshow", this);
+ addMessageListener("Ext:Theme:Render", this);
+ },
+
+ handleEvent(event) {
+ if (event.target != content.document) {
+ return;
+ }
+ let uri = content.document.documentURI;
+ if (uri != "about:home" && uri != "about:newtab") {
+ return;
+ }
+
+ switch (event.type) {
+ case "DOMContentLoaded":
+ sendAsyncMessage("Ext:Theme:RequestRender", {});
+ break;
+ case "pagehide":
+ if (this.video && !this.video.paused)
+ this.video.pause();
+ break;
+ case "pageshow":
+ if (this.video && this.video.paused)
+ this.video.play();
+ break;
+ }
+ },
+
+ receiveMessage(message) {
+ if (!message.data.video)
+ return;
+
+ if (this.video) {
+ try {
+ this.video.remove();
+ } catch (ex) {}
+ }
+
+ let document = content.document;
+ this.video = document.createElement("video");
+ this.video.autoplay = this.video.loop = this.video.muted = true;
+ document.mozSetImageElement("${kVideoElementID}", this.video);
+ this.video.src = message.data.video;
+ }
+ };
+ Theme.init();
+}`;
+
class Theme {
- constructor(manifest) {
+ constructor(manifest, baseURI) {
+ this.loadContentScript();
+ this.baseURI = baseURI;
this.userSheetURI = null;
this.LWTStyles = {};
this.aboutHomeCSSVars = {};
this.browserStyleOverrides = {};
this.cssVars = {};
+ this.ntp_video = null;
this.load(manifest.theme);
this.render();
}
load(theme) {
// Order of sections matters here!
if (theme.images) {
this.loadImages(theme.images);
@@ -236,77 +318,113 @@ class Theme {
loadColors(colors) {
for (let color of Object.getOwnPropertyNames(colors)) {
let val = colors[color];
// Since values are optional, they may be `null`.
if (val === null) {
continue;
}
+ let cssVarMap = this.cssVars;
+ if (color.startsWith("ntp_")) {
+ cssVarMap = this.aboutHomeCSSVars;
+ }
+
let cssColor = Array.isArray(val) ?
"rgb" + (val.length > 3 ? "a" : "") + "(" + val.join(",") + ")" : val;
switch (color) {
case "accentcolor":
case "frame":
this.LWTStyles.accentcolor = cssColor;
break;
case "tab_text":
case "textcolor":
this.LWTStyles.textcolor = cssColor;
break;
}
if (kChromeThemeColorsVarMap.has(color)) {
- addToCSSVars.apply(null, [this.cssVars, cssColor].concat(kChromeThemeColorsVarMap.get(color)));
+ addToCSSVars.apply(null, [cssVarMap, cssColor].concat(kChromeThemeColorsVarMap.get(color)));
}
if (kBrowserOverrideColorsVarMap.has(color)) {
addToBrowserOverrides(this.browserStyleOverrides, cssColor, kBrowserOverrideColorsVarMap.get(color));
}
}
}
+ loadContentScript() {
+ if (!gContentScriptLoaded) {
+ gContentScriptLoaded = true;
+ Services.mm.loadFrameScript("data:,(" + gContentScript + ")();", true);
+ }
+ Services.mm.addMessageListener("Ext:Theme:RequestRender", this);
+ }
+
loadGradients(gradients) {
for (let gradient of Object.getOwnPropertyNames(gradients || {})) {
let val = gradients[gradient];
// Since values are optional, they may be `null`.
if (val === null) {
continue;
}
if (kChromeThemeGradientsVarMap.has(gradient)) {
addToCSSVars.apply(null, [this.cssVars, val].concat(kChromeThemeGradientsVarMap.get(gradient)));
}
}
}
loadImages(images) {
// Use a temporary element to filter the CSS values that themes can provide.
- if (WindowManager.topWindow) {
- let temp = WindowManager.topWindow.document.createElement("temp");
- for (let image of Object.getOwnPropertyNames(images)) {
- if (!images[image]) {
- continue;
+ if (!WindowManager.topWindow) {
+ return;
+ }
+
+ let temp = WindowManager.topWindow.document.createElement("temp");
+ for (let image of Object.getOwnPropertyNames(images)) {
+ let val = images[image];
+ if (!val) {
+ continue;
+ }
+ val = this.baseURI.resolve(val);
+ let cssURL = 'url("' + val.replace(/"/g, '\\"') + '")';
+ if (image == "theme_ntp_background") {
+ if (val.endsWith(".ogv") || val.endsWith(".mp4") || val.endsWith(".webm")) {
+ this.ntp_video = val;
+ addToCSSVars(this.aboutHomeCSSVars, `-moz-element(#${kVideoElementID})`,
+ "--page-background-image", ":root");
+ } else {
+ temp.style.backgroundImage = cssURL;
+ addToCSSVars(this.aboutHomeCSSVars, `${temp.style.backgroundImage}`,
+ "--page-background-image", ":root");
}
- let cssURL = 'url("' + images[image].replace(/"/g, '\\"') + '")';
- if (image == "theme_ntp_background") {
- temp.style.background = cssURL;
- this.aboutHomeCSSVars["--page-background"] = `${temp.style.background} !important;`;
- } else if (image == "theme_frame" || image == "headerURL") {
- this.LWTStyles.headerURL = images[image];
- }
+ } else if (image == "theme_frame" || image == "headerURL") {
+ this.LWTStyles.headerURL = val;
+ } else if (image == "theme_toolbar") {
+ temp.style.backgroundImage = cssURL;
+ addToCSSVars(this.cssVars, `${temp.style.backgroundImage}`,
+ "--toolbar-background-image", ":root");
+ }
+ if (kBrowserOverrideImagesVarMap.has(image)) {
+ addToBrowserOverrides(this.browserStyleOverrides, cssURL, kBrowserOverrideImagesVarMap.get(image));
}
}
}
loadProperties(properties) {
for (let property of Object.getOwnPropertyNames(properties || {})) {
let val = properties[property];
// Since values are optional, they may be `null`.
if (val === null) {
continue;
}
+ let cssVarMap = this.cssVars;
+ if (property.startsWith("ntp_")) {
+ cssVarMap = this.aboutHomeCSSVars;
+ }
+
switch (property) {
case "space_above_tabbar":
if (val === 0 && kIsMac) {
addToBrowserOverrides(this.browserStyleOverrides, val + "px", [
// Include extra space on left/right for dragging since there is no
// space above the tabs.
["#main-window[tabsintitlebar] #TabsToolbar", {
"padding-left": "50px",
@@ -401,17 +519,17 @@ class Theme {
case "tab_curve_half_width":
case "toolbar_navbar_highlight_overlap":
case "toolbar_navbar_overlap":
val += "px";
break;
}
if (typeof val != "boolean") {
if (kThemePropertiesVarMap.has(property)) {
- addToCSSVars.apply(null, [this.cssVars, val].concat(kThemePropertiesVarMap.get(property)));
+ addToCSSVars.apply(null, [cssVarMap, val].concat(kThemePropertiesVarMap.get(property)));
}
if (kBrowserOverridePropertiesVarMap.has(property)) {
addToBrowserOverrides(this.browserStyleOverrides, val, kBrowserOverridePropertiesVarMap.get(property));
}
}
}
}
@@ -436,27 +554,50 @@ class Theme {
if (kBrowserOverrideTintsVarMap.has(tint)) {
addToBrowserOverrides(this.browserStyleOverrides, cssColor, kBrowserOverrideTintsVarMap.get(tint));
}
}
}
}
}
- render() {
- let browserStyles = [];
- if (Object.getOwnPropertyNames(this.aboutHomeCSSVars).length) {
+ receiveMessage(message) {
+ if (message.name != "Ext:Theme:RequestRender") {
+ return;
+ }
+
+ if (this.ntp_video) {
+ Services.mm.broadcastAsyncMessage("Ext:Theme:Render", {
+ video: this.ntp_video,
+ });
+ }
+ }
+
+ render({asUpdate} = {asUpdate: false}) {
+ let browserStyles = [`
+ @-moz-document url("about:home"),
+ url("about:newtab"),
+ url("chrome://browser/content/abouthome/aboutHome.xhtml"),
+ url("chrome://browser/content/newtab/newTab.xhtml") {`];
+ for (let selector of Object.getOwnPropertyNames(this.aboutHomeCSSVars)) {
browserStyles.push(`
- @-moz-document url("about:home"),
- url("chrome://browser/content/abouthome/aboutHome.xhtml") {
- :root {
- --page-background: ${this.aboutHomeCSSVars["--page-background"]}
- }
+ ${selector} {
+ ${this.aboutHomeCSSVars[selector].join(" !important;")} !important;
}`);
}
+ if (this.ntp_video) {
+ browserStyles.push(`
+ body {
+ background-size: 100% !important;
+ }`);
+ }
+ // Close the '@-moz-document' block.
+ browserStyles.push(`
+ }`);
+
for (let selector of Object.getOwnPropertyNames(this.cssVars)) {
browserStyles.push(`
${selector} {
${this.cssVars[selector].join(" !important;")} !important;
}`);
}
for (let selector of Object.getOwnPropertyNames(this.browserStyleOverrides)) {
let styles = this.browserStyleOverrides[selector];
@@ -465,40 +606,43 @@ class Theme {
}
browserStyles.push(`
${selector} {
${styles.join(" !important;")} !important;
}`);
}
let dataURL = `data:text/css,${browserStyles.join("")}`;
- if (this.userSheetURI) {
- styleSheetService.unregisterSheet(this.userSheetURI, styleSheetService.USER_SHEET);
+ let whichSheet = asUpdate ? "userSheetUpdateURI" : "userSheetURI";
+ if (this[whichSheet]) {
+ styleSheetService.unregisterSheet(this[whichSheet], styleSheetService.USER_SHEET);
}
-
- this.userSheetURI = ioService.newURI(dataURL.replace(/#/g, "%23"), null, null);
- styleSheetService.loadAndRegisterSheet(this.userSheetURI, styleSheetService.USER_SHEET);
+ this[whichSheet] = ioService.newURI(dataURL.replace(/#/g, "%23"), null, null);
+ styleSheetService.loadAndRegisterSheet(this[whichSheet], styleSheetService.USER_SHEET);
if (!this.LWTStyles.headerURL) {
this.LWTStyles.headerURL = kTransparentGif;
}
Services.obs.notifyObservers(null, "lightweight-theme-styling-update", JSON.stringify(this.LWTStyles));
}
shutdown() {
if (this.userSheetURI) {
styleSheetService.unregisterSheet(this.userSheetURI, styleSheetService.USER_SHEET);
}
+ if (this.userSheetUpdateURI) {
+ styleSheetService.unregisterSheet(this.userSheetUpdateURI, styleSheetService.USER_SHEET);
+ }
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));
+ themeMap.set(extension, new Theme(manifest, extension.baseURI));
});
extensions.on("shutdown", (type, extension) => {
if (themeMap.has(extension)) {
themeMap.get(extension).shutdown();
themeMap.delete(extension);
}
});
@@ -506,14 +650,14 @@ extensions.on("shutdown", (type, extensi
extensions.registerSchemaAPI("theme", "addon_parent", context => {
let {extension} = context;
return {
theme: {
update(details) {
let theme = themeMap.get(extension);
theme.load(details);
- theme.render();
+ theme.render({asUpdate: true});
return Promise.resolve();
},
},
};
});
--- a/browser/components/extensions/schemas/theme.json
+++ b/browser/components/extensions/schemas/theme.json
@@ -20,16 +20,20 @@
},
"theme_frame": {
"type": "string",
"optional": true
},
"theme_ntp_background": {
"type": "string",
"optional": true
+ },
+ "theme_toolbar": {
+ "type": "string",
+ "optional": true
}
}
},
"colors": {
"type": "object",
"optional": true,
"properties": {
"accentcolor": {
@@ -52,31 +56,112 @@
},
"background_tab_text": {
"type": "array",
"items": {
"type": "number"
},
"optional": true
},
+ "bookmark_text": {
+ "type": "array",
+ "items": {
+ "type": "number"
+ },
+ "optional": true
+ },
"button_background": {
"type": "array",
"items": {
"type": "number"
},
"optional": true
},
"frame": {
+ "type": "any",
+ "optional": true
+ },
+ "frame_inactive": {
+ "type": "array",
+ "items": {
+ "type": "number"
+ },
+ "optional": true
+ },
+ "ntp_background": {
+ "type": "array",
+ "items": {
+ "type": "number"
+ },
+ "optional": true
+ },
+ "ntp_background": {
+ "type": "array",
+ "items": {
+ "type": "number"
+ },
+ "optional": true
+ },
+ "ntp_header": {
+ "type": "array",
+ "items": {
+ "type": "number"
+ },
+ "optional": true
+ },
+ "ntp_link": {
"type": "array",
"items": {
"type": "number"
},
"optional": true
},
- "frame_inactive": {
+ "ntp_link_underline": {
+ "type": "array",
+ "items": {
+ "type": "number"
+ },
+ "optional": true
+ },
+ "ntp_section": {
+ "type": "array",
+ "items": {
+ "type": "number"
+ },
+ "optional": true
+ },
+ "ntp_section_link": {
+ "type": "array",
+ "items": {
+ "type": "number"
+ },
+ "optional": true
+ },
+ "ntp_section_link_underline": {
+ "type": "array",
+ "items": {
+ "type": "number"
+ },
+ "optional": true
+ },
+ "ntp_section_text": {
+ "type": "array",
+ "items": {
+ "type": "number"
+ },
+ "optional": true
+ },
+ "ntp_text": {
+ "type": "array",
+ "items": {
+ "type": "number"
+ },
+ "optional": true
+ },
+ "tab_background_text": {
"type": "array",
"items": {
"type": "number"
},
"optional": true
},
"tab_text": {
"type": "array",
@@ -164,16 +249,31 @@
"properties": {
"type": "object",
"optional": true,
"properties": {
"navbar_padding": {
"type": "number",
"optional": true
},
+ "ntp_background_alignment": {
+ "type": "string",
+ "enum": ["bottom", "center", "left", "right", "top"],
+ "optional": true
+ },
+ "ntp_background_repeat": {
+ "type": "string",
+ "enum": ["no-repeat", "repeat", "repeat-x", "repeat-y"],
+ "optional": true
+ },
+ "ntp_background_tiling": {
+ "type": "string",
+ "enum": ["no-repeat", "repeat", "repeat-x", "repeat-y"],
+ "optional": true
+ },
"space_above_tabbar": {
"type": "number",
"optional": true
},
"square_backbutton": {
"type": "boolean",
"optional": true
},
--- a/browser/components/extensions/test/browser/browser.ini
+++ b/browser/components/extensions/test/browser/browser.ini
@@ -91,16 +91,17 @@ tags = webextensions
[browser_ext_tabs_sendMessage.js]
[browser_ext_tabs_cookieStoreId.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_chromeThemeSupport.js]
[browser_ext_theme_devEdition.js]
+[browser_ext_theme_extreme.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_allowScriptsToClose.js]
[browser_ext_windows_create.js]
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/test/browser/browser_ext_theme_extreme.js
@@ -0,0 +1,142 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+"use strict";
+
+const inIDOMUtils = Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
+
+add_task(function* testExtremeThemeProperties() {
+ let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:home", true);
+
+ let manifest = {
+ manifest: {
+ "theme": {
+ "colors": {
+ "background_tab": [76, 158, 217], // #4c9ed9
+ "background_tab_inactive": [39, 43, 53], // #272b35
+ "background_tab_text": [245, 247, 250], // #F5F7FA
+ "button_background": [37, 44, 51], // #252C33
+ "frame": [39, 43, 53], // #272b35
+ "frame_inactive": [57, 63, 76], // #393F4C
+ "ntp_background": [255, 170, 255],
+ "ntp_text": [0, 85, 187],
+ "ntp_link": [170, 255, 255],
+ "ntp_link_underline": [0, 85, 187],
+ "ntp_header": [0, 85, 187],
+ "ntp_section": [0, 85, 187],
+ "ntp_section_text": [0, 85, 187],
+ "ntp_section_link": [0, 85, 187],
+ "ntp_section_link_underline": [0, 85, 187],
+ "tab_text": [245, 247, 250], // #f5f7fa
+ "toolbar": [39, 43, 53], // #272b35
+ "toolbar_bottom_separator": [0, 0, 0, 0.2], // rgba(0,0,0,.2)
+ "toolbar_button_stroke": [25, 33, 38, 0.8], // rgba(25,33,38,.8)
+ "toolbar_button_stroke_inactive": [25, 33, 38, 0.6], // rgba(25,33,38,.6)
+ "toolbar_input_control": [23, 27, 31], // #171B1F
+ "toolbar_input_control_stroke": [29, 35, 40], // #1D2328
+ "toolbar_top_separator": [0, 0, 0, 0.2], // rgba(0,0,0,.2)
+ "toolbar_vertical_separator": [95, 102, 112], // #5F6670
+ },
+ "tints": {
+ "background_tab": [0.56, 0.18, 0.04], // #07090a
+ "buttons": [0.64, 0.56, 0.22], // rgb(25,33,38)
+ },
+ "gradients": {
+ "toolbar_button": "rgba(25,33, 38,.6) linear-gradient(rgba(25,33,38,.6), rgba(25,33,38,.6)) padding-box",
+ "toolbar_button_pressed": "rgba(25,33,38,1) linear-gradient(rgba(25,33,38,1), rgba(25,33,38,1)) border-box",
+ },
+ "properties": {
+ "navbar_padding": 0,
+ "space_above_tabbar": 0,
+ "square_tabs": true,
+ "square_backbutton": true,
+ "toolbar_button_shadow": "none",
+ "toolbar_button_shadow_inactive": "none",
+ "toolbar_navbar_highlight_overlap": 0,
+ "toolbar_navbar_overlap": 0,
+ "toolbar_text_shadow": "none",
+ },
+ },
+ },
+ background() {
+ browser.test.onMessage.addListener((message) => {
+ if (message !== "update-theme") {
+ return;
+ }
+
+ const kGradient = ["#ffaaff linear-gradient(", "deg, #ffaaff, #aaffff)"];
+ const kTimeoutMs = 100;
+
+ let gDegrees = 135;
+
+ browser.theme.update({
+ "colors": {
+ "background_tab": [255, 255, 255, 0.6],
+ "background_tab_inactive": [255, 255, 255, 0.2],
+ "background_tab_text": [0, 85, 187],
+ "button_background": [255, 255, 255, 0.05],
+ "frame": kGradient.join(gDegrees),
+ "frame_inactive": [238, 68, 255, 0.1],
+ "tab_text": [0, 85, 187], // #0055bb
+ "toolbar": [255, 255, 255, .05],
+ "toolbar_bottom_separator": [238, 68, 255, .2], // #ee44ff
+ "toolbar_button_stroke": [238, 68, 255, .8], // #ee44ff
+ "toolbar_button_stroke_inactive": [238, 68, 255, .6], // #ee44ff
+ "toolbar_input_control": [255, 255, 255, .95],
+ "toolbar_input_control_stroke": [238, 68, 255], // #ee44ff
+ "toolbar_top_separator": [255, 255, 255, 0.2],
+ "toolbar_vertical_separator": [238, 68, 255], // #ee44ff
+ },
+ "tints": {
+ "background_tab": [0, 0, 1, 0.25],
+ "buttons": [0, 0, 1, 0.05],
+ },
+ "gradients": {
+ "toolbar_button": "none",
+ "toolbar_button_pressed": "none",
+ },
+ });
+
+ setTimeout(() => {
+ gDegrees++;
+ browser.theme.update({
+ colors: {frame: kGradient.join(gDegrees)},
+ });
+
+ browser.test.sendMessage("theme-updated");
+ }, kTimeoutMs);
+ });
+ },
+ };
+ let extension = ExtensionTestUtils.loadExtension(manifest);
+
+ yield extension.startup();
+
+ let document = tab.ownerDocument;
+ let window = document.defaultView;
+
+ let style = window.getComputedStyle(document.documentElement);
+
+ Assert.ok(style.backgroundImage, "Expected background image");
+ Assert.equal(style.color, "rgb(245, 247, 250)", "Expected correct text color");
+
+ extension.sendMessage("update-theme");
+ yield extension.awaitMessage("theme-updated");
+
+ style = window.getComputedStyle(document.documentElement);
+ // HALP! The following style fetch keeps returning the old, initial value. Why?
+ // Is this a bug?
+ // Assert.equal(style.backgroundImage, "linear-gradient(136deg, #ffaaff, #aaffff)",
+ // "Expected correct background image");
+ Assert.equal(style.color, "rgb(0, 85, 187)", "Expected correct text color");
+
+ // Pick a button to test updated colors.
+ let button = document.getElementById("PanelUI-menu-button");
+ inIDOMUtils.addPseudoClassLock(button, ":hover");
+ style = window.getComputedStyle(button);
+ Assert.equal(style.backgroundImage, "none", "Expected explicitly empty background");
+ inIDOMUtils.clearPseudoClassLocks(button);
+
+ yield extension.unload();
+
+ yield BrowserTestUtils.removeTab(tab);
+});
--- a/browser/themes/shared/newtab/newTab.inc.css
+++ b/browser/themes/shared/newtab/newTab.inc.css
@@ -1,16 +1,18 @@
/* 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/. */
:root {
-moz-appearance: none;
font-size: 75%;
background-color: transparent;
+ --section-link-color: #5c5c5c;
+ --section-link-hover-color: white;
}
/* UNDO */
#newtab-undo-container {
padding: 4px 3px;
border: 1px solid;
border-color: rgba(8,22,37,.12) rgba(8,22,37,.14) rgba(8,22,37,.16);
background-color: rgba(255,255,255,.4);
@@ -165,30 +167,30 @@
background-position: center center;
background-size: auto;
}
/* TITLES */
.newtab-sponsored,
.newtab-title,
.newtab-suggested {
- color: #5c5c5c;
+ color: var(--section-link-color);
}
.newtab-suggested[active] {
background-color: rgba(51, 51, 51, 0.95);
border: 0;
color: white;
}
.newtab-site:hover .newtab-title {
- color: white;
+ color: var(--section-link-hover-color);
background-color: #333;
border: 1px solid #333;
- border-top: 1px solid white;
+ border-top: 1px solid var(--section-link-hover-color);
}
.newtab-site[pinned] .newtab-title {
padding-inline-start: 24px;
}
.newtab-site[pinned] .newtab-title::before {
background-image: -moz-image-rect(url("chrome://browser/skin/newtab/controls.svg"), 7, 278, 28, 266);
--- a/toolkit/modules/LightweightThemeConsumer.jsm
+++ b/toolkit/modules/LightweightThemeConsumer.jsm
@@ -111,20 +111,20 @@ LightweightThemeConsumer.prototype = {
let root = this._doc.documentElement;
let active = !!aData.headerURL;
let stateChanging = (active != this._active);
// We need to clear these either way: either because the theme is being removed,
// or because we are applying a new theme and the data might be bogus CSS,
// so if we don't reset first, it'll keep the old value.
root.style.removeProperty("color");
- root.style.removeProperty("background-color");
+ root.style.removeProperty("background");
if (active) {
root.style.color = aData.textcolor || "black";
- root.style.backgroundColor = aData.accentcolor || "white";
+ root.style.background = aData.accentcolor || "white";
let [r, g, b] = _parseRGB(this._doc.defaultView.getComputedStyle(root, "").color);
let luminance = 0.2125 * r + 0.7154 * g + 0.0721 * b;
root.setAttribute("lwthemetextcolor", luminance <= 110 ? "dark" : "bright");
root.setAttribute("lwtheme", "true");
} else {
root.removeAttribute("lwthemetextcolor");
root.removeAttribute("lwtheme");
}