michael@0: // Stuff to link the old imp to the new api - will go away! michael@0: michael@0: #include "CacheLog.h" michael@0: #include "OldWrappers.h" michael@0: #include "CacheStorage.h" michael@0: #include "CacheStorageService.h" michael@0: #include "LoadContextInfo.h" michael@0: michael@0: #include "nsIURI.h" michael@0: #include "nsICacheService.h" michael@0: #include "nsICacheSession.h" michael@0: #include "nsIApplicationCache.h" michael@0: #include "nsIApplicationCacheService.h" michael@0: #include "nsIStreamTransportService.h" michael@0: #include "nsIFile.h" michael@0: #include "nsICacheEntryDoomCallback.h" michael@0: #include "nsICacheListener.h" michael@0: #include "nsICacheStorageVisitor.h" michael@0: michael@0: #include "nsServiceManagerUtils.h" michael@0: #include "nsNetCID.h" michael@0: #include "nsProxyRelease.h" michael@0: #include "mozilla/Telemetry.h" michael@0: michael@0: static NS_DEFINE_CID(kStreamTransportServiceCID, michael@0: NS_STREAMTRANSPORTSERVICE_CID); michael@0: michael@0: static uint32_t const CHECK_MULTITHREADED = nsICacheStorage::CHECK_MULTITHREADED; michael@0: michael@0: namespace mozilla { michael@0: namespace net { michael@0: michael@0: namespace { // anon michael@0: michael@0: // Fires the doom callback back on the main thread michael@0: // after the cache I/O thread is looped. michael@0: michael@0: class DoomCallbackSynchronizer : public nsRunnable michael@0: { michael@0: public: michael@0: DoomCallbackSynchronizer(nsICacheEntryDoomCallback* cb) : mCB(cb) michael@0: { michael@0: MOZ_COUNT_CTOR(DoomCallbackSynchronizer); michael@0: } michael@0: nsresult Dispatch(); michael@0: michael@0: private: michael@0: virtual ~DoomCallbackSynchronizer() michael@0: { michael@0: MOZ_COUNT_DTOR(DoomCallbackSynchronizer); michael@0: } michael@0: michael@0: NS_DECL_NSIRUNNABLE michael@0: nsCOMPtr mCB; michael@0: }; michael@0: michael@0: nsresult DoomCallbackSynchronizer::Dispatch() michael@0: { michael@0: nsresult rv; michael@0: michael@0: nsCOMPtr serv = michael@0: do_GetService(NS_CACHESERVICE_CONTRACTID, &rv); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsCOMPtr eventTarget; michael@0: rv = serv->GetCacheIOTarget(getter_AddRefs(eventTarget)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = eventTarget->Dispatch(this, NS_DISPATCH_NORMAL); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP DoomCallbackSynchronizer::Run() michael@0: { michael@0: if (!NS_IsMainThread()) { michael@0: NS_DispatchToMainThread(this); michael@0: } michael@0: else { michael@0: if (mCB) michael@0: mCB->OnCacheEntryDoomed(NS_OK); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Receives doom callback from the old API and forwards to the new API michael@0: michael@0: class DoomCallbackWrapper : public nsICacheListener michael@0: { michael@0: NS_DECL_THREADSAFE_ISUPPORTS michael@0: NS_DECL_NSICACHELISTENER michael@0: michael@0: DoomCallbackWrapper(nsICacheEntryDoomCallback* cb) : mCB(cb) michael@0: { michael@0: MOZ_COUNT_CTOR(DoomCallbackWrapper); michael@0: } michael@0: michael@0: private: michael@0: virtual ~DoomCallbackWrapper() michael@0: { michael@0: MOZ_COUNT_DTOR(DoomCallbackWrapper); michael@0: } michael@0: michael@0: nsCOMPtr mCB; michael@0: }; michael@0: michael@0: NS_IMPL_ISUPPORTS(DoomCallbackWrapper, nsICacheListener); michael@0: michael@0: NS_IMETHODIMP DoomCallbackWrapper::OnCacheEntryAvailable(nsICacheEntryDescriptor *descriptor, michael@0: nsCacheAccessMode accessGranted, michael@0: nsresult status) michael@0: { michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP DoomCallbackWrapper::OnCacheEntryDoomed(nsresult status) michael@0: { michael@0: if (!mCB) michael@0: return NS_ERROR_NULL_POINTER; michael@0: michael@0: mCB->OnCacheEntryDoomed(status); michael@0: mCB = nullptr; michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Receives visit callbacks from the old API and forwards it to the new API michael@0: michael@0: class VisitCallbackWrapper : public nsICacheVisitor michael@0: { michael@0: NS_DECL_THREADSAFE_ISUPPORTS michael@0: NS_DECL_NSICACHEVISITOR michael@0: michael@0: VisitCallbackWrapper(char* const deviceID, michael@0: nsICacheStorageVisitor* cb, michael@0: bool visitEntries) michael@0: : mCB(cb) michael@0: , mVisitEntries(visitEntries) michael@0: , mDeviceID(deviceID) michael@0: { michael@0: MOZ_COUNT_CTOR(VisitCallbackWrapper); michael@0: } michael@0: michael@0: private: michael@0: virtual ~VisitCallbackWrapper(); michael@0: nsCOMPtr mCB; michael@0: bool mVisitEntries; michael@0: char* const mDeviceID; michael@0: }; michael@0: michael@0: NS_IMPL_ISUPPORTS(VisitCallbackWrapper, nsICacheVisitor) michael@0: michael@0: VisitCallbackWrapper::~VisitCallbackWrapper() michael@0: { michael@0: if (mVisitEntries) michael@0: mCB->OnCacheEntryVisitCompleted(); michael@0: michael@0: MOZ_COUNT_DTOR(VisitCallbackWrapper); michael@0: } michael@0: michael@0: NS_IMETHODIMP VisitCallbackWrapper::VisitDevice(const char * deviceID, michael@0: nsICacheDeviceInfo *deviceInfo, michael@0: bool *_retval) michael@0: { michael@0: if (!mCB) michael@0: return NS_ERROR_NULL_POINTER; michael@0: michael@0: *_retval = false; michael@0: if (strcmp(deviceID, mDeviceID)) { michael@0: // Not the device we want to visit michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult rv; michael@0: michael@0: uint32_t entryCount; michael@0: rv = deviceInfo->GetEntryCount(&entryCount); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: uint32_t totalSize; michael@0: rv = deviceInfo->GetTotalSize(&totalSize); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: mCB->OnCacheStorageInfo(entryCount, totalSize); michael@0: *_retval = mVisitEntries; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP VisitCallbackWrapper::VisitEntry(const char * deviceID, michael@0: nsICacheEntryInfo *entryInfo, michael@0: bool *_retval) michael@0: { michael@0: MOZ_ASSERT(!strcmp(deviceID, mDeviceID)); michael@0: michael@0: nsRefPtr<_OldCacheEntryWrapper> wrapper = new _OldCacheEntryWrapper(entryInfo); michael@0: nsresult rv = mCB->OnCacheEntryInfo(wrapper); michael@0: *_retval = NS_SUCCEEDED(rv); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: } // anon michael@0: michael@0: michael@0: // _OldGetDiskConsumption michael@0: michael@0: //static michael@0: nsresult _OldGetDiskConsumption::Get(nsICacheStorageConsumptionObserver* aCallback) michael@0: { michael@0: nsresult rv; michael@0: michael@0: nsCOMPtr serv = michael@0: do_GetService(NS_CACHESERVICE_CONTRACTID, &rv); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsRefPtr<_OldGetDiskConsumption> cb = new _OldGetDiskConsumption(aCallback); michael@0: michael@0: // _OldGetDiskConsumption stores the found size value, but until dispatched michael@0: // to the main thread it doesn't call on the consupmtion observer. See bellow. michael@0: rv = serv->VisitEntries(cb); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // We are called from CacheStorageService::AsyncGetDiskConsumption whose IDL michael@0: // documentation claims the callback is always delievered asynchronously michael@0: // back to the main thread. Despite we know the result synchronosusly when michael@0: // querying the old cache, we need to stand the word and dispatch the result michael@0: // to the main thread asynchronously. Hence the dispatch here. michael@0: return NS_DispatchToMainThread(cb); michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS_INHERITED(_OldGetDiskConsumption, michael@0: nsRunnable, michael@0: nsICacheVisitor) michael@0: michael@0: _OldGetDiskConsumption::_OldGetDiskConsumption( michael@0: nsICacheStorageConsumptionObserver* aCallback) michael@0: : mCallback(aCallback) michael@0: , mSize(0) michael@0: { michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: _OldGetDiskConsumption::Run() michael@0: { michael@0: mCallback->OnNetworkCacheDiskConsumption(mSize); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: _OldGetDiskConsumption::VisitDevice(const char * deviceID, michael@0: nsICacheDeviceInfo *deviceInfo, michael@0: bool *_retval) michael@0: { michael@0: if (!strcmp(deviceID, "disk")) { michael@0: uint32_t size; michael@0: nsresult rv = deviceInfo->GetTotalSize(&size); michael@0: if (NS_SUCCEEDED(rv)) michael@0: mSize = (int64_t)size; michael@0: } michael@0: michael@0: *_retval = false; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: _OldGetDiskConsumption::VisitEntry(const char * deviceID, michael@0: nsICacheEntryInfo *entryInfo, michael@0: bool *_retval) michael@0: { michael@0: MOZ_CRASH("Unexpected"); michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: // _OldCacheEntryWrapper michael@0: michael@0: _OldCacheEntryWrapper::_OldCacheEntryWrapper(nsICacheEntryDescriptor* desc) michael@0: : mOldDesc(desc), mOldInfo(desc) michael@0: { michael@0: MOZ_COUNT_CTOR(_OldCacheEntryWrapper); michael@0: LOG(("Creating _OldCacheEntryWrapper %p for descriptor %p", this, desc)); michael@0: } michael@0: michael@0: _OldCacheEntryWrapper::_OldCacheEntryWrapper(nsICacheEntryInfo* info) michael@0: : mOldDesc(nullptr), mOldInfo(info) michael@0: { michael@0: MOZ_COUNT_CTOR(_OldCacheEntryWrapper); michael@0: LOG(("Creating _OldCacheEntryWrapper %p for info %p", this, info)); michael@0: } michael@0: michael@0: _OldCacheEntryWrapper::~_OldCacheEntryWrapper() michael@0: { michael@0: MOZ_COUNT_DTOR(_OldCacheEntryWrapper); michael@0: LOG(("Destroying _OldCacheEntryWrapper %p for descriptor %p", this, mOldInfo.get())); michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS(_OldCacheEntryWrapper, nsICacheEntry) michael@0: michael@0: NS_IMETHODIMP _OldCacheEntryWrapper::AsyncDoom(nsICacheEntryDoomCallback* listener) michael@0: { michael@0: nsRefPtr cb = listener michael@0: ? new DoomCallbackWrapper(listener) michael@0: : nullptr; michael@0: return AsyncDoom(cb); michael@0: } michael@0: michael@0: NS_IMETHODIMP _OldCacheEntryWrapper::GetDataSize(int64_t *aSize) michael@0: { michael@0: uint32_t size; michael@0: nsresult rv = GetDataSize(&size); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: *aSize = size; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP _OldCacheEntryWrapper::GetPersistent(bool *aPersistToDisk) michael@0: { michael@0: if (!mOldDesc) { michael@0: return NS_ERROR_NULL_POINTER; michael@0: } michael@0: michael@0: nsresult rv; michael@0: michael@0: nsCacheStoragePolicy policy; michael@0: rv = mOldDesc->GetStoragePolicy(&policy); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: *aPersistToDisk = policy != nsICache::STORE_IN_MEMORY; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP _OldCacheEntryWrapper::Recreate(bool aMemoryOnly, michael@0: nsICacheEntry** aResult) michael@0: { michael@0: NS_ENSURE_TRUE(mOldDesc, NS_ERROR_NOT_AVAILABLE); michael@0: michael@0: nsCacheAccessMode mode; michael@0: nsresult rv = mOldDesc->GetAccessGranted(&mode); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (!(mode & nsICache::ACCESS_WRITE)) michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: michael@0: LOG(("_OldCacheEntryWrapper::Recreate [this=%p]", this)); michael@0: michael@0: if (aMemoryOnly) michael@0: mOldDesc->SetStoragePolicy(nsICache::STORE_IN_MEMORY); michael@0: michael@0: nsCOMPtr self(this); michael@0: self.forget(aResult); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP _OldCacheEntryWrapper::OpenInputStream(int64_t offset, michael@0: nsIInputStream * *_retval) michael@0: { michael@0: if (offset > PR_UINT32_MAX) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: return OpenInputStream(uint32_t(offset), _retval); michael@0: } michael@0: NS_IMETHODIMP _OldCacheEntryWrapper::OpenOutputStream(int64_t offset, michael@0: nsIOutputStream * *_retval) michael@0: { michael@0: if (offset > PR_UINT32_MAX) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: return OpenOutputStream(uint32_t(offset), _retval); michael@0: } michael@0: michael@0: NS_IMETHODIMP _OldCacheEntryWrapper::MaybeMarkValid() michael@0: { michael@0: LOG(("_OldCacheEntryWrapper::MaybeMarkValid [this=%p]", this)); michael@0: michael@0: NS_ENSURE_TRUE(mOldDesc, NS_ERROR_NULL_POINTER); michael@0: michael@0: nsCacheAccessMode mode; michael@0: nsresult rv = mOldDesc->GetAccessGranted(&mode); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (mode & nsICache::ACCESS_WRITE) { michael@0: LOG(("Marking cache entry valid [entry=%p, descr=%p]", this, mOldDesc)); michael@0: return mOldDesc->MarkValid(); michael@0: } michael@0: michael@0: LOG(("Not marking read-only cache entry valid [entry=%p, descr=%p]", this, mOldDesc)); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP _OldCacheEntryWrapper::HasWriteAccess(bool aWriteAllowed_unused, bool *aWriteAccess) michael@0: { michael@0: NS_ENSURE_TRUE(mOldDesc, NS_ERROR_NULL_POINTER); michael@0: NS_ENSURE_ARG(aWriteAccess); michael@0: michael@0: nsCacheAccessMode mode; michael@0: nsresult rv = mOldDesc->GetAccessGranted(&mode); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: *aWriteAccess = !!(mode & nsICache::ACCESS_WRITE); michael@0: michael@0: LOG(("_OldCacheEntryWrapper::HasWriteAccess [this=%p, write-access=%d]", this, *aWriteAccess)); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: namespace { // anon michael@0: michael@0: nsresult michael@0: GetCacheSessionNameForStoragePolicy( michael@0: nsCSubstring const &scheme, michael@0: nsCacheStoragePolicy storagePolicy, michael@0: bool isPrivate, michael@0: uint32_t appId, michael@0: bool inBrowser, michael@0: nsACString& sessionName) michael@0: { michael@0: MOZ_ASSERT(!isPrivate || storagePolicy == nsICache::STORE_IN_MEMORY); michael@0: michael@0: // HTTP michael@0: if (scheme.Equals(NS_LITERAL_CSTRING("http")) || michael@0: scheme.Equals(NS_LITERAL_CSTRING("https"))) { michael@0: switch (storagePolicy) { michael@0: case nsICache::STORE_IN_MEMORY: michael@0: if (isPrivate) michael@0: sessionName.Assign(NS_LITERAL_CSTRING("HTTP-memory-only-PB")); michael@0: else michael@0: sessionName.Assign(NS_LITERAL_CSTRING("HTTP-memory-only")); michael@0: break; michael@0: case nsICache::STORE_OFFLINE: michael@0: // XXX This is actually never used, only added to prevent michael@0: // any compatibility damage. michael@0: sessionName.Assign(NS_LITERAL_CSTRING("HTTP-offline")); michael@0: break; michael@0: default: michael@0: sessionName.Assign(NS_LITERAL_CSTRING("HTTP")); michael@0: break; michael@0: } michael@0: } michael@0: // WYCIWYG michael@0: else if (scheme.Equals(NS_LITERAL_CSTRING("wyciwyg"))) { michael@0: if (isPrivate) michael@0: sessionName.Assign(NS_LITERAL_CSTRING("wyciwyg-private")); michael@0: else michael@0: sessionName.Assign(NS_LITERAL_CSTRING("wyciwyg")); michael@0: } michael@0: // FTP michael@0: else if (scheme.Equals(NS_LITERAL_CSTRING("ftp"))) { michael@0: if (isPrivate) michael@0: sessionName.Assign(NS_LITERAL_CSTRING("FTP-private")); michael@0: else michael@0: sessionName.Assign(NS_LITERAL_CSTRING("FTP")); michael@0: } michael@0: // all remaining URL scheme michael@0: else { michael@0: // Since with the new API a consumer cannot specify its own session name michael@0: // and partitioning of the cache is handled stricly only by the cache michael@0: // back-end internally, we will use a separate session name to pretend michael@0: // functionality of the new API wrapping the Darin's cache for all other michael@0: // URL schemes. michael@0: // Deliberately omitting |anonymous| since other session types don't michael@0: // recognize it too. michael@0: sessionName.Assign(NS_LITERAL_CSTRING("other")); michael@0: if (isPrivate) michael@0: sessionName.Append(NS_LITERAL_CSTRING("-private")); michael@0: } michael@0: michael@0: if (appId != nsILoadContextInfo::NO_APP_ID || inBrowser) { michael@0: sessionName.Append('~'); michael@0: sessionName.AppendInt(appId); michael@0: sessionName.Append('~'); michael@0: sessionName.AppendInt(inBrowser); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: GetCacheSession(nsCSubstring const &aScheme, michael@0: bool aWriteToDisk, michael@0: nsILoadContextInfo* aLoadInfo, michael@0: nsIApplicationCache* aAppCache, michael@0: nsICacheSession** _result) michael@0: { michael@0: nsresult rv; michael@0: michael@0: nsCacheStoragePolicy storagePolicy; michael@0: if (aAppCache) michael@0: storagePolicy = nsICache::STORE_OFFLINE; michael@0: else if (!aWriteToDisk || aLoadInfo->IsPrivate()) michael@0: storagePolicy = nsICache::STORE_IN_MEMORY; michael@0: else michael@0: storagePolicy = nsICache::STORE_ANYWHERE; michael@0: michael@0: nsAutoCString clientId; michael@0: if (aAppCache) { michael@0: aAppCache->GetClientID(clientId); michael@0: } michael@0: else { michael@0: rv = GetCacheSessionNameForStoragePolicy( michael@0: aScheme, michael@0: storagePolicy, michael@0: aLoadInfo->IsPrivate(), michael@0: aLoadInfo->AppId(), michael@0: aLoadInfo->IsInBrowserElement(), michael@0: clientId); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: LOG((" GetCacheSession for client=%s, policy=%d", clientId.get(), storagePolicy)); michael@0: michael@0: nsCOMPtr serv = michael@0: do_GetService(NS_CACHESERVICE_CONTRACTID, &rv); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsCOMPtr session; michael@0: rv = serv->CreateSession(clientId.get(), michael@0: storagePolicy, michael@0: nsICache::STREAM_BASED, michael@0: getter_AddRefs(session)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = session->SetIsPrivate(aLoadInfo->IsPrivate()); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = session->SetDoomEntriesIfExpired(false); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (aAppCache) { michael@0: nsCOMPtr profileDirectory; michael@0: aAppCache->GetProfileDirectory(getter_AddRefs(profileDirectory)); michael@0: if (profileDirectory) michael@0: rv = session->SetProfileDirectory(profileDirectory); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: session.forget(_result); michael@0: return NS_OK; michael@0: } michael@0: michael@0: } // anon michael@0: michael@0: michael@0: NS_IMPL_ISUPPORTS_INHERITED(_OldCacheLoad, nsRunnable, nsICacheListener) michael@0: michael@0: _OldCacheLoad::_OldCacheLoad(nsCSubstring const& aScheme, michael@0: nsCSubstring const& aCacheKey, michael@0: nsICacheEntryOpenCallback* aCallback, michael@0: nsIApplicationCache* aAppCache, michael@0: nsILoadContextInfo* aLoadInfo, michael@0: bool aWriteToDisk, michael@0: uint32_t aFlags) michael@0: : mScheme(aScheme) michael@0: , mCacheKey(aCacheKey) michael@0: , mCallback(aCallback) michael@0: , mLoadInfo(GetLoadContextInfo(aLoadInfo)) michael@0: , mFlags(aFlags) michael@0: , mWriteToDisk(aWriteToDisk) michael@0: , mNew(true) michael@0: , mOpening(true) michael@0: , mSync(false) michael@0: , mStatus(NS_ERROR_UNEXPECTED) michael@0: , mRunCount(0) michael@0: , mAppCache(aAppCache) michael@0: { michael@0: MOZ_COUNT_CTOR(_OldCacheLoad); michael@0: } michael@0: michael@0: _OldCacheLoad::~_OldCacheLoad() michael@0: { michael@0: ProxyReleaseMainThread(mAppCache); michael@0: MOZ_COUNT_DTOR(_OldCacheLoad); michael@0: } michael@0: michael@0: nsresult _OldCacheLoad::Start() michael@0: { michael@0: LOG(("_OldCacheLoad::Start [this=%p, key=%s]", this, mCacheKey.get())); michael@0: michael@0: mLoadStart = mozilla::TimeStamp::Now(); michael@0: michael@0: nsresult rv; michael@0: michael@0: // Consumers that can invoke this code as first and off the main thread michael@0: // are responsible for initiating these two services on the main thread. michael@0: // Currently this is only nsWyciwygChannel. michael@0: michael@0: // XXX: Start the cache service; otherwise DispatchToCacheIOThread will michael@0: // fail. michael@0: nsCOMPtr service = michael@0: do_GetService(NS_CACHESERVICE_CONTRACTID, &rv); michael@0: michael@0: // Ensure the stream transport service gets initialized on the main thread michael@0: if (NS_SUCCEEDED(rv) && NS_IsMainThread()) { michael@0: nsCOMPtr sts = michael@0: do_GetService(kStreamTransportServiceCID, &rv); michael@0: } michael@0: michael@0: if (NS_SUCCEEDED(rv)) { michael@0: rv = service->GetCacheIOTarget(getter_AddRefs(mCacheThread)); michael@0: } michael@0: michael@0: if (NS_SUCCEEDED(rv)) { michael@0: bool onCacheTarget; michael@0: rv = mCacheThread->IsOnCurrentThread(&onCacheTarget); michael@0: if (NS_SUCCEEDED(rv) && onCacheTarget) { michael@0: mSync = true; michael@0: } michael@0: } michael@0: michael@0: if (NS_SUCCEEDED(rv)) { michael@0: if (mSync) { michael@0: rv = Run(); michael@0: } michael@0: else { michael@0: rv = mCacheThread->Dispatch(this, NS_DISPATCH_NORMAL); michael@0: } michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: _OldCacheLoad::Run() michael@0: { michael@0: LOG(("_OldCacheLoad::Run [this=%p, key=%s, cb=%p]", this, mCacheKey.get(), mCallback.get())); michael@0: michael@0: nsresult rv; michael@0: michael@0: if (mOpening) { michael@0: mOpening = false; michael@0: nsCOMPtr session; michael@0: rv = GetCacheSession(mScheme, mWriteToDisk, mLoadInfo, mAppCache, michael@0: getter_AddRefs(session)); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: // AsyncOpenCacheEntry isn't really async when its called on the michael@0: // cache service thread. michael@0: michael@0: nsCacheAccessMode cacheAccess; michael@0: if (mFlags & nsICacheStorage::OPEN_TRUNCATE) michael@0: cacheAccess = nsICache::ACCESS_WRITE; michael@0: else if ((mFlags & nsICacheStorage::OPEN_READONLY) || mAppCache) michael@0: cacheAccess = nsICache::ACCESS_READ; michael@0: else michael@0: cacheAccess = nsICache::ACCESS_READ_WRITE; michael@0: michael@0: LOG((" session->AsyncOpenCacheEntry with access=%d", cacheAccess)); michael@0: michael@0: bool bypassBusy = mFlags & nsICacheStorage::OPEN_BYPASS_IF_BUSY; michael@0: michael@0: if (mSync && cacheAccess == nsICache::ACCESS_WRITE) { michael@0: nsCOMPtr entry; michael@0: rv = session->OpenCacheEntry(mCacheKey, cacheAccess, bypassBusy, michael@0: getter_AddRefs(entry)); michael@0: michael@0: nsCacheAccessMode grantedAccess = 0; michael@0: if (NS_SUCCEEDED(rv)) { michael@0: entry->GetAccessGranted(&grantedAccess); michael@0: } michael@0: michael@0: return OnCacheEntryAvailable(entry, grantedAccess, rv); michael@0: } michael@0: michael@0: rv = session->AsyncOpenCacheEntry(mCacheKey, cacheAccess, this, bypassBusy); michael@0: if (NS_SUCCEEDED(rv)) michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Opening failed, propagate the error to the consumer michael@0: LOG((" Opening cache entry failed with rv=0x%08x", rv)); michael@0: mStatus = rv; michael@0: mNew = false; michael@0: NS_DispatchToMainThread(this); michael@0: } else { michael@0: if (!mCallback) { michael@0: LOG((" duplicate call, bypassed")); michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (NS_SUCCEEDED(mStatus)) { michael@0: if (mFlags & nsICacheStorage::OPEN_TRUNCATE) { michael@0: mozilla::Telemetry::AccumulateTimeDelta( michael@0: mozilla::Telemetry::NETWORK_CACHE_V1_TRUNCATE_TIME_MS, michael@0: mLoadStart); michael@0: } michael@0: else if (mNew) { michael@0: mozilla::Telemetry::AccumulateTimeDelta( michael@0: mozilla::Telemetry::NETWORK_CACHE_V1_MISS_TIME_MS, michael@0: mLoadStart); michael@0: } michael@0: else { michael@0: mozilla::Telemetry::AccumulateTimeDelta( michael@0: mozilla::Telemetry::NETWORK_CACHE_V1_HIT_TIME_MS, michael@0: mLoadStart); michael@0: } michael@0: } michael@0: michael@0: if (!(mFlags & CHECK_MULTITHREADED)) michael@0: Check(); michael@0: michael@0: // break cycles michael@0: nsCOMPtr cb = mCallback.forget(); michael@0: mCacheThread = nullptr; michael@0: nsCOMPtr entry = mCacheEntry.forget(); michael@0: michael@0: rv = cb->OnCacheEntryAvailable(entry, mNew, mAppCache, mStatus); michael@0: michael@0: if (NS_FAILED(rv) && entry) { michael@0: LOG((" cb->OnCacheEntryAvailable failed with rv=0x%08x", rv)); michael@0: if (mNew) michael@0: entry->AsyncDoom(nullptr); michael@0: else michael@0: entry->Close(); michael@0: } michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: _OldCacheLoad::OnCacheEntryAvailable(nsICacheEntryDescriptor *entry, michael@0: nsCacheAccessMode access, michael@0: nsresult status) michael@0: { michael@0: LOG(("_OldCacheLoad::OnCacheEntryAvailable [this=%p, ent=%p, cb=%p, appcache=%p, access=%x]", michael@0: this, entry, mCallback.get(), mAppCache.get(), access)); michael@0: michael@0: // XXX Bug 759805: Sometimes we will call this method directly from michael@0: // HttpCacheQuery::Run when AsyncOpenCacheEntry fails, but michael@0: // AsyncOpenCacheEntry will also call this method. As a workaround, we just michael@0: // ensure we only execute this code once. michael@0: NS_ENSURE_TRUE(mRunCount == 0, NS_ERROR_UNEXPECTED); michael@0: ++mRunCount; michael@0: michael@0: mCacheEntry = entry ? new _OldCacheEntryWrapper(entry) : nullptr; michael@0: mStatus = status; michael@0: mNew = access == nsICache::ACCESS_WRITE; michael@0: michael@0: if (mFlags & CHECK_MULTITHREADED) michael@0: Check(); michael@0: michael@0: if (mSync) michael@0: return Run(); michael@0: michael@0: return NS_DispatchToMainThread(this); michael@0: } michael@0: michael@0: void michael@0: _OldCacheLoad::Check() michael@0: { michael@0: if (!mCacheEntry) michael@0: return; michael@0: michael@0: if (mNew) michael@0: return; michael@0: michael@0: uint32_t result; michael@0: nsresult rv = mCallback->OnCacheEntryCheck(mCacheEntry, mAppCache, &result); michael@0: LOG((" OnCacheEntryCheck result ent=%p, cb=%p, appcache=%p, rv=0x%08x, result=%d", michael@0: mCacheEntry.get(), mCallback.get(), mAppCache.get(), rv, result)); michael@0: michael@0: if (NS_FAILED(rv)) { michael@0: NS_WARNING("cache check failed"); michael@0: } michael@0: michael@0: if (NS_FAILED(rv) || result == nsICacheEntryOpenCallback::ENTRY_NOT_WANTED) { michael@0: mCacheEntry->Close(); michael@0: mCacheEntry = nullptr; michael@0: mStatus = NS_ERROR_CACHE_KEY_NOT_FOUND; michael@0: } michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: _OldCacheLoad::OnCacheEntryDoomed(nsresult) michael@0: { michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: // nsICacheStorage old cache wrapper michael@0: michael@0: NS_IMPL_ISUPPORTS(_OldStorage, nsICacheStorage) michael@0: michael@0: _OldStorage::_OldStorage(nsILoadContextInfo* aInfo, michael@0: bool aAllowDisk, michael@0: bool aLookupAppCache, michael@0: bool aOfflineStorage, michael@0: nsIApplicationCache* aAppCache) michael@0: : mLoadInfo(GetLoadContextInfo(aInfo)) michael@0: , mAppCache(aAppCache) michael@0: , mWriteToDisk(aAllowDisk) michael@0: , mLookupAppCache(aLookupAppCache) michael@0: , mOfflineStorage(aOfflineStorage) michael@0: { michael@0: MOZ_COUNT_CTOR(_OldStorage); michael@0: } michael@0: michael@0: _OldStorage::~_OldStorage() michael@0: { michael@0: MOZ_COUNT_DTOR(_OldStorage); michael@0: } michael@0: michael@0: NS_IMETHODIMP _OldStorage::AsyncOpenURI(nsIURI *aURI, michael@0: const nsACString & aIdExtension, michael@0: uint32_t aFlags, michael@0: nsICacheEntryOpenCallback *aCallback) michael@0: { michael@0: NS_ENSURE_ARG(aURI); michael@0: NS_ENSURE_ARG(aCallback); michael@0: michael@0: #ifdef MOZ_LOGGING michael@0: nsAutoCString uriSpec; michael@0: aURI->GetAsciiSpec(uriSpec); michael@0: LOG(("_OldStorage::AsyncOpenURI [this=%p, uri=%s, ide=%s, flags=%x]", michael@0: this, uriSpec.get(), aIdExtension.BeginReading(), aFlags)); michael@0: #endif michael@0: michael@0: nsresult rv; michael@0: michael@0: nsAutoCString cacheKey, scheme; michael@0: rv = AssembleCacheKey(aURI, aIdExtension, cacheKey, scheme); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (!mAppCache && (mLookupAppCache || mOfflineStorage)) { michael@0: rv = ChooseApplicationCache(cacheKey, getter_AddRefs(mAppCache)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (mAppCache) { michael@0: // From a chosen appcache open only as readonly michael@0: aFlags &= ~nsICacheStorage::OPEN_TRUNCATE; michael@0: } michael@0: } michael@0: michael@0: nsRefPtr<_OldCacheLoad> cacheLoad = michael@0: new _OldCacheLoad(scheme, cacheKey, aCallback, mAppCache, michael@0: mLoadInfo, mWriteToDisk, aFlags); michael@0: michael@0: rv = cacheLoad->Start(); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP _OldStorage::AsyncDoomURI(nsIURI *aURI, const nsACString & aIdExtension, michael@0: nsICacheEntryDoomCallback* aCallback) michael@0: { michael@0: LOG(("_OldStorage::AsyncDoomURI")); michael@0: michael@0: nsresult rv; michael@0: michael@0: nsAutoCString cacheKey, scheme; michael@0: rv = AssembleCacheKey(aURI, aIdExtension, cacheKey, scheme); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsCOMPtr session; michael@0: rv = GetCacheSession(scheme, mWriteToDisk, mLoadInfo, mAppCache, michael@0: getter_AddRefs(session)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsRefPtr cb = aCallback michael@0: ? new DoomCallbackWrapper(aCallback) michael@0: : nullptr; michael@0: rv = session->DoomEntry(cacheKey, cb); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP _OldStorage::AsyncEvictStorage(nsICacheEntryDoomCallback* aCallback) michael@0: { michael@0: LOG(("_OldStorage::AsyncEvictStorage")); michael@0: michael@0: nsresult rv; michael@0: michael@0: if (!mAppCache && mOfflineStorage) { michael@0: // Special casing for pure offline storage michael@0: if (mLoadInfo->AppId() == nsILoadContextInfo::NO_APP_ID && michael@0: !mLoadInfo->IsInBrowserElement()) { michael@0: michael@0: // Clear everything. michael@0: nsCOMPtr serv = michael@0: do_GetService(NS_CACHESERVICE_CONTRACTID, &rv); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = serv->EvictEntries(nsICache::STORE_OFFLINE); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: else { michael@0: // Clear app or inbrowser staff. michael@0: nsCOMPtr appCacheService = michael@0: do_GetService(NS_APPLICATIONCACHESERVICE_CONTRACTID, &rv); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = appCacheService->DiscardByAppId(mLoadInfo->AppId(), michael@0: mLoadInfo->IsInBrowserElement()); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: } michael@0: else { michael@0: if (mAppCache) { michael@0: nsCOMPtr session; michael@0: rv = GetCacheSession(EmptyCString(), michael@0: mWriteToDisk, mLoadInfo, mAppCache, michael@0: getter_AddRefs(session)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = session->EvictEntries(); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: else { michael@0: // Oh, I'll be so happy when session names are gone... michael@0: nsCOMPtr session; michael@0: rv = GetCacheSession(NS_LITERAL_CSTRING("http"), michael@0: mWriteToDisk, mLoadInfo, mAppCache, michael@0: getter_AddRefs(session)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = session->EvictEntries(); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = GetCacheSession(NS_LITERAL_CSTRING("wyciwyg"), michael@0: mWriteToDisk, mLoadInfo, mAppCache, michael@0: getter_AddRefs(session)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = session->EvictEntries(); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // This clears any data from scheme other then http, wyciwyg or ftp michael@0: rv = GetCacheSession(EmptyCString(), michael@0: mWriteToDisk, mLoadInfo, mAppCache, michael@0: getter_AddRefs(session)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = session->EvictEntries(); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: } michael@0: michael@0: if (aCallback) { michael@0: nsRefPtr sync = michael@0: new DoomCallbackSynchronizer(aCallback); michael@0: rv = sync->Dispatch(); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP _OldStorage::AsyncVisitStorage(nsICacheStorageVisitor* aVisitor, michael@0: bool aVisitEntries) michael@0: { michael@0: LOG(("_OldStorage::AsyncVisitStorage")); michael@0: michael@0: NS_ENSURE_ARG(aVisitor); michael@0: michael@0: if (mLoadInfo->IsAnonymous()) { michael@0: // There is no concept of 'anonymous' storage in the old cache michael@0: // since anon cache entries are stored in 'non-anon' storage michael@0: // with a special prefix. michael@0: // Just fake we have 0 items with 0 consumption. This at least michael@0: // prevents displaying double size in the advanced section of michael@0: // the Options dialog. michael@0: aVisitor->OnCacheStorageInfo(0, 0); michael@0: if (aVisitEntries) michael@0: aVisitor->OnCacheEntryVisitCompleted(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult rv; michael@0: michael@0: nsCOMPtr serv = michael@0: do_GetService(NS_CACHESERVICE_CONTRACTID, &rv); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: char* deviceID; michael@0: if (mAppCache || mOfflineStorage) { michael@0: deviceID = const_cast("offline"); michael@0: } else if (!mWriteToDisk || mLoadInfo->IsPrivate()) { michael@0: deviceID = const_cast("memory"); michael@0: } else { michael@0: deviceID = const_cast("disk"); michael@0: } michael@0: michael@0: nsRefPtr cb = new VisitCallbackWrapper( michael@0: deviceID, aVisitor, aVisitEntries); michael@0: rv = serv->VisitEntries(cb); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Internal michael@0: michael@0: nsresult _OldStorage::AssembleCacheKey(nsIURI *aURI, michael@0: nsACString const & aIdExtension, michael@0: nsACString & aCacheKey, michael@0: nsACString & aScheme) michael@0: { michael@0: // Copied from nsHttpChannel::AssembleCacheKey michael@0: michael@0: aCacheKey.Truncate(); michael@0: michael@0: nsresult rv; michael@0: michael@0: rv = aURI->GetScheme(aScheme); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsAutoCString uriSpec; michael@0: if (aScheme.Equals(NS_LITERAL_CSTRING("http")) || michael@0: aScheme.Equals(NS_LITERAL_CSTRING("https"))) { michael@0: if (mLoadInfo->IsAnonymous()) { michael@0: aCacheKey.AssignLiteral("anon&"); michael@0: } michael@0: michael@0: if (!aIdExtension.IsEmpty()) { michael@0: aCacheKey.AppendPrintf("id=%s&", aIdExtension.BeginReading()); michael@0: } michael@0: michael@0: nsCOMPtr noRefURI; michael@0: rv = aURI->CloneIgnoringRef(getter_AddRefs(noRefURI)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = noRefURI->GetAsciiSpec(uriSpec); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (!aCacheKey.IsEmpty()) { michael@0: aCacheKey.AppendLiteral("uri="); michael@0: } michael@0: } michael@0: else if (aScheme.Equals(NS_LITERAL_CSTRING("wyciwyg"))) { michael@0: rv = aURI->GetSpec(uriSpec); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: else { michael@0: rv = aURI->GetAsciiSpec(uriSpec); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: aCacheKey.Append(uriSpec); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult _OldStorage::ChooseApplicationCache(nsCSubstring const &cacheKey, michael@0: nsIApplicationCache** aCache) michael@0: { michael@0: nsresult rv; michael@0: michael@0: nsCOMPtr appCacheService = michael@0: do_GetService(NS_APPLICATIONCACHESERVICE_CONTRACTID, &rv); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = appCacheService->ChooseApplicationCache(cacheKey, mLoadInfo, aCache); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: } // net michael@0: } // mozilla