lists.torproject.org
Sign In Sign Up
Manage this list Sign In Sign Up

Keyboard Shortcuts

Thread View

  • j: Next unread message
  • k: Previous unread message
  • j a: Jump to all threads
  • j l: Jump to MailingList overview

tbb-commits

Thread Start a new thread
Threads by month
  • ----- 2025 -----
  • October
  • September
  • August
  • July
  • June
  • May
  • April
  • March
  • February
  • January
  • ----- 2024 -----
  • December
  • November
  • October
  • September
  • August
  • July
  • June
  • May
  • April
  • March
  • February
  • January
  • ----- 2023 -----
  • December
  • November
  • October
  • September
  • August
  • July
  • June
  • May
  • April
  • March
  • February
  • January
  • ----- 2022 -----
  • December
  • November
  • October
  • September
  • August
  • July
  • June
  • May
  • April
  • March
  • February
  • January
  • ----- 2021 -----
  • December
  • November
  • October
  • September
  • August
  • July
  • June
  • May
  • April
  • March
  • February
  • January
  • ----- 2020 -----
  • December
  • November
  • October
  • September
  • August
  • July
  • June
  • May
  • April
  • March
  • February
  • January
  • ----- 2019 -----
  • December
  • November
  • October
  • September
  • August
  • July
  • June
  • May
  • April
  • March
  • February
  • January
  • ----- 2018 -----
  • December
  • November
  • October
  • September
  • August
  • July
  • June
  • May
  • April
  • March
  • February
  • January
  • ----- 2017 -----
  • December
  • November
  • October
  • September
  • August
  • July
  • June
  • May
  • April
  • March
  • February
  • January
  • ----- 2016 -----
  • December
  • November
  • October
  • September
  • August
  • July
  • June
  • May
  • April
  • March
  • February
  • January
  • ----- 2015 -----
  • December
  • November
  • October
  • September
  • August
  • July
  • June
  • May
  • April
  • March
  • February
  • January
  • ----- 2014 -----
  • December
  • November
  • October
  • September
  • August
  • July
  • June
  • May
  • April
  • March
  • February
tbb-commits@lists.torproject.org

October 2025

  • 1 participants
  • 140 discussions
[Git][tpo/applications/tor-browser][base-browser-140.4.0esr-15.0-1] 12 commits: fixup! BB 41919: Letterboxing, add temporarily visible web content-size...
by henry (@henry) 15 Oct '25

15 Oct '25
henry pushed to branch base-browser-140.4.0esr-15.0-1 at The Tor Project / Applications / Tor Browser Commits: a8d9aeef by Henry Wilkes at 2025-10-15T18:01:07+01:00 fixup! BB 41919: Letterboxing, add temporarily visible web content-size indicator on window resizing. TB 44214: Fix letterboxing status indicator for RTL. - - - - - 73862bbd by Henry Wilkes at 2025-10-15T18:01:08+01:00 fixup! BB 32308: Use direct browser sizing for letterboxing. TB 44214: Drop unnecessary CSS rules. - - - - - 09abf933 by Henry Wilkes at 2025-10-15T18:01:09+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". - - - - - 943bae35 by Henry Wilkes at 2025-10-15T18:01:09+01:00 fixup! BB 32308: Use direct browser sizing for letterboxing. TB 44214: Drop upstream's rules for placing content. - - - - - 9b4b135b by Henry Wilkes at 2025-10-15T18:01:10+01:00 fixup! BB 32308: Use direct browser sizing for letterboxing. TB 44214: Drop letterboxing gradient. - - - - - 4d9307d1 by Henry Wilkes at 2025-10-15T18:01:11+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. - - - - - e4d77eb2 by Henry Wilkes at 2025-10-15T18:01:12+01:00 fixup! BB 32308: Use direct browser sizing for letterboxing. TB 44214: Use CSS nesting. Part 1. - - - - - 261e8522 by Henry Wilkes at 2025-10-15T18:01:12+01:00 fixup! BB 32308: Use direct browser sizing for letterboxing. TB 44214: Use CSS nesting. Part 2. - - - - - 2282a161 by Henry Wilkes at 2025-10-15T18:01:13+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. - - - - - f48a8536 by Henry Wilkes at 2025-10-15T18:01:14+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. - - - - - 26d1b64a by Henry Wilkes at 2025-10-15T18:01:15+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. - - - - - 940d5e2c by Henry Wilkes at 2025-10-15T18:01:15+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/tor-browser/-/compare/29c65a… -- View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/29c65a… You're receiving this email because of your account on gitlab.torproject.org.
1 0
0 0
[Git][tpo/applications/tor-browser][tor-browser-140.4.0esr-15.0-1] 13 commits: dropme! fixup! TB 41917: Change letterboxing styling for Tor Browser.
by henry (@henry) 15 Oct '25

15 Oct '25
henry pushed to branch tor-browser-140.4.0esr-15.0-1 at The Tor Project / Applications / Tor Browser Commits: 0dddcb33 by Henry Wilkes at 2025-10-15T15:59:48+01:00 dropme! fixup! TB 41917: Change letterboxing styling for Tor Browser. TB 44214: Drop letterboxing changes for Tor Browser only. The commit "TB 41917: Change letterboxing styling for Tor Browser." should be entirely dropped. - - - - - fdc52857 by Henry Wilkes at 2025-10-15T15:59:49+01:00 fixup! BB 41919: Letterboxing, add temporarily visible web content-size indicator on window resizing. TB 44214: Fix letterboxing status indicator for RTL. - - - - - f00744fe by Henry Wilkes at 2025-10-15T15:59:50+01:00 fixup! BB 32308: Use direct browser sizing for letterboxing. TB 44214: Drop unnecessary CSS rules. - - - - - 805650c4 by Henry Wilkes at 2025-10-15T15:59:51+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". - - - - - bfe8d56b by Henry Wilkes at 2025-10-15T15:59:52+01:00 fixup! BB 32308: Use direct browser sizing for letterboxing. TB 44214: Drop upstream's rules for placing content. - - - - - b8a35ecc by Henry Wilkes at 2025-10-15T15:59:53+01:00 fixup! BB 32308: Use direct browser sizing for letterboxing. TB 44214: Drop letterboxing gradient. - - - - - 999bb9f2 by Henry Wilkes at 2025-10-15T15:59:53+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. - - - - - 31b90c2e by Henry Wilkes at 2025-10-15T15:59:54+01:00 fixup! BB 32308: Use direct browser sizing for letterboxing. TB 44214: Use CSS nesting. Part 1. - - - - - 58d4acdc by Henry Wilkes at 2025-10-15T15:59:55+01:00 fixup! BB 32308: Use direct browser sizing for letterboxing. TB 44214: Use CSS nesting. Part 2. - - - - - 317b197e by Henry Wilkes at 2025-10-15T15:59:56+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. - - - - - cb287829 by Henry Wilkes at 2025-10-15T15:59:57+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. - - - - - ff6fdbef by Henry Wilkes at 2025-10-15T15:59:58+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. - - - - - 196110ba by Henry Wilkes at 2025-10-15T17:19:07+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,30 +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; - - /* Re-styling for Tor Browser. */ - /* stylelint-disable declaration-block-no-duplicate-custom-properties */ - --letterboxing-bgcolor: light-dark(#F0F0F4, #52525E); - --letterboxing-gradient-color1: light-dark( - rgba(0, 219, 222, 0.02), - rgba(0, 219, 222, 0.06) - ); - --letterboxing-gradient-color2: light-dark( - rgba(252, 0, 255, 0.02), - rgba(252, 0, 255, 0.06) - ); - /* stylelint-enable declaration-block-no-duplicate-custom-properties */ + --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 { /* @@ -39,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/tor-browser/-/compare/be15e4… -- View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/be15e4… You're receiving this email because of your account on gitlab.torproject.org.
1 0
0 0
[Git][tpo/applications/mullvad-browser][mullvad-browser-128.14.0esr-14.5-1] fixup! BB 40925: Implemented the Security Level component
by ma1 (@ma1) 15 Oct '25

15 Oct '25
ma1 pushed to branch mullvad-browser-128.14.0esr-14.5-1 at The Tor Project / Applications / Mullvad Browser Commits: accaaaf6 by hackademix at 2025-10-15T15:57:20+02:00 fixup! BB 40925: Implemented the Security Level component BB 44242: Hand over Security Level's WebAssembly controls to NoScript - - - - - 1 changed file: - toolkit/components/securitylevel/SecurityLevel.sys.mjs Changes: ===================================== toolkit/components/securitylevel/SecurityLevel.sys.mjs ===================================== @@ -79,6 +79,7 @@ const max_caps = [ "object", "other", "script", + "wasm", "webgl", "noscript", ]; @@ -259,7 +260,6 @@ var initializeNoScriptControl = () => { // for each security setting. Note that 2-m and 3-m are identical, // corresponding to the old 2-medium-high setting. We also separately // bind NoScript settings to the browser.security_level.security_slider -// (see noscript-control.js). /* eslint-disable */ // prettier-ignore const kSecuritySettings = { @@ -272,7 +272,9 @@ const kSecuritySettings = { "gfx.font_rendering.opentype_svg.enabled": [, false, false, false, true ], "svg.disabled": [, true, false, false, false], "javascript.options.asmjs": [, false, false, false, true ], - "javascript.options.wasm": [, false, false, false, true ], + // tor-browser#44234, tor-browser#44242: this interferes with the correct + // functioning of the browser. So, WASM is also handled by NoScript now. + "javascript.options.wasm": [, true, true, true, true ], }; /* eslint-enable */ @@ -339,16 +341,19 @@ var write_setting_to_prefs = function (settingIndex) { // security settings matches. Otherwise return null. var read_setting_from_prefs = function (prefNames) { prefNames = prefNames || Object.keys(kSecuritySettings); - for (let settingIndex of [1, 2, 3, 4]) { + for (const settingIndex of [1, 2, 3, 4]) { let possibleSetting = true; // For the given settingIndex, check if all current pref values // match the setting. - for (let prefName of prefNames) { - if ( - kSecuritySettings[prefName][settingIndex] !== - Services.prefs.getBoolPref(prefName) - ) { + for (const prefName of prefNames) { + const wanted = kSecuritySettings[prefName][settingIndex]; + const actual = Services.prefs.getBoolPref(prefName); + if (wanted !== actual) { possibleSetting = false; + logger.info( + `${prefName} does not match level ${settingIndex}: ${actual}, should be ${wanted}!` + ); + break; } } if (possibleSetting) { @@ -373,7 +378,7 @@ var initializeSecurityPrefs = function () { if (initializedSecPrefs) { return; } - logger.info("Initializing security-prefs.js"); + logger.info("Initializing security level"); initializedSecPrefs = true; const wasCustom = Services.prefs.getBoolPref(kCustomPref, false); @@ -381,6 +386,21 @@ var initializeSecurityPrefs = function () { // and it should not be custom. let desiredIndex = Services.prefs.getIntPref(kSliderPref, 4); desiredIndex = fixupIndex(desiredIndex); + + if (!(wasCustom && desiredIndex == 4)) { + // The current level is non-customized Standard, or + // Safer / Safest (either customized or not): the global + // javascript.options.wasm pref interferes with the correct + // functioning of the browser, so instead we rely on NoScript + // to disable WebAssembly now (tor-browser#44234, tor-browser#44242). + // We skip flipping in customized Standard, because if its value was + // found false under such as circumstance, that would suggest + // an intentional user choice we don't want to interfere with. + // Unlike other javascript.options.* preferences, this one is safe + // to flip without a browser restart because it's checked whenever a + // context is created. + Services.prefs.setBoolPref("javascript.options.wasm", true); + } // Make sure the user has a set preference user value. Services.prefs.setIntPref(kSliderPref, desiredIndex); Services.prefs.setBoolPref(kCustomPref, wasCustom); @@ -453,7 +473,7 @@ var initializeSecurityPrefs = function () { }); } - logger.info("security-prefs.js initialization complete"); + logger.info("Security level initialization complete"); }; // tor-browser#41460: we changed preference names in 12.0. View it on GitLab: https://gitlab.torproject.org/tpo/applications/mullvad-browser/-/commit/acc… -- View it on GitLab: https://gitlab.torproject.org/tpo/applications/mullvad-browser/-/commit/acc… You're receiving this email because of your account on gitlab.torproject.org.
1 0
0 0
[Git][tpo/applications/tor-browser][base-browser-128.14.0esr-14.5-1] fixup! BB 40925: Implemented the Security Level component
by ma1 (@ma1) 15 Oct '25

