[tor-commits] [Git][tpo/applications/tor-browser][tor-browser-128.2.0esr-14.0-1] Bug 1885101: Match screen and window properties with top window for...

Pier Angelo Vendrame (@pierov) git at gitlab.torproject.org
Mon Sep 2 08:35:03 UTC 2024



Pier Angelo Vendrame pushed to branch tor-browser-128.2.0esr-14.0-1 at The Tor Project / Applications / Tor Browser


Commits:
eb4cf955 by Fatih at 2024-09-02T08:34:39+00: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
=====================================
@@ -3514,9 +3514,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/tor-browser/-/commit/eb4cf9556945f0c2f51c3347f97eb4ac31e9b7fa

-- 
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/eb4cf9556945f0c2f51c3347f97eb4ac31e9b7fa
You're receiving this email because of your account on gitlab.torproject.org.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.torproject.org/pipermail/tor-commits/attachments/20240902/cde4b0bd/attachment-0001.htm>


More information about the tor-commits mailing list