From 8dd16259287f58f9273002717ec4d27e97127719 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 12 Jun 2024 07:43:14 +0200 Subject: Merging upstream version 127.0. Signed-off-by: Daniel Baumann --- netwerk/base/FuzzySocketControl.cpp | 6 + netwerk/base/LoadInfo.cpp | 48 +- netwerk/base/NetworkConnectivityService.cpp | 45 +- netwerk/base/NetworkConnectivityService.h | 18 +- netwerk/base/nsIClassOfService.idl | 4 +- netwerk/base/nsINetAddr.idl | 2 +- netwerk/base/nsINetworkConnectivityService.idl | 2 + netwerk/base/nsINetworkInterceptController.idl | 2 +- netwerk/base/nsIOService.cpp | 2 +- netwerk/base/nsIServerSocket.idl | 2 +- netwerk/base/nsITLSServerSocket.idl | 2 +- netwerk/base/nsITimedChannel.idl | 4 +- netwerk/base/nsIUDPSocket.idl | 8 +- netwerk/base/nsProtocolProxyService.cpp | 4 + netwerk/build/components.conf | 1 + netwerk/cache2/CacheFileIOManager.cpp | 44 +- netwerk/cache2/CacheFileIOManager.h | 8 + netwerk/cache2/CacheFileInputStream.cpp | 1 + netwerk/cache2/CacheFileMetadata.cpp | 6 +- netwerk/cache2/CacheFileOutputStream.cpp | 2 +- netwerk/cache2/CacheIndex.cpp | 14 +- netwerk/cookie/CookieCommons.cpp | 42 +- netwerk/cookie/CookieService.cpp | 207 +++++-- netwerk/cookie/CookieService.h | 1 - netwerk/cookie/CookieServiceChild.cpp | 140 ++++- netwerk/cookie/CookieServiceParent.cpp | 35 +- netwerk/cookie/test/browser/browser.toml | 6 + .../cookie/test/browser/browser_cookie_chips.js | 539 +++++++++++++++++ .../test/browser/browser_cookies_serviceWorker.js | 540 +++++++++++++++++ netwerk/cookie/test/browser/chips.sjs | 28 + netwerk/cookie/test/browser/cookies.sjs | 17 + netwerk/cookie/test/browser/serviceWorker.js | 21 + netwerk/dns/DNS.cpp | 34 +- netwerk/dns/PlatformDNSAndroid.cpp | 4 + netwerk/dns/PlatformDNSUnix.cpp | 4 + netwerk/dns/PlatformDNSWin.cpp | 7 + netwerk/dns/TRRService.cpp | 7 +- netwerk/dns/TRRService.h | 2 +- netwerk/dns/TRRServiceBase.cpp | 3 +- netwerk/dns/TRRServiceBase.h | 1 + netwerk/dns/effective_tld_names.dat | 650 +++++++++++---------- netwerk/dns/nsDNSService2.cpp | 18 +- netwerk/dns/nsDNSService2.h | 2 + netwerk/dns/nsHostResolver.cpp | 14 +- netwerk/dns/nsHostResolver.h | 2 + netwerk/dns/nsIDNSByTypeRecord.idl | 6 +- netwerk/dns/nsIDNSRecord.idl | 4 +- netwerk/dns/nsIDNSService.idl | 4 +- netwerk/ipc/DocumentLoadListener.h | 2 +- netwerk/locales/en-US/necko.properties | 2 + netwerk/metrics.yaml | 44 +- netwerk/protocol/about/nsAboutProtocolHandler.cpp | 11 + netwerk/protocol/data/nsDataHandler.cpp | 14 +- netwerk/protocol/http/ConnectionEntry.cpp | 3 + netwerk/protocol/http/Http3WebTransportSession.cpp | 10 +- netwerk/protocol/http/HttpBaseChannel.cpp | 15 +- netwerk/protocol/http/HttpBaseChannel.h | 8 +- netwerk/protocol/http/HttpChannelChild.cpp | 8 + netwerk/protocol/http/HttpChannelChild.h | 3 + netwerk/protocol/http/NullHttpChannel.cpp | 10 + netwerk/protocol/http/ObliviousHttpChannel.cpp | 10 + netwerk/protocol/http/nsHttpChannel.cpp | 135 +++-- netwerk/protocol/http/nsHttpChannel.h | 5 +- netwerk/protocol/http/nsHttpConnection.cpp | 8 - netwerk/protocol/http/nsHttpConnectionMgr.cpp | 36 +- netwerk/protocol/http/nsHttpHandler.cpp | 20 +- netwerk/protocol/http/nsHttpTransaction.cpp | 24 +- netwerk/protocol/http/nsHttpTransaction.h | 6 + netwerk/protocol/http/nsIHttpChannel.idl | 8 + netwerk/protocol/http/nsIHttpChannelInternal.idl | 2 +- .../protocol/res/SubstitutingProtocolHandler.cpp | 8 +- netwerk/protocol/res/SubstitutingProtocolHandler.h | 12 +- netwerk/protocol/res/nsResProtocolHandler.cpp | 17 +- netwerk/protocol/res/nsResProtocolHandler.h | 8 +- .../protocol/viewsource/nsViewSourceChannel.cpp | 12 + netwerk/protocol/websocket/nsIWebSocketChannel.idl | 2 +- .../webtransport/WebTransportSessionProxy.cpp | 1 + .../webtransport/WebTransportSessionProxy.h | 17 +- netwerk/protocol/webtransport/nsIWebTransport.idl | 25 +- netwerk/socket/neqo_glue/Cargo.toml | 12 +- netwerk/socket/neqo_glue/src/lib.rs | 30 +- netwerk/socket/nsINamedPipeService.idl | 4 +- .../system/android/nsAndroidNetworkLinkService.cpp | 1 + netwerk/system/linux/nsNetworkLinkService.cpp | 1 + netwerk/system/win32/nsNotifyAddrListener.cpp | 2 +- netwerk/test/browser/browser.toml | 3 + .../browser/browser_dns_prefetch_link_header.js | 282 +++++++++ netwerk/test/browser/file_link_dns_prefetch.sjs | 25 + netwerk/test/gtest/TestCommon.h | 8 +- netwerk/test/gtest/TestCookie.cpp | 4 +- netwerk/test/gtest/TestIDNA.cpp | 43 +- netwerk/test/gtest/TestInputStreamTransport.cpp | 2 +- netwerk/test/gtest/TestNamedPipeService.cpp | 2 +- netwerk/test/gtest/moz.build | 14 +- netwerk/test/http3server/Cargo.toml | 10 +- netwerk/test/unit/head_channels.js | 2 +- netwerk/test/unit/test_cookies_privatebrowsing.js | 13 +- netwerk/test/unit/test_dns_override.js | 25 +- netwerk/test/unit/test_httpssvc_iphint.js | 4 +- netwerk/test/unit/test_verify_traffic.js | 39 ++ netwerk/test/unit/xpcshell.toml | 2 +- netwerk/wifi/gtest/TestWifiMonitor.cpp | 4 +- 102 files changed, 2856 insertions(+), 725 deletions(-) create mode 100644 netwerk/cookie/test/browser/browser_cookie_chips.js create mode 100644 netwerk/cookie/test/browser/browser_cookies_serviceWorker.js create mode 100644 netwerk/cookie/test/browser/chips.sjs create mode 100644 netwerk/cookie/test/browser/cookies.sjs create mode 100644 netwerk/cookie/test/browser/serviceWorker.js create mode 100644 netwerk/test/browser/browser_dns_prefetch_link_header.js create mode 100644 netwerk/test/browser/file_link_dns_prefetch.sjs (limited to 'netwerk') diff --git a/netwerk/base/FuzzySocketControl.cpp b/netwerk/base/FuzzySocketControl.cpp index ff53358417..77a7ed3ac8 100644 --- a/netwerk/base/FuzzySocketControl.cpp +++ b/netwerk/base/FuzzySocketControl.cpp @@ -123,6 +123,12 @@ FuzzySocketControl::ProxyStartSSL() { return NS_OK; } NS_IMETHODIMP FuzzySocketControl::StartTLS() { return NS_OK; } +NS_IMETHODIMP +FuzzySocketControl::AsyncStartTLS(JSContext* aCx, + mozilla::dom::Promise** aPromise) { + return NS_OK; +} + NS_IMETHODIMP FuzzySocketControl::SetNPNList(nsTArray& protocolArray) { return NS_OK; diff --git a/netwerk/base/LoadInfo.cpp b/netwerk/base/LoadInfo.cpp index 6be031113f..7a9f8905b6 100644 --- a/netwerk/base/LoadInfo.cpp +++ b/netwerk/base/LoadInfo.cpp @@ -423,20 +423,47 @@ LoadInfo::LoadInfo(dom::CanonicalBrowsingContext* aBrowsingContext, } #endif - // If we think we should not resist fingerprinting, defer to the opener's - // RFP bit (if there is an opener.) If the opener is also exempted, it stays - // true, otherwise we will put a false into the CJS and that will be respected - // on this document. + // This code path can be taken when loading an about:blank document, which + // means we might think that we should be exempted from resist fingerprinting. + // If we think that, we should defer to any opener, if it is present. If the + // opener is also exempted, then it continues to be exempted. Regardless of + // what ShouldRFP says, we _also_ need to propagate any RandomizationKey we + // have. bool shouldResistFingerprinting = nsContentUtils::ShouldResistFingerprinting_dangerous( aURI, mOriginAttributes, "We are creating CookieJarSettings, so we can't have one already.", RFPTarget::IsAlwaysEnabledForPrecompute); + + nsresult rv = NS_ERROR_NOT_AVAILABLE; + nsTArray randomKey; RefPtr opener = aBrowsingContext->GetOpener(); - if (!shouldResistFingerprinting && opener && - opener->GetCurrentWindowContext()) { - shouldResistFingerprinting = - opener->GetCurrentWindowContext()->ShouldResistFingerprinting(); + if (opener) { + MOZ_ASSERT(opener->GetCurrentWindowContext()); + if (opener->GetCurrentWindowContext()) { + shouldResistFingerprinting |= + opener->GetCurrentWindowContext()->ShouldResistFingerprinting(); + } + + // In the parent, we need to get the CJS from the CanonicalBrowsingContext's + // WindowGlobalParent If we're in the child, we probably have a reference to + // the opener's document, and can get it from there. + if (XRE_IsParentProcess()) { + MOZ_ASSERT(opener->Canonical()->GetCurrentWindowGlobal()); + if (opener->Canonical()->GetCurrentWindowGlobal()) { + MOZ_ASSERT( + opener->Canonical()->GetCurrentWindowGlobal()->CookieJarSettings()); + rv = opener->Canonical() + ->GetCurrentWindowGlobal() + ->CookieJarSettings() + ->GetFingerprintingRandomizationKey(randomKey); + } + } else if (opener->GetDocument()) { + MOZ_ASSERT(false, "Code is in child"); + rv = opener->GetDocument() + ->CookieJarSettings() + ->GetFingerprintingRandomizationKey(randomKey); + } } const bool isPrivate = mOriginAttributes.mPrivateBrowsingId > 0; @@ -447,6 +474,11 @@ LoadInfo::LoadInfo(dom::CanonicalBrowsingContext* aBrowsingContext, mCookieJarSettings = CookieJarSettings::Create( isPrivate ? CookieJarSettings::ePrivate : CookieJarSettings::eRegular, shouldResistFingerprinting); + + if (NS_SUCCEEDED(rv)) { + net::CookieJarSettings::Cast(mCookieJarSettings) + ->SetFingerprintingRandomizationKey(randomKey); + } } LoadInfo::LoadInfo(dom::WindowGlobalParent* aParentWGP, diff --git a/netwerk/base/NetworkConnectivityService.cpp b/netwerk/base/NetworkConnectivityService.cpp index 1e126742ce..2f3b80b724 100644 --- a/netwerk/base/NetworkConnectivityService.cpp +++ b/netwerk/base/NetworkConnectivityService.cpp @@ -4,6 +4,7 @@ #include "DNSUtils.h" #include "NetworkConnectivityService.h" +#include "mozilla/AppShutdown.h" #include "mozilla/ClearOnShutdown.h" #include "mozilla/net/SocketProcessParent.h" #include "mozilla/Preferences.h" @@ -30,14 +31,6 @@ NS_IMPL_ISUPPORTS(NetworkConnectivityService, nsIDNSListener, nsIObserver, static StaticRefPtr gConnService; -NetworkConnectivityService::NetworkConnectivityService() - : mDNSv4(UNKNOWN), - mDNSv6(UNKNOWN), - mIPv4(UNKNOWN), - mIPv6(UNKNOWN), - mNAT64(UNKNOWN), - mLock("nat64prefixes") {} - // static already_AddRefed NetworkConnectivityService::GetSingleton() { @@ -45,6 +38,10 @@ NetworkConnectivityService::GetSingleton() { return do_AddRef(gConnService); } + if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) { + return nullptr; + } + RefPtr service = new NetworkConnectivityService(); service->Init(); @@ -60,6 +57,8 @@ nsresult NetworkConnectivityService::Init() { observerService->AddObserver(this, NS_NETWORK_LINK_TOPIC, false); observerService->AddObserver(this, "network:captive-portal-connectivity", false); + observerService->AddObserver(this, "browser-idle-startup-tasks-finished", + false); return NS_OK; } @@ -78,6 +77,13 @@ NetworkConnectivityService::GetDNSv6(ConnectivityState* aState) { return NS_OK; } +NS_IMETHODIMP +NetworkConnectivityService::GetDNS_HTTPS(ConnectivityState* aState) { + NS_ENSURE_ARG(aState); + *aState = mDNS_HTTPS; + return NS_OK; +} + NS_IMETHODIMP NetworkConnectivityService::GetIPv4(ConnectivityState* aState) { NS_ENSURE_ARG(aState); @@ -154,6 +160,7 @@ static inline bool NAT64PrefixCompare(const NetAddr& prefix1, void NetworkConnectivityService::PerformChecks() { mDNSv4 = UNKNOWN; mDNSv6 = UNKNOWN; + mDNS_HTTPS = UNKNOWN; mIPv4 = UNKNOWN; mIPv6 = UNKNOWN; @@ -281,12 +288,16 @@ NetworkConnectivityService::OnLookupComplete(nsICancelable* aRequest, } else if (aRequest == mDNSv6Request) { mDNSv6 = state; mDNSv6Request = nullptr; + } else if (aRequest == mDNS_HTTPSRequest) { + mDNS_HTTPS = state; + mDNS_HTTPSRequest = nullptr; } else if (aRequest == mNAT64Request) { mNAT64Request = nullptr; SaveNAT64Prefixes(aRecord); } - if (!mDNSv4Request && !mDNSv6Request && !mNAT64Request) { + if (!mDNSv4Request && !mDNSv6Request && !mDNS_HTTPSRequest && + !mNAT64Request) { NotifyObservers("network:connectivity-service:dns-checks-complete"); } return NS_OK; @@ -328,6 +339,16 @@ NetworkConnectivityService::RecheckDNS() { getter_AddRefs(mDNSv6Request)); NS_ENSURE_SUCCESS(rv, rv); + Preferences::GetCString("network.connectivity-service.DNS_HTTPS.domain", + host); + rv = dns->AsyncResolveNative(host, nsIDNSService::RESOLVE_TYPE_HTTPSSVC, + nsIDNSService::RESOLVE_TRR_DISABLED_MODE, + nullptr, this, NS_GetCurrentThread(), attrs, + getter_AddRefs(mDNS_HTTPSRequest)); + if (NS_FAILED(rv)) { + mDNS_HTTPSRequest = nullptr; + } + if (StaticPrefs::network_connectivity_service_nat64_check()) { rv = dns->AsyncResolveNative("ipv4only.arpa"_ns, nsIDNSService::RESOLVE_TYPE_DEFAULT, @@ -355,6 +376,10 @@ NetworkConnectivityService::Observe(nsISupports* aSubject, const char* aTopic, mDNSv6Request->Cancel(NS_ERROR_ABORT); mDNSv6Request = nullptr; } + if (mDNS_HTTPSRequest) { + mDNS_HTTPSRequest->Cancel(NS_ERROR_ABORT); + mDNS_HTTPSRequest = nullptr; + } if (mNAT64Request) { mNAT64Request->Cancel(NS_ERROR_ABORT); mNAT64Request = nullptr; @@ -370,6 +395,8 @@ NetworkConnectivityService::Observe(nsISupports* aSubject, const char* aTopic, !NS_LITERAL_STRING_FROM_CSTRING(NS_NETWORK_LINK_DATA_UNKNOWN) .Equals(aData)) { PerformChecks(); + } else if (!strcmp(aTopic, "browser-idle-startup-tasks-finished")) { + PerformChecks(); } return NS_OK; diff --git a/netwerk/base/NetworkConnectivityService.h b/netwerk/base/NetworkConnectivityService.h index 6315fb192b..b29825d5d5 100644 --- a/netwerk/base/NetworkConnectivityService.h +++ b/netwerk/base/NetworkConnectivityService.h @@ -33,7 +33,7 @@ class NetworkConnectivityService : public nsINetworkConnectivityService, static already_AddRefed GetSingleton(); private: - NetworkConnectivityService(); + NetworkConnectivityService() = default; virtual ~NetworkConnectivityService() = default; nsresult Init(); @@ -47,18 +47,20 @@ class NetworkConnectivityService : public nsINetworkConnectivityService, // Will be set to OK if the DNS request returned in IP of this type, // NOT_AVAILABLE if that type of resolution is not available // UNKNOWN if the check wasn't performed - Atomic mDNSv4; - Atomic mDNSv6; + Atomic mDNSv4{ConnectivityState::UNKNOWN}; + Atomic mDNSv6{ConnectivityState::UNKNOWN}; + Atomic mDNS_HTTPS{ConnectivityState::UNKNOWN}; - Atomic mIPv4; - Atomic mIPv6; + Atomic mIPv4{ConnectivityState::UNKNOWN}; + Atomic mIPv6{ConnectivityState::UNKNOWN}; - Atomic mNAT64; + Atomic mNAT64{ConnectivityState::UNKNOWN}; - nsTArray mNAT64Prefixes; + nsTArray mNAT64Prefixes{ConnectivityState::UNKNOWN}; nsCOMPtr mDNSv4Request; nsCOMPtr mDNSv6Request; + nsCOMPtr mDNS_HTTPSRequest; nsCOMPtr mNAT64Request; nsCOMPtr mIPv4Channel; @@ -67,7 +69,7 @@ class NetworkConnectivityService : public nsINetworkConnectivityService, bool mCheckedNetworkId = false; bool mHasNetworkId = false; - Mutex mLock MOZ_UNANNOTATED; + Mutex mLock MOZ_UNANNOTATED{"nat64prefixes"}; }; } // namespace net diff --git a/netwerk/base/nsIClassOfService.idl b/netwerk/base/nsIClassOfService.idl index 7b133107de..2d5fa03dc1 100644 --- a/netwerk/base/nsIClassOfService.idl +++ b/netwerk/base/nsIClassOfService.idl @@ -22,7 +22,7 @@ class ClassOfService; %} native ClassOfService(mozilla::net::ClassOfService); -[scriptable, uuid(1ccb58ec-5e07-4cf9-a30d-ac5490d23b41)] +[scriptable, builtinclass, uuid(1ccb58ec-5e07-4cf9-a30d-ac5490d23b41)] interface nsIClassOfService : nsISupports { attribute unsigned long classFlags; @@ -30,7 +30,7 @@ interface nsIClassOfService : nsISupports void clearClassFlags(in unsigned long flags); void addClassFlags(in unsigned long flags); - void setClassOfService(in ClassOfService s); + [noscript] void setClassOfService(in ClassOfService s); // All these flags have a (de)prioritization effect. diff --git a/netwerk/base/nsINetAddr.idl b/netwerk/base/nsINetAddr.idl index bbbcd28c0e..3e86442415 100644 --- a/netwerk/base/nsINetAddr.idl +++ b/netwerk/base/nsINetAddr.idl @@ -21,7 +21,7 @@ native NetAddr(mozilla::net::NetAddr); * This interface represents a native NetAddr struct in a readonly * interface. */ -[scriptable, uuid(652B9EC5-D159-45D7-9127-50BB559486CD)] +[scriptable, builtinclass, uuid(652B9EC5-D159-45D7-9127-50BB559486CD)] interface nsINetAddr : nsISupports { /** diff --git a/netwerk/base/nsINetworkConnectivityService.idl b/netwerk/base/nsINetworkConnectivityService.idl index 482eaf45ee..126e85b3fa 100644 --- a/netwerk/base/nsINetworkConnectivityService.idl +++ b/netwerk/base/nsINetworkConnectivityService.idl @@ -25,6 +25,8 @@ interface nsINetworkConnectivityService : nsISupports readonly attribute nsINetworkConnectivityService_ConnectivityState DNSv4; [infallible] readonly attribute nsINetworkConnectivityService_ConnectivityState DNSv6; + [infallible] + readonly attribute nsINetworkConnectivityService_ConnectivityState DNS_HTTPS; /* If connecting to IPv4/v6 works on the current network */ [infallible] diff --git a/netwerk/base/nsINetworkInterceptController.idl b/netwerk/base/nsINetworkInterceptController.idl index 946cc95a88..7f91d2df6f 100644 --- a/netwerk/base/nsINetworkInterceptController.idl +++ b/netwerk/base/nsINetworkInterceptController.idl @@ -48,7 +48,7 @@ interface nsIInterceptedBodyCallback : nsISupports * which do not implement nsIChannel. */ -[scriptable, uuid(f4b82975-6a86-4cc4-87fe-9a1fd430c86d)] +[scriptable, builtinclass, uuid(f4b82975-6a86-4cc4-87fe-9a1fd430c86d)] interface nsIInterceptedChannel : nsISupports { /** diff --git a/netwerk/base/nsIOService.cpp b/netwerk/base/nsIOService.cpp index 998c389ace..07dde50227 100644 --- a/netwerk/base/nsIOService.cpp +++ b/netwerk/base/nsIOService.cpp @@ -414,7 +414,7 @@ nsresult nsIOService::InitializeCaptivePortalService() { mCaptivePortalService = do_GetService(NS_CAPTIVEPORTAL_CID); if (mCaptivePortalService) { - return static_cast(mCaptivePortalService.get()) + static_cast(mCaptivePortalService.get()) ->Initialize(); } diff --git a/netwerk/base/nsIServerSocket.idl b/netwerk/base/nsIServerSocket.idl index d6fd348778..3c21bdb707 100644 --- a/netwerk/base/nsIServerSocket.idl +++ b/netwerk/base/nsIServerSocket.idl @@ -19,7 +19,7 @@ typedef unsigned long nsServerSocketFlag; * * An interface to a server socket that can accept incoming connections. */ -[scriptable, uuid(7a9c39cb-a13f-4eef-9bdf-a74301628742)] +[scriptable, builtinclass, uuid(7a9c39cb-a13f-4eef-9bdf-a74301628742)] interface nsIServerSocket : nsISupports { /** diff --git a/netwerk/base/nsITLSServerSocket.idl b/netwerk/base/nsITLSServerSocket.idl index e944f23af7..a3588ddce2 100644 --- a/netwerk/base/nsITLSServerSocket.idl +++ b/netwerk/base/nsITLSServerSocket.idl @@ -8,7 +8,7 @@ interface nsIX509Cert; interface nsITLSServerSecurityObserver; interface nsISocketTransport; -[scriptable, uuid(cc2c30f9-cfaa-4b8a-bd44-c24881981b74)] +[scriptable, builtinclass, uuid(cc2c30f9-cfaa-4b8a-bd44-c24881981b74)] interface nsITLSServerSocket : nsIServerSocket { /** diff --git a/netwerk/base/nsITimedChannel.idl b/netwerk/base/nsITimedChannel.idl index 4707bf1b7a..f6d85cf945 100644 --- a/netwerk/base/nsITimedChannel.idl +++ b/netwerk/base/nsITimedChannel.idl @@ -25,7 +25,7 @@ interface nsIServerTiming : nsISupports { [ref] native nsServerTimingArrayRef(nsTArray>); // All properties return zero if the value is not available -[scriptable, uuid(ca63784d-959c-4c3a-9a59-234a2a520de0)] +[scriptable, builtinclass, uuid(ca63784d-959c-4c3a-9a59-234a2a520de0)] interface nsITimedChannel : nsISupports { // Set this attribute to true to enable collection of timing data. // channelCreationTime will be available even with this attribute set to @@ -124,5 +124,5 @@ interface nsITimedChannel : nsISupports { [noscript] attribute boolean reportResourceTiming; readonly attribute nsIArray serverTiming; - nsServerTimingArrayRef getNativeServerTiming(); + [noscript] nsServerTimingArrayRef getNativeServerTiming(); }; diff --git a/netwerk/base/nsIUDPSocket.idl b/netwerk/base/nsIUDPSocket.idl index 5c23c1bb6f..d07be55349 100644 --- a/netwerk/base/nsIUDPSocket.idl +++ b/netwerk/base/nsIUDPSocket.idl @@ -31,7 +31,7 @@ native NetAddr(mozilla::net::NetAddr); * * An interface to a UDP socket that can accept incoming connections. */ -[scriptable, uuid(d423bf4e-4499-40cf-bc03-153e2bf206d1)] +[scriptable, builtinclass, uuid(d423bf4e-4499-40cf-bc03-153e2bf206d1)] interface nsIUDPSocket : nsISupports { /** @@ -126,7 +126,7 @@ interface nsIUDPSocket : nsISupports * @param aRemoteAddr * The remote address to connect to */ - void connect([const] in NetAddrPtr aAddr); + [noscript] void connect([const] in NetAddrPtr aAddr); /** * Returns the local address of this UDP socket @@ -217,8 +217,8 @@ interface nsIUDPSocket : nsISupports * @param addr The remote host address. * @param stream The input stream to be sent. This must be a buffered stream implementation. */ - void sendBinaryStreamWithAddress([const] in NetAddrPtr addr, - in nsIInputStream stream); + [noscript] void sendBinaryStreamWithAddress([const] in NetAddrPtr addr, + in nsIInputStream stream); /** * joinMulticast diff --git a/netwerk/base/nsProtocolProxyService.cpp b/netwerk/base/nsProtocolProxyService.cpp index 7ccfc9363a..743c977c74 100644 --- a/netwerk/base/nsProtocolProxyService.cpp +++ b/netwerk/base/nsProtocolProxyService.cpp @@ -2292,6 +2292,10 @@ void nsProtocolProxyService::MaybeDisableDNSPrefetch(nsIProxyInfo* aProxy) { nsCOMPtr pi = do_QueryInterface(aProxy); if (!pi || !pi->mType || pi->mType == kProxyType_DIRECT) return; + if (StaticPrefs::network_dns_prefetch_via_proxy()) { + return; + } + // To avoid getting DNS service recursively, we directly use // GetXPCOMSingleton(). nsCOMPtr dns = nsDNSService::GetXPCOMSingleton(); diff --git a/netwerk/build/components.conf b/netwerk/build/components.conf index 68d7be7ec3..bebf9695e4 100644 --- a/netwerk/build/components.conf +++ b/netwerk/build/components.conf @@ -646,6 +646,7 @@ Classes = [ '@mozilla.org/streamconv;1?from=gzip&to=uncompressed', '@mozilla.org/streamconv;1?from=x-compress&to=uncompressed', '@mozilla.org/streamconv;1?from=x-gzip&to=uncompressed', + '@mozilla.org/streamconv;1?from=zstd&to=uncompressed', ], 'legacy_constructor': 'CreateNewHTTPCompressConvFactory', 'processes': ProcessSelector.ALLOW_IN_SOCKET_PROCESS, diff --git a/netwerk/cache2/CacheFileIOManager.cpp b/netwerk/cache2/CacheFileIOManager.cpp index 8cff1c1cd9..89cd4497ad 100644 --- a/netwerk/cache2/CacheFileIOManager.cpp +++ b/netwerk/cache2/CacheFileIOManager.cpp @@ -2026,26 +2026,46 @@ nsresult CacheFileIOManager::Write(CacheFileHandle* aHandle, int64_t aOffset, "validate=%d, truncate=%d, listener=%p]", aHandle, aOffset, aCount, aValidate, aTruncate, aCallback)); - nsresult rv; + MOZ_ASSERT(aCallback); + RefPtr ioMan = gInstance; - if (aHandle->IsClosed() || (aCallback && aCallback->IsKilled()) || !ioMan) { - if (!aCallback) { - // When no callback is provided, CacheFileIOManager is responsible for - // releasing the buffer. We must release it even in case of failure. - free(const_cast(aBuf)); - } + if (aHandle->IsClosed() || aCallback->IsKilled() || !ioMan) { return NS_ERROR_NOT_INITIALIZED; } RefPtr ev = new WriteEvent(aHandle, aOffset, aBuf, aCount, aValidate, aTruncate, aCallback); - rv = ioMan->mIOThread->Dispatch(ev, aHandle->mPriority - ? CacheIOThread::WRITE_PRIORITY - : CacheIOThread::WRITE); - NS_ENSURE_SUCCESS(rv, rv); + return ioMan->mIOThread->Dispatch(ev, aHandle->mPriority + ? CacheIOThread::WRITE_PRIORITY + : CacheIOThread::WRITE); +} - return NS_OK; +// static +nsresult CacheFileIOManager::WriteWithoutCallback(CacheFileHandle* aHandle, + int64_t aOffset, char* aBuf, + int32_t aCount, + bool aValidate, + bool aTruncate) { + LOG(("CacheFileIOManager::WriteWithoutCallback() [handle=%p, offset=%" PRId64 + ", count=%d, " + "validate=%d, truncate=%d]", + aHandle, aOffset, aCount, aValidate, aTruncate)); + + RefPtr ioMan = gInstance; + + if (aHandle->IsClosed() || !ioMan) { + // When no callback is provided, CacheFileIOManager is responsible for + // releasing the buffer. We must release it even in case of failure. + free(aBuf); + return NS_ERROR_NOT_INITIALIZED; + } + + RefPtr ev = new WriteEvent(aHandle, aOffset, aBuf, aCount, + aValidate, aTruncate, nullptr); + return ioMan->mIOThread->Dispatch(ev, aHandle->mPriority + ? CacheIOThread::WRITE_PRIORITY + : CacheIOThread::WRITE); } static nsresult TruncFile(PRFileDesc* aFD, int64_t aEOF) { diff --git a/netwerk/cache2/CacheFileIOManager.h b/netwerk/cache2/CacheFileIOManager.h index 5be21c8db8..67c67e718d 100644 --- a/netwerk/cache2/CacheFileIOManager.h +++ b/netwerk/cache2/CacheFileIOManager.h @@ -287,9 +287,17 @@ class CacheFileIOManager final : public nsITimerCallback, public nsINamed { CacheFileIOListener* aCallback); static nsresult Read(CacheFileHandle* aHandle, int64_t aOffset, char* aBuf, int32_t aCount, CacheFileIOListener* aCallback); + // This function must be called with a callback. The caller is responsible for + // releasing |aBuf|. static nsresult Write(CacheFileHandle* aHandle, int64_t aOffset, const char* aBuf, int32_t aCount, bool aValidate, bool aTruncate, CacheFileIOListener* aCallback); + // Similar to the above, but without the callback. Note that |aBuf| will be + // released by CacheFileIOManager. + static nsresult WriteWithoutCallback(CacheFileHandle* aHandle, + int64_t aOffset, char* aBuf, + int32_t aCount, bool aValidate, + bool aTruncate); // PinningDoomRestriction: // NO_RESTRICTION // no restriction is checked, the file is simply always doomed diff --git a/netwerk/cache2/CacheFileInputStream.cpp b/netwerk/cache2/CacheFileInputStream.cpp index 99302baf54..564a08fd2b 100644 --- a/netwerk/cache2/CacheFileInputStream.cpp +++ b/netwerk/cache2/CacheFileInputStream.cpp @@ -8,6 +8,7 @@ #include "CacheFile.h" #include "nsStreamUtils.h" #include "nsThreadUtils.h" +#include "mozilla/IntegerPrintfMacros.h" #include namespace mozilla::net { diff --git a/netwerk/cache2/CacheFileMetadata.cpp b/netwerk/cache2/CacheFileMetadata.cpp index 68d55b1988..5e299a7852 100644 --- a/netwerk/cache2/CacheFileMetadata.cpp +++ b/netwerk/cache2/CacheFileMetadata.cpp @@ -257,14 +257,16 @@ nsresult CacheFileMetadata::WriteMetadata( char* writeBuffer = mWriteBuf; if (aListener) { mListener = aListener; + rv = CacheFileIOManager::Write(mHandle, aOffset, writeBuffer, + p - writeBuffer, true, true, this); } else { // We are not going to pass |this| as a callback so the buffer will be // released by CacheFileIOManager. Just null out mWriteBuf here. mWriteBuf = nullptr; + rv = CacheFileIOManager::WriteWithoutCallback(mHandle, aOffset, writeBuffer, + p - writeBuffer, true, true); } - rv = CacheFileIOManager::Write(mHandle, aOffset, writeBuffer, p - writeBuffer, - true, true, aListener ? this : nullptr); if (NS_FAILED(rv)) { LOG( ("CacheFileMetadata::WriteMetadata() - CacheFileIOManager::Write() " diff --git a/netwerk/cache2/CacheFileOutputStream.cpp b/netwerk/cache2/CacheFileOutputStream.cpp index 985082efba..b541132bd9 100644 --- a/netwerk/cache2/CacheFileOutputStream.cpp +++ b/netwerk/cache2/CacheFileOutputStream.cpp @@ -9,7 +9,7 @@ #include "CacheEntry.h" #include "nsStreamUtils.h" #include "nsThreadUtils.h" -#include "mozilla/DebugOnly.h" +#include "mozilla/IntegerPrintfMacros.h" #include namespace mozilla::net { diff --git a/netwerk/cache2/CacheIndex.cpp b/netwerk/cache2/CacheIndex.cpp index c0cb63ef6d..34931087c0 100644 --- a/netwerk/cache2/CacheIndex.cpp +++ b/netwerk/cache2/CacheIndex.cpp @@ -2213,16 +2213,10 @@ void CacheIndex::ParseRecords(const StaticMutexAutoLock& aProofOfLock) { reinterpret_cast(moz_xmalloc(sizeof(uint32_t))); NetworkEndian::writeUint32(isDirty, 1); - // Mark index dirty. The buffer is freed by CacheFileIOManager when - // nullptr is passed as the listener and the call doesn't fail - // synchronously. - rv = CacheFileIOManager::Write(mIndexHandle, 2 * sizeof(uint32_t), - reinterpret_cast(isDirty), - sizeof(uint32_t), true, false, nullptr); - if (NS_FAILED(rv)) { - // This is not fatal, just free the memory - free(isDirty); - } + // Mark index dirty. The buffer will be freed by CacheFileIOManager. + CacheFileIOManager::WriteWithoutCallback( + mIndexHandle, 2 * sizeof(uint32_t), reinterpret_cast(isDirty), + sizeof(uint32_t), true, false); } pos += sizeof(uint32_t); diff --git a/netwerk/cookie/CookieCommons.cpp b/netwerk/cookie/CookieCommons.cpp index 9b26fc4a6e..4c4ae0c848 100644 --- a/netwerk/cookie/CookieCommons.cpp +++ b/netwerk/cookie/CookieCommons.cpp @@ -349,10 +349,6 @@ already_AddRefed CookieCommons::CreateCookieFromDocument( std::function&& aHasExistingCookiesLambda, nsIURI** aDocumentURI, nsACString& aBaseDomain, OriginAttributes& aAttrs) { - nsCOMPtr storagePrincipal = - aDocument->EffectiveCookiePrincipal(); - MOZ_ASSERT(storagePrincipal); - nsCOMPtr principalURI; auto* basePrincipal = BasePrincipal::Cast(aDocument->NodePrincipal()); basePrincipal->GetURI(getter_AddRefs(principalURI)); @@ -379,15 +375,6 @@ already_AddRefed CookieCommons::CreateCookieFromDocument( return nullptr; } - // Check if limit-foreign is required. - uint32_t dummyRejectedReason = 0; - if (aDocument->CookieJarSettings()->GetLimitForeignContexts() && - !aHasExistingCookiesLambda(baseDomain, - storagePrincipal->OriginAttributesRef()) && - !ShouldAllowAccessFor(innerWindow, principalURI, &dummyRejectedReason)) { - return nullptr; - } - bool isForeignAndNotAddon = false; if (!BasePrincipal::Cast(aDocument->NodePrincipal())->AddonPolicy()) { rv = aThirdPartyUtil->IsThirdPartyWindow( @@ -439,8 +426,29 @@ already_AddRefed CookieCommons::CreateCookieFromDocument( return nullptr; } + // CHIPS - If the partitioned attribute is set, store cookie in partitioned + // cookie jar independent of context. If the cookies are stored in the + // partitioned cookie jar anyway no special treatment of CHIPS cookies + // necessary. + bool needPartitioned = + StaticPrefs::network_cookie_cookieBehavior_optInPartitioning() && + cookieData.isPartitioned(); + nsCOMPtr cookiePrincipal = + needPartitioned ? aDocument->PartitionedPrincipal() + : aDocument->EffectiveCookiePrincipal(); + MOZ_ASSERT(cookiePrincipal); + + // Check if limit-foreign is required. + uint32_t dummyRejectedReason = 0; + if (aDocument->CookieJarSettings()->GetLimitForeignContexts() && + !aHasExistingCookiesLambda(baseDomain, + cookiePrincipal->OriginAttributesRef()) && + !ShouldAllowAccessFor(innerWindow, principalURI, &dummyRejectedReason)) { + return nullptr; + } + RefPtr cookie = - Cookie::Create(cookieData, storagePrincipal->OriginAttributesRef()); + Cookie::Create(cookieData, cookiePrincipal->OriginAttributesRef()); MOZ_ASSERT(cookie); cookie->SetLastAccessed(currentTimeInUsec); @@ -448,7 +456,7 @@ already_AddRefed CookieCommons::CreateCookieFromDocument( Cookie::GenerateUniqueCreationTime(currentTimeInUsec)); aBaseDomain = baseDomain; - aAttrs = storagePrincipal->OriginAttributesRef(); + aAttrs = cookiePrincipal->OriginAttributesRef(); principalURI.forget(aDocumentURI); return cookie.forget(); @@ -486,9 +494,11 @@ bool CookieCommons::ShouldIncludeCrossSiteCookieForDocument( int32_t sameSiteAttr = 0; aCookie->GetSameSite(&sameSiteAttr); + // CHIPS - If a third-party has storage access it can access both it's + // partitioned and unpartitioned cookie jars, else its cookies are blocked. if (aDocument->CookieJarSettings()->GetPartitionForeign() && StaticPrefs::network_cookie_cookieBehavior_optInPartitioning() && - !aCookie->IsPartitioned()) { + !aCookie->IsPartitioned() && !aDocument->UsingStorageAccess()) { return false; } diff --git a/netwerk/cookie/CookieService.cpp b/netwerk/cookie/CookieService.cpp index 0e90b8cbae..78698d44a0 100644 --- a/netwerk/cookie/CookieService.cpp +++ b/netwerk/cookie/CookieService.cpp @@ -373,15 +373,6 @@ CookieService::GetCookieStringFromDocument(Document* aDocument, return NS_OK; } - nsCOMPtr cookiePrincipal = - aDocument->EffectiveCookiePrincipal(); - - // TODO (Bug 1874174): A document could access both unpartitioned and - // partitioned cookie jars. We will need to prepare partitioned and - // unpartitioned principals for access both cookie jars. - nsTArray> principals; - principals.AppendElement(cookiePrincipal); - bool thirdParty = true; nsPIDOMWindowInner* innerWindow = aDocument->GetInnerWindow(); // in gtests we don't have a window, let's consider those requests as 3rd @@ -395,6 +386,26 @@ CookieService::GetCookieStringFromDocument(Document* aDocument, } } + nsCOMPtr cookiePrincipal = + aDocument->EffectiveCookiePrincipal(); + + nsTArray> principals; + principals.AppendElement(cookiePrincipal); + + // CHIPS - If CHIPS is enabled the partitioned cookie jar is always available + // (and therefore the partitioned principal), the unpartitioned cookie jar is + // only available in first-party or third-party with storageAccess contexts. + bool isCHIPS = StaticPrefs::network_cookie_cookieBehavior_optInPartitioning(); + bool isUnpartitioned = + cookiePrincipal->OriginAttributesRef().mPartitionKey.IsEmpty(); + if (isCHIPS && isUnpartitioned) { + // Assert that we are only doing this if we are first-party or third-party + // with storageAccess. + MOZ_ASSERT(!thirdParty || aDocument->UsingStorageAccess()); + // Add the partitioned principal to principals + principals.AppendElement(aDocument->PartitionedPrincipal()); + } + nsTArray cookieList; for (auto& principal : principals) { @@ -519,20 +530,40 @@ CookieService::GetCookieStringFromHttp(nsIURI* aHostURI, nsIChannel* aChannel, ThirdPartyAnalysisResult result = mThirdPartyUtil->AnalyzeChannel( aChannel, false, aHostURI, nullptr, &rejectedReason); - OriginAttributes attrs; - StoragePrincipalHelper::GetOriginAttributes( - aChannel, attrs, StoragePrincipalHelper::eStorageAccessPrincipal); - bool isSafeTopLevelNav = CookieCommons::IsSafeTopLevelNav(aChannel); bool hadCrossSiteRedirects = false; bool isSameSiteForeign = CookieCommons::IsSameSiteForeign( aChannel, aHostURI, &hadCrossSiteRedirects); - // TODO (Bug 1874174): A channel could load both unpartitioned and partitioned - // cookie jars together. We will need to get cookies from both unpartitioned - // and partitioned cookie jars according to storage access. + OriginAttributes storageOriginAttributes; + StoragePrincipalHelper::GetOriginAttributes( + aChannel, storageOriginAttributes, + StoragePrincipalHelper::eStorageAccessPrincipal); + nsTArray originAttributesList; - originAttributesList.AppendElement(attrs); + originAttributesList.AppendElement(storageOriginAttributes); + + // CHIPS - If CHIPS is enabled the partitioned cookie jar is always available + // (and therefore the partitioned OriginAttributes), the unpartitioned cookie + // jar is only available in first-party or third-party with storageAccess + // contexts. + bool isCHIPS = StaticPrefs::network_cookie_cookieBehavior_optInPartitioning(); + bool isUnpartitioned = storageOriginAttributes.mPartitionKey.IsEmpty(); + if (isCHIPS && isUnpartitioned) { + // Assert that we are only doing this if we are first-party or third-party + // with storageAccess. + MOZ_ASSERT( + !result.contains(ThirdPartyAnalysis::IsForeign) || + result.contains(ThirdPartyAnalysis::IsStorageAccessPermissionGranted)); + // Add the partitioned principal to principals + OriginAttributes partitionedOriginAttributes; + StoragePrincipalHelper::GetOriginAttributes( + aChannel, partitionedOriginAttributes, + StoragePrincipalHelper::ePartitionedPrincipal); + originAttributesList.AppendElement(partitionedOriginAttributes); + // Assert partitionedOAs have partitioneKey set. + MOZ_ASSERT(!partitionedOriginAttributes.mPartitionKey.IsEmpty()); + } AutoTArray foundCookieList; GetCookiesForURI( @@ -630,9 +661,10 @@ CookieService::SetCookieStringFromHttp(nsIURI* aHostURI, ThirdPartyAnalysisResult result = mThirdPartyUtil->AnalyzeChannel( aChannel, false, aHostURI, nullptr, &rejectedReason); - OriginAttributes attrs; + OriginAttributes storagePrincipalOriginAttributes; StoragePrincipalHelper::GetOriginAttributes( - aChannel, attrs, StoragePrincipalHelper::eStorageAccessPrincipal); + aChannel, storagePrincipalOriginAttributes, + StoragePrincipalHelper::eStorageAccessPrincipal); // get the base domain for the host URI. // e.g. for "www.bbc.co.uk", this would be "bbc.co.uk". @@ -660,11 +692,11 @@ CookieService::SetCookieStringFromHttp(nsIURI* aHostURI, baseDomainFromURI); NS_ENSURE_SUCCESS(rv, NS_OK); - CookieStorage* storage = PickStorage(attrs); + CookieStorage* storage = PickStorage(storagePrincipalOriginAttributes); // check default prefs uint32_t priorCookieCount = storage->CountCookiesFromHost( - baseDomainFromURI, attrs.mPrivateBrowsingId); + baseDomainFromURI, storagePrincipalOriginAttributes.mPrivateBrowsingId); nsCOMPtr crc = do_QueryInterface(aChannel); @@ -674,7 +706,8 @@ CookieService::SetCookieStringFromHttp(nsIURI* aHostURI, result.contains(ThirdPartyAnalysis::IsThirdPartyTrackingResource), result.contains(ThirdPartyAnalysis::IsThirdPartySocialTrackingResource), result.contains(ThirdPartyAnalysis::IsStorageAccessPermissionGranted), - aCookieHeader, priorCookieCount, attrs, &rejectedReason); + aCookieHeader, priorCookieCount, storagePrincipalOriginAttributes, + &rejectedReason); MOZ_ASSERT_IF(rejectedReason, cookieStatus == STATUS_REJECTED); @@ -718,9 +751,24 @@ CookieService::SetCookieStringFromHttp(nsIURI* aHostURI, nsCString cookieHeader(aCookieHeader); - bool moreCookieToRead = true; + // CHIPS - The partitioned cookie jar is always available and it is always + // possible to store cookies in it using the "Partitioned" attribute. + // Prepare the partitioned principals OAs to enable possible partitioned + // cookie storing from first-party or with StorageAccess. + // Similar behavior to CookieServiceChild::SetCookieStringFromHttp(). + OriginAttributes partitionedPrincipalOriginAttributes; + bool isPartitionedPrincipal = + !storagePrincipalOriginAttributes.mPartitionKey.IsEmpty(); + bool isCHIPS = StaticPrefs::network_cookie_cookieBehavior_optInPartitioning(); + // Only need to get OAs if we don't already use the partitioned principal. + if (isCHIPS && !isPartitionedPrincipal) { + StoragePrincipalHelper::GetOriginAttributes( + aChannel, partitionedPrincipalOriginAttributes, + StoragePrincipalHelper::ePartitionedPrincipal); + } // process each cookie in the header + bool moreCookieToRead = true; while (moreCookieToRead) { CookieStruct cookieData; bool canSetCookie = false; @@ -751,8 +799,22 @@ CookieService::SetCookieStringFromHttp(nsIURI* aHostURI, continue; } + // CHIPS - If the partitioned attribute is set, store cookie in partitioned + // cookie jar independent of context. If the cookies are stored in the + // partitioned cookie jar anyway no special treatment of CHIPS cookies + // necessary. + bool needPartitioned = + isCHIPS && cookieData.isPartitioned() && !isPartitionedPrincipal; + OriginAttributes& cookieOriginAttributes = + needPartitioned ? partitionedPrincipalOriginAttributes + : storagePrincipalOriginAttributes; + // Assert that partitionedPrincipalOriginAttributes are initialized if used. + MOZ_ASSERT_IF( + needPartitioned, + !partitionedPrincipalOriginAttributes.mPartitionKey.IsEmpty()); + // create a new Cookie - RefPtr cookie = Cookie::Create(cookieData, attrs); + RefPtr cookie = Cookie::Create(cookieData, cookieOriginAttributes); MOZ_ASSERT(cookie); int64_t currentTimeInUsec = PR_Now(); @@ -763,8 +825,8 @@ CookieService::SetCookieStringFromHttp(nsIURI* aHostURI, RefPtr bc = loadInfo->GetBrowsingContext(); // add the cookie to the list. AddCookie() takes care of logging. - storage->AddCookie(crc, baseDomain, attrs, cookie, currentTimeInUsec, - aHostURI, aCookieHeader, true, bc); + storage->AddCookie(crc, baseDomain, cookieOriginAttributes, cookie, + currentTimeInUsec, aHostURI, aCookieHeader, true, bc); } return NS_OK; @@ -1180,6 +1242,16 @@ static void RecordPartitionedTelemetry(const CookieStruct& aCookieData, } } +static bool HasSecurePrefix(const nsACString& aString) { + return StringBeginsWith(aString, "__Secure-"_ns, + nsCaseInsensitiveCStringComparator); +} + +static bool HasHostPrefix(const nsACString& aString) { + return StringBeginsWith(aString, "__Host-"_ns, + nsCaseInsensitiveCStringComparator); +} + // processes a single cookie, and returns true if there are more cookies // to be processed bool CookieService::CanSetCookie( @@ -1290,9 +1362,12 @@ bool CookieService::CanSetCookie( return newCookie; } - if (!CheckHiddenPrefix(aCookieData)) { + // If a cookie is nameless, then its value must not start with + // `__Host-` or `__Secure-` + if (aCookieData.name().IsEmpty() && (HasSecurePrefix(aCookieData.value()) || + HasHostPrefix(aCookieData.value()))) { COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, savedCookieHeader, - "failed the CheckHiddenPrefix tests"); + "failed hidden prefix tests"); CookieLogging::LogMessageToConsole( aCRC, aHostURI, nsIScriptError::warningFlag, CONSOLE_REJECTION_CATEGORY, "CookieRejectedInvalidPrefix"_ns, @@ -1449,10 +1524,9 @@ bool CookieService::CanSetCookie( separators = ";" | "=" value-sep = ";" cookie-sep = CR | LF - allowed-chars = + allowed-chars = OCTET = LWS = SP | HT - NUL = CR = LF = SP = @@ -1472,6 +1546,8 @@ bool CookieService::CanSetCookie( | "Max-Age" "=" value | "Comment" "=" value | "Version" "=" value + | "Partitioned" + | "SameSite" | "Secure" | "HttpOnly" @@ -1479,7 +1555,6 @@ bool CookieService::CanSetCookie( // clang-format on // helper functions for GetTokenValue -static inline bool isnull(char c) { return c == 0; } static inline bool iswhitespace(char c) { return c == ' ' || c == '\t'; } static inline bool isterminator(char c) { return c == '\n' || c == '\r'; } static inline bool isvalueseparator(char c) { @@ -1507,7 +1582,7 @@ bool CookieService::GetTokenValue(nsACString::const_char_iterator& aIter, ++aIter; } start = aIter; - while (aIter != aEndIter && !isnull(*aIter) && !istokenseparator(*aIter)) { + while (aIter != aEndIter && !istokenseparator(*aIter)) { ++aIter; } @@ -1530,7 +1605,7 @@ bool CookieService::GetTokenValue(nsACString::const_char_iterator& aIter, // process // just look for ';' to terminate ('=' allowed) - while (aIter != aEndIter && !isnull(*aIter) && !isvalueseparator(*aIter)) { + while (aIter != aEndIter && !isvalueseparator(*aIter)) { ++aIter; } @@ -1549,6 +1624,21 @@ bool CookieService::GetTokenValue(nsACString::const_char_iterator& aIter, // if on terminator, increment past & return true to process new cookie if (isterminator(*aIter)) { ++aIter; + while (aIter != aEndIter && isvalueseparator(*aIter)) { + ++aIter; + } + nsACString::const_char_iterator end = aIter - 1; + if (!isterminator(*end)) { + // The cookie isn't valid because we have multiple terminators or + // a terminator followed by a value separator. Add those invalid + // characters to the cookie string or value so it will be rejected. + if (aEqualsFound) { + aTokenString.Rebind(start, end); + } else { + aTokenValue.Rebind(start, end); + } + return false; + } return true; } // fall-through: aIter is on ';', increment and return false @@ -1570,6 +1660,17 @@ static inline void SetSameSiteAttribute(CookieStruct& aCookieData, aCookieData.rawSameSite() = aValue; } +// Tests for control characters, defined by RFC 5234 to be %x00-1F / %x7F. +// An exception is made for HTAB as the cookie spec treats that as whitespace. +static bool ContainsControlChars(const nsACString& aString) { + const auto* start = aString.BeginReading(); + const auto* end = aString.EndReading(); + + return std::find_if(start, end, [](unsigned char c) { + return (c <= 0x1F && c != 0x09) || c == 0x7F; + }) != end; +} + // Parses attributes from cookie header. expires/max-age attributes aren't // folded into the cookie struct here, because we don't know which one to use // until we've parsed the header. @@ -1627,6 +1728,14 @@ bool CookieService::ParseAttributes(nsIConsoleReportCollector* aCRC, newCookie = GetTokenValue(cookieStart, cookieEnd, tokenString, tokenValue, equalsFound); + if (ContainsControlChars(tokenString) || ContainsControlChars(tokenValue)) { + CookieLogging::LogMessageToConsole( + aCRC, aHostURI, nsIScriptError::errorFlag, CONSOLE_REJECTION_CATEGORY, + "CookieRejectedInvalidCharAttributes"_ns, + AutoTArray{NS_ConvertUTF8toUTF16(aCookieData.name())}); + return newCookie; + } + // decide which attribute we have, and copy the string if (tokenString.LowerCaseEqualsLiteral(kPath)) { aCookieData.path() = tokenValue; @@ -1967,25 +2076,6 @@ bool CookieService::CheckDomain(CookieStruct& aCookieData, nsIURI* aHostURI, return true; } -// static -bool CookieService::CheckHiddenPrefix(CookieStruct& aCookieData) { - // If a cookie is nameless, then its value must not start with - // `__Host-` or `__Secure-` - if (!aCookieData.name().IsEmpty()) { - return true; - } - - if (StringBeginsWith(aCookieData.value(), "__Host-"_ns)) { - return false; - } - - if (StringBeginsWith(aCookieData.value(), "__Secure-"_ns)) { - return false; - } - - return true; -} - namespace { nsAutoCString GetPathFromURI(nsIURI* aHostURI) { // strip down everything after the last slash to get the path, @@ -2053,15 +2143,10 @@ bool CookieService::CheckPath(CookieStruct& aCookieData, // regularized and validated the CookieStruct values! bool CookieService::CheckPrefixes(CookieStruct& aCookieData, bool aSecureRequest) { - static const char kSecure[] = "__Secure-"; - static const char kHost[] = "__Host-"; - static const int kSecureLen = sizeof(kSecure) - 1; - static const int kHostLen = sizeof(kHost) - 1; - - bool isSecure = strncmp(aCookieData.name().get(), kSecure, kSecureLen) == 0; - bool isHost = strncmp(aCookieData.name().get(), kHost, kHostLen) == 0; + bool hasSecurePrefix = HasSecurePrefix(aCookieData.name()); + bool hasHostPrefix = HasHostPrefix(aCookieData.name()); - if (!isSecure && !isHost) { + if (!hasSecurePrefix && !hasHostPrefix) { // not one of the magic prefixes: carry on return true; } @@ -2072,7 +2157,7 @@ bool CookieService::CheckPrefixes(CookieStruct& aCookieData, return false; } - if (isHost) { + if (hasHostPrefix) { // The host prefix requires that the path is "/" and that the cookie // had no domain attribute. CheckDomain() and CheckPath() MUST be run // first to make sure invalid attributes are rejected and to regularlize diff --git a/netwerk/cookie/CookieService.h b/netwerk/cookie/CookieService.h index 51344d6909..ca409cb12f 100644 --- a/netwerk/cookie/CookieService.h +++ b/netwerk/cookie/CookieService.h @@ -124,7 +124,6 @@ class CookieService final : public nsICookieService, static bool CheckDomain(CookieStruct& aCookieData, nsIURI* aHostURI, const nsACString& aBaseDomain, bool aRequireHostMatch); - static bool CheckHiddenPrefix(CookieStruct& aCookieData); static bool CheckPath(CookieStruct& aCookieData, nsIConsoleReportCollector* aCRC, nsIURI* aHostURI); static bool CheckPrefixes(CookieStruct& aCookieData, bool aSecureRequest); diff --git a/netwerk/cookie/CookieServiceChild.cpp b/netwerk/cookie/CookieServiceChild.cpp index 84f34a9a37..dab62d1523 100644 --- a/netwerk/cookie/CookieServiceChild.cpp +++ b/netwerk/cookie/CookieServiceChild.cpp @@ -96,9 +96,9 @@ RefPtr CookieServiceChild::TrackCookieLoad( aChannel->GetURI(getter_AddRefs(uri)); nsCOMPtr loadInfo = aChannel->LoadInfo(); - OriginAttributes attrs = loadInfo->GetOriginAttributes(); + OriginAttributes storageOriginAttributes = loadInfo->GetOriginAttributes(); StoragePrincipalHelper::PrepareEffectiveStoragePrincipalOriginAttributes( - aChannel, attrs); + aChannel, storageOriginAttributes); bool isSafeTopLevelNav = CookieCommons::IsSafeTopLevelNav(aChannel); bool hadCrossSiteRedirects = false; @@ -107,11 +107,30 @@ RefPtr CookieServiceChild::TrackCookieLoad( RefPtr self(this); - // TODO (Bug 1874174): A channel could access both unpartitioned and - // partitioned cookie jars. We will need to pass partitioned and unpartitioned - // originAttributes according the storage access. - nsTArray attrsList; - attrsList.AppendElement(attrs); + nsTArray originAttributesList; + originAttributesList.AppendElement(storageOriginAttributes); + + // CHIPS - If CHIPS is enabled the partitioned cookie jar is always available + // (and therefore the partitioned OriginAttributes), the unpartitioned cookie + // jar is only available in first-party or third-party with storageAccess + // contexts. + bool isCHIPS = StaticPrefs::network_cookie_cookieBehavior_optInPartitioning(); + bool isUnpartitioned = storageOriginAttributes.mPartitionKey.IsEmpty(); + if (isCHIPS && isUnpartitioned) { + // Assert that we are only doing this if we are first-party or third-party + // with storageAccess. + MOZ_ASSERT( + !result.contains(ThirdPartyAnalysis::IsForeign) || + result.contains(ThirdPartyAnalysis::IsStorageAccessPermissionGranted)); + // Add the partitioned principal to principals. + OriginAttributes partitionedOriginAttributes; + StoragePrincipalHelper::GetOriginAttributes( + aChannel, partitionedOriginAttributes, + StoragePrincipalHelper::ePartitionedPrincipal); + originAttributesList.AppendElement(partitionedOriginAttributes); + // Assert partitionedOAs have partitioneKey set. + MOZ_ASSERT(!partitionedOriginAttributes.mPartitionKey.IsEmpty()); + } return SendGetCookieList( uri, result.contains(ThirdPartyAnalysis::IsForeign), @@ -121,7 +140,7 @@ RefPtr CookieServiceChild::TrackCookieLoad( result.contains( ThirdPartyAnalysis::IsStorageAccessPermissionGranted), rejectedReason, isSafeTopLevelNav, isSameSiteForeign, - hadCrossSiteRedirects, attrsList) + hadCrossSiteRedirects, originAttributesList) ->Then( GetCurrentSerialEventTarget(), __func__, [self, uri](const nsTArray& aCookiesListTable) { @@ -325,15 +344,6 @@ CookieServiceChild::GetCookieStringFromDocument(dom::Document* aDocument, aCookieString.Truncate(); - nsCOMPtr cookiePrincipal = - aDocument->EffectiveCookiePrincipal(); - - // TODO (Bug 1874174): A document could access both unpartitioned and - // partitioned cookie jars. We will need to prepare partitioned and - // unpartitioned principals for access both cookie jars. - nsTArray> principals; - principals.AppendElement(cookiePrincipal); - bool thirdParty = true; nsPIDOMWindowInner* innerWindow = aDocument->GetInnerWindow(); // in gtests we don't have a window, let's consider those requests as 3rd @@ -347,6 +357,26 @@ CookieServiceChild::GetCookieStringFromDocument(dom::Document* aDocument, } } + nsCOMPtr cookiePrincipal = + aDocument->EffectiveCookiePrincipal(); + + nsTArray> principals; + principals.AppendElement(cookiePrincipal); + + // CHIPS - If CHIPS is enabled the partitioned cookie jar is always available + // (and therefore the partitioned principal), the unpartitioned cookie jar is + // only available in first-party or third-party with storageAccess contexts. + bool isCHIPS = StaticPrefs::network_cookie_cookieBehavior_optInPartitioning(); + bool isUnpartitioned = + cookiePrincipal->OriginAttributesRef().mPartitionKey.IsEmpty(); + if (isCHIPS && isUnpartitioned) { + // Assert that we are only doing this if we are first-party or third-party + // with storageAccess. + MOZ_ASSERT(!thirdParty || aDocument->UsingStorageAccess()); + // Add the partitioned principal to principals + principals.AppendElement(aDocument->PartitionedPrincipal()); + } + for (auto& principal : principals) { if (!CookieCommons::IsSchemeSupported(principal)) { return NS_OK; @@ -488,7 +518,14 @@ CookieServiceChild::SetCookieStringFromDocument( // (those come from the parent, which already checks this), // but script could see an inconsistent view of things. - nsCOMPtr principal = aDocument->EffectiveCookiePrincipal(); + // CHIPS - If the cookie has the "Partitioned" attribute set it will be + // stored in the partitioned cookie jar. + bool needPartitioned = + StaticPrefs::network_cookie_cookieBehavior_optInPartitioning() && + cookie->RawIsPartitioned(); + nsCOMPtr principal = + needPartitioned ? aDocument->PartitionedPrincipal() + : aDocument->EffectiveCookiePrincipal(); bool isPotentiallyTrustworthy = principal->GetIsOriginPotentiallyTrustworthy(); @@ -560,9 +597,10 @@ CookieServiceChild::SetCookieStringFromHttp(nsIURI* aHostURI, nsCString cookieString(aCookieString); - OriginAttributes attrs = loadInfo->GetOriginAttributes(); + OriginAttributes storagePrincipalOriginAttributes = + loadInfo->GetOriginAttributes(); StoragePrincipalHelper::PrepareEffectiveStoragePrincipalOriginAttributes( - aChannel, attrs); + aChannel, storagePrincipalOriginAttributes); bool requireHostMatch; nsCString baseDomain; @@ -580,18 +618,15 @@ CookieServiceChild::SetCookieStringFromHttp(nsIURI* aHostURI, result.contains(ThirdPartyAnalysis::IsThirdPartyTrackingResource), result.contains(ThirdPartyAnalysis::IsThirdPartySocialTrackingResource), result.contains(ThirdPartyAnalysis::IsStorageAccessPermissionGranted), - aCookieString, CountCookiesFromHashTable(baseDomain, attrs), attrs, - &rejectedReason); + aCookieString, + CountCookiesFromHashTable(baseDomain, storagePrincipalOriginAttributes), + storagePrincipalOriginAttributes, &rejectedReason); if (cookieStatus != STATUS_ACCEPTED && cookieStatus != STATUS_ACCEPT_SESSION) { return NS_OK; } - CookieKey key(baseDomain, attrs); - - nsTArray cookiesToSend; - int64_t currentTimeInUsec = PR_Now(); bool addonAllowsLoad = false; @@ -612,6 +647,23 @@ CookieServiceChild::SetCookieStringFromHttp(nsIURI* aHostURI, nsICookieService::BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN && !result.contains(ThirdPartyAnalysis::IsStorageAccessPermissionGranted); + // CHIPS - The partitioned cookie jar is always available and it is always + // possible to store cookies in it using the "Partitioned" attribute. + // Prepare the partitioned principals OAs to enable possible partitioned + // cookie storing from first-party or with StorageAccess. + // Similar behavior to CookieService::SetCookieStringFromHttp(). + OriginAttributes partitionedPrincipalOriginAttributes; + bool isPartitionedPrincipal = + !storagePrincipalOriginAttributes.mPartitionKey.IsEmpty(); + bool isCHIPS = StaticPrefs::network_cookie_cookieBehavior_optInPartitioning(); + // Only need to get OAs if we don't already use the partitioned principal. + if (isCHIPS && !isPartitionedPrincipal) { + StoragePrincipalHelper::GetOriginAttributes( + aChannel, partitionedPrincipalOriginAttributes, + StoragePrincipalHelper::ePartitionedPrincipal); + } + + nsTArray cookiesToSend, partitionedCookiesToSend; bool moreCookies; do { CookieStruct cookieData; @@ -642,23 +694,47 @@ CookieServiceChild::SetCookieStringFromHttp(nsIURI* aHostURI, continue; } - RefPtr cookie = Cookie::Create(cookieData, attrs); + // CHIPS - If the partitioned attribute is set, store cookie in partitioned + // cookie jar independent of context. If the cookies are stored in the + // partitioned cookie jar anyway no special treatment of CHIPS cookies + // necessary. + bool needPartitioned = + isCHIPS && cookieData.isPartitioned() && !isPartitionedPrincipal; + nsTArray& cookiesToSendRef = + needPartitioned ? partitionedCookiesToSend : cookiesToSend; + OriginAttributes& cookieOriginAttributes = + needPartitioned ? partitionedPrincipalOriginAttributes + : storagePrincipalOriginAttributes; + // Assert that partitionedPrincipalOriginAttributes are initialized if used. + MOZ_ASSERT_IF( + needPartitioned, + !partitionedPrincipalOriginAttributes.mPartitionKey.IsEmpty()); + + RefPtr cookie = Cookie::Create(cookieData, cookieOriginAttributes); MOZ_ASSERT(cookie); cookie->SetLastAccessed(currentTimeInUsec); cookie->SetCreationTime( Cookie::GenerateUniqueCreationTime(currentTimeInUsec)); - RecordDocumentCookie(cookie, attrs); - cookiesToSend.AppendElement(cookieData); + RecordDocumentCookie(cookie, cookieOriginAttributes); + cookiesToSendRef.AppendElement(cookieData); } while (moreCookies); // Asynchronously call the parent. - if (CanSend() && !cookiesToSend.IsEmpty()) { + if (CanSend()) { RefPtr httpChannelChild = do_QueryObject(aChannel); MOZ_ASSERT(httpChannelChild); - httpChannelChild->SendSetCookies(baseDomain, attrs, aHostURI, true, - cookiesToSend); + if (!cookiesToSend.IsEmpty()) { + httpChannelChild->SendSetCookies(baseDomain, + storagePrincipalOriginAttributes, + aHostURI, true, cookiesToSend); + } + if (!partitionedCookiesToSend.IsEmpty()) { + httpChannelChild->SendSetCookies( + baseDomain, partitionedPrincipalOriginAttributes, aHostURI, true, + partitionedCookiesToSend); + } } return NS_OK; diff --git a/netwerk/cookie/CookieServiceParent.cpp b/netwerk/cookie/CookieServiceParent.cpp index 75c024cec6..187a31e002 100644 --- a/netwerk/cookie/CookieServiceParent.cpp +++ b/netwerk/cookie/CookieServiceParent.cpp @@ -15,6 +15,7 @@ #include "mozIThirdPartyUtil.h" #include "nsArrayUtils.h" #include "nsIChannel.h" +#include "mozilla/StaticPrefs_network.h" #include "nsIEffectiveTLDService.h" #include "nsNetCID.h" #include "nsMixedContentBlocker.h" @@ -119,17 +120,11 @@ void CookieServiceParent::TrackCookieLoad(nsIChannel* aChannel) { aChannel->GetURI(getter_AddRefs(uri)); nsCOMPtr loadInfo = aChannel->LoadInfo(); - OriginAttributes attrs = loadInfo->GetOriginAttributes(); bool isSafeTopLevelNav = CookieCommons::IsSafeTopLevelNav(aChannel); bool hadCrossSiteRedirects = false; bool isSameSiteForeign = CookieCommons::IsSameSiteForeign(aChannel, uri, &hadCrossSiteRedirects); - // TODO (Bug 1874174): A channel could load both unpartitioned and partitioned - // cookie jars together. We will need to track both originAttributes for them. - StoragePrincipalHelper::PrepareEffectiveStoragePrincipalOriginAttributes( - aChannel, attrs); - nsCOMPtr thirdPartyUtil; thirdPartyUtil = do_GetService(THIRDPARTYUTIL_CONTRACTID); @@ -137,8 +132,34 @@ void CookieServiceParent::TrackCookieLoad(nsIChannel* aChannel) { ThirdPartyAnalysisResult result = thirdPartyUtil->AnalyzeChannel( aChannel, false, nullptr, nullptr, &rejectedReason); + OriginAttributes storageOriginAttributes = loadInfo->GetOriginAttributes(); + StoragePrincipalHelper::PrepareEffectiveStoragePrincipalOriginAttributes( + aChannel, storageOriginAttributes); + nsTArray originAttributesList; - originAttributesList.AppendElement(attrs); + originAttributesList.AppendElement(storageOriginAttributes); + + // CHIPS - If CHIPS is enabled the partitioned cookie jar is always available + // (and therefore the partitioned OriginAttributes), the unpartitioned cookie + // jar is only available in first-party or third-party with storageAccess + // contexts. + bool isCHIPS = StaticPrefs::network_cookie_cookieBehavior_optInPartitioning(); + bool isUnpartitioned = storageOriginAttributes.mPartitionKey.IsEmpty(); + if (isCHIPS && isUnpartitioned) { + // Assert that we are only doing this if we are first-party or third-party + // with storageAccess. + MOZ_ASSERT( + !result.contains(ThirdPartyAnalysis::IsForeign) || + result.contains(ThirdPartyAnalysis::IsStorageAccessPermissionGranted)); + // Add the partitioned principal to principals + OriginAttributes partitionedOriginAttributes; + StoragePrincipalHelper::GetOriginAttributes( + aChannel, partitionedOriginAttributes, + StoragePrincipalHelper::ePartitionedPrincipal); + originAttributesList.AppendElement(partitionedOriginAttributes); + // Assert partitionedOAs have partitioneKey set. + MOZ_ASSERT(!partitionedOriginAttributes.mPartitionKey.IsEmpty()); + } for (auto& originAttributes : originAttributesList) { UpdateCookieInContentList(uri, originAttributes); diff --git a/netwerk/cookie/test/browser/browser.toml b/netwerk/cookie/test/browser/browser.toml index 05a302ddbd..56807e4bf1 100644 --- a/netwerk/cookie/test/browser/browser.toml +++ b/netwerk/cookie/test/browser/browser.toml @@ -8,6 +8,9 @@ support-files = [ ["browser_broadcastChannel.js"] +["browser_cookie_chips.js"] +support-files = ["chips.sjs"] + ["browser_cookie_insecure_overwrites_secure.js"] ["browser_cookie_purge_sync.js"] @@ -17,6 +20,9 @@ support-files = ["server.sjs"] ["browser_cookies_ipv6.js"] +["browser_cookies_serviceWorker.js"] +support-files = ["cookies.sjs", "serviceWorker.js"] + ["browser_domCache.js"] ["browser_indexedDB.js"] diff --git a/netwerk/cookie/test/browser/browser_cookie_chips.js b/netwerk/cookie/test/browser/browser_cookie_chips.js new file mode 100644 index 0000000000..ba0c8f3a0e --- /dev/null +++ b/netwerk/cookie/test/browser/browser_cookie_chips.js @@ -0,0 +1,539 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +add_setup(() => { + // These are functional and not integration tests, cookieBehavior accept lets + // us have StorageAccess on thirparty. + Services.prefs.setIntPref( + "network.cookie.cookieBehavior", + Ci.nsICookieService.BEHAVIOR_ACCEPT + ); + Services.prefs.setBoolPref( + "network.cookieJarSettings.unblocked_for_testing", + true + ); + Services.prefs.setBoolPref( + "network.cookie.cookieBehavior.optInPartitioning", + true + ); + Services.prefs.setBoolPref("dom.storage_access.enabled", true); + Services.prefs.setBoolPref("dom.storage_access.prompt.testing", true); + Services.cookies.removeAll(); + Services.perms.removeAll(); +}); + +registerCleanupFunction(() => { + Services.prefs.clearUserPref("network.cookie.cookieBehavior"); + Services.prefs.clearUserPref( + "network.cookieJarSettings.unblocked_for_testing" + ); + Services.prefs.clearUserPref( + "network.cookie.cookieBehavior.optInPartitioning" + ); + Services.prefs.clearUserPref("dom.storage_access.enabled"); + Services.prefs.clearUserPref("dom.storage_access.prompt.testing"); + Services.cookies.removeAll(); + Services.perms.removeAll(); +}); + +const COOKIE_PARTITIONED = + "cookie=partitioned; Partitioned; Secure; SameSite=None;"; +const COOKIE_UNPARTITIONED = "cookie=unpartitioned; Secure; SameSite=None;"; + +const PATH = "/browser/netwerk/cookie/test/browser/"; +const PATH_EMPTY = PATH + "file_empty.html"; +const HTTP_COOKIE_SET = PATH + "chips.sjs?set"; +const HTTP_COOKIE_GET = PATH + "chips.sjs?get"; + +const FIRST_PARTY = "example.com"; +const THIRD_PARTY = "example.org"; + +const URL_DOCUMENT_FIRSTPARTY = "https://" + FIRST_PARTY + PATH_EMPTY; +const URL_DOCUMENT_THIRDPARTY = "https://" + THIRD_PARTY + PATH_EMPTY; +const URL_HTTP_FIRSTPARTY = "https://" + FIRST_PARTY + "/" + HTTP_COOKIE_SET; +const URL_HTTP_THIRDPARTY = "https://" + THIRD_PARTY + "/" + HTTP_COOKIE_SET; + +function createOriginAttributes(partitionKey) { + return JSON.stringify({ + firstPartyDomain: "", + geckoViewSessionContextId: "", + inIsolatedMozBrowser: false, + partitionKey, + privateBrowsingId: 0, + userContextId: 0, + }); +} + +function createPartitonKey(url) { + let uri = NetUtil.newURI(url); + return `(${uri.scheme},${uri.host})`; +} + +// OriginAttributes used to access partitioned and unpartitioned cookie jars +// in all tests. +const partitionedOAs = createOriginAttributes( + createPartitonKey(URL_DOCUMENT_FIRSTPARTY) +); +const unpartitionedOAs = createOriginAttributes(""); + +// Set partitioned and unpartitioned cookie from first-party document. +// CHIPS "Partitioned" cookie MUST always be stored in partitioned jar. +// This calls CookieServiceChild::SetCookieStringFromDocument() internally. +// CookieService::SetCookieStringFromDocument() is not explicitly tested since +// CHIPS are in the common function CookieCommons::CreateCookieFromDocument(). +add_task( + async function test_chips_store_partitioned_document_first_party_child() { + const tab = BrowserTestUtils.addTab(gBrowser, URL_DOCUMENT_FIRSTPARTY); + const browser = gBrowser.getBrowserForTab(tab); + await BrowserTestUtils.browserLoaded(browser); + + // Set partitioned and unpartitioned cookie from document child-side + await SpecialPowers.spawn( + browser, + [COOKIE_PARTITIONED, COOKIE_UNPARTITIONED], + (partitioned, unpartitioned) => { + content.document.cookie = partitioned; + content.document.cookie = unpartitioned; + } + ); + + // Get cookies from partitioned jar + let partitioned = Services.cookies.getCookiesWithOriginAttributes( + partitionedOAs, + FIRST_PARTY + ); + // Get cookies from unpartitioned jar + let unpartitioned = Services.cookies.getCookiesWithOriginAttributes( + unpartitionedOAs, + FIRST_PARTY + ); + + // Assert partitioned/unpartitioned cookie were stored in correct jars + Assert.equal(partitioned.length, 1); + Assert.equal(partitioned[0].value, "partitioned"); + Assert.equal(unpartitioned.length, 1); + Assert.equal(unpartitioned[0].value, "unpartitioned"); + + // Cleanup + BrowserTestUtils.removeTab(tab); + Services.cookies.removeAll(); + } +); + +// Set partitioned and unpartitioned cookie from third-party document with storage +// access. CHIPS "Partitioned" cookie MUST always be stored in partitioned jar. +// This calls CookieServiceChild::SetCookieStringFromDocument() internally. +// CookieService::SetCookieStringFromDocument() is not explicitly tested since +// CHIPS are in the common function CookieCommons::CreateCookieFromDocument(). +add_task( + async function test_chips_store_partitioned_document_third_party_storage_access_child() { + const tab = BrowserTestUtils.addTab(gBrowser, URL_DOCUMENT_FIRSTPARTY); + const browser = gBrowser.getBrowserForTab(tab); + await BrowserTestUtils.browserLoaded(browser); + + // Spawn document bc + await SpecialPowers.spawn( + browser, + [URL_DOCUMENT_THIRDPARTY, COOKIE_PARTITIONED, COOKIE_UNPARTITIONED], + async (url, partitioned, unpartitioned) => { + let ifr = content.document.createElement("iframe"); + ifr.src = url; + content.document.body.appendChild(ifr); + await ContentTaskUtils.waitForEvent(ifr, "load"); + + // Spawn iframe bc + await SpecialPowers.spawn( + await ifr.browsingContext, + [partitioned, unpartitioned], + async (partitioned, unpartitioned) => { + ok( + await content.document.hasStorageAccess(), + "example.org should have storageAccess by CookieBehavior 0 / test setup" + ); + + content.document.cookie = partitioned; + content.document.cookie = unpartitioned; + } + ); + } + ); + + // Get cookies from partitioned jar + let partitioned = Services.cookies.getCookiesWithOriginAttributes( + partitionedOAs, + THIRD_PARTY + ); + // Get cookies from unpartitioned jar + let unpartitioned = Services.cookies.getCookiesWithOriginAttributes( + unpartitionedOAs, + THIRD_PARTY + ); + + // Assert partitioned/unpartitioned cookie were stored in correct jars + Assert.equal(partitioned.length, 1); + Assert.equal(partitioned[0].value, "partitioned"); + Assert.equal(unpartitioned.length, 1); + Assert.equal(unpartitioned[0].value, "unpartitioned"); + + // Cleanup + BrowserTestUtils.removeTab(tab); + Services.cookies.removeAll(); + } +); + +// Set partitioned and unpartitioned cookie from first-party http load. +// CHIPS "Partitioned" cookie MUST always be stored in partitioned jar. +// This calls CookieService::SetCookieStringFromHttp() internally. +add_task(async function test_chips_store_partitioned_http_first_party_parent() { + // Set partitioned and unpartitioned cookie from http parent side through + // chips.sjs being loaded. + const tab = BrowserTestUtils.addTab(gBrowser, URL_HTTP_FIRSTPARTY); + const browser = gBrowser.getBrowserForTab(tab); + await BrowserTestUtils.browserLoaded(browser); + + // Get cookies from partitioned jar + let partitioned = Services.cookies.getCookiesWithOriginAttributes( + partitionedOAs, + FIRST_PARTY + ); + // Get cookies from unpartitioned jar + let unpartitioned = Services.cookies.getCookiesWithOriginAttributes( + unpartitionedOAs, + FIRST_PARTY + ); + + // Assert partitioned/unpartitioned cookie were stored in correct jars + Assert.equal(partitioned.length, 1); + Assert.equal(partitioned[0].value, "partitioned"); + Assert.equal(unpartitioned.length, 1); + Assert.equal(unpartitioned[0].value, "unpartitioned"); + + // Cleanup + BrowserTestUtils.removeTab(tab); + Services.cookies.removeAll(); +}); + +// Set partitioned and unpartitioned cookie from third-party http load. +// CHIPS "Partitioned" cookie MUST always be stored in partitioned jar. +// This calls CookieService::SetCookieStringFromHttp() internally. +add_task( + async function test_chips_store_partitioned_http_third_party_storage_access_parent() { + const tab = BrowserTestUtils.addTab(gBrowser, URL_DOCUMENT_FIRSTPARTY); + const browser = gBrowser.getBrowserForTab(tab); + await BrowserTestUtils.browserLoaded(browser); + + // Spawn document bc + await SpecialPowers.spawn(browser, [URL_HTTP_THIRDPARTY], async url => { + let ifr = content.document.createElement("iframe"); + ifr.src = url; + content.document.body.appendChild(ifr); + // Send http request with "set" query parameter, partitioned and + // unpartitioned cookie will be set through http response from chips.sjs. + await ContentTaskUtils.waitForEvent(ifr, "load"); + + // Spawn iframe bc + await SpecialPowers.spawn(await ifr.browsingContext, [], async () => { + ok( + await content.document.hasStorageAccess(), + "example.org should have storageAccess by CookieBehavior 0 / test setup" + ); + }); + }); + + // Get cookies from partitioned jar + let partitioned = Services.cookies.getCookiesWithOriginAttributes( + partitionedOAs, + THIRD_PARTY + ); + // Get cookies from unpartitioned jar + let unpartitioned = Services.cookies.getCookiesWithOriginAttributes( + unpartitionedOAs, + THIRD_PARTY + ); + + // Assert partitioned/unpartitioned cookie were stored in correct jars + Assert.equal(partitioned.length, 1); + Assert.equal(partitioned[0].value, "partitioned"); + Assert.equal(unpartitioned.length, 1); + Assert.equal(unpartitioned[0].value, "unpartitioned"); + + // Cleanup + BrowserTestUtils.removeTab(tab); + Services.cookies.removeAll(); + } +); + +// TODO CHIPS - Tests for CookieServiceChild::SetCookieStringFromHttp() need +// to be added. Since this is only checkable on onProxyConnectSuccess needs +// proxy setup test harness. It is also called after onStartRequest() (Http) +// but cookies are already set by the parents +// CookieService::SetCookieStringFromHttp() call. + +// Get partitioned and unpartitioned cookies from document (child). +// This calls CookieServiceChild::GetCookieStringFromDocument() internally. +add_task( + async function test_chips_send_partitioned_and_unpartitioned_document_child() { + const tab = BrowserTestUtils.addTab(gBrowser, URL_DOCUMENT_FIRSTPARTY); + const browser = gBrowser.getBrowserForTab(tab); + await BrowserTestUtils.browserLoaded(browser); + + // Spawn document bc + await SpecialPowers.spawn( + browser, + [COOKIE_PARTITIONED, COOKIE_UNPARTITIONED], + async (partitioned, unpartitioned) => { + content.document.cookie = partitioned; + content.document.cookie = unpartitioned; + + // Assert both unpartitioned and partitioned cookie are returned. + let cookies = content.document.cookie; + ok( + cookies.includes("cookie=partitioned"), + "Cookie from partitioned jar was sent." + ); + ok( + cookies.includes("cookie=unpartitioned"), + "Cookie from unpartitioned jar was sent." + ); + } + ); + + // Cleanup + BrowserTestUtils.removeTab(tab); + Services.cookies.removeAll(); + } +); + +// Get partitioned and unpartitioned cookies from document (child) after +// storageAccess was granted. This calls CookieServiceChild::TrackCookieLoad() +// internally to update child's cookies. +add_task( + async function test_chips_send_partitioned_and_unpartitioned_on_storage_access_child() { + // Set cookieBehavior to BEHAVIOR_REJECT_TRACKERS_AND_PARTITION_FOREIGN for + // requestStorageAccess() based test. + Services.prefs.setIntPref("network.cookie.cookieBehavior", 5); + + // Set example.org first-party unpartitioned cookie + await BrowserTestUtils.withNewTab( + URL_DOCUMENT_THIRDPARTY, + async browser => { + info("Set a first party cookie via `document.cookie`."); + await SpecialPowers.spawn( + browser, + [COOKIE_UNPARTITIONED], + async unpartitioned => { + content.document.cookie = unpartitioned; + is( + content.document.cookie, + "cookie=unpartitioned", + "Unpartitioned cookie was set." + ); + } + ); + } + ); + + // Assert cookie was set on parent cookie service for example.org. + // Get cookies from unpartitioned jar + let unpartitioned = Services.cookies.getCookiesWithOriginAttributes( + unpartitionedOAs, + THIRD_PARTY + ); + Assert.equal(unpartitioned.length, 1); + Assert.equal(unpartitioned[0].value, "unpartitioned"); + + // Load example.com as first-party in tab + const tab = BrowserTestUtils.addTab(gBrowser, URL_DOCUMENT_FIRSTPARTY); + const browser = gBrowser.getBrowserForTab(tab); + await BrowserTestUtils.browserLoaded(browser); + + // Set third-party cookie from example.org iframe, get storageAccess and + // check cookies. + // Spawn document bc + await SpecialPowers.spawn( + browser, + [URL_DOCUMENT_THIRDPARTY, COOKIE_PARTITIONED], + async (url, partitioned) => { + // Create third-party iframe + let ifr = content.document.createElement("iframe"); + ifr.src = url; + content.document.body.appendChild(ifr); + await ContentTaskUtils.waitForEvent(ifr, "load"); + + // Spawn iframe bc + await SpecialPowers.spawn( + await ifr.browsingContext, + [partitioned], + async partitioned => { + ok( + !(await content.document.hasStorageAccess()), + "example.org should not have storageAccess initially." + ); + + // Set a partitioned third-party cookie and assert its the only. + content.document.cookie = partitioned; + is( + content.document.cookie, + "cookie=partitioned", + "Partitioned cookie was set." + ); + + info("Simulate user activation."); + SpecialPowers.wrap(content.document).notifyUserGestureActivation(); + + info("Request storage access."); + await content.document.requestStorageAccess(); + + ok( + await content.document.hasStorageAccess(), + "example.org should now have storageAccess." + ); + + // Assert both unpartitioned and partitioned cookie are returned. + let cookies = content.document.cookie; + ok( + cookies.includes("cookie=partitioned"), + "Cookie from partitioned jar was sent." + ); + ok( + cookies.includes("cookie=unpartitioned"), + "Cookie from unpartitioned jar was sent." + ); + } + ); + } + ); + + // Cleanup + BrowserTestUtils.removeTab(tab); + Services.cookies.removeAll(); + Services.perms.removeAll(); + Services.prefs.setIntPref("network.cookie.cookieBehavior", 0); + } +); + +// Set partitioned and unpartitioned cookies for URL_DOCUMENT_FIRSTPARTY, then +// load URL again, assure cookies are correctly send to content child process. +// This tests CookieServiceParent::TrackCookieLoad() internally. +add_task( + async function test_chips_send_partitioned_and_unpartitioned_document_parent() { + // Set example.com first-party unpartitioned and partitioned cookie, then + // close tab. + await BrowserTestUtils.withNewTab( + URL_DOCUMENT_FIRSTPARTY, + async browser => { + await SpecialPowers.spawn( + browser, + [COOKIE_PARTITIONED, COOKIE_UNPARTITIONED], + async (partitioned, unpartitioned) => { + content.document.cookie = unpartitioned; + content.document.cookie = partitioned; + let cookies = content.document.cookie; + ok( + cookies.includes("cookie=unpartitioned"), + "Unpartitioned cookie was set." + ); + ok( + cookies.includes("cookie=partitioned"), + "Partitioned cookie was set." + ); + } + ); + } + ); + + // Assert we have one partitioned and one unpartitioned cookie set. + // Get cookies from partitioned jar + let partitioned = Services.cookies.getCookiesWithOriginAttributes( + partitionedOAs, + FIRST_PARTY + ); + // Get cookies from unpartitioned jar + let unpartitioned = Services.cookies.getCookiesWithOriginAttributes( + unpartitionedOAs, + FIRST_PARTY + ); + Assert.equal(partitioned.length, 1); + Assert.equal(partitioned[0].value, "partitioned"); + Assert.equal(unpartitioned.length, 1); + Assert.equal(unpartitioned[0].value, "unpartitioned"); + + // Reload example.com and assert previously set cookies are correctly + // send to content child document. + await BrowserTestUtils.withNewTab( + URL_DOCUMENT_FIRSTPARTY, + async browser => { + await SpecialPowers.spawn(browser, [], () => { + let cookies = content.document.cookie; + ok( + cookies.includes("cookie=unpartitioned"), + "Unpartitioned cookie was sent." + ); + ok( + cookies.includes("cookie=partitioned"), + "Partitioned cookie was sent." + ); + }); + } + ); + + // Cleanup + Services.cookies.removeAll(); + } +); + +// Set partitioned and unpartitioned cookies for URL_DOCUMENT_FIRSTPARTY, then +// send http request, assure cookies are correctly send in "Cookie" header. +// This tests CookieService::GetCookieStringFromHttp() internally. +add_task( + async function test_chips_send_partitioned_and_unpartitioned_http_parent() { + // Load empty document. + let tab = BrowserTestUtils.addTab(gBrowser, URL_DOCUMENT_FIRSTPARTY); + let browser = gBrowser.getBrowserForTab(tab); + await BrowserTestUtils.browserLoaded(browser); + + await SpecialPowers.spawn( + browser, + [HTTP_COOKIE_SET, HTTP_COOKIE_GET], + async (set, get) => { + // Send http request with "set" query parameter, partitioned and + // unpartitioned cookie will be set through http response. + await content.fetch(set); + + // Assert cookies were set to document. + let cookies = content.document.cookie; + ok( + cookies.includes("cookie=unpartitioned"), + "Unpartitioned cookie was set to document." + ); + ok( + cookies.includes("cookie=partitioned"), + "Partitioned cookie was set to document." + ); + + // Send http request with "get" query parameter, chips.sjs will return + // the request "Cookie" header string. + await content + .fetch(get) + .then(response => response.text()) + .then(requestCookies => { + // Assert cookies were sent in http request. + ok( + requestCookies.includes("cookie=unpartitioned"), + "Unpartitioned cookie was sent in http request." + ); + ok( + requestCookies.includes("cookie=partitioned"), + "Partitioned cookie was sent in http request." + ); + }); + } + ); + + // Cleanup + BrowserTestUtils.removeTab(tab); + Services.cookies.removeAll(); + } +); diff --git a/netwerk/cookie/test/browser/browser_cookies_serviceWorker.js b/netwerk/cookie/test/browser/browser_cookies_serviceWorker.js new file mode 100644 index 0000000000..813e1fe7cb --- /dev/null +++ b/netwerk/cookie/test/browser/browser_cookies_serviceWorker.js @@ -0,0 +1,540 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const TEST_CROSS_SITE_DOMAIN = "https://example.net/"; +const TEST_CROSS_SITE_PAGE = + TEST_CROSS_SITE_DOMAIN + TEST_PATH + "file_empty.html"; + +add_setup(async function () { + await SpecialPowers.pushPrefEnv({ + set: [ + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.testing.enabled", true], + ], + }); + + Services.cookies.removeAll(); +}); + +function registerSW(browser) { + return SpecialPowers.spawn(browser, [], async _ => { + let reg = await content.navigator.serviceWorker.register( + "serviceWorker.js" + ); + + await ContentTaskUtils.waitForCondition(() => { + return reg.active && reg.active.state === "activated"; + }, "The service worker is activated"); + + ok( + content.navigator.serviceWorker.controller, + "The service worker controls the document successfully." + ); + }); +} + +function fetchCookiesFromSW(browser) { + return SpecialPowers.spawn(browser, [], async _ => { + return new content.Promise(resolve => { + content.navigator.serviceWorker.addEventListener("message", event => { + resolve(event.data.content); + }); + + content.navigator.serviceWorker.controller.postMessage({ + action: "fetch", + url: `cookies.sjs`, + }); + }); + }); +} + +function setCookiesFromSW(browser, cookies) { + let setCookieQuery = ""; + + for (let cookie of cookies) { + setCookieQuery += `Set-Cookie=${cookie}&`; + } + + return SpecialPowers.spawn(browser, [setCookieQuery], async query => { + return new content.Promise(resolve => { + content.navigator.serviceWorker.addEventListener("message", event => { + resolve(event.data.content); + }); + + content.navigator.serviceWorker.controller.postMessage({ + action: "fetch", + url: `cookies.sjs?${query}`, + }); + }); + }); +} + +function unregisterSW(browser) { + return SpecialPowers.spawn(browser, [], async _ => { + const regs = await content.navigator.serviceWorker.getRegistrations(); + for (const reg of regs) { + await reg.unregister(); + } + }); +} + +/** + * Verify a first-party service worker can access both SameSite=None and + * SameSite=Lax cookies set in the first-party context. + */ +add_task(async function testCookiesWithFirstPartyServiceWorker() { + info("Open a tab"); + let tab = await BrowserTestUtils.openNewForegroundTab( + gBrowser, + TEST_TOP_PAGE + ); + + info("Writing cookies to the first-party context."); + await SpecialPowers.spawn(tab.linkedBrowser, [], async _ => { + const cookies = [ + "foo=bar; SameSite=None; Secure", + "fooLax=barLax; SameSite=Lax; Secure", + ]; + + let query = ""; + + for (let cookie of cookies) { + query += `Set-Cookie=${cookie}&`; + } + + await content.fetch(`cookies.sjs?${query}`); + }); + + info("Register a service worker and trigger a fetch request to get cookies."); + await registerSW(tab.linkedBrowser); + let cookieStr = await fetchCookiesFromSW(tab.linkedBrowser); + + is(cookieStr, "foo=bar; fooLax=barLax", "The cookies are expected"); + + info("Set cookies from the service worker."); + await setCookiesFromSW(tab.linkedBrowser, [ + "foo=barSW; SameSite=None; Secure", + "fooLax=barLaxSW; SameSite=Lax; Secure", + ]); + + info("Get cookies from the service worker."); + cookieStr = await fetchCookiesFromSW(tab.linkedBrowser); + + is(cookieStr, "foo=barSW; fooLax=barLaxSW", "The cookies are expected"); + + await unregisterSW(tab.linkedBrowser); + BrowserTestUtils.removeTab(tab); + Services.cookies.removeAll(); +}); + +/** + * Verify a cross-site service worker can only access cookies set in the + * same cross-site context. + */ +add_task(async function testCookiesWithCrossSiteServiceWorker() { + // Disable blocking third-party cookies. + await SpecialPowers.pushPrefEnv({ + set: [["network.cookie.cookieBehavior.optInPartitioning", false]], + }); + + info("Open a cross-site tab"); + let crossSiteTab = await BrowserTestUtils.openNewForegroundTab( + gBrowser, + TEST_CROSS_SITE_PAGE + ); + + info("Writing cookies to the cross site in the first-party context."); + await SpecialPowers.spawn(crossSiteTab.linkedBrowser, [], async _ => { + const cookies = [ + "foo=bar; SameSite=None; Secure", + "fooLax=barLax; SameSite=Lax; Secure", + ]; + + let query = ""; + + for (let cookie of cookies) { + query += `Set-Cookie=${cookie}&`; + } + + await content.fetch(`cookies.sjs?${query}`); + }); + + info("Open a tab"); + let tab = await BrowserTestUtils.openNewForegroundTab( + gBrowser, + TEST_TOP_PAGE + ); + + info("Load a cross-site iframe"); + let crossSiteBc = await SpecialPowers.spawn( + tab.linkedBrowser, + [TEST_CROSS_SITE_PAGE], + async url => { + let ifr = content.document.createElement("iframe"); + + await new content.Promise(resolve => { + ifr.onload = resolve; + content.document.body.appendChild(ifr); + ifr.src = url; + }); + + return ifr.browsingContext; + } + ); + + info("Write cookies in the cross-site iframe"); + await SpecialPowers.spawn(crossSiteBc, [], async _ => { + const cookies = [ + "foo=crossBar; SameSite=None; Secure", + "fooLax=crossBarLax; SameSite=Lax; Secure", + ]; + + let query = ""; + + for (let cookie of cookies) { + query += `Set-Cookie=${cookie}&`; + } + + await content.fetch(`cookies.sjs?${query}`); + }); + + info( + "Register a service worker and trigger a fetch request to get cookies in cross-site context." + ); + await registerSW(crossSiteBc); + let cookieStr = await fetchCookiesFromSW(crossSiteBc); + + is( + cookieStr, + "foo=crossBar", + "Only the SameSite=None cookie set in the third-party iframe is available." + ); + + info("Set cookies from the third-party service worker."); + await setCookiesFromSW(crossSiteBc, [ + "foo=crossBarSW; SameSite=None; Secure", + "fooLax=crossBarLaxSW; SameSite=Lax; Secure", + ]); + + info("Get cookies from the third-party service worker."); + cookieStr = await fetchCookiesFromSW(crossSiteBc); + + is( + cookieStr, + "foo=crossBarSW", + "Only the SameSite=None cookie set in the third-party service worker is available." + ); + + await unregisterSW(crossSiteBc); + BrowserTestUtils.removeTab(crossSiteTab); + BrowserTestUtils.removeTab(tab); + Services.cookies.removeAll(); +}); + +/** + * Verify a cross-site service worker can only access partitioned cookies set in + * the same cross-site context if third-party cookies are blocked. + */ +add_task(async function testPartitionedCookiesWithCrossSiteServiceWorker() { + // Enable blocking third-party cookies. + await SpecialPowers.pushPrefEnv({ + set: [["network.cookie.cookieBehavior.optInPartitioning", true]], + }); + + info("Open a tab"); + let tab = await BrowserTestUtils.openNewForegroundTab( + gBrowser, + TEST_TOP_PAGE + ); + + info("Load a cross-site iframe"); + let crossSiteBc = await SpecialPowers.spawn( + tab.linkedBrowser, + [TEST_CROSS_SITE_PAGE], + async url => { + let ifr = content.document.createElement("iframe"); + + await new content.Promise(resolve => { + ifr.onload = resolve; + content.document.body.appendChild(ifr); + ifr.src = url; + }); + + return ifr.browsingContext; + } + ); + + info("Write cookies in the cross-site iframe"); + await SpecialPowers.spawn(crossSiteBc, [], async _ => { + const cookies = [ + "foo=crossBar; SameSite=None; Secure", + "fooLax=crossBarLax; SameSite=Lax; Secure", + "fooPartitioned=crossBar; SameSite=None; Secure; Partitioned;", + "fooLaxPartitioned=crossBarLax; SameSite=Lax; Secure; Partitioned;", + ]; + + let query = ""; + + for (let cookie of cookies) { + query += `Set-Cookie=${cookie}&`; + } + + await content.fetch(`cookies.sjs?${query}`); + }); + + info( + "Register a service worker and trigger a fetch request to get cookies in cross-site context." + ); + await registerSW(crossSiteBc); + let cookieStr = await fetchCookiesFromSW(crossSiteBc); + + is( + cookieStr, + "fooPartitioned=crossBar", + "Only the SameSite=None partitioned cookie set in the third-party iframe is available." + ); + + info("Set cookies from the third-party service worker."); + await setCookiesFromSW(crossSiteBc, [ + "foo=crossBarSW; SameSite=None; Secure", + "fooLax=crossBarLaxSW; SameSite=Lax; Secure", + "fooPartitioned=crossBarSW; SameSite=None; Secure; Partitioned;", + "fooLaxPartitioned=crossBarLaxSW; SameSite=Lax; Secure; Partitioned;", + ]); + + info("Get cookies from the third-party service worker."); + cookieStr = await fetchCookiesFromSW(crossSiteBc); + + is( + cookieStr, + "fooPartitioned=crossBarSW", + "Only the SameSite=None partitioned cookie set in the third-party service worker is available." + ); + + await unregisterSW(crossSiteBc); + BrowserTestUtils.removeTab(tab); + Services.cookies.removeAll(); +}); + +/** + * Verify a ABA service worker can only access cookies set in the ABA context. + */ +add_task(async function testCookiesWithABAServiceWorker() { + // Disable blocking third-party cookies. + await SpecialPowers.pushPrefEnv({ + set: [["network.cookie.cookieBehavior.optInPartitioning", false]], + }); + + info("Open a tab"); + let tab = await BrowserTestUtils.openNewForegroundTab( + gBrowser, + TEST_TOP_PAGE + ); + + info("Writing cookies to the first-party context."); + await SpecialPowers.spawn(tab.linkedBrowser, [], async _ => { + const cookies = [ + "foo=bar; SameSite=None; Secure", + "fooLax=barLax; SameSite=Lax; Secure", + ]; + + let query = ""; + + for (let cookie of cookies) { + query += `Set-Cookie=${cookie}&`; + } + + await content.fetch(`cookies.sjs?${query}`); + }); + + info("Load a ABA iframe"); + let crossSiteBc = await SpecialPowers.spawn( + tab.linkedBrowser, + [TEST_CROSS_SITE_PAGE], + async url => { + let ifr = content.document.createElement("iframe"); + + await new content.Promise(resolve => { + ifr.onload = resolve; + content.document.body.appendChild(ifr); + ifr.src = url; + }); + + return ifr.browsingContext; + } + ); + + let ABABc = await SpecialPowers.spawn( + crossSiteBc, + [TEST_TOP_PAGE], + async url => { + let ifr = content.document.createElement("iframe"); + + await new content.Promise(resolve => { + ifr.onload = resolve; + content.document.body.appendChild(ifr); + ifr.src = url; + }); + + return ifr.browsingContext; + } + ); + + info( + "Register a service worker and trigger a fetch request to get cookies in the ABA context." + ); + + await registerSW(ABABc); + let cookieStr = await fetchCookiesFromSW(ABABc); + is(cookieStr, "", "No cookie should be available in ABA context."); + + info("Set cookies in the ABA iframe"); + await SpecialPowers.spawn(ABABc, [], async _ => { + const cookies = [ + "fooABA=barABA; SameSite=None; Secure", + "fooABALax=BarABALax; SameSite=Lax; Secure", + ]; + + let query = ""; + + for (let cookie of cookies) { + query += `Set-Cookie=${cookie}&`; + } + + await content.fetch(`cookies.sjs?${query}`); + }); + + info("Get cookies in the ABA service worker."); + cookieStr = await fetchCookiesFromSW(ABABc); + + is( + cookieStr, + "fooABA=barABA", + "Only the SameSite=None cookie set in ABA iframe is available." + ); + + info("Set cookies from the service worker in ABA context"); + await setCookiesFromSW(ABABc, [ + "fooABA=barABASW; SameSite=None; Secure", + "fooABALax=BarABALaxSW; SameSite=Lax; Secure", + ]); + + info("Get cookies from the service worker in the ABA context."); + cookieStr = await fetchCookiesFromSW(ABABc); + + is( + cookieStr, + "fooABA=barABASW", + "Only the SameSite=None cookie set in ABA service worker is available." + ); + + await unregisterSW(ABABc); + BrowserTestUtils.removeTab(tab); + Services.cookies.removeAll(); +}); + +/** + * Verify a ABA service worker can only access partitioned cookies set in the + * ABA context if third-party cookies are blocked. + */ +add_task(async function testCookiesWithABAServiceWorker() { + // Disable blocking third-party cookies. + await SpecialPowers.pushPrefEnv({ + set: [["network.cookie.cookieBehavior.optInPartitioning", true]], + }); + + info("Open a tab"); + let tab = await BrowserTestUtils.openNewForegroundTab( + gBrowser, + TEST_TOP_PAGE + ); + + info("Load a ABA iframe"); + let crossSiteBc = await SpecialPowers.spawn( + tab.linkedBrowser, + [TEST_CROSS_SITE_PAGE], + async url => { + let ifr = content.document.createElement("iframe"); + + await new content.Promise(resolve => { + ifr.onload = resolve; + content.document.body.appendChild(ifr); + ifr.src = url; + }); + + return ifr.browsingContext; + } + ); + + let ABABc = await SpecialPowers.spawn( + crossSiteBc, + [TEST_TOP_PAGE], + async url => { + let ifr = content.document.createElement("iframe"); + + await new content.Promise(resolve => { + ifr.onload = resolve; + content.document.body.appendChild(ifr); + ifr.src = url; + }); + + return ifr.browsingContext; + } + ); + + info("Set cookies in the ABA iframe"); + await SpecialPowers.spawn(ABABc, [], async _ => { + const cookies = [ + "fooABA=barABA; SameSite=None; Secure", + "fooABALax=BarABALax; SameSite=Lax; Secure", + "fooABAPartitioned=barABA; SameSite=None; Secure; Partitioned;", + "fooABALaxPartitioned=BarABALax; SameSite=Lax; Secure; Partitioned;", + ]; + + let query = ""; + + for (let cookie of cookies) { + query += `Set-Cookie=${cookie}&`; + } + + await content.fetch(`cookies.sjs?${query}`); + }); + + info( + "Register a service worker and trigger a fetch request to get cookies in the ABA context." + ); + + await registerSW(ABABc); + + info("Get cookies in the ABA service worker."); + let cookieStr = await fetchCookiesFromSW(ABABc); + + is( + cookieStr, + "fooABAPartitioned=barABA", + "Only the SameSite=None partitioned cookie set in ABA iframe is available." + ); + + info("Set cookies from the service worker in ABA context"); + await setCookiesFromSW(ABABc, [ + "fooABA=barABASW; SameSite=None; Secure", + "fooABALax=BarABALaxSW; SameSite=Lax; Secure", + "fooABAPartitioned=barABASW; SameSite=None; Secure; Partitioned;", + "fooABALaxPartitioned=BarABALaxSW; SameSite=Lax; Secure; Partitioned;", + ]); + + info("Get cookies from the service worker in the ABA context."); + cookieStr = await fetchCookiesFromSW(ABABc); + + is( + cookieStr, + "fooABAPartitioned=barABASW", + "Only the SameSite=None partitioned cookie set in ABA service worker is available." + ); + + await unregisterSW(ABABc); + BrowserTestUtils.removeTab(tab); + Services.cookies.removeAll(); +}); diff --git a/netwerk/cookie/test/browser/chips.sjs b/netwerk/cookie/test/browser/chips.sjs new file mode 100644 index 0000000000..e60109f6fa --- /dev/null +++ b/netwerk/cookie/test/browser/chips.sjs @@ -0,0 +1,28 @@ +function handleRequest(aRequest, aResponse) { + aResponse.setStatusLine(aRequest.httpVersion, 200); + + var params = new URLSearchParams(aRequest.queryString); + + // Get Cookie header string. + if (params.has("get")) { + if (aRequest.hasHeader("Cookie")) { + let cookie = aRequest.getHeader("Cookie"); + aResponse.write(cookie); + } + return; + } + + // Set a partitioned and a unpartitioned cookie. + if (params.has("set")) { + aResponse.setHeader( + "Set-Cookie", + "cookie=partitioned; Partitioned; SameSite=None; Secure", + true + ); + aResponse.setHeader( + "Set-Cookie", + "cookie=unpartitioned; SameSite=None; Secure", + true + ); + } +} diff --git a/netwerk/cookie/test/browser/cookies.sjs b/netwerk/cookie/test/browser/cookies.sjs new file mode 100644 index 0000000000..9beb861d44 --- /dev/null +++ b/netwerk/cookie/test/browser/cookies.sjs @@ -0,0 +1,17 @@ +function handleRequest(aRequest, aResponse) { + aResponse.setStatusLine(aRequest.httpVersion, 200); + let query = new URLSearchParams(aRequest.queryString); + + if (query.has("Set-Cookie")) { + for (let value of query.getAll("Set-Cookie")) { + aResponse.setHeader("Set-Cookie", value, true); + } + return; + } + + let cookieStr = ""; + if (aRequest.hasHeader("Cookie")) { + cookieStr = aRequest.getHeader("Cookie"); + } + aResponse.write(cookieStr); +} diff --git a/netwerk/cookie/test/browser/serviceWorker.js b/netwerk/cookie/test/browser/serviceWorker.js new file mode 100644 index 0000000000..b2eab40ce4 --- /dev/null +++ b/netwerk/cookie/test/browser/serviceWorker.js @@ -0,0 +1,21 @@ +self.addEventListener("install", function () { + self.skipWaiting(); +}); + +self.addEventListener("activate", function (event) { + event.waitUntil(self.clients.claim()); +}); + +self.addEventListener("message", function (event) { + if (event.data.action === "fetch") { + fetch(event.data.url) + .then(response => response.text()) + .then(data => { + self.clients.matchAll().then(clients => { + clients.forEach(client => { + client.postMessage({ content: data }); + }); + }); + }); + } +}); diff --git a/netwerk/dns/DNS.cpp b/netwerk/dns/DNS.cpp index 91730989db..31d910851b 100644 --- a/netwerk/dns/DNS.cpp +++ b/netwerk/dns/DNS.cpp @@ -21,36 +21,6 @@ namespace mozilla { namespace net { -const char* inet_ntop_internal(int af, const void* src, char* dst, - socklen_t size) { -#ifdef XP_WIN - if (af == AF_INET) { - struct sockaddr_in s; - memset(&s, 0, sizeof(s)); - s.sin_family = AF_INET; - memcpy(&s.sin_addr, src, sizeof(struct in_addr)); - int result = getnameinfo((struct sockaddr*)&s, sizeof(struct sockaddr_in), - dst, size, nullptr, 0, NI_NUMERICHOST); - if (result == 0) { - return dst; - } - } else if (af == AF_INET6) { - struct sockaddr_in6 s; - memset(&s, 0, sizeof(s)); - s.sin6_family = AF_INET6; - memcpy(&s.sin6_addr, src, sizeof(struct in_addr6)); - int result = getnameinfo((struct sockaddr*)&s, sizeof(struct sockaddr_in6), - dst, size, nullptr, 0, NI_NUMERICHOST); - if (result == 0) { - return dst; - } - } - return nullptr; -#else - return inet_ntop(af, src, dst, size); -#endif -} - // Copies the contents of a PRNetAddr to a NetAddr. // Does not do a ptr safety check! void PRNetAddrToNetAddr(const PRNetAddr* prAddr, NetAddr* addr) { @@ -135,7 +105,7 @@ bool NetAddr::ToStringBuffer(char* buf, uint32_t bufSize) const { } struct in_addr nativeAddr = {}; nativeAddr.s_addr = addr->inet.ip; - return !!inet_ntop_internal(AF_INET, &nativeAddr, buf, bufSize); + return !!inet_ntop(AF_INET, &nativeAddr, buf, bufSize); } if (addr->raw.family == AF_INET6) { if (bufSize < INET6_ADDRSTRLEN) { @@ -143,7 +113,7 @@ bool NetAddr::ToStringBuffer(char* buf, uint32_t bufSize) const { } struct in6_addr nativeAddr = {}; memcpy(&nativeAddr.s6_addr, &addr->inet6.ip, sizeof(addr->inet6.ip.u8)); - return !!inet_ntop_internal(AF_INET6, &nativeAddr, buf, bufSize); + return !!inet_ntop(AF_INET6, &nativeAddr, buf, bufSize); } #if defined(XP_UNIX) if (addr->raw.family == AF_LOCAL) { diff --git a/netwerk/dns/PlatformDNSAndroid.cpp b/netwerk/dns/PlatformDNSAndroid.cpp index 171797b938..7875ea7dec 100644 --- a/netwerk/dns/PlatformDNSAndroid.cpp +++ b/netwerk/dns/PlatformDNSAndroid.cpp @@ -5,6 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "GetAddrInfo.h" +#include "mozilla/glean/GleanMetrics.h" #include "mozilla/net/DNSPacket.h" #include "nsIDNSService.h" #include "mozilla/Maybe.h" @@ -71,6 +72,7 @@ nsresult ResolveHTTPSRecordImpl(const nsACString& aHost, uint16_t aFlags, } LOG("resolving %s\n", host.get()); + TimeStamp startTime = TimeStamp::Now(); // Perform the query rv = packet.FillBuffer( [&](unsigned char response[DNSPacket::MAX_SIZE]) -> int { @@ -118,6 +120,8 @@ nsresult ResolveHTTPSRecordImpl(const nsACString& aHost, uint16_t aFlags, return len - 8; }); + mozilla::glean::networking::dns_native_https_call_time.AccumulateRawDuration( + TimeStamp::Now() - startTime); if (NS_FAILED(rv)) { LOG("failed rv"); return rv; diff --git a/netwerk/dns/PlatformDNSUnix.cpp b/netwerk/dns/PlatformDNSUnix.cpp index c7f57fcdda..8a328f3da5 100644 --- a/netwerk/dns/PlatformDNSUnix.cpp +++ b/netwerk/dns/PlatformDNSUnix.cpp @@ -5,6 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "GetAddrInfo.h" +#include "mozilla/glean/GleanMetrics.h" #include "mozilla/net/DNSPacket.h" #include "nsIDNSService.h" #include "mozilla/Maybe.h" @@ -55,6 +56,7 @@ nsresult ResolveHTTPSRecordImpl(const nsACString& aHost, uint16_t aFlags, rv = packet.FillBuffer( [&](unsigned char response[DNSPacket::MAX_SIZE]) -> int { int len = 0; + TimeStamp startTime = TimeStamp::Now(); #if defined(HAVE_RES_NINIT) len = res_nquery(sThreadRes.get(), host.get(), ns_c_in, nsIDNSService::RESOLVE_TYPE_HTTPSSVC, response, @@ -65,6 +67,8 @@ nsresult ResolveHTTPSRecordImpl(const nsACString& aHost, uint16_t aFlags, response, DNSPacket::MAX_SIZE); #endif + mozilla::glean::networking::dns_native_https_call_time + .AccumulateRawDuration(TimeStamp::Now() - startTime); if (len < 0) { LOG("DNS query failed"); } diff --git a/netwerk/dns/PlatformDNSWin.cpp b/netwerk/dns/PlatformDNSWin.cpp index 42f1483ec3..93e936f4dc 100644 --- a/netwerk/dns/PlatformDNSWin.cpp +++ b/netwerk/dns/PlatformDNSWin.cpp @@ -5,6 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "GetAddrInfo.h" +#include "mozilla/glean/GleanMetrics.h" #include "mozilla/net/DNSPacket.h" #include "nsIDNSService.h" #include "mozilla/Maybe.h" @@ -39,9 +40,15 @@ nsresult ResolveHTTPSRecordImpl(const nsACString& aHost, uint16_t aFlags, return NS_ERROR_UNKNOWN_HOST; } + TimeStamp startTime = TimeStamp::Now(); + DNS_STATUS status = DnsQuery_A(host.get(), nsIDNSService::RESOLVE_TYPE_HTTPSSVC, DNS_QUERY_STANDARD, nullptr, &result, nullptr); + + mozilla::glean::networking::dns_native_https_call_time.AccumulateRawDuration( + TimeStamp::Now() - startTime); + if (status != ERROR_SUCCESS) { LOG("DnsQuery_A failed with error: %ld\n", status); return NS_ERROR_UNKNOWN_HOST; diff --git a/netwerk/dns/TRRService.cpp b/netwerk/dns/TRRService.cpp index fbaa67ee14..4599e7b5c0 100644 --- a/netwerk/dns/TRRService.cpp +++ b/netwerk/dns/TRRService.cpp @@ -170,7 +170,7 @@ static void EventTelemetryPrefChanged(const char* aPref, void* aData) { StaticPrefs::network_trr_confirmation_telemetry_enabled()); } -nsresult TRRService::Init() { +nsresult TRRService::Init(bool aNativeHTTPSQueryEnabled) { MOZ_ASSERT(NS_IsMainThread(), "wrong thread"); if (mInitialized) { return NS_OK; @@ -189,6 +189,7 @@ nsresult TRRService::Init() { sTRRServicePtr = this; + mNativeHTTPSQueryEnabled = aNativeHTTPSQueryEnabled; ReadPrefs(nullptr); mConfirmation.HandleEvent(ConfirmationEvent::Init); @@ -1021,7 +1022,9 @@ bool TRRService::IsExcludedFromTRR_unlocked(const nsACString& aHost) { return true; } if (mDNSSuffixDomains.Contains(subdomain)) { - LOG(("Subdomain [%s] of host [%s] Is Excluded From TRR via pref\n", + LOG( + ("Subdomain [%s] of host [%s] Is Excluded From TRR via DNSSuffix " + "domains\n", subdomain.BeginReading(), aHost.BeginReading())); return true; } diff --git a/netwerk/dns/TRRService.h b/netwerk/dns/TRRService.h index 3283a8ea06..4c757bd90f 100644 --- a/netwerk/dns/TRRService.h +++ b/netwerk/dns/TRRService.h @@ -42,7 +42,7 @@ class TRRService : public TRRServiceBase, bool OnWritingThread() const override { return NS_IsMainThread(); } - nsresult Init(); + nsresult Init(bool aNativeHTTPSQueryEnabled); nsresult Start(); bool Enabled(nsIRequest::TRRMode aRequestMode = nsIRequest::TRR_DEFAULT_MODE); bool IsConfirmed() { return mConfirmation.State() == CONFIRM_OK; } diff --git a/netwerk/dns/TRRServiceBase.cpp b/netwerk/dns/TRRServiceBase.cpp index 943edc41dd..31d09f3720 100644 --- a/netwerk/dns/TRRServiceBase.cpp +++ b/netwerk/dns/TRRServiceBase.cpp @@ -163,8 +163,9 @@ void TRRServiceBase::OnTRRModeChange() { } static bool readHosts = false; + // When native HTTPS query is enabled, we need to read etc/hosts. if ((mMode == nsIDNSService::MODE_TRRFIRST || - mMode == nsIDNSService::MODE_TRRONLY) && + mMode == nsIDNSService::MODE_TRRONLY || mNativeHTTPSQueryEnabled) && !readHosts) { readHosts = true; ReadEtcHostsFile(); diff --git a/netwerk/dns/TRRServiceBase.h b/netwerk/dns/TRRServiceBase.h index a7f85fc95d..f79e31cdb3 100644 --- a/netwerk/dns/TRRServiceBase.h +++ b/netwerk/dns/TRRServiceBase.h @@ -82,6 +82,7 @@ class TRRServiceBase : public nsIProxyConfigChangedCallback { Atomic mURISetByDetection{false}; Atomic mTRRConnectionInfoInited{false}; DataMutex> mDefaultTRRConnectionInfo; + bool mNativeHTTPSQueryEnabled{false}; }; } // namespace net diff --git a/netwerk/dns/effective_tld_names.dat b/netwerk/dns/effective_tld_names.dat index fdcaca41d5..d4886d518b 100644 --- a/netwerk/dns/effective_tld_names.dat +++ b/netwerk/dns/effective_tld_names.dat @@ -6710,7 +6710,7 @@ org.zw // newGTLDs -// List of new gTLDs imported from https://www.icann.org/resources/registries/gtlds/v2/gtlds.json on 2024-05-04T15:12:50Z +// List of new gTLDs imported from https://www.icann.org/resources/registries/gtlds/v2/gtlds.json on 2024-05-31T15:16:08Z // This list is auto-generated, don't edit it manually. // aaa : American Automobile Association, Inc. // https://www.iana.org/domains/root/db/aaa.html @@ -8976,7 +8976,7 @@ lotte // https://www.iana.org/domains/root/db/lotto.html lotto -// love : Merchant Law Group LLP +// love : Waterford Limited // https://www.iana.org/domains/root/db/love.html love @@ -11387,9 +11387,9 @@ auth-fips.us-west-2.amazoncognito.com // Amazon EC2 // Submitted by Luke Wells // Reference: 4c38fa71-58ac-4768-99e5-689c1767e537 +*.compute.amazonaws.com.cn *.compute.amazonaws.com *.compute-1.amazonaws.com -*.compute.amazonaws.com.cn us-east-1.amazonaws.com // Amazon EMR @@ -12288,26 +12288,26 @@ ju.mp // CentralNic : http://www.centralnic.com/names/domains // Submitted by registry -ae.org +za.bz br.com cn.com -com.de -com.se de.com eu.com -gb.net -hu.net -jp.net jpn.com mex.com ru.com sa.com -se.net uk.com -uk.net us.com -za.bz za.com +com.de +gb.net +hu.net +jp.net +se.net +uk.net +ae.org +com.se // No longer operated by CentralNic, these entries should be adopted and/or removed by current operators // Submitted by Gavin Brown @@ -12328,8 +12328,8 @@ gr.com // Radix FZC : http://domains.in.net // Submitted by Gavin Brown -in.net web.in +in.net // US REGISTRY LLC : http://us.org // Submitted by Gavin Brown @@ -12367,7 +12367,10 @@ discourse.team // Clever Cloud : https://www.clever-cloud.com/ // Submitted by Quentin Adam +cleverapps.cc +*.services.clever-cloud.com cleverapps.io +cleverapps.tech // Clerk : https://www.clerk.dev // Submitted by Colin Sidoti @@ -12398,8 +12401,8 @@ cloudaccess.net // cloudControl : https://www.cloudcontrol.com/ // Submitted by Tobias Wilken -cloudcontrolled.com cloudcontrolapp.com +cloudcontrolled.com // Cloudera, Inc. : https://www.cloudera.com/ // Submitted by Kedarnath Waikar @@ -12439,11 +12442,11 @@ co.cz // Submitted by Jan Krpes cdn77-storage.com rsc.contentproxy9.cz -cdn77-ssl.net r.cdn77.net -ssl.origin.cdn77-secure.org +cdn77-ssl.net c.cdn77.org rsc.cdn77.org +ssl.origin.cdn77-secure.org // Cloud DNS Ltd : http://www.cloudns.net // Submitted by Aleksander Hristov & Boyan Peychev @@ -12506,8 +12509,8 @@ test.ru // COSIMO GmbH : http://www.cosimo.de // Submitted by Rene Marticke dyn.cosidns.de -dynamisches-dns.de dnsupdater.de +dynamisches-dns.de internet-dns.de l-o-g-i-n.de dynamic-dns.info @@ -12561,9 +12564,9 @@ cyon.site // Danger Science Group: https://dangerscience.com/ // Submitted by Skylar MacDonald +platform0.app fnwk.site folionetwork.site -platform0.app // Daplie, Inc : https://daplie.com // Submitted by AJ ONeal @@ -12693,6 +12696,26 @@ dy.fi tunk.org // DynDNS.com : http://www.dyndns.com/services/dns/dyndns/ +dyndns.biz +for-better.biz +for-more.biz +for-some.biz +for-the.biz +selfip.biz +webhop.biz +ftpaccess.cc +game-server.cc +myphotos.cc +scrapping.cc +blogdns.com +cechire.com +dnsalias.com +dnsdojo.com +doesntexist.com +dontexist.com +doomdns.com +dyn-o-saur.com +dynalias.com dyndns-at-home.com dyndns-at-work.com dyndns-blog.com @@ -12707,64 +12730,14 @@ dyndns-server.com dyndns-web.com dyndns-wiki.com dyndns-work.com -dyndns.biz -dyndns.info -dyndns.org -dyndns.tv -at-band-camp.net -ath.cx -barrel-of-knowledge.info -barrell-of-knowledge.info -better-than.tv -blogdns.com -blogdns.net -blogdns.org -blogsite.org -boldlygoingnowhere.org -broke-it.net -buyshouses.net -cechire.com -dnsalias.com -dnsalias.net -dnsalias.org -dnsdojo.com -dnsdojo.net -dnsdojo.org -does-it.net -doesntexist.com -doesntexist.org -dontexist.com -dontexist.net -dontexist.org -doomdns.com -doomdns.org -dvrdns.org -dyn-o-saur.com -dynalias.com -dynalias.net -dynalias.org -dynathome.net -dyndns.ws -endofinternet.net -endofinternet.org -endoftheinternet.org est-a-la-maison.com est-a-la-masion.com est-le-patron.com est-mon-blogueur.com -for-better.biz -for-more.biz -for-our.info -for-some.biz -for-the.biz -forgot.her.name -forgot.his.name from-ak.com from-al.com from-ar.com -from-az.net from-ca.com -from-co.net from-ct.com from-dc.com from-de.com @@ -12777,10 +12750,8 @@ from-il.com from-in.com from-ks.com from-ky.com -from-la.net from-ma.com from-md.com -from-me.org from-mi.com from-mn.com from-mo.com @@ -12793,7 +12764,6 @@ from-nh.com from-nj.com from-nm.com from-nv.com -from-ny.net from-oh.com from-ok.com from-or.com @@ -12811,45 +12781,18 @@ from-wa.com from-wi.com from-wv.com from-wy.com -ftpaccess.cc -fuettertdasnetz.de -game-host.org -game-server.cc getmyip.com -gets-it.net -go.dyndns.org gotdns.com -gotdns.org -groks-the.info -groks-this.info -ham-radio-op.net -here-for-more.info hobby-site.com -hobby-site.org -home.dyndns.org -homedns.org -homeftp.net -homeftp.org -homeip.net homelinux.com -homelinux.net -homelinux.org homeunix.com -homeunix.net -homeunix.org iamallama.com -in-the-band.net is-a-anarchist.com is-a-blogger.com is-a-bookkeeper.com -is-a-bruinsfan.org is-a-bulls-fan.com -is-a-candidate.org is-a-caterer.com -is-a-celticsfan.org is-a-chef.com -is-a-chef.net -is-a-chef.org is-a-conservative.com is-a-cpa.com is-a-cubicle-slave.com @@ -12858,31 +12801,25 @@ is-a-designer.com is-a-doctor.com is-a-financialadvisor.com is-a-geek.com -is-a-geek.net -is-a-geek.org is-a-green.com is-a-guru.com is-a-hard-worker.com is-a-hunter.com -is-a-knight.org is-a-landscaper.com is-a-lawyer.com is-a-liberal.com is-a-libertarian.com -is-a-linux-user.org is-a-llama.com is-a-musician.com is-a-nascarfan.com is-a-nurse.com is-a-painter.com -is-a-patsfan.org is-a-personaltrainer.com is-a-photographer.com is-a-player.com is-a-republican.com is-a-rockstar.com is-a-socialist.com -is-a-soxfan.org is-a-student.com is-a-teacher.com is-a-techie.com @@ -12894,92 +12831,158 @@ is-an-anarchist.com is-an-artist.com is-an-engineer.com is-an-entertainer.com -is-by.us is-certified.com -is-found.org is-gone.com is-into-anime.com is-into-cars.com is-into-cartoons.com is-into-games.com is-leet.com -is-lost.org is-not-certified.com -is-saved.org is-slick.com is-uberleet.com -is-very-bad.org -is-very-evil.org -is-very-good.org -is-very-nice.org -is-very-sweet.org is-with-theband.com isa-geek.com -isa-geek.net -isa-geek.org isa-hockeynut.com issmarterthanyou.com +likes-pie.com +likescandy.com +neat-url.com +saves-the-whales.com +selfip.com +sells-for-less.com +sells-for-u.com +servebbs.com +simple-url.com +space-to-rent.com +teaches-yoga.com +writesthisblog.com +ath.cx +fuettertdasnetz.de isteingeek.de istmein.de -kicks-ass.net -kicks-ass.org -knowsitall.info -land-4-sale.us lebtimnetz.de leitungsen.de -likes-pie.com -likescandy.com +traeumtgerade.de +barrel-of-knowledge.info +barrell-of-knowledge.info +dyndns.info +for-our.info +groks-the.info +groks-this.info +here-for-more.info +knowsitall.info +selfip.info +webhop.info +forgot.her.name +forgot.his.name +at-band-camp.net +blogdns.net +broke-it.net +buyshouses.net +dnsalias.net +dnsdojo.net +does-it.net +dontexist.net +dynalias.net +dynathome.net +endofinternet.net +from-az.net +from-co.net +from-la.net +from-ny.net +gets-it.net +ham-radio-op.net +homeftp.net +homeip.net +homelinux.net +homeunix.net +in-the-band.net +is-a-chef.net +is-a-geek.net +isa-geek.net +kicks-ass.net +office-on-the.net +podzone.net +scrapper-site.net +selfip.net +sells-it.net +servebbs.net +serveftp.net +thruhere.net +webhop.net merseine.nu mine.nu +shacknet.nu +blogdns.org +blogsite.org +boldlygoingnowhere.org +dnsalias.org +dnsdojo.org +doesntexist.org +dontexist.org +doomdns.org +dvrdns.org +dynalias.org +dyndns.org +go.dyndns.org +home.dyndns.org +endofinternet.org +endoftheinternet.org +from-me.org +game-host.org +gotdns.org +hobby-site.org +homedns.org +homeftp.org +homelinux.org +homeunix.org +is-a-bruinsfan.org +is-a-candidate.org +is-a-celticsfan.org +is-a-chef.org +is-a-geek.org +is-a-knight.org +is-a-linux-user.org +is-a-patsfan.org +is-a-soxfan.org +is-found.org +is-lost.org +is-saved.org +is-very-bad.org +is-very-evil.org +is-very-good.org +is-very-nice.org +is-very-sweet.org +isa-geek.org +kicks-ass.org misconfused.org -mypets.ws -myphotos.cc -neat-url.com -office-on-the.net -on-the-web.tv -podzone.net podzone.org readmyblog.org -saves-the-whales.com -scrapper-site.net -scrapping.cc -selfip.biz -selfip.com -selfip.info -selfip.net selfip.org -sells-for-less.com -sells-for-u.com -sells-it.net sellsyourhome.org -servebbs.com -servebbs.net servebbs.org -serveftp.net serveftp.org servegame.org -shacknet.nu -simple-url.com -space-to-rent.com stuff-4-sale.org -stuff-4-sale.us -teaches-yoga.com -thruhere.net -traeumtgerade.de -webhop.biz -webhop.info -webhop.net webhop.org +better-than.tv +dyndns.tv +on-the-web.tv worse-than.tv -writesthisblog.com +is-by.us +land-4-sale.us +stuff-4-sale.us +dyndns.ws +mypets.ws // ddnss.de : https://www.ddnss.de/ // Submitted by Robert Niedziela ddnss.de dyn.ddnss.de dyndns.ddnss.de -dyndns1.de dyn-ip24.de +dyndns1.de home-webserver.de dyn.home-webserver.de myhome-server.de @@ -12987,8 +12990,8 @@ ddnss.org // Definima : http://www.definima.com/ // Submitted by Maxence Bitterli -definima.net definima.io +definima.net // DigitalOcean App Platform : https://www.digitalocean.com/products/app-platform/ // Submitted by Braxton Huggins @@ -13153,6 +13156,11 @@ us-2.evennode.com us-3.evennode.com us-4.evennode.com +// Expo : https://expo.dev/ +// Submitted by James Ide +expo.app +staging.expo.app + // eDirect Corp. : https://hosting.url.com.tw/ // Submitted by C.S. chang twmail.cc @@ -13251,8 +13259,6 @@ u.channelsdvr.net edgecompute.app fastly-edge.com fastly-terrarium.com -fastlylb.net -map.fastlylb.net freetls.fastly.net map.fastly.net a.prod.fastly.net @@ -13260,6 +13266,8 @@ global.prod.fastly.net a.ssl.fastly.net b.ssl.fastly.net global.ssl.fastly.net +fastlylb.net +map.fastlylb.net // Fastmail : https://www.fastmail.com/ // Submitted by Marc Bradshaw @@ -13329,8 +13337,8 @@ flutterflow.app // fly.io: https://fly.io // Submitted by Kurt Mackey fly.dev -edgeapp.net shw.io +edgeapp.net // Flynn : https://flynn.io // Submitted by Jonathan Rudenberg @@ -13419,6 +13427,8 @@ aliases121.com // GDS : https://www.gov.uk/service-manual/technology/managing-domain-names // Submitted by Stephen Ford +campaign.gov.uk +service.gov.uk independent-commission.uk independent-inquest.uk independent-inquiry.uk @@ -13426,8 +13436,6 @@ independent-panel.uk independent-review.uk public-inquiry.uk royal-commission.uk -campaign.gov.uk -service.gov.uk // CDDO : https://www.gov.uk/guidance/get-an-api-domain-on-govuk // Submitted by Jamie Tanna @@ -13615,71 +13623,64 @@ ro.im goip.de // Google, Inc. -// Submitted by Eduardo Vela -*.run.app -web.app -*.0emm.com -appspot.com -*.r.appspot.com -codespot.com -googleapis.com -googlecode.com -pagespeedmobilizer.com -publishproxy.com -withgoogle.com -withyoutube.com -*.gateway.dev -cloud.goog -translate.goog -*.usercontent.goog -cloudfunctions.net +// Submitted by Shannon McCabe blogspot.ae blogspot.al blogspot.am +*.hosted.app +*.run.app +web.app +blogspot.com.ar +blogspot.co.at +blogspot.com.au blogspot.ba blogspot.be blogspot.bg blogspot.bj +blogspot.com.br +blogspot.com.by blogspot.ca blogspot.cf blogspot.ch blogspot.cl -blogspot.co.at -blogspot.co.id -blogspot.co.il -blogspot.co.ke -blogspot.co.nz -blogspot.co.uk -blogspot.co.za -blogspot.com -blogspot.com.ar -blogspot.com.au -blogspot.com.br -blogspot.com.by blogspot.com.co -blogspot.com.cy -blogspot.com.ee -blogspot.com.eg -blogspot.com.es -blogspot.com.mt -blogspot.com.ng -blogspot.com.tr -blogspot.com.uy +*.0emm.com +appspot.com +*.r.appspot.com +blogspot.com +codespot.com +googleapis.com +googlecode.com +pagespeedmobilizer.com +publishproxy.com +withgoogle.com +withyoutube.com blogspot.cv +blogspot.com.cy blogspot.cz blogspot.de +*.gateway.dev blogspot.dk +blogspot.com.ee +blogspot.com.eg +blogspot.com.es blogspot.fi blogspot.fr +cloud.goog +translate.goog +*.usercontent.goog blogspot.gr blogspot.hk blogspot.hr blogspot.hu +blogspot.co.id blogspot.ie +blogspot.co.il blogspot.in blogspot.is blogspot.it blogspot.jp +blogspot.co.ke blogspot.kr blogspot.li blogspot.lt @@ -13687,10 +13688,14 @@ blogspot.lu blogspot.md blogspot.mk blogspot.mr +blogspot.com.mt blogspot.mx blogspot.my +cloudfunctions.net +blogspot.com.ng blogspot.nl blogspot.no +blogspot.co.nz blogspot.pe blogspot.pt blogspot.qa @@ -13704,9 +13709,13 @@ blogspot.si blogspot.sk blogspot.sn blogspot.td +blogspot.com.tr blogspot.tw blogspot.ug +blogspot.co.uk +blogspot.com.uy blogspot.vn +blogspot.co.za // Goupile : https://goupile.fr // Submitted by Niels Martignene @@ -13739,8 +13748,8 @@ conf.se // Handshake : https://handshake.org // Submitted by Mike Damm -hs.zone hs.run +hs.zone // Hashbang : https://hashbang.sh hashbang.sh @@ -13813,6 +13822,10 @@ ie.ua // HostyHosting (hostyhosting.com) hostyhosting.io +// Hypernode B.V. : https://www.hypernode.com/ +// Submitted by Cipriano Groenendal +hypernode.io + // Häkkinen.fi // Submitted by Eero Häkkinen häkkinen.fi @@ -13833,8 +13846,8 @@ iliadboxos.it // Impertrix Solutions : // Submitted by Zhixiang Zhao -impertrixcdn.com impertrix.com +impertrixcdn.com // Incsub, LLC: https://incsub.com/ // Submitted by Aaron Edwards @@ -13851,10 +13864,10 @@ in-berlin.de in-brb.de in-butter.de in-dsl.de -in-dsl.net -in-dsl.org in-vpn.de +in-dsl.net in-vpn.net +in-dsl.org in-vpn.org // info.at : http://www.info.at/ @@ -14029,13 +14042,13 @@ jotelulu.cloud // JouwWeb B.V. : https://www.jouwweb.nl // Submitted by Camilo Sperberg -jouwweb.site webadorsite.com +jouwweb.site // Joyent : https://www.joyent.com/ // Submitted by Brian Bennett -*.triton.zone *.cns.joyent.com +*.triton.zone // JS.ORG : http://dns.js.org // Submitted by Stefan Keim @@ -14077,8 +14090,8 @@ oya.to // Katholieke Universiteit Leuven: https://www.kuleuven.be // Submitted by Abuse KU Leuven -kuleuven.cloud ezproxy.kuleuven.be +kuleuven.cloud // .KRD : http://nic.krd/data/krd/Registration%20Policy.pdf co.krd @@ -14086,8 +14099,8 @@ edu.krd // Krellian Ltd. : https://krellian.com // Submitted by Ben Francis -krellian.net webthings.io +krellian.net // LCube - Professional hosting e.K. : https://www.lcube-webhosting.de // Submitted by Lars Laehn @@ -14121,8 +14134,8 @@ co.technology // linkyard ldt: https://www.linkyard.ch/ // Submitted by Mario Siegenthaler -linkyard.cloud linkyard-cloud.ch +linkyard.cloud // Linode : https://linode.com // Submitted by @@ -14177,11 +14190,9 @@ lugs.org.uk // Lukanet Ltd : https://lukanet.com // Submitted by Anton Avramov barsy.bg -barsy.co.uk -barsyonline.co.uk +barsy.club barsycenter.com barsyonline.com -barsy.club barsy.de barsy.eu barsy.in @@ -14200,6 +14211,8 @@ barsy.shop barsy.site barsy.support barsy.uk +barsy.co.uk +barsyonline.co.uk // Magento Commerce // Submitted by Damien Tournoud @@ -14230,8 +14243,8 @@ mcpe.me // Submitted by Evgeniy Subbotin mcdir.me mcdir.ru -mcpre.ru vps.mcdir.ru +mcpre.ru // Mediatech : https://mediatech.by // Submitted by Evgeniy Kozhuhovskiy @@ -14280,10 +14293,9 @@ co.pl // Microsoft Azure : https://home.azure *.azurecontainer.io azure-api.net +azure-mobile.net azureedge.net azurefd.net -azurewebsites.net -azure-mobile.net azurestaticapps.net 1.azurestaticapps.net 2.azurestaticapps.net @@ -14297,6 +14309,7 @@ eastasia.azurestaticapps.net eastus2.azurestaticapps.net westeurope.azurestaticapps.net westus2.azurestaticapps.net +azurewebsites.net cloudapp.net trafficmanager.net blob.core.windows.net @@ -14335,8 +14348,8 @@ pp.ru // Mythic Beasts : https://www.mythic-beasts.com // Submitted by Paul Cammish hostedpi.com -customer.mythic-beasts.com caracal.mythic-beasts.com +customer.mythic-beasts.com fentiger.mythic-beasts.com lynx.mythic-beasts.com ocelot.mythic-beasts.com @@ -14418,6 +14431,10 @@ noop.app // Submitted by Laurent Pellegrino noticeable.news +// Notion Labs, Inc : https://www.notion.so/ +// Submitted by Jess Yao +notion.site + // Now-DNS : https://now-dns.com // Submitted by Steve Russell dnsking.ch @@ -14453,91 +14470,91 @@ nerdpol.ovh // No-IP.com : https://noip.com/ // Submitted by Deven Reza +mmafan.biz +myftp.biz +no-ip.biz +no-ip.ca +fantasyleague.cc +gotdns.ch +3utilities.com blogsyte.com -brasilia.me -cable-modem.org ciscofreak.com -collegefan.org -couchpotatofries.org damnserver.com -ddns.me +ddnsking.com ditchyourip.com -dnsfor.me dnsiskinky.com -dvrcam.info dynns.com -eating-organic.net -fantasyleague.cc geekgalaxy.com -golffan.us health-carereform.com homesecuritymac.com homesecuritypc.com -hopto.me -ilovecollege.info -loginto.me -mlbfan.org -mmafan.biz myactivedirectory.com -mydissent.net -myeffect.net -mymediapc.net -mypsx.net mysecuritycamera.com -mysecuritycamera.net -mysecuritycamera.org +myvnc.com net-freaks.com -nflfan.org -nhlfan.net -no-ip.ca -no-ip.co.uk -no-ip.net -noip.us onthewifi.com -pgafan.net point2this.com -pointto.us -privatizehealthinsurance.net quicksytes.com -read-books.org securitytactics.com +servebeer.com +servecounterstrike.com serveexchange.com +serveftp.com +servegame.com +servehalflife.com +servehttp.com servehumour.com +serveirc.com +servemp3.com servep2p.com +servepics.com +servequake.com servesarcasm.com stufftoread.com -ufcfan.org unusualperson.com workisboring.com -3utilities.com -bounceme.net -ddns.net -ddnsking.com -gotdns.ch -hopto.org -myftp.biz -myftp.org -myvnc.com -no-ip.biz +dvrcam.info +ilovecollege.info no-ip.info -no-ip.org +brasilia.me +ddns.me +dnsfor.me +hopto.me +loginto.me noip.me +webhop.me +bounceme.net +ddns.net +eating-organic.net +mydissent.net +myeffect.net +mymediapc.net +mypsx.net +mysecuritycamera.net +nhlfan.net +no-ip.net +pgafan.net +privatizehealthinsurance.net redirectme.net -servebeer.com serveblog.net -servecounterstrike.com -serveftp.com -servegame.com -servehalflife.com -servehttp.com -serveirc.com serveminecraft.net -servemp3.com -servepics.com -servequake.com sytes.net -webhop.me +cable-modem.org +collegefan.org +couchpotatofries.org +hopto.org +mlbfan.org +myftp.org +mysecuritycamera.org +nflfan.org +no-ip.org +read-books.org +ufcfan.org zapto.org +no-ip.co.uk +golffan.us +noip.us +pointto.us // NodeArt : https://nodeart.io // Submitted by Konstantin Nosov @@ -14557,6 +14574,7 @@ prvcy.page // Observable, Inc. : https://observablehq.com // Submitted by Mike Bostock +observablehq.cloud static.observableusercontent.com // Octopodal Solutions, LLC. : https://ulterius.io/ @@ -14577,25 +14595,25 @@ omniwe.site // One.com: https://www.one.com/ // Submitted by Jacob Bunk Nielsen -123hjemmeside.dk -123hjemmeside.no -123homepage.it -123kotisivu.fi -123minsida.se -123miweb.es -123paginaweb.pt -123siteweb.fr 123webseite.at -123webseite.de 123website.be +simplesite.com.br 123website.ch +simplesite.com +123webseite.de +123hjemmeside.dk +123miweb.es +123kotisivu.fi +123siteweb.fr +simplesite.gr +123homepage.it 123website.lu 123website.nl +123hjemmeside.no service.one -simplesite.com -simplesite.com.br -simplesite.gr simplesite.pl +123paginaweb.pt +123minsida.se // One Fold Media : http://www.onefoldmedia.com/ // Submitted by Eddie Jones @@ -14645,8 +14663,8 @@ outsystemscloud.com // OVHcloud: https://ovhcloud.com // Submitted by Vincent Cassé -*.webpaas.ovh.net *.hosting.ovh.net +*.webpaas.ovh.net // OwnProvider GmbH: http://www.ownprovider.com // Submitted by Jan Moennich @@ -14699,8 +14717,8 @@ zakopane.pl // Pantheon Systems, Inc. : https://pantheon.io/ // Submitted by Gary Dylina -pantheonsite.io gotpantheon.com +pantheonsite.io // Peplink | Pepwave : http://peplink.com/ // Submitted by Steve Leung @@ -14736,9 +14754,9 @@ platterp.us // Plesk : https://www.plesk.com/ // Submitted by Anton Akhtyamov +pleskns.com pdns.page plesk.page -pleskns.com // Pley AB : https://www.pley.com/ // Submitted by Henning Pohl @@ -14869,8 +14887,8 @@ g.vbrplsbx.io // Rancher Labs, Inc : https://rancher.com // Submitted by Vincent Fiduccia -*.on-k3s.io *.on-rancher.cloud +*.on-k3s.io *.on-rio.io // Read The Docs, Inc : https://www.readthedocs.org @@ -14883,8 +14901,8 @@ rhcloud.com // Render : https://render.com // Submitted by Anurag Goel -app.render.com onrender.com +app.render.com // Repl.it : https://repl.it // Submitted by Lincoln Bergeson @@ -15029,8 +15047,8 @@ sandcats.io // SBE network solutions GmbH : https://www.sbe.de/ // Submitted by Norman Meilick -logoip.de logoip.com +logoip.de // Scaleway : https://www.scaleway.com/ // Submitted by Rémy Léone @@ -15119,6 +15137,10 @@ biz.ua co.ua pp.ua +// Sheezy.Art : https://sheezy.art +// Submitted by Nyoom +sheezy.games + // Shift Crypto AG : https://shiftcrypto.ch // Submitted by alex shiftcrypto.dev @@ -15223,8 +15245,8 @@ stackit.zone // Staclar : https://staclar.com // Submitted by Q Misell -musician.io // Submitted by Matthias Merkel +musician.io novecore.site // staticland : https://static.land @@ -15336,8 +15358,8 @@ su.paba.se // Symfony, SAS : https://symfony.com/ // Submitted by Fabien Potencier -*.s5y.io *.sensiosite.cloud +*.s5y.io // Syncloud : https://syncloud.org // Submitted by Boris Rybalkin @@ -15359,14 +15381,14 @@ dsmynas.net familyds.net dsmynas.org familyds.org -vpnplus.to direct.quickconnect.to +vpnplus.to // Tabit Technologies Ltd. : https://tabit.cloud/ // Submitted by Oren Agiv -tabitorder.co.il -mytabit.co.il mytabit.com +mytabit.co.il +tabitorder.co.il // TAIFUN Software AG : http://taifun-software.de // Submitted by Bjoern Henke @@ -15407,11 +15429,11 @@ telebit.io reservd.com thingdustdata.com cust.dev.thingdust.io +reservd.dev.thingdust.io cust.disrec.thingdust.io +reservd.disrec.thingdust.io cust.prod.thingdust.io cust.testing.thingdust.io -reservd.dev.thingdust.io -reservd.disrec.thingdust.io reservd.testing.thingdust.io // ticket i/O GmbH : https://ticket.io @@ -15473,8 +15495,6 @@ tuxfamily.org // TwoDNS : https://www.twodns.de/ // Submitted by TwoDNS-Support dd-dns.de -diskstation.eu -diskstation.org dray-dns.de draydns.de dyn-vpn.de @@ -15485,6 +15505,8 @@ my-wan.de syno-ds.de synology-diskstation.de synology-ds.de +diskstation.eu +diskstation.org // Typedream : https://typedream.com // Submitted by Putri Karunia @@ -15496,15 +15518,15 @@ pro.typeform.com // Uberspace : https://uberspace.de // Submitted by Moritz Werner -uber.space *.uberspace.de +uber.space // UDR Limited : http://www.udr.hk.com // Submitted by registry hk.com -hk.org -ltd.hk inc.hk +ltd.hk +hk.org // UK Intis Telecom LTD : https://it.com // Submitted by ITComdomains @@ -15525,8 +15547,8 @@ org.yt // United Gameserver GmbH : https://united-gameserver.de // Submitted by Stefan Schwarz -virtualuser.de virtual-user.de +virtualuser.de // Upli : https://upli.io // Submitted by Lenny Bakkalian @@ -15541,6 +15563,11 @@ dnsupdate.info // Submitted by Ed Moore lib.de.us +// Val Town, Inc : https://val.town/ +// Submitted by Tom MacWright +express.val.run +web.val.run + // VeryPositive SIA : http://very.lv // Submitted by Danko Aleksejevs 2038.io @@ -15586,10 +15613,10 @@ webflowtest.io // WebHotelier Technologies Ltd: https://www.webhotelier.net/ // Submitted by Apostolos Tsakpinis -reserve-online.net -reserve-online.com bookonline.app hotelwithflight.com +reserve-online.com +reserve-online.net // WebWaddle Ltd: https://webwaddle.com/ // Submitted by Merlin Glander @@ -15605,21 +15632,33 @@ wedeploy.sh // Submitted by Jung Jin remotewd.com +// Whatbox Inc. : https://whatbox.ca/ +// Submitted by Anthony Ryan +box.ca + // WIARD Enterprises : https://wiardweb.com // Submitted by Kidd Hustle pages.wiardweb.com // Wikimedia Labs : https://wikitech.wikimedia.org // Submitted by Arturo Borrero Gonzalez -wmflabs.org toolforge.org wmcloud.org +wmflabs.org // WISP : https://wisp.gg // Submitted by Stepan Fedotov panel.gg daemon.panel.gg +// Wix.com, Inc. : https://www.wix.com +// Submitted by Shahar Talmi / Alon Kochba +wixsite.com +wixstudio.com +editorx.io +wixstudio.io +wix.run + // Wizard Zines : https://wizardzines.com // Submitted by Julia Evans messwithdns.com @@ -15645,13 +15684,6 @@ weeklylottery.org.uk wpenginepowered.com js.wpenginepowered.com -// Wix.com, Inc. : https://www.wix.com -// Submitted by Shahar Talmi -wixsite.com -editorx.io -wixstudio.io -wix.run - // XenonCloud GbR: https://xenoncloud.net // Submitted by Julian Uphoff half.host @@ -15707,6 +15739,10 @@ za.org // Submitted by Julian Alker zap.cloud +// Zeabur : https://zeabur.com/ +// Submitted by Zeabur Team +zeabur.app + // Zine EOOD : https://zine.bg/ // Submitted by Martin Angelov bss.design diff --git a/netwerk/dns/nsDNSService2.cpp b/netwerk/dns/nsDNSService2.cpp index 68a76a0c1f..b688daadc9 100644 --- a/netwerk/dns/nsDNSService2.cpp +++ b/netwerk/dns/nsDNSService2.cpp @@ -874,7 +874,7 @@ nsDNSService::Init() { do_GetService("@mozilla.org/network/oblivious-http-service;1")); mTrrService = new TRRService(); - if (NS_FAILED(mTrrService->Init())) { + if (NS_FAILED(mTrrService->Init(mResolver->IsNativeHTTPSEnabled()))) { mTrrService = nullptr; } @@ -975,6 +975,15 @@ nsresult nsDNSService::PreprocessHostname(bool aLocalDomain, return NS_OK; } +bool nsDNSService::IsLocalDomain(const nsACString& aHostname) const { + bool localDomain = mLocalDomains.Contains(aHostname); + if (StringEndsWith(aHostname, "."_ns)) { + localDomain = localDomain || mLocalDomains.Contains(Substring( + aHostname, 0, aHostname.Length() - 1)); + } + return localDomain; +} + nsresult nsDNSService::AsyncResolveInternal( const nsACString& aHostname, uint16_t type, nsIDNSService::DNSFlags flags, nsIDNSAdditionalInfo* aInfo, nsIDNSListener* aListener, @@ -996,7 +1005,8 @@ nsresult nsDNSService::AsyncResolveInternal( res = mResolver; idn = mIDN; - localDomain = mLocalDomains.Contains(aHostname); + + localDomain = IsLocalDomain(aHostname); } if (mNotifyResolution) { @@ -1076,7 +1086,7 @@ nsresult nsDNSService::CancelAsyncResolveInternal( res = mResolver; idn = mIDN; - localDomain = mLocalDomains.Contains(aHostname); + localDomain = IsLocalDomain(aHostname); } if (!res) { return NS_ERROR_OFFLINE; @@ -1212,7 +1222,7 @@ nsresult nsDNSService::ResolveInternal( MutexAutoLock lock(mLock); res = mResolver; idn = mIDN; - localDomain = mLocalDomains.Contains(aHostname); + localDomain = IsLocalDomain(aHostname); } if (mNotifyResolution) { diff --git a/netwerk/dns/nsDNSService2.h b/netwerk/dns/nsDNSService2.h index 11f22c038e..523abcb613 100644 --- a/netwerk/dns/nsDNSService2.h +++ b/netwerk/dns/nsDNSService2.h @@ -83,6 +83,8 @@ class nsDNSService final : public mozilla::net::DNSServiceBase, nsresult PreprocessHostname(bool aLocalDomain, const nsACString& aInput, nsIIDNService* aIDN, nsACString& aACE); + bool IsLocalDomain(const nsACString& aHostname) const; + nsresult AsyncResolveInternal( const nsACString& aHostname, uint16_t type, nsIDNSService::DNSFlags flags, nsIDNSAdditionalInfo* aInfo, nsIDNSListener* aListener, diff --git a/netwerk/dns/nsHostResolver.cpp b/netwerk/dns/nsHostResolver.cpp index b74b974041..4dcdb3ac79 100644 --- a/netwerk/dns/nsHostResolver.cpp +++ b/netwerk/dns/nsHostResolver.cpp @@ -455,7 +455,8 @@ already_AddRefed nsHostResolver::InitLoopbackRecord( return rec.forget(); } -static bool IsNativeHTTPSEnabled() { +// static +bool nsHostResolver::IsNativeHTTPSEnabled() { if (!StaticPrefs::network_dns_native_https_query()) { return false; } @@ -527,6 +528,7 @@ nsresult nsHostResolver::ResolveHost(const nsACString& aHost, bool excludedFromTRR = false; if (TRRService::Get() && TRRService::Get()->IsExcludedFromTRR(host)) { flags |= nsIDNSService::RESOLVE_DISABLE_TRR; + flags |= nsIDNSService::RESOLVE_DISABLE_NATIVE_HTTPS_QUERY; excludedFromTRR = true; if (!aTrrServer.IsEmpty()) { @@ -1182,8 +1184,14 @@ nsresult nsHostResolver::NameLookup(nsHostRecord* rec, (rec->mEffectiveTRRMode == nsIRequest::TRR_FIRST_MODE && (rec->flags & nsIDNSService::RESOLVE_DISABLE_TRR || serviceNotReady || NS_FAILED(rv)))) { - if (!IsNativeHTTPSEnabled() && !rec->IsAddrRecord()) { - return rv; + if (!rec->IsAddrRecord()) { + if (!IsNativeHTTPSEnabled()) { + return NS_ERROR_UNKNOWN_HOST; + } + + if (rec->flags & nsIDNSService::RESOLVE_DISABLE_NATIVE_HTTPS_QUERY) { + return NS_ERROR_UNKNOWN_HOST; + } } #ifdef DEBUG diff --git a/netwerk/dns/nsHostResolver.h b/netwerk/dns/nsHostResolver.h index 02e6a343f8..15ab3a7349 100644 --- a/netwerk/dns/nsHostResolver.h +++ b/netwerk/dns/nsHostResolver.h @@ -339,6 +339,8 @@ class nsHostResolver : public nsISupports, public AHostResolver { * Called by the networking dashboard via the DnsService2 */ void GetDNSCacheEntries(nsTArray*); + + static bool IsNativeHTTPSEnabled(); }; #endif // nsHostResolver_h__ diff --git a/netwerk/dns/nsIDNSByTypeRecord.idl b/netwerk/dns/nsIDNSByTypeRecord.idl index 13290e260e..89829c5c2a 100644 --- a/netwerk/dns/nsIDNSByTypeRecord.idl +++ b/netwerk/dns/nsIDNSByTypeRecord.idl @@ -32,7 +32,7 @@ native TypeResult(mozilla::net::TypeRecordResultType); native MaybePort(mozilla::Maybe); native MaybeAlpnTuple(mozilla::Maybe>); -[scriptable, uuid(5d13241b-9d46-448a-90d8-77c418491026)] +[scriptable, builtinclass, uuid(5d13241b-9d46-448a-90d8-77c418491026)] interface nsIDNSByTypeRecord : nsIDNSRecord { /** @@ -43,10 +43,10 @@ interface nsIDNSByTypeRecord : nsIDNSRecord [noscript] readonly attribute TypeResult results; }; -[scriptable, uuid(2a71750d-cb21-45f1-9e1c-666d18dd7645)] +[scriptable, builtinclass, uuid(2a71750d-cb21-45f1-9e1c-666d18dd7645)] interface nsIDNSTXTRecord : nsISupports { - CStringArrayRef getRecords(); + [noscript] CStringArrayRef getRecords(); /* * Return concatenated strings. diff --git a/netwerk/dns/nsIDNSRecord.idl b/netwerk/dns/nsIDNSRecord.idl index 27df2e28be..ebe8869c60 100644 --- a/netwerk/dns/nsIDNSRecord.idl +++ b/netwerk/dns/nsIDNSRecord.idl @@ -26,12 +26,12 @@ interface nsINetAddr; * like an enumerator, allowing the caller to easily step through the * list of IP addresses. */ -[scriptable, uuid(f92228ae-c417-4188-a604-0830a95e7eb9)] +[scriptable, builtinclass, uuid(f92228ae-c417-4188-a604-0830a95e7eb9)] interface nsIDNSRecord : nsISupports { }; -[scriptable, uuid(cb260e20-943f-4309-953b-78c90d3a7638)] +[scriptable, builtinclass, uuid(cb260e20-943f-4309-953b-78c90d3a7638)] interface nsIDNSAddrRecord : nsIDNSRecord { /** diff --git a/netwerk/dns/nsIDNSService.idl b/netwerk/dns/nsIDNSService.idl index c1aecccd8c..8ceb8f3958 100644 --- a/netwerk/dns/nsIDNSService.idl +++ b/netwerk/dns/nsIDNSService.idl @@ -91,9 +91,11 @@ interface nsIDNSService : nsISupports // If set, the DNS service will pass a DNS record to // OnLookupComplete even when there was a resolution error. RESOLVE_WANT_RECORD_ON_ERROR = (1 << 16), + // If set, the native HTTPS query is not allowed. + RESOLVE_DISABLE_NATIVE_HTTPS_QUERY = (1 << 17), // Bitflag containing all possible flags. - ALL_DNSFLAGS_BITS = ((1 << 17) - 1), + ALL_DNSFLAGS_BITS = ((1 << 18) - 1), }; cenum ConfirmationState : 8 { diff --git a/netwerk/ipc/DocumentLoadListener.h b/netwerk/ipc/DocumentLoadListener.h index a8e4ffa7ab..6f6ad357ae 100644 --- a/netwerk/ipc/DocumentLoadListener.h +++ b/netwerk/ipc/DocumentLoadListener.h @@ -603,7 +603,7 @@ class DocumentLoadListener : public nsIInterfaceRequestor, RefPtr mContentParent; void RejectOpenPromise(nsresult aStatus, nsresult aLoadGroupStatus, - bool aContinueNavigating, const char* aLocation) { + bool aContinueNavigating, StaticString aLocation) { // It is possible for mOpenPromise to not be set if AsyncOpen failed and // the DocumentChannel got canceled. if (!mOpenPromiseResolved && mOpenPromise) { diff --git a/netwerk/locales/en-US/necko.properties b/netwerk/locales/en-US/necko.properties index 7498cca20b..14d16f73a9 100644 --- a/netwerk/locales/en-US/necko.properties +++ b/netwerk/locales/en-US/necko.properties @@ -70,6 +70,8 @@ CookiePathOversize=Cookie “%1$S” is invalid because its path size is too big CookieRejectedByPermissionManager=Cookie “%1$S” has been rejected by user set permissions. # LOCALIZATION NOTE (CookieRejectedInvalidCharName): %1$S is the cookie name. CookieRejectedInvalidCharName=Cookie “%1$S” has been rejected for invalid characters in the name. +# LOCALIZATION NOTE (CookieRejectedInvalidCharAttributes): %1$S is the cookie name. +CookieRejectedInvalidCharAttributes=Cookie “%1$S” has been rejected for invalid characters in the attributes. # LOCALIZATION NOTE (CookieRejectedInvalidDomain): %1$S is the cookie name. CookieRejectedInvalidDomain=Cookie “%1$S” has been rejected for invalid domain. # LOCALIZATION NOTE (CookieRejectedInvalidPrefix): %1$S is the cookie name. diff --git a/netwerk/metrics.yaml b/netwerk/metrics.yaml index 648dced01e..c14b8f2288 100644 --- a/netwerk/metrics.yaml +++ b/netwerk/metrics.yaml @@ -104,14 +104,16 @@ networking: unit: integer bugs: - https://bugzilla.mozilla.org/show_bug.cgi?id=1828942 + - https://bugzilla.mozilla.org/show_bug.cgi?id=1892158 data_reviews: - https://bugzilla.mozilla.org/show_bug.cgi?id=1828942#TDB + - https://bugzilla.mozilla.org/show_bug.cgi?id=1892158 data_sensitivity: - technical notification_emails: - necko@mozilla.com - edgul@mozilla.com - expires: 128 + expires: 134 cookie_count_partitioned: type: custom_distribution @@ -124,14 +126,16 @@ networking: unit: integer bugs: - https://bugzilla.mozilla.org/show_bug.cgi?id=1828942 + - https://bugzilla.mozilla.org/show_bug.cgi?id=1892158 data_reviews: - https://bugzilla.mozilla.org/show_bug.cgi?id=1828942#TDB + - https://bugzilla.mozilla.org/show_bug.cgi?id=1892158 data_sensitivity: - technical notification_emails: - necko@mozilla.com - edgul@mozilla.com - expires: 128 + expires: 134 cookie_count_unpartitioned: type: custom_distribution @@ -144,14 +148,16 @@ networking: unit: integer bugs: - https://bugzilla.mozilla.org/show_bug.cgi?id=1828942 + - https://bugzilla.mozilla.org/show_bug.cgi?id=1892158 data_reviews: - https://bugzilla.mozilla.org/show_bug.cgi?id=1828942#TDB + - https://bugzilla.mozilla.org/show_bug.cgi?id=1892158 data_sensitivity: - technical notification_emails: - necko@mozilla.com - edgul@mozilla.com - expires: 128 + expires: 134 cookie_count_part_by_key: type: custom_distribution @@ -165,14 +171,16 @@ networking: unit: integer bugs: - https://bugzilla.mozilla.org/show_bug.cgi?id=1828942 + - https://bugzilla.mozilla.org/show_bug.cgi?id=1892158 data_reviews: - https://bugzilla.mozilla.org/show_bug.cgi?id=1828942#TDB + - https://bugzilla.mozilla.org/show_bug.cgi?id=1892158 data_sensitivity: - technical notification_emails: - necko@mozilla.com - edgul@mozilla.com - expires: 128 + expires: 134 cookie_count_unpart_by_key: type: custom_distribution @@ -186,14 +194,16 @@ networking: unit: integer bugs: - https://bugzilla.mozilla.org/show_bug.cgi?id=1828942 + - https://bugzilla.mozilla.org/show_bug.cgi?id=1892158 data_reviews: - https://bugzilla.mozilla.org/show_bug.cgi?id=1828942#TDB + - https://bugzilla.mozilla.org/show_bug.cgi?id=1892158 data_sensitivity: - technical notification_emails: - necko@mozilla.com - edgul@mozilla.com - expires: 128 + expires: 134 cookie_purge_max: type: custom_distribution @@ -208,14 +218,16 @@ networking: unit: integer bugs: - https://bugzilla.mozilla.org/show_bug.cgi?id=1828942 + - https://bugzilla.mozilla.org/show_bug.cgi?id=1892158 data_reviews: - https://bugzilla.mozilla.org/show_bug.cgi?id=1828942#TDB + - https://bugzilla.mozilla.org/show_bug.cgi?id=1892158 data_sensitivity: - technical notification_emails: - necko@mozilla.com - edgul@mozilla.com - expires: 128 + expires: 134 cookie_purge_entry_max: type: custom_distribution @@ -230,14 +242,16 @@ networking: unit: integer bugs: - https://bugzilla.mozilla.org/show_bug.cgi?id=1828942 + - https://bugzilla.mozilla.org/show_bug.cgi?id=1892158 data_reviews: - https://bugzilla.mozilla.org/show_bug.cgi?id=1828942#TDB + - https://bugzilla.mozilla.org/show_bug.cgi?id=1892158 data_sensitivity: - technical notification_emails: - necko@mozilla.com - edgul@mozilla.com - expires: 128 + expires: 134 set_cookie: type: counter @@ -368,6 +382,22 @@ networking: expires: never telemetry_mirror: DNS_FAILED_LOOKUP_TIME + dns_native_https_call_time: + type: timing_distribution + time_unit: millisecond + description: > + Time spent in the call to the native OS DNS resolution. + bugs: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1885529 + data_reviews: + - https://bugzilla.mozilla.org/show_bug.cgi?id=1885529 + data_sensitivity: + - technical + notification_emails: + - necko@mozilla.com + - vgosu@mozilla.com + expires: never + dns_native_count: type: labeled_counter description: > diff --git a/netwerk/protocol/about/nsAboutProtocolHandler.cpp b/netwerk/protocol/about/nsAboutProtocolHandler.cpp index 5bbaa9010e..ab4d62f989 100644 --- a/netwerk/protocol/about/nsAboutProtocolHandler.cpp +++ b/netwerk/protocol/about/nsAboutProtocolHandler.cpp @@ -21,6 +21,7 @@ #include "nsIChannel.h" #include "nsIScriptError.h" #include "nsIClassInfoImpl.h" +#include "DefaultURI.h" #include "mozilla/ipc/URIUtils.h" @@ -136,6 +137,16 @@ nsresult nsAboutProtocolHandler::CreateNewURI(const nsACString& aSpec, NS_ENSURE_SUCCESS(rv, rv); } + // use DefaultURI to check for validity when we have possible hostnames + // since nsSimpleURI doesn't know about hostnames + auto pos = aSpec.Find("about:/"); + if (pos != kNotFound) { + rv = NS_MutateURI(new mozilla::net::DefaultURI::Mutator()) + .SetSpec(aSpec) + .Finalize(url); + NS_ENSURE_SUCCESS(rv, rv); + } + url.swap(*aResult); return NS_OK; } diff --git a/netwerk/protocol/data/nsDataHandler.cpp b/netwerk/protocol/data/nsDataHandler.cpp index adb86bf484..e9a72cd9ed 100644 --- a/netwerk/protocol/data/nsDataHandler.cpp +++ b/netwerk/protocol/data/nsDataHandler.cpp @@ -68,16 +68,12 @@ nsDataHandler::GetScheme(nsACString& result) { // use DefaultURI to check for validity when we have possible hostnames // since nsSimpleURI doesn't know about hostnames - auto pos = aSpec.Find("data:"); + auto pos = aSpec.Find("data:/"); if (pos != kNotFound) { - nsDependentCSubstring rest(aSpec, pos + sizeof("data:") - 1, -1); - if (StringBeginsWith(rest, "//"_ns)) { - nsCOMPtr uriWithHost; - rv = NS_MutateURI(new mozilla::net::DefaultURI::Mutator()) - .SetSpec(aSpec) - .Finalize(uriWithHost); - NS_ENSURE_SUCCESS(rv, rv); - } + rv = NS_MutateURI(new mozilla::net::DefaultURI::Mutator()) + .SetSpec(aSpec) + .Finalize(uri); + NS_ENSURE_SUCCESS(rv, rv); } uri.forget(result); diff --git a/netwerk/protocol/http/ConnectionEntry.cpp b/netwerk/protocol/http/ConnectionEntry.cpp index dc0dfd103c..36ebba5ec3 100644 --- a/netwerk/protocol/http/ConnectionEntry.cpp +++ b/netwerk/protocol/http/ConnectionEntry.cpp @@ -509,6 +509,9 @@ void ConnectionEntry::VerifyTraffic() { mActiveConns.RemoveElementAt(index); gHttpHandler->ConnMgr()->DecrementActiveConnCount(conn); mPendingConns.AppendElement(conn); + // After DontReuse(), the connection will be closed after the last + // transition is done. + conn->DontReuse(); LOG(("Move active connection to pending list [conn=%p]\n", conn.get())); } diff --git a/netwerk/protocol/http/Http3WebTransportSession.cpp b/netwerk/protocol/http/Http3WebTransportSession.cpp index 9ef4da70c0..cee1fd75fd 100644 --- a/netwerk/protocol/http/Http3WebTransportSession.cpp +++ b/netwerk/protocol/http/Http3WebTransportSession.cpp @@ -447,7 +447,10 @@ Http3WebTransportSession::OnIncomingWebTransportStream( return nullptr; } - mListener->OnIncomingStreamAvailableInternal(stream); + if (nsCOMPtr listener = + do_QueryInterface(mListener)) { + listener->OnIncomingStreamAvailableInternal(stream); + } return stream.forget(); } @@ -470,7 +473,10 @@ void Http3WebTransportSession::OnDatagramReceived(nsTArray&& aData) { return; } - mListener->OnDatagramReceivedInternal(std::move(aData)); + if (nsCOMPtr listener = + do_QueryInterface(mListener)) { + listener->OnDatagramReceivedInternal(std::move(aData)); + } } void Http3WebTransportSession::GetMaxDatagramSize() { diff --git a/netwerk/protocol/http/HttpBaseChannel.cpp b/netwerk/protocol/http/HttpBaseChannel.cpp index ff88b02753..8483bb6144 100644 --- a/netwerk/protocol/http/HttpBaseChannel.cpp +++ b/netwerk/protocol/http/HttpBaseChannel.cpp @@ -1319,8 +1319,6 @@ void HttpBaseChannel::ExplicitSetUploadStreamLength( return; } - // SetRequestHeader propagates headers to chrome if HttpChannelChild - MOZ_ASSERT(!LoadWasOpened()); nsAutoCString contentLengthStr; contentLengthStr.AppendInt(aContentLength); SetRequestHeader(header, contentLengthStr, false); @@ -2297,6 +2295,19 @@ HttpBaseChannel::UpgradeToSecure() { return NS_OK; } +NS_IMETHODIMP +HttpBaseChannel::GetRequestObserversCalled(bool* aCalled) { + NS_ENSURE_ARG_POINTER(aCalled); + *aCalled = LoadRequestObserversCalled(); + return NS_OK; +} + +NS_IMETHODIMP +HttpBaseChannel::SetRequestObserversCalled(bool aCalled) { + StoreRequestObserversCalled(aCalled); + return NS_OK; +} + NS_IMETHODIMP HttpBaseChannel::GetRequestContextID(uint64_t* aRCID) { NS_ENSURE_ARG_POINTER(aRCID); diff --git a/netwerk/protocol/http/HttpBaseChannel.h b/netwerk/protocol/http/HttpBaseChannel.h index d6d693212b..3a2c6192d1 100644 --- a/netwerk/protocol/http/HttpBaseChannel.h +++ b/netwerk/protocol/http/HttpBaseChannel.h @@ -228,6 +228,8 @@ class HttpBaseChannel : public nsHashPropertyBag, NS_IMETHOD GetRequestSucceeded(bool* aValue) override; NS_IMETHOD RedirectTo(nsIURI* newURI) override; NS_IMETHOD UpgradeToSecure() override; + NS_IMETHOD GetRequestObserversCalled(bool* aCalled) override; + NS_IMETHOD SetRequestObserversCalled(bool aCalled) override; NS_IMETHOD GetRequestContextID(uint64_t* aRCID) override; NS_IMETHOD GetTransferSize(uint64_t* aTransferSize) override; NS_IMETHOD GetRequestSize(uint64_t* aRequestSize) override; @@ -690,6 +692,9 @@ class HttpBaseChannel : public nsHashPropertyBag, void SetChannelBlockedByOpaqueResponse(); bool Http3Allowed() const; + virtual void ExplicitSetUploadStreamLength(uint64_t aContentLength, + bool aSetContentLengthHeader); + friend class OpaqueResponseBlocker; friend class PrivateBrowsingChannel; friend class InterceptFailedOnStop; @@ -721,9 +726,6 @@ class HttpBaseChannel : public nsHashPropertyBag, // Proxy release all members above on main thread. void ReleaseMainThreadOnlyReferences(); - void ExplicitSetUploadStreamLength(uint64_t aContentLength, - bool aSetContentLengthHeader); - void MaybeResumeAsyncOpen(); protected: diff --git a/netwerk/protocol/http/HttpChannelChild.cpp b/netwerk/protocol/http/HttpChannelChild.cpp index 8b581b070e..97c80b046a 100644 --- a/netwerk/protocol/http/HttpChannelChild.cpp +++ b/netwerk/protocol/http/HttpChannelChild.cpp @@ -3418,4 +3418,12 @@ NS_IMETHODIMP HttpChannelChild::SetWebTransportSessionEventListener( return NS_OK; } +void HttpChannelChild::ExplicitSetUploadStreamLength( + uint64_t aContentLength, bool aSetContentLengthHeader) { + // SetRequestHeader propagates headers to chrome if HttpChannelChild + MOZ_ASSERT(!LoadWasOpened()); + HttpBaseChannel::ExplicitSetUploadStreamLength(aContentLength, + aSetContentLengthHeader); +} + } // namespace mozilla::net diff --git a/netwerk/protocol/http/HttpChannelChild.h b/netwerk/protocol/http/HttpChannelChild.h index 8cfc820a23..190b195aaa 100644 --- a/netwerk/protocol/http/HttpChannelChild.h +++ b/netwerk/protocol/http/HttpChannelChild.h @@ -187,6 +187,9 @@ class HttpChannelChild final : public PHttpChannelChild, const nsAString& aURL, const nsAString& aContentType) override; + virtual void ExplicitSetUploadStreamLength( + uint64_t aContentLength, bool aSetContentLengthHeader) override; + private: // We want to handle failure result of AsyncOpen, hence AsyncOpen calls the // Internal method diff --git a/netwerk/protocol/http/NullHttpChannel.cpp b/netwerk/protocol/http/NullHttpChannel.cpp index eae11441af..8916c8a48e 100644 --- a/netwerk/protocol/http/NullHttpChannel.cpp +++ b/netwerk/protocol/http/NullHttpChannel.cpp @@ -246,6 +246,16 @@ NullHttpChannel::RedirectTo(nsIURI* aNewURI) { NS_IMETHODIMP NullHttpChannel::UpgradeToSecure() { return NS_ERROR_NOT_IMPLEMENTED; } +NS_IMETHODIMP +NullHttpChannel::GetRequestObserversCalled(bool* aCalled) { + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +NullHttpChannel::SetRequestObserversCalled(bool aCalled) { + return NS_ERROR_NOT_IMPLEMENTED; +} + NS_IMETHODIMP NullHttpChannel::GetRequestContextID(uint64_t* _retval) { return NS_ERROR_NOT_IMPLEMENTED; diff --git a/netwerk/protocol/http/ObliviousHttpChannel.cpp b/netwerk/protocol/http/ObliviousHttpChannel.cpp index 1b4449f004..99b1def6d5 100644 --- a/netwerk/protocol/http/ObliviousHttpChannel.cpp +++ b/netwerk/protocol/http/ObliviousHttpChannel.cpp @@ -350,6 +350,16 @@ ObliviousHttpChannel::UpgradeToSecure() { return NS_ERROR_NOT_IMPLEMENTED; } +NS_IMETHODIMP +ObliviousHttpChannel::GetRequestObserversCalled(bool* aCalled) { + return mInnerChannel->GetRequestObserversCalled(aCalled); +} + +NS_IMETHODIMP +ObliviousHttpChannel::SetRequestObserversCalled(bool aCalled) { + return mInnerChannel->SetRequestObserversCalled(aCalled); +} + NS_IMETHODIMP ObliviousHttpChannel::GetRequestContextID(uint64_t* _retval) { return mInnerChannel->GetRequestContextID(_retval); diff --git a/netwerk/protocol/http/nsHttpChannel.cpp b/netwerk/protocol/http/nsHttpChannel.cpp index 4428b72426..c5e6deb241 100644 --- a/netwerk/protocol/http/nsHttpChannel.cpp +++ b/netwerk/protocol/http/nsHttpChannel.cpp @@ -637,6 +637,32 @@ nsresult nsHttpChannel::OnBeforeConnect() { return MaybeUseHTTPSRRForUpgrade(shouldUpgrade, NS_OK); } +// Returns true if the network connectivity checker indicated +// that HTTPS records can be resolved on this network - false otherwise. +// When TRR is enabled, we always return true, as resolving HTTPS +// records don't depend on the network. +static bool canUseHTTPSRRonNetwork() { + if (nsCOMPtr dns = mozilla::components::DNS::Service()) { + nsIDNSService::ResolverMode mode; + // If the browser is currently using TRR/DoH, then it can + // definitely resolve HTTPS records. + if (NS_SUCCEEDED(dns->GetCurrentTrrMode(&mode)) && + (mode == nsIDNSService::MODE_TRRFIRST || + mode == nsIDNSService::MODE_TRRONLY)) { + return true; + } + } + if (RefPtr ncs = + NetworkConnectivityService::GetSingleton()) { + nsINetworkConnectivityService::ConnectivityState state; + if (NS_SUCCEEDED(ncs->GetDNS_HTTPS(&state)) && + state == nsINetworkConnectivityService::NOT_AVAILABLE) { + return false; + } + } + return true; +} + nsresult nsHttpChannel::MaybeUseHTTPSRRForUpgrade(bool aShouldUpgrade, nsresult aStatus) { if (NS_FAILED(aStatus)) { @@ -657,6 +683,13 @@ nsresult nsHttpChannel::MaybeUseHTTPSRRForUpgrade(bool aShouldUpgrade, return true; } + // If the network connectivity checker indicates the network is + // blocking HTTPS requests, then we should skip them so we don't + // needlessly wait for a timeout. + if (!canUseHTTPSRRonNetwork()) { + return true; + } + nsAutoCString uriHost; mURI->GetAsciiHost(uriHost); @@ -1022,11 +1055,22 @@ nsresult nsHttpChannel::DoConnectActual( LOG(("nsHttpChannel::DoConnectActual [this=%p, aTransWithStickyConn=%p]\n", this, aTransWithStickyConn)); - nsresult rv = SetupTransaction(); + nsresult rv = SetupChannelForTransaction(); if (NS_FAILED(rv)) { return rv; } + return DispatchTransaction(aTransWithStickyConn); +} + +nsresult nsHttpChannel::DispatchTransaction( + HttpTransactionShell* aTransWithStickyConn) { + LOG(("nsHttpChannel::DispatchTransaction [this=%p, aTransWithStickyConn=%p]", + this, aTransWithStickyConn)); + nsresult rv = InitTransaction(); + if (NS_FAILED(rv)) { + return rv; + } if (aTransWithStickyConn) { rv = gHttpHandler->InitiateTransactionWithStickyConn( mTransaction, mPriority, aTransWithStickyConn); @@ -1212,10 +1256,11 @@ void nsHttpChannel::HandleAsyncNotModified() { if (mLoadGroup) mLoadGroup->RemoveRequest(this, nullptr, mStatus); } -nsresult nsHttpChannel::SetupTransaction() { - LOG(("nsHttpChannel::SetupTransaction [this=%p, cos=%lu, inc=%d prio=%d]\n", - this, mClassOfService.Flags(), mClassOfService.Incremental(), - mPriority)); +nsresult nsHttpChannel::SetupChannelForTransaction() { + LOG(( + "nsHttpChannel::SetupChannelForTransaction [this=%p, cos=%lu, inc=%d " + "prio=%d]\n", + this, mClassOfService.Flags(), mClassOfService.Incremental(), mPriority)); NS_ENSURE_TRUE(!mTransaction, NS_ERROR_ALREADY_INITIALIZED); @@ -1340,24 +1385,37 @@ nsresult nsHttpChannel::SetupTransaction() { // We need to send 'Pragma:no-cache' to inhibit proxy caching even if // no proxy is configured since we might be talking with a transparent // proxy, i.e. one that operates at the network level. See bug #14772. - rv = mRequestHead.SetHeaderOnce(nsHttp::Pragma, "no-cache", true); - MOZ_ASSERT(NS_SUCCEEDED(rv)); + // But we should not touch Pragma if Cache-Control is already set + // (https://fetch.spec.whatwg.org/#ref-for-concept-request-cache-mode%E2%91%A3) + if (!mRequestHead.HasHeader(nsHttp::Pragma)) { + rv = mRequestHead.SetHeaderOnce(nsHttp::Pragma, "no-cache", true); + MOZ_ASSERT(NS_SUCCEEDED(rv)); + } // If we're configured to speak HTTP/1.1 then also send 'Cache-control: - // no-cache' - if (mRequestHead.Version() >= HttpVersion::v1_1) { + // no-cache'. But likewise don't touch Cache-Control if it's already set. + if (mRequestHead.Version() >= HttpVersion::v1_1 && + !mRequestHead.HasHeader(nsHttp::Cache_Control)) { rv = mRequestHead.SetHeaderOnce(nsHttp::Cache_Control, "no-cache", true); MOZ_ASSERT(NS_SUCCEEDED(rv)); } - } else if ((mLoadFlags & VALIDATE_ALWAYS) && !LoadCacheEntryIsWriteOnly()) { + } else if (mLoadFlags & VALIDATE_ALWAYS) { // We need to send 'Cache-Control: max-age=0' to force each cache along // the path to the origin server to revalidate its own entry, if any, // with the next cache or server. See bug #84847. // // If we're configured to speak HTTP/1.0 then just send 'Pragma: no-cache' + // + // But don't send the headers if they're already set: + // https://fetch.spec.whatwg.org/#ref-for-concept-request-cache-mode%E2%91%A2 if (mRequestHead.Version() >= HttpVersion::v1_1) { - rv = mRequestHead.SetHeaderOnce(nsHttp::Cache_Control, "max-age=0", true); + if (!mRequestHead.HasHeader(nsHttp::Cache_Control)) { + rv = mRequestHead.SetHeaderOnce(nsHttp::Cache_Control, "max-age=0", + true); + } } else { - rv = mRequestHead.SetHeaderOnce(nsHttp::Pragma, "no-cache", true); + if (!mRequestHead.HasHeader(nsHttp::Pragma)) { + rv = mRequestHead.SetHeaderOnce(nsHttp::Pragma, "no-cache", true); + } } MOZ_ASSERT(NS_SUCCEEDED(rv)); } @@ -1395,6 +1453,30 @@ nsresult nsHttpChannel::SetupTransaction() { } } + // See bug #466080. Transfer LOAD_ANONYMOUS flag to socket-layer. + if (mLoadFlags & LOAD_ANONYMOUS) mCaps |= NS_HTTP_LOAD_ANONYMOUS; + + if (LoadTimingEnabled()) mCaps |= NS_HTTP_TIMING_ENABLED; + + if (mUpgradeProtocolCallback) { + rv = mRequestHead.SetHeader(nsHttp::Upgrade, mUpgradeProtocol, false); + MOZ_ASSERT(NS_SUCCEEDED(rv)); + rv = mRequestHead.SetHeaderOnce(nsHttp::Connection, nsHttp::Upgrade.get(), + true); + MOZ_ASSERT(NS_SUCCEEDED(rv)); + mCaps |= NS_HTTP_STICKY_CONNECTION; + mCaps &= ~NS_HTTP_ALLOW_KEEPALIVE; + } + + if (mWebTransportSessionEventListener) { + mCaps |= NS_HTTP_STICKY_CONNECTION; + } + + return NS_OK; +} + +nsresult nsHttpChannel::InitTransaction() { + nsresult rv; // create wrapper for this channel's notification callbacks nsCOMPtr callbacks; NS_NewNotificationCallbacksAggregation(mCallbacks, mLoadGroup, @@ -1445,25 +1527,6 @@ nsresult nsHttpChannel::SetupTransaction() { // nsIHttpActivityObserver. gHttpHandler->AddHttpChannel(mChannelId, ToSupports(this)); - // See bug #466080. Transfer LOAD_ANONYMOUS flag to socket-layer. - if (mLoadFlags & LOAD_ANONYMOUS) mCaps |= NS_HTTP_LOAD_ANONYMOUS; - - if (LoadTimingEnabled()) mCaps |= NS_HTTP_TIMING_ENABLED; - - if (mUpgradeProtocolCallback) { - rv = mRequestHead.SetHeader(nsHttp::Upgrade, mUpgradeProtocol, false); - MOZ_ASSERT(NS_SUCCEEDED(rv)); - rv = mRequestHead.SetHeaderOnce(nsHttp::Connection, nsHttp::Upgrade.get(), - true); - MOZ_ASSERT(NS_SUCCEEDED(rv)); - mCaps |= NS_HTTP_STICKY_CONNECTION; - mCaps &= ~NS_HTTP_ALLOW_KEEPALIVE; - } - - if (mWebTransportSessionEventListener) { - mCaps |= NS_HTTP_STICKY_CONNECTION; - } - nsCOMPtr pushListener; NS_QueryNotificationCallbacks(mCallbacks, mLoadGroup, NS_GET_IID(nsIHttpPushListener), @@ -6566,7 +6629,7 @@ nsresult nsHttpChannel::BeginConnect() { !(mLoadInfo->TriggeringPrincipal()->IsSystemPrincipal() && mLoadInfo->GetExternalContentPolicyType() != ExtContentPolicy::TYPE_DOCUMENT) && - !mConnectionInfo->UsingConnect(); + !mConnectionInfo->UsingConnect() && canUseHTTPSRRonNetwork(); if (!httpsRRAllowed) { mCaps |= NS_HTTP_DISALLOW_HTTPS_RR; } @@ -6611,8 +6674,10 @@ nsresult nsHttpChannel::BeginConnect() { Unused << gHttpHandler->AddConnectionHeader(&mRequestHead, mCaps); if (!LoadIsTRRServiceChannel() && - (mLoadFlags & VALIDATE_ALWAYS || - BYPASS_LOCAL_CACHE(mLoadFlags, LoadPreferCacheLoadOverBypass()))) { + ((mLoadFlags & LOAD_FRESH_CONNECTION) || + (!StaticPrefs::network_dns_only_refresh_on_fresh_connection() && + (mLoadFlags & VALIDATE_ALWAYS || + BYPASS_LOCAL_CACHE(mLoadFlags, LoadPreferCacheLoadOverBypass()))))) { mCaps |= NS_HTTP_REFRESH_DNS; } @@ -6753,7 +6818,7 @@ nsresult nsHttpChannel::MaybeStartDNSPrefetch() { } if (gHttpHandler->UseHTTPSRRAsAltSvcEnabled() && !mHTTPSSVCRecord && - !(mCaps & NS_HTTP_DISALLOW_HTTPS_RR)) { + !(mCaps & NS_HTTP_DISALLOW_HTTPS_RR) && canUseHTTPSRRonNetwork()) { MOZ_ASSERT(!mHTTPSSVCRecord); OriginAttributes originAttributes; diff --git a/netwerk/protocol/http/nsHttpChannel.h b/netwerk/protocol/http/nsHttpChannel.h index e2fb5abde9..78dac11a4d 100644 --- a/netwerk/protocol/http/nsHttpChannel.h +++ b/netwerk/protocol/http/nsHttpChannel.h @@ -335,7 +335,10 @@ class nsHttpChannel final : public HttpBaseChannel, void OnHTTPSRRAvailable(nsIDNSHTTPSSVCRecord* aRecord); [[nodiscard]] nsresult Connect(); void SpeculativeConnect(); - [[nodiscard]] nsresult SetupTransaction(); + [[nodiscard]] nsresult SetupChannelForTransaction(); + [[nodiscard]] nsresult InitTransaction(); + [[nodiscard]] nsresult DispatchTransaction( + HttpTransactionShell* aTransWithStickyConn); [[nodiscard]] nsresult CallOnStartRequest(); [[nodiscard]] nsresult ProcessResponse(); void AsyncContinueProcessResponse(); diff --git a/netwerk/protocol/http/nsHttpConnection.cpp b/netwerk/protocol/http/nsHttpConnection.cpp index cd41cd65bc..54e1d25557 100644 --- a/netwerk/protocol/http/nsHttpConnection.cpp +++ b/netwerk/protocol/http/nsHttpConnection.cpp @@ -895,14 +895,6 @@ bool nsHttpConnection::IsAlive() { nsresult rv = mSocketTransport->IsAlive(&alive); if (NS_FAILED(rv)) alive = false; -// #define TEST_RESTART_LOGIC -#ifdef TEST_RESTART_LOGIC - if (!alive) { - LOG(("pretending socket is still alive to test restart logic\n")); - alive = true; - } -#endif - return alive; } diff --git a/netwerk/protocol/http/nsHttpConnectionMgr.cpp b/netwerk/protocol/http/nsHttpConnectionMgr.cpp index dbbd8fe0ca..28e2f3f07c 100644 --- a/netwerk/protocol/http/nsHttpConnectionMgr.cpp +++ b/netwerk/protocol/http/nsHttpConnectionMgr.cpp @@ -62,16 +62,23 @@ struct UrlMarker { } static void StreamJSONMarkerData( mozilla::baseprofiler::SpliceableJSONWriter& aWriter, - const mozilla::ProfilerString8View& aURL) { + const mozilla::ProfilerString8View& aURL, const TimeDuration& aDuration, + uint64_t aChannelId) { if (aURL.Length() != 0) { aWriter.StringProperty("url", aURL); } + if (!aDuration.IsZero()) { + aWriter.DoubleProperty("duration", aDuration.ToMilliseconds()); + } + aWriter.IntProperty("channelId", static_cast(aChannelId)); } static MarkerSchema MarkerTypeDisplay() { using MS = MarkerSchema; MS schema(MS::Location::MarkerChart, MS::Location::MarkerTable); schema.SetTableLabel("{marker.name} - {marker.data.url}"); - schema.AddKeyFormat("url", MS::Format::Url); + schema.AddKeyFormatSearchable("url", MS::Format::Url, + MS::Searchable::Searchable); + schema.AddKeyLabelFormat("duration", "Duration", MS::Format::Duration); return schema; } }; @@ -505,9 +512,10 @@ nsresult nsHttpConnectionMgr::SpeculativeConnect( return NS_OK; } - nsCString url = ci->EndToEndSSL() ? "https://"_ns : "http://"_ns; + nsAutoCString url(ci->EndToEndSSL() ? "https://"_ns : "http://"_ns); url += ci->GetOrigin(); - PROFILER_MARKER("SpeculativeConnect", NETWORK, {}, UrlMarker, url); + PROFILER_MARKER("SpeculativeConnect", NETWORK, {}, UrlMarker, url, + TimeDuration::Zero(), 0); RefPtr args = new SpeculativeConnectArgs(); @@ -800,6 +808,11 @@ HttpConnectionBase* nsHttpConnectionMgr::FindCoalescableConnection( MOZ_ASSERT(ent->mConnInfo); nsHttpConnectionInfo* ci = ent->mConnInfo; LOG(("FindCoalescableConnection %s\n", ci->HashKey().get())); + + if (ci->GetWebTransport()) { + LOG(("Don't coalesce a WebTransport conn ")); + return nullptr; + } // First try and look it up by origin frame nsCString newKey; BuildOriginFrameHashKey(newKey, ci, ci->GetOrigin(), ci->OriginPort()); @@ -1649,9 +1662,9 @@ nsresult nsHttpConnectionMgr::DispatchTransaction(ConnectionEntry* ent, trans->CancelPacing(NS_OK); TimeStamp now = TimeStamp::Now(); + TimeDuration elapsed = now - trans->GetPendingTime(); auto recordPendingTimeForHTTPSRR = [&](nsCString& aKey) { uint32_t stage = trans->HTTPSSVCReceivedStage(); - TimeDuration elapsed = now - trans->GetPendingTime(); if (HTTPS_RR_IS_USED(stage)) { glean::networking::transaction_wait_time_https_rr.AccumulateRawDuration( elapsed); @@ -1659,10 +1672,16 @@ nsresult nsHttpConnectionMgr::DispatchTransaction(ConnectionEntry* ent, } else { glean::networking::transaction_wait_time.AccumulateRawDuration(elapsed); } - PerfStats::RecordMeasurement(PerfStats::Metric::HttpTransactionWaitTime, - elapsed); }; + PerfStats::RecordMeasurement(PerfStats::Metric::HttpTransactionWaitTime, + elapsed); + + PROFILER_MARKER( + "DispatchTransaction", NETWORK, + MarkerOptions(MarkerTiming::Interval(trans->GetPendingTime(), now)), + UrlMarker, trans->GetUrl(), elapsed, trans->ChannelId()); + nsAutoCString httpVersionkey("h1"_ns); if (conn->UsingSpdy() || conn->UsingHttp3()) { LOG( @@ -1770,6 +1789,9 @@ nsresult nsHttpConnectionMgr::ProcessNewTransaction(nsHttpTransaction* trans) { trans->SetPendingTime(); + PROFILER_MARKER("ProcessNewTransaction", NETWORK, {}, UrlMarker, + trans->GetUrl(), TimeDuration::Zero(), trans->ChannelId()); + RefPtr pushedStreamWrapper = trans->GetPushedStream(); if (pushedStreamWrapper) { diff --git a/netwerk/protocol/http/nsHttpHandler.cpp b/netwerk/protocol/http/nsHttpHandler.cpp index b6440c88b5..d0aebdf965 100644 --- a/netwerk/protocol/http/nsHttpHandler.cpp +++ b/netwerk/protocol/http/nsHttpHandler.cpp @@ -2204,12 +2204,8 @@ nsresult nsHttpHandler::SpeculativeConnectInternal( originAttributes = std::move(aOriginAttributes.ref()); } else if (aPrincipal) { originAttributes = aPrincipal->OriginAttributesRef(); - StoragePrincipalHelper::UpdateOriginAttributesForNetworkState( - aURI, originAttributes); } else if (loadContext) { loadContext->GetOriginAttributes(originAttributes); - StoragePrincipalHelper::UpdateOriginAttributesForNetworkState( - aURI, originAttributes); } nsCOMPtr clone; @@ -2222,6 +2218,15 @@ nsresult nsHttpHandler::SpeculativeConnectInternal( } } + if (!aOriginAttributes) { + // We must update the originAttributes with the network state first party + // domain **after** we upgrade aURI to https. + // Otherwise the speculative connection will be keyed by a http URL + // and end up not being used. + StoragePrincipalHelper::UpdateOriginAttributesForNetworkState( + aURI, originAttributes); + } + nsAutoCString scheme; nsresult rv = aURI->GetScheme(scheme); if (NS_FAILED(rv)) return rv; @@ -2748,12 +2753,15 @@ bool nsHttpHandler::UseHTTPSRRAsAltSvcEnabled() const { } bool nsHttpHandler::EchConfigEnabled(bool aIsHttp3) const { + if (mParentalControlEnabled) { + return false; + } + if (!aIsHttp3) { return StaticPrefs::network_dns_echconfig_enabled(); } - return StaticPrefs::network_dns_echconfig_enabled() && - StaticPrefs::network_dns_http3_echconfig_enabled(); + return StaticPrefs::network_dns_http3_echconfig_enabled(); } bool nsHttpHandler::FallbackToOriginIfConfigsAreECHAndAllFailed() const { diff --git a/netwerk/protocol/http/nsHttpTransaction.cpp b/netwerk/protocol/http/nsHttpTransaction.cpp index 24ef50fb85..545dd5a142 100644 --- a/netwerk/protocol/http/nsHttpTransaction.cpp +++ b/netwerk/protocol/http/nsHttpTransaction.cpp @@ -398,11 +398,16 @@ nsresult nsHttpTransaction::Init( } RefPtr httpChannel = do_QueryObject(eventsink); - RefPtr listener = - httpChannel ? httpChannel->GetWebTransportSessionEventListener() - : nullptr; - if (listener) { - mWebTransportSessionEventListener = std::move(listener); + if (httpChannel) { + RefPtr listener = + httpChannel->GetWebTransportSessionEventListener(); + if (listener) { + mWebTransportSessionEventListener = std::move(listener); + } + nsCOMPtr uri; + if (NS_SUCCEEDED(httpChannel->GetURI(getter_AddRefs(uri)))) { + mUrl = uri->GetSpecOrDefault(); + } } return NS_OK; @@ -2198,8 +2203,9 @@ bool nsHttpTransaction::HandleWebTransportResponse(uint16_t aStatus) { webTransportListener = mWebTransportSessionEventListener; mWebTransportSessionEventListener = nullptr; } - if (webTransportListener) { - webTransportListener->OnSessionReadyInternal(wtSession); + if (nsCOMPtr listener = + do_QueryInterface(webTransportListener)) { + listener->OnSessionReadyInternal(wtSession); wtSession->SetWebTransportSessionEventListener(webTransportListener); } @@ -3289,7 +3295,9 @@ nsresult nsHttpTransaction::OnHTTPSRRAvailable( RefPtr newInfo = mConnInfo->CloneAndAdoptHTTPSSVCRecord(svcbRecord); - bool needFastFallback = newInfo->IsHttp3(); + // Don't fallback until we support WebTransport over HTTP/2. + // TODO: implement fallback in bug 1874102. + bool needFastFallback = newInfo->IsHttp3() && !newInfo->GetWebTransport(); bool foundInPendingQ = gHttpHandler->ConnMgr()->RemoveTransFromConnEntry( this, mHashKeyOfConnectionEntry); diff --git a/netwerk/protocol/http/nsHttpTransaction.h b/netwerk/protocol/http/nsHttpTransaction.h index 5256de4cf2..dd019e00e8 100644 --- a/netwerk/protocol/http/nsHttpTransaction.h +++ b/netwerk/protocol/http/nsHttpTransaction.h @@ -196,6 +196,10 @@ class nsHttpTransaction final : public nsAHttpTransaction, bool IsForWebTransport() { return mIsForWebTransport; } + nsAutoCString GetUrl() { return mUrl; } + + uint64_t ChannelId() { return mChannelId; } + private: friend class DeleteHttpTransaction; virtual ~nsHttpTransaction(); @@ -593,6 +597,8 @@ class nsHttpTransaction final : public nsAHttpTransaction, nsCString mHashKeyOfConnectionEntry; nsCOMPtr mWebTransportSessionEventListener; + + nsAutoCString mUrl; }; } // namespace mozilla::net diff --git a/netwerk/protocol/http/nsIHttpChannel.idl b/netwerk/protocol/http/nsIHttpChannel.idl index 1bd7aeba37..a8372ba080 100644 --- a/netwerk/protocol/http/nsIHttpChannel.idl +++ b/netwerk/protocol/http/nsIHttpChannel.idl @@ -494,4 +494,12 @@ interface nsIHttpChannel : nsIIdentChannel [must_use] attribute AString classicScriptHintCharset; [must_use] attribute AString documentCharacterSet; + + /** + * Update the requestObserversCalled boolean flag. + * + * Used by WebDriver BiDi network interception to modify properties of the + * request such as `method` or `body` as late as possible. + */ + [must_use] attribute boolean requestObserversCalled; }; diff --git a/netwerk/protocol/http/nsIHttpChannelInternal.idl b/netwerk/protocol/http/nsIHttpChannelInternal.idl index 64f41be28b..620718f4bb 100644 --- a/netwerk/protocol/http/nsIHttpChannelInternal.idl +++ b/netwerk/protocol/http/nsIHttpChannelInternal.idl @@ -56,7 +56,7 @@ interface nsIHttpUpgradeListener : nsISupports [must_use] void onUpgradeFailed(in nsresult aErrorCode); - void onWebSocketConnectionAvailable(in WebSocketConnectionBase aConnection); + [noscript] void onWebSocketConnectionAvailable(in WebSocketConnectionBase aConnection); }; /** diff --git a/netwerk/protocol/res/SubstitutingProtocolHandler.cpp b/netwerk/protocol/res/SubstitutingProtocolHandler.cpp index 523aaa435d..a7d87b9fb2 100644 --- a/netwerk/protocol/res/SubstitutingProtocolHandler.cpp +++ b/netwerk/protocol/res/SubstitutingProtocolHandler.cpp @@ -437,7 +437,7 @@ nsresult SubstitutingProtocolHandler::NewURI(const nsACString& aSpec, // "android" is the only root that would return the RESOLVE_JAR_URI flag // see nsResProtocolHandler::GetSubstitutionInternal - if (MustResolveJAR(host)) { + if (GetJARFlags(host) & nsISubstitutingProtocolHandler::RESOLVE_JAR_URI) { return ResolveJARURI(uri, aResult); } @@ -599,8 +599,7 @@ nsresult SubstitutingProtocolHandler::GetSubstitution( } } - uint32_t flags; - return GetSubstitutionInternal(root, result, &flags); + return GetSubstitutionInternal(root, result); } nsresult SubstitutingProtocolHandler::GetSubstitutionFlags( @@ -625,7 +624,8 @@ nsresult SubstitutingProtocolHandler::GetSubstitutionFlags( } nsCOMPtr baseURI; - return GetSubstitutionInternal(root, getter_AddRefs(baseURI), flags); + *flags = GetJARFlags(root); + return GetSubstitutionInternal(root, getter_AddRefs(baseURI)); } nsresult SubstitutingProtocolHandler::HasSubstitution( diff --git a/netwerk/protocol/res/SubstitutingProtocolHandler.h b/netwerk/protocol/res/SubstitutingProtocolHandler.h index 6bdb27f38a..1d3033a5a0 100644 --- a/netwerk/protocol/res/SubstitutingProtocolHandler.h +++ b/netwerk/protocol/res/SubstitutingProtocolHandler.h @@ -58,9 +58,8 @@ class SubstitutingProtocolHandler { // Override this in the subclass to try additional lookups after checking // mSubstitutions. [[nodiscard]] virtual nsresult GetSubstitutionInternal( - const nsACString& aRoot, nsIURI** aResult, uint32_t* aFlags) { + const nsACString& aRoot, nsIURI** aResult) { *aResult = nullptr; - *aFlags = 0; return NS_ERROR_NOT_AVAILABLE; } @@ -73,10 +72,11 @@ class SubstitutingProtocolHandler { return false; } - // This method should only return true if GetSubstitutionInternal would - // return the RESOLVE_JAR_URI flag. - [[nodiscard]] virtual bool MustResolveJAR(const nsACString& aRoot) { - return false; + // This method should only return RESOLVE_JAR_URI when + // GetSubstitutionalInternal will return nsIJARURI instead of a nsIFileURL. + // Currently, this only happens on Android. + [[nodiscard]] virtual uint32_t GetJARFlags(const nsACString& aRoot) { + return 0; } // Override this in the subclass to check for special case when opening diff --git a/netwerk/protocol/res/nsResProtocolHandler.cpp b/netwerk/protocol/res/nsResProtocolHandler.cpp index 208baedf2b..568f929f35 100644 --- a/netwerk/protocol/res/nsResProtocolHandler.cpp +++ b/netwerk/protocol/res/nsResProtocolHandler.cpp @@ -123,20 +123,23 @@ nsResProtocolHandler::AllowContentToAccess(nsIURI* aURI, bool* aResult) { return NS_OK; } +uint32_t nsResProtocolHandler::GetJARFlags(const nsACString& aRoot) { + if (aRoot.Equals(kAndroid)) { + return nsISubstitutingProtocolHandler::RESOLVE_JAR_URI; + } + + // 0 implies no content access. + return 0; +} + nsresult nsResProtocolHandler::GetSubstitutionInternal(const nsACString& aRoot, - nsIURI** aResult, - uint32_t* aFlags) { + nsIURI** aResult) { nsAutoCString uri; if (!ResolveSpecialCases(aRoot, "/"_ns, "/"_ns, uri)) { return NS_ERROR_NOT_AVAILABLE; } - if (aRoot.Equals(kAndroid)) { - *aFlags = nsISubstitutingProtocolHandler::RESOLVE_JAR_URI; - } else { - *aFlags = 0; // No content access. - } return NS_NewURI(aResult, uri); } diff --git a/netwerk/protocol/res/nsResProtocolHandler.h b/netwerk/protocol/res/nsResProtocolHandler.h index 50e790a53a..3c3781ac67 100644 --- a/netwerk/protocol/res/nsResProtocolHandler.h +++ b/netwerk/protocol/res/nsResProtocolHandler.h @@ -48,9 +48,9 @@ class nsResProtocolHandler final } protected: + [[nodiscard]] uint32_t GetJARFlags(const nsACString& aRoot) override; [[nodiscard]] nsresult GetSubstitutionInternal(const nsACString& aRoot, - nsIURI** aResult, - uint32_t* aFlags) override; + nsIURI** aResult) override; virtual ~nsResProtocolHandler() = default; [[nodiscard]] bool ResolveSpecialCases(const nsACString& aHost, @@ -58,10 +58,6 @@ class nsResProtocolHandler final const nsACString& aPathname, nsACString& aResult) override; - [[nodiscard]] virtual bool MustResolveJAR(const nsACString& aRoot) override { - return aRoot.EqualsLiteral("android"); - } - private: [[nodiscard]] nsresult Init(); static mozilla::StaticRefPtr sSingleton; diff --git a/netwerk/protocol/viewsource/nsViewSourceChannel.cpp b/netwerk/protocol/viewsource/nsViewSourceChannel.cpp index 3b5f95d5f3..d38cc43586 100644 --- a/netwerk/protocol/viewsource/nsViewSourceChannel.cpp +++ b/netwerk/protocol/viewsource/nsViewSourceChannel.cpp @@ -994,6 +994,18 @@ nsViewSourceChannel::UpgradeToSecure() { : mHttpChannel->UpgradeToSecure(); } +NS_IMETHODIMP +nsViewSourceChannel::GetRequestObserversCalled(bool* aCalled) { + return !mHttpChannel ? NS_ERROR_NULL_POINTER + : mHttpChannel->GetRequestObserversCalled(aCalled); +} + +NS_IMETHODIMP +nsViewSourceChannel::SetRequestObserversCalled(bool aCalled) { + return !mHttpChannel ? NS_ERROR_NULL_POINTER + : mHttpChannel->SetRequestObserversCalled(aCalled); +} + NS_IMETHODIMP nsViewSourceChannel::GetRequestContextID(uint64_t* _retval) { return !mHttpChannel ? NS_ERROR_NULL_POINTER diff --git a/netwerk/protocol/websocket/nsIWebSocketChannel.idl b/netwerk/protocol/websocket/nsIWebSocketChannel.idl index 7092c3c6b1..555d6d8168 100644 --- a/netwerk/protocol/websocket/nsIWebSocketChannel.idl +++ b/netwerk/protocol/websocket/nsIWebSocketChannel.idl @@ -146,7 +146,7 @@ interface nsIWebSocketChannel : nsISupports in nsIWebSocketListener aListener, in nsISupports aContext); - [must_use] + [must_use, noscript] void asyncOpenNative(in nsIURI aURI, in ACString aOrigin, in OriginAttributes aOriginAttributes, diff --git a/netwerk/protocol/webtransport/WebTransportSessionProxy.cpp b/netwerk/protocol/webtransport/WebTransportSessionProxy.cpp index dd4ba7e7d0..dd01cc6ff8 100644 --- a/netwerk/protocol/webtransport/WebTransportSessionProxy.cpp +++ b/netwerk/protocol/webtransport/WebTransportSessionProxy.cpp @@ -27,6 +27,7 @@ namespace mozilla::net { LazyLogModule webTransportLog("nsWebTransport"); NS_IMPL_ISUPPORTS(WebTransportSessionProxy, WebTransportSessionEventListener, + WebTransportSessionEventListenerInternal, WebTransportConnectionSettings, nsIWebTransport, nsIRedirectResultListener, nsIStreamListener, nsIChannelEventSink, nsIInterfaceRequestor); diff --git a/netwerk/protocol/webtransport/WebTransportSessionProxy.h b/netwerk/protocol/webtransport/WebTransportSessionProxy.h index 9e6f785d80..23407f3aaa 100644 --- a/netwerk/protocol/webtransport/WebTransportSessionProxy.h +++ b/netwerk/protocol/webtransport/WebTransportSessionProxy.h @@ -119,17 +119,20 @@ namespace mozilla::net { class WebTransportStreamCallbackWrapper; -class WebTransportSessionProxy final : public nsIWebTransport, - public WebTransportSessionEventListener, - public WebTransportConnectionSettings, - public nsIStreamListener, - public nsIChannelEventSink, - public nsIRedirectResultListener, - public nsIInterfaceRequestor { +class WebTransportSessionProxy final + : public nsIWebTransport, + public WebTransportSessionEventListener, + public WebTransportSessionEventListenerInternal, + public WebTransportConnectionSettings, + public nsIStreamListener, + public nsIChannelEventSink, + public nsIRedirectResultListener, + public nsIInterfaceRequestor { public: NS_DECL_THREADSAFE_ISUPPORTS NS_DECL_NSIWEBTRANSPORT NS_DECL_WEBTRANSPORTSESSIONEVENTLISTENER + NS_DECL_WEBTRANSPORTSESSIONEVENTLISTENERINTERNAL NS_DECL_WEBTRANSPORTCONNECTIONSETTINGS NS_DECL_NSIREQUESTOBSERVER NS_DECL_NSISTREAMLISTENER diff --git a/netwerk/protocol/webtransport/nsIWebTransport.idl b/netwerk/protocol/webtransport/nsIWebTransport.idl index 5ed4e96517..3afe9979ba 100644 --- a/netwerk/protocol/webtransport/nsIWebTransport.idl +++ b/netwerk/protocol/webtransport/nsIWebTransport.idl @@ -44,6 +44,7 @@ interface nsIWebTransport : nsISupports { in unsigned long aSecurityFlags, in WebTransportSessionEventListener aListener); + [noscript] void asyncConnectWithClient(in nsIURI aURI, in boolean aDedicated, in Array aServerCertHashes, @@ -79,9 +80,6 @@ interface WebTransportSessionEventListener : nsISupports { // This is used to let the consumer of nsIWebTransport know that the // underlying WebTransportSession object is ready to use. void onSessionReady(in uint64_t aSessionId); - // This is used internally to pass the reference of WebTransportSession - // object to WebTransportSessionProxy. - void onSessionReadyInternal(in Http3WebTransportSessionPtr aSession); void onSessionClosed(in boolean aCleanly, in uint32_t aErrorCode, in ACString aReason); @@ -90,19 +88,12 @@ interface WebTransportSessionEventListener : nsISupports { void onIncomingBidirectionalStreamAvailable(in nsIWebTransportBidirectionalStream aStream); void onIncomingUnidirectionalStreamAvailable(in nsIWebTransportReceiveStream aStream); - // This is used internally to pass the reference of Http3WebTransportStream - // object to WebTransportSessionProxy. - void onIncomingStreamAvailableInternal(in Http3WebTransportStreamPtr aStream); - void onStopSending(in uint64_t aStreamId, in nsresult aError); void onResetReceived(in uint64_t aStreamId, in nsresult aError); // When a new datagram has been received. void onDatagramReceived(in Array aData); - // This is used internally to pass the datagram to WebTransportSessionProxy. - void onDatagramReceivedInternal(in Datagram aData); - void onMaxDatagramSize(in uint64_t aSize); cenum DatagramOutcome: 32 { @@ -118,6 +109,20 @@ interface WebTransportSessionEventListener : nsISupports { // void onStatsAvailable(in WebTransportStats aStats); }; +[uuid(8fb30aa9-5163-4eb3-81f3-371e1ccb5b0e)] +interface WebTransportSessionEventListenerInternal : nsISupports { + // This is used internally to pass the reference of WebTransportSession + // object to WebTransportSessionProxy. + void onSessionReadyInternal(in Http3WebTransportSessionPtr aSession); + + // This is used internally to pass the reference of Http3WebTransportStream + // object to WebTransportSessionProxy. + void onIncomingStreamAvailableInternal(in Http3WebTransportStreamPtr aStream); + + // This is used internally to pass the datagram to WebTransportSessionProxy. + void onDatagramReceivedInternal(in Datagram aData); +}; + [uuid(faad75bd-83c6-420b-9fdb-a70bd70be449)] interface WebTransportConnectionSettings : nsISupports { // WebTransport specific connection information diff --git a/netwerk/socket/neqo_glue/Cargo.toml b/netwerk/socket/neqo_glue/Cargo.toml index 8355c54c19..dfcd0d8244 100644 --- a/netwerk/socket/neqo_glue/Cargo.toml +++ b/netwerk/socket/neqo_glue/Cargo.toml @@ -9,16 +9,16 @@ license = "MPL-2.0" name = "neqo_glue" [dependencies] -neqo-http3 = { tag = "v0.7.5", git = "https://github.com/mozilla/neqo" } -neqo-transport = { tag = "v0.7.5", git = "https://github.com/mozilla/neqo" } -neqo-common = { tag = "v0.7.5", git = "https://github.com/mozilla/neqo" } -neqo-qpack = { tag = "v0.7.5", git = "https://github.com/mozilla/neqo" } +neqo-http3 = { tag = "v0.7.7", git = "https://github.com/mozilla/neqo" } +neqo-transport = { tag = "v0.7.7", git = "https://github.com/mozilla/neqo" } +neqo-common = { tag = "v0.7.7", git = "https://github.com/mozilla/neqo" } +neqo-qpack = { tag = "v0.7.7", git = "https://github.com/mozilla/neqo" } nserror = { path = "../../../xpcom/rust/nserror" } nsstring = { path = "../../../xpcom/rust/nsstring" } xpcom = { path = "../../../xpcom/rust/xpcom" } thin-vec = { version = "0.2.1", features = ["gecko-ffi"] } log = "0.4.0" -qlog = "0.12" +qlog = "0.13" libc = "0.2.0" static_prefs = { path = "../../../modules/libpref/init/static_prefs"} uuid = { version = "1.0", features = ["v4"] } @@ -27,7 +27,7 @@ uuid = { version = "1.0", features = ["v4"] } winapi = {version = "0.3", features = ["ws2def"] } [dependencies.neqo-crypto] -tag = "v0.7.5" +tag = "v0.7.7" git = "https://github.com/mozilla/neqo" default-features = false features = ["gecko"] diff --git a/netwerk/socket/neqo_glue/src/lib.rs b/netwerk/socket/neqo_glue/src/lib.rs index fd527a7fb9..9f11a783be 100644 --- a/netwerk/socket/neqo_glue/src/lib.rs +++ b/netwerk/socket/neqo_glue/src/lib.rs @@ -671,11 +671,11 @@ impl From for CloseError { } } -impl From for CloseError { - fn from(error: neqo_transport::ConnectionError) -> CloseError { +impl From for CloseError { + fn from(error: neqo_transport::CloseReason) -> CloseError { match error { - neqo_transport::ConnectionError::Transport(c) => c.into(), - neqo_transport::ConnectionError::Application(c) => CloseError::AppError(c), + neqo_transport::CloseReason::Transport(c) => c.into(), + neqo_transport::CloseReason::Application(c) => CloseError::AppError(c), } } } @@ -1056,12 +1056,11 @@ pub extern "C" fn neqo_http3conn_event( Http3State::Connected => Http3Event::ConnectionConnected, Http3State::Closing(error_code) => { match error_code { - neqo_transport::ConnectionError::Transport( - TransportError::CryptoError(neqo_crypto::Error::EchRetry(ref c)), - ) - | neqo_transport::ConnectionError::Transport(TransportError::EchRetry( - ref c, - )) => { + neqo_transport::CloseReason::Transport(TransportError::CryptoError( + neqo_crypto::Error::EchRetry(ref c), + )) + | neqo_transport::CloseReason::Transport(TransportError::EchRetry(ref c)) => + { data.extend_from_slice(c.as_ref()); } _ => {} @@ -1072,12 +1071,11 @@ pub extern "C" fn neqo_http3conn_event( } Http3State::Closed(error_code) => { match error_code { - neqo_transport::ConnectionError::Transport( - TransportError::CryptoError(neqo_crypto::Error::EchRetry(ref c)), - ) - | neqo_transport::ConnectionError::Transport(TransportError::EchRetry( - ref c, - )) => { + neqo_transport::CloseReason::Transport(TransportError::CryptoError( + neqo_crypto::Error::EchRetry(ref c), + )) + | neqo_transport::CloseReason::Transport(TransportError::EchRetry(ref c)) => + { data.extend_from_slice(c.as_ref()); } _ => {} diff --git a/netwerk/socket/nsINamedPipeService.idl b/netwerk/socket/nsINamedPipeService.idl index 9db557577d..ce189ae9d3 100644 --- a/netwerk/socket/nsINamedPipeService.idl +++ b/netwerk/socket/nsINamedPipeService.idl @@ -12,7 +12,7 @@ * This is the callback interface for nsINamedPipeService. * The functions are called by the internal thread in the nsINamedPipeService. */ -[scriptable, uuid(de4f460b-94fd-442c-9002-1637beb2185a)] +[uuid(de4f460b-94fd-442c-9002-1637beb2185a)] interface nsINamedPipeDataObserver : nsISupports { /** @@ -41,7 +41,7 @@ interface nsINamedPipeDataObserver : nsISupports /** * nsINamedPipeService */ -[scriptable, uuid(1bf19133-5625-4ac8-836a-80b1c215f72b)] +[uuid(1bf19133-5625-4ac8-836a-80b1c215f72b)] interface nsINamedPipeService : nsISupports { /** diff --git a/netwerk/system/android/nsAndroidNetworkLinkService.cpp b/netwerk/system/android/nsAndroidNetworkLinkService.cpp index 850ac5b565..a3d0328d8b 100644 --- a/netwerk/system/android/nsAndroidNetworkLinkService.cpp +++ b/netwerk/system/android/nsAndroidNetworkLinkService.cpp @@ -9,6 +9,7 @@ #include "nsIObserverService.h" #include "mozilla/StaticPrefs_network.h" +#include "mozilla/IntegerPrintfMacros.h" #include "mozilla/Services.h" #include "mozilla/Logging.h" diff --git a/netwerk/system/linux/nsNetworkLinkService.cpp b/netwerk/system/linux/nsNetworkLinkService.cpp index 363b058eed..59894ba1fa 100644 --- a/netwerk/system/linux/nsNetworkLinkService.cpp +++ b/netwerk/system/linux/nsNetworkLinkService.cpp @@ -8,6 +8,7 @@ #include "nsNetworkLinkService.h" #include "nsString.h" #include "mozilla/Logging.h" +#include "mozilla/IntegerPrintfMacros.h" #include "nsNetAddr.h" #include "mozilla/StaticPrefs_network.h" diff --git a/netwerk/system/win32/nsNotifyAddrListener.cpp b/netwerk/system/win32/nsNotifyAddrListener.cpp index a64206fbc6..6174754c8c 100644 --- a/netwerk/system/win32/nsNotifyAddrListener.cpp +++ b/netwerk/system/win32/nsNotifyAddrListener.cpp @@ -600,7 +600,7 @@ nsNotifyAddrListener::CheckAdaptersAddresses(void) { list.StripWhitespace(); for (const nsACString& suffix : list.Split(',')) { LOG((" appending DNS suffix from registry: %s\n", - suffix.BeginReading())); + PromiseFlatCString(suffix).get())); if (!suffix.IsEmpty()) { dnsSuffixList.AppendElement(suffix); } diff --git a/netwerk/test/browser/browser.toml b/netwerk/test/browser/browser.toml index 002bd2769d..a12ffed212 100644 --- a/netwerk/test/browser/browser.toml +++ b/netwerk/test/browser/browser.toml @@ -72,6 +72,7 @@ support-files = [ "x_frame_options.html^headers^", "test_1629307.html", "file_link_header.sjs", + "file_link_dns_prefetch.sjs", ] prefs = [ @@ -156,6 +157,8 @@ skip-if = ["os == 'win'"] # Bug 1775761 ["browser_cookie_sync_across_tabs.js"] +["browser_dns_prefetch_link_header.js"] + ["browser_fetch_lnk.js"] run-if = ["os == 'win'"] support-files = ["file_lnk.lnk",] diff --git a/netwerk/test/browser/browser_dns_prefetch_link_header.js b/netwerk/test/browser/browser_dns_prefetch_link_header.js new file mode 100644 index 0000000000..a7954cadb5 --- /dev/null +++ b/netwerk/test/browser/browser_dns_prefetch_link_header.js @@ -0,0 +1,282 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// Test steps: +// 1. Load file_link_dns_prefetch.sjs +// 2.`` is in +// the server-side sjs, so we will make the dns-request. +// 3. We verify that the dns request was made + +const gDashboard = Cc["@mozilla.org/network/dashboard;1"].getService( + Ci.nsIDashboard +); + +///////////////////////////////////////////////////////////////////////////// +// To observe DNS requests when running via the mochitest proxy we must first take a few steps: +// +// 1. Update the mochitest proxy pac file to include dns resolution. +// We do this by injecting "dnsResolve(host);" into the `FindProxyForURL()` pac function. +let existingPACScript = Services.prefs.getCharPref( + "network.proxy.autoconfig_url" +); + +let findProxyForURLFunction = "function FindProxyForURL(url, host){"; +let directDnsPacScript = existingPACScript.replace( + findProxyForURLFunction, + `${findProxyForURLFunction} + dnsResolve(host); + ` +); +Services.prefs.setStringPref( + "network.proxy.autoconfig_url", + directDnsPacScript +); + +// 2. Ensure we don't disable dns prefetch despite using a proxy (this would otherwise happen after every request that the proxy completed) +Services.prefs.setBoolPref("network.dns.prefetch_via_proxy", true); + +// 3. And finally enable dns prefetching via the private dns service api (generally disabled in mochitest proxy) +Services.dns.QueryInterface(Ci.nsPIDNSService).prefetchEnabled = true; +///////////////////////////////////////////////////////////////////////////// + +registerCleanupFunction(function () { + // Restore proxy pac and dns prefetch behaviour via proxy + Services.prefs.setCharPref("network.proxy.autoconfig_url", existingPACScript); + Services.prefs.clearUserPref("network.dns.prefetch_via_proxy"); + Services.dns.QueryInterface(Ci.nsPIDNSService).prefetchEnabled = false; +}); + +async function isRecordFound(hostname) { + return new Promise(resolve => { + gDashboard.requestDNSInfo(function (data) { + let found = false; + for (let i = 0; i < data.entries.length; i++) { + if (data.entries[i].hostname == hostname) { + found = true; + break; + } + } + resolve(found); + }); + }); +} + +let https_requestUrl = `https://example.com/browser/netwerk/test/browser/file_link_dns_prefetch.sjs`; +let http_requestUrl = `http://example.com/browser/netwerk/test/browser/file_link_dns_prefetch.sjs`; // eslint-disable-line @microsoft/sdl/no-insecure-url + +// Test dns-prefetch on https +add_task(async function test_https_dns_prefetch() { + Services.dns.clearCache(true); + + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: https_requestUrl, + waitForLoad: true, + }, + async function () {} + ); + + Assert.ok( + await TestUtils.waitForCondition(() => { + return isRecordFound("example.org"); + }), + "Record from link rel=dns-prefetch element should be found" + ); + Assert.ok(await isRecordFound("example.com"), "Host record should be found"); +}); + +// Test dns-prefetch on http +add_task(async function test_http_dns_prefetch() { + Services.dns.clearCache(true); + + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: http_requestUrl, + waitForLoad: true, + }, + async function () {} + ); + + Assert.ok( + await TestUtils.waitForCondition(() => { + return isRecordFound("example.org"); + }), + "Record from link rel=dns-prefetch element should be found" + ); + Assert.ok(await isRecordFound("example.com"), "Host record should be found"); +}); + +// Test dns-prefetch on https with the feature disabled +add_task(async function test_https_dns_prefetch_disabled() { + Services.dns.clearCache(true); + + // Disable the feature to verify that it will not prefetch + Services.prefs.setBoolPref("network.dns.disablePrefetchFromHTTPS", true); + + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: https_requestUrl, + waitForLoad: true, + }, + async function () {} + ); + + Assert.ok(await isRecordFound("example.com"), "Host record should be found"); + Assert.ok( + !(await isRecordFound("example.org")), + "Record from link rel=dns-prefetch element should not be found with disablePrefetchFromHTTPS set" + ); + + Services.prefs.clearUserPref("network.dns.disablePrefetchFromHTTPS"); +}); + +// Test dns-prefetch on http with the feature disabled +add_task(async function test_http_dns_prefetch_disabled() { + Services.dns.clearCache(true); + + // Disable the feature to verify, but this test is http, and so prefetch will execute + Services.prefs.setBoolPref("network.dns.disablePrefetchFromHTTPS", true); + + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: http_requestUrl, + waitForLoad: true, + }, + async function () {} + ); + + Assert.ok( + await TestUtils.waitForCondition(() => { + return isRecordFound("example.org"); + }), + "Record from link rel=dns-prefetch element should be found on http page with disablePrefetchFromHTTPS set" + ); + Assert.ok(await isRecordFound("example.com"), "Host record should be found"); + + Services.prefs.clearUserPref("network.dns.disablePrefetchFromHTTPS"); +}); + +// Test if we speculatively prefetch dns for anchor elements on https documents +add_task(async function test_https_anchor_speculative_dns_prefetch() { + Services.dns.clearCache(true); + + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: https_requestUrl, + waitForLoad: true, + }, + async function () { + Assert.ok( + await isRecordFound("example.com"), + "Host record should be found" + ); + Assert.ok( + !(await isRecordFound("www.mozilla.org")), + "By default we do not speculatively prefetch dns for anchor elements on https documents" + ); + } + ); + + // And enable the pref to verify that it works + Services.prefs.setBoolPref( + "dom.prefetch_dns_for_anchor_https_document", + true + ); + Services.dns.clearCache(true); + + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: https_requestUrl, + waitForLoad: true, + }, + async function () { + // The anchor element prefetchs are sent after pageload event; wait for them + Assert.ok( + await TestUtils.waitForCondition(() => { + return isRecordFound("www.mozilla.org"); + }), + "Speculatively prefetch dns for anchor elements on https documents" + ); + Assert.ok( + await isRecordFound("example.com"), + "Host record should be found" + ); + } + ); + + Services.prefs.clearUserPref("dom.prefetch_dns_for_anchor_https_document"); +}); + +// Test that we speculatively prefetch dns for anchor elements on http documents +add_task(async function test_http_anchor_speculative_dns_prefetch() { + Services.dns.clearCache(true); + + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: http_requestUrl, + waitForLoad: true, + }, + async function () { + Assert.ok( + await TestUtils.waitForCondition(() => { + return isRecordFound("example.org"); + }), + "Record from link rel=dns-prefetch element should be found" + ); + + // The anchor element prefetchs are sent after pageload event; wait for them + Assert.ok( + await TestUtils.waitForCondition(() => { + return isRecordFound("www.mozilla.org"); + }), + "By default we speculatively prefetch dns for anchor elements on http documents" + ); + + Assert.ok( + await isRecordFound("example.com"), + "Host record should be found" + ); + } + ); + + // And disable the pref to verify that we no longer make the requests + Services.prefs.setBoolPref( + "dom.prefetch_dns_for_anchor_http_document", + false + ); + Services.dns.clearCache(true); + + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: http_requestUrl, + waitForLoad: true, + }, + async function () { + Assert.ok( + await TestUtils.waitForCondition(() => { + return isRecordFound("example.org"); + }), + "Record from link rel=dns-prefetch element should be found" + ); + Assert.ok( + !(await isRecordFound("www.mozilla.org")), + "We disabled speculative prefetch dns for anchor elements on http documents" + ); + Assert.ok( + await isRecordFound("example.com"), + "Host record should be found" + ); + } + ); + + Services.prefs.clearUserPref("dom.prefetch_dns_for_anchor_http_document"); +}); diff --git a/netwerk/test/browser/file_link_dns_prefetch.sjs b/netwerk/test/browser/file_link_dns_prefetch.sjs new file mode 100644 index 0000000000..6ca959bda1 --- /dev/null +++ b/netwerk/test/browser/file_link_dns_prefetch.sjs @@ -0,0 +1,25 @@ +"use strict"; + +function handleRequest(request, response) { + // write to raw socket + response.seizePower(); + let body = ` + + + + + +

Test rel=dns-prefetch

+ Test link + + `; + + response.write("HTTP/1.1 200 OK\r\n"); + response.write("Content-Type: text/html;charset=utf-8\r\n"); + response.write("Cache-Control: no-cache\r\n"); + response.write(`Content-Length: ${body.length}\r\n`); + response.write("\r\n"); + response.write(body); + + response.finish(); +} diff --git a/netwerk/test/gtest/TestCommon.h b/netwerk/test/gtest/TestCommon.h index 0d2fd74e5b..76620fbb44 100644 --- a/netwerk/test/gtest/TestCommon.h +++ b/netwerk/test/gtest/TestCommon.h @@ -17,8 +17,8 @@ class WaitForCondition final : public nsIRunnable { NS_DECL_THREADSAFE_ISUPPORTS void Wait(int pending) { - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(mPending == 0); + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + MOZ_RELEASE_ASSERT(mPending == 0); mPending = pending; mozilla::SpinEventLoopUntil("TestCommon.h:WaitForCondition::Wait"_ns, @@ -32,8 +32,8 @@ class WaitForCondition final : public nsIRunnable { virtual ~WaitForCondition() = default; NS_IMETHOD Run() override { - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(mPending); + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + MOZ_RELEASE_ASSERT(mPending); --mPending; return NS_OK; diff --git a/netwerk/test/gtest/TestCookie.cpp b/netwerk/test/gtest/TestCookie.cpp index 4812ee47f1..e99e9f6332 100644 --- a/netwerk/test/gtest/TestCookie.cpp +++ b/netwerk/test/gtest/TestCookie.cpp @@ -95,7 +95,7 @@ void SetACookieInternal(nsICookieService* aCookieService, const char* aSpec, /* shouldResistFingerprinting */ false) : CookieJarSettings::GetBlockingAll( /* shouldResistFingerprinting */ false); - MOZ_ASSERT(cookieJarSettings); + MOZ_RELEASE_ASSERT(cookieJarSettings); nsCOMPtr loadInfo = dummyChannel->LoadInfo(); loadInfo->SetCookieJarSettings(cookieJarSettings); @@ -140,7 +140,7 @@ void GetACookieNoHttp(nsICookieService* aCookieService, const char* aSpec, RefPtr principal = BasePrincipal::CreateContentPrincipal(uri, OriginAttributes()); - MOZ_ASSERT(principal); + MOZ_RELEASE_ASSERT(principal); nsCOMPtr document; nsresult rv = NS_NewDOMDocument(getter_AddRefs(document), diff --git a/netwerk/test/gtest/TestIDNA.cpp b/netwerk/test/gtest/TestIDNA.cpp index 544debbd43..56b05025d7 100644 --- a/netwerk/test/gtest/TestIDNA.cpp +++ b/netwerk/test/gtest/TestIDNA.cpp @@ -3,6 +3,9 @@ #include "gtest/BlackBox.h" #include "nsNetUtil.h" +#include "nsIURI.h" +#include "nsCOMPtr.h" +#include "mozilla/Encoding.h" #define TEST_COUNT 10000 @@ -10,7 +13,7 @@ class TestIDNA : public ::testing::Test { protected: void SetUp() override { // Intentionally Assign and not AssignLiteral - // to simulate the usual heap case. + // to simulate the heap case. mPlainASCII.Assign("example.com"); mLeadingDigitASCII.Assign("1test.example"); mUnicodeMixed.Assign("مثال.example"); @@ -20,6 +23,16 @@ class TestIDNA : public ::testing::Test { mUnicodeRTL.Assign("الاسم.مثال"); mPunycodeRTL.Assign("xn--mgba0b1dh.xn--mgbh0fb"); // Intentionally not assigning to mEmpty + + // For measuring the case inside nsStandardURL + mUrlPlainASCII.Assign("https://example.com/"); + mUrlLeadingDigitASCII.Assign("https://1test.example/"); + mUrlUnicodeMixed.Assign("https://مثال.example/"); + mUrlPunycodeMixed.Assign("https://xn--mgbh0fb.example/"); + mUrlUnicodeLTR.Assign("https://නම.උදාහරණ/"); + mUrlPunycodeLTR.Assign("https://xn--r0co.xn--ozc8dl2c3bxd/"); + mUrlUnicodeRTL.Assign("https://الاسم.مثال/"); + mUrlPunycodeRTL.Assign("https://xn--mgba0b1dh.xn--mgbh0fb/"); } public: @@ -32,6 +45,14 @@ class TestIDNA : public ::testing::Test { nsCString mUnicodeRTL; nsCString mPunycodeRTL; nsCString mEmpty; // Extremely suspicious measurement! + nsCString mUrlPlainASCII; + nsCString mUrlLeadingDigitASCII; + nsCString mUrlUnicodeMixed; + nsCString mUrlPunycodeMixed; + nsCString mUrlUnicodeLTR; + nsCString mUrlPunycodeLTR; + nsCString mUrlUnicodeRTL; + nsCString mUrlPunycodeRTL; }; #define IDNA_ITERATIONS 50000 @@ -44,6 +65,17 @@ class TestIDNA : public ::testing::Test { } \ }); +#define IDNA_URL_BENCH(name, src) \ + MOZ_GTEST_BENCH_F(TestIDNA, name, [this] { \ + for (int i = 0; i < IDNA_ITERATIONS; i++) { \ + nsCOMPtr dst; \ + nsresult rv = NS_NewURI(getter_AddRefs(dst), *mozilla::BlackBox(&src), \ + UTF_8_ENCODING); \ + ASSERT_EQ(NS_OK, rv); \ + mozilla::BlackBox(&dst); \ + } \ + }); + IDNA_BENCH(BenchToASCIIPlainASCII, NS_DomainToASCII, mPlainASCII); IDNA_BENCH(BenchToASCIILeadingDigitASCII, NS_DomainToASCII, mLeadingDigitASCII); IDNA_BENCH(BenchToASCIIUnicodeMixed, NS_DomainToASCII, mUnicodeMixed); @@ -75,3 +107,12 @@ IDNA_BENCH(BenchToUnicodePunycodeLTR, NS_DomainToUnicode, mPunycodeLTR); IDNA_BENCH(BenchToUnicodeUnicodeRTL, NS_DomainToUnicode, mUnicodeRTL); IDNA_BENCH(BenchToUnicodePunycodeRTL, NS_DomainToUnicode, mPunycodeRTL); IDNA_BENCH(BenchToUnicodeEmpty, NS_DomainToUnicode, mEmpty); + +IDNA_URL_BENCH(BenchUrlPlainASCII, mUrlPlainASCII); +IDNA_URL_BENCH(BenchUrlLeadingDigitASCII, mUrlLeadingDigitASCII); +IDNA_URL_BENCH(BenchUrlUnicodeMixed, mUrlUnicodeMixed); +IDNA_URL_BENCH(BenchUrlPunycodeMixed, mUrlPunycodeMixed); +IDNA_URL_BENCH(BenchUrlUnicodeLTR, mUrlUnicodeLTR); +IDNA_URL_BENCH(BenchUrlPunycodeLTR, mUrlPunycodeLTR); +IDNA_URL_BENCH(BenchUrlUnicodeRTL, mUrlUnicodeRTL); +IDNA_URL_BENCH(BenchUrlPunycodeRTL, mUrlPunycodeRTL); diff --git a/netwerk/test/gtest/TestInputStreamTransport.cpp b/netwerk/test/gtest/TestInputStreamTransport.cpp index 43df0e193a..de19f48147 100644 --- a/netwerk/test/gtest/TestInputStreamTransport.cpp +++ b/netwerk/test/gtest/TestInputStreamTransport.cpp @@ -29,7 +29,7 @@ void CreateStream(already_AddRefed aSource, ASSERT_EQ(NS_OK, rv); nsCOMPtr asyncStream = do_QueryInterface(wrapper); - MOZ_ASSERT(asyncStream); + MOZ_RELEASE_ASSERT(asyncStream); asyncStream.forget(aStream); } diff --git a/netwerk/test/gtest/TestNamedPipeService.cpp b/netwerk/test/gtest/TestNamedPipeService.cpp index b91a17a93e..ef764dde13 100644 --- a/netwerk/test/gtest/TestNamedPipeService.cpp +++ b/netwerk/test/gtest/TestNamedPipeService.cpp @@ -31,7 +31,7 @@ class Event { void Set() { MonitorAutoLock lock(mMonitor); - MOZ_ASSERT(!mSignaled); + MOZ_RELEASE_ASSERT(!mSignaled); mSignaled = true; mMonitor.Notify(); } diff --git a/netwerk/test/gtest/moz.build b/netwerk/test/gtest/moz.build index 8e0d66f4e2..a0333ecde7 100644 --- a/netwerk/test/gtest/moz.build +++ b/netwerk/test/gtest/moz.build @@ -23,6 +23,7 @@ UNIFIED_SOURCES += [ "TestLinkHeader.cpp", "TestMIMEInputStream.cpp", "TestMozURL.cpp", + "TestPACMan.cpp", "TestProtocolProxyService.cpp", "TestReadStreamToString.cpp", "TestServerTimingHeader.cpp", @@ -30,24 +31,15 @@ UNIFIED_SOURCES += [ "TestSSLTokensCache.cpp", "TestStandardURL.cpp", "TestUDPSocket.cpp", + "TestURIMutator.cpp", ] if CONFIG["OS_TARGET"] == "WINNT": UNIFIED_SOURCES += [ "TestNamedPipeService.cpp", + "TestNetworkLinkIdHashingWindows.cpp", ] -# skip the test on windows10-aarch64 -if not (CONFIG["OS_TARGET"] == "WINNT" and CONFIG["TARGET_CPU"] == "aarch64"): - UNIFIED_SOURCES += [ - "TestPACMan.cpp", - "TestURIMutator.cpp", - ] - -# run the test on windows only -if CONFIG["OS_TARGET"] == "WINNT": - UNIFIED_SOURCES += ["TestNetworkLinkIdHashingWindows.cpp"] - # run the test on mac only if CONFIG["TARGET_OS"] == "OSX": UNIFIED_SOURCES += ["TestNetworkLinkIdHashingDarwin.cpp"] diff --git a/netwerk/test/http3server/Cargo.toml b/netwerk/test/http3server/Cargo.toml index 4905760d3d..d2413636f7 100644 --- a/netwerk/test/http3server/Cargo.toml +++ b/netwerk/test/http3server/Cargo.toml @@ -6,10 +6,10 @@ edition = "2018" license = "MPL-2.0" [dependencies] -neqo-transport = { tag = "v0.7.5", git = "https://github.com/mozilla/neqo" } -neqo-common = { tag = "v0.7.5", git = "https://github.com/mozilla/neqo" } -neqo-http3 = { tag = "v0.7.5", git = "https://github.com/mozilla/neqo" } -neqo-qpack = { tag = "v0.7.5", git = "https://github.com/mozilla/neqo" } +neqo-transport = { tag = "v0.7.7", git = "https://github.com/mozilla/neqo" } +neqo-common = { tag = "v0.7.7", git = "https://github.com/mozilla/neqo" } +neqo-http3 = { tag = "v0.7.7", git = "https://github.com/mozilla/neqo" } +neqo-qpack = { tag = "v0.7.7", git = "https://github.com/mozilla/neqo" } mio = "0.6.17" mio-extras = "2.0.5" log = "0.4.0" @@ -21,7 +21,7 @@ tokio = { version = "1", features = ["rt-multi-thread"] } mozilla-central-workspace-hack = { version = "0.1", features = ["http3server"], optional = true } [dependencies.neqo-crypto] -tag = "v0.7.5" +tag = "v0.7.7" git = "https://github.com/mozilla/neqo" default-features = false features = ["gecko"] diff --git a/netwerk/test/unit/head_channels.js b/netwerk/test/unit/head_channels.js index ca16bd2835..94a20b0a3d 100644 --- a/netwerk/test/unit/head_channels.js +++ b/netwerk/test/unit/head_channels.js @@ -480,7 +480,7 @@ function bytesToString(bytes) { function check_http_info(request, expected_httpVersion, expected_proxy) { let httpVersion = ""; try { - httpVersion = request.protocolVersion; + httpVersion = request.QueryInterface(Ci.nsIHttpChannel).protocolVersion; } catch (e) {} request.QueryInterface(Ci.nsIProxiedChannel); diff --git a/netwerk/test/unit/test_cookies_privatebrowsing.js b/netwerk/test/unit/test_cookies_privatebrowsing.js index 9d3528440a..dab719954e 100644 --- a/netwerk/test/unit/test_cookies_privatebrowsing.js +++ b/netwerk/test/unit/test_cookies_privatebrowsing.js @@ -96,10 +96,21 @@ add_task(async () => { Services.cookies.setCookieStringFromHttp(uri2, "oh=hai; max-age=1000", chan2); Assert.equal(await getCookieStringFromPrivateDocument(uri2.spec), "oh=hai"); + // on android fission the privateBrowsingHolder prevents + // the cookies on the content process from being updated + // Let's release the last PB window. + await privateBrowsingHolder.close(); + // Fake a profile change. await promise_close_profile(); do_load_profile(); + // keep the private browsing window open again + const privateBrowsingHolder2 = await CookieXPCShellUtils.loadContentPage( + "http://bar.com/", + { privateBrowsing: true } + ); + // We're still in private browsing mode, but should have a new session. // Check counts. Assert.equal(await getCookieStringFromPrivateDocument(uri1.spec), ""); @@ -127,6 +138,6 @@ add_task(async () => { Assert.equal(Services.cookies.countCookiesFromHost(uri2.host), 0); // Let's release the last PB window. - privateBrowsingHolder.close(); + await privateBrowsingHolder2.close(); Services.prefs.clearUserPref("dom.security.https_first"); }); diff --git a/netwerk/test/unit/test_dns_override.js b/netwerk/test/unit/test_dns_override.js index f092dd531c..75fc69c289 100644 --- a/netwerk/test/unit/test_dns_override.js +++ b/netwerk/test/unit/test_dns_override.js @@ -353,7 +353,10 @@ function hexToUint8Array(hex) { add_task( { - skip_if: () => mozinfo.os == "win" || mozinfo.os == "android", + skip_if: () => + mozinfo.os == "win" || + mozinfo.os == "android" || + mozinfo.socketprocess_networking, }, async function test_https_record_override() { let trrServer = new TRRServer(); @@ -414,6 +417,7 @@ add_task( Services.prefs.setBoolPref("network.dns.native_https_query", true); registerCleanupFunction(async () => { Services.prefs.clearUserPref("network.dns.native_https_query"); + Services.prefs.clearUserPref("network.trr.excluded-domains"); }); let listener = new Listener(); @@ -511,5 +515,24 @@ add_task( "def...", "got correct answer" ); + + // Adding "service.com" into excluded-domains should fail + // native HTTPS query. + Services.prefs.setCharPref("network.trr.excluded-domains", "service.com"); + listener = new Listener(); + try { + Services.dns.asyncResolve( + "service.com", + Ci.nsIDNSService.RESOLVE_TYPE_HTTPSSVC, + 0, + null, + listener, + mainThread, + defaultOriginAttributes + ); + Assert.ok(false, "asyncResolve should fail"); + } catch (e) { + Assert.equal(e.result, Cr.NS_ERROR_UNKNOWN_HOST); + } } ); diff --git a/netwerk/test/unit/test_httpssvc_iphint.js b/netwerk/test/unit/test_httpssvc_iphint.js index 13d9a7e648..40f6727e08 100644 --- a/netwerk/test/unit/test_httpssvc_iphint.js +++ b/netwerk/test/unit/test_httpssvc_iphint.js @@ -316,7 +316,7 @@ add_task(async function testIPHintWithFreshDNS() { ); let chan = makeChan(`https://test.iphint.org/server-timing`); - chan.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE; + chan.loadFlags |= Ci.nsIRequest.LOAD_FRESH_CONNECTION; let [req] = await channelOpenPromise( chan, CL_EXPECT_FAILURE | CL_ALLOW_UNKNOWN_CL @@ -337,7 +337,7 @@ add_task(async function testIPHintWithFreshDNS() { }); chan = makeChan(`https://test.iphint.org/server-timing`); - chan.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE; + chan.loadFlags |= Ci.nsIRequest.LOAD_FRESH_CONNECTION; [req] = await channelOpenPromise(chan); Assert.equal(req.protocolVersion, "h2"); let internal = req.QueryInterface(Ci.nsIHttpChannelInternal); diff --git a/netwerk/test/unit/test_verify_traffic.js b/netwerk/test/unit/test_verify_traffic.js index be41223642..2b806332c9 100644 --- a/netwerk/test/unit/test_verify_traffic.js +++ b/netwerk/test/unit/test_verify_traffic.js @@ -9,6 +9,14 @@ /* import-globals-from head_channels.js */ /* import-globals-from head_servers.js */ +const gDashboard = Cc["@mozilla.org/network/dashboard;1"].getService( + Ci.nsIDashboard +); + +const { TestUtils } = ChromeUtils.importESModule( + "resource://testing-common/TestUtils.sys.mjs" +); + function makeChan(uri) { let chan = NetUtil.newChannel({ uri, @@ -106,5 +114,36 @@ add_task(async function test_verify_traffic_for_http2() { sessionCount = await server.sessionCount(); Assert.equal(sessionCount, 2); + // Create another request and trigger the network change event again. + // The second network change event is to put the second connection into the + // pending list. + res = await new Promise(resolve => { + // Create a request that takes 8s to finish. + let chan = makeChan(`https://localhost:${server.port()}/longDelay`); + chan.asyncOpen(new ChannelListener(resolve, null, CL_ALLOW_UNKNOWN_CL)); + + Services.obs.notifyObservers( + null, + "network:link-status-changed", + "changed" + ); + }); + + Assert.equal(res.status, Cr.NS_OK); + Assert.equal(res.QueryInterface(Ci.nsIHttpChannel).responseStatus, 200); + + async function getSocketCount() { + return new Promise(resolve => { + gDashboard.requestSockets(function (data) { + resolve(data.sockets.length); + }); + }); + } + + await TestUtils.waitForCondition(async () => { + const socketCount = await getSocketCount(); + return socketCount === 0; + }, "Socket count should be 0"); + await server.stop(); }); diff --git a/netwerk/test/unit/xpcshell.toml b/netwerk/test/unit/xpcshell.toml index c86a1759e7..a2c09da498 100644 --- a/netwerk/test/unit/xpcshell.toml +++ b/netwerk/test/unit/xpcshell.toml @@ -113,6 +113,7 @@ skip-if = [ ["test_auth_proxy.js"] ["test_authentication.js"] +requesttimeoutfactor = 2 ["test_authpromptwrapper.js"] @@ -484,7 +485,6 @@ skip-if = ["os == 'linux' && bits == 64 && !debug"] #Bug 1553353 ["test_cookies_partition_counting.js"] ["test_cookies_privatebrowsing.js"] -skip-if = ["os == 'android' && fission"] # Bug 1888227 ["test_cookies_profile_close.js"] skip-if = ["os == 'android'"] # Bug 1700483 diff --git a/netwerk/wifi/gtest/TestWifiMonitor.cpp b/netwerk/wifi/gtest/TestWifiMonitor.cpp index f12c5c78ef..37d46c658a 100644 --- a/netwerk/wifi/gtest/TestWifiMonitor.cpp +++ b/netwerk/wifi/gtest/TestWifiMonitor.cpp @@ -85,7 +85,7 @@ class TestWifiMonitor : public ::testing::Test { public: TestWifiMonitor() { mObs = mozilla::services::GetObserverService(); - MOZ_ASSERT(mObs); + MOZ_RELEASE_ASSERT(mObs); nsresult rv; nsCOMPtr nls = @@ -312,7 +312,7 @@ class TestWifiMonitor : public ::testing::Test { LOGI(("RunSingleTest: <%s, %s> | requestPolling: %s | shouldPoll: %s", aTopic, NS_ConvertUTF16toUTF8(aData).get(), aRequestPolling ? "true" : "false", aShouldPoll ? "true" : "false")); - MOZ_ASSERT(aShouldPoll || !aRequestPolling); + MOZ_RELEASE_ASSERT(aShouldPoll || !aRequestPolling); CreateObjects(); -- cgit v1.2.3