15 Oct '25
ma1 pushed to branch base-browser-128.14.0esr-14.5-1 at The Tor Project / Applications / Tor Browser Commits: 14084797 by hackademix at 2025-10-15T15:57:13+02:00 fixup! BB 40925: Implemented the Security Level component BB 44242: Hand over Security Level's WebAssembly controls to NoScript - - - - - 1 changed file: - toolkit/components/securitylevel/SecurityLevel.sys.mjs Changes: ===================================== toolkit/components/securitylevel/SecurityLevel.sys.mjs ===================================== @@ -79,6 +79,7 @@ const max_caps = [ "object", "other", "script", + "wasm", "webgl", "noscript", ]; @@ -247,7 +248,6 @@ var initializeNoScriptControl = () => { // for each security setting. Note that 2-m and 3-m are identical, // corresponding to the old 2-medium-high setting. We also separately // bind NoScript settings to the browser.security_level.security_slider -// (see noscript-control.js). /* eslint-disable */ // prettier-ignore const kSecuritySettings = { @@ -260,7 +260,9 @@ const kSecuritySettings = { "gfx.font_rendering.opentype_svg.enabled": [, false, false, false, true ], "svg.disabled": [, true, false, false, false], "javascript.options.asmjs": [, false, false, false, true ], - "javascript.options.wasm": [, false, false, false, true ], + // tor-browser#44234, tor-browser#44242: this interferes with the correct + // functioning of the browser. So, WASM is also handled by NoScript now. + "javascript.options.wasm": [, true, true, true, true ], }; /* eslint-enable */ @@ -327,16 +329,19 @@ var write_setting_to_prefs = function (settingIndex) { // security settings matches. Otherwise return null. var read_setting_from_prefs = function (prefNames) { prefNames = prefNames || Object.keys(kSecuritySettings); - for (let settingIndex of [1, 2, 3, 4]) { + for (const settingIndex of [1, 2, 3, 4]) { let possibleSetting = true; // For the given settingIndex, check if all current pref values // match the setting. - for (let prefName of prefNames) { - if ( - kSecuritySettings[prefName][settingIndex] !== - Services.prefs.getBoolPref(prefName) - ) { + for (const prefName of prefNames) { + const wanted = kSecuritySettings[prefName][settingIndex]; + const actual = Services.prefs.getBoolPref(prefName); + if (wanted !== actual) { possibleSetting = false; + logger.info( + `${prefName} does not match level ${settingIndex}: ${actual}, should be ${wanted}!` + ); + break; } } if (possibleSetting) { @@ -361,7 +366,7 @@ var initializeSecurityPrefs = function () { if (initializedSecPrefs) { return; } - logger.info("Initializing security-prefs.js"); + logger.info("Initializing security level"); initializedSecPrefs = true; const wasCustom = Services.prefs.getBoolPref(kCustomPref, false); @@ -369,6 +374,21 @@ var initializeSecurityPrefs = function () { // and it should not be custom. let desiredIndex = Services.prefs.getIntPref(kSliderPref, 4); desiredIndex = fixupIndex(desiredIndex); + + if (!(wasCustom && desiredIndex == 4)) { + // The current level is non-customized Standard, or + // Safer / Safest (either customized or not): the global + // javascript.options.wasm pref interferes with the correct + // functioning of the browser, so instead we rely on NoScript + // to disable WebAssembly now (tor-browser#44234, tor-browser#44242). + // We skip flipping in customized Standard, because if its value was + // found false under such as circumstance, that would suggest + // an intentional user choice we don't want to interfere with. + // Unlike other javascript.options.* preferences, this one is safe + // to flip without a browser restart because it's checked whenever a + // context is created. + Services.prefs.setBoolPref("javascript.options.wasm", true); + } // Make sure the user has a set preference user value. Services.prefs.setIntPref(kSliderPref, desiredIndex); Services.prefs.setBoolPref(kCustomPref, wasCustom); @@ -441,7 +461,7 @@ var initializeSecurityPrefs = function () { }); } - logger.info("security-prefs.js initialization complete"); + logger.info("Security level initialization complete"); }; // tor-browser#41460: we changed preference names in 12.0. View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/1408479… -- View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/1408479… You're receiving this email because of your account on gitlab.torproject.org.
1 0
0 0
[Git][tpo/applications/tor-browser][tor-browser-128.14.0esr-14.5-1] fixup! BB 40925: Implemented the Security Level component
by ma1 (@ma1) 15 Oct '25

15 Oct '25
ma1 pushed to branch tor-browser-128.14.0esr-14.5-1 at The Tor Project / Applications / Tor Browser Commits: 19fc83ce by hackademix at 2025-10-15T15:57:05+02:00 fixup! BB 40925: Implemented the Security Level component BB 44242: Hand over Security Level's WebAssembly controls to NoScript - - - - - 1 changed file: - toolkit/components/securitylevel/SecurityLevel.sys.mjs Changes: ===================================== toolkit/components/securitylevel/SecurityLevel.sys.mjs ===================================== @@ -79,6 +79,7 @@ const max_caps = [ "object", "other", "script", + "wasm", "webgl", "noscript", ]; @@ -247,7 +248,6 @@ var initializeNoScriptControl = () => { // for each security setting. Note that 2-m and 3-m are identical, // corresponding to the old 2-medium-high setting. We also separately // bind NoScript settings to the browser.security_level.security_slider -// (see noscript-control.js). /* eslint-disable */ // prettier-ignore const kSecuritySettings = { @@ -260,7 +260,9 @@ const kSecuritySettings = { "gfx.font_rendering.opentype_svg.enabled": [, false, false, false, true ], "svg.disabled": [, true, false, false, false], "javascript.options.asmjs": [, false, false, false, true ], - "javascript.options.wasm": [, false, false, false, true ], + // tor-browser#44234, tor-browser#44242: this interferes with the correct + // functioning of the browser. So, WASM is also handled by NoScript now. + "javascript.options.wasm": [, true, true, true, true ], }; /* eslint-enable */ @@ -327,16 +329,19 @@ var write_setting_to_prefs = function (settingIndex) { // security settings matches. Otherwise return null. var read_setting_from_prefs = function (prefNames) { prefNames = prefNames || Object.keys(kSecuritySettings); - for (let settingIndex of [1, 2, 3, 4]) { + for (const settingIndex of [1, 2, 3, 4]) { let possibleSetting = true; // For the given settingIndex, check if all current pref values // match the setting. - for (let prefName of prefNames) { - if ( - kSecuritySettings[prefName][settingIndex] !== - Services.prefs.getBoolPref(prefName) - ) { + for (const prefName of prefNames) { + const wanted = kSecuritySettings[prefName][settingIndex]; + const actual = Services.prefs.getBoolPref(prefName); + if (wanted !== actual) { possibleSetting = false; + logger.info( + `${prefName} does not match level ${settingIndex}: ${actual}, should be ${wanted}!` + ); + break; } } if (possibleSetting) { @@ -361,7 +366,7 @@ var initializeSecurityPrefs = function () { if (initializedSecPrefs) { return; } - logger.info("Initializing security-prefs.js"); + logger.info("Initializing security level"); initializedSecPrefs = true; const wasCustom = Services.prefs.getBoolPref(kCustomPref, false); @@ -369,6 +374,21 @@ var initializeSecurityPrefs = function () { // and it should not be custom. let desiredIndex = Services.prefs.getIntPref(kSliderPref, 4); desiredIndex = fixupIndex(desiredIndex); + + if (!(wasCustom && desiredIndex == 4)) { + // The current level is non-customized Standard, or + // Safer / Safest (either customized or not): the global + // javascript.options.wasm pref interferes with the correct + // functioning of the browser, so instead we rely on NoScript + // to disable WebAssembly now (tor-browser#44234, tor-browser#44242). + // We skip flipping in customized Standard, because if its value was + // found false under such as circumstance, that would suggest + // an intentional user choice we don't want to interfere with. + // Unlike other javascript.options.* preferences, this one is safe + // to flip without a browser restart because it's checked whenever a + // context is created. + Services.prefs.setBoolPref("javascript.options.wasm", true); + } // Make sure the user has a set preference user value. Services.prefs.setIntPref(kSliderPref, desiredIndex); Services.prefs.setBoolPref(kCustomPref, wasCustom); @@ -441,7 +461,7 @@ var initializeSecurityPrefs = function () { }); } - logger.info("security-prefs.js initialization complete"); + logger.info("Security level initialization complete"); }; // tor-browser#41460: we changed preference names in 12.0. View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/19fc83c… -- View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/19fc83c… You're receiving this email because of your account on gitlab.torproject.org.
1 0
0 0
[Git][tpo/applications/mullvad-browser][mullvad-browser-140.4.0esr-15.0-1] fixup! BB 40925: Implemented the Security Level component
by ma1 (@ma1) 15 Oct '25

15 Oct '25
ma1 pushed to branch mullvad-browser-140.4.0esr-15.0-1 at The Tor Project / Applications / Mullvad Browser Commits: 42b098c7 by hackademix at 2025-10-15T15:56:42+02:00 fixup! BB 40925: Implemented the Security Level component BB 44242: Hand over Security Level's WebAssembly controls to NoScript - - - - - 1 changed file: - toolkit/components/securitylevel/SecurityLevel.sys.mjs Changes: ===================================== toolkit/components/securitylevel/SecurityLevel.sys.mjs ===================================== @@ -79,6 +79,7 @@ const max_caps = [ "object", "other", "script", + "wasm", "webgl", "noscript", ]; @@ -259,7 +260,6 @@ var initializeNoScriptControl = () => { // for each security setting. Note that 2-m and 3-m are identical, // corresponding to the old 2-medium-high setting. We also separately // bind NoScript settings to the browser.security_level.security_slider -// (see noscript-control.js). /* eslint-disable */ // prettier-ignore const kSecuritySettings = { @@ -272,7 +272,9 @@ const kSecuritySettings = { "gfx.font_rendering.opentype_svg.enabled": [, false, false, false, true ], "svg.disabled": [, true, false, false, false], "javascript.options.asmjs": [, false, false, false, true ], - "javascript.options.wasm": [, false, false, false, true ], + // tor-browser#44234, tor-browser#44242: this interferes with the correct + // functioning of the browser. So, WASM is also handled by NoScript now. + "javascript.options.wasm": [, true, true, true, true ], }; /* eslint-enable */ @@ -339,16 +341,19 @@ var write_setting_to_prefs = function (settingIndex) { // security settings matches. Otherwise return null. var read_setting_from_prefs = function (prefNames) { prefNames = prefNames || Object.keys(kSecuritySettings); - for (let settingIndex of [1, 2, 3, 4]) { + for (const settingIndex of [1, 2, 3, 4]) { let possibleSetting = true; // For the given settingIndex, check if all current pref values // match the setting. - for (let prefName of prefNames) { - if ( - kSecuritySettings[prefName][settingIndex] !== - Services.prefs.getBoolPref(prefName) - ) { + for (const prefName of prefNames) { + const wanted = kSecuritySettings[prefName][settingIndex]; + const actual = Services.prefs.getBoolPref(prefName); + if (wanted !== actual) { possibleSetting = false; + logger.info( + `${prefName} does not match level ${settingIndex}: ${actual}, should be ${wanted}!` + ); + break; } } if (possibleSetting) { @@ -373,7 +378,7 @@ var initializeSecurityPrefs = function () { if (initializedSecPrefs) { return; } - logger.info("Initializing security-prefs.js"); + logger.info("Initializing security level"); initializedSecPrefs = true; const wasCustom = Services.prefs.getBoolPref(kCustomPref, false); @@ -381,6 +386,21 @@ var initializeSecurityPrefs = function () { // and it should not be custom. let desiredIndex = Services.prefs.getIntPref(kSliderPref, 4); desiredIndex = fixupIndex(desiredIndex); + + if (!(wasCustom && desiredIndex == 4)) { + // The current level is non-customized Standard, or + // Safer / Safest (either customized or not): the global + // javascript.options.wasm pref interferes with the correct + // functioning of the browser, so instead we rely on NoScript + // to disable WebAssembly now (tor-browser#44234, tor-browser#44242). + // We skip flipping in customized Standard, because if its value was + // found false under such as circumstance, that would suggest + // an intentional user choice we don't want to interfere with. + // Unlike other javascript.options.* preferences, this one is safe + // to flip without a browser restart because it's checked whenever a + // context is created. + Services.prefs.setBoolPref("javascript.options.wasm", true); + } // Make sure the user has a set preference user value. Services.prefs.setIntPref(kSliderPref, desiredIndex); Services.prefs.setBoolPref(kCustomPref, wasCustom); @@ -460,7 +480,7 @@ var initializeSecurityPrefs = function () { }); } - logger.info("security-prefs.js initialization complete"); + logger.info("Security level initialization complete"); }; // tor-browser#41460: we changed preference names in 12.0. View it on GitLab: https://gitlab.torproject.org/tpo/applications/mullvad-browser/-/commit/42b… -- View it on GitLab: https://gitlab.torproject.org/tpo/applications/mullvad-browser/-/commit/42b… You're receiving this email because of your account on gitlab.torproject.org.
1 0
0 0
[Git][tpo/applications/tor-browser][base-browser-140.4.0esr-15.0-1] fixup! BB 40925: Implemented the Security Level component
by ma1 (@ma1) 15 Oct '25

