From 5869164e3113f6b2859a28093fdcb37d029647d9 Mon Sep 17 00:00:00 2001 From: Moonchild Date: Sat, 19 Jul 2025 13:58:45 +0200 Subject: [PATCH 1/7] Issue #2691 - Follow-up: Add manual tests --- .../manual/at-support-selector_WPT-tests.html | 304 ++++++++++++++++++ 1 file changed, 304 insertions(+) create mode 100644 layout/style/test/manual/at-support-selector_WPT-tests.html diff --git a/layout/style/test/manual/at-support-selector_WPT-tests.html b/layout/style/test/manual/at-support-selector_WPT-tests.html new file mode 100644 index 0000000000..d16d94c2df --- /dev/null +++ b/layout/style/test/manual/at-support-selector_WPT-tests.html @@ -0,0 +1,304 @@ + + + + + + CSS Conditional Tests: @supports selector() - Complete Test Suite + + + +

CSS Conditional Tests: @supports selector() - Complete Test Suite

+ + +
+

Test 1: @supports selector() with multiple selectors doesn't work

+

Test passes if there is a filled green square and no red.

+

This tests whether browsers correctly fail the condition for multiple selectors in selector().

+
+
+ + +
+

Test 2: @supports selector() with -webkit- unknown pseudo-elements and negation

+

Test passes if there is a filled green square and no red.

+

This tests whether browsers correctly handle negation with an unknown pseudo-element.

+
+
+ + +
+

Test 3: @supports selector() with pseudo-elements

+

Test passes if there is a filled green square and no red.

+

This tests whether browsers recognize pseudo-elements in the selector() function.

+
+
+ + +
+

Test 4: @supports selector() with compound selector

+

Test passes if there is a filled green square and no red.

+

This tests whether browsers can handle complex compound selectors in the selector() function.

+
+
+ + +
+

Test 5: @supports selector() with unsupported but valid pseudo and negation

+

Test passes if there is a filled green square and no red.

+

This tests whether browsers give back a valid syntax selector
+ (obscure edge case CSS used: :nth-child(1 of .foo)) that isn't actually supported.

+
+
+ + +
+

Test 6: @supports selector() with cascade of selectors

+

Test passes if there is a filled green square and no red.

+

This tests whether browsers can handle passing cascaded selectors.

+
+
+ + +
+

Test 7: @supports selector() with :has() pseudo-class and negation

+

Test passes if there is a filled green square and no red.

+

This tests whether browsers correctly identify support for the :has() pseudo-class. This is not supported yet.

+
+
+ + +
+

Test 8.1: @supports selector(:nth-child(5n))

+

Test passes if there is a filled green square.

+
+
+ + +
+

Test 8.2: @supports selector(:nth-child(2n))

+

Test passes if there is a filled green square.

+
+
+ + +
+

Test 8.3: @supports selector(li:nth-child(2n))

+

Test passes if there is a filled green square.

+
+
+ + +
+

Test 8.4: @supports selector(:nth-child(odd))

+

Test passes if there is a filled green square.

+
+
+ + +
+

Test 8.5: @supports selector(:nth-child(even))

+

Test passes if there is a filled green square.

+
+
+ + +
+

Test 8.6: @supports selector(:nth-child(3n+1))

+

Test passes if there is a filled green square.

+
+
+ + +
+

Test 8.7: @supports selector(:nth-child(2n + 2))

+

Test passes if there is a filled green square.

+
+
+ + +
+

Test 9: @supports not selector(div)

+

Test passes if there is a filled green square.

+
+
+ + +
+

Test 10: Complex selector chain
+ @supports selector(section > div:nth-child(2n+1):not(.foo, .bar):hover)

+

Test passes if there is a filled green square.

