commit aabfdecb3a623752e5a973d751d690ddd961a8bc
parent c9e18bdf3b9c591c2cac70f7880802b68a61081e
Author: Randell Jesup <rjesup@mozilla.com>
Date: Wed, 1 Oct 2025 18:45:59 +0000
Bug 1917974: Add OPEN_COMPLETE_ONLY and use it for Compression Dictionary Prefetch r=necko-reviewers,valentin
Differential Revision: https://phabricator.services.mozilla.com/D258166
Diffstat:
7 files changed, 51 insertions(+), 7 deletions(-)
diff --git a/netwerk/cache2/CacheEntry.cpp b/netwerk/cache2/CacheEntry.cpp
@@ -1095,6 +1095,13 @@ nsresult CacheEntry::GetOnStopTime(uint64_t* aTime) {
return mFile->GetOnStopTime(aTime);
}
+nsresult CacheEntry::GetReady(bool* aReady) {
+ mozilla::MutexAutoLock lock(mLock);
+ // XXX REVALIDATING?
+ *aReady = mState == READY;
+ return NS_OK;
+}
+
nsresult CacheEntry::SetNetworkTimes(uint64_t aOnStartTime,
uint64_t aOnStopTime) {
if (NS_SUCCEEDED(mFileStatus)) {
diff --git a/netwerk/cache2/CacheEntry.h b/netwerk/cache2/CacheEntry.h
@@ -76,6 +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 SetNetworkTimes(uint64_t onStartTime, uint64_t onStopTime);
nsresult SetContentType(uint8_t aContentType);
nsresult ForceValidFor(uint32_t aSecondsToTheFuture);
@@ -471,6 +472,9 @@ 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 SetNetworkTimes(uint64_t onStartTime,
uint64_t onStopTime) override {
return mEntry->SetNetworkTimes(onStartTime, onStopTime);
diff --git a/netwerk/cache2/CacheStorageService.cpp b/netwerk/cache2/CacheStorageService.cpp
@@ -1606,6 +1606,13 @@ nsresult CacheStorageService::AddStorageEntry(
StaticPrefs::network_cache_bug1708673()) {
return NS_ERROR_CACHE_KEY_NOT_FOUND;
}
+ if (entryExists && (aFlags & nsICacheStorage::OPEN_COMPLETE_ONLY)) {
+ bool ready = false;
+ entry->GetReady(&ready);
+ if (!ready) {
+ return NS_ERROR_CACHE_KEY_NOT_FOUND;
+ }
+ }
bool replace = aFlags & nsICacheStorage::OPEN_TRUNCATE;
diff --git a/netwerk/cache2/Dictionary.cpp b/netwerk/cache2/Dictionary.cpp
@@ -94,6 +94,10 @@ bool DictionaryCacheEntry::Match(const nsACString& aFilePath,
// We don't have the file yet
return false;
}
+ if (mNotCached) {
+ // Not actually in the cache
+ return false;
+ }
// Not worth checking if we wouldn't use it
// XXX Check match-dest
if (mPattern.Length() > aLongest) {
@@ -157,7 +161,7 @@ nsresult DictionaryCacheEntry::Prefetch(nsILoadContextInfo* aLoadContextInfo,
if (mWaitingPrefetch.IsEmpty()) {
if (!mDictionaryDataComplete) {
// We haven't requested it yet from the Cache and don't have it in memory
- // already
+ // already.
// We can't use sCacheStorage because we need the correct LoadContextInfo
nsCOMPtr<nsICacheStorageService> cacheStorageService(
components::CacheStorage::Service());
@@ -172,10 +176,20 @@ nsresult DictionaryCacheEntry::Prefetch(nsILoadContextInfo* aLoadContextInfo,
aShouldSuspend = false;
return NS_ERROR_FAILURE;
}
+ // If the file isn't available in the cache, AsyncOpenURIString()
+ // will synchronously make a callback to OnCacheEntryAvailable() with
+ // nullptr. We can key off that to fail Prefetch(), and also to
+ // remove ourselves from the origin.
if (NS_FAILED(cacheStorage->AsyncOpenURIString(
- mURI, ""_ns, nsICacheStorage::OPEN_READONLY, this))) {
- // For some reason the cache no longer has this entry;
+ mURI, ""_ns,
+ nsICacheStorage::OPEN_READONLY |
+ nsICacheStorage::OPEN_COMPLETE_ONLY,
+ this)) ||
+ mNotCached) {
+ // For some reason the cache no longer has this entry; fail Prefetch
+ // and also remove this from our origin
aShouldSuspend = false;
+ // XXX Remove from origin
return NS_ERROR_FAILURE;
}
mWaitingPrefetch.AppendElement(aFunc);
@@ -492,11 +506,9 @@ DictionaryCacheEntry::OnCacheEntryAvailable(nsICacheEntry* entry, bool isNew,
}
DICTIONARY_LOG(("Waiting for data"));
} else {
- // This shouldn't happen.... we should only be fetching something we know
- // is in the cache.
- // Presumably status is something like NS_ERROR_FILE_NOT_FOUND
// XXX Error out any channels waiting on this cache entry. Also,
- // remove the dictionary entry from the origin?
+ // remove the dictionary entry from the origin.
+ mNotCached = true; // For Prefetch()
DICTIONARY_LOG(("Prefetched cache entry not available!"));
}
diff --git a/netwerk/cache2/Dictionary.h b/netwerk/cache2/Dictionary.h
@@ -64,6 +64,7 @@ class DictionaryCacheEntry final : public nsICacheEntryOpenCallback,
// returns true if the pattern for the dictionary matches the path given
bool Match(const nsACString& aFilePath, uint32_t& aLongest);
+ // This will fail if the cache entry is no longer available.
// Start reading the cache entry into memory and call completion
// function when done
nsresult Prefetch(nsILoadContextInfo* aLoadContextInfo, bool& aShouldSuspend,
@@ -200,6 +201,9 @@ class DictionaryCacheEntry final : public nsICacheEntryOpenCallback,
// We should suspend until the ond entry has been read
bool mShouldSuspend{false};
+ // The cache entry has been removed
+ bool mNotCached{false};
+
// We're blocked from taking over for the old entry for now
bool mBlocked{false};
};
diff --git a/netwerk/cache2/nsICacheEntry.idl b/netwerk/cache2/nsICacheEntry.idl
@@ -60,6 +60,11 @@ interface nsICacheEntry : nsISupports
readonly attribute boolean persistent;
/**
+ * Get if the cache file is READY
+ */
+ readonly attribute boolean ready;
+
+ /**
* Get the number of times the cache entry has been opened.
*/
readonly attribute uint32_t fetchCount;
diff --git a/netwerk/cache2/nsICacheStorage.idl b/netwerk/cache2/nsICacheStorage.idl
@@ -63,6 +63,11 @@ interface nsICacheStorage : nsISupports
const uint32_t OPEN_INTERCEPTED = 1 << 6;
/**
+ * Only open an existing entry which is complete (i.e. not being written)
+ */
+ const uint32_t OPEN_COMPLETE_ONLY = 1 << 7;
+
+ /**
* Asynchronously opens a cache entry for the specified URI.
* Result is fetched asynchronously via the callback.
*