summaryrefslogtreecommitdiffstats
path: root/netwerk/cache/nsCacheEntry.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--netwerk/cache/nsCacheEntry.cpp415
1 files changed, 415 insertions, 0 deletions
diff --git a/netwerk/cache/nsCacheEntry.cpp b/netwerk/cache/nsCacheEntry.cpp
new file mode 100644
index 0000000000..c91748ef72
--- /dev/null
+++ b/netwerk/cache/nsCacheEntry.cpp
@@ -0,0 +1,415 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 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 "nsCache.h"
+#include "nspr.h"
+#include "nsCacheEntry.h"
+#include "nsCacheEntryDescriptor.h"
+#include "nsCacheMetaData.h"
+#include "nsCacheRequest.h"
+#include "nsThreadUtils.h"
+#include "nsError.h"
+#include "nsCacheService.h"
+#include "nsCacheDevice.h"
+#include "nsHashKeys.h"
+
+using namespace mozilla;
+
+nsCacheEntry::nsCacheEntry(const nsACString& key, bool streamBased,
+ nsCacheStoragePolicy storagePolicy)
+ : mKey(key),
+ mFetchCount(0),
+ mLastFetched(0),
+ mLastModified(0),
+ mLastValidated(0),
+ mExpirationTime(nsICache::NO_EXPIRATION_TIME),
+ mFlags(0),
+ mPredictedDataSize(-1),
+ mDataSize(0),
+ mCacheDevice(nullptr),
+ mCustomDevice(nullptr),
+ mData(nullptr),
+ mRequestQ{},
+ mDescriptorQ{} {
+ MOZ_COUNT_CTOR(nsCacheEntry);
+ PR_INIT_CLIST(this);
+ PR_INIT_CLIST(&mRequestQ);
+ PR_INIT_CLIST(&mDescriptorQ);
+
+ if (streamBased) MarkStreamBased();
+ SetStoragePolicy(storagePolicy);
+
+ MarkPublic();
+}
+
+nsCacheEntry::~nsCacheEntry() {
+ MOZ_COUNT_DTOR(nsCacheEntry);
+
+ if (mData) nsCacheService::ReleaseObject_Locked(mData, mEventTarget);
+}
+
+nsresult nsCacheEntry::Create(const char* key, bool streamBased,
+ nsCacheStoragePolicy storagePolicy,
+ nsCacheDevice* device, nsCacheEntry** result) {
+ nsCacheEntry* entry =
+ new nsCacheEntry(nsCString(key), streamBased, storagePolicy);
+ entry->SetCacheDevice(device);
+ *result = entry;
+ return NS_OK;
+}
+
+void nsCacheEntry::Fetched() {
+ mLastFetched = SecondsFromPRTime(PR_Now());
+ ++mFetchCount;
+ MarkEntryDirty();
+}
+
+const char* nsCacheEntry::GetDeviceID() {
+ if (mCacheDevice) return mCacheDevice->GetDeviceID();
+ return nullptr;
+}
+
+void nsCacheEntry::TouchData() {
+ mLastModified = SecondsFromPRTime(PR_Now());
+ MarkDataDirty();
+}
+
+void nsCacheEntry::SetData(nsISupports* data) {
+ if (mData) {
+ nsCacheService::ReleaseObject_Locked(mData, mEventTarget);
+ mData = nullptr;
+ }
+
+ if (data) {
+ NS_ADDREF(mData = data);
+ mEventTarget = GetCurrentEventTarget();
+ }
+}
+
+void nsCacheEntry::TouchMetaData() {
+ mLastModified = SecondsFromPRTime(PR_Now());
+ MarkMetaDataDirty();
+}
+
+/**
+ * cache entry states
+ * 0 descriptors (new entry)
+ * 0 descriptors (existing, bound entry)
+ * n descriptors (existing, bound entry) valid
+ * n descriptors (existing, bound entry) not valid (wait until valid or
+ * doomed)
+ */
+
+nsresult nsCacheEntry::RequestAccess(nsCacheRequest* request,
+ nsCacheAccessMode* accessGranted) {
+ nsresult rv = NS_OK;
+
+ if (IsDoomed()) return NS_ERROR_CACHE_ENTRY_DOOMED;
+
+ if (!IsInitialized()) {
+ // brand new, unbound entry
+ if (request->IsStreamBased()) MarkStreamBased();
+ MarkInitialized();
+
+ *accessGranted = request->AccessRequested() & nsICache::ACCESS_WRITE;
+ NS_ASSERTION(*accessGranted, "new cache entry for READ-ONLY request");
+ PR_APPEND_LINK(request, &mRequestQ);
+ return rv;
+ }
+
+ if (IsStreamData() != request->IsStreamBased()) {
+ *accessGranted = nsICache::ACCESS_NONE;
+ return request->IsStreamBased() ? NS_ERROR_CACHE_DATA_IS_NOT_STREAM
+ : NS_ERROR_CACHE_DATA_IS_STREAM;
+ }
+
+ if (PR_CLIST_IS_EMPTY(&mDescriptorQ)) {
+ // 1st descriptor for existing bound entry
+ *accessGranted = request->AccessRequested();
+ if (*accessGranted & nsICache::ACCESS_WRITE) {
+ MarkInvalid();
+ } else {
+ MarkValid();
+ }
+ } else {
+ // nth request for existing, bound entry
+ *accessGranted = request->AccessRequested() & ~nsICache::ACCESS_WRITE;
+ if (!IsValid()) rv = NS_ERROR_CACHE_WAIT_FOR_VALIDATION;
+ }
+ PR_APPEND_LINK(request, &mRequestQ);
+
+ return rv;
+}
+
+nsresult nsCacheEntry::CreateDescriptor(nsCacheRequest* request,
+ nsCacheAccessMode accessGranted,
+ nsICacheEntryDescriptor** result) {
+ NS_ENSURE_ARG_POINTER(request && result);
+
+ nsCacheEntryDescriptor* descriptor =
+ new nsCacheEntryDescriptor(this, accessGranted);
+
+ // XXX check request is on q
+ PR_REMOVE_AND_INIT_LINK(request); // remove request regardless of success
+
+ if (descriptor == nullptr) return NS_ERROR_OUT_OF_MEMORY;
+
+ PR_APPEND_LINK(descriptor, &mDescriptorQ);
+
+ CACHE_LOG_DEBUG((" descriptor %p created for request %p on entry %p\n",
+ descriptor, request, this));
+
+ *result = do_AddRef(descriptor).take();
+ return NS_OK;
+}
+
+bool nsCacheEntry::RemoveRequest(nsCacheRequest* request) {
+ // XXX if debug: verify this request belongs to this entry
+ PR_REMOVE_AND_INIT_LINK(request);
+
+ // return true if this entry should stay active
+ return !((PR_CLIST_IS_EMPTY(&mRequestQ)) &&
+ (PR_CLIST_IS_EMPTY(&mDescriptorQ)));
+}
+
+bool nsCacheEntry::RemoveDescriptor(nsCacheEntryDescriptor* descriptor,
+ bool* doomEntry) {
+ NS_ASSERTION(descriptor->CacheEntry() == this, "### Wrong cache entry!!");
+
+ *doomEntry = descriptor->ClearCacheEntry();
+
+ PR_REMOVE_AND_INIT_LINK(descriptor);
+
+ if (!PR_CLIST_IS_EMPTY(&mDescriptorQ))
+ return true; // stay active if we still have open descriptors
+
+ if (PR_CLIST_IS_EMPTY(&mRequestQ))
+ return false; // no descriptors or requests, we can deactivate
+
+ return true; // find next best request to give a descriptor to
+}
+
+void nsCacheEntry::DetachDescriptors() {
+ nsCacheEntryDescriptor* descriptor =
+ (nsCacheEntryDescriptor*)PR_LIST_HEAD(&mDescriptorQ);
+
+ while (descriptor != &mDescriptorQ) {
+ nsCacheEntryDescriptor* nextDescriptor =
+ (nsCacheEntryDescriptor*)PR_NEXT_LINK(descriptor);
+
+ descriptor->ClearCacheEntry();
+ PR_REMOVE_AND_INIT_LINK(descriptor);
+ descriptor = nextDescriptor;
+ }
+}
+
+void nsCacheEntry::GetDescriptors(
+ nsTArray<RefPtr<nsCacheEntryDescriptor> >& outDescriptors) {
+ nsCacheEntryDescriptor* descriptor =
+ (nsCacheEntryDescriptor*)PR_LIST_HEAD(&mDescriptorQ);
+
+ while (descriptor != &mDescriptorQ) {
+ nsCacheEntryDescriptor* nextDescriptor =
+ (nsCacheEntryDescriptor*)PR_NEXT_LINK(descriptor);
+
+ outDescriptors.AppendElement(descriptor);
+ descriptor = nextDescriptor;
+ }
+}
+
+/******************************************************************************
+ * nsCacheEntryInfo - for implementing about:cache
+ *****************************************************************************/
+
+NS_IMPL_ISUPPORTS(nsCacheEntryInfo, nsICacheEntryInfo)
+
+NS_IMETHODIMP
+nsCacheEntryInfo::GetClientID(nsACString& aClientID) {
+ if (!mCacheEntry) {
+ aClientID.Truncate();
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ return ClientIDFromCacheKey(*mCacheEntry->Key(), aClientID);
+}
+
+NS_IMETHODIMP
+nsCacheEntryInfo::GetDeviceID(nsACString& aDeviceID) {
+ if (!mCacheEntry) {
+ aDeviceID.Truncate();
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ aDeviceID.Assign(mCacheEntry->GetDeviceID());
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCacheEntryInfo::GetKey(nsACString& key) {
+ if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
+
+ return ClientKeyFromCacheKey(*mCacheEntry->Key(), key);
+}
+
+NS_IMETHODIMP
+nsCacheEntryInfo::GetFetchCount(int32_t* fetchCount) {
+ NS_ENSURE_ARG_POINTER(fetchCount);
+ if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
+
+ *fetchCount = mCacheEntry->FetchCount();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCacheEntryInfo::GetLastFetched(uint32_t* lastFetched) {
+ NS_ENSURE_ARG_POINTER(lastFetched);
+ if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
+
+ *lastFetched = mCacheEntry->LastFetched();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCacheEntryInfo::GetLastModified(uint32_t* lastModified) {
+ NS_ENSURE_ARG_POINTER(lastModified);
+ if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
+
+ *lastModified = mCacheEntry->LastModified();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCacheEntryInfo::GetExpirationTime(uint32_t* expirationTime) {
+ NS_ENSURE_ARG_POINTER(expirationTime);
+ if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
+
+ *expirationTime = mCacheEntry->ExpirationTime();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCacheEntryInfo::GetDataSize(uint32_t* dataSize) {
+ NS_ENSURE_ARG_POINTER(dataSize);
+ if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
+
+ *dataSize = mCacheEntry->DataSize();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCacheEntryInfo::IsStreamBased(bool* result) {
+ NS_ENSURE_ARG_POINTER(result);
+ if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
+
+ *result = mCacheEntry->IsStreamData();
+ return NS_OK;
+}
+
+/******************************************************************************
+ * nsCacheEntryHashTable
+ *****************************************************************************/
+
+const PLDHashTableOps nsCacheEntryHashTable::ops = {HashKey, MatchEntry,
+ MoveEntry, ClearEntry};
+
+nsCacheEntryHashTable::nsCacheEntryHashTable()
+ : table(&ops, sizeof(nsCacheEntryHashTableEntry), kInitialTableLength),
+ initialized(false) {
+ MOZ_COUNT_CTOR(nsCacheEntryHashTable);
+}
+
+nsCacheEntryHashTable::~nsCacheEntryHashTable() {
+ MOZ_COUNT_DTOR(nsCacheEntryHashTable);
+ if (initialized) Shutdown();
+}
+
+void nsCacheEntryHashTable::Init() {
+ table.ClearAndPrepareForLength(kInitialTableLength);
+ initialized = true;
+}
+
+void nsCacheEntryHashTable::Shutdown() {
+ if (initialized) {
+ table.ClearAndPrepareForLength(kInitialTableLength);
+ initialized = false;
+ }
+}
+
+nsCacheEntry* nsCacheEntryHashTable::GetEntry(const nsCString* key) const {
+ NS_ASSERTION(initialized, "nsCacheEntryHashTable not initialized");
+ if (!initialized) return nullptr;
+
+ PLDHashEntryHdr* hashEntry = table.Search(key);
+ return hashEntry ? ((nsCacheEntryHashTableEntry*)hashEntry)->cacheEntry
+ : nullptr;
+}
+
+nsresult nsCacheEntryHashTable::AddEntry(nsCacheEntry* cacheEntry) {
+ PLDHashEntryHdr* hashEntry;
+
+ NS_ASSERTION(initialized, "nsCacheEntryHashTable not initialized");
+ if (!initialized) return NS_ERROR_NOT_INITIALIZED;
+ if (!cacheEntry) return NS_ERROR_NULL_POINTER;
+
+ hashEntry = table.Add(&(cacheEntry->mKey), fallible);
+
+ if (!hashEntry) return NS_ERROR_FAILURE;
+ NS_ASSERTION(((nsCacheEntryHashTableEntry*)hashEntry)->cacheEntry == nullptr,
+ "### nsCacheEntryHashTable::AddEntry - entry already used");
+ ((nsCacheEntryHashTableEntry*)hashEntry)->cacheEntry = cacheEntry;
+
+ return NS_OK;
+}
+
+void nsCacheEntryHashTable::RemoveEntry(nsCacheEntry* cacheEntry) {
+ NS_ASSERTION(initialized, "nsCacheEntryHashTable not initialized");
+ NS_ASSERTION(cacheEntry, "### cacheEntry == nullptr");
+
+ if (!initialized) return; // NS_ERROR_NOT_INITIALIZED
+
+#if DEBUG
+ // XXX debug code to make sure we have the entry we're trying to remove
+ nsCacheEntry* check = GetEntry(&(cacheEntry->mKey));
+ NS_ASSERTION(check == cacheEntry,
+ "### Attempting to remove unknown cache entry!!!");
+#endif
+ table.Remove(&(cacheEntry->mKey));
+}
+
+PLDHashTable::Iterator nsCacheEntryHashTable::Iter() {
+ return PLDHashTable::Iterator(&table);
+}
+
+/**
+ * hash table operation callback functions
+ */
+
+PLDHashNumber nsCacheEntryHashTable::HashKey(const void* key) {
+ return HashString(*static_cast<const nsCString*>(key));
+}
+
+bool nsCacheEntryHashTable::MatchEntry(const PLDHashEntryHdr* hashEntry,
+ const void* key) {
+ NS_ASSERTION(key != nullptr,
+ "### nsCacheEntryHashTable::MatchEntry : null key");
+ nsCacheEntry* cacheEntry =
+ ((nsCacheEntryHashTableEntry*)hashEntry)->cacheEntry;
+
+ return cacheEntry->mKey.Equals(*(nsCString*)key);
+}
+
+void nsCacheEntryHashTable::MoveEntry(PLDHashTable* /* table */,
+ const PLDHashEntryHdr* from,
+ PLDHashEntryHdr* to) {
+ new (KnownNotNull, to) nsCacheEntryHashTableEntry(
+ std::move(*((nsCacheEntryHashTableEntry*)from)));
+ // No need to destroy `from`.
+}
+
+void nsCacheEntryHashTable::ClearEntry(PLDHashTable* /* table */,
+ PLDHashEntryHdr* hashEntry) {
+ ((nsCacheEntryHashTableEntry*)hashEntry)->cacheEntry = nullptr;
+}