Bug 1791785 - When dumping symbols never emit INLINE_ORIGIN directives with an empty...
[gecko.git] / browser / base / content / contentTheme.js
blobd99ac555390d9437308edc4442e6eac361fb35a6
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/. */
5 "use strict";
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;
12   }
14   const inContentVariableMap = [
15     [
16       "--newtab-background-color",
17       {
18         lwtProperty: "ntp_background",
19         processColor(rgbaChannels) {
20           if (!rgbaChannels) {
21             return null;
22           }
23           const { r, g, b } = rgbaChannels;
24           // Drop alpha channel
25           return `rgb(${r}, ${g}, ${b})`;
26         },
27       },
28     ],
29     [
30       "--newtab-background-color-secondary",
31       {
32         lwtProperty: "ntp_card_background",
33       },
34     ],
35     [
36       "--newtab-text-primary-color",
37       {
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.
43           let browserStyle =
44             element.ownerGlobal?.docShell?.chromeEventHandler.style;
46           if (!rgbaChannels) {
47             element.removeAttribute("lwt-newtab");
48             element.toggleAttribute(
49               "lwt-newtab-brighttext",
50               prefersDarkQuery.matches
51             );
52             if (browserStyle) {
53               browserStyle.colorScheme = "";
54             }
55             return null;
56           }
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);
62           if (browserStyle) {
63             browserStyle.colorScheme = darkMode ? "dark" : "light";
64           }
66           return `rgba(${r}, ${g}, ${b}, ${a})`;
67         },
68       },
69     ],
70     [
71       "--lwt-sidebar-background-color",
72       {
73         lwtProperty: "sidebar",
74         processColor(rgbaChannels) {
75           if (!rgbaChannels) {
76             return null;
77           }
78           const { r, g, b } = rgbaChannels;
79           // Drop alpha channel
80           return `rgb(${r}, ${g}, ${b})`;
81         },
82       },
83     ],
84     [
85       "--lwt-sidebar-text-color",
86       {
87         lwtProperty: "sidebar_text",
88         processColor(rgbaChannels, element) {
89           if (!rgbaChannels) {
90             element.removeAttribute("lwt-sidebar");
91             element.removeAttribute("lwt-sidebar-brighttext");
92             return null;
93           }
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");
99           } else {
100             element.removeAttribute("lwt-sidebar-brighttext");
101           }
103           return `rgba(${r}, ${g}, ${b}, ${a})`;
104         },
105       },
106     ],
107     [
108       "--lwt-sidebar-highlight-background-color",
109       {
110         lwtProperty: "sidebar_highlight",
111         processColor(rgbaChannels, element) {
112           if (!rgbaChannels) {
113             element.removeAttribute("lwt-sidebar-highlight");
114             return null;
115           }
116           element.setAttribute("lwt-sidebar-highlight", "true");
118           const { r, g, b, a } = rgbaChannels;
119           return `rgba(${r}, ${g}, ${b}, ${a})`;
120         },
121       },
122     ],
123     [
124       "--lwt-sidebar-highlight-text-color",
125       {
126         lwtProperty: "sidebar_highlight_text",
127       },
128     ],
129   ];
131   /**
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
135    */
136   const ContentThemeController = {
137     /**
138      * Listen for theming updates from the LightweightThemeChild actor, and
139      * begin listening to changes in preferred color scheme.
140      */
141     init() {
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);
148     },
150     /**
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.
154      */
155     handleEvent(event) {
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;
161         if (!data) {
162           data = {};
163         }
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);
170         }
171       }
172     },
174     /**
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.
179      */
180     _setProperty(elem, variableName, value) {
181       if (value) {
182         elem.style.setProperty(variableName, value);
183       } else {
184         elem.style.removeProperty(variableName);
185       }
186     },
188     /**
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.
192      */
193     _setProperties(elem, themeData) {
194       for (let [cssVarName, definition] of inContentVariableMap) {
195         const { lwtProperty, processColor } = definition;
196         let value = themeData[lwtProperty];
198         if (processColor) {
199           value = processColor(value, elem);
200         } else if (value) {
201           const { r, g, b, a } = value;
202           value = `rgba(${r}, ${g}, ${b}, ${a})`;
203         }
205         this._setProperty(elem, cssVarName, value);
206       }
207     },
208   };
209   ContentThemeController.init();