/* -*- 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 mCB; }; nsresult DoomCallbackSynchronizer::Dispatch() { nsresult rv; nsCOMPtr serv = do_GetService(NS_CACHESERVICE_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr 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 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 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 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 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 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 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 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 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 serv = do_GetService(NS_CACHESERVICE_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr 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 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 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 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 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 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(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 cb = std::move(mCallback); mCacheThread = nullptr; nsCOMPtr 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(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(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 session; rv = GetCacheSession(scheme, mWriteToDisk, mLoadInfo, mAppCache, getter_AddRefs(session)); NS_ENSURE_SUCCESS(rv, rv); RefPtr 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 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 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 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 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 serv = do_GetService(NS_CACHESERVICE_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); char* deviceID; if (mAppCache || mOfflineStorage) { deviceID = const_cast("offline"); } else if (!mWriteToDisk || mLoadInfo->IsPrivate()) { deviceID = const_cast("memory"); } else { deviceID = const_cast("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 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 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