15 Oct '25
ma1 pushed to branch base-browser-140.4.0esr-15.0-1 at The Tor Project / Applications / Tor Browser Commits: 29c65ad8 by hackademix at 2025-10-15T15:56:35+02:00 fixup! BB 40925: Implemented the Security Level component BB 44242: Hand over Security Level's WebAssembly controls to NoScript - - - - - 1 changed file: - toolkit/components/securitylevel/SecurityLevel.sys.mjs Changes: ===================================== toolkit/components/securitylevel/SecurityLevel.sys.mjs ===================================== @@ -79,6 +79,7 @@ const max_caps = [ "object", "other", "script", + "wasm", "webgl", "noscript", ]; @@ -247,7 +248,6 @@ var initializeNoScriptControl = () => { // for each security setting. Note that 2-m and 3-m are identical, // corresponding to the old 2-medium-high setting. We also separately // bind NoScript settings to the browser.security_level.security_slider -// (see noscript-control.js). /* eslint-disable */ // prettier-ignore const kSecuritySettings = { @@ -260,7 +260,9 @@ const kSecuritySettings = { "gfx.font_rendering.opentype_svg.enabled": [, false, false, false, true ], "svg.disabled": [, true, false, false, false], "javascript.options.asmjs": [, false, false, false, true ], - "javascript.options.wasm": [, false, false, false, true ], + // tor-browser#44234, tor-browser#44242: this interferes with the correct + // functioning of the browser. So, WASM is also handled by NoScript now. + "javascript.options.wasm": [, true, true, true, true ], }; /* eslint-enable */ @@ -327,16 +329,19 @@ var write_setting_to_prefs = function (settingIndex) { // security settings matches. Otherwise return null. var read_setting_from_prefs = function (prefNames) { prefNames = prefNames || Object.keys(kSecuritySettings); - for (let settingIndex of [1, 2, 3, 4]) { + for (const settingIndex of [1, 2, 3, 4]) { let possibleSetting = true; // For the given settingIndex, check if all current pref values // match the setting. - for (let prefName of prefNames) { - if ( - kSecuritySettings[prefName][settingIndex] !== - Services.prefs.getBoolPref(prefName) - ) { + for (const prefName of prefNames) { + const wanted = kSecuritySettings[prefName][settingIndex]; + const actual = Services.prefs.getBoolPref(prefName); + if (wanted !== actual) { possibleSetting = false; + logger.info( + `${prefName} does not match level ${settingIndex}: ${actual}, should be ${wanted}!` + ); + break; } } if (possibleSetting) { @@ -361,7 +366,7 @@ var initializeSecurityPrefs = function () { if (initializedSecPrefs) { return; } - logger.info("Initializing security-prefs.js"); + logger.info("Initializing security level"); initializedSecPrefs = true; const wasCustom = Services.prefs.getBoolPref(kCustomPref, false); @@ -369,6 +374,21 @@ var initializeSecurityPrefs = function () { // and it should not be custom. let desiredIndex = Services.prefs.getIntPref(kSliderPref, 4); desiredIndex = fixupIndex(desiredIndex); + + if (!(wasCustom && desiredIndex == 4)) { + // The current level is non-customized Standard, or + // Safer / Safest (either customized or not): the global + // javascript.options.wasm pref interferes with the correct + // functioning of the browser, so instead we rely on NoScript + // to disable WebAssembly now (tor-browser#44234, tor-browser#44242). + // We skip flipping in customized Standard, because if its value was + // found false under such as circumstance, that would suggest + // an intentional user choice we don't want to interfere with. + // Unlike other javascript.options.* preferences, this one is safe + // to flip without a browser restart because it's checked whenever a + // context is created. + Services.prefs.setBoolPref("javascript.options.wasm", true); + } // Make sure the user has a set preference user value. Services.prefs.setIntPref(kSliderPref, desiredIndex); Services.prefs.setBoolPref(kCustomPref, wasCustom); @@ -448,7 +468,7 @@ var initializeSecurityPrefs = function () { }); } - logger.info("security-prefs.js initialization complete"); + logger.info("Security level initialization complete"); }; // tor-browser#41460: we changed preference names in 12.0. View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/29c65ad… -- View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/29c65ad… You're receiving this email because of your account on gitlab.torproject.org.
1 0
0 0
[Git][tpo/applications/mullvad-browser][mullvad-browser-140.4.0esr-15.0-1] 2 commits: BB 43869: Hide pens with RFP.
by morgan (@morgan) 15 Oct '25

