Skip to content
Snippets Groups Projects
Commit b3f11e36 authored by ma1's avatar ma1 Committed by Pier Angelo Vendrame
Browse files

BB 41631: Prevent weird initial window dimensions caused by subpixel computations

parent 0cf94534
No related branches found
No related tags found
1 merge request!1503TB 43415, part 3: Shuffle commits
......@@ -4,6 +4,7 @@
* You can obtain one at https://mozilla.org/MPL/2.0/. */
import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
import * as constants from "resource://gre/modules/RFPTargetConstants.sys.mjs";
const kPrefResistFingerprinting = "privacy.resistFingerprinting";
......@@ -35,6 +36,20 @@ function log(...args) {
lazy.logConsole.log(...args);
}
function forEachWindow(callback) {
const windowList = Services.wm.getEnumerator("navigator:browser");
while (windowList.hasMoreElements()) {
const win = windowList.getNext();
if (win.gBrowser && !win.closed) {
try {
callback(win);
} catch (e) {
lazy.logConsole.error(e);
}
}
}
}
class _RFPHelper {
// ============================================================================
// Shared Setup
......@@ -172,7 +187,11 @@ class _RFPHelper {
(this.rfpEnabled = Services.prefs.getBoolPref(kPrefResistFingerprinting))
) {
this._addRFPObservers();
Services.ww.registerNotification(this);
forEachWindow(win => this._attachWindow(win));
} else {
forEachWindow(win => this._detachWindow(win));
Services.ww.unregisterNotification(this);
this._removeRFPObservers();
}
}
......@@ -304,12 +323,12 @@ class _RFPHelper {
}
_handleLetterboxingPrefChanged() {
if (Services.prefs.getBoolPref(kPrefLetterboxing, false)) {
Services.ww.registerNotification(this);
this._attachAllWindows();
} else {
this._detachAllWindows();
Services.ww.unregisterNotification(this);
this.letterboxingEnabled = Services.prefs.getBoolPref(
kPrefLetterboxing,
false
);
if (this.rfpEnabled) {
forEachWindow(win => this._updateSizeForTabsInWindow(win));
}
}
......@@ -387,22 +406,23 @@ class _RFPHelper {
}
}
stepping(aDimension, aIsWidth) {
if (aDimension <= 500) {
return 50;
} else if (aDimension <= 1600) {
return aIsWidth ? 200 : 100;
}
return 200;
}
/**
* Given a width or height, rounds it with the proper stepping.
*/
steppedSize(aDimension, aIsWidth = false) {
let stepping;
if (aDimension <= 50) {
return aDimension;
} else if (aDimension <= 500) {
stepping = 50;
} else if (aDimension <= 1600) {
stepping = aIsWidth ? 200 : 100;
} else {
stepping = 200;
}
return aDimension - (aDimension % stepping);
return aDimension - (aDimension % this.stepping(aDimension, aIsWidth));
}
/**
......@@ -429,6 +449,17 @@ class _RFPHelper {
])
);
const isInitialSize =
win._rfpOriginalSize &&
win.outerWidth === win._rfpOriginalSize.width &&
win.outerHeight === win._rfpOriginalSize.height;
// We may need to shrink this window to rounded size if the browser container
// area is taller than the original, meaning extra chrome (like the optional
// "Only Show on New Tab" bookmarks toobar) was present and now gone.
const needToShrink =
isInitialSize && containerHeight > win._rfpOriginalSize.containerHeight;
log(
`${logPrefix} contentWidth=${contentWidth} contentHeight=${contentHeight} parentWidth=${parentWidth} parentHeight=${parentHeight} containerWidth=${containerWidth} containerHeight=${containerHeight}${
isNewTab ? " (new tab)." : "."
......@@ -456,7 +487,10 @@ class _RFPHelper {
log(`${logPrefix} roundDimensions(${aWidth}, ${aHeight})`);
let result;
if (!this.letterboxingEnabled) {
// just round size to int
return r(aWidth, aHeight);
}
// If the set is empty, we will round the content with the default
// stepping size.
......@@ -511,6 +545,17 @@ class _RFPHelper {
lazy.logConsole.error(e);
}
}
if (needToShrink && win.shrinkToLetterbox()) {
win.addEventListener(
"resize",
() => {
// We need to record the "new" initial size in this listener
// because resized dimensions are not immediately available.
RFPHelper._recordWindowSize(win);
},
{ once: true }
);
}
});
},
});
......@@ -609,10 +654,41 @@ class _RFPHelper {
// we need to add this class late because otherwise new windows get maximized
aWindow.setTimeout(() => {
tabBrowser.tabpanels?.classList.add("letterboxing-ready");
if (!aWindow._rfpOriginalSize) {
this._recordWindowSize(aWindow);
}
});
}
_recordWindowSize(aWindow) {
aWindow.promiseDocumentFlushed(() => {
aWindow._rfpOriginalSize = {
width: aWindow.outerWidth,
height: aWindow.outerHeight,
containerHeight: aWindow.gBrowser.getBrowserContainer()?.clientHeight,
};
log("Recording original window size", aWindow._rfpOriginalSize);
});
}
// We will attach this method to each browser window. When called
// it will instantly resize the window to exactly fit the selected
// (possibly letterboxed) browser.
// Returns true if a window resize will occur, false otherwise.
shrinkToLetterbox() {
let { selectedBrowser } = this.gBrowser;
let stack = selectedBrowser.closest(".browserStack");
const outer = stack.getBoundingClientRect();
const inner = selectedBrowser.getBoundingClientRect();
if (inner.width !== outer.witdh || inner.height !== outer.height) {
this.resizeBy(inner.width - outer.width, inner.height - outer.height);
return true;
}
return false;
}
_attachWindow(aWindow) {
this._fixRounding(aWindow);
aWindow.gBrowser.addTabsProgressListener(this);
aWindow.addEventListener("TabOpen", this);
const resizeObserver = (aWindow._rfpResizeObserver =
......@@ -629,20 +705,6 @@ class _RFPHelper {
this._updateSizeForTabsInWindow(aWindow);
}
_attachAllWindows() {
let windowList = Services.wm.getEnumerator("navigator:browser");
while (windowList.hasMoreElements()) {
let win = windowList.getNext();
if (win.closed || !win.gBrowser) {
continue;
}
this._attachWindow(win);
}
}
_detachWindow(aWindow) {
let tabBrowser = aWindow.gBrowser;
tabBrowser.removeTabsProgressListener(this);
......@@ -660,20 +722,6 @@ class _RFPHelper {
}
}
_detachAllWindows() {
let windowList = Services.wm.getEnumerator("navigator:browser");
while (windowList.hasMoreElements()) {
let win = windowList.getNext();
if (win.closed || !win.gBrowser) {
continue;
}
this._detachWindow(win);
}
}
_handleDOMWindowOpened(win) {
let self = this;
......@@ -694,6 +742,49 @@ class _RFPHelper {
);
}
_fixRounding(aWindow) {
if (!this.rfpEnabled) {
return;
}
// tor-browser#43205: in case of subpixels, new windows might have a wrong
// size because of platform-specific bugs (e.g., Bug 1947439 on Windows).
const contentContainer = aWindow.document.getElementById("browser");
const rect = contentContainer.getBoundingClientRect();
const steppingWidth = this.stepping(rect.width, true);
const steppingHeight = this.stepping(rect.height, false);
const deltaWidth =
rect.width - steppingWidth * Math.round(rect.width / steppingWidth);
const deltaHeight =
rect.height - steppingHeight * Math.round(rect.height / steppingHeight);
// It seems that under X11, a window cannot have all the possible (integer)
// sizes (see the videos on tor-browser#43205 and Bug 1947439)...
// We observed this behavior with 1.25 scaling, but we could not find
// where it happens exactly, so this code might be wrong.
// On the same system, this problem does not happen with Wayland.
if (AppConstants.platform === "linux") {
let targetWidth = aWindow.outerWidth - deltaWidth;
let targetHeight = aWindow.outerHeight - deltaHeight;
const x11Size = s =>
Math.floor(
// This first rounding is done by Gecko, rather than X11.
Math.round(s * aWindow.devicePixelRatio) / aWindow.devicePixelRatio
);
const x11Width = x11Size(targetWidth);
const x11Height = x11Size(targetHeight);
if (x11Width < targetWidth) {
targetWidth = x11Width + 2;
}
if (x11Height < targetHeight) {
targetHeight = x11Height + 2;
}
aWindow.resizeTo(targetWidth, targetHeight);
} else {
aWindow.resizeBy(deltaWidth, deltaHeight);
}
}
getTargets() {
return constants.Targets;
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment