Pier Angelo Vendrame pushed to branch mullvad-browser-128.2.0esr-14.0-1 at The Tor Project / Applications / Mullvad Browser
Commits: f5fe7501 by Fatih at 2024-09-02T10:35:23+02:00 Bug 1885101: Match screen and window properties with top window for ScreenRect, ScreenAvailRect and WindowOuterSize. r=timhuang,emilio
This patch removes test_iframe.html. We remove it because the newly introduced test covers the tests done in that test. The reason for removing it in the first place is now that screen properties are inherited/spoofed xorigin, we get a 4px difference. The reasosn for 4px difference is the test runner runs tests in an iframe with a 2px border on each side.
Differential Revision: https://phabricator.services.mozilla.com/D215509
- - - - -
12 changed files:
- browser/components/resistfingerprinting/test/mochitest/mochitest.toml - + browser/components/resistfingerprinting/test/mochitest/test_bug1885101_screenwindow_sizes.html - − browser/components/resistfingerprinting/test/mochitest/test_iframe.html - docshell/base/BrowsingContext.h - docshell/base/CanonicalBrowsingContext.cpp - dom/base/nsGlobalWindowOuter.cpp - dom/base/nsScreen.cpp - dom/base/nsScreen.h - dom/base/test/chrome/bug418986-1.js - layout/base/nsPresContext.cpp - layout/base/nsPresContext.h - toolkit/components/resistfingerprinting/tests/browser/browser_fingerprintingWebCompat.js
Changes:
===================================== browser/components/resistfingerprinting/test/mochitest/mochitest.toml ===================================== @@ -27,8 +27,6 @@ scheme = "https" scheme = "https" support-files = ["test_hide_gamepad_info_iframe.html"]
-["test_iframe.html"] - ["test_keyboard_event.html"]
["test_pointer_event.html"] @@ -36,3 +34,5 @@ support-files = ["../../../../../dom/events/test/pointerevents/mochitest_support
["test_speech_synthesis.html"] skip-if = ["verify"] + +["test_bug1885101_screenwindow_sizes.html"]
===================================== browser/components/resistfingerprinting/test/mochitest/test_bug1885101_screenwindow_sizes.html ===================================== @@ -0,0 +1,100 @@ +<!DOCTYPE html> +<html> + <head> + <title>Tests if +WindowOuterSizeExceptIFrame works properly</title> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + </head> + + <body> + <iframe id="mainFrame"></iframe> + + <template id="mainFrameContents"> + <script> + window.parent.postMessage( + { + screen: { + height: window.screen.height, + width: window.screen.width, + availHeight: window.screen.availHeight, + availWidth: window.screen.availWidth, + }, + outerHeight, + outerWidth, + }, + "*" + ); + </script> + </template> + + <script> + document.addEventListener("DOMContentLoaded", function () { + SimpleTest.waitForExplicitFinish(); + + window.addEventListener("message", e => { + const data = e.data; + + // Check for outer size + SimpleTest.is( + data.outerHeight, + window.outerHeight, + "iframe's window.outerHeight should be equal to window.top.outerHeight" + ); + + SimpleTest.is( + data.outerWidth, + window.outerWidth, + "iframe's window.outerWidth should be equal to window.top.outerWidth" + ); + + // Check for screen size + SimpleTest.is( + data.screen.height, + window.screen.height, + "iframe's window.screen.height should be equal to window.top.screen.height" + ); + + SimpleTest.is( + data.screen.width, + window.screen.width, + "iframe's window.screen.width should be equal to window.top.screen.width" + ); + + // Check for avail size + SimpleTest.is( + data.screen.availHeight, + window.screen.availHeight, + "iframe's window.screen.availHeight should be equal to window.top.screen.availHeight" + ); + + SimpleTest.is( + data.screen.availWidth, + window.screen.availWidth, + "iframe's window.screen.availWidth should be equal to window.top.screen.availWidth" + ); + + SimpleTest.finish(); + }); + + function setFrameSource() { + const frame = document.getElementById("mainFrame"); + const template = document.getElementById("mainFrameContents"); + frame.srcdoc = template.innerHTML; + } + + SpecialPowers.pushPrefEnv( + { + set: [ + ["privacy.fingerprintingProtection", true], + [ + "privacy.fingerprintingProtection.overrides", + "+WindowOuterSize,+ScreenRect,+ScreenAvailRect", + ], + ], + }, + () => setFrameSource() + ); + }); + </script> + </body> +</html>
===================================== browser/components/resistfingerprinting/test/mochitest/test_iframe.html deleted ===================================== @@ -1,18 +0,0 @@ -<!doctype html> -<script src="/tests/SimpleTest/SimpleTest.js"></script> -<link rel="stylesheet" href="/tests/SimpleTest/test.css"/> -<body> -<script> - add_task(async function() { - await SpecialPowers.pushPrefEnv({ - "set": [["privacy.resistFingerprinting", true]], - }); - is(screen.width, window.innerWidth, "Width should be spoofed"); - is(screen.height, window.innerHeight, "Height should be spoofed"); - let iframe = document.createElement("iframe"); - document.body.appendChild(iframe); - is(iframe.contentWindow.screen.width, iframe.contentWindow.innerWidth, "Width should be spoofed in iframe"); - is(iframe.contentWindow.screen.height, iframe.contentWindow.innerHeight, "Height should be spoofed in iframe"); - }); -</script> -</body>
===================================== docshell/base/BrowsingContext.h ===================================== @@ -272,7 +272,10 @@ struct EmbedderColorSchemes { /* If true, this browsing context is within a hidden embedded document. */ \ FIELD(IsUnderHiddenEmbedderElement, bool) \ /* If true, this browsing context is offline */ \ - FIELD(ForceOffline, bool) + FIELD(ForceOffline, bool) \ + /* Used to propagate window.top's inner size for RFPTarget::Window* \ + * protections */ \ + FIELD(TopInnerSizeForRFP, CSSIntSize)
// BrowsingContext, in this context, is the cross process replicated // environment in which information about documents is stored. In @@ -1253,6 +1256,10 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache { bool CanSet(FieldIndex<IDX_ForceOffline>, bool aNewValue, ContentParent* aSource);
+ bool CanSet(FieldIndex<IDX_TopInnerSizeForRFP>, bool, ContentParent*) { + return IsTop(); + } + bool CanSet(FieldIndex<IDX_EmbeddedInContentDocument>, bool, ContentParent* aSource) { return CheckOnlyEmbedderCanSet(aSource);
===================================== docshell/base/CanonicalBrowsingContext.cpp ===================================== @@ -324,6 +324,7 @@ void CanonicalBrowsingContext::ReplacedBy( txn.SetHasRestoreData(GetHasRestoreData()); txn.SetShouldDelayMediaFromStart(GetShouldDelayMediaFromStart()); txn.SetForceOffline(GetForceOffline()); + txn.SetTopInnerSizeForRFP(GetTopInnerSizeForRFP());
// Propagate some settings on BrowsingContext replacement so they're not lost // on bfcached navigations. These are important for GeckoView (see bug
===================================== dom/base/nsGlobalWindowOuter.cpp ===================================== @@ -3513,9 +3513,10 @@ CSSIntSize nsGlobalWindowOuter::GetOuterSize(CallerType aCallerType, ErrorResult& aError) { if (nsIGlobalObject::ShouldResistFingerprinting(aCallerType, RFPTarget::WindowOuterSize)) { - CSSSize size; - aError = GetInnerSize(size); - return RoundedToInt(size); + if (BrowsingContext* bc = GetBrowsingContext()) { + return bc->Top()->GetTopInnerSizeForRFP(); + } + return {}; }
// Windows showing documents in RDM panes and any subframes within them
===================================== dom/base/nsScreen.cpp ===================================== @@ -62,7 +62,7 @@ nsDeviceContext* nsScreen::GetDeviceContext() const { CSSIntRect nsScreen::GetRect() { // Return window inner rect to prevent fingerprinting. if (ShouldResistFingerprinting(RFPTarget::ScreenRect)) { - return GetWindowInnerRect(); + return GetTopWindowInnerRectForRFP(); }
// Here we manipulate the value of aRect to represent the screen size, @@ -91,7 +91,7 @@ CSSIntRect nsScreen::GetRect() { CSSIntRect nsScreen::GetAvailRect() { // Return window inner rect to prevent fingerprinting. if (ShouldResistFingerprinting(RFPTarget::ScreenAvailRect)) { - return GetWindowInnerRect(); + return GetTopWindowInnerRectForRFP(); }
// Here we manipulate the value of aRect to represent the screen size, @@ -165,18 +165,14 @@ JSObject* nsScreen::WrapObject(JSContext* aCx, return Screen_Binding::Wrap(aCx, this, aGivenProto); }
-CSSIntRect nsScreen::GetWindowInnerRect() { - nsCOMPtr<nsPIDOMWindowInner> win = GetOwner(); - if (!win) { - return {}; - } - double width; - double height; - if (NS_FAILED(win->GetInnerWidth(&width)) || - NS_FAILED(win->GetInnerHeight(&height))) { - return {}; +CSSIntRect nsScreen::GetTopWindowInnerRectForRFP() { + if (nsPIDOMWindowInner* inner = GetOwner()) { + if (BrowsingContext* bc = inner->GetBrowsingContext()) { + CSSIntSize size = bc->Top()->GetTopInnerSizeForRFP(); + return {0, 0, size.width, size.height}; + } } - return {0, 0, int32_t(std::round(width)), int32_t(std::round(height))}; + return {}; }
bool nsScreen::ShouldResistFingerprinting(RFPTarget aTarget) const {
===================================== dom/base/nsScreen.h ===================================== @@ -88,7 +88,7 @@ class nsScreen : public mozilla::DOMEventTargetHelper { nsDeviceContext* GetDeviceContext() const; mozilla::CSSIntRect GetRect(); mozilla::CSSIntRect GetAvailRect(); - mozilla::CSSIntRect GetWindowInnerRect(); + mozilla::CSSIntRect GetTopWindowInnerRectForRFP();
private: virtual ~nsScreen();
===================================== dom/base/test/chrome/bug418986-1.js ===================================== @@ -24,14 +24,14 @@ var test = function (isContent) { ["mozInnerScreenY", 0], ["screen.pixelDepth", 24], ["screen.colorDepth", 24], - ["screen.availWidth", "innerWidth"], - ["screen.availHeight", "innerHeight"], + ["screen.availWidth", "outerWidth"], + ["screen.availHeight", "outerHeight"], ["screen.left", 0], ["screen.top", 0], ["screen.availLeft", 0], ["screen.availTop", 0], - ["screen.width", "innerWidth"], - ["screen.height", "innerHeight"], + ["screen.width", "outerWidth"], + ["screen.height", "outerHeight"], ["screen.orientation.type", "'landscape-primary'"], ["screen.orientation.angle", 0], ["screen.mozOrientation", "'landscape-primary'"],
===================================== layout/base/nsPresContext.cpp ===================================== @@ -1459,6 +1459,32 @@ void nsPresContext::SetOverrideDPPX(float aDPPX) { MediaFeatureChangePropagation::JustThisDocument); }
+void nsPresContext::UpdateTopInnerSizeForRFP() { + if (!mDocument->ShouldResistFingerprinting(RFPTarget::WindowOuterSize) || + !mDocument->GetBrowsingContext() || + !mDocument->GetBrowsingContext()->IsTop()) { + return; + } + + CSSSize size = CSSPixel::FromAppUnits(GetVisibleArea().Size()); + + switch (StaticPrefs::dom_innerSize_rounding()) { + case 1: + size.width = std::roundf(size.width); + size.height = std::roundf(size.height); + break; + case 2: + size.width = std::truncf(size.width); + size.height = std::truncf(size.height); + break; + default: + break; + } + + Unused << mDocument->GetBrowsingContext()->SetTopInnerSizeForRFP( + CSSIntSize{(int)size.width, (int)size.height}); +} + gfxSize nsPresContext::ScreenSizeInchesForFontInflation(bool* aChanged) { if (aChanged) { *aChanged = false; @@ -2972,6 +2998,8 @@ void nsPresContext::SetVisibleArea(const nsRect& r) { {mozilla::MediaFeatureChangeReason::ViewportChange}, MediaFeatureChangePropagation::JustThisDocument); } + + UpdateTopInnerSizeForRFP(); } }
===================================== layout/base/nsPresContext.h ===================================== @@ -540,6 +540,7 @@ class nsPresContext : public nsISupports, public mozilla::SupportsWeakPtr { void SetFullZoom(float aZoom); void SetOverrideDPPX(float); void SetInRDMPane(bool aInRDMPane); + void UpdateTopInnerSizeForRFP();
public: float GetFullZoom() { return mFullZoom; }
===================================== toolkit/components/resistfingerprinting/tests/browser/browser_fingerprintingWebCompat.js ===================================== @@ -34,12 +34,12 @@ const TEST_CASES = [ { id: "1", last_modified: 1000000000000001, - overrides: "+WindowOuterSize", + overrides: "+ScreenRect,+ScreenAvailRect", firstPartyDomain: "*", }, ], expects: { - windowOuter: { + screenAvailRect: { top: true, firstParty: true, thirdParty: true, @@ -57,12 +57,12 @@ const TEST_CASES = [ { id: "1", last_modified: 1000000000000001, - overrides: "+WindowOuterSize", + overrides: "+ScreenRect,+ScreenAvailRect", firstPartyDomain: "example.com", }, ], expects: { - windowOuter: { + screenAvailRect: { top: true, firstParty: true, thirdParty: false, @@ -80,13 +80,13 @@ const TEST_CASES = [ { id: "1", last_modified: 1000000000000001, - overrides: "+WindowOuterSize", + overrides: "+ScreenRect,+ScreenAvailRect", firstPartyDomain: "example.com", thirdPartyDomain: "*", }, ], expects: { - windowOuter: { + screenAvailRect: { top: true, firstParty: true, thirdParty: true, @@ -104,13 +104,13 @@ const TEST_CASES = [ { id: "1", last_modified: 1000000000000001, - overrides: "+WindowOuterSize", + overrides: "+ScreenRect,+ScreenAvailRect", firstPartyDomain: "example.com", thirdPartyDomain: "example.org", }, ], expects: { - windowOuter: { + screenAvailRect: { top: false, firstParty: false, thirdParty: true, @@ -128,13 +128,13 @@ const TEST_CASES = [ { id: "1", last_modified: 1000000000000001, - overrides: "+WindowOuterSize", + overrides: "+ScreenRect,+ScreenAvailRect", firstPartyDomain: "*", thirdPartyDomain: "example.org", }, ], expects: { - windowOuter: { + screenAvailRect: { top: false, firstParty: false, thirdParty: true, @@ -153,12 +153,12 @@ const TEST_CASES = [ { id: "1", last_modified: 1000000000000001, - overrides: "+WindowOuterSize", + overrides: "+ScreenRect,+ScreenAvailRect", firstPartyDomain: "example.net", }, ], expects: { - windowOuter: { + screenAvailRect: { top: false, firstParty: false, thirdParty: false, @@ -177,13 +177,13 @@ const TEST_CASES = [ { id: "1", last_modified: 1000000000000001, - overrides: "+WindowOuterSize", + overrides: "+ScreenRect,+ScreenAvailRect", firstPartyDomain: "example.net", thirdPartyDomain: "*", }, ], expects: { - windowOuter: { + screenAvailRect: { top: false, firstParty: false, thirdParty: false, @@ -202,13 +202,13 @@ const TEST_CASES = [ { id: "1", last_modified: 1000000000000001, - overrides: "+WindowOuterSize", + overrides: "+ScreenRect,+ScreenAvailRect", firstPartyDomain: "example.net", thirdPartyDomain: "example.com", }, ], expects: { - windowOuter: { + screenAvailRect: { top: false, firstParty: false, thirdParty: false, @@ -221,13 +221,13 @@ const TEST_CASES = [ }, }, // Test multiple entries that enable HW concurrency in the first-party context - // and WindowOuter in the third-party context. + // and ScreenAvailRect in the third-party context. { entires: [ { id: "1", last_modified: 1000000000000001, - overrides: "+WindowOuterSize", + overrides: "+ScreenRect,+ScreenAvailRect", firstPartyDomain: "example.com", }, { @@ -239,7 +239,7 @@ const TEST_CASES = [ }, ], expects: { - windowOuter: { + screenAvailRect: { top: true, firstParty: true, thirdParty: false, @@ -335,22 +335,22 @@ async function openAndSetupTestPageForPopup() { }
async function verifyResultInTab(tab, firstPartyBC, thirdPartyBC, expected) { - let testWindowOuter = enabled => { + let testScreenAvailRect = enabled => { if (enabled) { ok( - content.wrappedJSObject.outerHeight == - content.wrappedJSObject.innerHeight && - content.wrappedJSObject.outerWidth == - content.wrappedJSObject.innerWidth, - "Fingerprinting target WindowOuterSize is enabled for WindowOuterSize." + content.wrappedJSObject.screen.availHeight == + content.wrappedJSObject.screen.height && + content.wrappedJSObject.screen.availWidth == + content.wrappedJSObject.screen.width, + "Fingerprinting target ScreenAvailRect is enabled for ScreenAvailRect." ); } else { ok( - content.wrappedJSObject.outerHeight != - content.wrappedJSObject.innerHeight || - content.wrappedJSObject.outerWidth != - content.wrappedJSObject.innerWidth, - "Fingerprinting target WindowOuterSize is not enabled for WindowOuterSize." + content.wrappedJSObject.screen.availHeight != + content.wrappedJSObject.screen.height || + content.wrappedJSObject.screen.availWidth != + content.wrappedJSObject.screen.width, + "Fingerprinting target ScreenAvailRect is not enabled for ScreenAvailRect." ); } }; @@ -370,8 +370,8 @@ async function verifyResultInTab(tab, firstPartyBC, thirdPartyBC, expected) { ); await SpecialPowers.spawn( tab.linkedBrowser, - [expected.windowOuter.top], - testWindowOuter + [expected.screenAvailRect.top], + testScreenAvailRect ); let expectHWConcurrencyTop = expected.hwConcurrency.top ? SPOOFED_HW_CONCURRENCY @@ -401,8 +401,8 @@ async function verifyResultInTab(tab, firstPartyBC, thirdPartyBC, expected) { ); await SpecialPowers.spawn( firstPartyBC, - [expected.windowOuter.firstParty], - testWindowOuter + [expected.screenAvailRect.firstParty], + testScreenAvailRect ); let expectHWConcurrencyFirstParty = expected.hwConcurrency.firstParty ? SPOOFED_HW_CONCURRENCY @@ -432,8 +432,8 @@ async function verifyResultInTab(tab, firstPartyBC, thirdPartyBC, expected) { ); await SpecialPowers.spawn( thirdPartyBC, - [expected.windowOuter.thirdParty], - testWindowOuter + [expected.screenAvailRect.thirdParty], + testScreenAvailRect ); let expectHWConcurrencyThirdParty = expected.hwConcurrency.thirdParty ? SPOOFED_HW_CONCURRENCY @@ -517,7 +517,7 @@ add_task(async function test_popup_inheritance() { { id: "1", last_modified: 1000000000000001, - overrides: "+WindowOuterSize", + overrides: "+ScreenRect,+ScreenAvailRect", firstPartyDomain: "example.com", thirdPartyDomain: "example.org", }, @@ -532,22 +532,22 @@ add_task(async function test_popup_inheritance() { // Ensure the third-party iframe has the correct overrides. await SpecialPowers.spawn(thirdPartyFrameBC, [], _ => { ok( - content.wrappedJSObject.outerHeight == - content.wrappedJSObject.innerHeight && - content.wrappedJSObject.outerWidth == - content.wrappedJSObject.innerWidth, - "Fingerprinting target WindowOuterSize is enabled for third-party iframe." + content.wrappedJSObject.screen.availHeight == + content.wrappedJSObject.screen.height && + content.wrappedJSObject.screen.availWidth == + content.wrappedJSObject.screen.width, + "Fingerprinting target ScreenAvailRect is enabled for third-party iframe." ); });
// Verify the popup inherits overrides from the opener. await SpecialPowers.spawn(popupBC, [], _ => { ok( - content.wrappedJSObject.outerHeight == - content.wrappedJSObject.innerHeight && - content.wrappedJSObject.outerWidth == - content.wrappedJSObject.innerWidth, - "Fingerprinting target WindowOuterSize is enabled for the pop-up." + content.wrappedJSObject.screen.availHeight == + content.wrappedJSObject.screen.height && + content.wrappedJSObject.screen.availWidth == + content.wrappedJSObject.screen.width, + "Fingerprinting target ScreenAvailRect is enabled for the pop-up." );
content.close();
View it on GitLab: https://gitlab.torproject.org/tpo/applications/mullvad-browser/-/commit/f5fe...
tbb-commits@lists.torproject.org