Commit 2b027164 authored by henry's avatar henry Committed by Pier Angelo Vendrame
Browse files

fixup! BB 32308: Use direct browser sizing for letterboxing.

TB 44214: Update letterboxing styling for ESR 140.
parent 050be65c
Loading
Loading
Loading
Loading
+14 −2
Original line number Diff line number Diff line
@@ -879,7 +879,13 @@ var FullScreen = {
    }

    this._isChromeCollapsed = false;
    Services.obs.notifyObservers(null, "fullscreen-nav-toolbox", "shown");
    // Need a subject to know which window this applies to.
    // Base browser patch can be dropped after bugzilla bug 1992036.
    Services.obs.notifyObservers(
      gNavToolbox,
      "fullscreen-nav-toolbox",
      "shown"
    );
  },

  hideNavToolbox(aAnimate = false) {
@@ -943,7 +949,13 @@ var FullScreen = {
    gNavToolbox.style.marginTop =
      -gNavToolbox.getBoundingClientRect().height + "px";
    this._isChromeCollapsed = true;
    Services.obs.notifyObservers(null, "fullscreen-nav-toolbox", "hidden");
    // Need a subject to know which window this applies to.
    // Base browser patch can be dropped after bugzilla bug 1880918.
    Services.obs.notifyObservers(
      gNavToolbox,
      "fullscreen-nav-toolbox",
      "hidden"
    );

    MousePosTracker.removeListener(this);
  },
+288 −1
Original line number Diff line number Diff line
@@ -21,8 +21,15 @@ const kPrefLetterboxingVcenter =
const kTopicDOMWindowOpened = "domwindowopened";
const kTopicDOMWindowClosed = "domwindowclosed";

const kTopicFullscreenNavToolbox = "fullscreen-nav-toolbox";

const kPrefVerticalTabs = "sidebar.verticalTabs";

const lazy = {};

ChromeUtils.defineESModuleGetters(lazy, {
  Color: "resource://gre/modules/Color.sys.mjs",
});
ChromeUtils.defineLazyGetter(lazy, "logConsole", () =>
  console.createInstance({
    prefix: "RFPHelper",
@@ -54,6 +61,8 @@ class _RFPHelper {
    Services.prefs.addObserver(kPrefResistFingerprinting, this);
    Services.prefs.addObserver(kPrefLetterboxing, this);
    Services.prefs.addObserver(kPrefLetterboxingVcenter, this);
    Services.prefs.addObserver(kPrefVerticalTabs, this);
    Services.obs.addObserver(this, kTopicFullscreenNavToolbox);

    XPCOMUtils.defineLazyPreferenceGetter(
      this,
@@ -88,6 +97,8 @@ class _RFPHelper {
    Services.prefs.removeObserver(kPrefResistFingerprinting, this);
    Services.prefs.removeObserver(kPrefLetterboxingVcenter, this);
    Services.prefs.removeObserver(kPrefLetterboxing, this);
    Services.prefs.removeObserver(kPrefVerticalTabs, this);
    Services.obs.removeObserver(this, kTopicFullscreenNavToolbox);
    // Remove the RFP observers, swallowing exceptions if they weren't present
    this._removeLanguagePrefObservers();
  }
@@ -109,6 +120,15 @@ class _RFPHelper {
      case kTopicDOMWindowClosed:
        this._handleDOMWindowClosed(subject);
        break;
      case kTopicFullscreenNavToolbox:
        // The `subject` is the gNavToolbox.
        // Record whether the toobox has been hidden when the browser (not
        // content) is in fullscreen.
        subject.ownerGlobal.gBrowser.tabbox.classList.toggle(
          "letterboxing-nav-toolbox-hidden",
          data === "hidden"
        );
        break;
      default:
        break;
    }
@@ -123,6 +143,13 @@ class _RFPHelper {
        resizeObserver.observe(browser.parentElement);
        break;
      }
      case "nativethemechange":
        // NOTE: "nativethemechange" seems to always be sent after
        // "windowlwthemeupdate". So all the lwtheme CSS properties should be
        // set to the new theme's values already, so we don't need to wait for
        // windowlwthemeupdate.
        this._updateLetterboxingColors(aMessage.currentTarget, true);
        break;
      default:
        break;
    }
@@ -141,6 +168,11 @@ class _RFPHelper {
      case kPrefLetterboxingVcenter:
        this._handleLetterboxingPrefChanged();
        break;
      case kPrefVerticalTabs:
        if (this.letterboxingEnabled) {
          forEachWindow(win => this._updateLetterboxingColors(win));
        }
        break;
      default:
        break;
    }
@@ -556,6 +588,14 @@ class _RFPHelper {
        gapVertical >= this._letterboxingBorderRadius ||
          gapHorizontal >= this._letterboxingBorderRadius
      );
      // When the Letterboxing area is top-aligned, only show the sidebar corner
      // if there is enough horizontal space.
      // The factor of 4 is from the horizontal centre-alignment and wanting
      // enough space for twice the corner radius.
      browserParent.classList.toggle(
        "letterboxing-show-sidebar-corner",
        gapHorizontal >= 4 * this._letterboxingBorderRadius
      );
    }

    // If the size of the content is already quantized, we do nothing.
@@ -588,7 +628,10 @@ class _RFPHelper {

  _resetContentSize(aBrowser) {
    aBrowser.parentElement.classList.add("exclude-letterboxing");
    aBrowser.parentElement.classList.remove("letterboxing-show-outline");
    aBrowser.parentElement.classList.remove(
      "letterboxing-show-outline",
      "letterboxing-show-sidebar-corner"
    );
  }

  _updateSizeForTabsInWindow(aWindow) {
@@ -638,6 +681,247 @@ class _RFPHelper {
    this._resizeObservers.set(aWindow, resizeObserver);
    // Rounding the content viewport.
    this._updateSizeForTabsInWindow(aWindow);

    this._updateLetterboxingColors(aWindow, true);
    aWindow.addEventListener("nativethemechange", this);
  }

  /**
   * Convert a CSS property to its RGBA value.
   *
   * @param {Window} win - The window for the element.
   * @param {CSSStyleDeclaration} style - The computed style for the element we
   *   want to grab the color from.
   * @param {string} property - The name of the property we want.
   *
   * @returns {InspectorRGBATuple} - The RGBA color. The "r", "g", "b" fields
   *   are relative to the 0-255 color range. The "a" field is in the 0-1 range.
   */
  _convertToRGBA(win, style, property) {
    let cssColor = style.getPropertyValue(property);
    if (!cssColor) {
      lazy.logConsole.error(`Missing color "${property}"`);
      return { r: 0, g: 0, b: 0, a: 0 };
    }
    const currentColorRegex =
      /(^|[^a-zA-Z0-9_-])currentColor($|[^a-zA-Z0-9_-])/g;
    if (currentColorRegex.test(cssColor)) {
      const currentColor = style.color;
      cssColor = cssColor.replace(currentColorRegex, (_, pre, post) => {
        return pre + currentColor + post;
      });
      lazy.logConsole.debug(
        "Replaced currentColor.",
        property,
        currentColor,
        cssColor
      );
    }
    /* Can drop the document argument after bugzilla bug 1973684 (142). */
    const colorRGBA = win.InspectorUtils.colorToRGBA(cssColor, win.document);
    if (!colorRGBA) {
      lazy.logConsole.error(
        `Failed to convert "${property}" color (${cssColor}) to RGBA`
      );
      return { r: 0, g: 0, b: 0, a: 0 };
    }
    return colorRGBA;
  }

  /**
   * Compose two colors with alpha values on top of each other.
   *
   * @param {InspectorRGBATuple} topRGBA - The color to place on the top.
   * @param {InspectorRGBATuple} bottomRGBA - The color to place on the bottom.
   *
   * @returns {InspectorRGBATuple} - The composed color.
   */
  _composeRGBA(topRGBA, bottomRGBA) {
    const topA = Math.max(0, Math.min(1, topRGBA.a));
    const bottomA = Math.max(0, Math.min(1, bottomRGBA.a));
    const a = topA + bottomA - topA * bottomA; // Should be 1 if either is 1.
    if (a === 0) {
      return { r: 0, g: 0, b: 0, a };
    }
    const ret = { a };
    for (const field of ["r", "g", "b"]) {
      ret[field] =
        (topRGBA[field] * topA + bottomRGBA[field] * bottomA * (1 - topA)) / a;
    }
    return ret;
  }

  /**
   * Calculate the urlbar's container opaque background color, removing any
   * transparency.
   *
   * @param {Window} win - The window to calculate the color for.
   * @param {CSSStyleDeclaration} style - The computed style for the #nav-bar
   *   element.
   *
   * @returns {InspectorRGBATuple} - The calculated color, which will be opaque.
   */
  _calculateUrlbarContainerColor(win, style) {
    let colorRGBA;
    if (!Services.prefs.getBoolPref(kPrefVerticalTabs)) {
      lazy.logConsole.debug("Toolbar background used.");
      colorRGBA = this._convertToRGBA(win, style, "--toolbar-bgcolor");
      if (colorRGBA.a === 1) {
        return colorRGBA;
      }
    } else {
      // The urlbar only has the toolbox colour.
      colorRGBA = { r: 0, g: 0, b: 0, a: 0 };
    }
    let toolboxHasBackgroundImage = false;
    const isLwTheme = win.document.documentElement.hasAttribute("lwtheme");
    if (isLwTheme) {
      for (const prop of ["--lwt-header-image", "--lwt-additional-images"]) {
        const headerImage = style.getPropertyValue(prop);
        if (headerImage && headerImage !== "none") {
          // The theme sets a background image behind the urlbar. No easy way to
          // derive a single colour from this.
          toolboxHasBackgroundImage = true;
          lazy.logConsole.debug(
            "Toolbox has background image.",
            prop,
            headerImage
          );
          break;
        }
      }
    }
    if (!toolboxHasBackgroundImage) {
      lazy.logConsole.debug("Toolbox background used.");
      colorRGBA = this._composeRGBA(
        colorRGBA,
        this._convertToRGBA(win, style, "--toolbox-bgcolor")
      );
      if (colorRGBA.a === 1) {
        return colorRGBA;
      }
    }

    // Determine whether the urlbar is dark.
    // At this point, the urlbar background has some transparency, likely on top
    // of an image.
    // We use the theme's text colour to figure out whether the urlbar
    // background is overall meant to be light or dark. Unlike the urlbar, we
    // expect this colour to be (almost) opaque.
    const textRGBA = this._convertToRGBA(win, style, "--toolbar-field-color");
    const textColor = new lazy.Color(textRGBA.r, textRGBA.g, textRGBA.b);
    if (textColor.relativeLuminance >= 0.5) {
      // Light text, so assume it has a dark background.
      // Combine with a generic opaque dark colour. Copied from "frame" for the
      // built-in dark theme.
      lazy.logConsole.debug("Generic dark background used.");
      const darkFrameRGBA = { r: 28, g: 27, b: 34, a: 1 };
      return this._composeRGBA(colorRGBA, darkFrameRGBA);
    }
    // Combine with an opaque light colour. Copied from "frame" for the built-in
    // light theme.
    lazy.logConsole.debug("Generic light background used.");
    const lightFrameRGBA = { r: 234, g: 234, b: 237, a: 1 };
    return this._composeRGBA(colorRGBA, lightFrameRGBA);
  }

  /**
   * Update the Letterboxing colors and related classes, or clear them if
   * Letterboxing is not enabled.
   *
   * @param {Window} win - The window to update the colors for.
   * @param {boolean} letterboxingEnabled - Whether Letterboxing is enabled.
   */
  _updateLetterboxingColors(win, letterboxingEnabled) {
    let urlbarBackgroundRGBA;
    let urlbarTextRGBA;
    let contentSeparatorRGBA;
    let urlbarBackgroundDark = false;
    let lowBackgroundOutlineContrast = false;

    if (letterboxingEnabled) {
      // Want the effective colour of various elements without any alpha values
      // so they can be used consistently.
      const navbarStyle = win.getComputedStyle(
        win.document.getElementById("nav-bar")
      );
      const containerRGBA = this._calculateUrlbarContainerColor(
        win,
        navbarStyle
      );
      urlbarBackgroundRGBA = this._composeRGBA(
        this._convertToRGBA(
          win,
          navbarStyle,
          "--toolbar-field-background-color"
        ),
        containerRGBA
      );
      urlbarTextRGBA = this._composeRGBA(
        this._convertToRGBA(win, navbarStyle, "--toolbar-field-color"),
        urlbarBackgroundRGBA
      );
      /* Separator between the urlbar container #nav-bar and the tabbox. */
      const tabboxStyle = win.getComputedStyle(win.gBrowser.tabbox);
      contentSeparatorRGBA = this._composeRGBA(
        this._convertToRGBA(
          win,
          tabboxStyle,
          "--chrome-content-separator-color"
        ),
        containerRGBA
      );
      const bgColor = new lazy.Color(
        urlbarBackgroundRGBA.r,
        urlbarBackgroundRGBA.g,
        urlbarBackgroundRGBA.b
      );
      const outlineColor = new lazy.Color(
        contentSeparatorRGBA.r,
        contentSeparatorRGBA.g,
        contentSeparatorRGBA.b
      );
      const contrastRatio = bgColor.contrastRatio(outlineColor);
      lazy.logConsole.debug(
        "Outline-background contrast ratio.",
        contrastRatio
      );
      urlbarBackgroundDark = bgColor.relativeLuminance < 0.5;
      /* Very low contrast ratio. For reference the default light theme has
       * a contrast ratio of ~1.1. */
      lowBackgroundOutlineContrast = contrastRatio < 1.05;
    }
    for (const { name, colorRGBA } of [
      {
        name: "--letterboxing-urlbar-text-color",
        colorRGBA: urlbarTextRGBA,
      },
      {
        name: "--letterboxing-urlbar-background-color",
        colorRGBA: urlbarBackgroundRGBA,
      },
      {
        name: "--letterboxing-content-separator-color",
        colorRGBA: contentSeparatorRGBA,
      },
    ]) {
      if (letterboxingEnabled) {
        win.gBrowser.tabbox.style.setProperty(
          name,
          `rgb(${colorRGBA.r}, ${colorRGBA.g}, ${colorRGBA.b})`
        );
      } else {
        win.gBrowser.tabbox.style.removeProperty(name);
      }
    }
    win.gBrowser.tabbox.classList.toggle(
      "letterboxing-urlbar-background-dark",
      urlbarBackgroundDark
    );
    win.gBrowser.tabbox.classList.toggle(
      "letterboxing-low-background-outline-contrast",
      lowBackgroundOutlineContrast
    );
  }

  _attachAllWindows() {
@@ -676,6 +960,9 @@ class _RFPHelper {
      let browser = tab.linkedBrowser;
      this._resetContentSize(browser);
    }

    aWindow.removeEventListener("nativethemechange", this);
    this._updateLetterboxingColors(aWindow, false);
  }

  _detachAllWindows() {
+103 −20
Original line number Diff line number Diff line
@@ -18,12 +18,95 @@
}

#tabbrowser-tabbox.letterboxing {
  --letterboxing-bgcolor: var(--tabpanel-background-color);
  --letterboxing-border-radius: 8px;
  --letterboxing-bgcolor: var(--background-color-canvas);
  /* Match the border radius used for the sidebar. */
  --letterboxing-border-radius: var(--border-radius-medium);
  --letterboxing-border-radius-top: 0;
  --letterboxing-vertical-alignment: start;
  --letterboxing-shadow-color: rgba(12, 12, 13, 0.10);
  --letterboxing-border-color: var(--letterboxing-bgcolor);
  --letterboxing-shadow: none;
  --letterboxing-outline-color: var(--border-color);
  --letterboxing-outline-width: 1px;

  @media not ((prefers-contrast) or (forced-colors)) {
    /* Match the #sidebar outline width. */
    --letterboxing-outline-width: 0.5px;
    --letterboxing-shadow-color: rgba(58, 57, 68, 0.20);
    --letterboxing-shadow: 0 2px 14px 0 var(--letterboxing-shadow-color);

    /* Match the effective urlbar background colour. */
    --letterboxing-bgcolor: var(--letterboxing-urlbar-background-color);
    /* Match the effective colour of the separator between the urlbar container
     * and the content. */
    --letterboxing-outline-color: var(--letterboxing-content-separator-color);

    &.letterboxing-urlbar-background-dark {
      --letterboxing-shadow-color: #15141a;
    }

    &.letterboxing-low-background-outline-contrast {
      /* The default content separator colour has insufficient contrast. */
      --letterboxing-outline-color: color-mix(in srgb, var(--letterboxing-content-separator-color) 90%, black);

      &.letterboxing-urlbar-background-dark {
        /* Lighten the colour. */
        --letterboxing-outline-color: color-mix(in srgb, var(--letterboxing-content-separator-color) 90%, white);
      }
    }
  }

  @media (prefers-contrast) and (not (forced-colors)) {
    :root[lwtheme] & {
      /* User with prefers-contrast coming from the system settings, but also an
       * installed theme. */
      --letterboxing-bgcolor: var(--letterboxing-urlbar-background-color);
      /* Presumably a user with prefers-contrast and a custom theme has chosen
       * a theme where the contrast between the urlbar and the text is
       * sufficiently high or low. */
      --letterboxing-outline-color: var(--letterboxing-urlbar-text-color);
    }
  }

  background: var(--letterboxing-bgcolor);

  &:has(.deck-selected .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing).letterboxing-show-outline) {
    /* Letterboxing outline is visible for the current tab. Replace the usual
     * outline to match the Letterboxing outline. For most scenarios, this
     * should be mostly the same colour as when Letterboxing is not visible. But
     * it may make a difference for some theme combinations. */
    outline-color: var(--letterboxing-outline-color);
    outline-width: var(--letterboxing-outline-width);
  }

  #tabbrowser-tabpanels {
    /* Override the --tabpanel-background-color.
     * Also, make sure this remains transparent, otherwise it will overlap the
     * parent's corner's border-radius due to it's "position: relative" rule. */
    /* TODO: FIX this for newtab pages. tor-browser#44085 */
    background: transparent;
  }

  /* stylelint-disable-next-line media-query-no-invalid */
  @media -moz-pref("sidebar.revamp") {
    :root:not([inDOMFullscreen]) &[sidebar-shown]:not(.letterboxing-nav-toolbox-hidden):is(
      /* When the Letterboxing area is aligned to the top, show the rounded
       * corner if there is enough vertical space between the sidebar and the
       * browser element, which is not rounded at the top. */
      :not(.letterboxing-vcenter):has(.deck-selected .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing).letterboxing-show-sidebar-corner),
      /* When the Letterboxing area is aligned to the centre, show the rounded
       * corner if the Letterboxing border is shown. */
      .letterboxing-vcenter:has(.deck-selected .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing).letterboxing-show-outline)
    ) {
      /* stylelint-disable-next-line media-query-no-invalid */
      @media -moz-pref("sidebar.position_start") {
        border-start-start-radius: var(--letterboxing-border-radius);
      }

      /* stylelint-disable-next-line media-query-no-invalid */
      @media not -moz-pref("sidebar.position_start") {
        border-start-end-radius: var(--letterboxing-border-radius);
      }
    }
  }

  .browserContainer {
    /*
@@ -32,7 +115,6 @@
     * doesn't get notified on horizontal shrinking.
     */
    overflow: hidden;
    background: var(--letterboxing-bgcolor);
  }

  &.letterboxing-vcenter {
@@ -42,29 +124,30 @@
}

.browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing) {
  .letterboxing & browser {
    box-shadow: 0 4px 8px 0 var(--letterboxing-shadow-color);
  }

  :root:not([inDOMFullscreen]) .letterboxing.letterboxing-ready & {
    place-content: var(--letterboxing-vertical-alignment) center;
  }

  :root:not([inDOMFullscreen]) .letterboxing &.letterboxing-show-outline {
    browser {
      border-radius: var(--letterboxing-border-radius);
      border-top-left-radius: var(--letterboxing-border-radius-top);
      border-top-right-radius: var(--letterboxing-border-radius-top);
      /* We use clip-path rather than border-radius because border-radius on its
       * own leads to rendering artefacts in the corners (tested with GNOME).
       * See tor-browser#44214 (comment 3262962). */
      /* TODO: Use border-radius once bugzilla bug 1991874 is resolved. */
      clip-path: rect(auto auto auto auto round var(--letterboxing-border-radius-top) var(--letterboxing-border-radius-top) var(--letterboxing-border-radius) var(--letterboxing-border-radius));
    }

    .browserDecorator {
      display: initial;
      border-radius: var(--letterboxing-border-radius);
      border-top-left-radius: var(--letterboxing-border-radius-top);
      border-top-right-radius: var(--letterboxing-border-radius-top);
      box-shadow: var(--letterboxing-border-color) 0 0 .1px inset, var(--letterboxing-border-color) 0 0 .1px;
      border: .1px solid var(--letterboxing-border-color);
      outline: .1px solid var(--letterboxing-bgcolor);
      /* Need a separate browserDecorator element because the clip-path on the
       * browser would exclude the outline and box-shadow. */
      /* TODO: Move these rules to the browser element once bugzilla bug 1991874
       * is resolved, and drop browserDecorator. */
      display: block;
      border-radius: var(--letterboxing-border-radius-top) var(--letterboxing-border-radius-top) var(--letterboxing-border-radius) var(--letterboxing-border-radius);
      /* NOTE: The top outline will not be visible when this is aligned to the
       * top. */
      outline: var(--letterboxing-outline-width) solid var(--letterboxing-outline-color);
      box-shadow: var(--letterboxing-shadow);
    }

    #statuspanel:not([mirror]) #statuspanel-label {
@@ -88,7 +171,7 @@

  #statuspanel-label {
    margin: 0;
    border: 1px solid var(--letterboxing-border-color);
    outline: var(--letterboxing-outline-width) solid var(--letterboxing-outline-color);
    max-width: calc(var(--letterboxing-width) * .5);
  }
}