15 Oct '25
morgan pushed to branch mullvad-browser-140.4.0esr-15.0-1 at The Tor Project / Applications / Mullvad Browser Commits: 4517a0aa by Pier Angelo Vendrame at 2025-10-15T13:56:10+00:00 BB 43869: Hide pens with RFP. - - - - - 1a3fdcf6 by Pier Angelo Vendrame at 2025-10-15T13:56:10+00:00 fixup! Firefox preference overrides. BB 43869: Remove prefs for touch. RFP now overrides them. - - - - - 11 changed files: - browser/app/profile/001-base-profile.js - dom/base/Element.cpp - dom/events/PointerEvent.cpp - dom/events/PointerEvent.h - dom/events/PointerEventHandler.cpp - dom/events/TouchEvent.cpp - dom/webidl/PointerEvent.webidl - layout/base/PositionedEventTargeting.cpp - toolkit/components/resistfingerprinting/RFPTargets.inc - toolkit/components/resistfingerprinting/nsRFPService.cpp - widget/WidgetEventImpl.cpp Changes: ===================================== browser/app/profile/001-base-profile.js ===================================== @@ -497,12 +497,6 @@ pref("dom.webmidi.enabled", false); // Bug 41398: Disable Web MIDI API // randomized IDs when this pref is true). // Defense-in-depth (already the default value) from Firefox 119 or 120. pref("media.devices.enumerate.legacy.enabled", false); -// Touch events (tor-browser#10286, tor-browser#42069, tor-browser#44062) -#if defined(XP_WIN) || defined(ANDROID) -pref("dom.w3c_touch_events.enabled", 1); -#else -pref("dom.w3c_touch_events.enabled", 0); -#endif #ifndef ANDROID // Bug 42138: Disable touch-based overscroll UX pref("apz.overscroll.enabled", false); ===================================== dom/base/Element.cpp ===================================== @@ -302,11 +302,6 @@ nsDOMAttributeMap* Element::Attributes() { } void Element::SetPointerCapture(int32_t aPointerId, ErrorResult& aError) { - if (OwnerDoc()->ShouldResistFingerprinting(RFPTarget::PointerId) && - aPointerId != PointerEventHandler::GetSpoofedPointerIdForRFP()) { - aError.ThrowNotFoundError("Invalid pointer id"); - return; - } const PointerInfo* pointerInfo = PointerEventHandler::GetPointerInfo(aPointerId); if (!pointerInfo) { @@ -334,11 +329,6 @@ void Element::SetPointerCapture(int32_t aPointerId, ErrorResult& aError) { } void Element::ReleasePointerCapture(int32_t aPointerId, ErrorResult& aError) { - if (OwnerDoc()->ShouldResistFingerprinting(RFPTarget::PointerId) && - aPointerId != PointerEventHandler::GetSpoofedPointerIdForRFP()) { - aError.ThrowNotFoundError("Invalid pointer id"); - return; - } if (!PointerEventHandler::GetPointerInfo(aPointerId)) { aError.ThrowNotFoundError("Invalid pointer id"); return; ===================================== dom/events/PointerEvent.cpp ===================================== @@ -224,39 +224,78 @@ NS_INTERFACE_MAP_END_INHERITING(MouseEvent) NS_IMPL_ADDREF_INHERITED(PointerEvent, MouseEvent) NS_IMPL_RELEASE_INHERITED(PointerEvent, MouseEvent) -void PointerEvent::GetPointerType(nsAString& aPointerType) { +uint16_t PointerEvent::ResistantInputSource(CallerType aCallerType) const { + const uint16_t inputSource = mEvent->AsPointerEvent()->mInputSource; + if (!ShouldResistFingerprinting(aCallerType)) { + return inputSource; + } + + MOZ_ASSERT(IsTrusted()); + + // Bug 1953665: Pen events are inconsistent between platforms. + // They might emit touch events on Windows and Android, but only mouse events + // in other platforms. In particular, touch is always disabled on macOS. +#if defined(XP_WIN) + if (inputSource == MouseEvent_Binding::MOZ_SOURCE_TOUCH || + inputSource == MouseEvent_Binding::MOZ_SOURCE_MOUSE) { + return inputSource; + } + // Similar to nsWindow::DispatchTouchEventFromWMPointer. + switch (mEvent->mMessage) { + case ePointerMove: + return mEvent->AsPointerEvent()->mPressure == 0 + ? MouseEvent_Binding::MOZ_SOURCE_MOUSE // hover + : MouseEvent_Binding::MOZ_SOURCE_TOUCH; + case ePointerUp: + case ePointerDown: + case ePointerCancel: + return MouseEvent_Binding::MOZ_SOURCE_TOUCH; + default: + return MouseEvent_Binding::MOZ_SOURCE_MOUSE; + } +#elif defined(MOZ_WIDGET_ANDROID) + return inputSource == MouseEvent_Binding::MOZ_SOURCE_MOUSE + ? MouseEvent_Binding::MOZ_SOURCE_MOUSE + : MouseEvent_Binding::MOZ_SOURCE_TOUCH; +#elif defined(MOZ_WIDGET_GTK) + return inputSource == MouseEvent_Binding::MOZ_SOURCE_TOUCH + ? MouseEvent_Binding::MOZ_SOURCE_TOUCH + : MouseEvent_Binding::MOZ_SOURCE_MOUSE; +#elif defined(MOZ_WIDGET_COCOA) + return MouseEvent_Binding::MOZ_SOURCE_MOUSE; +#else + return inputSource; +#endif +} + +void PointerEvent::GetPointerType(nsAString& aPointerType, + CallerType aCallerType) { if (mPointerType.isSome()) { aPointerType = mPointerType.value(); return; } - -#if SPOOFED_MAX_TOUCH_POINTS <= 0 - if (ShouldResistFingerprinting()) { - aPointerType.AssignLiteral("mouse"); - return; - } -#endif - - ConvertPointerTypeToString(mEvent->AsPointerEvent()->mInputSource, - aPointerType); + ConvertPointerTypeToString(ResistantInputSource(aCallerType), aPointerType); } int32_t PointerEvent::PointerId() { - return (ShouldResistFingerprinting(true)) - ? PointerEventHandler::GetSpoofedPointerIdForRFP() - : mEvent->AsPointerEvent()->pointerId; + return mEvent->AsPointerEvent()->pointerId; } -double PointerEvent::Width() const { - return ShouldResistFingerprinting() ? 1.0 : mEvent->AsPointerEvent()->mWidth; +double PointerEvent::Width(CallerType aCallerType) const { + return ShouldResistFingerprinting(aCallerType) + ? 1.0 + : mEvent->AsPointerEvent()->mWidth; } -double PointerEvent::Height() const { - return ShouldResistFingerprinting() ? 1.0 : mEvent->AsPointerEvent()->mHeight; +double PointerEvent::Height(CallerType aCallerType) const { + return ShouldResistFingerprinting(aCallerType) + ? 1.0 + : mEvent->AsPointerEvent()->mHeight; } -float PointerEvent::Pressure() { - if (mEvent->mMessage == ePointerUp || !ShouldResistFingerprinting()) { +float PointerEvent::Pressure(CallerType aCallerType) { + if (mEvent->mMessage == ePointerUp || + !ShouldResistFingerprinting(aCallerType)) { return mEvent->AsPointerEvent()->mPressure; } @@ -273,14 +312,14 @@ float PointerEvent::Pressure() { return spoofedPressure; } -float PointerEvent::TangentialPressure() { - return ShouldResistFingerprinting() +float PointerEvent::TangentialPressure(CallerType aCallerType) { + return ShouldResistFingerprinting(aCallerType) ? 0 : mEvent->AsPointerEvent()->tangentialPressure; } -int32_t PointerEvent::TiltX() { - if (ShouldResistFingerprinting()) { +int32_t PointerEvent::TiltX(CallerType aCallerType) { + if (ShouldResistFingerprinting(aCallerType)) { return 0; } if (mTiltX.isSome()) { @@ -291,8 +330,8 @@ int32_t PointerEvent::TiltX() { return *mTiltX; } -int32_t PointerEvent::TiltY() { - if (ShouldResistFingerprinting()) { +int32_t PointerEvent::TiltY(CallerType aCallerType) { + if (ShouldResistFingerprinting(aCallerType)) { return 0; } if (mTiltY.isSome()) { @@ -303,12 +342,14 @@ int32_t PointerEvent::TiltY() { return *mTiltY; } -int32_t PointerEvent::Twist() { - return ShouldResistFingerprinting() ? 0 : mEvent->AsPointerEvent()->twist; +int32_t PointerEvent::Twist(CallerType aCallerType) { + return ShouldResistFingerprinting(aCallerType) + ? 0 + : mEvent->AsPointerEvent()->twist; } -double PointerEvent::AltitudeAngle() { - if (ShouldResistFingerprinting()) { +double PointerEvent::AltitudeAngle(CallerType aCallerType) { + if (ShouldResistFingerprinting(aCallerType)) { return WidgetPointerHelper::GetDefaultAltitudeAngle(); } if (mAltitudeAngle.isSome()) { @@ -319,8 +360,8 @@ double PointerEvent::AltitudeAngle() { return *mAltitudeAngle; } -double PointerEvent::AzimuthAngle() { - if (ShouldResistFingerprinting()) { +double PointerEvent::AzimuthAngle(CallerType aCallerType) { + if (ShouldResistFingerprinting(aCallerType)) { return WidgetPointerHelper::GetDefaultAzimuthAngle(); } if (mAzimuthAngle.isSome()) { @@ -421,22 +462,22 @@ void PointerEvent::GetPredictedEvents( aPointerEvents.AppendElements(mPredictedEvents); } -bool PointerEvent::ShouldResistFingerprinting(bool aForPointerId) const { - // There are three simple situations we don't need to spoof this pointer +bool PointerEvent::ShouldResistFingerprinting(CallerType aCallerType) const { + // There are a few simple situations we don't need to spoof this pointer // event. - // 1. The pref privcy.resistFingerprinting' is false, we fast return here - // since we don't need to do any QI of following codes. - // 2. This event is generated by scripts. - // 3. This event is a mouse pointer event. + // * We are being called by a System caller + // * The pref privcy.resistFingerprinting' is false, we fast return here + // since we don't need to do any QI of following codes. + // * This event is generated by scripts. + // * This event is a mouse pointer event. // We don't need to check for the system group since pointer events won't be // dispatched to the system group. - RFPTarget target = - aForPointerId ? RFPTarget::PointerId : RFPTarget::PointerEvents; - if (!nsContentUtils::ShouldResistFingerprinting("Efficiency Check", target) || + RFPTarget target = RFPTarget::PointerEvents; + if (aCallerType == CallerType::System || + !nsContentUtils::ShouldResistFingerprinting("Efficiency Check", target) || !mEvent->IsTrusted() || - (mEvent->AsPointerEvent()->mInputSource == - MouseEvent_Binding::MOZ_SOURCE_MOUSE && - SPOOFED_MAX_TOUCH_POINTS == 0)) { + mEvent->AsPointerEvent()->mInputSource == + MouseEvent_Binding::MOZ_SOURCE_MOUSE) { return false; } ===================================== dom/events/PointerEvent.h ===================================== @@ -41,17 +41,19 @@ class PointerEvent : public MouseEvent { PointerEvent* AsPointerEvent() final { return this; } int32_t PointerId(); - double Width() const; - double Height() const; - float Pressure(); - float TangentialPressure(); - int32_t TiltX(); - int32_t TiltY(); - int32_t Twist(); - double AltitudeAngle(); - double AzimuthAngle(); + double Width(CallerType aCallerType = CallerType::System) const; + double Height(CallerType aCallerType = CallerType::System) const; + float Pressure(CallerType aCallerType = CallerType::System); + float TangentialPressure(CallerType aCallerType = CallerType::System); + int32_t TiltX(CallerType aCallerType = CallerType::System); + int32_t TiltY(CallerType aCallerType = CallerType::System); + int32_t Twist(CallerType aCallerType = CallerType::System); + double AltitudeAngle(CallerType aCallerType = CallerType::System); + double AzimuthAngle(CallerType aCallerType = CallerType::System); bool IsPrimary(); - void GetPointerType(nsAString& aPointerType); + void GetPointerType( + nsAString& aPointerType, + mozilla::dom::CallerType aCallerType = CallerType::System); static bool EnableGetCoalescedEvents(JSContext* aCx, JSObject* aGlobal); void GetCoalescedEvents(nsTArray<RefPtr<PointerEvent>>& aPointerEvents); void GetPredictedEvents(nsTArray<RefPtr<PointerEvent>>& aPointerEvents); @@ -62,7 +64,11 @@ class PointerEvent : public MouseEvent { private: // This method returns the boolean to indicate whether spoofing pointer // event for fingerprinting resistance. - bool ShouldResistFingerprinting(bool aForPointerId = false) const; + bool ShouldResistFingerprinting( + CallerType aCallerType = CallerType::System) const; + + uint16_t ResistantInputSource( + CallerType aCallerType = CallerType::System) const; // When the instance is a trusted `pointermove` event but the widget event // does not have proper coalesced events (typically, the event is synthesized ===================================== dom/events/PointerEventHandler.cpp ===================================== @@ -421,32 +421,6 @@ void PointerEventHandler::CheckPointerCaptureState(WidgetPointerEvent* aEvent) { PointerCaptureInfo* captureInfo = GetPointerCaptureInfo(aEvent->pointerId); - // When fingerprinting resistance is enabled, we need to map other pointer - // ids into the spoofed one. We don't have to do the mapping if the capture - // info exists for the non-spoofed pointer id because of we won't allow - // content to set pointer capture other than the spoofed one. Thus, it must be - // from chrome if the capture info exists in this case. And we don't have to - // do anything if the pointer id is the same as the spoofed one. - if (nsContentUtils::ShouldResistFingerprinting("Efficiency Check", - RFPTarget::PointerId) && - aEvent->pointerId != (uint32_t)GetSpoofedPointerIdForRFP() && - !captureInfo) { - PointerCaptureInfo* spoofedCaptureInfo = - GetPointerCaptureInfo(GetSpoofedPointerIdForRFP()); - - // We need to check the target element's document should resist - // fingerprinting. If not, we don't need to send a capture event - // since the capture info of the original pointer id doesn't exist - // in this case. - if (!spoofedCaptureInfo || !spoofedCaptureInfo->mPendingElement || - !spoofedCaptureInfo->mPendingElement->OwnerDoc() - ->ShouldResistFingerprinting(RFPTarget::PointerEvents)) { - return; - } - - captureInfo = spoofedCaptureInfo; - } - if (!captureInfo || captureInfo->mPendingElement == captureInfo->mOverrideElement) { return; ===================================== dom/events/TouchEvent.cpp ===================================== @@ -225,38 +225,40 @@ bool TouchEvent::PrefEnabled(nsIDocShell* aDocShell) { } else if (touchEventsOverride == mozilla::dom::TouchEventsOverride::Disabled) { enabled = false; + } else if (nsContentUtils::ShouldResistFingerprinting( + aDocShell, RFPTarget::PointerEvents)) { +#ifdef MOZ_WIDGET_COCOA + enabled = false; +#else + enabled = true; +#endif } else { const int32_t prefValue = StaticPrefs::dom_w3c_touch_events_enabled(); if (prefValue == 2) { - if (nsContentUtils::ShouldResistFingerprinting( - aDocShell, RFPTarget::PointerEvents)) { - enabled = SPOOFED_MAX_TOUCH_POINTS != 0; - } else { - enabled = PlatformSupportsTouch(); - - static bool firstTime = true; - // The touch screen data seems to be inaccurate in the parent process, - // and we really need the crash annotation in child processes. - if (firstTime && !XRE_IsParentProcess()) { - CrashReporter::RecordAnnotationBool( - CrashReporter::Annotation::HasDeviceTouchScreen, enabled); - firstTime = false; - } + enabled = PlatformSupportsTouch(); + + static bool firstTime = true; + // The touch screen data seems to be inaccurate in the parent process, + // and we really need the crash annotation in child processes. + if (firstTime && !XRE_IsParentProcess()) { + CrashReporter::RecordAnnotationBool( + CrashReporter::Annotation::HasDeviceTouchScreen, enabled); + firstTime = false; + } #if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) - if (enabled && aDocShell) { - // APZ might be disabled on this particular widget, in which case - // TouchEvent support will also be disabled. Try to detect that. - RefPtr<nsPresContext> pc = aDocShell->GetPresContext(); - if (pc) { - nsCOMPtr<nsIWidget> widget = pc->GetRootWidget(); - if (widget) { - enabled &= widget->AsyncPanZoomEnabled(); - } + if (enabled && aDocShell) { + // APZ might be disabled on this particular widget, in which case + // TouchEvent support will also be disabled. Try to detect that. + RefPtr<nsPresContext> pc = aDocShell->GetPresContext(); + if (pc) { + nsCOMPtr<nsIWidget> widget = pc->GetRootWidget(); + if (widget) { + enabled &= widget->AsyncPanZoomEnabled(); } } -#endif } +#endif } else { enabled = !!prefValue; } ===================================== dom/webidl/PointerEvent.webidl ===================================== @@ -14,16 +14,26 @@ interface PointerEvent : MouseEvent readonly attribute long pointerId; + [NeedsCallerType] readonly attribute double width; + [NeedsCallerType] readonly attribute double height; + [NeedsCallerType] readonly attribute float pressure; + [NeedsCallerType] readonly attribute float tangentialPressure; + [NeedsCallerType] readonly attribute long tiltX; + [NeedsCallerType] readonly attribute long tiltY; + [NeedsCallerType] readonly attribute long twist; + [NeedsCallerType] readonly attribute double altitudeAngle; + [NeedsCallerType] readonly attribute double azimuthAngle; + [NeedsCallerType] readonly attribute DOMString pointerType; readonly attribute boolean isPrimary; ===================================== layout/base/PositionedEventTargeting.cpp ===================================== @@ -16,6 +16,7 @@ #include "mozilla/ToString.h" #include "mozilla/ViewportUtils.h" #include "mozilla/dom/MouseEventBinding.h" +#include "mozilla/dom/TouchEvent.h" #include "mozilla/gfx/Matrix.h" #include "mozilla/layers/LayersTypes.h" #include "nsContainerFrame.h" @@ -173,9 +174,7 @@ static bool HasTouchListener(const nsIContent* aContent) { return false; } - // FIXME: Should this really use the pref rather than TouchEvent::PrefEnabled - // or such? - if (!StaticPrefs::dom_w3c_touch_events_enabled()) { + if (!TouchEvent::PrefEnabled(aContent->OwnerDoc()->GetDocShell())) { return false; } ===================================== toolkit/components/resistfingerprinting/RFPTargets.inc ===================================== @@ -34,7 +34,7 @@ ITEM_VALUE(NavigatorHWConcurrency, 16) ITEM_VALUE(NavigatorOscpu, 17) ITEM_VALUE(NavigatorPlatform, 18) ITEM_VALUE(NavigatorUserAgent, 19) -ITEM_VALUE(PointerId, 20) +// We no longer use PointerId, it can renamed and reused ITEM_VALUE(StreamVideoFacingMode, 21) ITEM_VALUE(JSDateTimeUTC, 22) ITEM_VALUE(JSMathFdlibm, 23) @@ -104,6 +104,7 @@ ITEM_VALUE(DiskStorageLimit, 70) ITEM_VALUE(WebCodecs, 71) ITEM_VALUE(NavigatorHWConcurrencyTiered,74) +// !!! Adding a new target? Rename PointerId and repurpose it. // !!! Don't forget to update kDefaultFingerprintingProtections in nsRFPService.cpp // if necessary. ===================================== toolkit/components/resistfingerprinting/nsRFPService.cpp ===================================== @@ -303,13 +303,6 @@ Maybe<bool> nsRFPService::HandleExeptionalRFPTargets( StaticPrefs::privacy_spoof_english_DoNotUseDirectly() == 2); } - // We don't spoof the pointerId on multi-touch devices. -#if SPOOFED_MAX_TOUCH_POINTS > 0 - if (aTarget == RFPTarget::PointerId) { - return Some(false); - } -#endif - return Nothing(); } ===================================== widget/WidgetEventImpl.cpp ===================================== @@ -589,23 +589,6 @@ bool WidgetEvent::IsBlockedForFingerprintingResistance() const { keyboardEvent->mKeyNameIndex == KEY_NAME_INDEX_Control || keyboardEvent->mKeyNameIndex == KEY_NAME_INDEX_AltGraph); } - case ePointerEventClass: { - if (IsPointerEventMessageOriginallyMouseEventMessage(mMessage)) { - return false; - } - - if (SPOOFED_MAX_TOUCH_POINTS > 0) { - return false; - } - - const WidgetPointerEvent* pointerEvent = AsPointerEvent(); - - // We suppress the pointer events if it is not primary for fingerprinting - // resistance. It is because of that we want to spoof any pointer event - // into a mouse pointer event and the mouse pointer event only has - // isPrimary as true. - return !pointerEvent->mIsPrimary; - } default: return false; } View it on GitLab: https://gitlab.torproject.org/tpo/applications/mullvad-browser/-/compare/fb… -- View it on GitLab: https://gitlab.torproject.org/tpo/applications/mullvad-browser/-/compare/fb… You're receiving this email because of your account on gitlab.torproject.org.
1 0
0 0
[Git][tpo/applications/tor-browser][base-browser-140.4.0esr-15.0-1] 2 commits: BB 43869: Hide pens with RFP.
by morgan (@morgan) 15 Oct '25

