Richard Pospesel pushed to branch tor-browser-102.5.0esr-12.5-1 at The Tor Project / Applications / Tor Browser
Commits: 1f87c8fb by hackademix at 2022-12-20T18:26:01+01:00 Bug 32308: use direct browser sizing for letterboxing.
- - - - -
8 changed files:
- − browser/actors/RFPHelperChild.jsm - − browser/actors/RFPHelperParent.jsm - browser/actors/moz.build - browser/base/content/browser.css - browser/components/resistfingerprinting/test/browser/browser_dynamical_window_rounding.js - layout/style/res/ua.css - toolkit/components/resistfingerprinting/RFPHelper.jsm - toolkit/modules/FinderParent.jsm
Changes:
===================================== browser/actors/RFPHelperChild.jsm deleted ===================================== @@ -1,27 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -var EXPORTED_SYMBOLS = ["RFPHelperChild"]; - -const { XPCOMUtils } = ChromeUtils.import( - "resource://gre/modules/XPCOMUtils.jsm" -); - -const kPrefLetterboxing = "privacy.resistFingerprinting.letterboxing"; - -XPCOMUtils.defineLazyPreferenceGetter( - this, - "isLetterboxingEnabled", - kPrefLetterboxing, - false -); - -class RFPHelperChild extends JSWindowActorChild { - handleEvent(event) { - if (isLetterboxingEnabled && event.type == "resize") { - this.sendAsyncMessage("Letterboxing:ContentSizeUpdated"); - } - } -}
===================================== browser/actors/RFPHelperParent.jsm deleted ===================================== @@ -1,32 +0,0 @@ -1; /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -var EXPORTED_SYMBOLS = ["RFPHelperParent"]; - -const { XPCOMUtils } = ChromeUtils.import( - "resource://gre/modules/XPCOMUtils.jsm" -); - -const kPrefLetterboxing = "privacy.resistFingerprinting.letterboxing"; - -XPCOMUtils.defineLazyPreferenceGetter( - this, - "isLetterboxingEnabled", - kPrefLetterboxing, - false -); - -class RFPHelperParent extends JSWindowActorParent { - receiveMessage(aMessage) { - if ( - isLetterboxingEnabled && - aMessage.name == "Letterboxing:ContentSizeUpdated" - ) { - let browser = this.browsingContext.top.embedderElement; - let window = browser.ownerGlobal; - window.RFPHelper.contentSizeUpdated(window); - } - } -}
===================================== browser/actors/moz.build ===================================== @@ -82,8 +82,6 @@ FINAL_TARGET_FILES.actors += [ "PromptParent.jsm", "RefreshBlockerChild.jsm", "RefreshBlockerParent.jsm", - "RFPHelperChild.jsm", - "RFPHelperParent.jsm", "ScreenshotsComponentChild.jsm", "SearchSERPTelemetryChild.jsm", "SearchSERPTelemetryParent.jsm",
===================================== browser/base/content/browser.css ===================================== @@ -102,16 +102,19 @@ body { Never modify the following selector without synchronizing LETTERBOX_CSS_SELECTOR in RFPHelper.jsm! **/ -.letterboxing .browserStack > browser:not(.exclude-letterboxing) { - margin: 0; /* to be dynamically set by RFHelper.jsm */ +.letterboxing .browserStack:not(.exclude-letterboxing) > browser { + /* width & height to be dynamically set by RFPHelper.jsm */ outline: 1px solid var(--chrome-content-separator-color); }
-browser.exclude-letterboxing { - margin: 0 !important; +.exclude-letterboxing > browser { outline: initial; }
+.letterboxing-ready .browserStack:not(.exclude-letterboxing) { + place-content: start center; +} + /* extend down the toolbar's colors when letterboxing is enabled */ .letterboxing { background-color: var(--toolbar-bgcolor);
===================================== browser/components/resistfingerprinting/test/browser/browser_dynamical_window_rounding.js ===================================== @@ -82,7 +82,7 @@ async function test_dynamical_window_rounding(aWindow, aCheckFunc) { // We need to wait for the updating the margins for the newly opened tab, or // it will affect the following tests. let promiseForTheFirstRounding = TestUtils.topicObserved( - "test:letterboxing:update-margin-finish" + "test:letterboxing:update-size-finish" );
info("Open a content tab for testing."); @@ -108,7 +108,7 @@ async function test_dynamical_window_rounding(aWindow, aCheckFunc) { let caseString = "Case " + width + "x" + height + ": "; // Create a promise for waiting for the margin update. let promiseRounding = TestUtils.topicObserved( - "test:letterboxing:update-margin-finish" + "test:letterboxing:update-size-finish" );
let { containerWidth, containerHeight } = getContainerSize(tab); @@ -316,7 +316,7 @@ async function test_findbar(aWindow) { );
let promiseRounding = TestUtils.topicObserved( - "test:letterboxing:update-margin-finish" + "test:letterboxing:update-size-finish" );
let findBarOpenPromise = BrowserTestUtils.waitForEvent( @@ -330,7 +330,7 @@ async function test_findbar(aWindow) { ok(true, "Margin updated when findbar opened");
promiseRounding = TestUtils.topicObserved( - "test:letterboxing:update-margin-finish" + "test:letterboxing:update-size-finish" );
let findBarClosePromise = BrowserTestUtils.waitForEvent(
===================================== layout/style/res/ua.css ===================================== @@ -356,8 +356,8 @@ left: 0 !important; right: 0 !important; bottom: 0 !important; - width: 100% !important; - height: 100% !important; + width: 100%; + height: 100%; margin: 0 !important; min-width: 0 !important; max-width: none !important; @@ -368,6 +368,11 @@ transform: none !important; }
+*|*:fullscreen:not(:root, .letterboxing .browserStack:not(.exclude-letterboxing) > browser) { + width: 100% !important; + height: 100% !important; +} + xul|*:fullscreen:not(:root, [hidden="true"]) { /* The position: fixed; property above used to force the computed display * value to block. It is no longer the case now, so we manually set it here to
===================================== toolkit/components/resistfingerprinting/RFPHelper.jsm ===================================== @@ -95,7 +95,7 @@ class _RFPHelper { case kTopicDOMWindowOpened: // We attach to the newly created window by adding tabsProgressListener // and event listener on it. We listen for new tabs being added or - // the change of the content principal and apply margins accordingly. + // the change of the content principal and round browser sizes accordingly. this._handleDOMWindowOpened(subject); break; default: @@ -106,8 +106,9 @@ class _RFPHelper { handleEvent(aMessage) { switch (aMessage.type) { case "TabOpen": { - let tab = aMessage.target; - this._addOrClearContentMargin(tab.linkedBrowser, /* isNewTab = */ true); + let browser = aMessage.target.linkedBrowser; + this._roundOrResetContentSize(browser, /* isNewTab = */ true); + browser.ownerGlobal._rfpResizeObserver.observe(browser.parentElement); break; } default: @@ -131,10 +132,6 @@ class _RFPHelper { } }
- contentSizeUpdated(win) { - this._updateMarginsForTabsInWindow(win); - } - // ============================================================================ // Language Prompt // ============================================================================ @@ -291,44 +288,22 @@ class _RFPHelper { // ============================================================================ /** * We use the TabsProgressListener to catch the change of the content - * principal. We would clear the margins around the content viewport if - * it is the system principal. + * principal. We would reset browser size if it is the system principal. */ onLocationChange(aBrowser) { - this._addOrClearContentMargin(aBrowser); + this._roundOrResetContentSize(aBrowser); }
_handleLetterboxingPrefChanged() { if (Services.prefs.getBoolPref(kPrefLetterboxing, false)) { Services.ww.registerNotification(this); - this._registerActor(); this._attachAllWindows(); } else { - this._unregisterActor(); this._detachAllWindows(); Services.ww.unregisterNotification(this); } }
- _registerActor() { - ChromeUtils.registerWindowActor("RFPHelper", { - parent: { - moduleURI: "resource:///actors/RFPHelperParent.jsm", - }, - child: { - moduleURI: "resource:///actors/RFPHelperChild.jsm", - events: { - resize: {}, - }, - }, - allFrames: true, - }); - } - - _unregisterActor() { - ChromeUtils.unregisterWindowActor("RFPHelper"); - } - // The function to parse the dimension set from the pref value. The pref value // should be formated as 'width1xheight1, width2xheight2, ...'. For // example, '100x100, 200x200, 400x200 ...'. @@ -354,13 +329,13 @@ class _RFPHelper {
getLetterboxingDefaultRule(aBrowser) { let document = aBrowser.ownerDocument; - return (document._letterboxingMarginsRule ||= (() => { + return (document._letterBoxingSizingRule ||= (() => { // 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 const LETTERBOX_CSS_URL = "chrome://browser/content/browser.css"; const LETTERBOX_CSS_SELECTOR = - ".letterboxing .browserStack > browser:not(.exclude-letterboxing)"; + ".letterboxing .browserStack:not(.exclude-letterboxing) > browser"; for (let ss of document.styleSheets) { if (ss.href !== LETTERBOX_CSS_URL) { continue; @@ -389,23 +364,23 @@ class _RFPHelper { ); }
- _addOrClearContentMargin(aBrowser, isNewTab = false) { + _roundOrResetContentSize(aBrowser, isNewTab = false) { // We won't do anything for lazy browsers. - if (!aBrowser.isConnected) { + if (!aBrowser?.isConnected) { return; } if (this._noLetterBoxingFor(aBrowser)) { // this tab doesn't need letterboxing - this._clearContentViewMargin(aBrowser); + this._resetContentSize(aBrowser); } else { - this._roundContentView(aBrowser, isNewTab); + this._roundContentSize(aBrowser, isNewTab); } }
/** - * Given a width or height, returns the appropriate margin to apply. + * Given a width or height, rounds it with the proper stepping. */ - steppedRange(aDimension) { + steppedSize(aDimension) { let stepping; if (aDimension <= 50) { return 0; @@ -417,22 +392,21 @@ class _RFPHelper { stepping = 200; }
- return (aDimension % stepping) / 2; + return aDimension - (aDimension % stepping); }
/** - * The function will round the given browser by adding margins around the - * content viewport. + * The function will round the given browser size */ - async _roundContentView(aBrowser, isNewTab = false) { - let logPrefix = `_roundContentView[${Math.random()}]`; + async _roundContentSize(aBrowser, isNewTab = false) { + let logPrefix = `_roundContentSize[${Math.random()}]`; log(logPrefix); - aBrowser.classList.remove("exclude-letterboxing"); let win = aBrowser.ownerGlobal; let browserContainer = aBrowser .getTabBrowser() .getBrowserContainer(aBrowser); let browserParent = aBrowser.parentElement; + browserParent.classList.remove("exclude-letterboxing"); let [ [contentWidth, contentHeight], [parentWidth, parentHeight], @@ -455,25 +429,27 @@ class _RFPHelper { return; }
- const calcMargins = (aWidth, aHeight) => { + const roundDimensions = (aWidth, aHeight) => { + const r = (aWidth, aHeight) => ({ + width: `${aWidth}px`, + height: `${aHeight}px`, + }); + let result; - log(`${logPrefix} calcMargins(${aWidth}, ${aHeight})`); + log(`${logPrefix} roundDimensions(${aWidth}, ${aHeight})`); // If the set is empty, we will round the content with the default // stepping size. if (!this._letterboxingDimensions.length) { - result = { - width: this.steppedRange(aWidth), - height: this.steppedRange(aHeight), - }; + result = r(this.steppedSize(aWidth), this.steppedSize(aHeight)); log( - `${logPrefix} calcMargins(${aWidth}, ${aHeight}) = ${result.width} x ${result.height}` + `${logPrefix} roundDimensions(${aWidth}, ${aHeight}) = ${result.width} x ${result.height}` ); return result; }
let matchingArea = aWidth * aHeight; let minWaste = Number.MAX_SAFE_INTEGER; - let targetDimensions = undefined; + let targetDimensions;
// Find the desired dimensions which waste the least content area. for (let dim of this._letterboxingDimensions) { @@ -493,119 +469,119 @@ class _RFPHelper {
// If we cannot find any dimensions match to the real content window, this // means the content area is smaller the smallest size in the set. In this - // case, we won't apply any margins. - if (!targetDimensions) { - result = { - width: 0, - height: 0, - }; - } else { - result = { - width: (aWidth - targetDimensions.width) / 2, - height: (aHeight - targetDimensions.height) / 2, - }; - } + // case, we won't round the size and default to the max. + result = targetDimensions + ? r(targetDimensions.width, targetDimensions.height) + : r(aWidth, aHeight);
log( - `${logPrefix} calcMargins(${aWidth}, ${aHeight}) = ${result.width} x ${result.height}` + `${logPrefix} roundDimensions(${aWidth}, ${aHeight}) = ${result.width} x ${result.height}` ); return result; };
- // Calculating the margins around the browser element in order to round the - // content viewport. We will use a 200x100 stepping if the dimension set - // is not given. - // Margin and outline colors are set in browser.css (.letterboxing * selectors). - - const buildMarginStyleString = (aWidth, aHeight) => { - const marginDims = calcMargins(aWidth, aHeight); - - // snap browser element to top - const top = 0, - // and leave 'double' margin at the bottom - bottom = 2 * marginDims.height, - // identical margins left and right - left = marginDims.width, - right = marginDims.width; - - return `${top}px ${right}px ${bottom}px ${left}px`; - }; - - const marginChanges = Object.assign([], { - queueIfNeeded({ style }, margin) { - if (style.margin !== margin) { - this.push(() => { - style.margin = margin; - }); + const styleChanges = Object.assign([], { + queueIfNeeded({ style }, props) { + for (let [name, value] of Object.entries(props)) { + if (style[name] !== value) { + this.push(() => { + style.setProperty(name, value, "important"); + }); + } } }, perform() { win.requestAnimationFrame(() => { for (let change of this) { - change(); + try { + change(); + } catch (e) { + logConsole.error(e); + } } }); }, });
- marginChanges.queueIfNeeded( + const roundedDefault = roundDimensions(containerWidth, containerHeight); + + styleChanges.queueIfNeeded( this.getLetterboxingDefaultRule(aBrowser), - buildMarginStyleString(containerWidth, containerHeight) + roundedDefault );
- const marginStyleString = + const roundedInline = !isNewTab && // new tabs cannot have extra UI components (containerHeight > parentHeight || containerWidth > parentWidth) ? // optional UI components such as the notification box, the find bar // or devtools are constraining this browser's size: recompute custom - buildMarginStyleString(parentWidth, parentHeight) - : ""; // otherwise we can keep the default letterboxing margins - marginChanges.queueIfNeeded(aBrowser, marginStyleString); + roundDimensions(parentWidth, parentHeight) + : { width: "", height: "" }; // otherwise we can keep the default (rounded) size + styleChanges.queueIfNeeded(aBrowser, roundedInline);
// If the size of the content is already quantized, we do nothing. - if (!marginChanges.length) { + if (!styleChanges.length) { log(`${logPrefix} is_rounded == true`); if (this._isLetterboxingTesting) { log( - `${logPrefix} is_rounded == true test:letterboxing:update-margin-finish` + `${logPrefix} is_rounded == true test:letterboxing:update-size-finish` ); Services.obs.notifyObservers( null, - "test:letterboxing:update-margin-finish" + "test:letterboxing:update-size-finish" ); } return; }
- log(`${logPrefix} setting margins to ${marginStyleString}`); - // Here we set the browser's margin to round its content size. + log( + `${logPrefix} setting size to ${JSON.stringify({ + roundedDefault, + roundedInline, + })}` + ); + // Here we round the browser's size through CSS. // A "border" visual is created by using a CSS outline, which does't // affect layout, while the background appearance is borrowed from the // toolbar and set in the .letterboxing ancestor (see browser.css). - marginChanges.perform(); + styleChanges.perform(); }
- _clearContentViewMargin(aBrowser) { - aBrowser.classList.add("exclude-letterboxing"); + _resetContentSize(aBrowser) { + aBrowser.parentElement.classList.add("exclude-letterboxing"); }
- _updateMarginsForTabsInWindow(aWindow) { + _updateSizeForTabsInWindow(aWindow) { let tabBrowser = aWindow.gBrowser;
tabBrowser.tabpanels?.classList.add("letterboxing");
for (let tab of tabBrowser.tabs) { let browser = tab.linkedBrowser; - this._addOrClearContentMargin(browser); + this._roundOrResetContentSize(browser); } + // we need to add this class late because otherwise new windows get maximized + aWindow.setTimeout(() => { + tabBrowser.tabpanels?.classList.add("letterboxing-ready"); + }); }
_attachWindow(aWindow) { aWindow.gBrowser.addTabsProgressListener(this); aWindow.addEventListener("TabOpen", this); - + const resizeObserver = (aWindow._rfpResizeObserver = new aWindow.ResizeObserver( + entries => { + for (let { target } of entries) { + this._roundOrResetContentSize(target.querySelector("browser")); + } + } + )); + // observe resizing of each browser's parent (gets rid of RPC from content windows) + for (let bs of aWindow.document.querySelectorAll(".browserStack")) { + resizeObserver.observe(bs); + } // Rounding the content viewport. - this._updateMarginsForTabsInWindow(aWindow); + this._updateSizeForTabsInWindow(aWindow); }
_attachAllWindows() { @@ -625,15 +601,17 @@ class _RFPHelper { _detachWindow(aWindow) { let tabBrowser = aWindow.gBrowser; tabBrowser.removeTabsProgressListener(this); + aWindow._rfpResizeObserver.disconnect(); + delete aWindow._rfpResizeObserver; aWindow.removeEventListener("TabOpen", this);
// revert tabpanel's style to default tabBrowser.tabpanels?.classList.remove("letterboxing");
- // and restore default margins on each browser element + // and restore default size on each browser element for (let tab of tabBrowser.tabs) { let browser = tab.linkedBrowser; - this._clearContentViewMargin(browser); + this._resetContentSize(browser); } }
===================================== toolkit/modules/FinderParent.jsm ===================================== @@ -27,15 +27,6 @@ ChromeUtils.defineModuleGetter( "resource://gre/modules/Geometry.jsm" );
-const kPrefLetterboxing = "privacy.resistFingerprinting.letterboxing"; - -XPCOMUtils.defineLazyPreferenceGetter( - this, - "isLetterboxingEnabled", - kPrefLetterboxing, - false -); - XPCOMUtils.defineLazyPreferenceGetter( this, "isSoundEnabled", @@ -586,24 +577,10 @@ FinderParent.prototype = { onFindbarClose() { this._lastFoundBrowsingContext = null; this.sendMessageToAllContexts("Finder:FindbarClose"); - - if (isLetterboxingEnabled) { - let window = this._browser.ownerGlobal; - if (window.RFPHelper) { - window.RFPHelper.contentSizeUpdated(window); - } - } },
onFindbarOpen() { this.sendMessageToAllContexts("Finder:FindbarOpen"); - - if (isLetterboxingEnabled) { - let window = this._browser.ownerGlobal; - if (window.RFPHelper) { - window.RFPHelper.contentSizeUpdated(window); - } - } },
onModalHighlightChange(aUseModalHighlight) {
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/1f87c8fb...
tbb-commits@lists.torproject.org