
henry pushed to branch mullvad-browser-140.4.0esr-15.0-1 at The Tor Project / Applications / Mullvad Browser Commits: 81be023b by Henry Wilkes at 2025-10-15T18:07:04+01:00 fixup! BB 41919: Letterboxing, add temporarily visible web content-size indicator on window resizing. TB 44214: Fix letterboxing status indicator for RTL. - - - - - deed64ab by Henry Wilkes at 2025-10-15T18:07:05+01:00 fixup! BB 32308: Use direct browser sizing for letterboxing. TB 44214: Drop unnecessary CSS rules. - - - - - f18fc8f5 by Henry Wilkes at 2025-10-15T18:07:06+01:00 fixup! BB 32308: Use direct browser sizing for letterboxing. TB 44214: Use CSS logical positions for the status panel, rather than "left" and "right". - - - - - 77ea2cff by Henry Wilkes at 2025-10-15T18:07:06+01:00 fixup! BB 32308: Use direct browser sizing for letterboxing. TB 44214: Drop upstream's rules for placing content. - - - - - 725f9b44 by Henry Wilkes at 2025-10-15T18:07:07+01:00 fixup! BB 32308: Use direct browser sizing for letterboxing. TB 44214: Drop letterboxing gradient. - - - - - c5acc4f7 by Henry Wilkes at 2025-10-15T18:07:08+01:00 fixup! BB 32308: Use direct browser sizing for letterboxing. TB 44214: Separate out the essential --letterboxing-width and --letterboxing-height rules into their own .letterboxing block, to have the property values set on. - - - - - d3f14e86 by Henry Wilkes at 2025-10-15T18:07:09+01:00 fixup! BB 32308: Use direct browser sizing for letterboxing. TB 44214: Use CSS nesting. Part 1. - - - - - f622ced5 by Henry Wilkes at 2025-10-15T18:07:09+01:00 fixup! BB 32308: Use direct browser sizing for letterboxing. TB 44214: Use CSS nesting. Part 2. - - - - - 9bd8b0b8 by Henry Wilkes at 2025-10-15T18:07:10+01:00 fixup! BB 32308: Use direct browser sizing for letterboxing. TB 44214: Move the letterboxing classes one element up from tabpanels to tabbox. This is because we need to restyle the tabbox. - - - - - f6b94a03 by Henry Wilkes at 2025-10-15T18:07:11+01:00 fixup! BB 32308: Use direct browser sizing for letterboxing. TB 44214: Use a CSS class to show/hide the letterboxing border, rather than setting the border-radius in javascript. - - - - - 9f0d03ed by Henry Wilkes at 2025-10-15T18:07:12+01:00 fixup! BB 32308: Use direct browser sizing for letterboxing. TB 44214: Rename CSS variable from top-radius to radius-top. This is closer to what upstream has done recently for tokens, where higher specificity is appended. - - - - - a11336fe by Henry Wilkes at 2025-10-15T18:07:12+01:00 fixup! BB 32308: Use direct browser sizing for letterboxing. TB 44214: Update letterboxing styling for ESR 140. - - - - - 4 changed files: - browser/base/content/browser-fullScreenAndPointerLock.js - browser/themes/shared/tabbrowser/content-area.css - toolkit/components/resistfingerprinting/RFPHelper.sys.mjs - toolkit/components/resistfingerprinting/content/letterboxing.css Changes: ===================================== browser/base/content/browser-fullScreenAndPointerLock.js ===================================== @@ -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); }, ===================================== browser/themes/shared/tabbrowser/content-area.css ===================================== @@ -242,13 +242,17 @@ } } -#statuspanel[type=letterboxingStatus] > #statuspanel-label, -#statuspanel[previoustype=letterboxingStatus][inactive] > #statuspanel-label { +#statuspanel:is([type=letterboxingStatus], [previoustype=letterboxingStatus][inactive]) > #statuspanel-label { background-image: url("chrome://browser/skin/window.svg"); background-size: 1em; background-repeat: no-repeat; - background-position-x: .5em; background-position-y: center; + background-position-x: left .5em; + + &:-moz-locale-dir(rtl) { + background-position-x: right .5em; + } + padding-inline-start: 2em; -moz-context-properties: fill; fill: var(--color-accent-primary); ===================================== toolkit/components/resistfingerprinting/RFPHelper.sys.mjs ===================================== @@ -18,8 +18,6 @@ const kPrefLetterboxingTesting = "privacy.resistFingerprinting.letterboxing.testing"; const kPrefLetterboxingVcenter = "privacy.resistFingerprinting.letterboxing.vcenter"; -const kPrefLetterboxingGradient = - "privacy.resistFingerprinting.letterboxing.gradient"; const kPrefLetterboxingDidForceSize = "privacy.resistFingerprinting.letterboxing.didForceSize"; const kPrefLetterboxingRememberSize = @@ -28,10 +26,17 @@ const kPrefLetterboxingRememberSize = const kTopicDOMWindowOpened = "domwindowopened"; const kTopicDOMWindowClosed = "domwindowclosed"; +const kTopicFullscreenNavToolbox = "fullscreen-nav-toolbox"; + const kPrefResizeWarnings = "privacy.resistFingerprinting.resizeWarnings"; +const kPrefVerticalTabs = "sidebar.verticalTabs"; + const lazy = {}; +ChromeUtils.defineESModuleGetters(lazy, { + Color: "resource://gre/modules/Color.sys.mjs", +}); ChromeUtils.defineLazyGetter(lazy, "logConsole", () => console.createInstance({ prefix: "RFPHelper", @@ -155,7 +160,8 @@ class _RFPHelper { Services.prefs.addObserver(kPrefResistFingerprinting, this); Services.prefs.addObserver(kPrefLetterboxing, this); Services.prefs.addObserver(kPrefLetterboxingVcenter, this); - Services.prefs.addObserver(kPrefLetterboxingGradient, this); + Services.prefs.addObserver(kPrefVerticalTabs, this); + Services.obs.addObserver(this, kTopicFullscreenNavToolbox); XPCOMUtils.defineLazyPreferenceGetter( this, @@ -188,9 +194,10 @@ class _RFPHelper { // Remove unconditional observers Services.prefs.removeObserver(kPrefResistFingerprinting, this); - Services.prefs.removeObserver(kPrefLetterboxingGradient, 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(); } @@ -212,6 +219,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; } @@ -226,6 +242,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; } @@ -245,9 +268,13 @@ class _RFPHelper { Services.prefs.clearUserPref(kPrefLetterboxingDidForceSize); // fall-through case kPrefLetterboxingVcenter: - case kPrefLetterboxingGradient: this._handleLetterboxingPrefChanged(); break; + case kPrefVerticalTabs: + if (this.letterboxingEnabled) { + forEachWindow(win => this._updateLetterboxingColors(win)); + } + break; default: break; } @@ -452,7 +479,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"; @@ -688,26 +715,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 (win.gBrowser.selectedBrowser == aBrowser) { const updateStatus = async args => { win.XULBrowserWindow.letterboxingStatus = args @@ -769,20 +792,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.tabpanels?.classList.toggle( + tabBrowser.tabbox.classList.add("letterboxing"); + tabBrowser.tabbox.classList.toggle( "letterboxing-vcenter", Services.prefs.getBoolPref(kPrefLetterboxingVcenter, false) ); - tabBrowser.tabpanels?.classList.toggle( - "letterboxing-gradient", - Services.prefs.getBoolPref(kPrefLetterboxingGradient, 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; @@ -791,7 +825,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"); if (!aWindow._rfpOriginalSize) { this._recordWindowSize(aWindow); } @@ -869,6 +903,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 + ); } _detachWindow(aWindow) { @@ -886,7 +1161,7 @@ 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) { @@ -896,6 +1171,9 @@ class _RFPHelper { aWindow.removeEventListener("dblclick", this._onWindowDoubleClick); delete aWindow.shrinkToLetterbox; aWindow.removeEventListener("sizemodechange", windowResizeHandler); + + aWindow.removeEventListener("nativethemechange", this); + this._updateLetterboxingColors(aWindow, false); } _handleDOMWindowOpened(win) { ===================================== toolkit/components/resistfingerprinting/content/letterboxing.css ===================================== @@ -7,17 +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); - --letterboxing-border-radius: 8px; - --letterboxing-border-top-radius: 0; + .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-color: rgba(12, 12, 13, 0.10); - --letterboxing-gradient-color1: var(--letterboxing-bgcolor); - --letterboxing-gradient-color2: color-mix(in srgb, var(--chrome-content-separator-color) 50%, var(--letterboxing-bgcolor)); - --letterboxing-border-color: var(--letterboxing-bgcolor); - --letterboxing-decorator-visibility: visible; + --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 { /* @@ -26,101 +115,71 @@ * doesn't get notified on horizontal shrinking. */ overflow: hidden; - background: var(--letterboxing-bgcolor); } - .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing) > browser { - box-shadow: 0 4px 8px 0 var(--letterboxing-shadow-color); - border-radius: var(--letterboxing-border-radius); - border-top-left-radius: var(--letterboxing-border-top-radius); - border-top-right-radius: var(--letterboxing-border-top-radius); - width: var(--letterboxing-width) !important; - height: var(--letterboxing-height) !important; - background: var(--letterboxing-gradient-color2); + &.letterboxing-vcenter { + --letterboxing-border-radius-top: var(--letterboxing-border-radius); + --letterboxing-vertical-alignment: center; } } -: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; -} +.browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing) { + :root:not([inDOMFullscreen]) .letterboxing.letterboxing-ready & { + place-content: var(--letterboxing-vertical-alignment) center; + } -.letterboxing.letterboxing-vcenter .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing) { - --letterboxing-border-top-radius: var(--letterboxing-border-radius); - --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)); + } -.letterboxing.letterboxing-gradient .browserContainer { - background: linear-gradient(283deg, var(--letterboxing-gradient-color1) 0%, var(--letterboxing-gradient-color2) 100%), var(--letterboxing-bgcolor); -} + .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); + } -:root:not([inDOMFullscreen]) .letterboxing .browserContainer:not(.responsive-mode) - > .browserStack:not(.exclude-letterboxing) - > .browserDecorator { - display: initial; - visibility: var(--letterboxing-decorator-visibility); - border-radius: var(--letterboxing-border-radius); - border-top-left-radius: var(--letterboxing-border-top-radius); - border-top-right-radius: var(--letterboxing-border-top-radius); - 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); - height: calc(var(--letterboxing-height) + 1px); - top: -1px; -} + #statuspanel:not([mirror]) #statuspanel-label { + border-end-start-radius: var(--letterboxing-border-radius); + } -.letterboxing-vcenter .browserDecorator { - height: auto !important; - top: 0 !important; -} + #statuspanel[mirror] #statuspanel-label { + border-end-end-radius: var(--letterboxing-border-radius); + } + } -/* - Align status bar with content. - TODO: switch to nested CSS selectors for conciseness when available (Firefox >= 117) -*/ -.letterboxing .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing) - > #statuspanel:not([hidden]) { - position: relative; - place-self: end left; - left: 0; - right: 0; - z-index: 2; - --letterboxing-status-left-radius: var(--letterboxing-border-radius); - --letterboxing-status-right-radius: 0; -} -.letterboxing .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing) - > #statuspanel:not([mirror]):-moz-locale-dir(rtl), -.letterboxing .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing) - > #statuspanel[mirror]:-moz-locale-dir(ltr) { - left: 0; - right: 0; - --letterboxing-status-right-radius: var(--letterboxing-border-radius); - --letterboxing-status-left-radius: 0; - justify-self: right; -} + #statuspanel { + position: relative; + place-self: end start; + z-index: 2; -.letterboxing .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing) -#statuspanel-label { - border-radius: 0 0 var(--letterboxing-status-right-radius) var(--letterboxing-status-left-radius); - margin: 0; - border: 1px solid var(--letterboxing-border-color); - max-width: calc(var(--letterboxing-width) * .5); -} + &[mirror] { + justify-self: end; + } + } -browser:fullscreen { - --letterboxing-border-top-radius: 0; - --letterboxing-border-radius: 0; + #statuspanel-label { + margin: 0; + outline: var(--letterboxing-outline-width) solid var(--letterboxing-outline-color); + max-width: calc(var(--letterboxing-width) * .5); + } } -:root:not([inDOMFullscreen]) .letterboxing.letterboxing-ready .browserContainer:not(.responsive-mode) - > .browserStack:not(.exclude-letterboxing) { - place-content: var(--letterboxing-vertical-alignment) center; +.browserDecorator { + display: none; + pointer-events: none; + background: transparent; + position: relative; + z-index: 1; } View it on GitLab: https://gitlab.torproject.org/tpo/applications/mullvad-browser/-/compare/42b... -- View it on GitLab: https://gitlab.torproject.org/tpo/applications/mullvad-browser/-/compare/42b... You're receiving this email because of your account on gitlab.torproject.org.