/* 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/. */ "use strict"; { const prefersDarkQuery = window.matchMedia("(prefers-color-scheme: dark)"); function _isTextColorDark(r, g, b) { return 0.2125 * r + 0.7154 * g + 0.0721 * b <= 110; } const inContentVariableMap = [ [ "--newtab-background-color", { lwtProperty: "ntp_background", processColor(rgbaChannels) { if (!rgbaChannels) { return null; } const { r, g, b } = rgbaChannels; // Drop alpha channel return `rgb(${r}, ${g}, ${b})`; }, }, ], [ "--newtab-background-color-secondary", { lwtProperty: "ntp_card_background", }, ], [ "--newtab-text-primary-color", { lwtProperty: "ntp_text", processColor(rgbaChannels, element) { // We only have access to the browser when we're in a chrome // docshell, so for now only set the color scheme in that case, and // use the `lwt-newtab-brighttext` attribute as a fallback mechanism. let browserStyle = element.ownerGlobal?.docShell?.chromeEventHandler.style; if (!rgbaChannels) { element.removeAttribute("lwt-newtab"); element.toggleAttribute( "lwt-newtab-brighttext", prefersDarkQuery.matches ); if (browserStyle) { browserStyle.colorScheme = ""; } return null; } element.setAttribute("lwt-newtab", "true"); const { r, g, b, a } = rgbaChannels; let darkMode = !_isTextColorDark(r, g, b); element.toggleAttribute("lwt-newtab-brighttext", darkMode); if (browserStyle) { browserStyle.colorScheme = darkMode ? "dark" : "light"; } return `rgba(${r}, ${g}, ${b}, ${a})`; }, }, ], [ "--in-content-zap-gradient", { lwtProperty: "zap_gradient", processColor(value) { return value; }, }, ], [ "--lwt-sidebar-background-color", { lwtProperty: "sidebar", processColor(rgbaChannels) { if (!rgbaChannels) { return null; } const { r, g, b } = rgbaChannels; // Drop alpha channel return `rgb(${r}, ${g}, ${b})`; }, }, ], [ "--lwt-sidebar-text-color", { lwtProperty: "sidebar_text", processColor(rgbaChannels, element) { if (!rgbaChannels) { element.removeAttribute("lwt-sidebar"); element.removeAttribute("lwt-sidebar-brighttext"); return null; } element.setAttribute("lwt-sidebar", "true"); const { r, g, b, a } = rgbaChannels; if (!_isTextColorDark(r, g, b)) { element.setAttribute("lwt-sidebar-brighttext", "true"); } else { element.removeAttribute("lwt-sidebar-brighttext"); } return `rgba(${r}, ${g}, ${b}, ${a})`; }, }, ], [ "--lwt-sidebar-highlight-background-color", { lwtProperty: "sidebar_highlight", processColor(rgbaChannels, element) { if (!rgbaChannels) { element.removeAttribute("lwt-sidebar-highlight"); return null; } element.setAttribute("lwt-sidebar-highlight", "true"); const { r, g, b, a } = rgbaChannels; return `rgba(${r}, ${g}, ${b}, ${a})`; }, }, ], [ "--lwt-sidebar-highlight-text-color", { lwtProperty: "sidebar_highlight_text", }, ], ]; /** * ContentThemeController handles theme updates sent by the frame script. * To be able to use ContentThemeController, you must add your page to the whitelist * in LightweightThemeChildListener.jsm */ const ContentThemeController = { /** * Listen for theming updates from the LightweightThemeChild actor, and * begin listening to changes in preferred color scheme. */ init() { addEventListener("LightweightTheme:Set", this); // We don't sync default theme attributes in `init()`, as we may not have // a root element to attach the attribute to yet. They will be set when // the first LightweightTheme:Set event is delivered during pageshow. prefersDarkQuery.addEventListener("change", this); }, /** * Handle theme updates from the LightweightThemeChild actor or due to * changes to the prefers-color-scheme media query. * @param {Object} event object containing the theme or query update. */ handleEvent(event) { const root = document.documentElement; if (event.type == "LightweightTheme:Set") { let { data } = event.detail; if (!data) { data = {}; } this._setProperties(root, data); } else if (event.type == "change") { // If a lightweight theme doesn't apply, update lwt-newtab-brighttext to // reflect prefers-color-scheme. if (!root.hasAttribute("lwt-newtab")) { root.toggleAttribute("lwt-newtab-brighttext", event.matches); } } }, /** * Set a CSS variable to a given value * @param {Element} elem The element where the CSS variable should be added. * @param {string} variableName The CSS variable to set. * @param {string} value The new value of the CSS variable. */ _setProperty(elem, variableName, value) { if (value) { elem.style.setProperty(variableName, value); } else { elem.style.removeProperty(variableName); } }, /** * Apply theme data to an element * @param {Element} root The element where the properties should be applied. * @param {Object} themeData The theme data. */ _setProperties(elem, themeData) { for (let [cssVarName, definition] of inContentVariableMap) { const { lwtProperty, processColor } = definition; let value = themeData[lwtProperty]; if (processColor) { value = processColor(value, elem); } else if (value) { const { r, g, b, a } = value; value = `rgba(${r}, ${g}, ${b}, ${a})`; } this._setProperty(elem, cssVarName, value); } }, }; ContentThemeController.init(); }