Coverage Report

Created: 2018-09-25 14:53

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