tbb-commits
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
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
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

[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
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

[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
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

[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
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

[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
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

[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
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

[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
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

[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
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

[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
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

[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
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