Commit 9d24a583 authored by ma1's avatar ma1 Committed by brizental
Browse files

BB 32308: Use direct browser sizing for letterboxing.

Bug 30556: align letterboxing with 200x100 new win width stepping
parent 116d7bd0
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -508,6 +508,10 @@ pref("security.remote_settings.intermediates.enabled", false);
pref("dom.use_components_shim", false);
// Enable letterboxing
pref("privacy.resistFingerprinting.letterboxing", true);
// tor-browser#41917: Center letterboxed area vertically
pref("privacy.resistFingerprinting.letterboxing.vcenter", true);
// tor-browser#41917: Letterboxing gradient background
pref("privacy.resistFingerprinting.letterboxing.gradient", true);
// tor-browser#43402: Avoid a resize from the skeleton to the newwin size.
// Should be fixed in ESR-140 with Bug 1448423.
pref("browser.startup.blankWindow", false);
+4 −0
Original line number Diff line number Diff line
@@ -2410,6 +2410,10 @@
      stack.className = "browserStack";
      stack.appendChild(b);

      let decorator = document.createXULElement("hbox");
      decorator.className = "browserDecorator";
      stack.appendChild(decorator);

      let browserContainer = document.createXULElement("vbox");
      browserContainer.className = "browserContainer";
      browserContainer.appendChild(stack);
+5 −0
Original line number Diff line number Diff line
@@ -380,6 +380,11 @@ split-view-footer {
  position: absolute;
  inset: 0;

  /* Override the <stack> `grid-area: 1 / 1` rule with an `auto` placement.
   * Otherwise the .dialogStack start edges are placed relative to the
   * center-aligned grid items, rather than the grid's padding area. */
  grid-area: auto;

  /* Hide tab-modal dialogs when a window-modal one is up. */
  :root[window-modal-open] .browserStack > &,
  /* For some printing use cases we need to visually hide the dialog before
+320 −23
Original line number Diff line number Diff line
@@ -15,12 +15,21 @@ const kPrefLetterboxingDimensions =
  "privacy.resistFingerprinting.letterboxing.dimensions";
const kPrefLetterboxingTesting =
  "privacy.resistFingerprinting.letterboxing.testing";
const kPrefLetterboxingVcenter =
  "privacy.resistFingerprinting.letterboxing.vcenter";

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",
@@ -56,6 +65,10 @@ class _RFPHelper {
    // Add unconditional observers
    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,
      "_letterboxingDimensions",
@@ -87,7 +100,10 @@ class _RFPHelper {

    // Remove unconditional observers
    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 +125,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 +148,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;
    }
@@ -138,8 +170,14 @@ class _RFPHelper {
        this._handleSpoofEnglishChanged();
        break;
      case kPrefLetterboxing:
      case kPrefLetterboxingVcenter:
        this._handleLetterboxingPrefChanged();
        break;
      case kPrefVerticalTabs:
        if (this.letterboxingEnabled) {
          forEachWindow(win => this._updateLetterboxingColors(win));
        }
        break;
      default:
        break;
    }
@@ -346,7 +384,7 @@ class _RFPHelper {
    // If not already cached on the document object, traverse the CSSOM and
    // find the rule applying the default letterboxing styles to browsers
    // preemptively in order to beat race conditions on tab/window creation
    return (document._letterboxingMarginsRule ||= (() => {
    return (document._letterboxingDefaultRule ||= (() => {
      const LETTERBOX_CSS_SELECTOR = ".letterboxing";
      const LETTERBOX_CSS_URL =
        "chrome://global/content/resistfingerprinting/letterboxing.css";
@@ -553,26 +591,22 @@ class _RFPHelper {

    if (lastRoundedSize) {
      // Check whether the letterboxing margin is less than the border radius,
      // and if so flatten the borders.
      let borderRadius = parseInt(
        win
          .getComputedStyle(browserContainer)
          .getPropertyValue("--letterboxing-border-radius")
      // and if so do not show an outline.
      const gapVertical = parentHeight - lastRoundedSize.height;
      const gapHorizontal = parentWidth - lastRoundedSize.width;
      browserParent.classList.toggle(
        "letterboxing-show-outline",
        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 (
        borderRadius &&
        parentWidth - lastRoundedSize.width < borderRadius &&
        parentHeight - lastRoundedSize.height < borderRadius
      ) {
        borderRadius = 0;
      } else {
        borderRadius = "";
      }
      styleChanges.queueIfNeeded(browserParent, {
        "--letterboxing-decorator-visibility":
          borderRadius === 0 ? "hidden" : "",
        "--letterboxing-border-radius": borderRadius,
      });
    }

    // If the size of the content is already quantized, we do nothing.
@@ -605,12 +639,31 @@ class _RFPHelper {

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

  _updateSizeForTabsInWindow(aWindow) {
    let tabBrowser = aWindow.gBrowser;

    tabBrowser.tabpanels?.classList.add("letterboxing");
    tabBrowser.tabbox.classList.add("letterboxing");
    tabBrowser.tabbox.classList.toggle(
      "letterboxing-vcenter",
      Services.prefs.getBoolPref(kPrefLetterboxingVcenter, false)
    );
    if (this._letterboxingBorderRadius === undefined && tabBrowser.tabbox) {
      // Cache the value since it is not expected to change in a session for any
      // window.
      this._letterboxingBorderRadius = Math.ceil(
        parseFloat(
          aWindow
            .getComputedStyle(tabBrowser.tabbox)
            .getPropertyValue("--letterboxing-border-radius")
        )
      );
    }

    for (let tab of tabBrowser.tabs) {
      let browser = tab.linkedBrowser;
@@ -619,7 +672,7 @@ class _RFPHelper {
    // We need to add this class late because otherwise new windows get
    // maximized.
    aWindow.setTimeout(() => {
      tabBrowser.tabpanels?.classList.add("letterboxing-ready");
      tabBrowser.tabbox.classList.add("letterboxing-ready");
    });
  }

@@ -639,6 +692,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() {
@@ -670,13 +964,16 @@ class _RFPHelper {
    aWindow.removeEventListener("TabOpen", this);

    // revert tabpanel's style to default
    tabBrowser.tabpanels?.classList.remove("letterboxing");
    tabBrowser.tabbox.classList.remove("letterboxing");

    // and restore default size on each browser element
    for (let tab of tabBrowser.tabs) {
      let browser = tab.linkedBrowser;
      this._resetContentSize(browser);
    }

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

  _detachAllWindows() {
+164 −7
Original line number Diff line number Diff line
@@ -7,9 +7,106 @@
 * RFPHelper.sys.mjs (LETTERBOX_CSS_SELECTOR and LETTERBOX_CSS_URL,
 * respectively), where --letterboxing-width & --letterboxing-height are
 * actually set.
 * Keep this block first and separate to the rules that do not necessarily
 * require --letterboxing-width or --letterboxing-height.
 */
.letterboxing {
  --letterboxing-bgcolor: var(--tabpanel-background-color);
  .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing) > browser {
    width: var(--letterboxing-width) !important;
    height: var(--letterboxing-height) !important;
  }
}

