diff options
Diffstat (limited to 'netwerk/cache2/OldWrappers.cpp')
-rw-r--r-- | netwerk/cache2/OldWrappers.cpp | 1155 |
1 files changed, 1155 insertions, 0 deletions
diff --git a/netwerk/cache2/OldWrappers.cpp b/netwerk/cache2/OldWrappers.cpp new file mode 100644 index 000000000..81df88df0 --- /dev/null +++ b/netwerk/cache2/OldWrappers.cpp @@ -0,0 +1,1155 @@ +// 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/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) : mCB(cb) + { + MOZ_COUNT_CTOR(DoomCallbackSynchronizer); + } + nsresult Dispatch(); + +private: + virtual ~DoomCallbackSynchronizer() + { + MOZ_COUNT_DTOR(DoomCallbackSynchronizer); + } + + 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) + { + MOZ_COUNT_CTOR(DoomCallbackWrapper); + } + +private: + virtual ~DoomCallbackWrapper() + { + MOZ_COUNT_DTOR(DoomCallbackWrapper); + } + + 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(); + } + + MOZ_COUNT_DTOR(_OldVisitCallbackWrapper); +} + +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->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. + nsXPIDLCString clientId; + rv = entryInfo->GetClientID(getter_Copies(clientId)); + if (NS_FAILED(rv)) + return NS_OK; + + if (mLoadInfo->IsPrivate() != + StringBeginsWith(clientId, NS_LITERAL_CSTRING("HTTP-memory-only-PB"))) { + return NS_OK; + } + + nsAutoCString cacheKey, enhanceId; + rv = entryInfo->GetKey(cacheKey); + if (NS_FAILED(rv)) + return NS_OK; + + if (StringBeginsWith(cacheKey, NS_LITERAL_CSTRING("anon&"))) { + if (!mLoadInfo->IsAnonymous()) + return NS_OK; + + cacheKey = Substring(cacheKey, 5, cacheKey.Length()); + } else if (mLoadInfo->IsAnonymous()) { + return NS_OK; + } + + if (StringBeginsWith(cacheKey, NS_LITERAL_CSTRING("id="))) { + 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, NS_LITERAL_CSTRING("uri="))) { + 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); + + *_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) + : 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) +{ + MOZ_COUNT_CTOR(_OldCacheEntryWrapper); + LOG(("Creating _OldCacheEntryWrapper %p for descriptor %p", this, desc)); +} + +_OldCacheEntryWrapper::_OldCacheEntryWrapper(nsICacheEntryInfo* info) +: mOldDesc(nullptr), mOldInfo(info) +{ + MOZ_COUNT_CTOR(_OldCacheEntryWrapper); + LOG(("Creating _OldCacheEntryWrapper %p for info %p", this, info)); +} + +_OldCacheEntryWrapper::~_OldCacheEntryWrapper() +{ + MOZ_COUNT_DTOR(_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::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, + 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() {} + + 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 { + +nsresult +GetCacheSessionNameForStoragePolicy( + nsCSubstring const &scheme, + nsCacheStoragePolicy storagePolicy, + bool isPrivate, + NeckoOriginAttributes 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; + } + } + // WYCIWYG + else if (scheme.EqualsLiteral("wyciwyg")) { + if (isPrivate) + sessionName.AssignLiteral("wyciwyg-private"); + else + sessionName.AssignLiteral("wyciwyg"); + } + // 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); + + return NS_OK; +} + +nsresult +GetCacheSession(nsCSubstring const &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 { + rv = GetCacheSessionNameForStoragePolicy( + aScheme, + storagePolicy, + aLoadInfo->IsPrivate(), + aLoadInfo->OriginAttributesPtr(), + clientId); + NS_ENSURE_SUCCESS(rv, rv); + } + + 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(nsCSubstring const& aScheme, + nsCSubstring const& aCacheKey, + nsICacheEntryOpenCallback* aCallback, + nsIApplicationCache* aAppCache, + nsILoadContextInfo* aLoadInfo, + bool aWriteToDisk, + uint32_t aFlags) + : 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) +{ + MOZ_COUNT_CTOR(_OldCacheLoad); +} + +_OldCacheLoad::~_OldCacheLoad() +{ + ProxyReleaseMainThread(mAppCache); + MOZ_COUNT_DTOR(_OldCacheLoad); +} + +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 this is only nsWyciwygChannel. + + // 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%08x", 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 = mCallback.forget(); + mCacheThread = nullptr; + nsCOMPtr<nsICacheEntry> entry = mCacheEntry.forget(); + + rv = cb->OnCacheEntryAvailable(entry, mNew, mAppCache, mStatus); + + if (NS_FAILED(rv) && entry) { + LOG((" cb->OnCacheEntryAvailable failed with rv=0x%08x", 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%08x, result=%d", + mCacheEntry.get(), mCallback.get(), mAppCache.get(), 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) +{ + MOZ_COUNT_CTOR(_OldStorage); +} + +_OldStorage::~_OldStorage() +{ + MOZ_COUNT_DTOR(_OldStorage); +} + +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(EmptyCString(), + 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(NS_LITERAL_CSTRING("http"), + mWriteToDisk, mLoadInfo, mAppCache, + getter_AddRefs(session)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = session->EvictEntries(); + NS_ENSURE_SUCCESS(rv, rv); + + rv = GetCacheSession(NS_LITERAL_CSTRING("wyciwyg"), + mWriteToDisk, mLoadInfo, mAppCache, + getter_AddRefs(session)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = session->EvictEntries(); + NS_ENSURE_SUCCESS(rv, rv); + + // This clears any data from scheme other then http, wyciwyg or ftp + rv = GetCacheSession(EmptyCString(), + 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; +} + +// 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 = aURI->CloneIgnoringRef(getter_AddRefs(noRefURI)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = noRefURI->GetAsciiSpec(uriSpec); + NS_ENSURE_SUCCESS(rv, rv); + + if (!aCacheKey.IsEmpty()) { + aCacheKey.AppendLiteral("uri="); + } + } + else if (aScheme.EqualsLiteral("wyciwyg")) { + rv = aURI->GetSpec(uriSpec); + NS_ENSURE_SUCCESS(rv, rv); + } + else { + rv = aURI->GetAsciiSpec(uriSpec); + NS_ENSURE_SUCCESS(rv, rv); + } + + aCacheKey.Append(uriSpec); + + return NS_OK; +} + +nsresult _OldStorage::ChooseApplicationCache(nsCSubstring const &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 |