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 "--in-content-zap-gradient",
73 lwtProperty: "zap_gradient",
80 "--lwt-sidebar-background-color",
82 lwtProperty: "sidebar",
83 processColor(rgbaChannels) {
87 const { r, g, b } = rgbaChannels;
89 return `rgb(${r}, ${g}, ${b})`;
94 "--lwt-sidebar-text-color",
96 lwtProperty: "sidebar_text",
97 processColor(rgbaChannels, element) {
99 element.removeAttribute("lwt-sidebar");
103 // TODO(emilio): Can we share this code somehow with LightWeightThemeConsumer?
104 const { r, g, b, a } = rgbaChannels;
105 element.setAttribute(
107 _isTextColorDark(r, g, b) ? "light" : "dark"
109 return `rgba(${r}, ${g}, ${b}, ${a})`;
114 "--lwt-sidebar-highlight-background-color",
116 lwtProperty: "sidebar_highlight",
117 processColor(rgbaChannels, element) {
119 element.removeAttribute("lwt-sidebar-highlight");
122 element.setAttribute("lwt-sidebar-highlight", "true");
124 const { r, g, b, a } = rgbaChannels;
125 return `rgba(${r}, ${g}, ${b}, ${a})`;
130 "--lwt-sidebar-highlight-text-color",
132 lwtProperty: "sidebar_highlight_text",
138 * ContentThemeController handles theme updates sent by the frame script.
139 * To be able to use ContentThemeController, you must add your page to the whitelist
140 * in LightweightThemeChild.sys.mjs
142 const ContentThemeController = {
144 * Listen for theming updates from the LightweightThemeChild actor, and
145 * begin listening to changes in preferred color scheme.
148 addEventListener("LightweightTheme:Set", this);
150 // We don't sync default theme attributes in `init()`, as we may not have
151 // a root element to attach the attribute to yet. They will be set when
152 // the first LightweightTheme:Set event is delivered during pageshow.
153 prefersDarkQuery.addEventListener("change", this);
157 * Handle theme updates from the LightweightThemeChild actor or due to
158 * changes to the prefers-color-scheme media query.
159 * @param {Object} event object containing the theme or query update.
162 const root = document.documentElement;
164 if (event.type == "LightweightTheme:Set") {
165 let { data } = event.detail;
169 this._setProperties(root, data);
170 } else if (event.type == "change") {
171 // If a lightweight theme doesn't apply, update lwt-newtab-brighttext to
172 // reflect prefers-color-scheme.
173 if (!root.hasAttribute("lwt-newtab")) {
174 root.toggleAttribute("lwt-newtab-brighttext", event.matches);
180 * Set a CSS variable to a given value
181 * @param {Element} elem The element where the CSS variable should be added.
182 * @param {string} variableName The CSS variable to set.
183 * @param {string} value The new value of the CSS variable.
185 _setProperty(elem, variableName, value) {
187 elem.style.setProperty(variableName, value);
189 elem.style.removeProperty(variableName);
194 * Apply theme data to an element
195 * @param {Element} root The element where the properties should be applied.
196 * @param {Object} themeData The theme data.
198 _setProperties(elem, themeData) {
199 for (let [cssVarName, definition] of inContentVariableMap) {
200 const { lwtProperty, processColor } = definition;
201 let value = themeData[lwtProperty];
204 value = processColor(value, elem);
206 const { r, g, b, a } = value;
207 value = `rgba(${r}, ${g}, ${b}, ${a})`;
210 this._setProperty(elem, cssVarName, value);
214 ContentThemeController.init();