tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit e0b8468a2e4550756eae119830eba4e9fbb1d50d
parent c6ed100fd966dfd2b57512bd510e67d21e6b17d9
Author: Randell Jesup <rjesup@mozilla.com>
Date:   Tue,  7 Oct 2025 17:55:52 +0000

Bug 1917965: Add initial Dictionary in-memory structures r=necko-reviewers,kershaw

Differential Revision: https://phabricator.services.mozilla.com/D258121

Diffstat:
Mmodules/libpref/init/all.js | 2++
Anetwerk/cache2/Dictionary.cpp | 291++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Anetwerk/cache2/Dictionary.h | 167+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mnetwerk/cache2/moz.build | 6+++++-
Mnetwerk/protocol/http/HttpBaseChannel.cpp | 2+-
Mnetwerk/protocol/http/nsHttpAtomList.h | 2++
Mnetwerk/protocol/http/nsHttpHandler.cpp | 75++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------
Mnetwerk/protocol/http/nsHttpHandler.h | 12++++++++++--
Mnetwerk/protocol/http/nsHttpRequestHead.cpp | 6++++++
Mnetwerk/protocol/http/nsHttpRequestHead.h | 6++++++
10 files changed, 556 insertions(+), 13 deletions(-)

diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js @@ -1167,6 +1167,8 @@ pref("network.http.redirection-limit", 20); // NOTE: separate values with comma+space (", "): see bug 576033 pref("network.http.accept-encoding", "gzip, deflate"); pref("network.http.accept-encoding.secure", "gzip, deflate, br, zstd"); +// dictionary compression is always only for secure connections +pref("network.http.accept-encoding.dictionary", "gzip, deflate, br, zstd, dcb, dcz"); // Prompt for redirects resulting in unsafe HTTP requests pref("network.http.prompt-temp-redirect", false); diff --git a/netwerk/cache2/Dictionary.cpp b/netwerk/cache2/Dictionary.cpp @@ -0,0 +1,291 @@ +/* vim: set ts=2 sts=2 et sw=2: */ +/* 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/. */ + +#include <algorithm> + +#include "Dictionary.h" + +#include "nsAppDirectoryServiceDefs.h" +#include "nsICacheStorage.h" +#include "nsICachingChannel.h" +#include "nsICancelable.h" +#include "nsIChannel.h" +#include "nsContentUtils.h" +#include "mozilla/dom/Document.h" +#include "nsIFile.h" +#include "nsIHttpChannel.h" +#include "nsIInputStream.h" +#include "nsILoadContext.h" +#include "nsILoadContextInfo.h" +#include "nsILoadGroup.h" +#include "nsIObserverService.h" +#include "nsITimer.h" +#include "nsIURI.h" +#include "nsNetUtil.h" +#include "nsServiceManagerUtils.h" +#include "nsStreamUtils.h" +#include "nsString.h" +#include "nsThreadUtils.h" +#include "mozilla/Logging.h" + +#include "mozilla/Components.h" +#include "mozilla/OriginAttributes.h" +#include "mozilla/Preferences.h" +#include "mozilla/SchedulerGroup.h" +#include "mozilla/StaticPrefs_network.h" +#include "mozilla/glean/NetwerkMetrics.h" + +#include "mozilla/net/NeckoCommon.h" +#include "mozilla/net/NeckoParent.h" + +#include "LoadContextInfo.h" +#include "mozilla/ipc/URIUtils.h" +#include "SerializedLoadContext.h" +#include "mozilla/net/NeckoChild.h" + +#include "mozilla/dom/ContentParent.h" +#include "mozilla/ClearOnShutdown.h" + +#include "ReferrerInfo.h" + +using namespace mozilla; + +namespace mozilla { +namespace net { + +static LazyLogModule gDictionaryLog("CompressionDictionaries"); + +#define DICTIONARY_LOG(args) \ + MOZ_LOG(gDictionaryLog, mozilla::LogLevel::Debug, args) + +/** + * Reference to the DictionaryCache singleton. May be null. + */ +StaticRefPtr<DictionaryCache> gDictionaryCache; + +DictionaryCacheEntry::DictionaryCacheEntry(const nsACString& aURI, + const nsACString& aPattern, + const nsACString& aId, + const Maybe<nsCString>& aHash) + : mURI(aURI), mPattern(aPattern), mId(aId) { + if (aHash) { + mHash = aHash.value(); + } +} + +// returns true if the pattern for the dictionary matches the path given +bool DictionaryCacheEntry::Match(const nsACString& aFilePath, + uint32_t& aLongest) { + MOZ_ASSERT(NS_IsMainThread()); + // Not worth checking if we wouldn't use it + // XXX Check match-dest + if (mPattern.Length() > aLongest) { + DICTIONARY_LOG(("Match: %s to %s", PromiseFlatCString(aFilePath).get(), + PromiseFlatCString(mPattern).get())); + // XXX remove this when we get URLPattern + // XXX temp: handle https://site/foo* or https://site/foo?query=*, or + // https://site/foo/*, etc + if (mPattern.Last() == '*' && aFilePath.Length() >= mPattern.Length()) { + // XXX not efficient, but this is throw-away code + nsAutoCString partial(aFilePath); + partial.Truncate(mPattern.Length() - 1); + nsAutoCString pattern(mPattern); + pattern.Truncate(mPattern.Length() - 1); + if (partial.Equals(pattern)) { + aLongest = mPattern.Length(); + DICTIONARY_LOG(("Match: %s (longest %u)", mURI.get(), aLongest)); + return true; + } + return false; + // XXX handle https://site/foo/* ( + } else if (mPattern.Equals(aFilePath)) { + if (mHash.IsEmpty()) { + return false; + } + aLongest = mPattern.Length(); + DICTIONARY_LOG( + ("Match: %s (longest %u)", PromiseFlatCString(mURI).get(), aLongest)); + return true; + } + } + return false; +} + +void DictionaryCacheEntry::Prefetch() { + // Start reading the cache entry into memory + // XXX +} + +// static +already_AddRefed<DictionaryCache> DictionaryCache::GetInstance() { + if (!gDictionaryCache) { + gDictionaryCache = new DictionaryCache(); + MOZ_ASSERT(NS_SUCCEEDED(gDictionaryCache->Init())); + } + return do_AddRef(gDictionaryCache); +} + +nsresult DictionaryCache::Init() { return NS_OK; } + +nsresult DictionaryCache::AddEntry(nsIURI* aURI, const nsACString& aKey, + const nsACString& aPattern, + const nsACString& aId, + const Maybe<nsCString>& aHash, + DictionaryCacheEntry** aDictEntry) { + // Note that normally we're getting an entry in and until all the data + // has been received, we can't use it. The Hash being null is a flag + // that it's not yet valid. + nsCString hostport; + if (NS_FAILED(aURI->GetHostPort(hostport))) { + return NS_ERROR_FAILURE; + } + // create for the origin if it doesn't exist + Unused << mDictionaryCache.WithEntryHandle(hostport, [&](auto&& entry) { + auto& list = entry.OrInsertWith([&] { return new DictCacheList; }); + + for (const auto& dict : *list) { + if (dict->GetURI().Equals(aKey)) { + // We're overwriting an existing entry. It might be the same, of + // course + DICTIONARY_LOG(("Reusing dictionary for %s: %p", + PromiseFlatCString(dict->GetURI()).get(), dict)); + dict->Clear(); + *aDictEntry = do_AddRef(dict).take(); + return NS_OK; + } + } + // New entry + RefPtr<DictionaryCacheEntry> dict = + new DictionaryCacheEntry(aKey, aPattern, aId, aHash); + DICTIONARY_LOG(("New dictionary for %s: %p", PromiseFlatCString(aKey).get(), + dict.get())); + list->insertFront(dict); + *aDictEntry = do_AddRef(dict).take(); + return NS_OK; + }); + return NS_OK; +} + +nsresult DictionaryCache::AddEntry(nsIURI* aURI, + DictionaryCacheEntry* aDictEntry) { + // Note that normally we're getting an entry in and until all the data + // has been received, we can't use it. The Hash being null is a flag + // that it's not yet valid. + nsCString prepath; + if (NS_FAILED(aURI->GetPrePath(prepath))) { + return NS_ERROR_FAILURE; + } + // create for the origin if it doesn't exist + Unused << mDictionaryCache.WithEntryHandle(prepath, [&](auto&& entry) { + auto& list = entry.OrInsertWith([&] { return new DictCacheList; }); + + // Remove any entry for the same item + for (const auto& dict : *list) { + if (dict->GetURI().Equals(aDictEntry->GetURI())) { + // We're overwriting an existing entry. It might be the same, of + // course + dict->remove(); + return NS_OK; + } + } + + list->insertFront(aDictEntry); + return NS_OK; + }); + return NS_OK; +} + +nsresult DictionaryCache::RemoveEntry(nsIURI* aURI, const nsACString& aKey) { + nsCString prepath; + if (NS_FAILED(aURI->GetPrePath(prepath))) { + return NS_ERROR_FAILURE; + } + if (auto origin = mDictionaryCache.Lookup(prepath)) { + for (const auto& dict : *(origin->get())) { + if (dict->GetURI().Equals(aKey)) { + dict->remove(); + return NS_OK; + } + } + return NS_ERROR_FAILURE; + } + return NS_ERROR_FAILURE; +} + +// return an entry +already_AddRefed<DictionaryCacheEntry> DictionaryCache::GetDictionaryFor( + nsIURI* aURI) { + // Note: IETF 2.2.3 Multiple Matching Directories + // We need to return match-dest matches first + // If no match-dest, then the longest match + nsCString prepath; + if (NS_FAILED(aURI->GetPrePath(prepath))) { + return nullptr; + } + if (auto origin = mDictionaryCache.Lookup(prepath)) { + // Find the longest match + uint32_t longest = 0; + nsCString path; + RefPtr<DictionaryCacheEntry> result; + + aURI->GetPathQueryRef(path); + + for (const auto& dict : *(origin->get())) { + if (dict->Valid() && dict->Match(path, longest)) { + result = dict; + } + } + if (result) { + result->removeFrom(*(origin->get())); + origin->get()->insertFront(result); + } + return result.forget(); + } + // We don't have an entry at all. We need to check if there's an + // entry on disk for <origin>, unless we know we have all entries + // in the memory cache. For now, assume we have all entries, so a + // miss means no dictionaries. + + // XXX handle unknown origins by checking the disk cache. This means + // the lookup will have to be async (or return that we need to look + // it up in the caller + // Maybe pass in a lambda for completion when we have the origin + // If we know ALL origins with dictionaries in the cache and all dictionaries + // for each origin (i.e. if this isn't a cache, but an in-memory index), + // then this can be synchronous + + return nullptr; +} + +// Overall structure: +// Dictionary: +// DictionaryCache: +// OriginHashmap: +// LinkedList: DictionaryCacheEntry +// Data from cache (sometimes) +// +// Each origin is in the cache as a dictionary-origin entry. In that +// entry's metadata, we have an LRU-sorted list of dictionary entries to be able +// to build a DictionaryCacheEntry. +// When we offer a dictionary on a load, we'll start prefetching the data into +// the DictionaryCacheEntry for the item in the cache. When the response comes +// in, we'll either use it to decompress, or indicate we no longer care about +// the data. If no one cares about the data, we'll purge it from memory. +// Hold refs to the data in requests. When the only ref is in the +// DictionaryCacheEntry, purge the data. This could also be done via the +// InUse counter +// +// XXX be careful about thrashing the cache loading and purging, esp with RCWN. +// Note that this makes RCWN somewhat superfluous for loads that have a +// dictionary. +// XXX Perhaps allow a little dwell time in ram if not too large? + +// When a load comes in, we need to block decompressing it on having the data +// from the cache if it's dcb or dcz. +// XXX If the cache fails for some reason, we probably should consider +// re-fetching the data without Dictionary-Available. + +} // namespace net +} // namespace mozilla diff --git a/netwerk/cache2/Dictionary.h b/netwerk/cache2/Dictionary.h @@ -0,0 +1,167 @@ +/* vim: set ts=2 sts=2 et sw=2: */ +/* 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/. */ + +#ifndef mozilla_net_Dictionary_h +#define mozilla_net_Dictionary_h + +#include "nsCOMPtr.h" +#include "nsICacheEntry.h" +#include "nsICacheEntryOpenCallback.h" +#include "nsICacheStorageService.h" +#include "nsICacheStorageVisitor.h" +#include "nsICryptoHash.h" +#include "nsIInterfaceRequestor.h" +#include "nsIObserver.h" +#include "nsIStreamListener.h" +#include "mozilla/RefPtr.h" +#include "nsString.h" +#include "nsTArray.h" +#include "mozilla/TimeStamp.h" + +class nsICacheStorage; +class nsIIOService; +class nsILoadContextInfo; +class nsITimer; + +namespace mozilla { +namespace net { + +// Outstanding requests that offer this dictionary will hold a reference to it. +// If it's replaced (or removed) during the request, we would a) read the data +// into memory* b) unlink this from the origin in the memory cache. +// +// * or we wait for read-into-memory to finish, if we start reading entries +// when we send the request. +// +// When creating an entry from incoming data, we'll create it with no hash +// initially until the full data has arrived, then update the Hash. +class DictionaryCacheEntry final + : public LinkedListElement<RefPtr<DictionaryCacheEntry>> { + private: + ~DictionaryCacheEntry() { MOZ_ASSERT(mUsers == 0); } + + public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DictionaryCacheEntry) + DictionaryCacheEntry(const nsACString& aKey, const nsACString& aPattern, + const nsACString& aId, + const Maybe<nsCString>& aHash = Nothing()); + + // returns true if the pattern for the dictionary matches the path given + bool Match(const nsACString& aFilePath, uint32_t& aLongest); + + void Prefetch(); + + const nsACString& GetHash() const { + MOZ_ASSERT(NS_IsMainThread()); + return mHash; + } + + void SetHash(const nsACString& aHash) { + MOZ_ASSERT(NS_IsMainThread()); + mHash = aHash; + } + + bool Valid() const { return !mHash.IsEmpty(); } + + const nsCString& GetId() const { return mId; } + + // keep track of requests that may need the data + void InUse() { mUsers++; } + void UseCompleted() { + MOZ_ASSERT(mUsers > 0); + mUsers--; + // Purge mDictionaryData + // XXX Don't clear until we can reload it from disk + // mDictionaryData.clear(); + } + + const nsACString& GetURI() const { return mURI; } + + void Clear() { + MOZ_ASSERT(NS_IsMainThread()); + mHash.Truncate(0); + mDictionaryData.clear(); + } + + const Vector<uint8_t>& GetDictionary() const { return mDictionaryData; } + + size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const { + // XXX + return mallocSizeOf(this); + } + + private: + nsCString mURI; // URI (without ref) for the dictionary + nsCString mPattern; + nsCString mId; // max length 1024 + // dcb and dcz use type 'raw'. We're allowed to ignore types we don't + // understand, so we can fail to record a dictionary with type != 'raw' + // nsCString mType; + + // SHA-256 hash value ready to put into a header + nsCString mHash; + uint32_t mUsers{0}; // active requests using this entry + // in-memory copy of the entry to use to decompress incoming data + Vector<uint8_t> mDictionaryData; + + // for accumulating SHA-256 hash values for dictionaries + nsCOMPtr<nsICryptoHash> mCrypto; +}; + +// XXX Do we want to pre-read dictionaries into RAM at startup (lazily)? +// If we have all dictionaries stored in the cache, we don't need to do +// lookups to find if an origin has dictionaries or not, and we don't need to +// store empty entries (and LRU them). Downside would be if there are a LOT of +// origins with dictionaries, which may eventually happen, it would use more +// memory for rarely used origins. We could have a limit for dictionaries, and +// above that switch to partial caching and empty entries for origins without. + +// XXX Clear all dictionaries when cookies are cleared for a site + +using DictCacheList = AutoCleanLinkedList<RefPtr<DictionaryCacheEntry>>; + +// singleton class +class DictionaryCache final { + private: + DictionaryCache() {}; + ~DictionaryCache() {}; + + public: + NS_INLINE_DECL_REFCOUNTING(DictionaryCache) + static already_AddRefed<DictionaryCache> GetInstance(); + + nsresult Init(); + + nsresult AddEntry(nsIURI* aURI, const nsACString& aKey, + const nsACString& aPattern, const nsACString& aId, + const Maybe<nsCString>& aHash, + DictionaryCacheEntry** aDictEntry); + + nsresult AddEntry(nsIURI* aURI, DictionaryCacheEntry* aDictEntry); + + nsresult RemoveEntry(nsIURI* aURI, const nsACString& aKey); + + // return an entry + already_AddRefed<DictionaryCacheEntry> GetDictionaryFor(nsIURI* aURI); + + size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const { + // XXX + return mallocSizeOf(this); + } + + private: + // In-memory cache of dictionary entries. HashMap, keyed by origin, of + // Linked list (LRU order) of valid dictionaries for the origin. + // We keep empty entries in there to avoid hitting the disk cache to find out + // if there are dictionaries for an origin. + // Static assertions fire if we try to have a LinkedList directly in an + // nsTHashMap + nsTHashMap<nsCStringHashKey, UniquePtr<DictCacheList>> mDictionaryCache; +}; + +} // namespace net +} // namespace mozilla + +#endif // mozilla_net_Dictionary_h diff --git a/netwerk/cache2/moz.build b/netwerk/cache2/moz.build @@ -25,7 +25,10 @@ EXPORTS += [ "CacheStorageService.h", ] -EXPORTS.mozilla.net += ["CachePurgeLock.h"] +EXPORTS.mozilla.net += [ + "CachePurgeLock.h", + "Dictionary.h", +] SOURCES += [ "CacheStorage.cpp", @@ -50,6 +53,7 @@ UNIFIED_SOURCES += [ "CacheLog.cpp", "CacheObserver.cpp", "CacheStorageService.cpp", + "Dictionary.cpp", ] if CONFIG["MOZ_WIDGET_TOOLKIT"] != "android": diff --git a/netwerk/protocol/http/HttpBaseChannel.cpp b/netwerk/protocol/http/HttpBaseChannel.cpp @@ -395,7 +395,7 @@ nsresult HttpBaseChannel::Init(nsIURI* aURI, uint32_t aCaps, } rv = gHttpHandler->AddStandardRequestHeaders( - &mRequestHead, isHTTPS, contentPolicyType, + &mRequestHead, isHTTPS, aURI, contentPolicyType, nsContentUtils::ShouldResistFingerprinting(this, RFPTarget::HttpUserAgent)); if (NS_FAILED(rv)) return rv; diff --git a/netwerk/protocol/http/nsHttpAtomList.h b/netwerk/protocol/http/nsHttpAtomList.h @@ -29,6 +29,7 @@ HTTP_ATOM(Alternate_Service_Used, "Alt-Used") HTTP_ATOM(Assoc_Req, "Assoc-Req") HTTP_ATOM(Authentication, "Authentication") HTTP_ATOM(Authorization, "Authorization") +HTTP_ATOM(Available_Dictionary, "Available-Dictionary") HTTP_ATOM(Cache_Control, "Cache-Control") HTTP_ATOM(Connection, "Connection") HTTP_ATOM(Content_Disposition, "Content-Disposition") @@ -48,6 +49,7 @@ HTTP_ATOM(Date, "Date") HTTP_ATOM(DAV, "DAV") HTTP_ATOM(Depth, "Depth") HTTP_ATOM(Destination, "Destination") +HTTP_ATOM(Dictionary_Id, "Dictionary-Id") HTTP_ATOM(DoNotTrack, "DNT") HTTP_ATOM(ETag, "Etag") HTTP_ATOM(Expect, "Expect") diff --git a/netwerk/protocol/http/nsHttpHandler.cpp b/netwerk/protocol/http/nsHttpHandler.cpp @@ -359,6 +359,8 @@ nsresult nsHttpHandler::Init() { // This preference is only used in parent process. if (!IsNeckoChild()) { if (XRE_IsParentProcess()) { + mDictionaryCache = DictionaryCache::GetInstance(); + std::bitset<3> usageOfHTTPSRRPrefs; usageOfHTTPSRRPrefs[0] = StaticPrefs::network_dns_upgrade_with_https_rr(); usageOfHTTPSRRPrefs[1] = @@ -651,8 +653,53 @@ nsresult nsHttpHandler::InitConnectionMgr() { // We're using RequestOverride because this can get called when these are // set by Fetch from the old request +// only call if we're https! +nsresult nsHttpHandler::AddAcceptAndDictionaryHeaders( + nsIURI* aURI, nsHttpRequestHead* aRequest) { + LOG(("Adding Dictionary headers")); + nsresult rv; + // The dictionary info may require us to check the cache. + // XXX This would require that AddAcceptAndDictionaryHeaders be effectively + // async, perhaps by passing a lambda in. + RefPtr<DictionaryCacheEntry> dict = + mDictionaryCache ? mDictionaryCache->GetDictionaryFor(aURI) : nullptr; + if (dict) { + rv = + aRequest->SetHeader(nsHttp::Accept_Encoding, mDictionaryAcceptEncodings, + false, nsHttpHeaderArray::eVarietyRequestOverride); + if (NS_FAILED(rv)) { + return rv; + } + + LOG(("Found dictionary %s", PromiseFlatCString(dict->GetHash()).get())); + rv = aRequest->SetHeader(nsHttp::Available_Dictionary, dict->GetHash(), + false, nsHttpHeaderArray::eVarietyRequestOverride); + if (NS_FAILED(rv)) { + return rv; + } + if (!dict->GetId().IsEmpty()) { + rv = aRequest->SetHeader(nsHttp::Dictionary_Id, dict->GetId(), false, + nsHttpHeaderArray::eVarietyRequestOverride); + if (NS_FAILED(rv)) { + return rv; + } + } + // Need to retain access to the dictionary until the request completes. + // Note that this includes if the dictionary we offered gets replaced + // by another request while we're waiting for a response; in that case we + // need to read in a copy of the dictionary into memory before overwriting + // it and store in dict temporarily. + dict->InUse(); + aRequest->SetDictionary(dict); + } else { + rv = aRequest->SetHeader(nsHttp::Accept_Encoding, mHttpsAcceptEncodings, + false, nsHttpHeaderArray::eVarietyRequestOverride); + } + return rv; +} + nsresult nsHttpHandler::AddStandardRequestHeaders( - nsHttpRequestHead* request, bool isSecure, + nsHttpRequestHead* request, bool isSecure, nsIURI* aURI, ExtContentPolicyType aContentPolicyType, bool aShouldResistFingerprinting) { nsresult rv; @@ -701,8 +748,7 @@ nsresult nsHttpHandler::AddStandardRequestHeaders( // Add the "Accept-Encoding" header if (isSecure) { - rv = request->SetHeader(nsHttp::Accept_Encoding, mHttpsAcceptEncodings, - false, nsHttpHeaderArray::eVarietyRequestDefault); + rv = AddAcceptAndDictionaryHeaders(aURI, request); } else { rv = request->SetHeader(nsHttp::Accept_Encoding, mHttpAcceptEncodings, false, nsHttpHeaderArray::eVarietyRequestDefault); @@ -723,8 +769,7 @@ nsresult nsHttpHandler::AddEncodingHeaders(nsHttpRequestHead* request, // Add the "Accept-Encoding" header and any dictionary headers nsresult rv; if (isSecure) { - rv = request->SetHeader(nsHttp::Accept_Encoding, mHttpsAcceptEncodings, - false, nsHttpHeaderArray::eVarietyRequestOverride); + rv = AddAcceptAndDictionaryHeaders(aURI, request); } else { rv = request->SetHeader(nsHttp::Accept_Encoding, mHttpAcceptEncodings, false, nsHttpHeaderArray::eVarietyRequestOverride); @@ -1432,7 +1477,7 @@ void nsHttpHandler::PrefsChanged(const char* pref) { nsAutoCString acceptEncodings; rv = Preferences::GetCString(HTTP_PREF("accept-encoding"), acceptEncodings); if (NS_SUCCEEDED(rv)) { - rv = SetAcceptEncodings(acceptEncodings.get(), false); + rv = SetAcceptEncodings(acceptEncodings.get(), false, false); MOZ_ASSERT(NS_SUCCEEDED(rv)); } } @@ -1442,7 +1487,17 @@ void nsHttpHandler::PrefsChanged(const char* pref) { rv = Preferences::GetCString(HTTP_PREF("accept-encoding.secure"), acceptEncodings); if (NS_SUCCEEDED(rv)) { - rv = SetAcceptEncodings(acceptEncodings.get(), true); + rv = SetAcceptEncodings(acceptEncodings.get(), true, false); + MOZ_ASSERT(NS_SUCCEEDED(rv)); + } + } + + if (PREF_CHANGED(HTTP_PREF("accept-encoding.dictionary"))) { + nsAutoCString acceptDictionaryEncodings; + rv = Preferences::GetCString(HTTP_PREF("accept-encoding.dictionary"), + acceptDictionaryEncodings); + if (NS_SUCCEEDED(rv)) { + rv = SetAcceptEncodings(acceptDictionaryEncodings.get(), true, true); MOZ_ASSERT(NS_SUCCEEDED(rv)); } } @@ -2018,8 +2073,10 @@ nsresult nsHttpHandler::SetAcceptLanguages() { } nsresult nsHttpHandler::SetAcceptEncodings(const char* aAcceptEncodings, - bool isSecure) { - if (isSecure) { + bool isSecure, bool isDictionary) { + if (isDictionary) { + mDictionaryAcceptEncodings = aAcceptEncodings; + } else if (isSecure) { mHttpsAcceptEncodings = aAcceptEncodings; } else { // use legacy list if a secure override is not specified diff --git a/netwerk/protocol/http/nsHttpHandler.h b/netwerk/protocol/http/nsHttpHandler.h @@ -21,6 +21,7 @@ #include "nsString.h" #include "nsCOMPtr.h" #include "nsWeakReference.h" +#include "mozilla/net/Dictionary.h" #include "nsIHttpProtocolHandler.h" #include "nsIObserver.h" @@ -116,8 +117,10 @@ class nsHttpHandler final : public nsIHttpProtocolHandler, static already_AddRefed<nsHttpHandler> GetInstance(); + [[nodiscard]] nsresult AddAcceptAndDictionaryHeaders( + nsIURI* aURI, nsHttpRequestHead* aRequest); [[nodiscard]] nsresult AddStandardRequestHeaders( - nsHttpRequestHead*, bool isSecure, + nsHttpRequestHead*, bool isSecure, nsIURI* aURI, ExtContentPolicyType aContentPolicyType, bool aShouldResistFingerprinting); [[nodiscard]] nsresult AddEncodingHeaders(nsHttpRequestHead* request, @@ -528,7 +531,8 @@ class nsHttpHandler final : public nsIHttpProtocolHandler, void PrefsChanged(const char* pref); [[nodiscard]] nsresult SetAcceptLanguages(); - [[nodiscard]] nsresult SetAcceptEncodings(const char*, bool mIsSecure); + [[nodiscard]] nsresult SetAcceptEncodings(const char*, bool aIsSecure, + bool aDictionary); [[nodiscard]] nsresult InitConnectionMgr(); @@ -565,6 +569,9 @@ class nsHttpHandler final : public nsIHttpProtocolHandler, UniquePtr<AltSvcCache> mAltSvcCache; + // Pointer to DictionaryCache singleton + RefPtr<DictionaryCache> mDictionaryCache; + // // prefs // @@ -625,6 +632,7 @@ class nsHttpHandler final : public nsIHttpProtocolHandler, nsCString mAcceptLanguages; nsCString mHttpAcceptEncodings; nsCString mHttpsAcceptEncodings; + nsCString mDictionaryAcceptEncodings; nsCString mDefaultSocketType; diff --git a/netwerk/protocol/http/nsHttpRequestHead.cpp b/netwerk/protocol/http/nsHttpRequestHead.cpp @@ -8,6 +8,7 @@ #include "nsHttpRequestHead.h" #include "nsIHttpHeaderVisitor.h" +#include "mozilla/net/Dictionary.h" //----------------------------------------------------------------------------- // nsHttpRequestHead @@ -98,6 +99,11 @@ void nsHttpRequestHead::SetPath(const nsACString& s) { mPath = s; } +void nsHttpRequestHead::SetDictionary(DictionaryCacheEntry* aDict) { + RecursiveMutexAutoLock mon(mRecursiveMutex); // XXX necessary? + mDict = aDict; +} + uint32_t nsHttpRequestHead::HeaderCount() { RecursiveMutexAutoLock mon(mRecursiveMutex); return mHeaders.Count(); diff --git a/netwerk/protocol/http/nsHttpRequestHead.h b/netwerk/protocol/http/nsHttpRequestHead.h @@ -23,6 +23,8 @@ struct ParamTraits; namespace mozilla { namespace net { +class DictionaryCacheEntry; + //----------------------------------------------------------------------------- // nsHttpRequestHead represents the request line and headers from an HTTP // request. @@ -54,6 +56,8 @@ class nsHttpRequestHead { void SetVersion(HttpVersion version); void SetRequestURI(const nsACString& s); void SetPath(const nsACString& s); + // keep a ref to the dictionary we offered, if any + void SetDictionary(DictionaryCacheEntry* aDict); uint32_t HeaderCount(); // Using this function it is possible to itereate through all headers @@ -136,6 +140,8 @@ class nsHttpRequestHead { nsCString mRequestURI MOZ_GUARDED_BY(mRecursiveMutex); nsCString mPath MOZ_GUARDED_BY(mRecursiveMutex); + RefPtr<DictionaryCacheEntry> mDict MOZ_GUARDED_BY(mRecursiveMutex); + nsCString mOrigin MOZ_GUARDED_BY(mRecursiveMutex); ParsedMethodType mParsedMethod MOZ_GUARDED_BY(mRecursiveMutex){kMethod_Get}; bool mHTTPS MOZ_GUARDED_BY(mRecursiveMutex){false};