tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

contentTheme.js (5894B)


      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/. */
      4 
      5 "use strict";
      6 
      7 {
      8  const prefersDarkQuery = window.matchMedia("(prefers-color-scheme: dark)");
      9 
     10  function _isTextColorDark(r, g, b) {
     11    return 0.2125 * r + 0.7154 * g + 0.0721 * b <= 110;
     12  }
     13 
     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-background-card",
     37      {
     38        lwtProperty: "ntp_card_background",
     39      },
     40    ],
     41    [
     42      "--newtab-text-primary-color",
     43      {
     44        lwtProperty: "ntp_text",
     45        processColor(rgbaChannels, element) {
     46          element.toggleAttribute("lwt-newtab", !!rgbaChannels);
     47          if (!rgbaChannels) {
     48            element.toggleAttribute(
     49              "lwt-newtab-brighttext",
     50              prefersDarkQuery.matches
     51            );
     52            return null;
     53          }
     54 
     55          const { r, g, b, a } = rgbaChannels;
     56          let darkMode = !_isTextColorDark(r, g, b);
     57          element.toggleAttribute("lwt-newtab-brighttext", darkMode);
     58          return `rgba(${r}, ${g}, ${b}, ${a})`;
     59        },
     60      },
     61    ],
     62    [
     63      "--in-content-zap-gradient",
     64      {
     65        lwtProperty: "zap_gradient",
     66        processColor(value) {
     67          return value;
     68        },
     69      },
     70    ],
     71    [
     72      "--sidebar-background-color",
     73      {
     74        lwtProperty: "sidebar",
     75        processColor(rgbaChannels) {
     76          if (!rgbaChannels) {
     77            return null;
     78          }
     79          const { r, g, b } = rgbaChannels;
     80          // Drop alpha channel
     81          return `rgb(${r}, ${g}, ${b})`;
     82        },
     83      },
     84    ],
     85    [
     86      "--sidebar-text-color",
     87      {
     88        lwtProperty: "sidebar_text",
     89        processColor(rgbaChannels, element) {
     90          if (!rgbaChannels) {
     91            element.removeAttribute("lwt-sidebar");
     92            return null;
     93          }
     94 
     95          // TODO(emilio): Can we share this code somehow with LightWeightThemeConsumer?
     96          const { r, g, b, a } = rgbaChannels;
     97          element.setAttribute(
     98            "lwt-sidebar",
     99            _isTextColorDark(r, g, b) ? "light" : "dark"
    100          );
    101          return `rgba(${r}, ${g}, ${b}, ${a})`;
    102        },
    103      },
    104    ],
    105    [
    106      "--lwt-sidebar-highlight-background-color",
    107      {
    108        lwtProperty: "sidebar_highlight",
    109        processColor(rgbaChannels, element) {
    110          element.toggleAttribute("lwt-sidebar-highlight", !!rgbaChannels);
    111          if (!rgbaChannels) {
    112            return null;
    113          }
    114 
    115          const { r, g, b, a } = rgbaChannels;
    116          return `rgba(${r}, ${g}, ${b}, ${a})`;
    117        },
    118      },
    119    ],
    120    [
    121      "--lwt-sidebar-highlight-text-color",
    122      {
    123        lwtProperty: "sidebar_highlight_text",
    124      },
    125    ],
    126  ];
    127 
    128  /**
    129   * ContentThemeController handles theme updates sent by the frame script.
    130   * To be able to use ContentThemeController, you must add your page to the whitelist
    131   * in LightweightThemeChild.sys.mjs
    132   */
    133  const ContentThemeController = {
    134    /**
    135     * Listen for theming updates from the LightweightThemeChild actor, and
    136     * begin listening to changes in preferred color scheme.
    137     */
    138    init() {
    139      addEventListener("LightweightTheme:Set", this);
    140 
    141      // We don't sync default theme attributes in `init()`, as we may not have
    142      // a root element to attach the attribute to yet. They will be set when
    143      // the first LightweightTheme:Set event is delivered during pageshow.
    144      prefersDarkQuery.addEventListener("change", this);
    145    },
    146 
    147    /**
    148     * Handle theme updates from the LightweightThemeChild actor or due to
    149     * changes to the prefers-color-scheme media query.
    150     *
    151     * @param {object} event object containing the theme or query update.
    152     */
    153    handleEvent(event) {
    154      if (event.type == "LightweightTheme:Set") {
    155        this._setProperties(event.detail.data || {});
    156      } else if (event.type == "change") {
    157        const root = document.documentElement;
    158        // If a lightweight theme doesn't apply, update lwt-newtab-brighttext to
    159        // reflect prefers-color-scheme.
    160        if (!root.hasAttribute("lwt-newtab")) {
    161          root.toggleAttribute("lwt-newtab-brighttext", event.matches);
    162        }
    163      }
    164    },
    165 
    166    /**
    167     * Set a CSS variable to a given value
    168     *
    169     * @param {Element} elem The element where the CSS variable should be added.
    170     * @param {string} variableName The CSS variable to set.
    171     * @param {string} value The new value of the CSS variable.
    172     */
    173    _setProperty(elem, variableName, value) {
    174      if (value) {
    175        elem.style.setProperty(variableName, value);
    176      } else {
    177        elem.style.removeProperty(variableName);
    178      }
    179    },
    180 
    181    /**
    182     * Apply theme data to an element
    183     *
    184     * @param {object} themeData The theme data.
    185     */
    186    _setProperties(themeData) {
    187      const root = document.documentElement;
    188      for (let [cssVarName, definition] of inContentVariableMap) {
    189        const { lwtProperty, processColor } = definition;
    190        let value = themeData[lwtProperty];
    191 
    192        if (processColor) {
    193          value = processColor(value, root);
    194        } else if (value) {
    195          const { r, g, b, a } = value;
    196          value = `rgba(${r}, ${g}, ${b}, ${a})`;
    197        }
    198 
    199        this._setProperty(root, cssVarName, value);
    200      }
    201    },
    202  };
    203  ContentThemeController.init();
    204 }