1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
8 const prefersDarkQuery = window.matchMedia("(prefers-color-scheme: dark)");
10 function _isTextColorDark(r, g, b) {
11 return 0.2125 * r + 0.7154 * g + 0.0721 * b <= 110;
14 const inContentVariableMap = [
16 "--newtab-background-color",
18 lwtProperty: "ntp_background",
19 processColor(rgbaChannels) {
23 const { r, g, b } = rgbaChannels;
25 return `rgb(${r}, ${g}, ${b})`;
30 "--newtab-background-color-secondary",
32 lwtProperty: "ntp_card_background",
36 "--newtab-text-primary-color",
38 lwtProperty: "ntp_text",
39 processColor(rgbaChannels, element) {
40 // We only have access to the browser when we're in a chrome
41 // docshell, so for now only set the color scheme in that case, and
42 // use the `lwt-newtab-brighttext` attribute as a fallback mechanism.
44 element.ownerGlobal?.docShell?.chromeEventHandler.style;
47 element.removeAttribute("lwt-newtab");
48 element.toggleAttribute(
49 "lwt-newtab-brighttext",
50 prefersDarkQuery.matches
53 browserStyle.colorScheme = "";
58 element.setAttribute("lwt-newtab", "true");
59 const { r, g, b, a } = rgbaChannels;
60 let darkMode = !_isTextColorDark(r, g, b);
61 element.toggleAttribute("lwt-newtab-brighttext", darkMode);
63 browserStyle.colorScheme = darkMode ? "dark" : "light";
66 return `rgba(${r}, ${g}, ${b}, ${a})`;
71 "--lwt-sidebar-background-color",
73 lwtProperty: "sidebar",
74 processColor(rgbaChannels) {
78 const { r, g, b } = rgbaChannels;
80 return `rgb(${r}, ${g}, ${b})`;
85 "--lwt-sidebar-text-color",
87 lwtProperty: "sidebar_text",
88 processColor(rgbaChannels, element) {
90 element.removeAttribute("lwt-sidebar");
91 element.removeAttribute("lwt-sidebar-brighttext");
95 element.setAttribute("lwt-sidebar", "true");
96 const { r, g, b, a } = rgbaChannels;
97 if (!_isTextColorDark(r, g, b)) {
98 element.setAttribute("lwt-sidebar-brighttext", "true");
100 element.removeAttribute("lwt-sidebar-brighttext");
103 return `rgba(${r}, ${g}, ${b}, ${a})`;
108 "--lwt-sidebar-highlight-background-color",
110 lwtProperty: "sidebar_highlight",
111 processColor(rgbaChannels, element) {
113 element.removeAttribute("lwt-sidebar-highlight");
116 element.setAttribute("lwt-sidebar-highlight", "true");
118 const { r, g, b, a } = rgbaChannels;
119 return `rgba(${r}, ${g}, ${b}, ${a})`;
124 "--lwt-sidebar-highlight-text-color",
126 lwtProperty: "sidebar_highlight_text",
132 * ContentThemeController handles theme updates sent by the frame script.
133 * To be able to use ContentThemeController, you must add your page to the whitelist
134 * in LightweightThemeChildListener.jsm
136 const ContentThemeController = {
138 * Listen for theming updates from the LightweightThemeChild actor, and
139 * begin listening to changes in preferred color scheme.
142 addEventListener("LightweightTheme:Set", this);
144 // We don't sync default theme attributes in `init()`, as we may not have
145 // a body element to attach the attribute to yet. They will be set when
146 // the first LightweightTheme:Set event is delivered during pageshow.
147 prefersDarkQuery.addEventListener("change", this);
151 * Handle theme updates from the LightweightThemeChild actor or due to
152 * changes to the prefers-color-scheme media query.
153 * @param {Object} event object containing the theme or query update.
156 // XUL documents don't have a body
157 const element = document.body ? document.body : document.documentElement;
159 if (event.type == "LightweightTheme:Set") {
160 let { data } = event.detail;
164 this._setProperties(element, data);
165 } else if (event.type == "change") {
166 // If a lightweight theme doesn't apply, update lwt-newtab-brighttext to
167 // reflect prefers-color-scheme.
168 if (!element.hasAttribute("lwt-newtab")) {
169 element.toggleAttribute("lwt-newtab-brighttext", event.matches);
175 * Set a CSS variable to a given value
176 * @param {Element} elem The element where the CSS variable should be added.
177 * @param {string} variableName The CSS variable to set.
178 * @param {string} value The new value of the CSS variable.
180 _setProperty(elem, variableName, value) {
182 elem.style.setProperty(variableName, value);
184 elem.style.removeProperty(variableName);
189 * Apply theme data to an element
190 * @param {Element} root The element where the properties should be applied.
191 * @param {Object} themeData The theme data.
193 _setProperties(elem, themeData) {
194 for (let [cssVarName, definition] of inContentVariableMap) {
195 const { lwtProperty, processColor } = definition;
196 let value = themeData[lwtProperty];
199 value = processColor(value, elem);
201 const { r, g, b, a } = value;
202 value = `rgba(${r}, ${g}, ${b}, ${a})`;
205 this._setProperty(elem, cssVarName, value);
209 ContentThemeController.init();