15 Oct '25
morgan pushed to branch base-browser-140.4.0esr-15.0-1 at The Tor Project / Applications / Tor Browser Commits: 822f61c9 by Pier Angelo Vendrame at 2025-10-15T13:54:52+00:00 BB 43869: Hide pens with RFP. - - - - - 0ed3602b by Pier Angelo Vendrame at 2025-10-15T13:54:54+00:00 fixup! Firefox preference overrides. BB 43869: Remove prefs for touch. RFP now overrides them. - - - - - 11 changed files: - browser/app/profile/001-base-profile.js - dom/base/Element.cpp - dom/events/PointerEvent.cpp - dom/events/PointerEvent.h - dom/events/PointerEventHandler.cpp - dom/events/TouchEvent.cpp - dom/webidl/PointerEvent.webidl - layout/base/PositionedEventTargeting.cpp - toolkit/components/resistfingerprinting/RFPTargets.inc - toolkit/components/resistfingerprinting/nsRFPService.cpp - widget/WidgetEventImpl.cpp Changes: ===================================== browser/app/profile/001-base-profile.js ===================================== @@ -497,12 +497,6 @@ pref("dom.webmidi.enabled", false); // Bug 41398: Disable Web MIDI API // randomized IDs when this pref is true). // Defense-in-depth (already the default value) from Firefox 119 or 120. pref("media.devices.enumerate.legacy.enabled", false); -// Touch events (tor-browser#10286, tor-browser#42069, tor-browser#44062) -#if defined(XP_WIN) || defined(ANDROID) -pref("dom.w3c_touch_events.enabled", 1); -#else -pref("dom.w3c_touch_events.enabled", 0); -#endif #ifndef ANDROID // Bug 42138: Disable touch-based overscroll UX pref("apz.overscroll.enabled", false); ===================================== dom/base/Element.cpp ===================================== @@ -302,11 +302,6 @@ nsDOMAttributeMap* Element::Attributes() { } void Element::SetPointerCapture(int32_t aPointerId, ErrorResult& aError) { - if (OwnerDoc()->ShouldResistFingerprinting(RFPTarget::PointerId) && - aPointerId != PointerEventHandler::GetSpoofedPointerIdForRFP()) { - aError.ThrowNotFoundError("Invalid pointer id"); - return; - } const PointerInfo* pointerInfo = PointerEventHandler::GetPointerInfo(aPointerId); if (!pointerInfo) { @@ -334,11 +329,6 @@ void Element::SetPointerCapture(int32_t aPointerId, ErrorResult& aError) { } void Element::ReleasePointerCapture(int32_t aPointerId, ErrorResult& aError) { - if (OwnerDoc()->ShouldResistFingerprinting(RFPTarget::PointerId) && - aPointerId != PointerEventHandler::GetSpoofedPointerIdForRFP()) { - aError.ThrowNotFoundError("Invalid pointer id"); - return; - } if (!PointerEventHandler::GetPointerInfo(aPointerId)) { aError.ThrowNotFoundError("Invalid pointer id"); return; ===================================== dom/events/PointerEvent.cpp ===================================== @@ -224,39 +224,78 @@ NS_INTERFACE_MAP_END_INHERITING(MouseEvent) NS_IMPL_ADDREF_INHERITED(PointerEvent, MouseEvent) NS_IMPL_RELEASE_INHERITED(PointerEvent, MouseEvent) -void PointerEvent::GetPointerType(nsAString& aPointerType) { +uint16_t PointerEvent::ResistantInputSource(CallerType aCallerType) const { + const uint16_t inputSource = mEvent->AsPointerEvent()->mInputSource; + if (!ShouldResistFingerprinting(aCallerType)) { + return inputSource; + } + + MOZ_ASSERT(IsTrusted()); + + // Bug 1953665: Pen events are inconsistent between platforms. + // They might emit touch events on Windows and Android, but only mouse events + // in other platforms. In particular, touch is always disabled on macOS. +#if defined(XP_WIN) + if (inputSource == MouseEvent_Binding::MOZ_SOURCE_TOUCH || + inputSource == MouseEvent_Binding::MOZ_SOURCE_MOUSE) { + return inputSource; + } + // Similar to nsWindow::DispatchTouchEventFromWMPointer. + switch (mEvent->mMessage) { + case ePointerMove: + return mEvent->AsPointerEvent()->mPressure == 0 + ? MouseEvent_Binding::MOZ_SOURCE_MOUSE // hover + : MouseEvent_Binding::MOZ_SOURCE_TOUCH; + case ePointerUp: + case ePointerDown: + case ePointerCancel: + return MouseEvent_Binding::MOZ_SOURCE_TOUCH; + default: + return MouseEvent_Binding::MOZ_SOURCE_MOUSE; + } +#elif defined(MOZ_WIDGET_ANDROID) + return inputSource == MouseEvent_Binding::MOZ_SOURCE_MOUSE + ? MouseEvent_Binding::MOZ_SOURCE_MOUSE + : MouseEvent_Binding::MOZ_SOURCE_TOUCH; +#elif defined(MOZ_WIDGET_GTK) + return inputSource == MouseEvent_Binding::MOZ_SOURCE_TOUCH + ? MouseEvent_Binding::MOZ_SOURCE_TOUCH + : MouseEvent_Binding::MOZ_SOURCE_MOUSE; +#elif defined(MOZ_WIDGET_COCOA) + return MouseEvent_Binding::MOZ_SOURCE_MOUSE; +#else + return inputSource; +#endif +} + +void PointerEvent::GetPointerType(nsAString& aPointerType, + CallerType aCallerType) { if (mPointerType.isSome()) { aPointerType = mPointerType.value(); return; } - -#if SPOOFED_MAX_TOUCH_POINTS <= 0 - if (ShouldResistFingerprinting()) { - aPointerType.AssignLiteral("mouse"); - return; - } -#endif - - ConvertPointerTypeToString(mEvent->AsPointerEvent()->mInputSource, - aPointerType); + ConvertPointerTypeToString(ResistantInputSource(aCallerType), aPointerType); } int32_t PointerEvent::PointerId() { - return (ShouldResistFingerprinting(true)) - ? PointerEventHandler::GetSpoofedPointerIdForRFP() - : mEvent->AsPointerEvent()->pointerId; + return mEvent->AsPointerEvent()->pointerId; } -double PointerEvent::Width() const { - return ShouldResistFingerprinting() ? 1.0 : mEvent->AsPointerEvent()->mWidth; +double PointerEvent::Width(CallerType aCallerType) const { + return ShouldResistFingerprinting(aCallerType) + ? 1.0 + : mEvent->AsPointerEvent()->mWidth; } -double PointerEvent::Height() const { - return ShouldResistFingerprinting() ? 1.0 : mEvent->AsPointerEvent()->mHeight; +double PointerEvent::Height(CallerType aCallerType) const { + return ShouldResistFingerprinting(aCallerType) + ? 1.0 + : mEvent->AsPointerEvent()->mHeight; } -float PointerEvent::Pressure() { - if (mEvent->mMessage == ePointerUp || !ShouldResistFingerprinting()) { +float PointerEvent::Pressure(CallerType aCallerType) { + if (mEvent->mMessage == ePointerUp || + !ShouldResistFingerprinting(aCallerType)) { return mEvent->AsPointerEvent()->mPressure; } @@ -273,14 +312,14 @@ float PointerEvent::Pressure() { return spoofedPressure; } -float PointerEvent::TangentialPressure() { - return ShouldResistFingerprinting() +float PointerEvent::TangentialPressure(CallerType aCallerType) { + return ShouldResistFingerprinting(aCallerType) ? 0 : mEvent->AsPointerEvent()->tangentialPressure; } -int32_t PointerEvent::TiltX() { - if (ShouldResistFingerprinting()) { +int32_t PointerEvent::TiltX(CallerType aCallerType) { + if (ShouldResistFingerprinting(aCallerType)) { return 0; } if (mTiltX.isSome()) { @@ -291,8 +330,8 @@ int32_t PointerEvent::TiltX() { return *mTiltX; } -int32_t PointerEvent::TiltY() { - if (ShouldResistFingerprinting()) { +int32_t PointerEvent::TiltY(CallerType aCallerType) { + if (ShouldResistFingerprinting(aCallerType)) { return 0; } if (mTiltY.isSome()) { @@ -303,12 +342,14 @@ int32_t PointerEvent::TiltY() { return *mTiltY; } -int32_t PointerEvent::Twist() { - return ShouldResistFingerprinting() ? 0 : mEvent->AsPointerEvent()->twist; +int32_t PointerEvent::Twist(CallerType aCallerType) { + return ShouldResistFingerprinting(aCallerType) + ? 0 + : mEvent->AsPointerEvent()->twist; } -double PointerEvent::AltitudeAngle() { - if (ShouldResistFingerprinting()) { +double PointerEvent::AltitudeAngle(CallerType aCallerType) { + if (ShouldResistFingerprinting(aCallerType)) { return WidgetPointerHelper::GetDefaultAltitudeAngle(); } if (mAltitudeAngle.isSome()) { @@ -319,8 +360,8 @@ double PointerEvent::AltitudeAngle() { return *mAltitudeAngle; } -double PointerEvent::AzimuthAngle() { - if (ShouldResistFingerprinting()) { +double PointerEvent::AzimuthAngle(CallerType aCallerType) { + if (ShouldResistFingerprinting(aCallerType)) { return WidgetPointerHelper::GetDefaultAzimuthAngle(); } if (mAzimuthAngle.isSome()) { @@ -421,22 +462,22 @@ void PointerEvent::GetPredictedEvents( aPointerEvents.AppendElements(mPredictedEvents); } -bool PointerEvent::ShouldResistFingerprinting(bool aForPointerId) const { - // There are three simple situations we don't need to spoof this pointer +bool PointerEvent::ShouldResistFingerprinting(CallerType aCallerType) const { + // There are a few simple situations we don't need to spoof this pointer // event. - // 1. The pref privcy.resistFingerprinting' is false, we fast return here - // since we don't need to do any QI of following codes. - // 2. This event is generated by scripts. - // 3. This event is a mouse pointer event. + // * We are being called by a System caller + // * The pref privcy.resistFingerprinting' is false, we fast return here + // since we don't need to do any QI of following codes. + // * This event is generated by scripts. + // * This event is a mouse pointer event. // We don't need to check for the system group since pointer events won't be // dispatched to the system group. - RFPTarget target = - aForPointerId ? RFPTarget::PointerId : RFPTarget::PointerEvents; - if (!nsContentUtils::ShouldResistFingerprinting("Efficiency Check", target) || + RFPTarget target = RFPTarget::PointerEvents; + if (aCallerType == CallerType::System || + !nsContentUtils::ShouldResistFingerprinting("Efficiency Check", target) || !mEvent->IsTrusted() || - (mEvent->AsPointerEvent()->mInputSource == - MouseEvent_Binding::MOZ_SOURCE_MOUSE && - SPOOFED_MAX_TOUCH_POINTS == 0)) { + mEvent->AsPointerEvent()->mInputSource == + MouseEvent_Binding::MOZ_SOURCE_MOUSE) { return false; } ===================================== dom/events/PointerEvent.h ===================================== @@ -41,17 +41,19 @@ class PointerEvent : public MouseEvent { PointerEvent* AsPointerEvent() final { return this; } int32_t PointerId(); - double Width() const; - double Height() const; - float Pressure(); - float TangentialPressure(); - int32_t TiltX(); - int32_t TiltY(); - int32_t Twist(); - double AltitudeAngle(); - double AzimuthAngle(); + double Width(CallerType aCallerType = CallerType::System) const; + double Height(CallerType aCallerType = CallerType::System) const; + float Pressure(CallerType aCallerType = CallerType::System); + float TangentialPressure(CallerType aCallerType = CallerType::System); + int32_t TiltX(CallerType aCallerType = CallerType::System); + int32_t TiltY(CallerType aCallerType = CallerType::System); + int32_t Twist(CallerType aCallerType = CallerType::System); + double AltitudeAngle(CallerType aCallerType = CallerType::System); + double AzimuthAngle(CallerType aCallerType = CallerType::System); bool IsPrimary(); - void GetPointerType(nsAString& aPointerType); + void GetPointerType( + nsAString& aPointerType, + mozilla::dom::CallerType aCallerType = CallerType::System); static bool EnableGetCoalescedEvents(JSContext* aCx, JSObject* aGlobal); void GetCoalescedEvents(nsTArray<RefPtr<PointerEvent>>& aPointerEvents); void GetPredictedEvents(nsTArray<RefPtr<PointerEvent>>& aPointerEvents); @@ -62,7 +64,11 @@ class PointerEvent : public MouseEvent { private: // This method returns the boolean to indicate whether spoofing pointer // event for fingerprinting resistance. - bool ShouldResistFingerprinting(bool aForPointerId = false) const; + bool ShouldResistFingerprinting( + CallerType aCallerType = CallerType::System) const; + + uint16_t ResistantInputSource( + CallerType aCallerType = CallerType::System) const; // When the instance is a trusted `pointermove` event but the widget event // does not have proper coalesced events (typically, the event is synthesized ===================================== dom/events/PointerEventHandler.cpp ===================================== @@ -421,32 +421,6 @@ void PointerEventHandler::CheckPointerCaptureState(WidgetPointerEvent* aEvent) { PointerCaptureInfo* captureInfo = GetPointerCaptureInfo(aEvent->pointerId); - // When fingerprinting resistance is enabled, we need to map other pointer - // ids into the spoofed one. We don't have to do the mapping if the capture - // info exists for the non-spoofed pointer id because of we won't allow - // content to set pointer capture other than the spoofed one. Thus, it must be - // from chrome if the capture info exists in this case. And we don't have to - // do anything if the pointer id is the same as the spoofed one. - if (nsContentUtils::ShouldResistFingerprinting("Efficiency Check", - RFPTarget::PointerId) && - aEvent->pointerId != (uint32_t)GetSpoofedPointerIdForRFP() && - !captureInfo) { - PointerCaptureInfo* spoofedCaptureInfo = - GetPointerCaptureInfo(GetSpoofedPointerIdForRFP()); - - // We need to check the target element's document should resist - // fingerprinting. If not, we don't need to send a capture event - // since the capture info of the original pointer id doesn't exist - // in this case. - if (!spoofedCaptureInfo || !spoofedCaptureInfo->mPendingElement || - !spoofedCaptureInfo->mPendingElement->OwnerDoc() - ->ShouldResistFingerprinting(RFPTarget::PointerEvents)) { - return; - } - - captureInfo = spoofedCaptureInfo; - } - if (!captureInfo || captureInfo->mPendingElement == captureInfo->mOverrideElement) { return; ===================================== dom/events/TouchEvent.cpp ===================================== @@ -225,38 +225,40 @@ bool TouchEvent::PrefEnabled(nsIDocShell* aDocShell) { } else if (touchEventsOverride == mozilla::dom::TouchEventsOverride::Disabled) { enabled = false; + } else if (nsContentUtils::ShouldResistFingerprinting( + aDocShell, RFPTarget::PointerEvents)) { +#ifdef MOZ_WIDGET_COCOA + enabled = false; +#else + enabled = true; +#endif } else { const int32_t prefValue = StaticPrefs::dom_w3c_touch_events_enabled(); if (prefValue == 2) { - if (nsContentUtils::ShouldResistFingerprinting( - aDocShell, RFPTarget::PointerEvents)) { - enabled = SPOOFED_MAX_TOUCH_POINTS != 0; - } else { - enabled = PlatformSupportsTouch(); - - static bool firstTime = true; - // The touch screen data seems to be inaccurate in the parent process, - // and we really need the crash annotation in child processes. - if (firstTime && !XRE_IsParentProcess()) { - CrashReporter::RecordAnnotationBool( - CrashReporter::Annotation::HasDeviceTouchScreen, enabled); - firstTime = false; - } + enabled = PlatformSupportsTouch(); + + static bool firstTime = true; + // The touch screen data seems to be inaccurate in the parent process, + // and we really need the crash annotation in child processes. + if (firstTime && !XRE_IsParentProcess()) { + CrashReporter::RecordAnnotationBool( + CrashReporter::Annotation::HasDeviceTouchScreen, enabled); + firstTime = false; + } #if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) - if (enabled && aDocShell) { - // APZ might be disabled on this particular widget, in which case - // TouchEvent support will also be disabled. Try to detect that. - RefPtr<nsPresContext> pc = aDocShell->GetPresContext(); - if (pc) { - nsCOMPtr<nsIWidget> widget = pc->GetRootWidget(); - if (widget) { - enabled &= widget->AsyncPanZoomEnabled(); - } + if (enabled && aDocShell) { + // APZ might be disabled on this particular widget, in which case + // TouchEvent support will also be disabled. Try to detect that. + RefPtr<nsPresContext> pc = aDocShell->GetPresContext(); + if (pc) { + nsCOMPtr<nsIWidget> widget = pc->GetRootWidget(); + if (widget) { + enabled &= widget->AsyncPanZoomEnabled(); } } -#endif } +#endif } else { enabled = !!prefValue; } ===================================== dom/webidl/PointerEvent.webidl ===================================== @@ -14,16 +14,26 @@ interface PointerEvent : MouseEvent readonly attribute long pointerId; + [NeedsCallerType] readonly attribute double width; + [NeedsCallerType] readonly attribute double height; + [NeedsCallerType] readonly attribute float pressure; + [NeedsCallerType] readonly attribute float tangentialPressure; + [NeedsCallerType] readonly attribute long tiltX; + [NeedsCallerType] readonly attribute long tiltY; + [NeedsCallerType] readonly attribute long twist; + [NeedsCallerType] readonly attribute double altitudeAngle; + [NeedsCallerType] readonly attribute double azimuthAngle; + [NeedsCallerType] readonly attribute DOMString pointerType; readonly attribute boolean isPrimary; ===================================== layout/base/PositionedEventTargeting.cpp ===================================== @@ -16,6 +16,7 @@ #include "mozilla/ToString.h" #include "mozilla/ViewportUtils.h" #include "mozilla/dom/MouseEventBinding.h" +#include "mozilla/dom/TouchEvent.h" #include "mozilla/gfx/Matrix.h" #include "mozilla/layers/LayersTypes.h" #include "nsContainerFrame.h" @@ -173,9 +174,7 @@ static bool HasTouchListener(const nsIContent* aContent) { return false; } - // FIXME: Should this really use the pref rather than TouchEvent::PrefEnabled - // or such? - if (!StaticPrefs::dom_w3c_touch_events_enabled()) { + if (!TouchEvent::PrefEnabled(aContent->OwnerDoc()->GetDocShell())) { return false; } ===================================== toolkit/components/resistfingerprinting/RFPTargets.inc ===================================== @@ -34,7 +34,7 @@ ITEM_VALUE(NavigatorHWConcurrency, 16) ITEM_VALUE(NavigatorOscpu, 17) ITEM_VALUE(NavigatorPlatform, 18) ITEM_VALUE(NavigatorUserAgent, 19) -ITEM_VALUE(PointerId, 20) +// We no longer use PointerId, it can renamed and reused ITEM_VALUE(StreamVideoFacingMode, 21) ITEM_VALUE(JSDateTimeUTC, 22) ITEM_VALUE(JSMathFdlibm, 23) @@ -104,6 +104,7 @@ ITEM_VALUE(DiskStorageLimit, 70) ITEM_VALUE(WebCodecs, 71) ITEM_VALUE(NavigatorHWConcurrencyTiered,74) +// !!! Adding a new target? Rename PointerId and repurpose it. // !!! Don't forget to update kDefaultFingerprintingProtections in nsRFPService.cpp // if necessary. ===================================== toolkit/components/resistfingerprinting/nsRFPService.cpp ===================================== @@ -303,13 +303,6 @@ Maybe<bool> nsRFPService::HandleExeptionalRFPTargets( StaticPrefs::privacy_spoof_english_DoNotUseDirectly() == 2); } - // We don't spoof the pointerId on multi-touch devices. -#if SPOOFED_MAX_TOUCH_POINTS > 0 - if (aTarget == RFPTarget::PointerId) { - return Some(false); - } -#endif - return Nothing(); } ===================================== widget/WidgetEventImpl.cpp ===================================== @@ -589,23 +589,6 @@ bool WidgetEvent::IsBlockedForFingerprintingResistance() const { keyboardEvent->mKeyNameIndex == KEY_NAME_INDEX_Control || keyboardEvent->mKeyNameIndex == KEY_NAME_INDEX_AltGraph); } - case ePointerEventClass: { - if (IsPointerEventMessageOriginallyMouseEventMessage(mMessage)) { - return false; - } - - if (SPOOFED_MAX_TOUCH_POINTS > 0) { - return false; - } - - const WidgetPointerEvent* pointerEvent = AsPointerEvent(); - - // We suppress the pointer events if it is not primary for fingerprinting - // resistance. It is because of that we want to spoof any pointer event - // into a mouse pointer event and the mouse pointer event only has - // isPrimary as true. - return !pointerEvent->mIsPrimary; - } default: return false; } View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/2c531e… -- View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/2c531e… You're receiving this email because of your account on gitlab.torproject.org.
1 0
0 0
[Git][tpo/applications/tor-browser][tor-browser-140.4.0esr-15.0-1] 2 commits: BB 43869: Hide pens with RFP.
by morgan (@morgan) 15 Oct '25

