From de4bc1fb040d77ce05d97f687e8fe4c031d0ddb0 Mon Sep 17 00:00:00 2001 From: Fatih Date: Tue, 6 Aug 2024 15:31:34 +0000 Subject: [PATCH] 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 --- .../test/mochitest/mochitest.toml | 4 +- .../test_bug1885101_screenwindow_sizes.html | 100 ++++++++++++++++++ .../test/mochitest/test_iframe.html | 18 ---- docshell/base/BrowsingContext.h | 9 +- docshell/base/CanonicalBrowsingContext.cpp | 1 + dom/base/nsGlobalWindowOuter.cpp | 7 +- dom/base/nsScreen.cpp | 22 ++-- dom/base/nsScreen.h | 2 +- dom/base/test/chrome/bug418986-1.js | 8 +- layout/base/nsPresContext.cpp | 28 +++++ layout/base/nsPresContext.h | 1 + .../browser_fingerprintingWebCompat.js | 94 ++++++++-------- 12 files changed, 205 insertions(+), 89 deletions(-) create mode 100644 browser/components/resistfingerprinting/test/mochitest/test_bug1885101_screenwindow_sizes.html delete mode 100644 browser/components/resistfingerprinting/test/mochitest/test_iframe.html diff --git a/browser/components/resistfingerprinting/test/mochitest/mochitest.toml b/browser/components/resistfingerprinting/test/mochitest/mochitest.toml index 9eea683df6b2..328ce6eae897 100644 --- a/browser/components/resistfingerprinting/test/mochitest/mochitest.toml +++ b/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"] diff --git a/browser/components/resistfingerprinting/test/mochitest/test_bug1885101_screenwindow_sizes.html b/browser/components/resistfingerprinting/test/mochitest/test_bug1885101_screenwindow_sizes.html new file mode 100644 index 000000000000..f97fce282d78 --- /dev/null +++ b/browser/components/resistfingerprinting/test/mochitest/test_bug1885101_screenwindow_sizes.html @@ -0,0 +1,100 @@ + + + + Tests if +WindowOuterSizeExceptIFrame works properly + + + + + + + + + + + + diff --git a/browser/components/resistfingerprinting/test/mochitest/test_iframe.html b/browser/components/resistfingerprinting/test/mochitest/test_iframe.html deleted file mode 100644 index f01809b28ca4..000000000000 --- a/browser/components/resistfingerprinting/test/mochitest/test_iframe.html +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - diff --git a/docshell/base/BrowsingContext.h b/docshell/base/BrowsingContext.h index 61135ab0d789..97c633faf806 100644 --- a/docshell/base/BrowsingContext.h +++ b/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, bool aNewValue, ContentParent* aSource); + bool CanSet(FieldIndex, bool, ContentParent*) { + return IsTop(); + } + bool CanSet(FieldIndex, bool, ContentParent* aSource) { return CheckOnlyEmbedderCanSet(aSource); diff --git a/docshell/base/CanonicalBrowsingContext.cpp b/docshell/base/CanonicalBrowsingContext.cpp index f0d8cb253984..55bc7fb77a6d 100644 --- a/docshell/base/CanonicalBrowsingContext.cpp +++ b/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 diff --git a/dom/base/nsGlobalWindowOuter.cpp b/dom/base/nsGlobalWindowOuter.cpp index badd4df769dc..01af5a1c7c15 100644 --- a/dom/base/nsGlobalWindowOuter.cpp +++ b/dom/base/nsGlobalWindowOuter.cpp @@ -3515,9 +3515,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 diff --git a/dom/base/nsScreen.cpp b/dom/base/nsScreen.cpp index 4bd1e0c9648e..07614a7b8384 100644 --- a/dom/base/nsScreen.cpp +++ b/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 win = GetOwnerWindow(); - if (!win) { - return {}; +CSSIntRect nsScreen::GetTopWindowInnerRectForRFP() { + if (nsPIDOMWindowInner* inner = GetOwnerWindow()) { + if (BrowsingContext* bc = inner->GetBrowsingContext()) { + CSSIntSize size = bc->Top()->GetTopInnerSizeForRFP(); + return {0, 0, size.width, size.height}; + } } - double width; - double height; - if (NS_FAILED(win->GetInnerWidth(&width)) || - NS_FAILED(win->GetInnerHeight(&height))) { - return {}; - } - return {0, 0, int32_t(std::round(width)), int32_t(std::round(height))}; + return {}; } bool nsScreen::ShouldResistFingerprinting(RFPTarget aTarget) const { diff --git a/dom/base/nsScreen.h b/dom/base/nsScreen.h index c20e6e2cbe49..8f08aeb08930 100644 --- a/dom/base/nsScreen.h +++ b/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(); diff --git a/dom/base/test/chrome/bug418986-1.js b/dom/base/test/chrome/bug418986-1.js index 565ae418dd2d..0f2873410b06 100644 --- a/dom/base/test/chrome/bug418986-1.js +++ b/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'"], diff --git a/layout/base/nsPresContext.cpp b/layout/base/nsPresContext.cpp index 40c87f487e86..bd544a463292 100644 --- a/layout/base/nsPresContext.cpp +++ b/layout/base/nsPresContext.cpp @@ -1463,6 +1463,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; @@ -2964,6 +2990,8 @@ void nsPresContext::SetVisibleArea(const nsRect& aRect) { {mozilla::MediaFeatureChangeReason::ViewportChange}, MediaFeatureChangePropagation::JustThisDocument); } + + UpdateTopInnerSizeForRFP(); } } diff --git a/layout/base/nsPresContext.h b/layout/base/nsPresContext.h index d89fd6b43b24..1383815f6ded 100644 --- a/layout/base/nsPresContext.h +++ b/layout/base/nsPresContext.h @@ -552,6 +552,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; } diff --git a/toolkit/components/resistfingerprinting/tests/browser/browser_fingerprintingWebCompat.js b/toolkit/components/resistfingerprinting/tests/browser/browser_fingerprintingWebCompat.js index 62335b2fe7e7..8a90ab88d258 100644 --- a/toolkit/components/resistfingerprinting/tests/browser/browser_fingerprintingWebCompat.js +++ b/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();