commit 19ab2d5af81085a112717f5a65baa17c4af205d0
parent e31fee540044734ce7f0f20fcc3f1c67e8f4137d
Author: Randell Jesup <rjesup@mozilla.com>
Date: Tue, 7 Oct 2025 14:07:02 +0000
Bug 1917965: Add initial Dictionary in-memory structures r=necko-reviewers,kershaw
Differential Revision: https://phabricator.services.mozilla.com/D258121
Diffstat:
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};