15 Oct '25
morgan pushed to branch tor-browser-140.4.0esr-15.0-1 at The Tor Project / Applications / Tor Browser Commits: 84fb1585 by Pier Angelo Vendrame at 2025-10-15T13:23:33+00:00 BB 43869: Hide pens with RFP. - - - - - be15e46e by Pier Angelo Vendrame at 2025-10-15T13:23:33+00:00 fixup! Firefox preference overrides. BB 43869: Remove prefs for touch. RFP now overrides them. - - - - - 11 changed files: - browser/app/profile/001-base-profile.js - dom/base/Element.cpp - dom/events/PointerEvent.cpp - dom/events/PointerEvent.h - dom/events/PointerEventHandler.cpp - dom/events/TouchEvent.cpp - dom/webidl/PointerEvent.webidl - layout/base/PositionedEventTargeting.cpp - toolkit/components/resistfingerprinting/RFPTargets.inc - toolkit/components/resistfingerprinting/nsRFPService.cpp - widget/WidgetEventImpl.cpp Changes: ===================================== browser/app/profile/001-base-profile.js ===================================== @@ -497,12 +497,6 @@ pref("dom.webmidi.enabled", false); // Bug 41398: Disable Web MIDI API // randomized IDs when this pref is true). // Defense-in-depth (already the default value) from Firefox 119 or 120. pref("media.devices.enumerate.legacy.enabled", false); -// Touch events (tor-browser#10286, tor-browser#42069, tor-browser#44062) -#if defined(XP_WIN) || defined(ANDROID) -pref("dom.w3c_touch_events.enabled", 1); -#else -pref("dom.w3c_touch_events.enabled", 0); -#endif #ifndef ANDROID // Bug 42138: Disable touch-based overscroll UX pref("apz.overscroll.enabled", false); ===================================== dom/base/Element.cpp ===================================== @@ -302,11 +302,6 @@ nsDOMAttributeMap* Element::Attributes() { } void Element::SetPointerCapture(int32_t aPointerId, ErrorResult& aError) { - if (OwnerDoc()->ShouldResistFingerprinting(RFPTarget::PointerId) && - aPointerId != PointerEventHandler::GetSpoofedPointerIdForRFP()) { - aError.ThrowNotFoundError("Invalid pointer id"); - return; - } const PointerInfo* pointerInfo = PointerEventHandler::GetPointerInfo(aPointerId); if (!pointerInfo) { @@ -334,11 +329,6 @@ void Element::SetPointerCapture(int32_t aPointerId, ErrorResult& aError) { } void Element::ReleasePointerCapture(int32_t aPointerId, ErrorResult& aError) { - if (OwnerDoc()->ShouldResistFingerprinting(RFPTarget::PointerId) && - aPointerId != PointerEventHandler::GetSpoofedPointerIdForRFP()) { - aError.ThrowNotFoundError("Invalid pointer id"); - return; - } if (!PointerEventHandler::GetPointerInfo(aPointerId)) { aError.ThrowNotFoundError("Invalid pointer id"); return; ===================================== dom/events/PointerEvent.cpp ===================================== @@ -224,39 +224,78 @@ NS_INTERFACE_MAP_END_INHERITING(MouseEvent) NS_IMPL_ADDREF_INHERITED(PointerEvent, MouseEvent) NS_IMPL_RELEASE_INHERITED(PointerEvent, MouseEvent) -void PointerEvent::GetPointerType(nsAString& aPointerType) { +uint16_t PointerEvent::ResistantInputSource(CallerType aCallerType) const { + const uint16_t inputSource = mEvent->AsPointerEvent()->mInputSource; + if (!ShouldResistFingerprinting(aCallerType)) { + return inputSource; + } + + MOZ_ASSERT(IsTrusted()); + + // Bug 1953665: Pen events are inconsistent between platforms. + // They might emit touch events on Windows and Android, but only mouse events + // in other platforms. In particular, touch is always disabled on macOS. +#if defined(XP_WIN) + if (inputSource == MouseEvent_Binding::MOZ_SOURCE_TOUCH || + inputSource == MouseEvent_Binding::MOZ_SOURCE_MOUSE) { + return inputSource; + } + // Similar to nsWindow::DispatchTouchEventFromWMPointer. + switch (mEvent->mMessage) { + case ePointerMove: + return mEvent->AsPointerEvent()->mPressure == 0 + ? MouseEvent_Binding::MOZ_SOURCE_MOUSE // hover + : MouseEvent_Binding::MOZ_SOURCE_TOUCH; + case ePointerUp: + case ePointerDown: + case ePointerCancel: + return MouseEvent_Binding::MOZ_SOURCE_TOUCH; + default: + return MouseEvent_Binding::MOZ_SOURCE_MOUSE; + } +#elif defined(MOZ_WIDGET_ANDROID) + return inputSource == MouseEvent_Binding::MOZ_SOURCE_MOUSE + ? MouseEvent_Binding::MOZ_SOURCE_MOUSE + : MouseEvent_Binding::MOZ_SOURCE_TOUCH; +#elif defined(MOZ_WIDGET_GTK) + return inputSource == MouseEvent_Binding::MOZ_SOURCE_TOUCH + ? MouseEvent_Binding::MOZ_SOURCE_TOUCH + : MouseEvent_Binding::MOZ_SOURCE_MOUSE; +#elif defined(MOZ_WIDGET_COCOA) + return MouseEvent_Binding::MOZ_SOURCE_MOUSE; +#else + return inputSource; +#endif +} + +void PointerEvent::GetPointerType(nsAString& aPointerType, + CallerType aCallerType) { if (mPointerType.isSome()) { aPointerType = mPointerType.value(); return; } - -#if SPOOFED_MAX_TOUCH_POINTS <= 0 - if (ShouldResistFingerprinting()) { - aPointerType.AssignLiteral("mouse"); - return; - } -#endif - - ConvertPointerTypeToString(mEvent->AsPointerEvent()->mInputSource, - aPointerType); + ConvertPointerTypeToString(ResistantInputSource(aCallerType), aPointerType); } int32_t PointerEvent::PointerId() { - return (ShouldResistFingerprinting(true)) - ? PointerEventHandler::GetSpoofedPointerIdForRFP() - : mEvent->AsPointerEvent()->pointerId; + return mEvent->AsPointerEvent()->pointerId; } -double PointerEvent::Width() const { - return ShouldResistFingerprinting() ? 1.0 : mEvent->AsPointerEvent()->mWidth; +double PointerEvent::Width(CallerType aCallerType) const { + return ShouldResistFingerprinting(aCallerType) + ? 1.0 + : mEvent->AsPointerEvent()->mWidth; } -double PointerEvent::Height() const { - return ShouldResistFingerprinting() ? 1.0 : mEvent->AsPointerEvent()->mHeight; +double PointerEvent::Height(CallerType aCallerType) const { + return ShouldResistFingerprinting(aCallerType) + ? 1.0 + : mEvent->AsPointerEvent()->mHeight; } -float PointerEvent::Pressure() { - if (mEvent->mMessage == ePointerUp || !ShouldResistFingerprinting()) { +float PointerEvent::Pressure(CallerType aCallerType) { + if (mEvent->mMessage == ePointerUp || + !ShouldResistFingerprinting(aCallerType)) { return mEvent->AsPointerEvent()->mPressure; } @@ -273,14 +312,14 @@ float PointerEvent::Pressure() { return spoofedPressure; } -float PointerEvent::TangentialPressure() { - return ShouldResistFingerprinting() +float PointerEvent::TangentialPressure(CallerType aCallerType) { + return ShouldResistFingerprinting(aCallerType) ? 0 : mEvent->AsPointerEvent()->tangentialPressure; } -int32_t PointerEvent::TiltX() { - if (ShouldResistFingerprinting()) { +int32_t PointerEvent::TiltX(CallerType aCallerType) { + if (ShouldResistFingerprinting(aCallerType)) { return 0; } if (mTiltX.isSome()) { @@ -291,8 +330,8 @@ int32_t PointerEvent::TiltX() { return *mTiltX; } -int32_t PointerEvent::TiltY() { - if (ShouldResistFingerprinting()) { +int32_t PointerEvent::TiltY(CallerType aCallerType) { + if (ShouldResistFingerprinting(aCallerType)) { return 0; } if (mTiltY.isSome()) { @@ -303,12 +342,14 @@ int32_t PointerEvent::TiltY() { return *mTiltY; } -int32_t PointerEvent::Twist() { - return ShouldResistFingerprinting() ? 0 : mEvent->AsPointerEvent()->twist; +int32_t PointerEvent::Twist(CallerType aCallerType) { + return ShouldResistFingerprinting(aCallerType) + ? 0 + : mEvent->AsPointerEvent()->twist; } -double PointerEvent::AltitudeAngle() { - if (ShouldResistFingerprinting()) { +double PointerEvent::AltitudeAngle(CallerType aCallerType) { + if (ShouldResistFingerprinting(aCallerType)) { return WidgetPointerHelper::GetDefaultAltitudeAngle(); } if (mAltitudeAngle.isSome()) { @@ -319,8 +360,8 @@ double PointerEvent::AltitudeAngle() { return *mAltitudeAngle; } -double PointerEvent::AzimuthAngle() { - if (ShouldResistFingerprinting()) { +double PointerEvent::AzimuthAngle(CallerType aCallerType) { + if (ShouldResistFingerprinting(aCallerType)) { return WidgetPointerHelper::GetDefaultAzimuthAngle(); } if (mAzimuthAngle.isSome()) { @@ -421,22 +462,22 @@ void PointerEvent::GetPredictedEvents( aPointerEvents.AppendElements(mPredictedEvents); } -bool PointerEvent::ShouldResistFingerprinting(bool aForPointerId) const { - // There are three simple situations we don't need to spoof this pointer +bool PointerEvent::ShouldResistFingerprinting(CallerType aCallerType) const { + // There are a few simple situations we don't need to spoof this pointer // event. - // 1. The pref privcy.resistFingerprinting' is false, we fast return here - // since we don't need to do any QI of following codes. - // 2. This event is generated by scripts. - // 3. This event is a mouse pointer event. + // * We are being called by a System caller + // * The pref privcy.resistFingerprinting' is false, we fast return here + // since we don't need to do any QI of following codes. + // * This event is generated by scripts. + // * This event is a mouse pointer event. // We don't need to check for the system group since pointer events won't be // dispatched to the system group. - RFPTarget target = - aForPointerId ? RFPTarget::PointerId : RFPTarget::PointerEvents; - if (!nsContentUtils::ShouldResistFingerprinting("Efficiency Check", target) || + RFPTarget target = RFPTarget::PointerEvents; + if (aCallerType == CallerType::System || + !nsContentUtils::ShouldResistFingerprinting("Efficiency Check", target) || !mEvent->IsTrusted() || - (mEvent->AsPointerEvent()->mInputSource == - MouseEvent_Binding::MOZ_SOURCE_MOUSE && - SPOOFED_MAX_TOUCH_POINTS == 0)) { + mEvent->AsPointerEvent()->mInputSource == + MouseEvent_Binding::MOZ_SOURCE_MOUSE) { return false; } ===================================== dom/events/PointerEvent.h ===================================== @@ -41,17 +41,19 @@ class PointerEvent : public MouseEvent { PointerEvent* AsPointerEvent() final { return this; } int32_t PointerId(); - double Width() const; - double Height() const; - float Pressure(); - float TangentialPressure(); - int32_t TiltX(); - int32_t TiltY(); - int32_t Twist(); - double AltitudeAngle(); - double AzimuthAngle(); + double Width(CallerType aCallerType = CallerType::System) const; + double Height(CallerType aCallerType = CallerType::System) const; + float Pressure(CallerType aCallerType = CallerType::System); + float TangentialPressure(CallerType aCallerType = CallerType::System); + int32_t TiltX(CallerType aCallerType = CallerType::System); + int32_t TiltY(CallerType aCallerType = CallerType::System); + int32_t Twist(CallerType aCallerType = CallerType::System); + double AltitudeAngle(CallerType aCallerType = CallerType::System); + double AzimuthAngle(CallerType aCallerType = CallerType::System); bool IsPrimary(); - void GetPointerType(nsAString& aPointerType); + void GetPointerType( + nsAString& aPointerType, + mozilla::dom::CallerType aCallerType = CallerType::System); static bool EnableGetCoalescedEvents(JSContext* aCx, JSObject* aGlobal); void GetCoalescedEvents(nsTArray<RefPtr<PointerEvent>>& aPointerEvents); void GetPredictedEvents(nsTArray<RefPtr<PointerEvent>>& aPointerEvents); @@ -62,7 +64,11 @@ class PointerEvent : public MouseEvent { private: // This method returns the boolean to indicate whether spoofing pointer // event for fingerprinting resistance. - bool ShouldResistFingerprinting(bool aForPointerId = false) const; + bool ShouldResistFingerprinting( + CallerType aCallerType = CallerType::System) const; + + uint16_t ResistantInputSource( + CallerType aCallerType = CallerType::System) const; // When the instance is a trusted `pointermove` event but the widget event // does not have proper coalesced events (typically, the event is synthesized ===================================== dom/events/PointerEventHandler.cpp ===================================== @@ -421,32 +421,6 @@ void PointerEventHandler::CheckPointerCaptureState(WidgetPointerEvent* aEvent) { PointerCaptureInfo* captureInfo = GetPointerCaptureInfo(aEvent->pointerId); - // When fingerprinting resistance is enabled, we need to map other pointer - // ids into the spoofed one. We don't have to do the mapping if the capture - // info exists for the non-spoofed pointer id because of we won't allow - // content to set pointer capture other than the spoofed one. Thus, it must be - // from chrome if the capture info exists in this case. And we don't have to - // do anything if the pointer id is the same as the spoofed one. - if (nsContentUtils::ShouldResistFingerprinting("Efficiency Check", - RFPTarget::PointerId) && - aEvent->pointerId != (uint32_t)GetSpoofedPointerIdForRFP() && - !captureInfo) { - PointerCaptureInfo* spoofedCaptureInfo = - GetPointerCaptureInfo(GetSpoofedPointerIdForRFP()); - - // We need to check the target element's document should resist - // fingerprinting. If not, we don't need to send a capture event - // since the capture info of the original pointer id doesn't exist - // in this case. - if (!spoofedCaptureInfo || !spoofedCaptureInfo->mPendingElement || - !spoofedCaptureInfo->mPendingElement->OwnerDoc() - ->ShouldResistFingerprinting(RFPTarget::PointerEvents)) { - return; - } - - captureInfo = spoofedCaptureInfo; - } - if (!captureInfo || captureInfo->mPendingElement == captureInfo->mOverrideElement) { return; ===================================== dom/events/TouchEvent.cpp ===================================== @@ -225,38 +225,40 @@ bool TouchEvent::PrefEnabled(nsIDocShell* aDocShell) { } else if (touchEventsOverride == mozilla::dom::TouchEventsOverride::Disabled) { enabled = false; + } else if (nsContentUtils::ShouldResistFingerprinting( + aDocShell, RFPTarget::PointerEvents)) { +#ifdef MOZ_WIDGET_COCOA + enabled = false; +#else + enabled = true; +#endif } else { const int32_t prefValue = StaticPrefs::dom_w3c_touch_events_enabled(); if (prefValue == 2) { - if (nsContentUtils::ShouldResistFingerprinting( - aDocShell, RFPTarget::PointerEvents)) { - enabled = SPOOFED_MAX_TOUCH_POINTS != 0; - } else { - enabled = PlatformSupportsTouch(); - - static bool firstTime = true; - // The touch screen data seems to be inaccurate in the parent process, - // and we really need the crash annotation in child processes. - if (firstTime && !XRE_IsParentProcess()) { - CrashReporter::RecordAnnotationBool( - CrashReporter::Annotation::HasDeviceTouchScreen, enabled); - firstTime = false; - } + enabled = PlatformSupportsTouch(); + + static bool firstTime = true; + // The touch screen data seems to be inaccurate in the parent process, + // and we really need the crash annotation in child processes. + if (firstTime && !XRE_IsParentProcess()) { + CrashReporter::RecordAnnotationBool( + CrashReporter::Annotation::HasDeviceTouchScreen, enabled); + firstTime = false; + } #if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) - if (enabled && aDocShell) { - // APZ might be disabled on this particular widget, in which case - // TouchEvent support will also be disabled. Try to detect that. - RefPtr<nsPresContext> pc = aDocShell->GetPresContext(); - if (pc) { - nsCOMPtr<nsIWidget> widget = pc->GetRootWidget(); - if (widget) { - enabled &= widget->AsyncPanZoomEnabled(); - } + if (enabled && aDocShell) { + // APZ might be disabled on this particular widget, in which case + // TouchEvent support will also be disabled. Try to detect that. + RefPtr<nsPresContext> pc = aDocShell->GetPresContext(); + if (pc) { + nsCOMPtr<nsIWidget> widget = pc->GetRootWidget(); + if (widget) { + enabled &= widget->AsyncPanZoomEnabled(); } } -#endif } +#endif } else { enabled = !!prefValue; } ===================================== dom/webidl/PointerEvent.webidl ===================================== @@ -14,16 +14,26 @@ interface PointerEvent : MouseEvent readonly attribute long pointerId; + [NeedsCallerType] readonly attribute double width; + [NeedsCallerType] readonly attribute double height; + [NeedsCallerType] readonly attribute float pressure; + [NeedsCallerType] readonly attribute float tangentialPressure; + [NeedsCallerType] readonly attribute long tiltX; + [NeedsCallerType] readonly attribute long tiltY; + [NeedsCallerType] readonly attribute long twist; + [NeedsCallerType] readonly attribute double altitudeAngle; + [NeedsCallerType] readonly attribute double azimuthAngle; + [NeedsCallerType] readonly attribute DOMString pointerType; readonly attribute boolean isPrimary; ===================================== layout/base/PositionedEventTargeting.cpp ===================================== @@ -16,6 +16,7 @@ #include "mozilla/ToString.h" #include "mozilla/ViewportUtils.h" #include "mozilla/dom/MouseEventBinding.h" +#include "mozilla/dom/TouchEvent.h" #include "mozilla/gfx/Matrix.h" #include "mozilla/layers/LayersTypes.h" #include "nsContainerFrame.h" @@ -173,9 +174,7 @@ static bool HasTouchListener(const nsIContent* aContent) { return false; } - // FIXME: Should this really use the pref rather than TouchEvent::PrefEnabled - // or such? - if (!StaticPrefs::dom_w3c_touch_events_enabled()) { + if (!TouchEvent::PrefEnabled(aContent->OwnerDoc()->GetDocShell())) { return false; } ===================================== toolkit/components/resistfingerprinting/RFPTargets.inc ===================================== @@ -34,7 +34,7 @@ ITEM_VALUE(NavigatorHWConcurrency, 16) ITEM_VALUE(NavigatorOscpu, 17) ITEM_VALUE(NavigatorPlatform, 18) ITEM_VALUE(NavigatorUserAgent, 19) -ITEM_VALUE(PointerId, 20) +// We no longer use PointerId, it can renamed and reused ITEM_VALUE(StreamVideoFacingMode, 21) ITEM_VALUE(JSDateTimeUTC, 22) ITEM_VALUE(JSMathFdlibm, 23) @@ -104,6 +104,7 @@ ITEM_VALUE(DiskStorageLimit, 70) ITEM_VALUE(WebCodecs, 71) ITEM_VALUE(NavigatorHWConcurrencyTiered,74) +// !!! Adding a new target? Rename PointerId and repurpose it. // !!! Don't forget to update kDefaultFingerprintingProtections in nsRFPService.cpp // if necessary. ===================================== toolkit/components/resistfingerprinting/nsRFPService.cpp ===================================== @@ -303,13 +303,6 @@ Maybe<bool> nsRFPService::HandleExeptionalRFPTargets( StaticPrefs::privacy_spoof_english_DoNotUseDirectly() == 2); } - // We don't spoof the pointerId on multi-touch devices. -#if SPOOFED_MAX_TOUCH_POINTS > 0 - if (aTarget == RFPTarget::PointerId) { - return Some(false); - } -#endif - #ifdef ANDROID if (aTarget == RFPTarget::FontVisibilityBaseSystem || aTarget == RFPTarget::FontVisibilityLangPack) { ===================================== widget/WidgetEventImpl.cpp ===================================== @@ -589,23 +589,6 @@ bool WidgetEvent::IsBlockedForFingerprintingResistance() const { keyboardEvent->mKeyNameIndex == KEY_NAME_INDEX_Control || keyboardEvent->mKeyNameIndex == KEY_NAME_INDEX_AltGraph); } - case ePointerEventClass: { - if (IsPointerEventMessageOriginallyMouseEventMessage(mMessage)) { - return false; - } - - if (SPOOFED_MAX_TOUCH_POINTS > 0) { - return false; - } - - const WidgetPointerEvent* pointerEvent = AsPointerEvent(); - - // We suppress the pointer events if it is not primary for fingerprinting - // resistance. It is because of that we want to spoof any pointer event - // into a mouse pointer event and the mouse pointer event only has - // isPrimary as true. - return !pointerEvent->mIsPrimary; - } default: return false; } View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/7966a2… -- View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/7966a2… You're receiving this email because of your account on gitlab.torproject.org.
1 0
0 0
  • ← Newer
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • ...
  • 14
  • Older →

HyperKitty Powered by HyperKitty version 1.3.12.