+
+
+ + + + From b992da05eb6b23feac26fa3f413e17aaed66ef44 Mon Sep 17 00:00:00 2001 From: Francis Dominic Fajardo Date: Sat, 19 Jul 2025 21:25:44 +0800 Subject: [PATCH 2/7] Issue #2826 - Set up fontconfig on XRE startup (GTK only) --- toolkit/xre/nsAppRunner.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/toolkit/xre/nsAppRunner.cpp b/toolkit/xre/nsAppRunner.cpp index 533bd543b8..e1f2eb939c 100644 --- a/toolkit/xre/nsAppRunner.cpp +++ b/toolkit/xre/nsAppRunner.cpp @@ -3164,6 +3164,9 @@ XREMain::XRE_mainStartup(bool* aExitFlag) #endif #if defined(MOZ_WIDGET_GTK) + // Set up fontconfig. + FcInit(); + // setup for private colormap. Ideally we'd like to do this // in nsAppShell::Create, but we need to get in before gtk // has been initialized to make sure everything is running From 0a8f50fafa18fb879a586e08e9a92e3b9f24631f Mon Sep 17 00:00:00 2001 From: Moonchild Date: Wed, 23 Jul 2025 07:40:45 +0200 Subject: [PATCH 3/7] No issue - Adjust CSP URI reporting more closely with the spec. According to the W3C discussion we should allow websocket URIs to be reported aside from http/https/ftp ones. This rewrites things slightly and updates comments to clarify. We do retain the non-standard reduction to origin only in case the report is for cross-origin URIs (stops leaking full URIs to the CSP reported destination). See https://github.com/w3c/webappsec-csp/issues/735 --- dom/security/nsCSPContext.cpp | 56 +++++++++++++++++++++++------------ 1 file changed, 37 insertions(+), 19 deletions(-) diff --git a/dom/security/nsCSPContext.cpp b/dom/security/nsCSPContext.cpp index 9a6fa568e2..2e31681eec 100644 --- a/dom/security/nsCSPContext.cpp +++ b/dom/security/nsCSPContext.cpp @@ -810,33 +810,51 @@ StripURIForReporting(nsIURI* aURI, nsIURI* aSelfURI, nsACString& outStrippedURI) { - // 1) If the origin of uri is a globally unique identifier (for example, - // aURI has a scheme of data, blob, or filesystem), then return the - // ASCII serialization of uri’s scheme. - bool isHttpOrFtp = - (NS_SUCCEEDED(aURI->SchemeIs("http", &isHttpOrFtp)) && isHttpOrFtp) || - (NS_SUCCEEDED(aURI->SchemeIs("https", &isHttpOrFtp)) && isHttpOrFtp) || - (NS_SUCCEEDED(aURI->SchemeIs("ftp", &isHttpOrFtp)) && isHttpOrFtp); + bool isAllowedScheme = + (NS_SUCCEEDED(aURI->SchemeIs("http", &isAllowedScheme)) && isAllowedScheme) || + (NS_SUCCEEDED(aURI->SchemeIs("https", &isAllowedScheme)) && isAllowedScheme) || + (NS_SUCCEEDED(aURI->SchemeIs("ftp", &isAllowedScheme)) && isAllowedScheme) || + (NS_SUCCEEDED(aURI->SchemeIs("ws", &isAllowedScheme)) && isAllowedScheme) || + (NS_SUCCEEDED(aURI->SchemeIs("wss", &isAllowedScheme)) && isAllowedScheme); - if (!isHttpOrFtp) { - // not strictly spec compliant, but what we really care about is - // http/https and also ftp. If it's not http/https or ftp, then treat aURI - // as if it's a globally unique identifier and just return the scheme. + if (!isAllowedScheme) { + // Step 1. If url's scheme is not an allowed scheme, then just return url's scheme, + // i.e. treat aURI as a globally unique identifier. + // What we really care about reporting is http/https/ftp. + // https://github.com/w3c/webappsec-csp/issues/735: We also allow WS(S) schemes. aURI->GetScheme(outStrippedURI); return; } - // 2) If the origin of uri is not the same as the origin of the protected - // resource, then return the ASCII serialization of uri’s origin. - if (!NS_SecurityCompareURIs(aSelfURI, aURI, false)) { - // cross origin redirects also fall into this category, see: - // http://www.w3.org/TR/CSP/#violation-reports - aURI->GetPrePath(outStrippedURI); + // Step 2. Set url's fragment to the empty string. + // Implicit in GetSpecIgnoringRef() below. + + // Step 3. Set url's username/password to the empty string. + nsCOMPtr stripped; + nsresult rv = aURI->Clone(getter_AddRefs(stripped)); + if (NS_FAILED(rv)) { + // Cloning the URI failed for some reason, just return the scheme. + aURI->GetScheme(outStrippedURI); + return; + } + rv = stripped->SetUserPass(EmptyCString()); + if (NS_FAILED(rv)) { + // Mutating the URI failed for some reason, just return the scheme. + aURI->GetScheme(outStrippedURI); return; } - // 3) Return uri, with any fragment component removed. - aURI->GetSpecIgnoringRef(outStrippedURI); + // Non-standard: https://github.com/w3c/webappsec-csp/issues/735 + // We match other browsers here: To avoid leaking the whole URL when blocking + // (or reporting!) cross-origin navigations inside a frame, we restrict the URLs + // to just the (ASCII serialization of) uri's origin. + if (!NS_SecurityCompareURIs(aSelfURI, stripped, false)) { + stripped->GetPrePath(outStrippedURI); + return; + } + + // Step 4. Return uri, with any unwanted component removed. + stripped->GetSpecIgnoringRef(outStrippedURI); } nsresult From 40f96b82ad6c3df8e862fba4d4249fe90105105d Mon Sep 17 00:00:00 2001 From: Moonchild Date: Wed, 23 Jul 2025 14:34:41 +0200 Subject: [PATCH 4/7] [gfx] Hold a stronger reference to gfxFontEntry. --- gfx/thebes/gfxFontEntry.cpp | 28 +++++++++++++++------------- gfx/thebes/gfxFontEntry.h | 6 +++--- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/gfx/thebes/gfxFontEntry.cpp b/gfx/thebes/gfxFontEntry.cpp index ac841a550c..e8bb39e689 100644 --- a/gfx/thebes/gfxFontEntry.cpp +++ b/gfx/thebes/gfxFontEntry.cpp @@ -448,7 +448,7 @@ class gfxFontEntry::FontTableBlobData { public: explicit FontTableBlobData(nsTArray&& aBuffer) : mTableData(Move(aBuffer)) - , mHashtable(nullptr) + , mFontEntry(nullptr) , mHashKey(0) { MOZ_COUNT_CTOR(FontTableBlobData); @@ -456,8 +456,9 @@ public: ~FontTableBlobData() { MOZ_COUNT_DTOR(FontTableBlobData); - if (mHashtable && mHashKey) { - mHashtable->RemoveEntry(mHashKey); + if (mFontEntry && mHashKey) { + RefPtr kungFuDeathGrip(mFontEntry); + mFontEntry->mFontTableCache->RemoveEntry(mHashKey); } } @@ -470,10 +471,10 @@ public: // Tell this FontTableBlobData to remove the HashEntry when this is // destroyed. - void ManageHashEntry(nsTHashtable *aHashtable, + void ManageHashEntry(gfxFontEntry* aFontEntry, uint32_t aHashKey) { - mHashtable = aHashtable; + mFontEntry = aFontEntry; mHashKey = aHashKey; } @@ -481,7 +482,7 @@ public: // removed from the hashtable). void ForgetHashEntry() { - mHashtable = nullptr; + mFontEntry = nullptr; mHashKey = 0; } @@ -496,10 +497,11 @@ private: // The font table data block nsTArray mTableData; - // The blob destroy function needs to know the owning hashtable - // and the hashtable key, so that it can remove the entry. - nsTHashtable *mHashtable; - uint32_t mHashKey; + // The blob destroy function needs to know the owning font entry + // so that it can hold the font-entry's reference while modifying the + // hashtable; and the hashtable key, so that it can remove the entry. + gfxFontEntry* mFontEntry; + uint32_t mHashKey; // not implemented FontTableBlobData(const FontTableBlobData&); @@ -508,7 +510,7 @@ private: hb_blob_t * gfxFontEntry::FontTableHashEntry:: ShareTableAndGetBlob(nsTArray&& aTable, - nsTHashtable *aHashtable) + gfxFontEntry* aFontEntry) { Clear(); // adopts elements of aTable @@ -528,7 +530,7 @@ ShareTableAndGetBlob(nsTArray&& aTable, // Tell the FontTableBlobData to remove this hash entry when destroyed. // The hashtable does not keep a strong reference. - mSharedBlobData->ManageHashEntry(aHashtable, GetKey()); + mSharedBlobData->ManageHashEntry(aFontEntry, GetKey()); return mBlob; } @@ -600,7 +602,7 @@ gfxFontEntry::ShareFontTableAndGetBlob(uint32_t aTag, return nullptr; } - return entry->ShareTableAndGetBlob(Move(*aBuffer), mFontTableCache.get()); + return entry->ShareTableAndGetBlob(Move(*aBuffer), this); } already_AddRefed diff --git a/gfx/thebes/gfxFontEntry.h b/gfx/thebes/gfxFontEntry.h index 77346f3ea3..162fe4f083 100644 --- a/gfx/thebes/gfxFontEntry.h +++ b/gfx/thebes/gfxFontEntry.h @@ -534,11 +534,11 @@ private: // Transfer (not copy) elements of aTable to a new hb_blob_t and // return ownership to the caller. A weak reference to the blob is - // recorded in the hashtable entry so that others may use the same - // table. + // recorded in the font entry's table cache so that others may use + // the same table. hb_blob_t * ShareTableAndGetBlob(nsTArray&& aTable, - nsTHashtable *aHashtable); + gfxFontEntry* aFontEntry); // Return a strong reference to the blob. // Callers must hb_blob_destroy the returned blob. From 3f297457d815c55629136d39a94ddf2a6e4c07ae Mon Sep 17 00:00:00 2001 From: Moonchild Date: Wed, 23 Jul 2025 20:40:50 +0200 Subject: [PATCH 5/7] [js] Align br_table limits with V8/Gecko. --- js/src/jit-test/tests/asm.js/testControlFlow.js | 6 +++--- js/src/wasm/WasmTypes.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/js/src/jit-test/tests/asm.js/testControlFlow.js b/js/src/jit-test/tests/asm.js/testControlFlow.js index 40711cd6ce..bef7f12e84 100644 --- a/js/src/jit-test/tests/asm.js/testControlFlow.js +++ b/js/src/jit-test/tests/asm.js/testControlFlow.js @@ -148,11 +148,11 @@ var exp = asmLink(asmCompile(USE_ASM + "var x=0; function a() { return x|0 } fun assertEq(exp.c(10), undefined); assertEq(exp.a(), 10); -var f = asmLink(asmCompile(USE_ASM + "function f(i) { i=i|0; switch(i|0) { case 1: i=-1; break; case 133742: i=2; break; default: i=42; break } return i|0 } return f")); +var f = asmLink(asmCompile(USE_ASM + "function f(i) { i=i|0; switch(i|0) { case 1: i=-1; break; case 65520: i=2; break; default: i=42; break } return i|0 } return f")); assertEq(f(1), -1); assertEq(f(2), 42); -assertEq(f(133742), 2); -assertEq(f(133743), 42); +assertEq(f(65520), 2); +assertEq(f(65521), 42); var f = asmLink(asmCompile(USE_ASM + "function f(i) { i=i|0; switch(i|0) { case 1: i=42; break; default: i=13 } return i|0 } return f")); assertEq(f(-1), 13); diff --git a/js/src/wasm/WasmTypes.h b/js/src/wasm/WasmTypes.h index 57c737e454..2ea527bdfb 100644 --- a/js/src/wasm/WasmTypes.h +++ b/js/src/wasm/WasmTypes.h @@ -1332,7 +1332,7 @@ static const unsigned MaxTableElems = 1024 * 1024; static const unsigned MaxDataSegments = 64 * 1024; static const unsigned MaxElemSegments = 64 * 1024; static const unsigned MaxArgsPerFunc = 4 * 1024; -static const unsigned MaxBrTableElems = 4 * 1024 * 1024; +static const unsigned MaxBrTableElems = 65520; // To be able to assign function indices during compilation while the number of // imports is still unknown, asm.js sets a maximum number of imports so it can From cdc8127cd17999708a00aff9718f95aa2fd3154d Mon Sep 17 00:00:00 2001 From: Moonchild Date: Wed, 23 Jul 2025 23:08:52 +0200 Subject: [PATCH 6/7] [network] Stop accepting nameless cookies. --- netwerk/cookie/nsCookieService.cpp | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/netwerk/cookie/nsCookieService.cpp b/netwerk/cookie/nsCookieService.cpp index 516261fa22..906513d2b1 100644 --- a/netwerk/cookie/nsCookieService.cpp +++ b/netwerk/cookie/nsCookieService.cpp @@ -3125,6 +3125,15 @@ nsCookieService::SetCookieInternal(nsIURI *aHostURI, COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, savedCookieHeader, "invalid name character"); return newCookie; } + + // RFC 6265 *explicitly* forbids nameless cookies (5.2 step 5) + // Note: we ignore RFC 6265 (bis)'s conflicting stipulation and treat equal-less cookies + // as value-less cookies, not nameless ones. + // This aligns with webkit/Safari and avoids serious sec issues like CVE-2025-8037. + if (cookieAttributes.name.IsEmpty()) { + COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, savedCookieHeader, "nameless cookies are not allowed"); + return newCookie; + } // domain & path checks if (!CheckDomain(cookieAttributes, aHostURI, aKey.mBaseDomain, aRequireHostMatch)) { @@ -3574,15 +3583,11 @@ nsCookieService::ParseAttributes(nsDependentCString &aCookieHeader, // extract cookie & (first attribute), and copy the strings. // if we find multiple cookies, return for processing - // note: if there's no '=', we assume token is . this is required by - // some sites (see bug 169091). - // XXX fix the parser to parse according to grammar for this case + // note: if there's no '=', we assume token is . newCookie = GetTokenValue(cookieStart, cookieEnd, tokenString, tokenValue, equalsFound); + aCookieAttributes.name = tokenString; if (equalsFound) { - aCookieAttributes.name = tokenString; aCookieAttributes.value = tokenValue; - } else { - aCookieAttributes.value = tokenString; } // extract remaining attributes From f902d2ad0e80e70daac3b50d051b8f5e2df09755 Mon Sep 17 00:00:00 2001 From: Moonchild Date: Wed, 23 Jul 2025 23:13:59 +0200 Subject: [PATCH 7/7] [DOM] Make object loading use an explicit scheme whitelist. --- dom/base/nsObjectLoadingContent.cpp | 42 ++++++++++++++++------------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/dom/base/nsObjectLoadingContent.cpp b/dom/base/nsObjectLoadingContent.cpp index 585c661a5c..80d923c142 100644 --- a/dom/base/nsObjectLoadingContent.cpp +++ b/dom/base/nsObjectLoadingContent.cpp @@ -2316,28 +2316,32 @@ nsObjectLoadingContent::LoadObject(bool aNotify, } } - // Don't allow view-source scheme. - // view-source is the only scheme to which this applies at the moment due to - // potential timing attacks to read data from cross-origin documents. If this - // widens we should add a protocol flag for whether the scheme is only allowed - // in top and use something like nsNetUtil::NS_URIChainHasFlags. + // https://html.spec.whatwg.org/multipage/iframe-embed-object.html#the-object-element + // requires that `embed` and `object` go through `Fetch` with mode=navigate, + // see 1.3.5. This will in https://fetch.spec.whatwg.org/#fetching plumb us + // through to https://fetch.spec.whatwg.org/#concept-main-fetch where in step + // 12 a switch is performed. Since `object` and `embed` have mode=navigate the + // result of https://fetch.spec.whatwg.org/#concept-scheme-fetch will decide + // if main fetch proceeds. We short-circuit that scheme-fetch here, inspecting + // if the scheme of `mURI` is one that would return a network error. The + // following schemes are allowed through in scheme fetch: + // "about", "blob", "data", "file", "http", "https". + // XXXMC: Should we include "ftp" as well? + // + // Some accessibility tests use our internal "chrome" scheme. if (mType != eType_Null) { - nsCOMPtr tempURI = mURI; - nsCOMPtr nestedURI = do_QueryInterface(tempURI); - while (nestedURI) { - // view-source should always be an nsINestedURI, loop and check the - // scheme on this and all inner URIs that are also nested URIs. - bool isViewSource = false; - rv = tempURI->SchemeIs("view-source", &isViewSource); - if (NS_FAILED(rv) || isViewSource) { - LOG(("OBJLC [%p]: Blocking as effective URI has view-source scheme", - this)); - mType = eType_Null; + bool isCandidate = false; + for (const auto& candidate : + {"about", "blob", "chrome", "data", "file", "http", "https"}) { + rv = mURI->SchemeIs(candidate, &isCandidate); + if (NS_SUCCEEDED(rv) && isCandidate) { break; } - - nestedURI->GetInnerURI(getter_AddRefs(tempURI)); - nestedURI = do_QueryInterface(tempURI); + } + if (!isCandidate) { + LOG(("OBJLC [%p]: Blocking as effective URI does not have an allowed scheme", + this)); + mType = eType_Null; } }