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();