summaryrefslogtreecommitdiffstats
path: root/netwerk/cache2/OldWrappers.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'netwerk/cache2/OldWrappers.cpp')
-rw-r--r--netwerk/cache2/OldWrappers.cpp1155
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