LCOV - code coverage report
Current view: top level - dom/cache - Context.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 451 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 68 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "mozilla/dom/cache/Context.h"
       8             : 
       9             : #include "mozilla/AutoRestore.h"
      10             : #include "mozilla/dom/cache/Action.h"
      11             : #include "mozilla/dom/cache/FileUtils.h"
      12             : #include "mozilla/dom/cache/Manager.h"
      13             : #include "mozilla/dom/cache/ManagerId.h"
      14             : #include "mozilla/dom/quota/QuotaManager.h"
      15             : #include "mozIStorageConnection.h"
      16             : #include "nsIFile.h"
      17             : #include "nsIPrincipal.h"
      18             : #include "nsIRunnable.h"
      19             : #include "nsThreadUtils.h"
      20             : 
      21             : namespace {
      22             : 
      23             : using mozilla::dom::cache::Action;
      24             : using mozilla::dom::cache::QuotaInfo;
      25             : 
      26           0 : class NullAction final : public Action
      27             : {
      28             : public:
      29           0 :   NullAction()
      30           0 :   {
      31           0 :   }
      32             : 
      33             :   virtual void
      34           0 :   RunOnTarget(Resolver* aResolver, const QuotaInfo&, Data*) override
      35             :   {
      36             :     // Resolve success immediately.  This Action does no actual work.
      37           0 :     MOZ_DIAGNOSTIC_ASSERT(aResolver);
      38           0 :     aResolver->Resolve(NS_OK);
      39           0 :   }
      40             : };
      41             : 
      42             : } // namespace
      43             : 
      44             : namespace mozilla {
      45             : namespace dom {
      46             : namespace cache {
      47             : 
      48             : using mozilla::dom::quota::AssertIsOnIOThread;
      49             : using mozilla::dom::quota::OpenDirectoryListener;
      50             : using mozilla::dom::quota::QuotaManager;
      51             : using mozilla::dom::quota::PERSISTENCE_TYPE_DEFAULT;
      52             : using mozilla::dom::quota::PersistenceType;
      53             : 
      54             : class Context::Data final : public Action::Data
      55             : {
      56             : public:
      57           0 :   explicit Data(nsISerialEventTarget* aTarget)
      58           0 :     : mTarget(aTarget)
      59             :   {
      60           0 :     MOZ_DIAGNOSTIC_ASSERT(mTarget);
      61           0 :   }
      62             : 
      63             :   virtual mozIStorageConnection*
      64           0 :   GetConnection() const override
      65             :   {
      66           0 :     MOZ_ASSERT(mTarget->IsOnCurrentThread());
      67           0 :     return mConnection;
      68             :   }
      69             : 
      70             :   virtual void
      71           0 :   SetConnection(mozIStorageConnection* aConn) override
      72             :   {
      73           0 :     MOZ_ASSERT(mTarget->IsOnCurrentThread());
      74           0 :     MOZ_DIAGNOSTIC_ASSERT(!mConnection);
      75           0 :     mConnection = aConn;
      76           0 :     MOZ_DIAGNOSTIC_ASSERT(mConnection);
      77           0 :   }
      78             : 
      79             : private:
      80           0 :   ~Data()
      81           0 :   {
      82             :     // We could proxy release our data here, but instead just assert.  The
      83             :     // Context code should guarantee that we are destroyed on the target
      84             :     // thread once the connection is initialized.  If we're not, then
      85             :     // QuotaManager might race and try to clear the origin out from under us.
      86           0 :     MOZ_ASSERT_IF(mConnection, mTarget->IsOnCurrentThread());
      87           0 :   }
      88             : 
      89             :   nsCOMPtr<nsISerialEventTarget> mTarget;
      90             :   nsCOMPtr<mozIStorageConnection> mConnection;
      91             : 
      92             :   // Threadsafe counting because we're created on the PBackground thread
      93             :   // and destroyed on the target IO thread.
      94           0 :   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Context::Data)
      95             : };
      96             : 
      97             : // Executed to perform the complicated dance of steps necessary to initialize
      98             : // the QuotaManager.  This must be performed for each origin before any disk
      99             : // IO occurrs.
     100             : class Context::QuotaInitRunnable final : public nsIRunnable
     101             :                                        , public OpenDirectoryListener
     102             : {
     103             : public:
     104           0 :   QuotaInitRunnable(Context* aContext,
     105             :                     Manager* aManager,
     106             :                     Data* aData,
     107             :                     nsISerialEventTarget* aTarget,
     108             :                     Action* aInitAction)
     109           0 :     : mContext(aContext)
     110           0 :     , mThreadsafeHandle(aContext->CreateThreadsafeHandle())
     111             :     , mManager(aManager)
     112             :     , mData(aData)
     113             :     , mTarget(aTarget)
     114             :     , mInitAction(aInitAction)
     115             :     , mInitiatingEventTarget(GetCurrentThreadEventTarget())
     116             :     , mResult(NS_OK)
     117             :     , mState(STATE_INIT)
     118           0 :     , mCanceled(false)
     119             :   {
     120           0 :     MOZ_DIAGNOSTIC_ASSERT(mContext);
     121           0 :     MOZ_DIAGNOSTIC_ASSERT(mManager);
     122           0 :     MOZ_DIAGNOSTIC_ASSERT(mData);
     123           0 :     MOZ_DIAGNOSTIC_ASSERT(mTarget);
     124           0 :     MOZ_DIAGNOSTIC_ASSERT(mInitiatingEventTarget);
     125           0 :     MOZ_DIAGNOSTIC_ASSERT(mInitAction);
     126           0 :   }
     127             : 
     128           0 :   nsresult Dispatch()
     129             :   {
     130           0 :     NS_ASSERT_OWNINGTHREAD(QuotaInitRunnable);
     131           0 :     MOZ_DIAGNOSTIC_ASSERT(mState == STATE_INIT);
     132             : 
     133           0 :     mState = STATE_GET_INFO;
     134           0 :     nsresult rv = NS_DispatchToMainThread(this, nsIThread::DISPATCH_NORMAL);
     135           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
     136           0 :       mState = STATE_COMPLETE;
     137           0 :       Clear();
     138             :     }
     139           0 :     return rv;
     140             :   }
     141             : 
     142           0 :   void Cancel()
     143             :   {
     144           0 :     NS_ASSERT_OWNINGTHREAD(QuotaInitRunnable);
     145           0 :     MOZ_DIAGNOSTIC_ASSERT(!mCanceled);
     146           0 :     mCanceled = true;
     147           0 :     mInitAction->CancelOnInitiatingThread();
     148           0 :   }
     149             : 
     150             :   void OpenDirectory();
     151             : 
     152             :   // OpenDirectoryListener methods
     153             :   virtual void
     154             :   DirectoryLockAcquired(DirectoryLock* aLock) override;
     155             : 
     156             :   virtual void
     157             :   DirectoryLockFailed() override;
     158             : 
     159             : private:
     160             :   class SyncResolver final : public Action::Resolver
     161             :   {
     162             :   public:
     163           0 :     SyncResolver()
     164           0 :       : mResolved(false)
     165           0 :       , mResult(NS_OK)
     166           0 :     { }
     167             : 
     168             :     virtual void
     169           0 :     Resolve(nsresult aRv) override
     170             :     {
     171           0 :       MOZ_DIAGNOSTIC_ASSERT(!mResolved);
     172           0 :       mResolved = true;
     173           0 :       mResult = aRv;
     174           0 :     };
     175             : 
     176           0 :     bool Resolved() const { return mResolved; }
     177           0 :     nsresult Result() const { return mResult; }
     178             : 
     179             :   private:
     180           0 :     ~SyncResolver() { }
     181             : 
     182             :     bool mResolved;
     183             :     nsresult mResult;
     184             : 
     185           0 :     NS_INLINE_DECL_REFCOUNTING(Context::QuotaInitRunnable::SyncResolver, override)
     186             :   };
     187             : 
     188           0 :   ~QuotaInitRunnable()
     189           0 :   {
     190           0 :     MOZ_DIAGNOSTIC_ASSERT(mState == STATE_COMPLETE);
     191           0 :     MOZ_DIAGNOSTIC_ASSERT(!mContext);
     192           0 :     MOZ_DIAGNOSTIC_ASSERT(!mInitAction);
     193           0 :   }
     194             : 
     195             :   enum State
     196             :   {
     197             :     STATE_INIT,
     198             :     STATE_GET_INFO,
     199             :     STATE_CREATE_QUOTA_MANAGER,
     200             :     STATE_OPEN_DIRECTORY,
     201             :     STATE_WAIT_FOR_DIRECTORY_LOCK,
     202             :     STATE_ENSURE_ORIGIN_INITIALIZED,
     203             :     STATE_RUN_ON_TARGET,
     204             :     STATE_RUNNING,
     205             :     STATE_COMPLETING,
     206             :     STATE_COMPLETE
     207             :   };
     208             : 
     209           0 :   void Complete(nsresult aResult)
     210             :   {
     211           0 :     MOZ_DIAGNOSTIC_ASSERT(mState == STATE_RUNNING || NS_FAILED(aResult));
     212             : 
     213           0 :     MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(mResult));
     214           0 :     mResult = aResult;
     215             : 
     216           0 :     mState = STATE_COMPLETING;
     217           0 :     MOZ_ALWAYS_SUCCEEDS(
     218             :       mInitiatingEventTarget->Dispatch(this, nsIThread::DISPATCH_NORMAL));
     219           0 :   }
     220             : 
     221           0 :   void Clear()
     222             :   {
     223           0 :     NS_ASSERT_OWNINGTHREAD(QuotaInitRunnable);
     224           0 :     MOZ_DIAGNOSTIC_ASSERT(mContext);
     225           0 :     mContext = nullptr;
     226           0 :     mManager = nullptr;
     227           0 :     mInitAction = nullptr;
     228           0 :   }
     229             : 
     230             :   RefPtr<Context> mContext;
     231             :   RefPtr<ThreadsafeHandle> mThreadsafeHandle;
     232             :   RefPtr<Manager> mManager;
     233             :   RefPtr<Data> mData;
     234             :   nsCOMPtr<nsISerialEventTarget> mTarget;
     235             :   RefPtr<Action> mInitAction;
     236             :   nsCOMPtr<nsIEventTarget> mInitiatingEventTarget;
     237             :   nsresult mResult;
     238             :   QuotaInfo mQuotaInfo;
     239             :   RefPtr<DirectoryLock> mDirectoryLock;
     240             :   State mState;
     241             :   Atomic<bool> mCanceled;
     242             : 
     243             : public:
     244             :   NS_DECL_THREADSAFE_ISUPPORTS
     245             :   NS_DECL_NSIRUNNABLE
     246             : };
     247             : 
     248             : void
     249           0 : Context::QuotaInitRunnable::OpenDirectory()
     250             : {
     251           0 :   NS_ASSERT_OWNINGTHREAD(QuotaInitRunnable);
     252           0 :   MOZ_DIAGNOSTIC_ASSERT(mState == STATE_CREATE_QUOTA_MANAGER ||
     253             :                         mState == STATE_OPEN_DIRECTORY);
     254           0 :   MOZ_DIAGNOSTIC_ASSERT(QuotaManager::Get());
     255             : 
     256             :   // QuotaManager::OpenDirectory() will hold a reference to us as
     257             :   // a listener.  We will then get DirectoryLockAcquired() on the owning
     258             :   // thread when it is safe to access our storage directory.
     259           0 :   mState = STATE_WAIT_FOR_DIRECTORY_LOCK;
     260           0 :   QuotaManager::Get()->OpenDirectory(PERSISTENCE_TYPE_DEFAULT,
     261             :                                      mQuotaInfo.mGroup,
     262             :                                      mQuotaInfo.mOrigin,
     263             :                                      quota::Client::DOMCACHE,
     264             :                                      /* aExclusive */ false,
     265           0 :                                      this);
     266           0 : }
     267             : 
     268             : void
     269           0 : Context::QuotaInitRunnable::DirectoryLockAcquired(DirectoryLock* aLock)
     270             : {
     271           0 :   NS_ASSERT_OWNINGTHREAD(QuotaInitRunnable);
     272           0 :   MOZ_DIAGNOSTIC_ASSERT(mState == STATE_WAIT_FOR_DIRECTORY_LOCK);
     273           0 :   MOZ_DIAGNOSTIC_ASSERT(!mDirectoryLock);
     274             : 
     275           0 :   mDirectoryLock = aLock;
     276             : 
     277           0 :   if (mCanceled) {
     278           0 :     Complete(NS_ERROR_ABORT);
     279           0 :     return;
     280             :   }
     281             : 
     282           0 :   QuotaManager* qm = QuotaManager::Get();
     283           0 :   MOZ_DIAGNOSTIC_ASSERT(qm);
     284             : 
     285           0 :   mState = STATE_ENSURE_ORIGIN_INITIALIZED;
     286           0 :   nsresult rv = qm->IOThread()->Dispatch(this, nsIThread::DISPATCH_NORMAL);
     287           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     288           0 :     Complete(rv);
     289           0 :     return;
     290             :   }
     291             : }
     292             : 
     293             : void
     294           0 : Context::QuotaInitRunnable::DirectoryLockFailed()
     295             : {
     296           0 :   NS_ASSERT_OWNINGTHREAD(QuotaInitRunnable);
     297           0 :   MOZ_DIAGNOSTIC_ASSERT(mState == STATE_WAIT_FOR_DIRECTORY_LOCK);
     298           0 :   MOZ_DIAGNOSTIC_ASSERT(!mDirectoryLock);
     299             : 
     300           0 :   NS_WARNING("Failed to acquire a directory lock!");
     301             : 
     302           0 :   Complete(NS_ERROR_FAILURE);
     303           0 : }
     304             : 
     305           0 : NS_IMPL_ISUPPORTS(mozilla::dom::cache::Context::QuotaInitRunnable, nsIRunnable);
     306             : 
     307             : // The QuotaManager init state machine is represented in the following diagram:
     308             : //
     309             : //    +---------------+
     310             : //    |     Start     |      Resolve(error)
     311             : //    | (Orig Thread) +---------------------+
     312             : //    +-------+-------+                     |
     313             : //            |                             |
     314             : // +----------v-----------+                 |
     315             : // |       GetInfo        |  Resolve(error) |
     316             : // |    (Main Thread)     +-----------------+
     317             : // +----------+-----------+                 |
     318             : //            |                             |
     319             : // +----------v-----------+                 |
     320             : // |  CreateQuotaManager  |  Resolve(error) |
     321             : // |    (Orig Thread)     +-----------------+
     322             : // +----------+-----------+                 |
     323             : //            |                             |
     324             : // +----------v-----------+                 |
     325             : // |    OpenDirectory     |  Resolve(error) |
     326             : // |    (Orig Thread)     +-----------------+
     327             : // +----------+-----------+                 |
     328             : //            |                             |
     329             : // +----------v-----------+                 |
     330             : // | WaitForDirectoryLock |  Resolve(error) |
     331             : // |    (Orig Thread)     +-----------------+
     332             : // +----------+-----------+                 |
     333             : //            |                             |
     334             : // +----------v------------+                |
     335             : // |EnsureOriginInitialized| Resolve(error) |
     336             : // |   (Quota IO Thread)   +----------------+
     337             : // +----------+------------+                |
     338             : //            |                             |
     339             : // +----------v------------+                |
     340             : // |     RunOnTarget       | Resolve(error) |
     341             : // |   (Target Thread)     +----------------+
     342             : // +----------+------------+                |
     343             : //            |                             |
     344             : //  +---------v---------+            +------v------+
     345             : //  |      Running      |            |  Completing |
     346             : //  | (Target Thread)   +------------>(Orig Thread)|
     347             : //  +-------------------+            +------+------+
     348             : //                                          |
     349             : //                                    +-----v----+
     350             : //                                    | Complete |
     351             : //                                    +----------+
     352             : //
     353             : // The initialization process proceeds through the main states.  If an error
     354             : // occurs, then we transition to Completing state back on the original thread.
     355             : NS_IMETHODIMP
     356           0 : Context::QuotaInitRunnable::Run()
     357             : {
     358             :   // May run on different threads depending on the state.  See individual
     359             :   // state cases for thread assertions.
     360             : 
     361           0 :   RefPtr<SyncResolver> resolver = new SyncResolver();
     362             : 
     363           0 :   switch(mState) {
     364             :     // -----------------------------------
     365             :     case STATE_GET_INFO:
     366             :     {
     367           0 :       MOZ_ASSERT(NS_IsMainThread());
     368             : 
     369           0 :       if (mCanceled) {
     370           0 :         resolver->Resolve(NS_ERROR_ABORT);
     371           0 :         break;
     372             :       }
     373             : 
     374           0 :       RefPtr<ManagerId> managerId = mManager->GetManagerId();
     375           0 :       nsCOMPtr<nsIPrincipal> principal = managerId->Principal();
     376           0 :       nsresult rv = QuotaManager::GetInfoFromPrincipal(principal,
     377             :                                                        &mQuotaInfo.mSuffix,
     378             :                                                        &mQuotaInfo.mGroup,
     379           0 :                                                        &mQuotaInfo.mOrigin);
     380           0 :       if (NS_WARN_IF(NS_FAILED(rv))) {
     381           0 :         resolver->Resolve(rv);
     382           0 :         break;
     383             :       }
     384             : 
     385           0 :       mState = STATE_CREATE_QUOTA_MANAGER;
     386           0 :       MOZ_ALWAYS_SUCCEEDS(
     387             :         mInitiatingEventTarget->Dispatch(this, nsIThread::DISPATCH_NORMAL));
     388           0 :       break;
     389             :     }
     390             :     // ----------------------------------
     391             :     case STATE_CREATE_QUOTA_MANAGER:
     392             :     {
     393           0 :       NS_ASSERT_OWNINGTHREAD(QuotaInitRunnable);
     394             : 
     395           0 :       if (mCanceled || QuotaManager::IsShuttingDown()) {
     396           0 :         resolver->Resolve(NS_ERROR_ABORT);
     397           0 :         break;
     398             :       }
     399             : 
     400           0 :       if (QuotaManager::Get()) {
     401           0 :         OpenDirectory();
     402           0 :         return NS_OK;
     403             :       }
     404             : 
     405           0 :       mState = STATE_OPEN_DIRECTORY;
     406           0 :       QuotaManager::GetOrCreate(this);
     407           0 :       break;
     408             :     }
     409             :     // ----------------------------------
     410             :     case STATE_OPEN_DIRECTORY:
     411             :     {
     412           0 :       NS_ASSERT_OWNINGTHREAD(QuotaInitRunnable);
     413             : 
     414           0 :       if (NS_WARN_IF(!QuotaManager::Get())) {
     415           0 :         resolver->Resolve(NS_ERROR_FAILURE);
     416           0 :         break;
     417             :       }
     418             : 
     419           0 :       OpenDirectory();
     420           0 :       break;
     421             :     }
     422             :     // ----------------------------------
     423             :     case STATE_ENSURE_ORIGIN_INITIALIZED:
     424             :     {
     425           0 :       AssertIsOnIOThread();
     426             : 
     427           0 :       if (mCanceled) {
     428           0 :         resolver->Resolve(NS_ERROR_ABORT);
     429           0 :         break;
     430             :       }
     431             : 
     432           0 :       QuotaManager* qm = QuotaManager::Get();
     433           0 :       MOZ_DIAGNOSTIC_ASSERT(qm);
     434           0 :       nsresult rv = qm->EnsureOriginIsInitialized(PERSISTENCE_TYPE_DEFAULT,
     435             :                                                   mQuotaInfo.mSuffix,
     436             :                                                   mQuotaInfo.mGroup,
     437             :                                                   mQuotaInfo.mOrigin,
     438           0 :                                                   getter_AddRefs(mQuotaInfo.mDir));
     439           0 :       if (NS_FAILED(rv)) {
     440           0 :         resolver->Resolve(rv);
     441           0 :         break;
     442             :       }
     443             : 
     444           0 :       mState = STATE_RUN_ON_TARGET;
     445             : 
     446           0 :       MOZ_ALWAYS_SUCCEEDS(
     447             :         mTarget->Dispatch(this, nsIThread::DISPATCH_NORMAL));
     448           0 :       break;
     449             :     }
     450             :     // -------------------
     451             :     case STATE_RUN_ON_TARGET:
     452             :     {
     453           0 :       MOZ_ASSERT(mTarget->IsOnCurrentThread());
     454             : 
     455           0 :       mState = STATE_RUNNING;
     456             : 
     457             :       // Execute the provided initialization Action.  The Action must Resolve()
     458             :       // before returning.
     459           0 :       mInitAction->RunOnTarget(resolver, mQuotaInfo, mData);
     460           0 :       MOZ_DIAGNOSTIC_ASSERT(resolver->Resolved());
     461             : 
     462           0 :       mData = nullptr;
     463             : 
     464             :       // If the database was opened, then we should always succeed when creating
     465             :       // the marker file.  If it wasn't opened successfully, then no need to
     466             :       // create a marker file anyway.
     467           0 :       if (NS_SUCCEEDED(resolver->Result())) {
     468           0 :         MOZ_ALWAYS_SUCCEEDS(CreateMarkerFile(mQuotaInfo));
     469             :       }
     470             : 
     471           0 :       break;
     472             :     }
     473             :     // -------------------
     474             :     case STATE_COMPLETING:
     475             :     {
     476           0 :       NS_ASSERT_OWNINGTHREAD(QuotaInitRunnable);
     477           0 :       mInitAction->CompleteOnInitiatingThread(mResult);
     478           0 :       mContext->OnQuotaInit(mResult, mQuotaInfo, mDirectoryLock.forget());
     479           0 :       mState = STATE_COMPLETE;
     480             : 
     481             :       // Explicitly cleanup here as the destructor could fire on any of
     482             :       // the threads we have bounced through.
     483           0 :       Clear();
     484           0 :       break;
     485             :     }
     486             :     // -----
     487             :     case STATE_WAIT_FOR_DIRECTORY_LOCK:
     488             :     default:
     489             :     {
     490           0 :       MOZ_CRASH("unexpected state in QuotaInitRunnable");
     491             :     }
     492             :   }
     493             : 
     494           0 :   if (resolver->Resolved()) {
     495           0 :     Complete(resolver->Result());
     496             :   }
     497             : 
     498           0 :   return NS_OK;
     499             : }
     500             : 
     501             : // Runnable wrapper around Action objects dispatched on the Context.  This
     502             : // runnable executes the Action on the appropriate threads while the Context
     503             : // is initialized.
     504             : class Context::ActionRunnable final : public nsIRunnable
     505             :                                     , public Action::Resolver
     506             :                                     , public Context::Activity
     507             : {
     508             : public:
     509           0 :   ActionRunnable(Context* aContext, Data* aData, nsISerialEventTarget* aTarget,
     510             :                  Action* aAction, const QuotaInfo& aQuotaInfo)
     511           0 :     : mContext(aContext)
     512             :     , mData(aData)
     513             :     , mTarget(aTarget)
     514             :     , mAction(aAction)
     515             :     , mQuotaInfo(aQuotaInfo)
     516             :     , mInitiatingThread(GetCurrentThreadEventTarget())
     517             :     , mState(STATE_INIT)
     518             :     , mResult(NS_OK)
     519           0 :     , mExecutingRunOnTarget(false)
     520             :   {
     521           0 :     MOZ_DIAGNOSTIC_ASSERT(mContext);
     522             :     // mData may be nullptr
     523           0 :     MOZ_DIAGNOSTIC_ASSERT(mTarget);
     524           0 :     MOZ_DIAGNOSTIC_ASSERT(mAction);
     525             :     // mQuotaInfo.mDir may be nullptr if QuotaInitRunnable failed
     526           0 :     MOZ_DIAGNOSTIC_ASSERT(mInitiatingThread);
     527           0 :   }
     528             : 
     529           0 :   nsresult Dispatch()
     530             :   {
     531           0 :     NS_ASSERT_OWNINGTHREAD(ActionRunnable);
     532           0 :     MOZ_DIAGNOSTIC_ASSERT(mState == STATE_INIT);
     533             : 
     534           0 :     mState = STATE_RUN_ON_TARGET;
     535           0 :     nsresult rv = mTarget->Dispatch(this, nsIEventTarget::DISPATCH_NORMAL);
     536           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
     537           0 :       mState = STATE_COMPLETE;
     538           0 :       Clear();
     539             :     }
     540           0 :     return rv;
     541             :   }
     542             : 
     543             :   virtual bool
     544           0 :   MatchesCacheId(CacheId aCacheId) const override
     545             :   {
     546           0 :     NS_ASSERT_OWNINGTHREAD(ActionRunnable);
     547           0 :     return mAction->MatchesCacheId(aCacheId);
     548             :   }
     549             : 
     550             :   virtual void
     551           0 :   Cancel() override
     552             :   {
     553           0 :     NS_ASSERT_OWNINGTHREAD(ActionRunnable);
     554           0 :     mAction->CancelOnInitiatingThread();
     555           0 :   }
     556             : 
     557           0 :   virtual void Resolve(nsresult aRv) override
     558             :   {
     559           0 :     MOZ_ASSERT(mTarget->IsOnCurrentThread());
     560           0 :     MOZ_DIAGNOSTIC_ASSERT(mState == STATE_RUNNING);
     561             : 
     562           0 :     mResult = aRv;
     563             : 
     564             :     // We ultimately must complete on the initiating thread, but bounce through
     565             :     // the current thread again to ensure that we don't destroy objects and
     566             :     // state out from under the currently running action's stack.
     567           0 :     mState = STATE_RESOLVING;
     568             : 
     569             :     // If we were resolved synchronously within Action::RunOnTarget() then we
     570             :     // can avoid a thread bounce and just resolve once RunOnTarget() returns.
     571             :     // The Run() method will handle this by looking at mState after
     572             :     // RunOnTarget() returns.
     573           0 :     if (mExecutingRunOnTarget) {
     574           0 :       return;
     575             :     }
     576             : 
     577             :     // Otherwise we are in an asynchronous resolve.  And must perform a thread
     578             :     // bounce to run on the target thread again.
     579           0 :     MOZ_ALWAYS_SUCCEEDS(
     580             :       mTarget->Dispatch(this, nsIThread::DISPATCH_NORMAL));
     581             :   }
     582             : 
     583             : private:
     584           0 :   ~ActionRunnable()
     585           0 :   {
     586           0 :     MOZ_DIAGNOSTIC_ASSERT(mState == STATE_COMPLETE);
     587           0 :     MOZ_DIAGNOSTIC_ASSERT(!mContext);
     588           0 :     MOZ_DIAGNOSTIC_ASSERT(!mAction);
     589           0 :   }
     590             : 
     591           0 :   void Clear()
     592             :   {
     593           0 :     NS_ASSERT_OWNINGTHREAD(ActionRunnable);
     594           0 :     MOZ_DIAGNOSTIC_ASSERT(mContext);
     595           0 :     MOZ_DIAGNOSTIC_ASSERT(mAction);
     596           0 :     mContext->RemoveActivity(this);
     597           0 :     mContext = nullptr;
     598           0 :     mAction = nullptr;
     599           0 :   }
     600             : 
     601             :   enum State
     602             :   {
     603             :     STATE_INIT,
     604             :     STATE_RUN_ON_TARGET,
     605             :     STATE_RUNNING,
     606             :     STATE_RESOLVING,
     607             :     STATE_COMPLETING,
     608             :     STATE_COMPLETE
     609             :   };
     610             : 
     611             :   RefPtr<Context> mContext;
     612             :   RefPtr<Data> mData;
     613             :   nsCOMPtr<nsISerialEventTarget> mTarget;
     614             :   RefPtr<Action> mAction;
     615             :   const QuotaInfo mQuotaInfo;
     616             :   nsCOMPtr<nsIEventTarget> mInitiatingThread;
     617             :   State mState;
     618             :   nsresult mResult;
     619             : 
     620             :   // Only accessible on target thread;
     621             :   bool mExecutingRunOnTarget;
     622             : 
     623             : public:
     624             :   NS_DECL_THREADSAFE_ISUPPORTS
     625             :   NS_DECL_NSIRUNNABLE
     626             : };
     627             : 
     628           0 : NS_IMPL_ISUPPORTS(mozilla::dom::cache::Context::ActionRunnable, nsIRunnable);
     629             : 
     630             : // The ActionRunnable has a simpler state machine.  It basically needs to run
     631             : // the action on the target thread and then complete on the original thread.
     632             : //
     633             : //   +-------------+
     634             : //   |    Start    |
     635             : //   |(Orig Thread)|
     636             : //   +-----+-------+
     637             : //         |
     638             : // +-------v---------+
     639             : // |  RunOnTarget    |
     640             : // |Target IO Thread)+---+ Resolve()
     641             : // +-------+---------+   |
     642             : //         |             |
     643             : // +-------v----------+  |
     644             : // |     Running      |  |
     645             : // |(Target IO Thread)|  |
     646             : // +------------------+  |
     647             : //         | Resolve()   |
     648             : // +-------v----------+  |
     649             : // |     Resolving    <--+                   +-------------+
     650             : // |                  |                      |  Completing |
     651             : // |(Target IO Thread)+---------------------->(Orig Thread)|
     652             : // +------------------+                      +-------+-----+
     653             : //                                                   |
     654             : //                                                   |
     655             : //                                              +----v---+
     656             : //                                              |Complete|
     657             : //                                              +--------+
     658             : //
     659             : // Its important to note that synchronous actions will effectively Resolve()
     660             : // out of the Running state immediately.  Asynchronous Actions may remain
     661             : // in the Running state for some time, but normally the ActionRunnable itself
     662             : // does not see any execution there.  Its all handled internal to the Action.
     663             : NS_IMETHODIMP
     664           0 : Context::ActionRunnable::Run()
     665             : {
     666           0 :   switch(mState) {
     667             :     // ----------------------
     668             :     case STATE_RUN_ON_TARGET:
     669             :     {
     670           0 :       MOZ_ASSERT(mTarget->IsOnCurrentThread());
     671           0 :       MOZ_DIAGNOSTIC_ASSERT(!mExecutingRunOnTarget);
     672             : 
     673             :       // Note that we are calling RunOnTarget().  This lets us detect
     674             :       // if Resolve() is called synchronously.
     675           0 :       AutoRestore<bool> executingRunOnTarget(mExecutingRunOnTarget);
     676           0 :       mExecutingRunOnTarget = true;
     677             : 
     678           0 :       mState = STATE_RUNNING;
     679           0 :       mAction->RunOnTarget(this, mQuotaInfo, mData);
     680             : 
     681           0 :       mData = nullptr;
     682             : 
     683             :       // Resolve was called synchronously from RunOnTarget().  We can
     684             :       // immediately move to completing now since we are sure RunOnTarget()
     685             :       // completed.
     686           0 :       if (mState == STATE_RESOLVING) {
     687             :         // Use recursion instead of switch case fall-through...  Seems slightly
     688             :         // easier to understand.
     689           0 :         Run();
     690             :       }
     691             : 
     692           0 :       break;
     693             :     }
     694             :     // -----------------
     695             :     case STATE_RESOLVING:
     696             :     {
     697           0 :       MOZ_ASSERT(mTarget->IsOnCurrentThread());
     698             :       // The call to Action::RunOnTarget() must have returned now if we
     699             :       // are running on the target thread again.  We may now proceed
     700             :       // with completion.
     701           0 :       mState = STATE_COMPLETING;
     702             :       // Shutdown must be delayed until all Contexts are destroyed.  Crash
     703             :       // for this invariant violation.
     704           0 :       MOZ_ALWAYS_SUCCEEDS(
     705             :         mInitiatingThread->Dispatch(this, nsIThread::DISPATCH_NORMAL));
     706           0 :       break;
     707             :     }
     708             :     // -------------------
     709             :     case STATE_COMPLETING:
     710             :     {
     711           0 :       NS_ASSERT_OWNINGTHREAD(ActionRunnable);
     712           0 :       mAction->CompleteOnInitiatingThread(mResult);
     713           0 :       mState = STATE_COMPLETE;
     714             :       // Explicitly cleanup here as the destructor could fire on any of
     715             :       // the threads we have bounced through.
     716           0 :       Clear();
     717           0 :       break;
     718             :     }
     719             :     // -----------------
     720             :     default:
     721             :     {
     722           0 :       MOZ_CRASH("unexpected state in ActionRunnable");
     723             :       break;
     724             :     }
     725             :   }
     726           0 :   return NS_OK;
     727             : }
     728             : 
     729             : void
     730           0 : Context::ThreadsafeHandle::AllowToClose()
     731             : {
     732           0 :   if (mOwningEventTarget->IsOnCurrentThread()) {
     733           0 :     AllowToCloseOnOwningThread();
     734           0 :     return;
     735             :   }
     736             : 
     737             :   // Dispatch is guaranteed to succeed here because we block shutdown until
     738             :   // all Contexts have been destroyed.
     739           0 :   nsCOMPtr<nsIRunnable> runnable = NewRunnableMethod(
     740             :     "dom::cache::Context::ThreadsafeHandle::AllowToCloseOnOwningThread",
     741             :     this,
     742           0 :     &ThreadsafeHandle::AllowToCloseOnOwningThread);
     743           0 :   MOZ_ALWAYS_SUCCEEDS(
     744             :     mOwningEventTarget->Dispatch(runnable.forget(), nsIThread::DISPATCH_NORMAL));
     745             : }
     746             : 
     747             : void
     748           0 : Context::ThreadsafeHandle::InvalidateAndAllowToClose()
     749             : {
     750           0 :   if (mOwningEventTarget->IsOnCurrentThread()) {
     751           0 :     InvalidateAndAllowToCloseOnOwningThread();
     752           0 :     return;
     753             :   }
     754             : 
     755             :   // Dispatch is guaranteed to succeed here because we block shutdown until
     756             :   // all Contexts have been destroyed.
     757           0 :   nsCOMPtr<nsIRunnable> runnable = NewRunnableMethod(
     758             :     "dom::cache::Context::ThreadsafeHandle::"
     759             :     "InvalidateAndAllowToCloseOnOwningThread",
     760             :     this,
     761           0 :     &ThreadsafeHandle::InvalidateAndAllowToCloseOnOwningThread);
     762           0 :   MOZ_ALWAYS_SUCCEEDS(
     763             :     mOwningEventTarget->Dispatch(runnable.forget(), nsIThread::DISPATCH_NORMAL));
     764             : }
     765             : 
     766           0 : Context::ThreadsafeHandle::ThreadsafeHandle(Context* aContext)
     767             :   : mStrongRef(aContext)
     768             :   , mWeakRef(aContext)
     769           0 :   , mOwningEventTarget(GetCurrentThreadSerialEventTarget())
     770             : {
     771           0 : }
     772             : 
     773           0 : Context::ThreadsafeHandle::~ThreadsafeHandle()
     774             : {
     775             :   // Normally we only touch mStrongRef on the owning thread.  This is safe,
     776             :   // however, because when we do use mStrongRef on the owning thread we are
     777             :   // always holding a strong ref to the ThreadsafeHandle via the owning
     778             :   // runnable.  So we cannot run the ThreadsafeHandle destructor simultaneously.
     779           0 :   if (!mStrongRef || mOwningEventTarget->IsOnCurrentThread()) {
     780           0 :     return;
     781             :   }
     782             : 
     783             :   // Dispatch is guaranteed to succeed here because we block shutdown until
     784             :   // all Contexts have been destroyed.
     785             :   NS_ProxyRelease(
     786           0 :     "Context::ThreadsafeHandle::mStrongRef", mOwningEventTarget, mStrongRef.forget());
     787           0 : }
     788             : 
     789             : void
     790           0 : Context::ThreadsafeHandle::AllowToCloseOnOwningThread()
     791             : {
     792           0 :   MOZ_ASSERT(mOwningEventTarget->IsOnCurrentThread());
     793             : 
     794             :   // A Context "closes" when its ref count drops to zero.  Dropping this
     795             :   // strong ref is necessary, but not sufficient for the close to occur.
     796             :   // Any outstanding IO will continue and keep the Context alive.  Once
     797             :   // the Context is idle, it will be destroyed.
     798             : 
     799             :   // First, tell the context to flush any target thread shared data.  This
     800             :   // data must be released on the target thread prior to running the Context
     801             :   // destructor.  This will schedule an Action which ensures that the
     802             :   // ~Context() is not immediately executed when we drop the strong ref.
     803           0 :   if (mStrongRef) {
     804           0 :     mStrongRef->DoomTargetData();
     805             :   }
     806             : 
     807             :   // Now drop our strong ref and let Context finish running any outstanding
     808             :   // Actions.
     809           0 :   mStrongRef = nullptr;
     810           0 : }
     811             : 
     812             : void
     813           0 : Context::ThreadsafeHandle::InvalidateAndAllowToCloseOnOwningThread()
     814             : {
     815           0 :   MOZ_ASSERT(mOwningEventTarget->IsOnCurrentThread());
     816             :   // Cancel the Context through the weak reference.  This means we can
     817             :   // allow the Context to close by dropping the strong ref, but then
     818             :   // still cancel ongoing IO if necessary.
     819           0 :   if (mWeakRef) {
     820           0 :     mWeakRef->Invalidate();
     821             :   }
     822             :   // We should synchronously have AllowToCloseOnOwningThread called when
     823             :   // the Context is canceled.
     824           0 :   MOZ_DIAGNOSTIC_ASSERT(!mStrongRef);
     825           0 : }
     826             : 
     827             : void
     828           0 : Context::ThreadsafeHandle::ContextDestroyed(Context* aContext)
     829             : {
     830           0 :   MOZ_ASSERT(mOwningEventTarget->IsOnCurrentThread());
     831           0 :   MOZ_DIAGNOSTIC_ASSERT(!mStrongRef);
     832           0 :   MOZ_DIAGNOSTIC_ASSERT(mWeakRef);
     833           0 :   MOZ_DIAGNOSTIC_ASSERT(mWeakRef == aContext);
     834           0 :   mWeakRef = nullptr;
     835           0 : }
     836             : 
     837             : // static
     838             : already_AddRefed<Context>
     839           0 : Context::Create(Manager* aManager, nsISerialEventTarget* aTarget,
     840             :                 Action* aInitAction, Context* aOldContext)
     841             : {
     842           0 :   RefPtr<Context> context = new Context(aManager, aTarget, aInitAction);
     843           0 :   context->Init(aOldContext);
     844           0 :   return context.forget();
     845             : }
     846             : 
     847           0 : Context::Context(Manager* aManager, nsISerialEventTarget* aTarget, Action* aInitAction)
     848             :   : mManager(aManager)
     849             :   , mTarget(aTarget)
     850           0 :   , mData(new Data(aTarget))
     851             :   , mState(STATE_CONTEXT_PREINIT)
     852             :   , mOrphanedData(false)
     853           0 :   , mInitAction(aInitAction)
     854             : {
     855           0 :   MOZ_DIAGNOSTIC_ASSERT(mManager);
     856           0 :   MOZ_DIAGNOSTIC_ASSERT(mTarget);
     857           0 : }
     858             : 
     859             : void
     860           0 : Context::Dispatch(Action* aAction)
     861             : {
     862           0 :   NS_ASSERT_OWNINGTHREAD(Context);
     863           0 :   MOZ_DIAGNOSTIC_ASSERT(aAction);
     864             : 
     865           0 :   MOZ_DIAGNOSTIC_ASSERT(mState != STATE_CONTEXT_CANCELED);
     866           0 :   if (mState == STATE_CONTEXT_CANCELED) {
     867           0 :     return;
     868           0 :   } else if (mState == STATE_CONTEXT_INIT ||
     869           0 :              mState == STATE_CONTEXT_PREINIT) {
     870           0 :     PendingAction* pending = mPendingActions.AppendElement();
     871           0 :     pending->mAction = aAction;
     872           0 :     return;
     873             :   }
     874             : 
     875           0 :   MOZ_DIAGNOSTIC_ASSERT(mState == STATE_CONTEXT_READY);
     876           0 :   DispatchAction(aAction);
     877             : }
     878             : 
     879             : void
     880           0 : Context::CancelAll()
     881             : {
     882           0 :   NS_ASSERT_OWNINGTHREAD(Context);
     883             : 
     884             :   // In PREINIT state we have not dispatch the init action yet.  Just
     885             :   // forget it.
     886           0 :   if (mState == STATE_CONTEXT_PREINIT) {
     887           0 :     MOZ_DIAGNOSTIC_ASSERT(!mInitRunnable);
     888           0 :     mInitAction = nullptr;
     889             : 
     890             :   // In INIT state we have dispatched the runnable, but not received the
     891             :   // async completion yet.  Cancel the runnable, but don't forget about it
     892             :   // until we get OnQuotaInit() callback.
     893           0 :   } else if (mState == STATE_CONTEXT_INIT) {
     894           0 :     mInitRunnable->Cancel();
     895             :   }
     896             : 
     897           0 :   mState = STATE_CONTEXT_CANCELED;
     898           0 :   mPendingActions.Clear();
     899             :   {
     900           0 :     ActivityList::ForwardIterator iter(mActivityList);
     901           0 :     while (iter.HasMore()) {
     902           0 :       iter.GetNext()->Cancel();
     903             :     }
     904             :   }
     905           0 :   AllowToClose();
     906           0 : }
     907             : 
     908             : bool
     909           0 : Context::IsCanceled() const
     910             : {
     911           0 :   NS_ASSERT_OWNINGTHREAD(Context);
     912           0 :   return mState == STATE_CONTEXT_CANCELED;
     913             : }
     914             : 
     915             : void
     916           0 : Context::Invalidate()
     917             : {
     918           0 :   NS_ASSERT_OWNINGTHREAD(Context);
     919           0 :   mManager->NoteClosing();
     920           0 :   CancelAll();
     921           0 : }
     922             : 
     923             : void
     924           0 : Context::AllowToClose()
     925             : {
     926           0 :   NS_ASSERT_OWNINGTHREAD(Context);
     927           0 :   if (mThreadsafeHandle) {
     928           0 :     mThreadsafeHandle->AllowToClose();
     929             :   }
     930           0 : }
     931             : 
     932             : void
     933           0 : Context::CancelForCacheId(CacheId aCacheId)
     934             : {
     935           0 :   NS_ASSERT_OWNINGTHREAD(Context);
     936             : 
     937             :   // Remove matching pending actions
     938           0 :   for (int32_t i = mPendingActions.Length() - 1; i >= 0; --i) {
     939           0 :     if (mPendingActions[i].mAction->MatchesCacheId(aCacheId)) {
     940           0 :       mPendingActions.RemoveElementAt(i);
     941             :     }
     942             :   }
     943             : 
     944             :   // Cancel activities and let them remove themselves
     945           0 :   ActivityList::ForwardIterator iter(mActivityList);
     946           0 :   while (iter.HasMore()) {
     947           0 :     Activity* activity = iter.GetNext();
     948           0 :     if (activity->MatchesCacheId(aCacheId)) {
     949           0 :       activity->Cancel();
     950             :     }
     951             :   }
     952           0 : }
     953             : 
     954           0 : Context::~Context()
     955             : {
     956           0 :   NS_ASSERT_OWNINGTHREAD(Context);
     957           0 :   MOZ_DIAGNOSTIC_ASSERT(mManager);
     958           0 :   MOZ_DIAGNOSTIC_ASSERT(!mData);
     959             : 
     960           0 :   if (mThreadsafeHandle) {
     961           0 :     mThreadsafeHandle->ContextDestroyed(this);
     962             :   }
     963             : 
     964             :   // Note, this may set the mOrphanedData flag.
     965           0 :   mManager->RemoveContext(this);
     966             : 
     967           0 :   if (mQuotaInfo.mDir && !mOrphanedData) {
     968           0 :     MOZ_ALWAYS_SUCCEEDS(DeleteMarkerFile(mQuotaInfo));
     969             :   }
     970             : 
     971           0 :   if (mNextContext) {
     972           0 :     mNextContext->Start();
     973             :   }
     974           0 : }
     975             : 
     976             : void
     977           0 : Context::Init(Context* aOldContext)
     978             : {
     979           0 :   NS_ASSERT_OWNINGTHREAD(Context);
     980             : 
     981           0 :   if (aOldContext) {
     982           0 :     aOldContext->SetNextContext(this);
     983           0 :     return;
     984             :   }
     985             : 
     986           0 :   Start();
     987             : }
     988             : 
     989             : void
     990           0 : Context::Start()
     991             : {
     992           0 :   NS_ASSERT_OWNINGTHREAD(Context);
     993             : 
     994             :   // Previous context closing delayed our start, but then we were canceled.
     995             :   // In this case, just do nothing here.
     996           0 :   if (mState == STATE_CONTEXT_CANCELED) {
     997           0 :     MOZ_DIAGNOSTIC_ASSERT(!mInitRunnable);
     998           0 :     MOZ_DIAGNOSTIC_ASSERT(!mInitAction);
     999             :     // If we can't initialize the quota subsystem we will never be able to
    1000             :     // clear our shared data object via the target IO thread.  Instead just
    1001             :     // clear it here to maintain the invariant that the shared data is
    1002             :     // cleared before Context destruction.
    1003           0 :     mData = nullptr;
    1004           0 :     return;
    1005             :   }
    1006             : 
    1007           0 :   MOZ_DIAGNOSTIC_ASSERT(mState == STATE_CONTEXT_PREINIT);
    1008           0 :   MOZ_DIAGNOSTIC_ASSERT(!mInitRunnable);
    1009             : 
    1010             :   mInitRunnable = new QuotaInitRunnable(this, mManager, mData, mTarget,
    1011           0 :                                         mInitAction);
    1012           0 :   mInitAction = nullptr;
    1013             : 
    1014           0 :   mState = STATE_CONTEXT_INIT;
    1015             : 
    1016           0 :   nsresult rv = mInitRunnable->Dispatch();
    1017           0 :   if (NS_FAILED(rv)) {
    1018             :     // Shutdown must be delayed until all Contexts are destroyed.  Shutdown
    1019             :     // must also prevent any new Contexts from being constructed.  Crash
    1020             :     // for this invariant violation.
    1021           0 :     MOZ_CRASH("Failed to dispatch QuotaInitRunnable.");
    1022             :   }
    1023             : }
    1024             : 
    1025             : void
    1026           0 : Context::DispatchAction(Action* aAction, bool aDoomData)
    1027             : {
    1028           0 :   NS_ASSERT_OWNINGTHREAD(Context);
    1029             : 
    1030             :   RefPtr<ActionRunnable> runnable =
    1031           0 :     new ActionRunnable(this, mData, mTarget, aAction, mQuotaInfo);
    1032             : 
    1033           0 :   if (aDoomData) {
    1034           0 :     mData = nullptr;
    1035             :   }
    1036             : 
    1037           0 :   nsresult rv = runnable->Dispatch();
    1038           0 :   if (NS_FAILED(rv)) {
    1039             :     // Shutdown must be delayed until all Contexts are destroyed.  Crash
    1040             :     // for this invariant violation.
    1041           0 :     MOZ_CRASH("Failed to dispatch ActionRunnable to target thread.");
    1042             :   }
    1043           0 :   AddActivity(runnable);
    1044           0 : }
    1045             : 
    1046             : void
    1047           0 : Context::OnQuotaInit(nsresult aRv, const QuotaInfo& aQuotaInfo,
    1048             :                      already_AddRefed<DirectoryLock> aDirectoryLock)
    1049             : {
    1050           0 :   NS_ASSERT_OWNINGTHREAD(Context);
    1051             : 
    1052           0 :   MOZ_DIAGNOSTIC_ASSERT(mInitRunnable);
    1053           0 :   mInitRunnable = nullptr;
    1054             : 
    1055           0 :   mQuotaInfo = aQuotaInfo;
    1056             : 
    1057             :   // Always save the directory lock to ensure QuotaManager does not shutdown
    1058             :   // before the Context has gone away.
    1059           0 :   MOZ_DIAGNOSTIC_ASSERT(!mDirectoryLock);
    1060           0 :   mDirectoryLock = aDirectoryLock;
    1061             : 
    1062             :   // If we opening the context failed, but we were not explicitly canceled,
    1063             :   // still treat the entire context as canceled.  We don't want to allow
    1064             :   // new actions to be dispatched.  We also cannot leave the context in
    1065             :   // the INIT state after failing to open.
    1066           0 :   if (NS_FAILED(aRv)) {
    1067           0 :     mState = STATE_CONTEXT_CANCELED;
    1068             :   }
    1069             : 
    1070           0 :   if (mState == STATE_CONTEXT_CANCELED) {
    1071           0 :     for (uint32_t i = 0; i < mPendingActions.Length(); ++i) {
    1072           0 :       mPendingActions[i].mAction->CompleteOnInitiatingThread(aRv);
    1073             :     }
    1074           0 :     mPendingActions.Clear();
    1075           0 :     mThreadsafeHandle->AllowToClose();
    1076             :     // Context will destruct after return here and last ref is released.
    1077           0 :     return;
    1078             :   }
    1079             : 
    1080           0 :   MOZ_DIAGNOSTIC_ASSERT(mState == STATE_CONTEXT_INIT);
    1081           0 :   mState = STATE_CONTEXT_READY;
    1082             : 
    1083           0 :   for (uint32_t i = 0; i < mPendingActions.Length(); ++i) {
    1084           0 :     DispatchAction(mPendingActions[i].mAction);
    1085             :   }
    1086           0 :   mPendingActions.Clear();
    1087             : }
    1088             : 
    1089             : void
    1090           0 : Context::AddActivity(Activity* aActivity)
    1091             : {
    1092           0 :   NS_ASSERT_OWNINGTHREAD(Context);
    1093           0 :   MOZ_DIAGNOSTIC_ASSERT(aActivity);
    1094           0 :   MOZ_ASSERT(!mActivityList.Contains(aActivity));
    1095           0 :   mActivityList.AppendElement(aActivity);
    1096           0 : }
    1097             : 
    1098             : void
    1099           0 : Context::RemoveActivity(Activity* aActivity)
    1100             : {
    1101           0 :   NS_ASSERT_OWNINGTHREAD(Context);
    1102           0 :   MOZ_DIAGNOSTIC_ASSERT(aActivity);
    1103           0 :   MOZ_ALWAYS_TRUE(mActivityList.RemoveElement(aActivity));
    1104           0 :   MOZ_ASSERT(!mActivityList.Contains(aActivity));
    1105           0 : }
    1106             : 
    1107             : void
    1108           0 : Context::NoteOrphanedData()
    1109             : {
    1110           0 :   NS_ASSERT_OWNINGTHREAD(Context);
    1111             :   // This may be called more than once
    1112           0 :   mOrphanedData = true;
    1113           0 : }
    1114             : 
    1115             : already_AddRefed<Context::ThreadsafeHandle>
    1116           0 : Context::CreateThreadsafeHandle()
    1117             : {
    1118           0 :   NS_ASSERT_OWNINGTHREAD(Context);
    1119           0 :   if (!mThreadsafeHandle) {
    1120           0 :     mThreadsafeHandle = new ThreadsafeHandle(this);
    1121             :   }
    1122           0 :   RefPtr<ThreadsafeHandle> ref = mThreadsafeHandle;
    1123           0 :   return ref.forget();
    1124             : }
    1125             : 
    1126             : void
    1127           0 : Context::SetNextContext(Context* aNextContext)
    1128             : {
    1129           0 :   NS_ASSERT_OWNINGTHREAD(Context);
    1130           0 :   MOZ_DIAGNOSTIC_ASSERT(aNextContext);
    1131           0 :   MOZ_DIAGNOSTIC_ASSERT(!mNextContext);
    1132           0 :   mNextContext = aNextContext;
    1133           0 : }
    1134             : 
    1135             : void
    1136           0 : Context::DoomTargetData()
    1137             : {
    1138           0 :   NS_ASSERT_OWNINGTHREAD(Context);
    1139           0 :   MOZ_DIAGNOSTIC_ASSERT(mData);
    1140             : 
    1141             :   // We are about to drop our reference to the Data.  We need to ensure that
    1142             :   // the ~Context() destructor does not run until contents of Data have been
    1143             :   // released on the Target thread.
    1144             : 
    1145             :   // Dispatch a no-op Action.  This will hold the Context alive through a
    1146             :   // roundtrip to the target thread and back to the owning thread.  The
    1147             :   // ref to the Data object is cleared on the owning thread after creating
    1148             :   // the ActionRunnable, but before dispatching it.
    1149           0 :   RefPtr<Action> action = new NullAction();
    1150           0 :   DispatchAction(action, true /* doomed data */);
    1151             : 
    1152           0 :   MOZ_DIAGNOSTIC_ASSERT(!mData);
    1153           0 : }
    1154             : 
    1155             : } // namespace cache
    1156             : } // namespace dom
    1157             : } // namespace mozilla

Generated by: LCOV version 1.13