diff options
Diffstat (limited to '')
-rw-r--r-- | netwerk/cache2/OldWrappers.cpp | 1037 |
1 files changed, 1037 insertions, 0 deletions
diff --git a/netwerk/cache2/OldWrappers.cpp b/netwerk/cache2/OldWrappers.cpp new file mode 100644 index 0000000000..b96dc2ff29 --- /dev/null +++ b/netwerk/cache2/OldWrappers.cpp @@ -0,0 +1,1037 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +// Stuff to link the old imp to the new api - will go away! + +#include "CacheLog.h" +#include "OldWrappers.h" +#include "CacheStorage.h" +#include "CacheStorageService.h" +#include "LoadContextInfo.h" +#include "nsCacheService.h" + +#include "nsIURI.h" +#include "nsICacheSession.h" +#include "nsIApplicationCache.h" +#include "nsIApplicationCacheService.h" +#include "nsIStreamTransportService.h" +#include "nsIFile.h" +#include "nsICacheEntryDoomCallback.h" +#include "nsICacheListener.h" +#include "nsICacheStorageVisitor.h" + +#include "nsServiceManagerUtils.h" +#include "nsNetCID.h" +#include "nsNetUtil.h" +#include "nsProxyRelease.h" +#include "mozilla/IntegerPrintfMacros.h" +#include "mozilla/Telemetry.h" + +static NS_DEFINE_CID(kStreamTransportServiceCID, NS_STREAMTRANSPORTSERVICE_CID); + +static uint32_t const CHECK_MULTITHREADED = + nsICacheStorage::CHECK_MULTITHREADED; + +namespace mozilla { +namespace net { + +namespace { + +// Fires the doom callback back on the main thread +// after the cache I/O thread is looped. + +class DoomCallbackSynchronizer : public Runnable { + public: + explicit DoomCallbackSynchronizer(nsICacheEntryDoomCallback* cb) + : Runnable("net::DoomCallbackSynchronizer"), mCB(cb) {} + nsresult Dispatch(); + + private: + virtual ~DoomCallbackSynchronizer() = default; + + NS_DECL_NSIRUNNABLE + nsCOMPtr<nsICacheEntryDoomCallback> mCB; +}; + +nsresult DoomCallbackSynchronizer::Dispatch() { + nsresult rv; + + nsCOMPtr<nsICacheService> serv = + do_GetService(NS_CACHESERVICE_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsIEventTarget> eventTarget; + rv = serv->GetCacheIOTarget(getter_AddRefs(eventTarget)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = eventTarget->Dispatch(this, NS_DISPATCH_NORMAL); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +NS_IMETHODIMP DoomCallbackSynchronizer::Run() { + if (!NS_IsMainThread()) { + NS_DispatchToMainThread(this); + } else { + if (mCB) mCB->OnCacheEntryDoomed(NS_OK); + } + return NS_OK; +} + +// Receives doom callback from the old API and forwards to the new API + +class DoomCallbackWrapper : public nsICacheListener { + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSICACHELISTENER + + explicit DoomCallbackWrapper(nsICacheEntryDoomCallback* cb) : mCB(cb) {} + + private: + virtual ~DoomCallbackWrapper() = default; + + nsCOMPtr<nsICacheEntryDoomCallback> mCB; +}; + +NS_IMPL_ISUPPORTS(DoomCallbackWrapper, nsICacheListener); + +NS_IMETHODIMP DoomCallbackWrapper::OnCacheEntryAvailable( + nsICacheEntryDescriptor* descriptor, nsCacheAccessMode accessGranted, + nsresult status) { + return NS_OK; +} + +NS_IMETHODIMP DoomCallbackWrapper::OnCacheEntryDoomed(nsresult status) { + if (!mCB) return NS_ERROR_NULL_POINTER; + + mCB->OnCacheEntryDoomed(status); + mCB = nullptr; + return NS_OK; +} + +} // namespace + +// _OldVisitCallbackWrapper +// Receives visit callbacks from the old API and forwards it to the new API + +NS_IMPL_ISUPPORTS(_OldVisitCallbackWrapper, nsICacheVisitor) + +_OldVisitCallbackWrapper::~_OldVisitCallbackWrapper() { + if (!mHit) { + // The device has not been found, to not break the chain, simulate + // storage info callback. + mCB->OnCacheStorageInfo(0, 0, 0, nullptr); + } + + if (mVisitEntries) { + mCB->OnCacheEntryVisitCompleted(); + } +} + +NS_IMETHODIMP _OldVisitCallbackWrapper::VisitDevice( + const char* deviceID, nsICacheDeviceInfo* deviceInfo, bool* _retval) { + if (!mCB) return NS_ERROR_NULL_POINTER; + + *_retval = false; + if (strcmp(deviceID, mDeviceID)) { + // Not the device we want to visit + return NS_OK; + } + + mHit = true; + + nsresult rv; + + uint32_t capacity; + rv = deviceInfo->GetMaximumSize(&capacity); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsIFile> dir; + if (!strcmp(mDeviceID, "disk")) { + nsCacheService::GetDiskCacheDirectory(getter_AddRefs(dir)); + } else if (!strcmp(mDeviceID, "offline")) { + nsCacheService::GetAppCacheDirectory(getter_AddRefs(dir)); + } + + if (mLoadInfo && mLoadInfo->IsAnonymous()) { + // Anonymous visiting reports 0, 0 since we cannot count that + // early the number of anon entries. + mCB->OnCacheStorageInfo(0, 0, capacity, dir); + } else { + // Non-anon visitor counts all non-anon + ALL ANON entries, + // there is no way to determine the number of entries when + // using the old cache APIs - there is no concept of anonymous + // storage. + uint32_t entryCount; + rv = deviceInfo->GetEntryCount(&entryCount); + NS_ENSURE_SUCCESS(rv, rv); + + uint32_t totalSize; + rv = deviceInfo->GetTotalSize(&totalSize); + NS_ENSURE_SUCCESS(rv, rv); + + mCB->OnCacheStorageInfo(entryCount, totalSize, capacity, dir); + } + + *_retval = mVisitEntries; + return NS_OK; +} + +NS_IMETHODIMP _OldVisitCallbackWrapper::VisitEntry(const char* deviceID, + nsICacheEntryInfo* entryInfo, + bool* _retval) { + MOZ_ASSERT(!strcmp(deviceID, mDeviceID)); + + nsresult rv; + + *_retval = true; + + // Read all informative properties from the entry. + nsAutoCString clientId; + rv = entryInfo->GetClientID(clientId); + if (NS_FAILED(rv)) return NS_OK; + + if (mLoadInfo->IsPrivate() != + StringBeginsWith(clientId, "HTTP-memory-only-PB"_ns)) { + return NS_OK; + } + + nsAutoCString cacheKey, enhanceId; + rv = entryInfo->GetKey(cacheKey); + if (NS_FAILED(rv)) return NS_OK; + + if (StringBeginsWith(cacheKey, "anon&"_ns)) { + if (!mLoadInfo->IsAnonymous()) return NS_OK; + + cacheKey = Substring(cacheKey, 5, cacheKey.Length()); + } else if (mLoadInfo->IsAnonymous()) { + return NS_OK; + } + + if (StringBeginsWith(cacheKey, "id="_ns)) { + int32_t uriSpecEnd = cacheKey.Find("&uri="); + if (uriSpecEnd == kNotFound) // Corrupted, ignore + return NS_OK; + + enhanceId = Substring(cacheKey, 3, uriSpecEnd - 3); + cacheKey = Substring(cacheKey, uriSpecEnd + 1, cacheKey.Length()); + } + + if (StringBeginsWith(cacheKey, "uri="_ns)) { + cacheKey = Substring(cacheKey, 4, cacheKey.Length()); + } + + nsCOMPtr<nsIURI> uri; + // cacheKey is strip of any prefixes + rv = NS_NewURI(getter_AddRefs(uri), cacheKey); + if (NS_FAILED(rv)) return NS_OK; + + uint32_t dataSize; + if (NS_FAILED(entryInfo->GetDataSize(&dataSize))) dataSize = 0; + int32_t fetchCount; + if (NS_FAILED(entryInfo->GetFetchCount(&fetchCount))) fetchCount = 0; + uint32_t expirationTime; + if (NS_FAILED(entryInfo->GetExpirationTime(&expirationTime))) + expirationTime = 0; + uint32_t lastModified; + if (NS_FAILED(entryInfo->GetLastModified(&lastModified))) lastModified = 0; + + // Send them to the consumer. + rv = mCB->OnCacheEntryInfo(uri, enhanceId, (int64_t)dataSize, fetchCount, + lastModified, expirationTime, false, mLoadInfo); + + *_retval = NS_SUCCEEDED(rv); + return NS_OK; +} + +// _OldGetDiskConsumption + +// static +nsresult _OldGetDiskConsumption::Get( + nsICacheStorageConsumptionObserver* aCallback) { + nsresult rv; + + nsCOMPtr<nsICacheService> serv = + do_GetService(NS_CACHESERVICE_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + RefPtr<_OldGetDiskConsumption> cb = new _OldGetDiskConsumption(aCallback); + + // _OldGetDiskConsumption stores the found size value, but until dispatched + // to the main thread it doesn't call on the consupmtion observer. See bellow. + rv = serv->VisitEntries(cb); + NS_ENSURE_SUCCESS(rv, rv); + + // We are called from CacheStorageService::AsyncGetDiskConsumption whose IDL + // documentation claims the callback is always delievered asynchronously + // back to the main thread. Despite we know the result synchronosusly when + // querying the old cache, we need to stand the word and dispatch the result + // to the main thread asynchronously. Hence the dispatch here. + return NS_DispatchToMainThread(cb); +} + +NS_IMPL_ISUPPORTS_INHERITED(_OldGetDiskConsumption, Runnable, nsICacheVisitor) + +_OldGetDiskConsumption::_OldGetDiskConsumption( + nsICacheStorageConsumptionObserver* aCallback) + : Runnable("net::_OldGetDiskConsumption"), mCallback(aCallback), mSize(0) {} + +NS_IMETHODIMP +_OldGetDiskConsumption::Run() { + mCallback->OnNetworkCacheDiskConsumption(mSize); + return NS_OK; +} + +NS_IMETHODIMP +_OldGetDiskConsumption::VisitDevice(const char* deviceID, + nsICacheDeviceInfo* deviceInfo, + bool* _retval) { + if (!strcmp(deviceID, "disk")) { + uint32_t size; + nsresult rv = deviceInfo->GetTotalSize(&size); + if (NS_SUCCEEDED(rv)) mSize = (int64_t)size; + } + + *_retval = false; + return NS_OK; +} + +NS_IMETHODIMP +_OldGetDiskConsumption::VisitEntry(const char* deviceID, + nsICacheEntryInfo* entryInfo, + bool* _retval) { + MOZ_CRASH("Unexpected"); + return NS_OK; +} + +// _OldCacheEntryWrapper + +_OldCacheEntryWrapper::_OldCacheEntryWrapper(nsICacheEntryDescriptor* desc) + : mOldDesc(desc), mOldInfo(desc), mCacheEntryId(CacheEntry::GetNextId()) { + LOG(("Creating _OldCacheEntryWrapper %p for descriptor %p", this, desc)); +} + +_OldCacheEntryWrapper::_OldCacheEntryWrapper(nsICacheEntryInfo* info) + : mOldDesc(nullptr), + mOldInfo(info), + mCacheEntryId(CacheEntry::GetNextId()) { + LOG(("Creating _OldCacheEntryWrapper %p for info %p", this, info)); +} + +_OldCacheEntryWrapper::~_OldCacheEntryWrapper() { + LOG(("Destroying _OldCacheEntryWrapper %p for descriptor %p", this, + mOldInfo.get())); +} + +NS_IMETHODIMP _OldCacheEntryWrapper::GetIsForcedValid(bool* aIsForcedValid) { + // Unused stub + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP _OldCacheEntryWrapper::ForceValidFor( + uint32_t aSecondsToTheFuture) { + // Unused stub + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMPL_ISUPPORTS(_OldCacheEntryWrapper, nsICacheEntry) + +NS_IMETHODIMP _OldCacheEntryWrapper::AsyncDoom( + nsICacheEntryDoomCallback* listener) { + RefPtr<DoomCallbackWrapper> cb = + listener ? new DoomCallbackWrapper(listener) : nullptr; + return AsyncDoom(cb); +} + +NS_IMETHODIMP _OldCacheEntryWrapper::GetDataSize(int64_t* aSize) { + uint32_t size; + nsresult rv = GetDataSize(&size); + if (NS_FAILED(rv)) return rv; + + *aSize = size; + return NS_OK; +} + +NS_IMETHODIMP _OldCacheEntryWrapper::GetAltDataSize(int64_t* aSize) { + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP _OldCacheEntryWrapper::GetAltDataType(nsACString& aType) { + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP _OldCacheEntryWrapper::GetPersistent(bool* aPersistToDisk) { + if (!mOldDesc) { + return NS_ERROR_NULL_POINTER; + } + + nsresult rv; + + nsCacheStoragePolicy policy; + rv = mOldDesc->GetStoragePolicy(&policy); + NS_ENSURE_SUCCESS(rv, rv); + + *aPersistToDisk = policy != nsICache::STORE_IN_MEMORY; + + return NS_OK; +} + +NS_IMETHODIMP _OldCacheEntryWrapper::Recreate(bool aMemoryOnly, + nsICacheEntry** aResult) { + NS_ENSURE_TRUE(mOldDesc, NS_ERROR_NOT_AVAILABLE); + + nsCacheAccessMode mode; + nsresult rv = mOldDesc->GetAccessGranted(&mode); + NS_ENSURE_SUCCESS(rv, rv); + + if (!(mode & nsICache::ACCESS_WRITE)) return NS_ERROR_NOT_AVAILABLE; + + LOG(("_OldCacheEntryWrapper::Recreate [this=%p]", this)); + + if (aMemoryOnly) mOldDesc->SetStoragePolicy(nsICache::STORE_IN_MEMORY); + + nsCOMPtr<nsICacheEntry> self(this); + self.forget(aResult); + return NS_OK; +} + +NS_IMETHODIMP _OldCacheEntryWrapper::OpenInputStream(int64_t offset, + nsIInputStream** _retval) { + if (offset > PR_UINT32_MAX) return NS_ERROR_INVALID_ARG; + + return OpenInputStream(uint32_t(offset), _retval); +} +NS_IMETHODIMP _OldCacheEntryWrapper::OpenOutputStream( + int64_t offset, int64_t predictedSize, nsIOutputStream** _retval) { + if (offset > PR_UINT32_MAX) return NS_ERROR_INVALID_ARG; + + return OpenOutputStream(uint32_t(offset), _retval); +} + +NS_IMETHODIMP _OldCacheEntryWrapper::MaybeMarkValid() { + LOG(("_OldCacheEntryWrapper::MaybeMarkValid [this=%p]", this)); + + NS_ENSURE_TRUE(mOldDesc, NS_ERROR_NULL_POINTER); + + nsCacheAccessMode mode; + nsresult rv = mOldDesc->GetAccessGranted(&mode); + NS_ENSURE_SUCCESS(rv, rv); + + if (mode & nsICache::ACCESS_WRITE) { + LOG(("Marking cache entry valid [entry=%p, descr=%p]", this, mOldDesc)); + return mOldDesc->MarkValid(); + } + + LOG(("Not marking read-only cache entry valid [entry=%p, descr=%p]", this, + mOldDesc)); + return NS_OK; +} + +NS_IMETHODIMP _OldCacheEntryWrapper::HasWriteAccess(bool aWriteAllowed_unused, + bool* aWriteAccess) { + NS_ENSURE_TRUE(mOldDesc, NS_ERROR_NULL_POINTER); + NS_ENSURE_ARG(aWriteAccess); + + nsCacheAccessMode mode; + nsresult rv = mOldDesc->GetAccessGranted(&mode); + NS_ENSURE_SUCCESS(rv, rv); + + *aWriteAccess = !!(mode & nsICache::ACCESS_WRITE); + + LOG(("_OldCacheEntryWrapper::HasWriteAccess [this=%p, write-access=%d]", this, + *aWriteAccess)); + + return NS_OK; +} + +namespace { + +class MetaDataVisitorWrapper : public nsICacheMetaDataVisitor { + virtual ~MetaDataVisitorWrapper() = default; + + NS_DECL_ISUPPORTS + NS_DECL_NSICACHEMETADATAVISITOR + explicit MetaDataVisitorWrapper(nsICacheEntryMetaDataVisitor* cb) : mCB(cb) {} + nsCOMPtr<nsICacheEntryMetaDataVisitor> mCB; +}; + +NS_IMPL_ISUPPORTS(MetaDataVisitorWrapper, nsICacheMetaDataVisitor) + +NS_IMETHODIMP +MetaDataVisitorWrapper::VisitMetaDataElement(char const* key, char const* value, + bool* goon) { + *goon = true; + return mCB->OnMetaDataElement(key, value); +} + +} // namespace + +NS_IMETHODIMP _OldCacheEntryWrapper::VisitMetaData( + nsICacheEntryMetaDataVisitor* cb) { + RefPtr<MetaDataVisitorWrapper> w = new MetaDataVisitorWrapper(cb); + return mOldDesc->VisitMetaData(w); +} + +namespace { + +void GetCacheSessionNameForStoragePolicy(const nsACString& scheme, + nsCacheStoragePolicy storagePolicy, + bool isPrivate, + OriginAttributes const* originAttribs, + nsACString& sessionName) { + MOZ_ASSERT(!isPrivate || storagePolicy == nsICache::STORE_IN_MEMORY); + + // HTTP + if (scheme.EqualsLiteral("http") || scheme.EqualsLiteral("https")) { + switch (storagePolicy) { + case nsICache::STORE_IN_MEMORY: + if (isPrivate) + sessionName.AssignLiteral("HTTP-memory-only-PB"); + else + sessionName.AssignLiteral("HTTP-memory-only"); + break; + case nsICache::STORE_OFFLINE: + // XXX This is actually never used, only added to prevent + // any compatibility damage. + sessionName.AssignLiteral("HTTP-offline"); + break; + default: + sessionName.AssignLiteral("HTTP"); + break; + } + } + // FTP + else if (scheme.EqualsLiteral("ftp")) { + if (isPrivate) + sessionName.AssignLiteral("FTP-private"); + else + sessionName.AssignLiteral("FTP"); + } + // all remaining URL scheme + else { + // Since with the new API a consumer cannot specify its own session name + // and partitioning of the cache is handled stricly only by the cache + // back-end internally, we will use a separate session name to pretend + // functionality of the new API wrapping the Darin's cache for all other + // URL schemes. + // Deliberately omitting |anonymous| since other session types don't + // recognize it too. + sessionName.AssignLiteral("other"); + if (isPrivate) sessionName.AppendLiteral("-private"); + } + + nsAutoCString suffix; + originAttribs->CreateSuffix(suffix); + sessionName.Append(suffix); +} + +nsresult GetCacheSession(const nsACString& aScheme, bool aWriteToDisk, + nsILoadContextInfo* aLoadInfo, + nsIApplicationCache* aAppCache, + nsICacheSession** _result) { + nsresult rv; + + nsCacheStoragePolicy storagePolicy; + if (aAppCache) + storagePolicy = nsICache::STORE_OFFLINE; + else if (!aWriteToDisk || aLoadInfo->IsPrivate()) + storagePolicy = nsICache::STORE_IN_MEMORY; + else + storagePolicy = nsICache::STORE_ANYWHERE; + + nsAutoCString clientId; + if (aAppCache) { + aAppCache->GetClientID(clientId); + } else { + GetCacheSessionNameForStoragePolicy( + aScheme, storagePolicy, aLoadInfo->IsPrivate(), + aLoadInfo->OriginAttributesPtr(), clientId); + } + + LOG((" GetCacheSession for client=%s, policy=%d", clientId.get(), + storagePolicy)); + + nsCOMPtr<nsICacheService> serv = + do_GetService(NS_CACHESERVICE_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsICacheSession> session; + rv = nsCacheService::GlobalInstance()->CreateSessionInternal( + clientId.get(), storagePolicy, nsICache::STREAM_BASED, + getter_AddRefs(session)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = session->SetIsPrivate(aLoadInfo->IsPrivate()); + NS_ENSURE_SUCCESS(rv, rv); + + rv = session->SetDoomEntriesIfExpired(false); + NS_ENSURE_SUCCESS(rv, rv); + + if (aAppCache) { + nsCOMPtr<nsIFile> profileDirectory; + aAppCache->GetProfileDirectory(getter_AddRefs(profileDirectory)); + if (profileDirectory) rv = session->SetProfileDirectory(profileDirectory); + NS_ENSURE_SUCCESS(rv, rv); + } + + session.forget(_result); + return NS_OK; +} + +} // namespace + +NS_IMPL_ISUPPORTS_INHERITED(_OldCacheLoad, Runnable, nsICacheListener) + +_OldCacheLoad::_OldCacheLoad(const nsACString& aScheme, + const nsACString& aCacheKey, + nsICacheEntryOpenCallback* aCallback, + nsIApplicationCache* aAppCache, + nsILoadContextInfo* aLoadInfo, bool aWriteToDisk, + uint32_t aFlags) + : Runnable("net::_OldCacheLoad"), + mScheme(aScheme), + mCacheKey(aCacheKey), + mCallback(aCallback), + mLoadInfo(GetLoadContextInfo(aLoadInfo)), + mFlags(aFlags), + mWriteToDisk(aWriteToDisk), + mNew(true), + mOpening(true), + mSync(false), + mStatus(NS_ERROR_UNEXPECTED), + mRunCount(0), + mAppCache(aAppCache) {} + +_OldCacheLoad::~_OldCacheLoad() { + ProxyReleaseMainThread("_OldCacheLoad::mAppCache", mAppCache); +} + +nsresult _OldCacheLoad::Start() { + LOG(("_OldCacheLoad::Start [this=%p, key=%s]", this, mCacheKey.get())); + + mLoadStart = mozilla::TimeStamp::Now(); + + nsresult rv; + + // Consumers that can invoke this code as first and off the main thread + // are responsible for initiating these two services on the main thread. + // Currently no one does that. + + // XXX: Start the cache service; otherwise DispatchToCacheIOThread will + // fail. + nsCOMPtr<nsICacheService> service = + do_GetService(NS_CACHESERVICE_CONTRACTID, &rv); + + // Ensure the stream transport service gets initialized on the main thread + if (NS_SUCCEEDED(rv) && NS_IsMainThread()) { + nsCOMPtr<nsIStreamTransportService> sts = + do_GetService(kStreamTransportServiceCID, &rv); + } + + if (NS_SUCCEEDED(rv)) { + rv = service->GetCacheIOTarget(getter_AddRefs(mCacheThread)); + } + + if (NS_SUCCEEDED(rv)) { + bool onCacheTarget; + rv = mCacheThread->IsOnCurrentThread(&onCacheTarget); + if (NS_SUCCEEDED(rv) && onCacheTarget) { + mSync = true; + } + } + + if (NS_SUCCEEDED(rv)) { + if (mSync) { + rv = Run(); + } else { + rv = mCacheThread->Dispatch(this, NS_DISPATCH_NORMAL); + } + } + + return rv; +} + +NS_IMETHODIMP +_OldCacheLoad::Run() { + LOG(("_OldCacheLoad::Run [this=%p, key=%s, cb=%p]", this, mCacheKey.get(), + mCallback.get())); + + nsresult rv; + + if (mOpening) { + mOpening = false; + nsCOMPtr<nsICacheSession> session; + rv = GetCacheSession(mScheme, mWriteToDisk, mLoadInfo, mAppCache, + getter_AddRefs(session)); + if (NS_SUCCEEDED(rv)) { + // AsyncOpenCacheEntry isn't really async when its called on the + // cache service thread. + + nsCacheAccessMode cacheAccess; + if (mFlags & nsICacheStorage::OPEN_TRUNCATE) + cacheAccess = nsICache::ACCESS_WRITE; + else if ((mFlags & nsICacheStorage::OPEN_READONLY) || mAppCache) + cacheAccess = nsICache::ACCESS_READ; + else + cacheAccess = nsICache::ACCESS_READ_WRITE; + + LOG((" session->AsyncOpenCacheEntry with access=%d", cacheAccess)); + + bool bypassBusy = mFlags & nsICacheStorage::OPEN_BYPASS_IF_BUSY; + + if (mSync && cacheAccess == nsICache::ACCESS_WRITE) { + nsCOMPtr<nsICacheEntryDescriptor> entry; + rv = session->OpenCacheEntry(mCacheKey, cacheAccess, bypassBusy, + getter_AddRefs(entry)); + + nsCacheAccessMode grantedAccess = 0; + if (NS_SUCCEEDED(rv)) { + entry->GetAccessGranted(&grantedAccess); + } + + return OnCacheEntryAvailable(entry, grantedAccess, rv); + } + + rv = session->AsyncOpenCacheEntry(mCacheKey, cacheAccess, this, + bypassBusy); + if (NS_SUCCEEDED(rv)) return NS_OK; + } + + // Opening failed, propagate the error to the consumer + LOG((" Opening cache entry failed with rv=0x%08" PRIx32, + static_cast<uint32_t>(rv))); + mStatus = rv; + mNew = false; + NS_DispatchToMainThread(this); + } else { + if (!mCallback) { + LOG((" duplicate call, bypassed")); + return NS_OK; + } + + if (NS_SUCCEEDED(mStatus)) { + if (mFlags & nsICacheStorage::OPEN_TRUNCATE) { + mozilla::Telemetry::AccumulateTimeDelta( + mozilla::Telemetry::NETWORK_CACHE_V1_TRUNCATE_TIME_MS, mLoadStart); + } else if (mNew) { + mozilla::Telemetry::AccumulateTimeDelta( + mozilla::Telemetry::NETWORK_CACHE_V1_MISS_TIME_MS, mLoadStart); + } else { + mozilla::Telemetry::AccumulateTimeDelta( + mozilla::Telemetry::NETWORK_CACHE_V1_HIT_TIME_MS, mLoadStart); + } + } + + if (!(mFlags & CHECK_MULTITHREADED)) Check(); + + // break cycles + nsCOMPtr<nsICacheEntryOpenCallback> cb = std::move(mCallback); + mCacheThread = nullptr; + nsCOMPtr<nsICacheEntry> entry = std::move(mCacheEntry); + + rv = cb->OnCacheEntryAvailable(entry, mNew, mAppCache, mStatus); + + if (NS_FAILED(rv) && entry) { + LOG((" cb->OnCacheEntryAvailable failed with rv=0x%08" PRIx32, + static_cast<uint32_t>(rv))); + if (mNew) + entry->AsyncDoom(nullptr); + else + entry->Close(); + } + } + + return rv; +} + +NS_IMETHODIMP +_OldCacheLoad::OnCacheEntryAvailable(nsICacheEntryDescriptor* entry, + nsCacheAccessMode access, + nsresult status) { + LOG( + ("_OldCacheLoad::OnCacheEntryAvailable [this=%p, ent=%p, cb=%p, " + "appcache=%p, access=%x]", + this, entry, mCallback.get(), mAppCache.get(), access)); + + // XXX Bug 759805: Sometimes we will call this method directly from + // HttpCacheQuery::Run when AsyncOpenCacheEntry fails, but + // AsyncOpenCacheEntry will also call this method. As a workaround, we just + // ensure we only execute this code once. + NS_ENSURE_TRUE(mRunCount == 0, NS_ERROR_UNEXPECTED); + ++mRunCount; + + mCacheEntry = entry ? new _OldCacheEntryWrapper(entry) : nullptr; + mStatus = status; + mNew = access == nsICache::ACCESS_WRITE; + + if (mFlags & CHECK_MULTITHREADED) Check(); + + if (mSync) return Run(); + + return NS_DispatchToMainThread(this); +} + +void _OldCacheLoad::Check() { + if (!mCacheEntry) return; + + if (mNew) return; + + uint32_t result; + nsresult rv = mCallback->OnCacheEntryCheck(mCacheEntry, mAppCache, &result); + LOG((" OnCacheEntryCheck result ent=%p, cb=%p, appcache=%p, rv=0x%08" PRIx32 + ", result=%d", + mCacheEntry.get(), mCallback.get(), mAppCache.get(), + static_cast<uint32_t>(rv), result)); + + if (NS_FAILED(rv)) { + NS_WARNING("cache check failed"); + } + + if (NS_FAILED(rv) || result == nsICacheEntryOpenCallback::ENTRY_NOT_WANTED) { + mCacheEntry->Close(); + mCacheEntry = nullptr; + mStatus = NS_ERROR_CACHE_KEY_NOT_FOUND; + } +} + +NS_IMETHODIMP +_OldCacheLoad::OnCacheEntryDoomed(nsresult) { return NS_ERROR_NOT_IMPLEMENTED; } + +// nsICacheStorage old cache wrapper + +NS_IMPL_ISUPPORTS(_OldStorage, nsICacheStorage) + +_OldStorage::_OldStorage(nsILoadContextInfo* aInfo, bool aAllowDisk, + bool aLookupAppCache, bool aOfflineStorage, + nsIApplicationCache* aAppCache) + : mLoadInfo(GetLoadContextInfo(aInfo)), + mAppCache(aAppCache), + mWriteToDisk(aAllowDisk), + mLookupAppCache(aLookupAppCache), + mOfflineStorage(aOfflineStorage) {} + +_OldStorage::~_OldStorage() = default; + +NS_IMETHODIMP _OldStorage::AsyncOpenURI(nsIURI* aURI, + const nsACString& aIdExtension, + uint32_t aFlags, + nsICacheEntryOpenCallback* aCallback) { + NS_ENSURE_ARG(aURI); + NS_ENSURE_ARG(aCallback); + +#ifdef MOZ_LOGGING + nsAutoCString uriSpec; + aURI->GetAsciiSpec(uriSpec); + LOG(("_OldStorage::AsyncOpenURI [this=%p, uri=%s, ide=%s, flags=%x]", this, + uriSpec.get(), aIdExtension.BeginReading(), aFlags)); +#endif + + nsresult rv; + + nsAutoCString cacheKey, scheme; + rv = AssembleCacheKey(aURI, aIdExtension, cacheKey, scheme); + NS_ENSURE_SUCCESS(rv, rv); + + if (!mAppCache && (mLookupAppCache || mOfflineStorage)) { + rv = ChooseApplicationCache(cacheKey, getter_AddRefs(mAppCache)); + NS_ENSURE_SUCCESS(rv, rv); + + if (mAppCache) { + // From a chosen appcache open only as readonly + aFlags &= ~nsICacheStorage::OPEN_TRUNCATE; + } + } + + RefPtr<_OldCacheLoad> cacheLoad = new _OldCacheLoad( + scheme, cacheKey, aCallback, mAppCache, mLoadInfo, mWriteToDisk, aFlags); + + rv = cacheLoad->Start(); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +NS_IMETHODIMP _OldStorage::OpenTruncate(nsIURI* aURI, + const nsACString& aIdExtension, + nsICacheEntry** aCacheEntry) { + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP _OldStorage::Exists(nsIURI* aURI, const nsACString& aIdExtension, + bool* aResult) { + return NS_ERROR_NOT_AVAILABLE; +} + +NS_IMETHODIMP _OldStorage::AsyncDoomURI(nsIURI* aURI, + const nsACString& aIdExtension, + nsICacheEntryDoomCallback* aCallback) { + LOG(("_OldStorage::AsyncDoomURI")); + + nsresult rv; + + nsAutoCString cacheKey, scheme; + rv = AssembleCacheKey(aURI, aIdExtension, cacheKey, scheme); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsICacheSession> session; + rv = GetCacheSession(scheme, mWriteToDisk, mLoadInfo, mAppCache, + getter_AddRefs(session)); + NS_ENSURE_SUCCESS(rv, rv); + + RefPtr<DoomCallbackWrapper> cb = + aCallback ? new DoomCallbackWrapper(aCallback) : nullptr; + rv = session->DoomEntry(cacheKey, cb); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +NS_IMETHODIMP _OldStorage::AsyncEvictStorage( + nsICacheEntryDoomCallback* aCallback) { + LOG(("_OldStorage::AsyncEvictStorage")); + + nsresult rv; + + if (!mAppCache && mOfflineStorage) { + nsCOMPtr<nsIApplicationCacheService> appCacheService = + do_GetService(NS_APPLICATIONCACHESERVICE_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + rv = appCacheService->Evict(mLoadInfo); + NS_ENSURE_SUCCESS(rv, rv); + } else if (mAppCache) { + nsCOMPtr<nsICacheSession> session; + rv = GetCacheSession(""_ns, mWriteToDisk, mLoadInfo, mAppCache, + getter_AddRefs(session)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = session->EvictEntries(); + NS_ENSURE_SUCCESS(rv, rv); + } else { + // Oh, I'll be so happy when session names are gone... + nsCOMPtr<nsICacheSession> session; + rv = GetCacheSession("http"_ns, mWriteToDisk, mLoadInfo, mAppCache, + getter_AddRefs(session)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = session->EvictEntries(); + NS_ENSURE_SUCCESS(rv, rv); + + // This clears any data from schemes other than http or ftp. + rv = GetCacheSession(""_ns, mWriteToDisk, mLoadInfo, mAppCache, + getter_AddRefs(session)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = session->EvictEntries(); + NS_ENSURE_SUCCESS(rv, rv); + } + + if (aCallback) { + RefPtr<DoomCallbackSynchronizer> sync = + new DoomCallbackSynchronizer(aCallback); + rv = sync->Dispatch(); + NS_ENSURE_SUCCESS(rv, rv); + } + + return NS_OK; +} + +NS_IMETHODIMP _OldStorage::AsyncVisitStorage(nsICacheStorageVisitor* aVisitor, + bool aVisitEntries) { + LOG(("_OldStorage::AsyncVisitStorage")); + + NS_ENSURE_ARG(aVisitor); + + nsresult rv; + + nsCOMPtr<nsICacheService> serv = + do_GetService(NS_CACHESERVICE_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + char* deviceID; + if (mAppCache || mOfflineStorage) { + deviceID = const_cast<char*>("offline"); + } else if (!mWriteToDisk || mLoadInfo->IsPrivate()) { + deviceID = const_cast<char*>("memory"); + } else { + deviceID = const_cast<char*>("disk"); + } + + RefPtr<_OldVisitCallbackWrapper> cb = new _OldVisitCallbackWrapper( + deviceID, aVisitor, aVisitEntries, mLoadInfo); + rv = nsCacheService::GlobalInstance()->VisitEntriesInternal(cb); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +NS_IMETHODIMP _OldStorage::GetCacheIndexEntryAttrs( + nsIURI* aURI, const nsACString& aIdExtension, bool* aHasAltData, + uint32_t* aSizeInKB) { + return NS_ERROR_NOT_IMPLEMENTED; +} + +// Internal + +nsresult _OldStorage::AssembleCacheKey(nsIURI* aURI, + nsACString const& aIdExtension, + nsACString& aCacheKey, + nsACString& aScheme) { + // Copied from nsHttpChannel::AssembleCacheKey + + aCacheKey.Truncate(); + + nsresult rv; + + rv = aURI->GetScheme(aScheme); + NS_ENSURE_SUCCESS(rv, rv); + + nsAutoCString uriSpec; + if (aScheme.EqualsLiteral("http") || aScheme.EqualsLiteral("https")) { + if (mLoadInfo->IsAnonymous()) { + aCacheKey.AssignLiteral("anon&"); + } + + if (!aIdExtension.IsEmpty()) { + aCacheKey.AppendPrintf("id=%s&", aIdExtension.BeginReading()); + } + + nsCOMPtr<nsIURI> noRefURI; + rv = NS_GetURIWithoutRef(aURI, getter_AddRefs(noRefURI)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = noRefURI->GetAsciiSpec(uriSpec); + NS_ENSURE_SUCCESS(rv, rv); + + if (!aCacheKey.IsEmpty()) { + aCacheKey.AppendLiteral("uri="); + } + } else { + rv = aURI->GetAsciiSpec(uriSpec); + NS_ENSURE_SUCCESS(rv, rv); + } + + aCacheKey.Append(uriSpec); + + return NS_OK; +} + +nsresult _OldStorage::ChooseApplicationCache(const nsACString& cacheKey, + nsIApplicationCache** aCache) { + nsresult rv; + + nsCOMPtr<nsIApplicationCacheService> appCacheService = + do_GetService(NS_APPLICATIONCACHESERVICE_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + rv = appCacheService->ChooseApplicationCache(cacheKey, mLoadInfo, aCache); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +} // namespace net +} // namespace mozilla |