commit 06974fe1c159fa16f26a317af39023c39b04e618
parent 77792ab3e3180d04ce426dec9b6bb4f6ad30f2d2
Author: Randell Jesup <rjesup@mozilla.com>
Date: Tue, 7 Oct 2025 17:55:53 +0000
Bug 1917974: Accumulate Dictionary hash values r=necko-reviewers,sunil
Differential Revision: https://phabricator.services.mozilla.com/D258123
Diffstat:
9 files changed, 115 insertions(+), 4 deletions(-)
diff --git a/netwerk/cache2/CacheEntry.cpp b/netwerk/cache2/CacheEntry.cpp
@@ -295,6 +295,12 @@ nsresult CacheEntry::HashingKey(const nsACString& aStorageID,
return NS_OK;
}
+nsresult CacheEntry::SetDictionary(DictionaryCacheEntry* aDict) {
+ mDict = aDict;
+ mFile->SetDictionary(aDict);
+ return NS_OK;
+}
+
void CacheEntry::AsyncOpen(nsICacheEntryOpenCallback* aCallback,
uint32_t aFlags) {
bool readonly = aFlags & nsICacheStorage::OPEN_READONLY;
diff --git a/netwerk/cache2/CacheEntry.h b/netwerk/cache2/CacheEntry.h
@@ -105,6 +105,8 @@ class CacheEntry final : public nsIRunnable,
nsIInputStream** _retval);
nsresult GetLoadContextInfo(nsILoadContextInfo** aInfo);
+ nsresult SetDictionary(DictionaryCacheEntry* aDict);
+
public:
uint32_t GetMetadataMemoryConsumption();
nsCString const& GetStorageID() const { return mStorageID; }
@@ -131,8 +133,6 @@ class CacheEntry final : public nsIRunnable,
TimeStamp const& LoadStart() const { return mLoadStart; }
- void SetDictionary(DictionaryCacheEntry* aDict) { mDict = aDict; }
-
enum EPurge {
PURGE_DATA_ONLY_DISK_BACKED,
PURGE_WHOLE_ONLY_DISK_BACKED,
@@ -547,6 +547,9 @@ class CacheEntryHandle final : public nsICacheEntry {
nsILoadContextInfo** aLoadContextInfo) override {
return mEntry->GetLoadContextInfo(aLoadContextInfo);
}
+ NS_IMETHOD SetDictionary(DictionaryCacheEntry* aDict) override {
+ return mEntry->SetDictionary(aDict);
+ }
// Specific implementation:
NS_IMETHOD Dismiss() override;
diff --git a/netwerk/cache2/CacheFile.cpp b/netwerk/cache2/CacheFile.cpp
@@ -290,6 +290,14 @@ nsresult CacheFile::Init(const nsACString& aKey, bool aCreateNew,
return NS_OK;
}
+void CacheFile::SetDictionary(DictionaryCacheEntry* aDict) {
+ CacheFileAutoLock lock(this);
+ mDict = aDict;
+ if (OutputStreamExists(false)) {
+ mOutput->SetDictionary(aDict);
+ }
+}
+
void CacheFile::Key(nsACString& aKey) {
CacheFileAutoLock lock(this);
aKey = mKey;
@@ -483,6 +491,10 @@ nsresult CacheFile::OnFileOpened(CacheFileHandle* aHandle, nsresult aResult) {
mOpeningFile = false;
+ if (mDict && OutputStreamExists(false)) {
+ mOutput->SetDictionary(mDict);
+ }
+
autoDoom.mListener.swap(mDoomAfterOpenListener);
if (mMemoryOnly) {
@@ -898,6 +910,10 @@ nsresult CacheFile::OpenOutputStream(CacheOutputCloseListener* aCloseListener,
"[this=%p]",
mOutput, this));
+ if (mDict) {
+ mOutput->SetDictionary(mDict);
+ }
+
mDataAccessed = true;
*_retval = do_AddRef(mOutput).take();
return NS_OK;
@@ -2105,7 +2121,12 @@ void CacheFile::RemoveOutput(CacheFileOutputStream* aOutput, nsresult aStatus) {
return;
}
- mOutput = nullptr;
+ // This is to finalize the Hash calculation
+ if (mDict) {
+ mDict->FinishFile();
+ }
+
+ mOutput = nullptr; // XXX should this be after NotifyCloseListener?
// Cancel all queued chunk and update listeners that cannot be satisfied
NotifyListenersAboutOutputRemoval();
diff --git a/netwerk/cache2/CacheFile.h b/netwerk/cache2/CacheFile.h
@@ -8,6 +8,7 @@
#include "CacheFileChunk.h"
#include "CacheFileIOManager.h"
#include "CacheFileMetadata.h"
+#include "Dictionary.h"
#include "nsRefPtrHashtable.h"
#include "nsClassHashtable.h"
#include "mozilla/Mutex.h"
@@ -125,6 +126,8 @@ class MOZ_CAPABILITY("mutex") CacheFile final
bool IsWriteInProgress();
bool EntryWouldExceedLimit(int64_t aOffset, int64_t aSize, bool aIsAltData);
+ void SetDictionary(DictionaryCacheEntry* aDict);
+
// Memory reporting
size_t SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
@@ -225,6 +228,8 @@ class MOZ_CAPABILITY("mutex") CacheFile final
nsCString mAltDataType
MOZ_GUARDED_BY(this); // The type of the saved alt-data. May be empty.
+ RefPtr<DictionaryCacheEntry> mDict MOZ_GUARDED_BY(this);
+
RefPtr<CacheFileHandle> mHandle MOZ_GUARDED_BY(this);
RefPtr<CacheFileMetadata> mMetadata MOZ_GUARDED_BY(this);
nsCOMPtr<CacheFileListener> mListener MOZ_GUARDED_BY(this);
diff --git a/netwerk/cache2/CacheFileOutputStream.cpp b/netwerk/cache2/CacheFileOutputStream.cpp
@@ -127,6 +127,12 @@ CacheFileOutputStream::Write(const char* aBuf, uint32_t aCount,
*_retval = aCount;
+ if (mDict) {
+ // We need to calculate the hash for the entry as we save it
+ // We don't necessarily need to save the data in memory, however
+ mDict->AccumulateHash(aBuf, aCount);
+ }
+
while (aCount) {
EnsureCorrectChunk(false);
if (NS_FAILED(mStatus)) {
diff --git a/netwerk/cache2/CacheFileOutputStream.h b/netwerk/cache2/CacheFileOutputStream.h
@@ -9,6 +9,7 @@
#include "nsISeekableStream.h"
#include "nsCOMPtr.h"
#include "CacheFileChunk.h"
+#include "Dictionary.h"
namespace mozilla {
namespace net {
@@ -39,6 +40,8 @@ class CacheFileOutputStream : public nsIAsyncOutputStream,
void NotifyCloseListener();
bool IsAlternativeData() const { return mAlternativeData; };
+ void SetDictionary(DictionaryCacheEntry* aDict) { mDict = aDict; }
+
// Memory reporting
size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
@@ -62,6 +65,7 @@ class CacheFileOutputStream : public nsIAsyncOutputStream,
nsCOMPtr<nsIOutputStreamCallback> mCallback;
uint32_t mCallbackFlags;
nsCOMPtr<nsIEventTarget> mCallbackTarget;
+ RefPtr<DictionaryCacheEntry> mDict;
};
} // namespace net
diff --git a/netwerk/cache2/Dictionary.cpp b/netwerk/cache2/Dictionary.cpp
@@ -55,7 +55,7 @@ using namespace mozilla;
namespace mozilla {
namespace net {
-static LazyLogModule gDictionaryLog("CompressionDictionaries");
+LazyLogModule gDictionaryLog("CompressionDictionaries");
#define DICTIONARY_LOG(args) \
MOZ_LOG(gDictionaryLog, mozilla::LogLevel::Debug, args)
@@ -118,6 +118,46 @@ void DictionaryCacheEntry::Prefetch() {
// XXX
}
+void DictionaryCacheEntry::AccumulateHash(const char* aBuf, int32_t aCount) {
+ if (!mCrypto) {
+ // If mCrypto is null, and mDictionaryData is set, we've already got the
+ // data for this dictionary.
+ if (!mDictionaryData.empty()) {
+ DICTIONARY_LOG(("%p: Trying to rewrite to Dictionary uri %s, pattern %s",
+ this, PromiseFlatCString(mURI).get(),
+ PromiseFlatCString(mPattern).get()));
+ return;
+ }
+ nsresult rv;
+ mCrypto = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return;
+ }
+ rv = mCrypto->Init(nsICryptoHash::SHA256);
+ NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Cache InitCrypto failed");
+ }
+ mCrypto->Update(reinterpret_cast<const uint8_t*>(aBuf), aCount);
+ DICTIONARY_LOG(("Accumulate Hash %p: %d bytes, total %zu", this, aCount,
+ mDictionaryData.length()));
+}
+
+void DictionaryCacheEntry::AccumulateFile(const char* aBuf, int32_t aCount) {
+ AccumulateHash(aBuf, aCount); // error?
+ Unused << mDictionaryData.append(aBuf, aCount);
+ DICTIONARY_LOG(("Accumulate %p: %d bytes, total %zu", this, aCount,
+ mDictionaryData.length()));
+}
+
+void DictionaryCacheEntry::FinishFile() {
+ MOZ_ASSERT(NS_IsMainThread());
+ if (mCrypto) {
+ DICTIONARY_LOG(("Hash was %s", mHash.get()));
+ mCrypto->Finish(true, mHash);
+ DICTIONARY_LOG(("Set dictionary hash for %p to %s", this, mHash.get()));
+ mCrypto = nullptr;
+ }
+}
+
// static
already_AddRefed<DictionaryCache> DictionaryCache::GetInstance() {
if (!gDictionaryCache) {
diff --git a/netwerk/cache2/Dictionary.h b/netwerk/cache2/Dictionary.h
@@ -89,6 +89,17 @@ class DictionaryCacheEntry final
const Vector<uint8_t>& GetDictionary() const { return mDictionaryData; }
+ void AccumulateHash(const char* aBuf, int32_t aCount);
+ void AccumulateFile(const char* aBuf, int32_t aCount);
+
+ void FinishFile();
+
+ // return a pointer to the data and length
+ uint8_t* DictionaryData(size_t* aLength) const {
+ *aLength = mDictionaryData.length();
+ return (uint8_t*)mDictionaryData.begin();
+ }
+
size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
// XXX
return mallocSizeOf(this);
@@ -107,6 +118,7 @@ class DictionaryCacheEntry final
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;
+ bool mDictionaryDataComplete{false};
// for accumulating SHA-256 hash values for dictionaries
nsCOMPtr<nsICryptoHash> mCrypto;
diff --git a/netwerk/cache2/nsICacheEntry.idl b/netwerk/cache2/nsICacheEntry.idl
@@ -12,6 +12,13 @@ interface nsILoadContextInfo;
interface nsIOutputStream;
interface nsITransportSecurityInfo;
+%{C++
+namespace mozilla::net {
+class DictionaryCacheEntry;
+}
+%}
+[ptr] native DictionaryCacheEntry(mozilla::net::DictionaryCacheEntry);
+
[scriptable, uuid(607c2a2c-0a48-40b9-a956-8cf2bb9857cf)]
interface nsICacheEntry : nsISupports
{
@@ -309,6 +316,13 @@ interface nsICacheEntry : nsISupports
* Get the nsILoadContextInfo of the cache entry
*/
readonly attribute nsILoadContextInfo loadContextInfo;
+
+ /**
+ * This method gets called to indicate that this entry will be used
+ * as a Dictionary in the future, so we know to calculate a hash for it.
+ */
+ [noscript] void SetDictionary(in DictionaryCacheEntry dict);
+
};
/**