зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1110487 P2 Implement the nsIOfflineStorage interface in Cache. r=janv,ehsan
This commit is contained in:
Родитель
63a4eb24e5
Коммит
12af2a6a57
|
@ -10,6 +10,7 @@
|
|||
#include "mozilla/dom/cache/Action.h"
|
||||
#include "mozilla/dom/cache/Manager.h"
|
||||
#include "mozilla/dom/cache/ManagerId.h"
|
||||
#include "mozilla/dom/cache/OfflineStorage.h"
|
||||
#include "mozilla/dom/quota/OriginOrPatternString.h"
|
||||
#include "mozilla/dom/quota/QuotaManager.h"
|
||||
#include "nsIFile.h"
|
||||
|
@ -21,19 +22,18 @@ namespace {
|
|||
|
||||
using mozilla::dom::Nullable;
|
||||
using mozilla::dom::cache::QuotaInfo;
|
||||
using mozilla::dom::quota::Client;
|
||||
using mozilla::dom::quota::OriginOrPatternString;
|
||||
using mozilla::dom::quota::QuotaManager;
|
||||
using mozilla::dom::quota::PERSISTENCE_TYPE_DEFAULT;
|
||||
using mozilla::dom::quota::PersistenceType;
|
||||
|
||||
// Executed when the context is destroyed to release our lock on the
|
||||
// QuotaManager.
|
||||
// Release our lock on the QuotaManager directory asynchronously.
|
||||
class QuotaReleaseRunnable final : public nsRunnable
|
||||
{
|
||||
public:
|
||||
QuotaReleaseRunnable(const QuotaInfo& aQuotaInfo, const nsACString& aQuotaId)
|
||||
explicit QuotaReleaseRunnable(const QuotaInfo& aQuotaInfo)
|
||||
: mQuotaInfo(aQuotaInfo)
|
||||
, mQuotaId(aQuotaId)
|
||||
{ }
|
||||
|
||||
NS_IMETHOD Run() override
|
||||
|
@ -43,7 +43,7 @@ public:
|
|||
MOZ_ASSERT(qm);
|
||||
qm->AllowNextSynchronizedOp(OriginOrPatternString::FromOrigin(mQuotaInfo.mOrigin),
|
||||
Nullable<PersistenceType>(PERSISTENCE_TYPE_DEFAULT),
|
||||
mQuotaId);
|
||||
mQuotaInfo.mStorageId);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -51,7 +51,6 @@ private:
|
|||
~QuotaReleaseRunnable() { }
|
||||
|
||||
const QuotaInfo mQuotaInfo;
|
||||
const nsCString mQuotaId;
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
@ -70,20 +69,20 @@ using mozilla::dom::quota::PersistenceType;
|
|||
// the QuotaManager. This must be performed for each origin before any disk
|
||||
// IO occurrs.
|
||||
class Context::QuotaInitRunnable final : public nsIRunnable
|
||||
, public Action::Resolver
|
||||
, public Action::Resolver
|
||||
{
|
||||
public:
|
||||
QuotaInitRunnable(Context* aContext,
|
||||
Manager* aManager,
|
||||
const nsACString& aQuotaId,
|
||||
Action* aQuotaIOThreadAction)
|
||||
: mContext(aContext)
|
||||
, mThreadsafeHandle(aContext->CreateThreadsafeHandle())
|
||||
, mManager(aManager)
|
||||
, mQuotaId(aQuotaId)
|
||||
, mQuotaIOThreadAction(aQuotaIOThreadAction)
|
||||
, mInitiatingThread(NS_GetCurrentThread())
|
||||
, mState(STATE_INIT)
|
||||
, mResult(NS_OK)
|
||||
, mState(STATE_INIT)
|
||||
, mNeedsQuotaRelease(false)
|
||||
{
|
||||
MOZ_ASSERT(mContext);
|
||||
MOZ_ASSERT(mManager);
|
||||
|
@ -152,13 +151,15 @@ private:
|
|||
}
|
||||
|
||||
nsRefPtr<Context> mContext;
|
||||
nsRefPtr<ThreadsafeHandle> mThreadsafeHandle;
|
||||
nsRefPtr<Manager> mManager;
|
||||
const nsCString mQuotaId;
|
||||
nsRefPtr<Action> mQuotaIOThreadAction;
|
||||
nsCOMPtr<nsIThread> mInitiatingThread;
|
||||
State mState;
|
||||
nsresult mResult;
|
||||
QuotaInfo mQuotaInfo;
|
||||
nsMainThreadPtrHandle<OfflineStorage> mOfflineStorage;
|
||||
State mState;
|
||||
bool mNeedsQuotaRelease;
|
||||
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
@ -230,13 +231,19 @@ Context::QuotaInitRunnable::Run()
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
QuotaManager::GetStorageId(PERSISTENCE_TYPE_DEFAULT,
|
||||
mQuotaInfo.mOrigin,
|
||||
Client::DOMCACHE,
|
||||
NS_LITERAL_STRING("cache"),
|
||||
mQuotaInfo.mStorageId);
|
||||
|
||||
// QuotaManager::WaitForOpenAllowed() will hold a reference to us as
|
||||
// a callback. We will then get executed again on the main thread when
|
||||
// it is safe to open the quota directory.
|
||||
mState = STATE_WAIT_FOR_OPEN_ALLOWED;
|
||||
rv = qm->WaitForOpenAllowed(OriginOrPatternString::FromOrigin(mQuotaInfo.mOrigin),
|
||||
Nullable<PersistenceType>(PERSISTENCE_TYPE_DEFAULT),
|
||||
mQuotaId, this);
|
||||
mQuotaInfo.mStorageId, this);
|
||||
if (NS_FAILED(rv)) {
|
||||
Resolve(rv);
|
||||
return NS_OK;
|
||||
|
@ -247,8 +254,16 @@ Context::QuotaInitRunnable::Run()
|
|||
case STATE_WAIT_FOR_OPEN_ALLOWED:
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
mNeedsQuotaRelease = true;
|
||||
|
||||
QuotaManager* qm = QuotaManager::Get();
|
||||
MOZ_ASSERT(qm);
|
||||
|
||||
nsRefPtr<OfflineStorage> offlineStorage =
|
||||
OfflineStorage::Register(mThreadsafeHandle, mQuotaInfo);
|
||||
mOfflineStorage = new nsMainThreadPtrHolder<OfflineStorage>(offlineStorage);
|
||||
|
||||
mState = STATE_ENSURE_ORIGIN_INITIALIZED;
|
||||
nsresult rv = qm->IOThread()->Dispatch(this, nsIThread::DISPATCH_NORMAL);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
|
@ -298,8 +313,15 @@ Context::QuotaInitRunnable::Run()
|
|||
if (mQuotaIOThreadAction) {
|
||||
mQuotaIOThreadAction->CompleteOnInitiatingThread(mResult);
|
||||
}
|
||||
mContext->OnQuotaInit(mResult, mQuotaInfo);
|
||||
mContext->OnQuotaInit(mResult, mQuotaInfo, mOfflineStorage);
|
||||
mState = STATE_COMPLETE;
|
||||
|
||||
if (mNeedsQuotaRelease) {
|
||||
// Unlock the quota dir if we locked it previously
|
||||
nsCOMPtr<nsIRunnable> runnable = new QuotaReleaseRunnable(mQuotaInfo);
|
||||
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(runnable)));
|
||||
}
|
||||
|
||||
// Explicitly cleanup here as the destructor could fire on any of
|
||||
// the threads we have bounced through.
|
||||
Clear();
|
||||
|
@ -321,6 +343,7 @@ Context::QuotaInitRunnable::Run()
|
|||
// is initialized.
|
||||
class Context::ActionRunnable final : public nsIRunnable
|
||||
, public Action::Resolver
|
||||
, public Context::Activity
|
||||
{
|
||||
public:
|
||||
ActionRunnable(Context* aContext, nsIEventTarget* aTarget, Action* aAction,
|
||||
|
@ -354,12 +377,15 @@ public:
|
|||
return rv;
|
||||
}
|
||||
|
||||
bool MatchesCacheId(CacheId aCacheId) {
|
||||
virtual bool
|
||||
MatchesCacheId(CacheId aCacheId) const override
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(Action::Resolver);
|
||||
return mAction->MatchesCacheId(aCacheId);
|
||||
}
|
||||
|
||||
void Cancel()
|
||||
virtual void
|
||||
Cancel() override
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(Action::Resolver);
|
||||
mAction->CancelOnInitiatingThread();
|
||||
|
@ -392,7 +418,7 @@ private:
|
|||
NS_ASSERT_OWNINGTHREAD(Action::Resolver);
|
||||
MOZ_ASSERT(mContext);
|
||||
MOZ_ASSERT(mAction);
|
||||
mContext->OnActionRunnableComplete(this);
|
||||
mContext->RemoveActivity(this);
|
||||
mContext = nullptr;
|
||||
mAction = nullptr;
|
||||
}
|
||||
|
@ -482,6 +508,99 @@ Context::ActionRunnable::Run()
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
Context::ThreadsafeHandle::AllowToClose()
|
||||
{
|
||||
if (mOwningThread == NS_GetCurrentThread()) {
|
||||
AllowToCloseOnOwningThread();
|
||||
return;
|
||||
}
|
||||
|
||||
// Dispatch is guaranteed to succeed here because we block shutdown until
|
||||
// all Contexts have been destroyed.
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
NS_NewRunnableMethod(this, &ThreadsafeHandle::AllowToCloseOnOwningThread);
|
||||
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
|
||||
mOwningThread->Dispatch(runnable, nsIThread::DISPATCH_NORMAL)));
|
||||
}
|
||||
|
||||
void
|
||||
Context::ThreadsafeHandle::InvalidateAndAllowToClose()
|
||||
{
|
||||
if (mOwningThread == NS_GetCurrentThread()) {
|
||||
InvalidateAndAllowToCloseOnOwningThread();
|
||||
return;
|
||||
}
|
||||
|
||||
// Dispatch is guaranteed to succeed here because we block shutdown until
|
||||
// all Contexts have been destroyed.
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
NS_NewRunnableMethod(this, &ThreadsafeHandle::InvalidateAndAllowToCloseOnOwningThread);
|
||||
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
|
||||
mOwningThread->Dispatch(runnable, nsIThread::DISPATCH_NORMAL)));
|
||||
}
|
||||
|
||||
Context::ThreadsafeHandle::ThreadsafeHandle(Context* aContext)
|
||||
: mStrongRef(aContext)
|
||||
, mWeakRef(aContext)
|
||||
, mOwningThread(NS_GetCurrentThread())
|
||||
{
|
||||
}
|
||||
|
||||
Context::ThreadsafeHandle::~ThreadsafeHandle()
|
||||
{
|
||||
// Normally we only touch mStrongRef on the owning thread. This is safe,
|
||||
// however, because when we do use mStrongRef on the owning thread we are
|
||||
// always holding a strong ref to the ThreadsafeHandle via the owning
|
||||
// runnable. So we cannot run the ThreadsafeHandle destructor simultaneously.
|
||||
if (!mStrongRef || mOwningThread == NS_GetCurrentThread()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Dispatch is guaranteed to succeed here because we block shutdown until
|
||||
// all Contexts have been destroyed.
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
NS_NewNonOwningRunnableMethod(mStrongRef.forget().take(), &Context::Release);
|
||||
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
|
||||
mOwningThread->Dispatch(runnable, nsIThread::DISPATCH_NORMAL)));
|
||||
}
|
||||
|
||||
void
|
||||
Context::ThreadsafeHandle::AllowToCloseOnOwningThread()
|
||||
{
|
||||
MOZ_ASSERT(mOwningThread == NS_GetCurrentThread());
|
||||
// A Context "closes" when its ref count drops to zero. Dropping this
|
||||
// strong ref is necessary, but not sufficient for the close to occur.
|
||||
// Any outstanding IO will continue and keep the Context alive. Once
|
||||
// the Context is idle, it will be destroyed.
|
||||
mStrongRef = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
Context::ThreadsafeHandle::InvalidateAndAllowToCloseOnOwningThread()
|
||||
{
|
||||
MOZ_ASSERT(mOwningThread == NS_GetCurrentThread());
|
||||
// Cancel the Context through the weak reference. This means we can
|
||||
// allow the Context to close by dropping the strong ref, but then
|
||||
// still cancel ongoing IO if necessary.
|
||||
if (mWeakRef) {
|
||||
mWeakRef->Invalidate();
|
||||
}
|
||||
// We should synchronously have AllowToCloseOnOwningThread called when
|
||||
// the Context is canceled.
|
||||
MOZ_ASSERT(!mStrongRef);
|
||||
}
|
||||
|
||||
void
|
||||
Context::ThreadsafeHandle::ContextDestroyed(Context* aContext)
|
||||
{
|
||||
MOZ_ASSERT(mOwningThread == NS_GetCurrentThread());
|
||||
MOZ_ASSERT(!mStrongRef);
|
||||
MOZ_ASSERT(mWeakRef);
|
||||
MOZ_ASSERT(mWeakRef == aContext);
|
||||
mWeakRef = nullptr;
|
||||
}
|
||||
|
||||
// static
|
||||
already_AddRefed<Context>
|
||||
Context::Create(Manager* aManager, Action* aQuotaIOThreadAction)
|
||||
|
@ -489,8 +608,7 @@ Context::Create(Manager* aManager, Action* aQuotaIOThreadAction)
|
|||
nsRefPtr<Context> context = new Context(aManager);
|
||||
|
||||
nsRefPtr<QuotaInitRunnable> runnable =
|
||||
new QuotaInitRunnable(context, aManager, NS_LITERAL_CSTRING("Cache"),
|
||||
aQuotaIOThreadAction);
|
||||
new QuotaInitRunnable(context, aManager, aQuotaIOThreadAction);
|
||||
nsresult rv = runnable->Dispatch();
|
||||
if (NS_FAILED(rv)) {
|
||||
// Shutdown must be delayed until all Contexts are destroyed. Shutdown
|
||||
|
@ -535,9 +653,29 @@ Context::CancelAll()
|
|||
NS_ASSERT_OWNINGTHREAD(Context);
|
||||
mState = STATE_CONTEXT_CANCELED;
|
||||
mPendingActions.Clear();
|
||||
for (uint32_t i = 0; i < mActionRunnables.Length(); ++i) {
|
||||
nsRefPtr<ActionRunnable> runnable = mActionRunnables[i];
|
||||
runnable->Cancel();
|
||||
{
|
||||
ActivityList::ForwardIterator iter(mActivityList);
|
||||
while (iter.HasMore()) {
|
||||
iter.GetNext()->Cancel();
|
||||
}
|
||||
}
|
||||
AllowToClose();
|
||||
}
|
||||
|
||||
void
|
||||
Context::Invalidate()
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(Context);
|
||||
mManager->Invalidate();
|
||||
CancelAll();
|
||||
}
|
||||
|
||||
void
|
||||
Context::AllowToClose()
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(Context);
|
||||
if (mThreadsafeHandle) {
|
||||
mThreadsafeHandle->AllowToClose();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -545,15 +683,20 @@ void
|
|||
Context::CancelForCacheId(CacheId aCacheId)
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(Context);
|
||||
for (uint32_t i = 0; i < mPendingActions.Length(); ++i) {
|
||||
|
||||
// Remove matching pending actions
|
||||
for (int32_t i = mPendingActions.Length() - 1; i >= 0; --i) {
|
||||
if (mPendingActions[i].mAction->MatchesCacheId(aCacheId)) {
|
||||
mPendingActions.RemoveElementAt(i);
|
||||
}
|
||||
}
|
||||
for (uint32_t i = 0; i < mActionRunnables.Length(); ++i) {
|
||||
nsRefPtr<ActionRunnable> runnable = mActionRunnables[i];
|
||||
if (runnable->MatchesCacheId(aCacheId)) {
|
||||
runnable->Cancel();
|
||||
|
||||
// Cancel activities and let them remove themselves
|
||||
ActivityList::ForwardIterator iter(mActivityList);
|
||||
while (iter.HasMore()) {
|
||||
Activity* activity = iter.GetNext();
|
||||
if (activity->MatchesCacheId(aCacheId)) {
|
||||
activity->Cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -563,14 +706,8 @@ Context::~Context()
|
|||
NS_ASSERT_OWNINGTHREAD(Context);
|
||||
MOZ_ASSERT(mManager);
|
||||
|
||||
// Unlock the quota dir as we go out of scope.
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
new QuotaReleaseRunnable(mQuotaInfo, NS_LITERAL_CSTRING("Cache"));
|
||||
nsresult rv = NS_DispatchToMainThread(runnable, nsIThread::DISPATCH_NORMAL);
|
||||
if (NS_FAILED(rv)) {
|
||||
// Shutdown must be delayed until all Contexts are destroyed. Crash
|
||||
// for this invariant violation.
|
||||
MOZ_CRASH("Failed to dispatch QuotaReleaseRunnable to main thread.");
|
||||
if (mThreadsafeHandle) {
|
||||
mThreadsafeHandle->ContextDestroyed(this);
|
||||
}
|
||||
|
||||
mManager->RemoveContext(this);
|
||||
|
@ -589,21 +726,28 @@ Context::DispatchAction(nsIEventTarget* aTarget, Action* aAction)
|
|||
// for this invariant violation.
|
||||
MOZ_CRASH("Failed to dispatch ActionRunnable to target thread.");
|
||||
}
|
||||
mActionRunnables.AppendElement(runnable);
|
||||
AddActivity(runnable);
|
||||
}
|
||||
|
||||
void
|
||||
Context::OnQuotaInit(nsresult aRv, const QuotaInfo& aQuotaInfo)
|
||||
Context::OnQuotaInit(nsresult aRv, const QuotaInfo& aQuotaInfo,
|
||||
nsMainThreadPtrHandle<OfflineStorage>& aOfflineStorage)
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(Context);
|
||||
|
||||
mQuotaInfo = aQuotaInfo;
|
||||
|
||||
// Always save the offline storage to ensure QuotaManager does not shutdown
|
||||
// before the Context has gone away.
|
||||
MOZ_ASSERT(!mOfflineStorage);
|
||||
mOfflineStorage = aOfflineStorage;
|
||||
|
||||
if (mState == STATE_CONTEXT_CANCELED || NS_FAILED(aRv)) {
|
||||
for (uint32_t i = 0; i < mPendingActions.Length(); ++i) {
|
||||
mPendingActions[i].mAction->CompleteOnInitiatingThread(aRv);
|
||||
}
|
||||
mPendingActions.Clear();
|
||||
mThreadsafeHandle->AllowToClose();
|
||||
// Context will destruct after return here and last ref is released.
|
||||
return;
|
||||
}
|
||||
|
@ -618,11 +762,32 @@ Context::OnQuotaInit(nsresult aRv, const QuotaInfo& aQuotaInfo)
|
|||
}
|
||||
|
||||
void
|
||||
Context::OnActionRunnableComplete(ActionRunnable* aActionRunnable)
|
||||
Context::AddActivity(Activity* aActivity)
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(Context);
|
||||
MOZ_ASSERT(aActionRunnable);
|
||||
MOZ_ALWAYS_TRUE(mActionRunnables.RemoveElement(aActionRunnable));
|
||||
MOZ_ASSERT(aActivity);
|
||||
MOZ_ASSERT(!mActivityList.Contains(aActivity));
|
||||
mActivityList.AppendElement(aActivity);
|
||||
}
|
||||
|
||||
void
|
||||
Context::RemoveActivity(Activity* aActivity)
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(Context);
|
||||
MOZ_ASSERT(aActivity);
|
||||
MOZ_ALWAYS_TRUE(mActivityList.RemoveElement(aActivity));
|
||||
MOZ_ASSERT(!mActivityList.Contains(aActivity));
|
||||
}
|
||||
|
||||
already_AddRefed<Context::ThreadsafeHandle>
|
||||
Context::CreateThreadsafeHandle()
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(Context);
|
||||
if (!mThreadsafeHandle) {
|
||||
mThreadsafeHandle = new ThreadsafeHandle(this);
|
||||
}
|
||||
nsRefPtr<ThreadsafeHandle> ref = mThreadsafeHandle;
|
||||
return ref.forget();
|
||||
}
|
||||
|
||||
} // namespace cache
|
||||
|
|
|
@ -11,10 +11,13 @@
|
|||
#include "nsAutoPtr.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsISupportsImpl.h"
|
||||
#include "nsProxyRelease.h"
|
||||
#include "nsString.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsTObserverArray.h"
|
||||
|
||||
class nsIEventTarget;
|
||||
class nsIThread;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
@ -22,6 +25,7 @@ namespace cache {
|
|||
|
||||
class Action;
|
||||
class Manager;
|
||||
class OfflineStorage;
|
||||
|
||||
// The Context class is RAII-style class for managing IO operations within the
|
||||
// Cache.
|
||||
|
@ -31,8 +35,20 @@ class Manager;
|
|||
// delayed until this initialization is complete. They are then allow to
|
||||
// execute on any specified thread. Once all references to the Context are
|
||||
// gone, then the steps necessary to release the QuotaManager are performed.
|
||||
// Since pending Action objects reference the Context, this allows overlapping
|
||||
// IO to opportunistically run without re-initializing the QuotaManager again.
|
||||
// After initialization the Context holds a self reference, so it will stay
|
||||
// alive until one of three conditions occur:
|
||||
//
|
||||
// 1) The Manager will call Context::AllowToClose() when all of the actors
|
||||
// have removed themselves as listener. This means an idle context with
|
||||
// no active DOM objects will close gracefully.
|
||||
// 2) The QuotaManager invalidates the storage area so it can delete the
|
||||
// files. In this case the OfflineStorage calls Cache::Invalidate() which
|
||||
// in turn cancels all existing Action objects and then marks the Manager
|
||||
// as invalid.
|
||||
// 3) Browser shutdown occurs and the Manager calls Context::CancelAll().
|
||||
//
|
||||
// In either case, though, the Action objects must be destroyed first to
|
||||
// allow the Context to be destroyed.
|
||||
//
|
||||
// While the Context performs operations asynchronously on threads, all of
|
||||
// methods in its public interface must be called on the same thread
|
||||
|
@ -44,6 +60,53 @@ class Manager;
|
|||
class Context final
|
||||
{
|
||||
public:
|
||||
// Define a class allowing other threads to hold the Context alive. This also
|
||||
// allows these other threads to safely close or cancel the Context.
|
||||
class ThreadsafeHandle final
|
||||
{
|
||||
friend class Context;
|
||||
public:
|
||||
void AllowToClose();
|
||||
void InvalidateAndAllowToClose();
|
||||
private:
|
||||
explicit ThreadsafeHandle(Context* aContext);
|
||||
~ThreadsafeHandle();
|
||||
|
||||
// disallow copying
|
||||
ThreadsafeHandle(const ThreadsafeHandle&) = delete;
|
||||
ThreadsafeHandle& operator=(const ThreadsafeHandle&) = delete;
|
||||
|
||||
void AllowToCloseOnOwningThread();
|
||||
void InvalidateAndAllowToCloseOnOwningThread();
|
||||
|
||||
void ContextDestroyed(Context* aContext);
|
||||
|
||||
// Cleared to allow the Context to close. Only safe to access on
|
||||
// owning thread.
|
||||
nsRefPtr<Context> mStrongRef;
|
||||
|
||||
// Used to support cancelation even while the Context is already allowed
|
||||
// to close. Cleared by ~Context() calling ContextDestroyed(). Only
|
||||
// safe to access on owning thread.
|
||||
Context* mWeakRef;
|
||||
|
||||
nsCOMPtr<nsIThread> mOwningThread;
|
||||
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(cache::Context::ThreadsafeHandle)
|
||||
};
|
||||
|
||||
// Different objects hold references to the Context while some work is being
|
||||
// performed asynchronously. These objects must implement the Activity
|
||||
// interface and register themselves with the AddActivity(). When they are
|
||||
// destroyed they must call RemoveActivity(). This allows the Context to
|
||||
// cancel any outstanding Activity work when the Context is cancelled.
|
||||
class Activity
|
||||
{
|
||||
public:
|
||||
virtual void Cancel() = 0;
|
||||
virtual bool MatchesCacheId(CacheId aCacheId) const = 0;
|
||||
};
|
||||
|
||||
static already_AddRefed<Context>
|
||||
Create(Manager* aManager, Action* aQuotaIOThreadAction);
|
||||
|
||||
|
@ -60,12 +123,28 @@ public:
|
|||
// Only callable from the thread that created the Context.
|
||||
void CancelAll();
|
||||
|
||||
// Like CancelAll(), but also marks the Manager as "invalid".
|
||||
void Invalidate();
|
||||
|
||||
// Remove any self references and allow the Context to be released when
|
||||
// there are no more Actions to process.
|
||||
void AllowToClose();
|
||||
|
||||
// Cancel any Actions running or waiting to run that operate on the given
|
||||
// cache ID.
|
||||
//
|
||||
// Only callable from the thread that created the Context.
|
||||
void CancelForCacheId(CacheId aCacheId);
|
||||
|
||||
void AddActivity(Activity* aActivity);
|
||||
void RemoveActivity(Activity* aActivity);
|
||||
|
||||
const QuotaInfo&
|
||||
GetQuotaInfo() const
|
||||
{
|
||||
return mQuotaInfo;
|
||||
}
|
||||
|
||||
private:
|
||||
class QuotaInitRunnable;
|
||||
class ActionRunnable;
|
||||
|
@ -86,16 +165,28 @@ private:
|
|||
explicit Context(Manager* aManager);
|
||||
~Context();
|
||||
void DispatchAction(nsIEventTarget* aTarget, Action* aAction);
|
||||
void OnQuotaInit(nsresult aRv, const QuotaInfo& aQuotaInfo);
|
||||
void OnActionRunnableComplete(ActionRunnable* const aAction);
|
||||
void OnQuotaInit(nsresult aRv, const QuotaInfo& aQuotaInfo,
|
||||
nsMainThreadPtrHandle<OfflineStorage>& aOfflineStorage);
|
||||
|
||||
already_AddRefed<ThreadsafeHandle>
|
||||
CreateThreadsafeHandle();
|
||||
|
||||
nsRefPtr<Manager> mManager;
|
||||
State mState;
|
||||
QuotaInfo mQuotaInfo;
|
||||
nsTArray<PendingAction> mPendingActions;
|
||||
|
||||
// weak refs since ~ActionRunnable() removes itself from this list
|
||||
nsTArray<ActionRunnable*> mActionRunnables;
|
||||
// Weak refs since activites must remove themselves from this list before
|
||||
// being destroyed by calling RemoveActivity().
|
||||
typedef nsTObserverArray<Activity*> ActivityList;
|
||||
ActivityList mActivityList;
|
||||
|
||||
// The ThreadsafeHandle may have a strong ref back to us. This creates
|
||||
// a ref-cycle that keeps the Context alive. The ref-cycle is broken
|
||||
// when ThreadsafeHandle::AllowToClose() is called.
|
||||
nsRefPtr<ThreadsafeHandle> mThreadsafeHandle;
|
||||
|
||||
nsMainThreadPtrHandle<OfflineStorage> mOfflineStorage;
|
||||
|
||||
public:
|
||||
NS_INLINE_DECL_REFCOUNTING(cache::Context)
|
||||
|
|
|
@ -177,7 +177,10 @@ public:
|
|||
ManagerList::ForwardIterator iter(sFactory->mManagerList);
|
||||
while (iter.HasMore()) {
|
||||
nsRefPtr<Manager> manager = iter.GetNext();
|
||||
if (*manager->mManagerId == *aManagerId) {
|
||||
// If there is an invalid Manager finishing up and a new Manager
|
||||
// is created for the same origin, then the new Manager will
|
||||
// be blocked until QuotaManager finishes clearing the origin.
|
||||
if (manager->IsValid() && *manager->mManagerId == *aManagerId) {
|
||||
return manager.forget();
|
||||
}
|
||||
}
|
||||
|
@ -1404,6 +1407,9 @@ Manager::RemoveListener(Listener* aListener)
|
|||
mListeners.RemoveElement(aListener, ListenerEntryListenerComparator());
|
||||
MOZ_ASSERT(!mListeners.Contains(aListener,
|
||||
ListenerEntryListenerComparator()));
|
||||
if (mListeners.IsEmpty() && mContext) {
|
||||
mContext->AllowToClose();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -1421,6 +1427,21 @@ Manager::RemoveContext(Context* aContext)
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
Manager::Invalidate()
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(Manager);
|
||||
// QuotaManager can trigger this more than once.
|
||||
mValid = false;
|
||||
}
|
||||
|
||||
bool
|
||||
Manager::IsValid() const
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(Manager);
|
||||
return mValid;
|
||||
}
|
||||
|
||||
void
|
||||
Manager::AddRefCacheId(CacheId aCacheId)
|
||||
{
|
||||
|
@ -1450,7 +1471,7 @@ Manager::ReleaseCacheId(CacheId aCacheId)
|
|||
bool orphaned = mCacheIdRefs[i].mOrphaned;
|
||||
mCacheIdRefs.RemoveElementAt(i);
|
||||
// TODO: note that we need to check this cache for staleness on startup (bug 1110446)
|
||||
if (orphaned && !mShuttingDown) {
|
||||
if (orphaned && !mShuttingDown && mValid) {
|
||||
nsRefPtr<Context> context = CurrentContext();
|
||||
context->CancelForCacheId(aCacheId);
|
||||
nsRefPtr<Action> action = new DeleteOrphanedCacheAction(this,
|
||||
|
@ -1493,7 +1514,7 @@ Manager::ReleaseBodyId(const nsID& aBodyId)
|
|||
bool orphaned = mBodyIdRefs[i].mOrphaned;
|
||||
mBodyIdRefs.RemoveElementAt(i);
|
||||
// TODO: note that we need to check this body for staleness on startup (bug 1110446)
|
||||
if (orphaned && !mShuttingDown) {
|
||||
if (orphaned && !mShuttingDown && mValid) {
|
||||
nsRefPtr<Action> action = new DeleteOrphanedBodyAction(aBodyId);
|
||||
nsRefPtr<Context> context = CurrentContext();
|
||||
context->Dispatch(mIOThread, action);
|
||||
|
@ -1535,9 +1556,8 @@ Manager::CacheMatch(Listener* aListener, RequestId aRequestId, CacheId aCacheId,
|
|||
{
|
||||
NS_ASSERT_OWNINGTHREAD(Manager);
|
||||
MOZ_ASSERT(aListener);
|
||||
if (mShuttingDown) {
|
||||
aListener->OnCacheMatch(aRequestId, NS_ERROR_ILLEGAL_DURING_SHUTDOWN,
|
||||
nullptr, nullptr);
|
||||
if (mShuttingDown || !mValid) {
|
||||
aListener->OnCacheMatch(aRequestId, NS_ERROR_FAILURE, nullptr, nullptr);
|
||||
return;
|
||||
}
|
||||
nsRefPtr<Context> context = CurrentContext();
|
||||
|
@ -1556,8 +1576,8 @@ Manager::CacheMatchAll(Listener* aListener, RequestId aRequestId,
|
|||
{
|
||||
NS_ASSERT_OWNINGTHREAD(Manager);
|
||||
MOZ_ASSERT(aListener);
|
||||
if (mShuttingDown) {
|
||||
aListener->OnCacheMatchAll(aRequestId, NS_ERROR_ILLEGAL_DURING_SHUTDOWN,
|
||||
if (mShuttingDown || !mValid) {
|
||||
aListener->OnCacheMatchAll(aRequestId, NS_ERROR_FAILURE,
|
||||
nsTArray<SavedResponse>(), nullptr);
|
||||
return;
|
||||
}
|
||||
|
@ -1578,8 +1598,8 @@ Manager::CachePutAll(Listener* aListener, RequestId aRequestId, CacheId aCacheId
|
|||
{
|
||||
NS_ASSERT_OWNINGTHREAD(Manager);
|
||||
MOZ_ASSERT(aListener);
|
||||
if (mShuttingDown) {
|
||||
aListener->OnCachePutAll(aRequestId, NS_ERROR_ILLEGAL_DURING_SHUTDOWN);
|
||||
if (mShuttingDown || !mValid) {
|
||||
aListener->OnCachePutAll(aRequestId, NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
ListenerId listenerId = SaveListener(aListener);
|
||||
|
@ -1598,8 +1618,8 @@ Manager::CacheDelete(Listener* aListener, RequestId aRequestId,
|
|||
{
|
||||
NS_ASSERT_OWNINGTHREAD(Manager);
|
||||
MOZ_ASSERT(aListener);
|
||||
if (mShuttingDown) {
|
||||
aListener->OnCacheDelete(aRequestId, NS_ERROR_ILLEGAL_DURING_SHUTDOWN, false);
|
||||
if (mShuttingDown || !mValid) {
|
||||
aListener->OnCacheDelete(aRequestId, NS_ERROR_FAILURE, false);
|
||||
return;
|
||||
}
|
||||
ListenerId listenerId = SaveListener(aListener);
|
||||
|
@ -1616,8 +1636,8 @@ Manager::CacheKeys(Listener* aListener, RequestId aRequestId,
|
|||
{
|
||||
NS_ASSERT_OWNINGTHREAD(Manager);
|
||||
MOZ_ASSERT(aListener);
|
||||
if (mShuttingDown) {
|
||||
aListener->OnCacheKeys(aRequestId, NS_ERROR_ILLEGAL_DURING_SHUTDOWN,
|
||||
if (mShuttingDown || !mValid) {
|
||||
aListener->OnCacheKeys(aRequestId, NS_ERROR_FAILURE,
|
||||
nsTArray<SavedRequest>(), nullptr);
|
||||
return;
|
||||
}
|
||||
|
@ -1637,8 +1657,8 @@ Manager::StorageMatch(Listener* aListener, RequestId aRequestId,
|
|||
{
|
||||
NS_ASSERT_OWNINGTHREAD(Manager);
|
||||
MOZ_ASSERT(aListener);
|
||||
if (mShuttingDown) {
|
||||
aListener->OnStorageMatch(aRequestId, NS_ERROR_ILLEGAL_DURING_SHUTDOWN,
|
||||
if (mShuttingDown || !mValid) {
|
||||
aListener->OnStorageMatch(aRequestId, NS_ERROR_FAILURE,
|
||||
nullptr, nullptr);
|
||||
return;
|
||||
}
|
||||
|
@ -1657,8 +1677,8 @@ Manager::StorageHas(Listener* aListener, RequestId aRequestId,
|
|||
{
|
||||
NS_ASSERT_OWNINGTHREAD(Manager);
|
||||
MOZ_ASSERT(aListener);
|
||||
if (mShuttingDown) {
|
||||
aListener->OnStorageHas(aRequestId, NS_ERROR_ILLEGAL_DURING_SHUTDOWN,
|
||||
if (mShuttingDown || !mValid) {
|
||||
aListener->OnStorageHas(aRequestId, NS_ERROR_FAILURE,
|
||||
false);
|
||||
return;
|
||||
}
|
||||
|
@ -1675,8 +1695,8 @@ Manager::StorageOpen(Listener* aListener, RequestId aRequestId,
|
|||
{
|
||||
NS_ASSERT_OWNINGTHREAD(Manager);
|
||||
MOZ_ASSERT(aListener);
|
||||
if (mShuttingDown) {
|
||||
aListener->OnStorageOpen(aRequestId, NS_ERROR_ILLEGAL_DURING_SHUTDOWN, 0);
|
||||
if (mShuttingDown || !mValid) {
|
||||
aListener->OnStorageOpen(aRequestId, NS_ERROR_FAILURE, 0);
|
||||
return;
|
||||
}
|
||||
ListenerId listenerId = SaveListener(aListener);
|
||||
|
@ -1692,8 +1712,8 @@ Manager::StorageDelete(Listener* aListener, RequestId aRequestId,
|
|||
{
|
||||
NS_ASSERT_OWNINGTHREAD(Manager);
|
||||
MOZ_ASSERT(aListener);
|
||||
if (mShuttingDown) {
|
||||
aListener->OnStorageDelete(aRequestId, NS_ERROR_ILLEGAL_DURING_SHUTDOWN,
|
||||
if (mShuttingDown || !mValid) {
|
||||
aListener->OnStorageDelete(aRequestId, NS_ERROR_FAILURE,
|
||||
false);
|
||||
return;
|
||||
}
|
||||
|
@ -1710,8 +1730,8 @@ Manager::StorageKeys(Listener* aListener, RequestId aRequestId,
|
|||
{
|
||||
NS_ASSERT_OWNINGTHREAD(Manager);
|
||||
MOZ_ASSERT(aListener);
|
||||
if (mShuttingDown) {
|
||||
aListener->OnStorageKeys(aRequestId, NS_ERROR_ILLEGAL_DURING_SHUTDOWN,
|
||||
if (mShuttingDown || !mValid) {
|
||||
aListener->OnStorageKeys(aRequestId, NS_ERROR_FAILURE,
|
||||
nsTArray<nsString>());
|
||||
return;
|
||||
}
|
||||
|
@ -1727,6 +1747,7 @@ Manager::Manager(ManagerId* aManagerId, nsIThread* aIOThread)
|
|||
, mIOThread(aIOThread)
|
||||
, mContext(nullptr)
|
||||
, mShuttingDown(false)
|
||||
, mValid(true)
|
||||
{
|
||||
MOZ_ASSERT(mManagerId);
|
||||
MOZ_ASSERT(mIOThread);
|
||||
|
@ -1765,11 +1786,6 @@ Manager::Shutdown()
|
|||
// complete before shutdown proceeds.
|
||||
mShuttingDown = true;
|
||||
|
||||
for (uint32_t i = 0; i < mStreamLists.Length(); ++i) {
|
||||
nsRefPtr<StreamList> streamList = mStreamLists[i];
|
||||
streamList->CloseAll();
|
||||
}
|
||||
|
||||
// If there is a context, then we must wait for it to complete. Cancel and
|
||||
// only note that we are done after its cleaned up.
|
||||
if (mContext) {
|
||||
|
@ -1789,6 +1805,7 @@ Manager::CurrentContext()
|
|||
nsRefPtr<Context> ref = mContext;
|
||||
if (!ref) {
|
||||
MOZ_ASSERT(!mShuttingDown);
|
||||
MOZ_ASSERT(mValid);
|
||||
nsRefPtr<Action> setupAction = new SetupAction();
|
||||
ref = Context::Create(this, setupAction);
|
||||
mContext = ref;
|
||||
|
|
|
@ -125,6 +125,11 @@ public:
|
|||
// Must be called by Context objects before they are destroyed.
|
||||
void RemoveContext(Context* aContext);
|
||||
|
||||
// Marks the Manager "invalid". Once the Context completes no new operations
|
||||
// will be permitted with this Manager. New actors will get a new Manager.
|
||||
void Invalidate();
|
||||
bool IsValid() const;
|
||||
|
||||
// If an actor represents a long term reference to a cache or body stream,
|
||||
// then they must call AddRefCacheId() or AddRefBodyId(). This will
|
||||
// cause the Manager to keep the backing data store alive for the given
|
||||
|
@ -214,8 +219,8 @@ private:
|
|||
struct ListenerEntry
|
||||
{
|
||||
ListenerEntry()
|
||||
: mId(UINT64_MAX),
|
||||
mListener(nullptr)
|
||||
: mId(UINT64_MAX)
|
||||
, mListener(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -255,6 +260,7 @@ private:
|
|||
nsTArray<StreamList*> mStreamLists;
|
||||
|
||||
bool mShuttingDown;
|
||||
bool mValid;
|
||||
|
||||
struct CacheIdRefCounter
|
||||
{
|
||||
|
|
|
@ -0,0 +1,135 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "mozilla/dom/cache/OfflineStorage.h"
|
||||
|
||||
#include "mozilla/dom/cache/Context.h"
|
||||
#include "mozilla/dom/cache/QuotaClient.h"
|
||||
#include "mozilla/dom/quota/QuotaManager.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
namespace cache {
|
||||
|
||||
using mozilla::dom::quota::Client;
|
||||
using mozilla::dom::quota::PERSISTENCE_TYPE_DEFAULT;
|
||||
using mozilla::dom::quota::QuotaManager;
|
||||
|
||||
NS_IMPL_ISUPPORTS(OfflineStorage, nsIOfflineStorage);
|
||||
|
||||
// static
|
||||
already_AddRefed<OfflineStorage>
|
||||
OfflineStorage::Register(Context::ThreadsafeHandle* aContext,
|
||||
const QuotaInfo& aQuotaInfo)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
QuotaManager* qm = QuotaManager::Get();
|
||||
if (NS_WARN_IF(!qm)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsRefPtr<Client> client = qm->GetClient(Client::DOMCACHE);
|
||||
|
||||
nsRefPtr<OfflineStorage> storage =
|
||||
new OfflineStorage(aContext, aQuotaInfo, client);
|
||||
|
||||
if (NS_WARN_IF(!qm->RegisterStorage(storage))) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return storage.forget();
|
||||
}
|
||||
|
||||
void
|
||||
OfflineStorage::AddDestroyCallback(nsIRunnable* aCallback)
|
||||
{
|
||||
MOZ_ASSERT(aCallback);
|
||||
MOZ_ASSERT(!mDestroyCallbacks.Contains(aCallback));
|
||||
mDestroyCallbacks.AppendElement(aCallback);
|
||||
}
|
||||
|
||||
OfflineStorage::OfflineStorage(Context::ThreadsafeHandle* aContext,
|
||||
const QuotaInfo& aQuotaInfo,
|
||||
Client* aClient)
|
||||
: mContext(aContext)
|
||||
, mQuotaInfo(aQuotaInfo)
|
||||
, mClient(aClient)
|
||||
{
|
||||
MOZ_ASSERT(mContext);
|
||||
MOZ_ASSERT(mClient);
|
||||
|
||||
mPersistenceType = PERSISTENCE_TYPE_DEFAULT;
|
||||
mGroup = mQuotaInfo.mGroup;
|
||||
}
|
||||
|
||||
OfflineStorage::~OfflineStorage()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
QuotaManager* qm = QuotaManager::Get();
|
||||
MOZ_ASSERT(qm);
|
||||
qm->UnregisterStorage(this);
|
||||
for (uint32_t i = 0; i < mDestroyCallbacks.Length(); ++i) {
|
||||
mDestroyCallbacks[i]->Run();
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP_(const nsACString&)
|
||||
OfflineStorage::Id()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
return mQuotaInfo.mStorageId;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP_(Client*)
|
||||
OfflineStorage::GetClient()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
return mClient;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP_(bool)
|
||||
OfflineStorage::IsOwnedByProcess(ContentParent* aOwner)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
// The Cache and Context can be shared by multiple client processes. They
|
||||
// are not exclusively owned by a single process.
|
||||
//
|
||||
// As far as I can tell this is used by QuotaManager to shutdown storages
|
||||
// when a particular process goes away. We definitely don't want this
|
||||
// since we are shared. Also, the Cache actor code already properly
|
||||
// handles asynchronous actor destruction when the child process dies.
|
||||
//
|
||||
// Therefore, always return false here.
|
||||
return false;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP_(const nsACString&)
|
||||
OfflineStorage::Origin()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
return mQuotaInfo.mOrigin;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP_(nsresult)
|
||||
OfflineStorage::Close()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
mContext->AllowToClose();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP_(void)
|
||||
OfflineStorage::Invalidate()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
mContext->InvalidateAndAllowToClose();
|
||||
}
|
||||
|
||||
} // namespace cache
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
|
@ -0,0 +1,50 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_cache_QuotaOfflineStorage_h
|
||||
#define mozilla_dom_cache_QuotaOfflineStorage_h
|
||||
|
||||
#include "nsISupportsImpl.h"
|
||||
#include "mozilla/AlreadyAddRefed.h"
|
||||
#include "mozilla/dom/cache/Context.h"
|
||||
#include "nsIOfflineStorage.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
class nsIThread;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
namespace cache {
|
||||
|
||||
class OfflineStorage final : public nsIOfflineStorage
|
||||
{
|
||||
public:
|
||||
static already_AddRefed<OfflineStorage>
|
||||
Register(Context::ThreadsafeHandle* aContext, const QuotaInfo& aQuotaInfo);
|
||||
|
||||
void
|
||||
AddDestroyCallback(nsIRunnable* aCallback);
|
||||
|
||||
private:
|
||||
OfflineStorage(Context::ThreadsafeHandle* aContext,
|
||||
const QuotaInfo& aQuotaInfo,
|
||||
Client* aClient);
|
||||
~OfflineStorage();
|
||||
|
||||
nsRefPtr<Context::ThreadsafeHandle> mContext;
|
||||
const QuotaInfo mQuotaInfo;
|
||||
nsRefPtr<Client> mClient;
|
||||
nsTArray<nsCOMPtr<nsIRunnable>> mDestroyCallbacks;
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIOFFLINESTORAGE
|
||||
};
|
||||
|
||||
} // namespace cache
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_cache_QuotaOfflineStorage_h
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include "mozilla/DebugOnly.h"
|
||||
#include "mozilla/dom/cache/Manager.h"
|
||||
#include "mozilla/dom/cache/OfflineStorage.h"
|
||||
#include "mozilla/dom/quota/QuotaManager.h"
|
||||
#include "mozilla/dom/quota/UsageInfo.h"
|
||||
#include "nsIFile.h"
|
||||
|
@ -18,6 +19,7 @@ namespace {
|
|||
|
||||
using mozilla::DebugOnly;
|
||||
using mozilla::dom::cache::Manager;
|
||||
using mozilla::dom::cache::OfflineStorage;
|
||||
using mozilla::dom::quota::Client;
|
||||
using mozilla::dom::quota::PersistenceType;
|
||||
using mozilla::dom::quota::QuotaManager;
|
||||
|
@ -60,6 +62,41 @@ GetBodyUsage(nsIFile* aDir, UsageInfo* aUsageInfo)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
class StoragesDestroyedRunnable final : public nsRunnable
|
||||
{
|
||||
uint32_t mExpectedCalls;
|
||||
nsCOMPtr<nsIRunnable> mCallback;
|
||||
|
||||
public:
|
||||
StoragesDestroyedRunnable(uint32_t aExpectedCalls, nsIRunnable* aCallback)
|
||||
: mExpectedCalls(aExpectedCalls)
|
||||
, mCallback(aCallback)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(mExpectedCalls);
|
||||
MOZ_ASSERT(mCallback);
|
||||
}
|
||||
|
||||
NS_IMETHOD Run() override
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(mExpectedCalls);
|
||||
mExpectedCalls -= 1;
|
||||
if (!mExpectedCalls) {
|
||||
mCallback->Run();
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
~StoragesDestroyedRunnable()
|
||||
{
|
||||
// This is a callback runnable and not used for thread dispatch. It should
|
||||
// always be destroyed on the main thread.
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
}
|
||||
};
|
||||
|
||||
class CacheQuotaClient final : public Client
|
||||
{
|
||||
public:
|
||||
|
@ -151,13 +188,14 @@ public:
|
|||
OnOriginClearCompleted(PersistenceType aPersistenceType,
|
||||
const nsACString& aOrigin) override
|
||||
{
|
||||
// nothing to do
|
||||
// Nothing to do here.
|
||||
}
|
||||
|
||||
virtual void
|
||||
ReleaseIOThreadObjects() override
|
||||
{
|
||||
// nothing to do
|
||||
// Nothing to do here as the Context handles cleaning everything up
|
||||
// automatically.
|
||||
}
|
||||
|
||||
virtual bool
|
||||
|
@ -169,15 +207,26 @@ public:
|
|||
virtual bool
|
||||
IsTransactionServiceActivated() override
|
||||
{
|
||||
// TODO: implement nsIOfflineStorage interface (bug 1110487)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void
|
||||
WaitForStoragesToComplete(nsTArray<nsIOfflineStorage*>& aStorages,
|
||||
nsIRunnable* aCallback) override
|
||||
{
|
||||
// TODO: implement nsIOfflineStorage interface (bug 1110487)
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(!aStorages.IsEmpty());
|
||||
|
||||
nsCOMPtr<nsIRunnable> callback =
|
||||
new StoragesDestroyedRunnable(aStorages.Length(), aCallback);
|
||||
|
||||
for (uint32_t i = 0; i < aStorages.Length(); ++i) {
|
||||
MOZ_ASSERT(aStorages[i]->GetClient());
|
||||
MOZ_ASSERT(aStorages[i]->GetClient()->GetType() == Client::DOMCACHE);
|
||||
nsRefPtr<OfflineStorage> storage =
|
||||
static_cast<OfflineStorage*>(aStorages[i]);
|
||||
storage->AddDestroyCallback(callback);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -191,9 +240,11 @@ public:
|
|||
}
|
||||
|
||||
private:
|
||||
~CacheQuotaClient() { }
|
||||
~CacheQuotaClient()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
}
|
||||
|
||||
public:
|
||||
NS_INLINE_DECL_REFCOUNTING(CacheQuotaClient, override)
|
||||
};
|
||||
|
||||
|
|
|
@ -14,7 +14,8 @@ namespace mozilla {
|
|||
namespace dom {
|
||||
namespace cache {
|
||||
|
||||
already_AddRefed<quota::Client> CreateQuotaClient();
|
||||
already_AddRefed<quota::Client>
|
||||
CreateQuotaClient();
|
||||
|
||||
} // namespace cache
|
||||
} // namespace dom
|
||||
|
|
|
@ -23,7 +23,7 @@ StreamList::StreamList(Manager* aManager, Context* aContext)
|
|||
, mActivated(false)
|
||||
{
|
||||
MOZ_ASSERT(mManager);
|
||||
MOZ_ASSERT(mContext);
|
||||
mContext->AddActivity(this);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -142,6 +142,20 @@ StreamList::CloseAll()
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
StreamList::Cancel()
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(StreamList);
|
||||
CloseAll();
|
||||
}
|
||||
|
||||
bool
|
||||
StreamList::MatchesCacheId(CacheId aCacheId) const
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(StreamList);
|
||||
return aCacheId == mCacheId;
|
||||
}
|
||||
|
||||
StreamList::~StreamList()
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(StreamList);
|
||||
|
@ -153,6 +167,7 @@ StreamList::~StreamList()
|
|||
}
|
||||
mManager->ReleaseCacheId(mCacheId);
|
||||
}
|
||||
mContext->RemoveActivity(this);
|
||||
}
|
||||
|
||||
} // namespace cache
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#ifndef mozilla_dom_cache_StreamList_h
|
||||
#define mozilla_dom_cache_StreamList_h
|
||||
|
||||
#include "mozilla/dom/cache/Context.h"
|
||||
#include "mozilla/dom/cache/Types.h"
|
||||
#include "nsRefPtr.h"
|
||||
#include "nsTArray.h"
|
||||
|
@ -18,10 +19,9 @@ namespace dom {
|
|||
namespace cache {
|
||||
|
||||
class CacheStreamControlParent;
|
||||
class Context;
|
||||
class Manager;
|
||||
|
||||
class StreamList final
|
||||
class StreamList final : public Context::Activity
|
||||
{
|
||||
public:
|
||||
StreamList(Manager* aManager, Context* aContext);
|
||||
|
@ -39,6 +39,10 @@ public:
|
|||
void Close(const nsID& aId);
|
||||
void CloseAll();
|
||||
|
||||
// Context::Activity methods
|
||||
virtual void Cancel() override;
|
||||
virtual bool MatchesCacheId(CacheId aCacheId) const override;
|
||||
|
||||
private:
|
||||
~StreamList();
|
||||
struct Entry
|
||||
|
|
|
@ -34,6 +34,7 @@ struct QuotaInfo
|
|||
nsCOMPtr<nsIFile> mDir;
|
||||
nsCString mGroup;
|
||||
nsCString mOrigin;
|
||||
nsCString mStorageId;
|
||||
bool mIsApp;
|
||||
};
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@ EXPORTS.mozilla.dom.cache += [
|
|||
'IPCUtils.h',
|
||||
'Manager.h',
|
||||
'ManagerId.h',
|
||||
'OfflineStorage.h',
|
||||
'PrincipalVerifier.h',
|
||||
'QuotaClient.h',
|
||||
'ReadStream.h',
|
||||
|
@ -61,6 +62,7 @@ UNIFIED_SOURCES += [
|
|||
'FileUtils.cpp',
|
||||
'Manager.cpp',
|
||||
'ManagerId.cpp',
|
||||
'OfflineStorage.cpp',
|
||||
'PrincipalVerifier.cpp',
|
||||
'QuotaClient.cpp',
|
||||
'ReadStream.cpp',
|
||||
|
|
|
@ -30,6 +30,22 @@ function runTests(testFile, order) {
|
|||
});
|
||||
}
|
||||
|
||||
// adapted from dom/indexedDB/test/helpers.js
|
||||
function clearStorage() {
|
||||
return new Promise(function(resolve, reject) {
|
||||
var principal = SpecialPowers.wrap(document).nodePrincipal;
|
||||
var appId, inBrowser;
|
||||
var nsIPrincipal = SpecialPowers.Components.interfaces.nsIPrincipal;
|
||||
if (principal.appId != nsIPrincipal.UNKNOWN_APP_ID &&
|
||||
principal.appId != nsIPrincipal.NO_APP_ID) {
|
||||
appId = principal.appId;
|
||||
inBrowser = principal.isInBrowserElement;
|
||||
}
|
||||
SpecialPowers.clearStorageForURI(document.documentURI, resolve, appId,
|
||||
inBrowser);
|
||||
});
|
||||
}
|
||||
|
||||
function loadScript(script) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
var s = document.createElement("script");
|
||||
|
@ -100,8 +116,11 @@ function runTests(testFile, order) {
|
|||
return setupPrefs()
|
||||
.then(importDrivers)
|
||||
.then(runWorkerTest)
|
||||
.then(clearStorage)
|
||||
.then(runServiceWorkerTest)
|
||||
.then(clearStorage)
|
||||
.then(runFrameTest)
|
||||
.then(clearStorage)
|
||||
.catch(function(e) {
|
||||
ok(false, "A promise was rejected during test execution: " + e);
|
||||
});
|
||||
|
@ -109,6 +128,7 @@ function runTests(testFile, order) {
|
|||
return setupPrefs()
|
||||
.then(importDrivers)
|
||||
.then(() => Promise.all([runWorkerTest(), runServiceWorkerTest(), runFrameTest()]))
|
||||
.then(clearStorage)
|
||||
.catch(function(e) {
|
||||
ok(false, "A promise was rejected during test execution: " + e);
|
||||
});
|
||||
|
|
Загрузка…
Ссылка в новой задаче