commit 115be236bf2b8aff98f8c885dcf2ea3886f174a7
parent 62d7f7168e4bb8d973fc73fff02ecf36f68347a7
Author: Randell Jesup <rjesup@mozilla.com>
Date: Tue, 7 Oct 2025 14:07:07 +0000
Bug 1950965: about:cache support for Compression Dictionaries and cleanup r=necko-reviewers,valentin
Differential Revision: https://phabricator.services.mozilla.com/D258168
Diffstat:
7 files changed, 97 insertions(+), 51 deletions(-)
diff --git a/netwerk/cache2/CacheEntry.cpp b/netwerk/cache2/CacheEntry.cpp
@@ -1095,10 +1095,9 @@ nsresult CacheEntry::GetOnStopTime(uint64_t* aTime) {
return mFile->GetOnStopTime(aTime);
}
-nsresult CacheEntry::GetReady(bool* aReady) {
+nsresult CacheEntry::GetReadyOrRevalidating(bool* aReady) {
mozilla::MutexAutoLock lock(mLock);
- // XXX REVALIDATING?
- *aReady = mState == READY;
+ *aReady = (mState == READY || mState == REVALIDATING);
return NS_OK;
}
diff --git a/netwerk/cache2/CacheEntry.h b/netwerk/cache2/CacheEntry.h
@@ -76,7 +76,7 @@ class CacheEntry final : public nsIRunnable,
nsresult SetExpirationTime(uint32_t expirationTime);
nsresult GetOnStartTime(uint64_t* aTime);
nsresult GetOnStopTime(uint64_t* aTime);
- nsresult GetReady(bool* aReady);
+ nsresult GetReadyOrRevalidating(bool* aReady);
nsresult SetNetworkTimes(uint64_t onStartTime, uint64_t onStopTime);
nsresult SetContentType(uint8_t aContentType);
nsresult ForceValidFor(uint32_t aSecondsToTheFuture);
@@ -472,8 +472,8 @@ class CacheEntryHandle final : public nsICacheEntry {
NS_IMETHOD GetOnStopTime(uint64_t* aOnStopTime) override {
return mEntry->GetOnStopTime(aOnStopTime);
}
- NS_IMETHOD GetReady(bool* aReady) override {
- return mEntry->GetReady(aReady);
+ NS_IMETHOD GetReadyOrRevalidating(bool* aReady) override {
+ return mEntry->GetReadyOrRevalidating(aReady);
}
NS_IMETHOD SetNetworkTimes(uint64_t onStartTime,
uint64_t onStopTime) override {
diff --git a/netwerk/cache2/CacheStorageService.cpp b/netwerk/cache2/CacheStorageService.cpp
@@ -1628,7 +1628,9 @@ nsresult CacheStorageService::AddStorageEntry(
}
if (entryExists && (aFlags & nsICacheStorage::OPEN_COMPLETE_ONLY)) {
bool ready = false;
- entry->GetReady(&ready);
+ // We're looking for complete files, even if they're being revalidated
+ // (for dictionaries)
+ entry->GetReadyOrRevalidating(&ready);
if (!ready) {
return NS_ERROR_CACHE_KEY_NOT_FOUND;
}
diff --git a/netwerk/cache2/Dictionary.cpp b/netwerk/cache2/Dictionary.cpp
@@ -27,9 +27,12 @@
#include "nsILoadGroup.h"
#include "nsIObserverService.h"
#include "nsIURI.h"
+#include "nsIURIMutator.h"
#include "nsInputStreamPump.h"
#include "nsNetUtil.h"
#include "nsServiceManagerUtils.h"
+#include "nsSimpleURI.h"
+#include "nsStandardURL.h"
#include "nsStreamUtils.h"
#include "nsString.h"
#include "nsThreadUtils.h"
@@ -73,6 +76,16 @@ LazyLogModule gDictionaryLog("CompressionDictionaries");
StaticRefPtr<DictionaryCache> gDictionaryCache;
nsCOMPtr<nsICacheStorage> DictionaryCache::sCacheStorage;
+// about:cache gets upset about entries that don't fit URL specs, so we need
+// to add the trailing '/' to GetPrePath()
+static nsresult GetDictPath(nsIURI* aURI, nsACString& aPrePath) {
+ if (NS_FAILED(aURI->GetPrePath(aPrePath))) {
+ return NS_ERROR_FAILURE;
+ }
+ aPrePath += '/';
+ return NS_OK;
+}
+
DictionaryCacheEntry::DictionaryCacheEntry(const char* aKey) { mURI = aKey; }
DictionaryCacheEntry::DictionaryCacheEntry(const nsACString& aURI,
@@ -189,7 +202,8 @@ nsresult DictionaryCacheEntry::Prefetch(nsILoadContextInfo* aLoadContextInfo,
if (NS_FAILED(cacheStorage->AsyncOpenURIString(
mURI, ""_ns,
nsICacheStorage::OPEN_READONLY |
- nsICacheStorage::OPEN_COMPLETE_ONLY,
+ nsICacheStorage::OPEN_COMPLETE_ONLY |
+ nsICacheStorage::CHECK_MULTITHREADED,
this)) ||
mNotCached) {
// For some reason the cache no longer has this entry; fail Prefetch
@@ -481,6 +495,8 @@ void DictionaryCacheEntry::WriteOnHash() {
// just saying with have this specific set of bits with this hash available
// to use as a dictionary.
+// This may be called on a random thread due to
+// nsICacheStorage::CHECK_MULTITHREADED
NS_IMETHODIMP
DictionaryCacheEntry::OnCacheEntryCheck(nsICacheEntry* aEntry,
uint32_t* result) {
@@ -533,8 +549,9 @@ class DictionaryOriginReader final : public nsICacheEntryOpenCallback,
DictionaryOriginReader() {}
- // if aOrigin is nullptr, we're just reading; if it's set then we're adding
- // a Dictionary to a maybe-existing Origin
+ // If aOrigin is nullptr, we're just reading; if it's set then we're adding
+ // a Dictionary to a maybe-existing Origin and we need to create it if it
+ // doesn't exist yet.
void Start(DictionaryOrigin* aOrigin, nsACString& aKey, nsIURI* aURI,
DictionaryCache* aCache,
const std::function<nsresult(DictionaryCacheEntry*)>& aCallback) {
@@ -552,8 +569,10 @@ class DictionaryOriginReader final : public nsICacheEntryOpenCallback,
DictionaryCache::sCacheStorage->AsyncOpenURIString(
aKey, META_DICTIONARY_PREFIX,
aOrigin
- ? nsICacheStorage::OPEN_NORMALLY
- : nsICacheStorage::OPEN_READONLY | nsICacheStorage::OPEN_SECRETLY,
+ ? nsICacheStorage::OPEN_NORMALLY |
+ nsICacheStorage::CHECK_MULTITHREADED
+ : nsICacheStorage::OPEN_READONLY | nsICacheStorage::OPEN_SECRETLY |
+ nsICacheStorage::CHECK_MULTITHREADED,
this);
}
@@ -574,6 +593,8 @@ NS_IMPL_ISUPPORTS(DictionaryOriginReader, nsICacheEntryOpenCallback,
// nsICacheEntryOpenCallback implementation
//-----------------------------------------------------------------------------
+// This may be called on a random thread due to
+// nsICacheStorage::CHECK_MULTITHREADED
NS_IMETHODIMP DictionaryOriginReader::OnCacheEntryCheck(nsICacheEntry* entry,
uint32_t* result) {
*result = nsICacheEntryOpenCallback::ENTRY_WANTED;
@@ -591,7 +612,7 @@ NS_IMETHODIMP DictionaryOriginReader::OnCacheEntryAvailable(
this, aCacheEntry));
if (!aCacheEntry) {
- // Didn't have any dictionaries for this origin
+ // Didn't have any dictionaries for this origin, and must have been readonly
(mCallback)(nullptr);
return NS_OK;
}
@@ -611,7 +632,7 @@ NS_IMETHODIMP DictionaryOriginReader::OnCacheEntryAvailable(
}
nsCString prepath;
- if (NS_FAILED(mURI->GetPrePath(prepath))) {
+ if (NS_FAILED(GetDictPath(mURI, prepath))) {
(mCallback)(nullptr);
return NS_ERROR_FAILURE;
}
@@ -716,7 +737,7 @@ already_AddRefed<DictionaryCacheEntry> DictionaryCache::AddEntry(
// 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))) {
+ if (NS_FAILED(GetDictPath(aURI, prepath))) {
return nullptr;
}
// create for the origin if it doesn't exist
@@ -726,7 +747,7 @@ already_AddRefed<DictionaryCacheEntry> DictionaryCache::AddEntry(
RefPtr<DictionaryOrigin> origin = new DictionaryOrigin(prepath, nullptr);
// Create a cache entry for this if it doesn't exist. Note
// that the entry we're adding will need to be saved later once
- // we have the entry
+ // we have the cache entry
// This creates a cycle until the dictionary is removed from the cache
aDictEntry->SetOrigin(origin);
@@ -754,7 +775,7 @@ already_AddRefed<DictionaryCacheEntry> DictionaryCache::AddEntry(
nsresult DictionaryCache::RemoveEntry(nsIURI* aURI, const nsACString& aKey) {
nsCString prepath;
- if (NS_FAILED(aURI->GetPrePath(prepath))) {
+ if (NS_FAILED(GetDictPath(aURI, prepath))) {
return NS_ERROR_FAILURE;
}
DICTIONARY_LOG(("Dictionary RemoveEntry for %s : %s", prepath.get(),
@@ -783,8 +804,6 @@ void DictionaryCache::RemoveDictionaryFor(const nsACString& aKey) {
// Remove a dictionary if it exists for the key given
void DictionaryCache::RemoveDictionary(const nsACString& aKey) {
- DICTIONARY_LOG(
- ("Removing dictionary for %s", PromiseFlatCString(aKey).get()));
nsCString enhance;
nsCString urlstring;
nsCOMPtr<nsILoadContextInfo> info =
@@ -794,14 +813,17 @@ void DictionaryCache::RemoveDictionary(const nsACString& aKey) {
DICTIONARY_LOG(("DictionaryCache::RemoveDictionary() - Cannot parse key!"));
return;
}
- nsCOMPtr<nsIURI> url;
- nsresult rv = NS_NewURI(getter_AddRefs(url), urlstring);
- if (NS_SUCCEEDED(rv)) {
- nsCString prepath;
- if (NS_SUCCEEDED(url->GetPrePath(prepath))) {
- if (auto origin = mDictionaryCache.Lookup(prepath)) {
- origin.Data()->RemoveEntry(urlstring);
- }
+ DICTIONARY_LOG(("Removing dictionary for %s, enhance %s", urlstring.get(),
+ enhance.get()));
+
+ nsCOMPtr<nsIURI> uri;
+ if (NS_FAILED(NS_NewURI(getter_AddRefs(uri), urlstring))) {
+ return;
+ }
+ nsAutoCString prepath;
+ if (NS_SUCCEEDED(GetDictPath(uri, prepath))) {
+ if (auto origin = mDictionaryCache.Lookup(prepath)) {
+ origin.Data()->RemoveEntry(urlstring);
}
}
}
@@ -872,7 +894,7 @@ void DictionaryCache::GetDictionaryFor(
// We need to return match-dest matches first
// If no match-dest, then the longest match
nsCString prepath;
- if (NS_FAILED(aURI->GetPrePath(prepath))) {
+ if (NS_FAILED(GetDictPath(aURI, prepath))) {
(aCallback)(nullptr);
return;
}
@@ -898,15 +920,28 @@ void DictionaryCache::GetDictionaryFor(
return;
}
- // To keep track of the callback, we need a new object to get the
- // OnCacheEntryAvailable can resolve the callback. It will keep a reference
- // to itself until it call aCallback after the cache read has resolved
- DICTIONARY_LOG(
- ("Reading %s for dictionary entries", PromiseFlatCString(prepath).get()));
- RefPtr<DictionaryOriginReader> reader = new DictionaryOriginReader();
- // After Start(), if we drop this ref reader will kill itself on
- // completion; it holds a self-ref
- reader->Start(nullptr, prepath, aURI, this, aCallback);
+ // Sync check to see if the entry exists
+ bool exists;
+ nsCOMPtr<nsIURI> prepathURI;
+
+ if (NS_SUCCEEDED(NS_MutateURI(new net::nsStandardURL::Mutator())
+ .SetSpec(prepath)
+ .Finalize(prepathURI)) &&
+ NS_SUCCEEDED(
+ sCacheStorage->Exists(prepathURI, META_DICTIONARY_PREFIX, &exists)) &&
+ exists) {
+ // To keep track of the callback, we need a new object to get the
+ // OnCacheEntryAvailable can resolve the callback. It will keep a reference
+ // to itself until it call aCallback after the cache read has resolved
+ DICTIONARY_LOG(("Reading %s for dictionary entries", prepath.get()));
+ RefPtr<DictionaryOriginReader> reader = new DictionaryOriginReader();
+ // After Start(), if we drop this ref reader will kill itself on
+ // completion; it holds a self-ref
+ reader->Start(nullptr, prepath, aURI, this, aCallback);
+ } else {
+ // No dictionaries for origin
+ (aCallback)(nullptr);
+ }
}
//-----------------------------------------------------------------------------
// DictionaryOrigin
@@ -915,7 +950,22 @@ NS_IMPL_ISUPPORTS(DictionaryOrigin, nsICacheEntryMetaDataVisitor)
void DictionaryOrigin::Write(DictionaryCacheEntry* aDictEntry) {
DICTIONARY_LOG(("DictionaryOrigin::Write %s %p", mOrigin.get(), aDictEntry));
- aDictEntry->Write(mEntry);
+ if (mEntry) {
+ aDictEntry->Write(mEntry);
+ } else {
+ // Write it once DictionaryOriginReader creates the entry
+ mDeferredWrites = true;
+ }
+}
+
+void DictionaryOrigin::SetCacheEntry(nsICacheEntry* aEntry) {
+ mEntry = aEntry;
+ if (mDeferredWrites) {
+ for (auto& entry : mEntries) {
+ Write(entry);
+ }
+ }
+ mDeferredWrites = false;
}
already_AddRefed<DictionaryCacheEntry> DictionaryOrigin::AddEntry(
diff --git a/netwerk/cache2/Dictionary.h b/netwerk/cache2/Dictionary.h
@@ -235,7 +235,7 @@ class DictionaryOrigin : public nsICacheEntryMetaDataVisitor {
DictionaryOrigin(const nsACString& aOrigin, nsICacheEntry* aEntry)
: mOrigin(aOrigin), mEntry(aEntry) {}
- void SetCacheEntry(nsICacheEntry* aEntry) { mEntry = aEntry; }
+ void SetCacheEntry(nsICacheEntry* aEntry);
void Write(DictionaryCacheEntry* aDictEntry);
already_AddRefed<DictionaryCacheEntry> AddEntry(
DictionaryCacheEntry* aDictEntry, bool aNewEntry);
@@ -254,6 +254,8 @@ class DictionaryOrigin : public nsICacheEntryMetaDataVisitor {
// Dictionaries currently being received. Once these get a Hash, move to
// mEntries
DictCacheList mPendingEntries;
+ // Write out all entries once we have a cacheentry
+ bool mDeferredWrites{false};
};
class DictionaryOriginReader;
diff --git a/netwerk/cache2/nsICacheEntry.idl b/netwerk/cache2/nsICacheEntry.idl
@@ -60,9 +60,9 @@ interface nsICacheEntry : nsISupports
readonly attribute boolean persistent;
/**
- * Get if the cache file is READY
+ * Get if the cache file is READY or REVALIDATING
*/
- readonly attribute boolean ready;
+ readonly attribute boolean readyOrRevalidating;
/**
* Get the number of times the cache entry has been opened.
diff --git a/netwerk/protocol/http/nsHttpHandler.cpp b/netwerk/protocol/http/nsHttpHandler.cpp
@@ -668,16 +668,6 @@ nsresult nsHttpHandler::AddAcceptAndDictionaryHeaders(
aCallback](DictionaryCacheEntry* aDict) {
nsresult rv;
if (aDict) {
- rv = aRequest->SetHeader(
- nsHttp::Accept_Encoding, self->mDictionaryAcceptEncodings,
- false, nsHttpHeaderArray::eVarietyRequestOverride);
- if (NS_FAILED(rv)) {
- (aCallback)(nullptr);
- return rv;
- }
- LOG_DICTIONARIES(("Setting Accept-Encoding: %s",
- self->mDictionaryAcceptEncodings.get()));
-
nsAutoCStringN<64> encodedHash =
":"_ns + aDict->GetHash() + ":"_ns;
@@ -715,6 +705,9 @@ nsresult nsHttpHandler::AddAcceptAndDictionaryHeaders(
return rv;
}
}
+ return aRequest->SetHeader(
+ nsHttp::Accept_Encoding, self->mDictionaryAcceptEncodings,
+ false, nsHttpHeaderArray::eVarietyRequestOverride);
}
return NS_OK;
}