#tabbrowser-tabbox.letterboxing {
  --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: 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.2);
    --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 {
    /*
@@ -18,15 +115,75 @@
     * doesn't get notified on horizontal shrinking.
     */
    overflow: hidden;
    background: var(--letterboxing-bgcolor);
  }

  .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing) > browser {
    width: var(--letterboxing-width) !important;
    height: var(--letterboxing-height) !important;
  &.letterboxing-vcenter {
    --letterboxing-border-radius-top: var(--letterboxing-border-radius);
    --letterboxing-vertical-alignment: center;
  }
}

.browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing) {
  :root:not([inDOMFullscreen]) .letterboxing.letterboxing-ready & {
    place-content: var(--letterboxing-vertical-alignment) center;
  }

  :root:not([inDOMFullscreen]) .letterboxing &.letterboxing-show-outline {
    browser {
      /* 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 {
      /* 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 {
      border-end-start-radius: var(--letterboxing-border-radius);
    }

    #statuspanel[mirror] #statuspanel-label {
      border-end-end-radius: var(--letterboxing-border-radius);
    }
  }

  #statuspanel {
    position: relative;
    place-self: end start;
    z-index: 2;

    &[mirror] {
      justify-self: end;
    }
  }

  #statuspanel-label {
    margin: 0;
    outline: var(--letterboxing-outline-width) solid var(--letterboxing-outline-color);
    max-width: calc(var(--letterboxing-width) * 0.5);
  }
}

:root:not([inDOMFullscreen]) .letterboxing.letterboxing-ready .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing) {
  place-content: start center;
.browserDecorator {
  display: none;
  pointer-events: none;
  background: transparent;
  position: relative;
  z-index: 1;
}