LCOV - code coverage report
Current view: top level - xpcom/base - CycleCollectedJSContext.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 91 238 38.2 %
Date: 2018-08-07 16:42:27 Functions: 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/CycleCollectedJSContext.h"
       8             : #include <algorithm>
       9             : #include "mozilla/ArrayUtils.h"
      10             : #include "mozilla/AutoRestore.h"
      11             : #include "mozilla/CycleCollectedJSRuntime.h"
      12             : #include "mozilla/Move.h"
      13             : #include "mozilla/MemoryReporting.h"
      14             : #include "mozilla/Sprintf.h"
      15             : #include "mozilla/Telemetry.h"
      16             : #include "mozilla/TimelineConsumers.h"
      17             : #include "mozilla/TimelineMarker.h"
      18             : #include "mozilla/Unused.h"
      19             : #include "mozilla/DebuggerOnGCRunnable.h"
      20             : #include "mozilla/dom/DOMJSClass.h"
      21             : #include "mozilla/dom/DOMException.h"
      22             : #include "mozilla/dom/ProfileTimelineMarkerBinding.h"
      23             : #include "mozilla/dom/Promise.h"
      24             : #include "mozilla/dom/PromiseBinding.h"
      25             : #include "mozilla/dom/PromiseDebugging.h"
      26             : #include "mozilla/dom/ScriptSettings.h"
      27             : #include "js/Debug.h"
      28             : #include "js/GCAPI.h"
      29             : #include "js/Utility.h"
      30             : #include "nsContentUtils.h"
      31             : #include "nsCycleCollectionNoteRootCallback.h"
      32             : #include "nsCycleCollectionParticipant.h"
      33             : #include "nsCycleCollector.h"
      34             : #include "nsDOMJSUtils.h"
      35             : #include "nsDOMMutationObserver.h"
      36             : #include "nsJSUtils.h"
      37             : #include "nsWrapperCache.h"
      38             : #include "nsStringBuffer.h"
      39             : 
      40             : #include "nsIPlatformInfo.h"
      41             : #include "nsThread.h"
      42             : #include "nsThreadUtils.h"
      43             : #include "xpcpublic.h"
      44             : 
      45             : using namespace mozilla;
      46             : using namespace mozilla::dom;
      47             : 
      48             : namespace mozilla {
      49             : 
      50           0 : CycleCollectedJSContext::CycleCollectedJSContext()
      51             :   : mIsPrimaryContext(true)
      52             :   , mRuntime(nullptr)
      53             :   , mJSContext(nullptr)
      54             :   , mDoingStableStates(false)
      55             :   , mTargetedMicroTaskRecursionDepth(0)
      56             :   , mMicroTaskLevel(0)
      57           0 :   , mMicroTaskRecursionDepth(0)
      58             : {
      59           0 :   MOZ_COUNT_CTOR(CycleCollectedJSContext);
      60             : 
      61             :   // Reinitialize PerThreadAtomCache because dom/bindings/Codegen.py compares
      62             :   // against zero rather than JSID_VOID to detect uninitialized jsid members.
      63           0 :   memset(static_cast<PerThreadAtomCache*>(this), 0, sizeof(PerThreadAtomCache));
      64             : 
      65           0 :   nsCOMPtr<nsIThread> thread = do_GetCurrentThread();
      66          12 :   mOwningThread = thread.forget().downcast<nsThread>().take();
      67           0 :   MOZ_RELEASE_ASSERT(mOwningThread);
      68           4 : }
      69             : 
      70           0 : CycleCollectedJSContext::~CycleCollectedJSContext()
      71             : {
      72           0 :   MOZ_COUNT_DTOR(CycleCollectedJSContext);
      73             :   // If the allocation failed, here we are.
      74           0 :   if (!mJSContext) {
      75             :     return;
      76             :   }
      77             : 
      78           0 :   JS_SetContextPrivate(mJSContext, nullptr);
      79             : 
      80           0 :   mRuntime->RemoveContext(this);
      81             : 
      82           0 :   if (mIsPrimaryContext) {
      83           0 :     mRuntime->Shutdown(mJSContext);
      84             :   }
      85             : 
      86             :   // Last chance to process any events.
      87           0 :   CleanupIDBTransactions(mBaseRecursionDepth);
      88           0 :   MOZ_ASSERT(mPendingIDBTransactions.IsEmpty());
      89             : 
      90           0 :   ProcessStableStateQueue();
      91           0 :   MOZ_ASSERT(mStableStateEvents.IsEmpty());
      92             : 
      93             :   // Clear mPendingException first, since it might be cycle collected.
      94           0 :   mPendingException = nullptr;
      95             : 
      96           0 :   MOZ_ASSERT(mDebuggerMicroTaskQueue.empty());
      97           0 :   MOZ_ASSERT(mPendingMicroTaskRunnables.empty());
      98             : 
      99           0 :   mUncaughtRejections.reset();
     100           0 :   mConsumedRejections.reset();
     101             : 
     102           0 :   JS_DestroyContext(mJSContext);
     103           0 :   mJSContext = nullptr;
     104             : 
     105           0 :   if (mIsPrimaryContext) {
     106           0 :     nsCycleCollector_forgetJSContext();
     107             :   } else {
     108           0 :     nsCycleCollector_forgetNonPrimaryContext();
     109             :   }
     110             : 
     111           0 :   mozilla::dom::DestroyScriptSettings();
     112             : 
     113           0 :   mOwningThread->SetScriptObserver(nullptr);
     114           0 :   NS_RELEASE(mOwningThread);
     115             : 
     116           0 :   if (mIsPrimaryContext) {
     117           0 :     delete mRuntime;
     118             :   }
     119           0 :   mRuntime = nullptr;
     120           0 : }
     121             : 
     122             : void
     123           4 : CycleCollectedJSContext::InitializeCommon()
     124             : {
     125           8 :   mRuntime->AddContext(this);
     126             : 
     127           4 :   mOwningThread->SetScriptObserver(this);
     128             :   // The main thread has a base recursion depth of 0, workers of 1.
     129           0 :   mBaseRecursionDepth = RecursionDepth();
     130             : 
     131           0 :   NS_GetCurrentThread()->SetCanInvokeJS(true);
     132             : 
     133           4 :   JS::SetGetIncumbentGlobalCallback(mJSContext, GetIncumbentGlobalCallback);
     134             : 
     135           0 :   JS::SetEnqueuePromiseJobCallback(mJSContext, EnqueuePromiseJobCallback, this);
     136           4 :   JS::SetPromiseRejectionTrackerCallback(mJSContext, PromiseRejectionTrackerCallback, this);
     137           8 :   mUncaughtRejections.init(mJSContext, JS::GCVector<JSObject*, 0, js::SystemAllocPolicy>(js::SystemAllocPolicy()));
     138           8 :   mConsumedRejections.init(mJSContext, JS::GCVector<JSObject*, 0, js::SystemAllocPolicy>(js::SystemAllocPolicy()));
     139             : 
     140             :   // Cast to PerThreadAtomCache for dom::GetAtomCache(JSContext*).
     141           0 :   JS_SetContextPrivate(mJSContext, static_cast<PerThreadAtomCache*>(this));
     142           0 : }
     143             : 
     144             : nsresult
     145           4 : CycleCollectedJSContext::Initialize(JSRuntime* aParentRuntime,
     146             :                                     uint32_t aMaxBytes,
     147             :                                     uint32_t aMaxNurseryBytes)
     148             : {
     149           0 :   MOZ_ASSERT(!mJSContext);
     150             : 
     151           0 :   mozilla::dom::InitScriptSettings();
     152           4 :   mJSContext = JS_NewContext(aMaxBytes, aMaxNurseryBytes, aParentRuntime);
     153           0 :   if (!mJSContext) {
     154             :     return NS_ERROR_OUT_OF_MEMORY;
     155             :   }
     156             : 
     157           1 :   mRuntime = CreateRuntime(mJSContext);
     158             : 
     159           1 :   InitializeCommon();
     160             : 
     161           1 :   nsCycleCollector_registerJSContext(this);
     162             : 
     163           1 :   return NS_OK;
     164             : }
     165             : 
     166             : nsresult
     167           0 : CycleCollectedJSContext::InitializeNonPrimary(CycleCollectedJSContext* aPrimaryContext)
     168             : {
     169           0 :   MOZ_ASSERT(!mJSContext);
     170             : 
     171           0 :   mIsPrimaryContext = false;
     172             : 
     173           0 :   mozilla::dom::InitScriptSettings();
     174           0 :   mJSContext = JS_NewCooperativeContext(aPrimaryContext->mJSContext);
     175           0 :   if (!mJSContext) {
     176             :     return NS_ERROR_OUT_OF_MEMORY;
     177             :   }
     178             : 
     179           0 :   mRuntime = aPrimaryContext->mRuntime;
     180             : 
     181           0 :   InitializeCommon();
     182             : 
     183           0 :   nsCycleCollector_registerNonPrimaryContext(this);
     184             : 
     185           0 :   return NS_OK;
     186             : }
     187             : 
     188             : /* static */ CycleCollectedJSContext*
     189         203 : CycleCollectedJSContext::GetFor(JSContext* aCx)
     190             : {
     191             :   // Cast from void* matching JS_SetContextPrivate.
     192         203 :   auto atomCache = static_cast<PerThreadAtomCache*>(JS_GetContextPrivate(aCx));
     193             :   // Down cast.
     194         203 :   return static_cast<CycleCollectedJSContext*>(atomCache);
     195             : }
     196             : 
     197             : size_t
     198           0 : CycleCollectedJSContext::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
     199             : {
     200           0 :   return 0;
     201             : }
     202             : 
     203             : class PromiseJobRunnable final : public MicroTaskRunnable
     204             : {
     205             : public:
     206           0 :   PromiseJobRunnable(JS::HandleObject aCallback,
     207             :                      JS::HandleObject aAllocationSite,
     208             :                      nsIGlobalObject* aIncumbentGlobal)
     209        4170 :     :mCallback(
     210           0 :        new PromiseJobCallback(aCallback, aAllocationSite, aIncumbentGlobal))
     211             :   {
     212        4170 :   }
     213             : 
     214           0 :   virtual ~PromiseJobRunnable()
     215        4170 :   {
     216       12510 :   }
     217             : 
     218             : protected:
     219        4170 :   virtual void Run(AutoSlowOperation& aAso) override
     220             :   {
     221       12510 :     JSObject* callback = mCallback->CallbackPreserveColor();
     222        4170 :     nsIGlobalObject* global = callback ? xpc::NativeGlobal(callback) : nullptr;
     223           0 :     if (global && !global->IsDying()) {
     224        4170 :       mCallback->Call("promise callback");
     225           0 :       aAso.CheckForInterrupt();
     226             :     }
     227           0 :   }
     228             : 
     229        4170 :   virtual bool Suppressed() override
     230             :   {
     231             :     nsIGlobalObject* global =
     232       12510 :       xpc::NativeGlobal(mCallback->CallbackPreserveColor());
     233        4170 :     return global && global->IsInSyncOperation();
     234             :   }
     235             : 
     236             : private:
     237             :   RefPtr<PromiseJobCallback> mCallback;
     238             : };
     239             : 
     240             : /* static */
     241             : JSObject*
     242           0 : CycleCollectedJSContext::GetIncumbentGlobalCallback(JSContext* aCx)
     243             : {
     244           0 :   nsIGlobalObject* global = mozilla::dom::GetIncumbentGlobal();
     245           0 :   if (global) {
     246           0 :     return global->GetGlobalJSObject();
     247             :   }
     248             :   return nullptr;
     249             : }
     250             : 
     251             : /* static */
     252             : bool
     253        4170 : CycleCollectedJSContext::EnqueuePromiseJobCallback(JSContext* aCx,
     254             :                                                    JS::HandleObject aJob,
     255             :                                                    JS::HandleObject aAllocationSite,
     256             :                                                    JS::HandleObject aIncumbentGlobal,
     257             :                                                    void* aData)
     258             : {
     259        4170 :   CycleCollectedJSContext* self = static_cast<CycleCollectedJSContext*>(aData);
     260        4170 :   MOZ_ASSERT(aCx == self->Context());
     261           0 :   MOZ_ASSERT(Get() == self);
     262             : 
     263           0 :   nsIGlobalObject* global = nullptr;
     264           0 :   if (aIncumbentGlobal) {
     265        4170 :     global = xpc::NativeGlobal(aIncumbentGlobal);
     266             :   }
     267           0 :   RefPtr<MicroTaskRunnable> runnable = new PromiseJobRunnable(aJob, aAllocationSite, global);
     268        8340 :   self->DispatchToMicroTask(runnable.forget());
     269           0 :   return true;
     270             : }
     271             : 
     272             : /* static */
     273             : void
     274           0 : CycleCollectedJSContext::PromiseRejectionTrackerCallback(JSContext* aCx,
     275             :                                                          JS::HandleObject aPromise,
     276             :                                                          JS::PromiseRejectionHandlingState state,
     277             :                                                          void* aData)
     278             : {
     279             : #ifdef DEBUG
     280           3 :   CycleCollectedJSContext* self = static_cast<CycleCollectedJSContext*>(aData);
     281             : #endif // DEBUG
     282           3 :   MOZ_ASSERT(aCx == self->Context());
     283           0 :   MOZ_ASSERT(Get() == self);
     284             : 
     285           0 :   if (state == JS::PromiseRejectionHandlingState::Unhandled) {
     286           0 :     PromiseDebugging::AddUncaughtRejection(aPromise);
     287             :   } else {
     288           1 :     PromiseDebugging::AddConsumedRejection(aPromise);
     289             :   }
     290           0 : }
     291             : 
     292             : already_AddRefed<Exception>
     293           0 : CycleCollectedJSContext::GetPendingException() const
     294             : {
     295          81 :   MOZ_ASSERT(mJSContext);
     296             : 
     297           0 :   nsCOMPtr<Exception> out = mPendingException;
     298         162 :   return out.forget();
     299             : }
     300             : 
     301             : void
     302        8486 : CycleCollectedJSContext::SetPendingException(Exception* aException)
     303             : {
     304           0 :   MOZ_ASSERT(mJSContext);
     305        8486 :   mPendingException = aException;
     306           0 : }
     307             : 
     308             : std::queue<RefPtr<MicroTaskRunnable>>&
     309           0 : CycleCollectedJSContext::GetMicroTaskQueue()
     310             : {
     311           0 :   MOZ_ASSERT(mJSContext);
     312           0 :   return mPendingMicroTaskRunnables;
     313             : }
     314             : 
     315             : std::queue<RefPtr<MicroTaskRunnable>>&
     316           0 : CycleCollectedJSContext::GetDebuggerMicroTaskQueue()
     317             : {
     318           0 :   MOZ_ASSERT(mJSContext);
     319           0 :   return mDebuggerMicroTaskQueue;
     320             : }
     321             : 
     322             : void
     323           0 : CycleCollectedJSContext::ProcessStableStateQueue()
     324             : {
     325        1938 :   MOZ_ASSERT(mJSContext);
     326           0 :   MOZ_RELEASE_ASSERT(!mDoingStableStates);
     327        1938 :   mDoingStableStates = true;
     328             : 
     329        3876 :   for (uint32_t i = 0; i < mStableStateEvents.Length(); ++i) {
     330           0 :     nsCOMPtr<nsIRunnable> event = mStableStateEvents[i].forget();
     331           0 :     event->Run();
     332             :   }
     333             : 
     334        1938 :   mStableStateEvents.Clear();
     335        1939 :   mDoingStableStates = false;
     336           0 : }
     337             : 
     338             : void
     339        3179 : CycleCollectedJSContext::CleanupIDBTransactions(uint32_t aRecursionDepth)
     340             : {
     341        3179 :   MOZ_ASSERT(mJSContext);
     342        3179 :   MOZ_RELEASE_ASSERT(!mDoingStableStates);
     343        3179 :   mDoingStableStates = true;
     344             : 
     345           0 :   nsTArray<PendingIDBTransactionData> localQueue = std::move(mPendingIDBTransactions);
     346             : 
     347           0 :   for (uint32_t i = 0; i < localQueue.Length(); ++i)
     348             :   {
     349           0 :     PendingIDBTransactionData& data = localQueue[i];
     350           0 :     if (data.mRecursionDepth != aRecursionDepth) {
     351             :       continue;
     352             :     }
     353             : 
     354             :     {
     355           0 :       nsCOMPtr<nsIRunnable> transaction = data.mTransaction.forget();
     356           0 :       transaction->Run();
     357             :     }
     358             : 
     359           0 :     localQueue.RemoveElementAt(i--);
     360             :   }
     361             : 
     362             :   // If the queue has events in it now, they were added from something we called,
     363             :   // so they belong at the end of the queue.
     364           0 :   localQueue.AppendElements(mPendingIDBTransactions);
     365        3179 :   localQueue.SwapElements(mPendingIDBTransactions);
     366        3179 :   mDoingStableStates = false;
     367           0 : }
     368             : 
     369             : void
     370        1943 : CycleCollectedJSContext::BeforeProcessTask(bool aMightBlock)
     371             : {
     372             :   // If ProcessNextEvent was called during a microtask callback, we
     373             :   // must process any pending microtasks before blocking in the event loop,
     374             :   // otherwise we may deadlock until an event enters the queue later.
     375        1943 :   if (aMightBlock && PerformMicroTaskCheckPoint()) {
     376             :     // If any microtask was processed, we post a dummy event in order to
     377             :     // force the ProcessNextEvent call not to block.  This is required
     378             :     // to support nested event loops implemented using a pattern like
     379             :     // "while (condition) thread.processNextEvent(true)", in case the
     380             :     // condition is triggered here by a Promise "then" callback.
     381           0 :     NS_DispatchToMainThread(new Runnable("BeforeProcessTask"));
     382             :   }
     383        1943 : }
     384             : 
     385             : void
     386           0 : CycleCollectedJSContext::AfterProcessTask(uint32_t aRecursionDepth)
     387             : {
     388        1939 :   MOZ_ASSERT(mJSContext);
     389             : 
     390             :   // See HTML 6.1.4.2 Processing model
     391             : 
     392             :   // Step 4.1: Execute microtasks.
     393        1939 :   PerformMicroTaskCheckPoint();
     394             : 
     395             :   // Step 4.2 Execute any events that were waiting for a stable state.
     396        1939 :   ProcessStableStateQueue();
     397             : 
     398             :   // This should be a fast test so that it won't affect the next task processing.
     399        1939 :   IsIdleGCTaskNeeded();
     400           0 : }
     401             : 
     402             : void
     403           0 : CycleCollectedJSContext::AfterProcessMicrotasks()
     404             : {
     405        3179 :   MOZ_ASSERT(mJSContext);
     406             :   // Cleanup Indexed Database transactions:
     407             :   // https://html.spec.whatwg.org/multipage/webappapis.html#perform-a-microtask-checkpoint
     408        3179 :   CleanupIDBTransactions(RecursionDepth());
     409           1 : }
     410             : 
     411           1 : void CycleCollectedJSContext::IsIdleGCTaskNeeded()
     412             : {
     413           4 :   class IdleTimeGCTaskRunnable : public mozilla::IdleRunnable
     414             :   {
     415             :   public:
     416             :     using mozilla::IdleRunnable::IdleRunnable;
     417             : 
     418             :   public:
     419           1 :     NS_IMETHOD Run() override
     420             :     {
     421           1 :       CycleCollectedJSRuntime* ccrt = CycleCollectedJSRuntime::Get();
     422           1 :       if (ccrt) {
     423           0 :         ccrt->RunIdleTimeGCTask();
     424             :       }
     425           0 :       return NS_OK;
     426             :     }
     427             : 
     428           0 :     nsresult Cancel() override
     429             :     {
     430           0 :       return NS_OK;
     431             :     }
     432             :   };
     433             : 
     434        1939 :   if (Runtime()->IsIdleGCTaskNeeded()) {
     435           2 :     nsCOMPtr<nsIRunnable> gc_task = new IdleTimeGCTaskRunnable();
     436           0 :     NS_IdleDispatchToCurrentThread(gc_task.forget());
     437           1 :     Runtime()->SetPendingIdleGCTask();
     438             :   }
     439        1939 : }
     440             : 
     441             : uint32_t
     442        7992 : CycleCollectedJSContext::RecursionDepth()
     443             : {
     444           0 :   return mOwningThread->RecursionDepth();
     445             : }
     446             : 
     447             : void
     448           0 : CycleCollectedJSContext::RunInStableState(already_AddRefed<nsIRunnable>&& aRunnable)
     449             : {
     450           0 :   MOZ_ASSERT(mJSContext);
     451           0 :   mStableStateEvents.AppendElement(std::move(aRunnable));
     452           0 : }
     453             : 
     454             : void
     455           0 : CycleCollectedJSContext::AddPendingIDBTransaction(already_AddRefed<nsIRunnable>&& aTransaction)
     456             : {
     457           0 :   MOZ_ASSERT(mJSContext);
     458             : 
     459           0 :   PendingIDBTransactionData data;
     460           0 :   data.mTransaction = aTransaction;
     461             : 
     462           0 :   MOZ_ASSERT(mOwningThread);
     463           0 :   data.mRecursionDepth = RecursionDepth();
     464             : 
     465             :   // There must be an event running to get here.
     466             : #ifndef MOZ_WIDGET_COCOA
     467           0 :   MOZ_ASSERT(data.mRecursionDepth > mBaseRecursionDepth);
     468             : #else
     469             :   // XXX bug 1261143
     470             :   // Recursion depth should be greater than mBaseRecursionDepth,
     471             :   // or the runnable will stay in the queue forever.
     472             :   if (data.mRecursionDepth <= mBaseRecursionDepth) {
     473             :     data.mRecursionDepth = mBaseRecursionDepth + 1;
     474             :   }
     475             : #endif
     476             : 
     477           0 :   mPendingIDBTransactions.AppendElement(std::move(data));
     478           0 : }
     479             : 
     480             : void
     481           0 : CycleCollectedJSContext::DispatchToMicroTask(
     482             :     already_AddRefed<MicroTaskRunnable> aRunnable)
     483             : {
     484           0 :   RefPtr<MicroTaskRunnable> runnable(aRunnable);
     485             : 
     486        4177 :   MOZ_ASSERT(NS_IsMainThread());
     487        4177 :   MOZ_ASSERT(runnable);
     488             : 
     489           0 :   mPendingMicroTaskRunnables.push(runnable.forget());
     490        4177 : }
     491             : 
     492           0 : class AsyncMutationHandler final : public mozilla::Runnable
     493             : {
     494             : public:
     495           0 :   AsyncMutationHandler() : mozilla::Runnable("AsyncMutationHandler") {}
     496             : 
     497           0 :   NS_IMETHOD Run() override
     498             :   {
     499           0 :     CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get();
     500           0 :     if (ccjs) {
     501           0 :       ccjs->PerformMicroTaskCheckPoint();
     502             :     }
     503           0 :     return NS_OK;
     504             :   }
     505             : };
     506             : 
     507             : bool
     508           0 : CycleCollectedJSContext::PerformMicroTaskCheckPoint()
     509             : {
     510       17048 :   if (mPendingMicroTaskRunnables.empty() && mDebuggerMicroTaskQueue.empty()) {
     511           0 :     AfterProcessMicrotasks();
     512             :     // Nothing to do, return early.
     513        2898 :     return false;
     514             :   }
     515             : 
     516           0 :   uint32_t currentDepth = RecursionDepth();
     517           0 :   if (mMicroTaskRecursionDepth >= currentDepth) {
     518             :     // We are already executing microtasks for the current recursion depth.
     519             :     return false;
     520             :   }
     521             : 
     522           0 :   if (mTargetedMicroTaskRecursionDepth != 0 &&
     523             :       mTargetedMicroTaskRecursionDepth != currentDepth) {
     524             :     return false;
     525             :   }
     526             : 
     527           0 :   if (NS_IsMainThread() && !nsContentUtils::IsSafeToRunScript()) {
     528             :     // Special case for main thread where DOM mutations may happen when
     529             :     // it is not safe to run scripts.
     530           0 :     nsContentUtils::AddScriptRunner(new AsyncMutationHandler());
     531           0 :     return false;
     532             :   }
     533             : 
     534         843 :   mozilla::AutoRestore<uint32_t> restore(mMicroTaskRecursionDepth);
     535           0 :   MOZ_ASSERT(currentDepth > 0);
     536         281 :   mMicroTaskRecursionDepth = currentDepth;
     537             : 
     538         281 :   bool didProcess = false;
     539           0 :   AutoSlowOperation aso;
     540             : 
     541         843 :   std::queue<RefPtr<MicroTaskRunnable>> suppressed;
     542             :   for (;;) {
     543           0 :     RefPtr<MicroTaskRunnable> runnable;
     544        8916 :     if (!mDebuggerMicroTaskQueue.empty()) {
     545           0 :       runnable = mDebuggerMicroTaskQueue.front().forget();
     546           0 :       mDebuggerMicroTaskQueue.pop();
     547        8916 :     } else if (!mPendingMicroTaskRunnables.empty()) {
     548       12531 :       runnable = mPendingMicroTaskRunnables.front().forget();
     549        4177 :       mPendingMicroTaskRunnables.pop();
     550             :     } else {
     551             :       break;
     552             :     }
     553             : 
     554        4177 :     if (runnable->Suppressed()) {
     555             :       // Microtasks in worker shall never be suppressed.
     556             :       // Otherwise, mPendingMicroTaskRunnables will be replaced later with
     557             :       // all suppressed tasks in mDebuggerMicroTaskQueue unexpectedly.
     558           0 :       MOZ_ASSERT(NS_IsMainThread());
     559             :       suppressed.push(runnable);
     560             :     } else {
     561        4177 :       didProcess = true;
     562        4177 :       runnable->Run(aso);
     563             :     }
     564             :   }
     565             : 
     566             :   // Put back the suppressed microtasks so that they will be run later.
     567             :   // Note, it is possible that we end up keeping these suppressed tasks around
     568             :   // for some time, but no longer than spinning the event loop nestedly
     569             :   // (sync XHR, alert, etc.)
     570         562 :   mPendingMicroTaskRunnables.swap(suppressed);
     571             : 
     572         281 :   AfterProcessMicrotasks();
     573             : 
     574         281 :   return didProcess;
     575             : }
     576             : 
     577             : void
     578           0 : CycleCollectedJSContext::PerformDebuggerMicroTaskCheckpoint()
     579             :  {
     580             :   // Don't do normal microtask handling checks here, since whoever is calling
     581             :   // this method is supposed to know what they are doing.
     582             : 
     583           0 :   AutoSlowOperation aso;
     584             :   for (;;) {
     585             :     // For a debugger microtask checkpoint, we always use the debugger microtask
     586             :     // queue.
     587             :     std::queue<RefPtr<MicroTaskRunnable>>* microtaskQueue =
     588             :       &GetDebuggerMicroTaskQueue();
     589             : 
     590             :     if (microtaskQueue->empty()) {
     591             :       break;
     592             :     }
     593             : 
     594             :     RefPtr<MicroTaskRunnable> runnable = microtaskQueue->front().forget();
     595             :     MOZ_ASSERT(runnable);
     596             : 
     597             :     // This function can re-enter, so we remove the element before calling.
     598             :     microtaskQueue->pop();
     599             :     runnable->Run(aso);
     600             :   }
     601             : 
     602             :   AfterProcessMicrotasks();
     603             : }
     604             : } // namespace mozilla

Generated by: LCOV version 1.13-14-ga5dd952