Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/workers/WorkerPrivate.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 "WorkerPrivate.h"
8
9
#include "js/CompilationAndEvaluation.h"
10
#include "js/LocaleSensitive.h"
11
#include "js/MemoryMetrics.h"
12
#include "js/SourceBufferHolder.h"
13
#include "MessageEventRunnable.h"
14
#include "mozilla/ScopeExit.h"
15
#include "mozilla/StaticPrefs.h"
16
#include "mozilla/dom/BlobURLProtocolHandler.h"
17
#include "mozilla/dom/ClientManager.h"
18
#include "mozilla/dom/ClientSource.h"
19
#include "mozilla/dom/ClientState.h"
20
#include "mozilla/dom/Console.h"
21
#include "mozilla/dom/DOMTypes.h"
22
#include "mozilla/dom/ErrorEvent.h"
23
#include "mozilla/dom/ErrorEventBinding.h"
24
#include "mozilla/dom/Event.h"
25
#include "mozilla/dom/FunctionBinding.h"
26
#include "mozilla/dom/IndexedDatabaseManager.h"
27
#include "mozilla/dom/MessageEvent.h"
28
#include "mozilla/dom/MessageEventBinding.h"
29
#include "mozilla/dom/MessagePort.h"
30
#include "mozilla/dom/MessagePortBinding.h"
31
#include "mozilla/dom/nsCSPUtils.h"
32
#include "mozilla/dom/Performance.h"
33
#include "mozilla/dom/PerformanceStorageWorker.h"
34
#include "mozilla/dom/PromiseDebugging.h"
35
#include "mozilla/dom/WorkerBinding.h"
36
#include "mozilla/ThreadEventQueue.h"
37
#include "mozilla/ThrottledEventQueue.h"
38
#include "mozilla/TimelineConsumers.h"
39
#include "mozilla/WorkerTimelineMarker.h"
40
#include "nsCycleCollector.h"
41
#include "nsGlobalWindowInner.h"
42
#include "nsNetUtil.h"
43
#include "nsIMemoryReporter.h"
44
#include "nsIPermissionManager.h"
45
#include "nsIRandomGenerator.h"
46
#include "nsIScriptError.h"
47
#include "nsIScriptTimeoutHandler.h"
48
#include "nsIURI.h"
49
#include "nsIURL.h"
50
#include "nsPrintfCString.h"
51
#include "nsQueryObject.h"
52
#include "nsRFPService.h"
53
#include "nsSandboxFlags.h"
54
#include "nsUTF8Utils.h"
55
56
#include "RuntimeService.h"
57
#include "ScriptLoader.h"
58
#include "mozilla/dom/ServiceWorkerEvents.h"
59
#include "mozilla/dom/ServiceWorkerManager.h"
60
#include "SharedWorker.h"
61
#include "WorkerCSPEventListener.h"
62
#include "WorkerDebugger.h"
63
#include "WorkerDebuggerManager.h"
64
#include "WorkerError.h"
65
#include "WorkerEventTarget.h"
66
#include "WorkerNavigator.h"
67
#include "WorkerRef.h"
68
#include "WorkerRunnable.h"
69
#include "WorkerScope.h"
70
#include "WorkerThread.h"
71
72
#include "nsThreadManager.h"
73
74
#ifdef XP_WIN
75
#undef PostMessage
76
#endif
77
78
// JS_MaybeGC will run once every second during normal execution.
79
0
#define PERIODIC_GC_TIMER_DELAY_SEC 1
80
81
// A shrinking GC will run five seconds after the last event is processed.
82
0
#define IDLE_GC_TIMER_DELAY_SEC 5
83
84
static mozilla::LazyLogModule sWorkerPrivateLog("WorkerPrivate");
85
static mozilla::LazyLogModule sWorkerTimeoutsLog("WorkerTimeouts");
86
87
mozilla::LogModule*
88
WorkerLog()
89
0
{
90
0
  return sWorkerPrivateLog;
91
0
}
92
93
mozilla::LogModule*
94
TimeoutsLog()
95
0
{
96
0
  return sWorkerTimeoutsLog;
97
0
}
98
99
#ifdef LOG
100
#undef LOG
101
#endif
102
0
#define LOG(log, _args) MOZ_LOG(log, LogLevel::Debug, _args);
103
104
namespace mozilla {
105
106
using namespace ipc;
107
108
namespace dom {
109
110
using namespace workerinternals;
111
112
MOZ_DEFINE_MALLOC_SIZE_OF(JsWorkerMallocSizeOf)
113
114
namespace {
115
116
#ifdef DEBUG
117
118
const nsIID kDEBUGWorkerEventTargetIID = {
119
  0xccaba3fa, 0x5be2, 0x4de2, { 0xba, 0x87, 0x3b, 0x3b, 0x5b, 0x1d, 0x5, 0xfb }
120
};
121
122
#endif
123
124
template <class T>
125
class AutoPtrComparator
126
{
127
  typedef nsAutoPtr<T> A;
128
  typedef T* B;
129
130
public:
131
0
  bool Equals(const A& a, const B& b) const {
132
0
    return a && b ? *a == *b : !a && !b ? true : false;
133
0
  }
134
0
  bool LessThan(const A& a, const B& b) const {
135
0
    return a && b ? *a < *b : b ? true : false;
136
0
  }
137
};
138
139
template <class T>
140
inline AutoPtrComparator<T>
141
GetAutoPtrComparator(const nsTArray<nsAutoPtr<T> >&)
142
0
{
143
0
  return AutoPtrComparator<T>();
144
0
}
145
146
// This class is used to wrap any runnables that the worker receives via the
147
// nsIEventTarget::Dispatch() method (either from NS_DispatchToCurrentThread or
148
// from the worker's EventTarget).
149
class ExternalRunnableWrapper final : public WorkerRunnable
150
{
151
  nsCOMPtr<nsIRunnable> mWrappedRunnable;
152
153
public:
154
  ExternalRunnableWrapper(WorkerPrivate* aWorkerPrivate,
155
                          nsIRunnable* aWrappedRunnable)
156
  : WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
157
    mWrappedRunnable(aWrappedRunnable)
158
0
  {
159
0
    MOZ_ASSERT(aWorkerPrivate);
160
0
    MOZ_ASSERT(aWrappedRunnable);
161
0
  }
162
163
  NS_INLINE_DECL_REFCOUNTING_INHERITED(ExternalRunnableWrapper, WorkerRunnable)
164
165
private:
166
  ~ExternalRunnableWrapper()
167
0
  { }
168
169
  virtual bool
170
  PreDispatch(WorkerPrivate* aWorkerPrivate) override
171
0
  {
172
0
    // Silence bad assertions.
173
0
    return true;
174
0
  }
175
176
  virtual void
177
  PostDispatch(WorkerPrivate* aWorkerPrivate, bool aDispatchResult) override
178
0
  {
179
0
    // Silence bad assertions.
180
0
  }
181
182
  virtual bool
183
  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
184
0
  {
185
0
    nsresult rv = mWrappedRunnable->Run();
186
0
    if (NS_FAILED(rv)) {
187
0
      if (!JS_IsExceptionPending(aCx)) {
188
0
        Throw(aCx, rv);
189
0
      }
190
0
      return false;
191
0
    }
192
0
    return true;
193
0
  }
194
195
  nsresult
196
  Cancel() override
197
0
  {
198
0
    nsresult rv;
199
0
    nsCOMPtr<nsICancelableRunnable> cancelable =
200
0
      do_QueryInterface(mWrappedRunnable);
201
0
    MOZ_ASSERT(cancelable); // We checked this earlier!
202
0
    rv = cancelable->Cancel();
203
0
    nsresult rv2 = WorkerRunnable::Cancel();
204
0
    return NS_FAILED(rv) ? rv : rv2;
205
0
  }
206
};
207
208
struct WindowAction
209
{
210
  nsPIDOMWindowInner* mWindow;
211
  bool mDefaultAction;
212
213
  MOZ_IMPLICIT WindowAction(nsPIDOMWindowInner* aWindow)
214
  : mWindow(aWindow), mDefaultAction(true)
215
0
  { }
216
217
  bool
218
  operator==(const WindowAction& aOther) const
219
0
  {
220
0
    return mWindow == aOther.mWindow;
221
0
  }
222
};
223
224
class WorkerFinishedRunnable final : public WorkerControlRunnable
225
{
226
  WorkerPrivate* mFinishedWorker;
227
228
public:
229
  WorkerFinishedRunnable(WorkerPrivate* aWorkerPrivate,
230
                         WorkerPrivate* aFinishedWorker)
231
  : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
232
    mFinishedWorker(aFinishedWorker)
233
0
  { }
234
235
private:
236
  virtual bool
237
  PreDispatch(WorkerPrivate* aWorkerPrivate) override
238
0
  {
239
0
    // Silence bad assertions.
240
0
    return true;
241
0
  }
242
243
  virtual void
244
  PostDispatch(WorkerPrivate* aWorkerPrivate, bool aDispatchResult) override
245
0
  {
246
0
    // Silence bad assertions.
247
0
  }
248
249
  virtual bool
250
  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
251
0
  {
252
0
    if (!mFinishedWorker->ProxyReleaseMainThreadObjects()) {
253
0
      NS_WARNING("Failed to dispatch, going to leak!");
254
0
    }
255
0
256
0
    RuntimeService* runtime = RuntimeService::GetService();
257
0
    NS_ASSERTION(runtime, "This should never be null!");
258
0
259
0
    mFinishedWorker->DisableDebugger();
260
0
261
0
    runtime->UnregisterWorker(mFinishedWorker);
262
0
263
0
    mFinishedWorker->ClearSelfAndParentEventTargetRef();
264
0
    return true;
265
0
  }
266
};
267
268
class TopLevelWorkerFinishedRunnable final : public Runnable
269
{
270
  WorkerPrivate* mFinishedWorker;
271
272
public:
273
  explicit TopLevelWorkerFinishedRunnable(WorkerPrivate* aFinishedWorker)
274
    : mozilla::Runnable("TopLevelWorkerFinishedRunnable")
275
    , mFinishedWorker(aFinishedWorker)
276
0
  {
277
0
    aFinishedWorker->AssertIsOnWorkerThread();
278
0
  }
279
280
  NS_INLINE_DECL_REFCOUNTING_INHERITED(TopLevelWorkerFinishedRunnable, Runnable)
281
282
private:
283
0
  ~TopLevelWorkerFinishedRunnable() {}
284
285
  NS_IMETHOD
286
  Run() override
287
0
  {
288
0
    AssertIsOnMainThread();
289
0
290
0
    RuntimeService* runtime = RuntimeService::GetService();
291
0
    MOZ_ASSERT(runtime);
292
0
293
0
    mFinishedWorker->DisableDebugger();
294
0
295
0
    runtime->UnregisterWorker(mFinishedWorker);
296
0
297
0
    if (!mFinishedWorker->ProxyReleaseMainThreadObjects()) {
298
0
      NS_WARNING("Failed to dispatch, going to leak!");
299
0
    }
300
0
301
0
    mFinishedWorker->ClearSelfAndParentEventTargetRef();
302
0
    return NS_OK;
303
0
  }
304
};
305
306
class ModifyBusyCountRunnable final : public WorkerControlRunnable
307
{
308
  bool mIncrease;
309
310
public:
311
  ModifyBusyCountRunnable(WorkerPrivate* aWorkerPrivate, bool aIncrease)
312
  : WorkerControlRunnable(aWorkerPrivate, ParentThreadUnchangedBusyCount),
313
    mIncrease(aIncrease)
314
0
  { }
315
316
private:
317
  virtual bool
318
  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
319
0
  {
320
0
    return aWorkerPrivate->ModifyBusyCount(mIncrease);
321
0
  }
322
323
  virtual void
324
  PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate, bool aRunResult)
325
          override
326
0
  {
327
0
    if (mIncrease) {
328
0
      WorkerControlRunnable::PostRun(aCx, aWorkerPrivate, aRunResult);
329
0
      return;
330
0
    }
331
0
    // Don't do anything here as it's possible that aWorkerPrivate has been
332
0
    // deleted.
333
0
  }
334
};
335
336
class CompileScriptRunnable final : public WorkerRunnable
337
{
338
  nsString mScriptURL;
339
340
public:
341
  explicit CompileScriptRunnable(WorkerPrivate* aWorkerPrivate,
342
                                 const nsAString& aScriptURL)
343
  : WorkerRunnable(aWorkerPrivate),
344
    mScriptURL(aScriptURL)
345
0
  { }
346
347
private:
348
  // We can't implement PreRun effectively, because at the point when that would
349
  // run we have not yet done our load so don't know things like our final
350
  // principal and whatnot.
351
352
  virtual bool
353
  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
354
0
  {
355
0
    aWorkerPrivate->AssertIsOnWorkerThread();
356
0
357
0
    if (NS_WARN_IF(!aWorkerPrivate->EnsureClientSource())) {
358
0
      return false;
359
0
    }
360
0
361
0
    if (NS_WARN_IF(!aWorkerPrivate->EnsureCSPEventListener())) {
362
0
      return false;
363
0
    }
364
0
365
0
    // PerformanceStorage & PerformanceCounter both need to be initialized
366
0
    // on the worker thread before being used on main-thread.
367
0
    // Let's be sure that it is created before any
368
0
    // content loading.
369
0
    aWorkerPrivate->EnsurePerformanceStorage();
370
0
371
0
    if (mozilla::StaticPrefs::dom_performance_enable_scheduler_timing()) {
372
0
      aWorkerPrivate->EnsurePerformanceCounter();
373
0
    }
374
0
375
0
    ErrorResult rv;
376
0
    workerinternals::LoadMainScript(aWorkerPrivate, mScriptURL, WorkerScript, rv);
377
0
    rv.WouldReportJSException();
378
0
    // Explicitly ignore NS_BINDING_ABORTED on rv.  Or more precisely, still
379
0
    // return false and don't SetWorkerScriptExecutedSuccessfully() in that
380
0
    // case, but don't throw anything on aCx.  The idea is to not dispatch error
381
0
    // events if our load is canceled with that error code.
382
0
    if (rv.ErrorCodeIs(NS_BINDING_ABORTED)) {
383
0
      rv.SuppressException();
384
0
      return false;
385
0
    }
386
0
387
0
    WorkerGlobalScope* globalScope = aWorkerPrivate->GlobalScope();
388
0
    if (NS_WARN_IF(!globalScope)) {
389
0
      // We never got as far as calling GetOrCreateGlobalScope, or it failed.
390
0
      // We have no way to enter a compartment, hence no sane way to report this
391
0
      // error.  :(
392
0
      rv.SuppressException();
393
0
      return false;
394
0
    }
395
0
396
0
    // Make sure to propagate exceptions from rv onto aCx, so that they will get
397
0
    // reported after we return.  We want to propagate just JS exceptions,
398
0
    // because all the other errors are handled when the script is loaded.
399
0
    // See: https://dom.spec.whatwg.org/#concept-event-fire
400
0
    if (rv.Failed() && !rv.IsJSException()) {
401
0
      WorkerErrorReport::CreateAndDispatchGenericErrorRunnableToParent(aWorkerPrivate);
402
0
      rv.SuppressException();
403
0
      return false;
404
0
    }
405
0
406
0
    // This is a little dumb, but aCx is in the null realm here because we
407
0
    // set it up that way in our Run(), since we had not created the global at
408
0
    // that point yet.  So we need to enter the realm of our global,
409
0
    // because setting a pending exception on aCx involves wrapping into its
410
0
    // current compartment.  Luckily we have a global now.
411
0
    JSAutoRealm ar(aCx, globalScope->GetGlobalJSObject());
412
0
    if (rv.MaybeSetPendingException(aCx)) {
413
0
      return false;
414
0
    }
415
0
416
0
    aWorkerPrivate->SetWorkerScriptExecutedSuccessfully();
417
0
    return true;
418
0
  }
419
420
  void
421
  PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate, bool aRunResult) override
422
0
  {
423
0
    if (!aRunResult) {
424
0
      aWorkerPrivate->CloseInternal();
425
0
    }
426
0
    WorkerRunnable::PostRun(aCx, aWorkerPrivate, aRunResult);
427
0
  }
428
};
429
430
class NotifyRunnable final : public WorkerControlRunnable
431
{
432
  WorkerStatus mStatus;
433
434
public:
435
  NotifyRunnable(WorkerPrivate* aWorkerPrivate, WorkerStatus aStatus)
436
  : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
437
    mStatus(aStatus)
438
0
  {
439
0
    MOZ_ASSERT(aStatus == Closing || aStatus == Canceling ||
440
0
               aStatus == Killing);
441
0
  }
442
443
private:
444
  virtual bool
445
  PreDispatch(WorkerPrivate* aWorkerPrivate) override
446
0
  {
447
0
    aWorkerPrivate->AssertIsOnParentThread();
448
0
    return aWorkerPrivate->ModifyBusyCount(true);
449
0
  }
450
451
  virtual void
452
  PostDispatch(WorkerPrivate* aWorkerPrivate, bool aDispatchResult) override
453
0
  {
454
0
    aWorkerPrivate->AssertIsOnParentThread();
455
0
    if (!aDispatchResult) {
456
0
      // We couldn't dispatch to the worker, which means it's already dead.
457
0
      // Undo the busy count modification.
458
0
      aWorkerPrivate->ModifyBusyCount(false);
459
0
    }
460
0
  }
461
462
  virtual void
463
  PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate, bool aRunResult)
464
          override
465
0
  {
466
0
    aWorkerPrivate->ModifyBusyCountFromWorker(false);
467
0
  }
468
469
  virtual bool
470
  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
471
0
  {
472
0
    return aWorkerPrivate->NotifyInternal(mStatus);
473
0
  }
474
};
475
476
class FreezeRunnable final : public WorkerControlRunnable
477
{
478
public:
479
  explicit FreezeRunnable(WorkerPrivate* aWorkerPrivate)
480
  : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
481
0
  { }
482
483
private:
484
  virtual bool
485
  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
486
0
  {
487
0
    return aWorkerPrivate->FreezeInternal();
488
0
  }
489
};
490
491
class ThawRunnable final : public WorkerControlRunnable
492
{
493
public:
494
  explicit ThawRunnable(WorkerPrivate* aWorkerPrivate)
495
  : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
496
0
  { }
497
498
private:
499
  virtual bool
500
  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
501
0
  {
502
0
    return aWorkerPrivate->ThawInternal();
503
0
  }
504
};
505
506
class PropagateFirstPartyStorageAccessGrantedRunnable final : public WorkerControlRunnable
507
{
508
public:
509
  explicit PropagateFirstPartyStorageAccessGrantedRunnable(WorkerPrivate* aWorkerPrivate)
510
    : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
511
0
  {}
512
513
private:
514
  bool
515
  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
516
0
  {
517
0
    aWorkerPrivate->PropagateFirstPartyStorageAccessGrantedInternal();
518
0
    return true;
519
0
  }
520
};
521
522
class ReportErrorToConsoleRunnable final : public WorkerRunnable
523
{
524
  const char* mMessage;
525
  const nsTArray<nsString> mParams;
526
527
public:
528
  // aWorkerPrivate is the worker thread we're on (or the main thread, if null)
529
  static void
530
  Report(WorkerPrivate* aWorkerPrivate, const char* aMessage,
531
         const nsTArray<nsString>& aParams)
532
0
  {
533
0
    if (aWorkerPrivate) {
534
0
      aWorkerPrivate->AssertIsOnWorkerThread();
535
0
    } else {
536
0
      AssertIsOnMainThread();
537
0
    }
538
0
539
0
    // Now fire a runnable to do the same on the parent's thread if we can.
540
0
    if (aWorkerPrivate) {
541
0
      RefPtr<ReportErrorToConsoleRunnable> runnable =
542
0
        new ReportErrorToConsoleRunnable(aWorkerPrivate, aMessage, aParams);
543
0
      runnable->Dispatch();
544
0
      return;
545
0
    }
546
0
547
0
    uint16_t paramCount = aParams.Length();
548
0
    const char16_t** params = new const char16_t*[paramCount];
549
0
    for (uint16_t i=0; i<paramCount; ++i) {
550
0
      params[i] = aParams[i].get();
551
0
    }
552
0
553
0
    // Log a warning to the console.
554
0
    nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
555
0
                                    NS_LITERAL_CSTRING("DOM"), nullptr,
556
0
                                    nsContentUtils::eDOM_PROPERTIES, aMessage,
557
0
                                    paramCount ? params : nullptr, paramCount);
558
0
    delete[] params;
559
0
  }
560
561
private:
562
  ReportErrorToConsoleRunnable(WorkerPrivate* aWorkerPrivate, const char* aMessage,
563
                               const nsTArray<nsString>& aParams)
564
  : WorkerRunnable(aWorkerPrivate, ParentThreadUnchangedBusyCount),
565
    mMessage(aMessage), mParams(aParams)
566
0
  { }
567
568
  virtual void
569
  PostDispatch(WorkerPrivate* aWorkerPrivate, bool aDispatchResult) override
570
0
  {
571
0
    aWorkerPrivate->AssertIsOnWorkerThread();
572
0
573
0
    // Dispatch may fail if the worker was canceled, no need to report that as
574
0
    // an error, so don't call base class PostDispatch.
575
0
  }
576
577
  virtual bool
578
  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
579
0
  {
580
0
    WorkerPrivate* parent = aWorkerPrivate->GetParent();
581
0
    MOZ_ASSERT_IF(!parent, NS_IsMainThread());
582
0
    Report(parent, mMessage, mParams);
583
0
    return true;
584
0
  }
585
};
586
587
class TimerRunnable final : public WorkerRunnable,
588
                            public nsITimerCallback,
589
                            public nsINamed
590
{
591
public:
592
  NS_DECL_ISUPPORTS_INHERITED
593
594
  explicit TimerRunnable(WorkerPrivate* aWorkerPrivate)
595
  : WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
596
0
  { }
597
598
private:
599
0
  ~TimerRunnable() {}
600
601
  virtual bool
602
  PreDispatch(WorkerPrivate* aWorkerPrivate) override
603
0
  {
604
0
    // Silence bad assertions.
605
0
    return true;
606
0
  }
607
608
  virtual void
609
  PostDispatch(WorkerPrivate* aWorkerPrivate, bool aDispatchResult) override
610
0
  {
611
0
    // Silence bad assertions.
612
0
  }
613
614
  virtual bool
615
  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
616
0
  {
617
0
    return aWorkerPrivate->RunExpiredTimeouts(aCx);
618
0
  }
619
620
  NS_IMETHOD
621
  Notify(nsITimer* aTimer) override
622
0
  {
623
0
    return Run();
624
0
  }
625
626
  NS_IMETHOD
627
  GetName(nsACString& aName) override
628
0
  {
629
0
    aName.AssignLiteral("TimerRunnable");
630
0
    return NS_OK;
631
0
  }
632
};
633
634
NS_IMPL_ISUPPORTS_INHERITED(TimerRunnable, WorkerRunnable, nsITimerCallback,
635
                            nsINamed)
636
637
class DebuggerImmediateRunnable : public WorkerRunnable
638
{
639
  RefPtr<dom::Function> mHandler;
640
641
public:
642
  explicit DebuggerImmediateRunnable(WorkerPrivate* aWorkerPrivate,
643
                                     dom::Function& aHandler)
644
  : WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
645
    mHandler(&aHandler)
646
0
  { }
647
648
private:
649
  virtual bool
650
  IsDebuggerRunnable() const override
651
0
  {
652
0
    return true;
653
0
  }
654
655
  virtual bool
656
  PreDispatch(WorkerPrivate* aWorkerPrivate) override
657
0
  {
658
0
    // Silence bad assertions.
659
0
    return true;
660
0
  }
661
662
  virtual void
663
  PostDispatch(WorkerPrivate* aWorkerPrivate, bool aDispatchResult) override
664
0
  {
665
0
    // Silence bad assertions.
666
0
  }
667
668
  virtual bool
669
  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
670
0
  {
671
0
    JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx));
672
0
    JS::Rooted<JS::Value> callable(aCx, JS::ObjectOrNullValue(mHandler->CallableOrNull()));
673
0
    JS::HandleValueArray args = JS::HandleValueArray::empty();
674
0
    JS::Rooted<JS::Value> rval(aCx);
675
0
    if (!JS_CallFunctionValue(aCx, global, callable, args, &rval)) {
676
0
      // Just return false; WorkerRunnable::Run will report the exception.
677
0
      return false;
678
0
    }
679
0
680
0
    return true;
681
0
  }
682
};
683
684
void
685
PeriodicGCTimerCallback(nsITimer* aTimer, void* aClosure)
686
0
{
687
0
  auto workerPrivate = static_cast<WorkerPrivate*>(aClosure);
688
0
  MOZ_DIAGNOSTIC_ASSERT(workerPrivate);
689
0
  workerPrivate->AssertIsOnWorkerThread();
690
0
  workerPrivate->GarbageCollectInternal(workerPrivate->GetJSContext(),
691
0
                                        false /* shrinking */,
692
0
                                        false /* collect children */);
693
0
}
694
695
void
696
IdleGCTimerCallback(nsITimer* aTimer, void* aClosure)
697
0
{
698
0
  auto workerPrivate = static_cast<WorkerPrivate*>(aClosure);
699
0
  MOZ_DIAGNOSTIC_ASSERT(workerPrivate);
700
0
  workerPrivate->AssertIsOnWorkerThread();
701
0
  workerPrivate->GarbageCollectInternal(workerPrivate->GetJSContext(),
702
0
                                        true /* shrinking */,
703
0
                                        false /* collect children */);
704
0
}
705
706
class UpdateContextOptionsRunnable final : public WorkerControlRunnable
707
{
708
  JS::ContextOptions mContextOptions;
709
710
public:
711
  UpdateContextOptionsRunnable(WorkerPrivate* aWorkerPrivate,
712
                               const JS::ContextOptions& aContextOptions)
713
  : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
714
    mContextOptions(aContextOptions)
715
0
  { }
716
717
private:
718
  virtual bool
719
  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
720
0
  {
721
0
    aWorkerPrivate->UpdateContextOptionsInternal(aCx, mContextOptions);
722
0
    return true;
723
0
  }
724
};
725
726
class UpdateLanguagesRunnable final : public WorkerRunnable
727
{
728
  nsTArray<nsString> mLanguages;
729
730
public:
731
  UpdateLanguagesRunnable(WorkerPrivate* aWorkerPrivate,
732
                          const nsTArray<nsString>& aLanguages)
733
    : WorkerRunnable(aWorkerPrivate),
734
      mLanguages(aLanguages)
735
0
  { }
736
737
  virtual bool
738
  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
739
0
  {
740
0
    aWorkerPrivate->UpdateLanguagesInternal(mLanguages);
741
0
    return true;
742
0
  }
743
};
744
745
class UpdateJSWorkerMemoryParameterRunnable final :
746
  public WorkerControlRunnable
747
{
748
  uint32_t mValue;
749
  JSGCParamKey mKey;
750
751
public:
752
  UpdateJSWorkerMemoryParameterRunnable(WorkerPrivate* aWorkerPrivate,
753
                                        JSGCParamKey aKey,
754
                                        uint32_t aValue)
755
  : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
756
    mValue(aValue), mKey(aKey)
757
0
  { }
758
759
private:
760
  virtual bool
761
  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
762
0
  {
763
0
    aWorkerPrivate->UpdateJSWorkerMemoryParameterInternal(aCx, mKey, mValue);
764
0
    return true;
765
0
  }
766
};
767
768
#ifdef JS_GC_ZEAL
769
class UpdateGCZealRunnable final : public WorkerControlRunnable
770
{
771
  uint8_t mGCZeal;
772
  uint32_t mFrequency;
773
774
public:
775
  UpdateGCZealRunnable(WorkerPrivate* aWorkerPrivate,
776
                       uint8_t aGCZeal,
777
                       uint32_t aFrequency)
778
  : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
779
    mGCZeal(aGCZeal), mFrequency(aFrequency)
780
  { }
781
782
private:
783
  virtual bool
784
  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
785
  {
786
    aWorkerPrivate->UpdateGCZealInternal(aCx, mGCZeal, mFrequency);
787
    return true;
788
  }
789
};
790
#endif
791
792
class GarbageCollectRunnable final : public WorkerControlRunnable
793
{
794
  bool mShrinking;
795
  bool mCollectChildren;
796
797
public:
798
  GarbageCollectRunnable(WorkerPrivate* aWorkerPrivate, bool aShrinking,
799
                         bool aCollectChildren)
800
  : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
801
    mShrinking(aShrinking), mCollectChildren(aCollectChildren)
802
0
  { }
803
804
private:
805
  virtual bool
806
  PreDispatch(WorkerPrivate* aWorkerPrivate) override
807
0
  {
808
0
    // Silence bad assertions, this can be dispatched from either the main
809
0
    // thread or the timer thread..
810
0
    return true;
811
0
  }
812
813
  virtual void
814
  PostDispatch(WorkerPrivate* aWorkerPrivate, bool aDispatchResult) override
815
0
  {
816
0
    // Silence bad assertions, this can be dispatched from either the main
817
0
    // thread or the timer thread..
818
0
  }
819
820
  virtual bool
821
  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
822
0
  {
823
0
    aWorkerPrivate->GarbageCollectInternal(aCx, mShrinking, mCollectChildren);
824
0
    return true;
825
0
  }
826
};
827
828
class CycleCollectRunnable : public WorkerControlRunnable
829
{
830
  bool mCollectChildren;
831
832
public:
833
  CycleCollectRunnable(WorkerPrivate* aWorkerPrivate, bool aCollectChildren)
834
  : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
835
    mCollectChildren(aCollectChildren)
836
0
  { }
837
838
  bool
839
  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
840
0
  {
841
0
    aWorkerPrivate->CycleCollectInternal(mCollectChildren);
842
0
    return true;
843
0
  }
844
};
845
846
class OfflineStatusChangeRunnable : public WorkerRunnable
847
{
848
public:
849
  OfflineStatusChangeRunnable(WorkerPrivate* aWorkerPrivate, bool aIsOffline)
850
    : WorkerRunnable(aWorkerPrivate),
851
      mIsOffline(aIsOffline)
852
0
  {
853
0
  }
854
855
  bool
856
  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
857
0
  {
858
0
    aWorkerPrivate->OfflineStatusChangeEventInternal(mIsOffline);
859
0
    return true;
860
0
  }
861
862
private:
863
  bool mIsOffline;
864
};
865
866
class MemoryPressureRunnable : public WorkerControlRunnable
867
{
868
public:
869
  explicit MemoryPressureRunnable(WorkerPrivate* aWorkerPrivate)
870
    : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
871
0
  {}
872
873
  bool
874
  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
875
0
  {
876
0
    aWorkerPrivate->MemoryPressureInternal();
877
0
    return true;
878
0
  }
879
};
880
881
#ifdef DEBUG
882
static bool
883
StartsWithExplicit(nsACString& s)
884
{
885
    return StringBeginsWith(s, NS_LITERAL_CSTRING("explicit/"));
886
}
887
#endif
888
889
class MessagePortRunnable final : public WorkerRunnable
890
{
891
  MessagePortIdentifier mPortIdentifier;
892
893
public:
894
  MessagePortRunnable(WorkerPrivate* aWorkerPrivate, MessagePort* aPort)
895
  : WorkerRunnable(aWorkerPrivate)
896
0
  {
897
0
    MOZ_ASSERT(aPort);
898
0
    // In order to move the port from one thread to another one, we have to
899
0
    // close and disentangle it. The output will be a MessagePortIdentifier that
900
0
    // will be used to recreate a new MessagePort on the other thread.
901
0
    aPort->CloneAndDisentangle(mPortIdentifier);
902
0
  }
903
904
private:
905
  ~MessagePortRunnable()
906
0
  { }
907
908
  virtual bool
909
  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
910
0
  {
911
0
    return aWorkerPrivate->ConnectMessagePort(aCx, mPortIdentifier);
912
0
  }
913
914
  nsresult
915
  Cancel() override
916
0
  {
917
0
    MessagePort::ForceClose(mPortIdentifier);
918
0
    return WorkerRunnable::Cancel();
919
0
  }
920
};
921
922
PRThread*
923
PRThreadFromThread(nsIThread* aThread)
924
0
{
925
0
  MOZ_ASSERT(aThread);
926
0
927
0
  PRThread* result;
928
0
  MOZ_ALWAYS_SUCCEEDS(aThread->GetPRThread(&result));
929
0
  MOZ_ASSERT(result);
930
0
931
0
  return result;
932
0
}
933
934
// A runnable to cancel the worker from the parent thread when self.close() is
935
// called. This runnable is executed on the parent process in order to cancel
936
// the current runnable. It uses a normal WorkerRunnable in order to be sure
937
// that all the pending WorkerRunnables are executed before this.
938
class CancelingOnParentRunnable final : public WorkerRunnable
939
{
940
public:
941
  explicit CancelingOnParentRunnable(WorkerPrivate* aWorkerPrivate)
942
    : WorkerRunnable(aWorkerPrivate, ParentThreadUnchangedBusyCount)
943
0
  {}
944
945
  bool
946
  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
947
0
  {
948
0
    aWorkerPrivate->Cancel();
949
0
    return true;
950
0
  }
951
};
952
953
// A runnable to cancel the worker from the parent process.
954
class CancelingWithTimeoutOnParentRunnable final : public WorkerControlRunnable
955
{
956
public:
957
  explicit CancelingWithTimeoutOnParentRunnable(WorkerPrivate* aWorkerPrivate)
958
    : WorkerControlRunnable(aWorkerPrivate, ParentThreadUnchangedBusyCount)
959
0
  {}
960
961
  bool
962
  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
963
0
  {
964
0
    aWorkerPrivate->AssertIsOnParentThread();
965
0
    aWorkerPrivate->StartCancelingTimer();
966
0
    return true;
967
0
  }
968
};
969
970
class CancelingTimerCallback final : public nsITimerCallback
971
{
972
public:
973
  NS_DECL_ISUPPORTS
974
975
  explicit CancelingTimerCallback(WorkerPrivate* aWorkerPrivate)
976
    : mWorkerPrivate(aWorkerPrivate)
977
0
  {}
978
979
  NS_IMETHOD
980
  Notify(nsITimer* aTimer) override
981
0
  {
982
0
    mWorkerPrivate->AssertIsOnParentThread();
983
0
    mWorkerPrivate->Cancel();
984
0
    return NS_OK;
985
0
  }
986
987
private:
988
  ~CancelingTimerCallback() = default;
989
990
  // Raw pointer here is OK because the timer is canceled during the shutdown
991
  // steps.
992
  WorkerPrivate* mWorkerPrivate;
993
};
994
995
NS_IMPL_ISUPPORTS(CancelingTimerCallback, nsITimerCallback)
996
997
// This runnable starts the canceling of a worker after a self.close().
998
class CancelingRunnable final : public Runnable
999
{
1000
public:
1001
  CancelingRunnable()
1002
    : Runnable("CancelingRunnable")
1003
0
  {}
1004
1005
  NS_IMETHOD
1006
  Run() override
1007
0
  {
1008
0
    WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
1009
0
    MOZ_ASSERT(workerPrivate);
1010
0
    workerPrivate->AssertIsOnWorkerThread();
1011
0
1012
0
    // Now we can cancel the this worker from the parent process.
1013
0
    RefPtr<CancelingOnParentRunnable> r =
1014
0
      new CancelingOnParentRunnable(workerPrivate);
1015
0
    r->Dispatch();
1016
0
1017
0
    return NS_OK;
1018
0
  }
1019
};
1020
1021
} /* anonymous namespace */
1022
1023
class WorkerPrivate::EventTarget final : public nsISerialEventTarget
1024
{
1025
  // This mutex protects mWorkerPrivate and must be acquired *before* the
1026
  // WorkerPrivate's mutex whenever they must both be held.
1027
  mozilla::Mutex mMutex;
1028
  WorkerPrivate* mWorkerPrivate;
1029
  nsIEventTarget* mWeakNestedEventTarget;
1030
  nsCOMPtr<nsIEventTarget> mNestedEventTarget;
1031
1032
public:
1033
  explicit EventTarget(WorkerPrivate* aWorkerPrivate)
1034
  : mMutex("WorkerPrivate::EventTarget::mMutex"),
1035
    mWorkerPrivate(aWorkerPrivate), mWeakNestedEventTarget(nullptr)
1036
0
  {
1037
0
    MOZ_ASSERT(aWorkerPrivate);
1038
0
  }
1039
1040
  EventTarget(WorkerPrivate* aWorkerPrivate, nsIEventTarget* aNestedEventTarget)
1041
  : mMutex("WorkerPrivate::EventTarget::mMutex"),
1042
    mWorkerPrivate(aWorkerPrivate), mWeakNestedEventTarget(aNestedEventTarget),
1043
    mNestedEventTarget(aNestedEventTarget)
1044
0
  {
1045
0
    MOZ_ASSERT(aWorkerPrivate);
1046
0
    MOZ_ASSERT(aNestedEventTarget);
1047
0
  }
1048
1049
  void
1050
  Disable()
1051
0
  {
1052
0
    nsCOMPtr<nsIEventTarget> nestedEventTarget;
1053
0
    {
1054
0
      MutexAutoLock lock(mMutex);
1055
0
1056
0
      // Note, Disable() can be called more than once safely.
1057
0
      mWorkerPrivate = nullptr;
1058
0
      mNestedEventTarget.swap(nestedEventTarget);
1059
0
    }
1060
0
  }
1061
1062
  nsIEventTarget*
1063
  GetWeakNestedEventTarget() const
1064
0
  {
1065
0
    MOZ_ASSERT(mWeakNestedEventTarget);
1066
0
    return mWeakNestedEventTarget;
1067
0
  }
1068
1069
  NS_DECL_THREADSAFE_ISUPPORTS
1070
  NS_DECL_NSIEVENTTARGET_FULL
1071
1072
private:
1073
  ~EventTarget()
1074
0
  { }
1075
};
1076
1077
struct WorkerPrivate::TimeoutInfo
1078
{
1079
  TimeoutInfo()
1080
  : mId(0), mIsInterval(false), mCanceled(false)
1081
0
  {
1082
0
    MOZ_COUNT_CTOR(mozilla::dom::WorkerPrivate::TimeoutInfo);
1083
0
  }
1084
1085
  ~TimeoutInfo()
1086
0
  {
1087
0
    MOZ_COUNT_DTOR(mozilla::dom::WorkerPrivate::TimeoutInfo);
1088
0
  }
1089
1090
  bool operator==(const TimeoutInfo& aOther)
1091
0
  {
1092
0
    return mTargetTime == aOther.mTargetTime;
1093
0
  }
1094
1095
  bool operator<(const TimeoutInfo& aOther)
1096
0
  {
1097
0
    return mTargetTime < aOther.mTargetTime;
1098
0
  }
1099
1100
  nsCOMPtr<nsIScriptTimeoutHandler> mHandler;
1101
  mozilla::TimeStamp mTargetTime;
1102
  mozilla::TimeDuration mInterval;
1103
  int32_t mId;
1104
  bool mIsInterval;
1105
  bool mCanceled;
1106
};
1107
1108
class WorkerJSContextStats final : public JS::RuntimeStats
1109
{
1110
  const nsCString mRtPath;
1111
1112
public:
1113
  explicit WorkerJSContextStats(const nsACString& aRtPath)
1114
  : JS::RuntimeStats(JsWorkerMallocSizeOf), mRtPath(aRtPath)
1115
0
  { }
1116
1117
  ~WorkerJSContextStats()
1118
0
  {
1119
0
    for (size_t i = 0; i != zoneStatsVector.length(); i++) {
1120
0
      delete static_cast<xpc::ZoneStatsExtras*>(zoneStatsVector[i].extra);
1121
0
    }
1122
0
1123
0
    for (size_t i = 0; i != realmStatsVector.length(); i++) {
1124
0
      delete static_cast<xpc::RealmStatsExtras*>(realmStatsVector[i].extra);
1125
0
    }
1126
0
  }
1127
1128
  const nsCString& Path() const
1129
0
  {
1130
0
    return mRtPath;
1131
0
  }
1132
1133
  virtual void
1134
  initExtraZoneStats(JS::Zone* aZone,
1135
                     JS::ZoneStats* aZoneStats)
1136
                     override
1137
0
  {
1138
0
    MOZ_ASSERT(!aZoneStats->extra);
1139
0
1140
0
    // ReportJSRuntimeExplicitTreeStats expects that
1141
0
    // aZoneStats->extra is a xpc::ZoneStatsExtras pointer.
1142
0
    xpc::ZoneStatsExtras* extras = new xpc::ZoneStatsExtras;
1143
0
    extras->pathPrefix = mRtPath;
1144
0
    extras->pathPrefix += nsPrintfCString("zone(0x%p)/", (void *)aZone);
1145
0
1146
0
    MOZ_ASSERT(StartsWithExplicit(extras->pathPrefix));
1147
0
1148
0
    aZoneStats->extra = extras;
1149
0
  }
1150
1151
  virtual void
1152
  initExtraRealmStats(JS::Handle<JS::Realm*> aRealm,
1153
                      JS::RealmStats* aRealmStats)
1154
                      override
1155
0
  {
1156
0
    MOZ_ASSERT(!aRealmStats->extra);
1157
0
1158
0
    // ReportJSRuntimeExplicitTreeStats expects that
1159
0
    // aRealmStats->extra is a xpc::RealmStatsExtras pointer.
1160
0
    xpc::RealmStatsExtras* extras = new xpc::RealmStatsExtras;
1161
0
1162
0
    // This is the |jsPathPrefix|.  Each worker has exactly one realm.
1163
0
    extras->jsPathPrefix.Assign(mRtPath);
1164
0
    extras->jsPathPrefix += nsPrintfCString("zone(0x%p)/",
1165
0
                                            (void *)js::GetRealmZone(aRealm));
1166
0
    extras->jsPathPrefix += NS_LITERAL_CSTRING("realm(web-worker)/");
1167
0
1168
0
    // This should never be used when reporting with workers (hence the "?!").
1169
0
    extras->domPathPrefix.AssignLiteral("explicit/workers/?!/");
1170
0
1171
0
    MOZ_ASSERT(StartsWithExplicit(extras->jsPathPrefix));
1172
0
    MOZ_ASSERT(StartsWithExplicit(extras->domPathPrefix));
1173
0
1174
0
    extras->location = nullptr;
1175
0
1176
0
    aRealmStats->extra = extras;
1177
0
  }
1178
};
1179
1180
class WorkerPrivate::MemoryReporter final : public nsIMemoryReporter
1181
{
1182
  NS_DECL_THREADSAFE_ISUPPORTS
1183
1184
  friend class WorkerPrivate;
1185
1186
  SharedMutex mMutex;
1187
  WorkerPrivate* mWorkerPrivate;
1188
1189
public:
1190
  explicit MemoryReporter(WorkerPrivate* aWorkerPrivate)
1191
  : mMutex(aWorkerPrivate->mMutex), mWorkerPrivate(aWorkerPrivate)
1192
0
  {
1193
0
    aWorkerPrivate->AssertIsOnWorkerThread();
1194
0
  }
1195
1196
  NS_IMETHOD
1197
  CollectReports(nsIHandleReportCallback* aHandleReport,
1198
                 nsISupports* aData, bool aAnonymize) override;
1199
1200
private:
1201
  class FinishCollectRunnable;
1202
1203
  class CollectReportsRunnable final : public MainThreadWorkerControlRunnable
1204
  {
1205
    RefPtr<FinishCollectRunnable> mFinishCollectRunnable;
1206
    const bool mAnonymize;
1207
1208
  public:
1209
    CollectReportsRunnable(
1210
      WorkerPrivate* aWorkerPrivate,
1211
      nsIHandleReportCallback* aHandleReport,
1212
      nsISupports* aHandlerData,
1213
      bool aAnonymize,
1214
      const nsACString& aPath);
1215
1216
  private:
1217
    bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override;
1218
1219
    ~CollectReportsRunnable()
1220
0
    {
1221
0
      if (NS_IsMainThread()) {
1222
0
        mFinishCollectRunnable->Run();
1223
0
        return;
1224
0
      }
1225
0
1226
0
      WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
1227
0
      MOZ_ASSERT(workerPrivate);
1228
0
      MOZ_ALWAYS_SUCCEEDS(
1229
0
        workerPrivate->DispatchToMainThread(mFinishCollectRunnable.forget()));
1230
0
    }
1231
  };
1232
1233
  class FinishCollectRunnable final : public Runnable
1234
  {
1235
    nsCOMPtr<nsIHandleReportCallback> mHandleReport;
1236
    nsCOMPtr<nsISupports> mHandlerData;
1237
    size_t mPerformanceUserEntries;
1238
    size_t mPerformanceResourceEntries;
1239
    const bool mAnonymize;
1240
    bool mSuccess;
1241
1242
  public:
1243
    WorkerJSContextStats mCxStats;
1244
1245
    explicit FinishCollectRunnable(
1246
      nsIHandleReportCallback* aHandleReport,
1247
      nsISupports* aHandlerData,
1248
      bool aAnonymize,
1249
      const nsACString& aPath);
1250
1251
    NS_IMETHOD Run() override;
1252
1253
    void SetPerformanceSizes(size_t userEntries, size_t resourceEntries)
1254
0
    {
1255
0
      mPerformanceUserEntries = userEntries;
1256
0
      mPerformanceResourceEntries = resourceEntries;
1257
0
    }
1258
1259
    void SetSuccess(bool success)
1260
0
    {
1261
0
      mSuccess = success;
1262
0
    }
1263
1264
  private:
1265
    ~FinishCollectRunnable()
1266
0
    {
1267
0
      // mHandleReport and mHandlerData are released on the main thread.
1268
0
      AssertIsOnMainThread();
1269
0
    }
1270
1271
    FinishCollectRunnable(const FinishCollectRunnable&) = delete;
1272
    FinishCollectRunnable& operator=(const FinishCollectRunnable&) = delete;
1273
    FinishCollectRunnable& operator=(const FinishCollectRunnable&&) = delete;
1274
  };
1275
1276
  ~MemoryReporter()
1277
0
  {
1278
0
  }
1279
1280
  void
1281
  Disable()
1282
0
  {
1283
0
    // Called from WorkerPrivate::DisableMemoryReporter.
1284
0
    mMutex.AssertCurrentThreadOwns();
1285
0
1286
0
    NS_ASSERTION(mWorkerPrivate, "Disabled more than once!");
1287
0
    mWorkerPrivate = nullptr;
1288
0
  }
1289
};
1290
1291
NS_IMPL_ISUPPORTS(WorkerPrivate::MemoryReporter, nsIMemoryReporter)
1292
1293
NS_IMETHODIMP
1294
WorkerPrivate::MemoryReporter::CollectReports(nsIHandleReportCallback* aHandleReport,
1295
                                              nsISupports* aData,
1296
                                              bool aAnonymize)
1297
0
{
1298
0
  AssertIsOnMainThread();
1299
0
1300
0
  RefPtr<CollectReportsRunnable> runnable;
1301
0
1302
0
  {
1303
0
    MutexAutoLock lock(mMutex);
1304
0
1305
0
    if (!mWorkerPrivate) {
1306
0
      // This will effectively report 0 memory.
1307
0
      nsCOMPtr<nsIMemoryReporterManager> manager =
1308
0
        do_GetService("@mozilla.org/memory-reporter-manager;1");
1309
0
      if (manager) {
1310
0
        manager->EndReport();
1311
0
      }
1312
0
      return NS_OK;
1313
0
    }
1314
0
1315
0
    nsAutoCString path;
1316
0
    path.AppendLiteral("explicit/workers/workers(");
1317
0
    if (aAnonymize && !mWorkerPrivate->Domain().IsEmpty()) {
1318
0
      path.AppendLiteral("<anonymized-domain>)/worker(<anonymized-url>");
1319
0
    } else {
1320
0
      nsAutoCString escapedDomain(mWorkerPrivate->Domain());
1321
0
      if (escapedDomain.IsEmpty()) {
1322
0
        escapedDomain += "chrome";
1323
0
      } else {
1324
0
        escapedDomain.ReplaceChar('/', '\\');
1325
0
      }
1326
0
      path.Append(escapedDomain);
1327
0
      path.AppendLiteral(")/worker(");
1328
0
      NS_ConvertUTF16toUTF8 escapedURL(mWorkerPrivate->ScriptURL());
1329
0
      escapedURL.ReplaceChar('/', '\\');
1330
0
      path.Append(escapedURL);
1331
0
    }
1332
0
    path.AppendPrintf(", 0x%p)/", static_cast<void*>(mWorkerPrivate));
1333
0
1334
0
    runnable =
1335
0
      new CollectReportsRunnable(mWorkerPrivate, aHandleReport, aData, aAnonymize, path);
1336
0
  }
1337
0
1338
0
  if (!runnable->Dispatch()) {
1339
0
    return NS_ERROR_UNEXPECTED;
1340
0
  }
1341
0
1342
0
  return NS_OK;
1343
0
}
1344
1345
WorkerPrivate::MemoryReporter::CollectReportsRunnable::CollectReportsRunnable(
1346
  WorkerPrivate* aWorkerPrivate,
1347
  nsIHandleReportCallback* aHandleReport,
1348
  nsISupports* aHandlerData,
1349
  bool aAnonymize,
1350
  const nsACString& aPath)
1351
  : MainThreadWorkerControlRunnable(aWorkerPrivate),
1352
    mFinishCollectRunnable(
1353
      new FinishCollectRunnable(aHandleReport, aHandlerData, aAnonymize, aPath)),
1354
    mAnonymize(aAnonymize)
1355
0
{ }
1356
1357
bool
1358
WorkerPrivate::MemoryReporter::CollectReportsRunnable::WorkerRun(JSContext* aCx,
1359
                                                                 WorkerPrivate* aWorkerPrivate)
1360
0
{
1361
0
  aWorkerPrivate->AssertIsOnWorkerThread();
1362
0
1363
0
  RefPtr<WorkerGlobalScope> scope = aWorkerPrivate->GlobalScope();
1364
0
  RefPtr<Performance> performance = scope ? scope->GetPerformanceIfExists()
1365
0
                                          : nullptr;
1366
0
  if (performance) {
1367
0
    size_t userEntries = performance->SizeOfUserEntries(JsWorkerMallocSizeOf);
1368
0
    size_t resourceEntries =
1369
0
      performance->SizeOfResourceEntries(JsWorkerMallocSizeOf);
1370
0
    mFinishCollectRunnable->SetPerformanceSizes(userEntries, resourceEntries);
1371
0
  }
1372
0
1373
0
  mFinishCollectRunnable->SetSuccess(
1374
0
    aWorkerPrivate->CollectRuntimeStats(&mFinishCollectRunnable->mCxStats, mAnonymize));
1375
0
1376
0
  return true;
1377
0
}
1378
1379
WorkerPrivate::MemoryReporter::FinishCollectRunnable::FinishCollectRunnable(
1380
  nsIHandleReportCallback* aHandleReport,
1381
  nsISupports* aHandlerData,
1382
  bool aAnonymize,
1383
  const nsACString& aPath)
1384
  : mozilla::Runnable("dom::WorkerPrivate::MemoryReporter::FinishCollectRunnable")
1385
  , mHandleReport(aHandleReport)
1386
  , mHandlerData(aHandlerData)
1387
  , mPerformanceUserEntries(0)
1388
  , mPerformanceResourceEntries(0)
1389
  , mAnonymize(aAnonymize)
1390
  , mSuccess(false)
1391
  , mCxStats(aPath)
1392
0
{ }
1393
1394
NS_IMETHODIMP
1395
WorkerPrivate::MemoryReporter::FinishCollectRunnable::Run()
1396
0
{
1397
0
  AssertIsOnMainThread();
1398
0
1399
0
  nsCOMPtr<nsIMemoryReporterManager> manager =
1400
0
    do_GetService("@mozilla.org/memory-reporter-manager;1");
1401
0
1402
0
  if (!manager)
1403
0
    return NS_OK;
1404
0
1405
0
  if (mSuccess) {
1406
0
    xpc::ReportJSRuntimeExplicitTreeStats(mCxStats, mCxStats.Path(),
1407
0
                                          mHandleReport, mHandlerData,
1408
0
                                          mAnonymize);
1409
0
1410
0
    if (mPerformanceUserEntries) {
1411
0
      nsCString path = mCxStats.Path();
1412
0
      path.AppendLiteral("dom/performance/user-entries");
1413
0
      mHandleReport->Callback(EmptyCString(), path,
1414
0
                              nsIMemoryReporter::KIND_HEAP,
1415
0
                              nsIMemoryReporter::UNITS_BYTES,
1416
0
                              mPerformanceUserEntries,
1417
0
                              NS_LITERAL_CSTRING("Memory used for performance user entries."),
1418
0
                              mHandlerData);
1419
0
    }
1420
0
1421
0
    if (mPerformanceResourceEntries) {
1422
0
      nsCString path = mCxStats.Path();
1423
0
      path.AppendLiteral("dom/performance/resource-entries");
1424
0
      mHandleReport->Callback(EmptyCString(), path,
1425
0
                              nsIMemoryReporter::KIND_HEAP,
1426
0
                              nsIMemoryReporter::UNITS_BYTES,
1427
0
                              mPerformanceResourceEntries,
1428
0
                              NS_LITERAL_CSTRING("Memory used for performance resource entries."),
1429
0
                              mHandlerData);
1430
0
    }
1431
0
  }
1432
0
1433
0
  manager->EndReport();
1434
0
1435
0
  return NS_OK;
1436
0
}
1437
1438
WorkerPrivate::SyncLoopInfo::SyncLoopInfo(EventTarget* aEventTarget)
1439
: mEventTarget(aEventTarget), mCompleted(false), mResult(false)
1440
#ifdef DEBUG
1441
  , mHasRun(false)
1442
#endif
1443
0
{
1444
0
}
1445
1446
nsIDocument*
1447
WorkerPrivate::GetDocument() const
1448
0
{
1449
0
  AssertIsOnMainThread();
1450
0
  if (mLoadInfo.mWindow) {
1451
0
    return mLoadInfo.mWindow->GetExtantDoc();
1452
0
  }
1453
0
  // if we don't have a document, we should query the document
1454
0
  // from the parent in case of a nested worker
1455
0
  WorkerPrivate* parent = mParent;
1456
0
  while (parent) {
1457
0
    if (parent->mLoadInfo.mWindow) {
1458
0
      return parent->mLoadInfo.mWindow->GetExtantDoc();
1459
0
    }
1460
0
    parent = parent->GetParent();
1461
0
  }
1462
0
  // couldn't query a document, give up and return nullptr
1463
0
  return nullptr;
1464
0
}
1465
1466
void
1467
WorkerPrivate::SetCSP(nsIContentSecurityPolicy* aCSP)
1468
0
{
1469
0
  AssertIsOnMainThread();
1470
0
  if (!aCSP) {
1471
0
    return;
1472
0
  }
1473
0
  aCSP->EnsureEventTarget(mMainThreadEventTarget);
1474
0
  aCSP->SetEventListener(mCSPEventListener);
1475
0
1476
0
  mLoadInfo.mCSP = aCSP;
1477
0
}
1478
1479
nsresult
1480
WorkerPrivate::SetCSPFromHeaderValues(const nsACString& aCSPHeaderValue,
1481
                                      const nsACString& aCSPReportOnlyHeaderValue)
1482
0
{
1483
0
  AssertIsOnMainThread();
1484
0
  MOZ_DIAGNOSTIC_ASSERT(!mLoadInfo.mCSP);
1485
0
1486
0
  NS_ConvertASCIItoUTF16 cspHeaderValue(aCSPHeaderValue);
1487
0
  NS_ConvertASCIItoUTF16 cspROHeaderValue(aCSPReportOnlyHeaderValue);
1488
0
1489
0
  nsCOMPtr<nsIContentSecurityPolicy> csp;
1490
0
  nsresult rv = mLoadInfo.mPrincipal->EnsureCSP(nullptr, getter_AddRefs(csp));
1491
0
  if (!csp) {
1492
0
    return NS_OK;
1493
0
  }
1494
0
1495
0
  csp->EnsureEventTarget(mMainThreadEventTarget);
1496
0
  csp->SetEventListener(mCSPEventListener);
1497
0
1498
0
  // If there's a CSP header, apply it.
1499
0
  if (!cspHeaderValue.IsEmpty()) {
1500
0
    rv = CSP_AppendCSPFromHeader(csp, cspHeaderValue, false);
1501
0
    NS_ENSURE_SUCCESS(rv, rv);
1502
0
  }
1503
0
  // If there's a report-only CSP header, apply it.
1504
0
  if (!cspROHeaderValue.IsEmpty()) {
1505
0
    rv = CSP_AppendCSPFromHeader(csp, cspROHeaderValue, true);
1506
0
    NS_ENSURE_SUCCESS(rv, rv);
1507
0
  }
1508
0
1509
0
  // Set evalAllowed, default value is set in GetAllowsEval
1510
0
  bool evalAllowed = false;
1511
0
  bool reportEvalViolations = false;
1512
0
  rv = csp->GetAllowsEval(&reportEvalViolations, &evalAllowed);
1513
0
  NS_ENSURE_SUCCESS(rv, rv);
1514
0
1515
0
  mLoadInfo.mCSP = csp;
1516
0
  mLoadInfo.mEvalAllowed = evalAllowed;
1517
0
  mLoadInfo.mReportCSPViolations = reportEvalViolations;
1518
0
1519
0
  return NS_OK;
1520
0
}
1521
1522
void
1523
WorkerPrivate::SetReferrerPolicyFromHeaderValue(const nsACString& aReferrerPolicyHeaderValue)
1524
0
{
1525
0
  NS_ConvertUTF8toUTF16 headerValue(aReferrerPolicyHeaderValue);
1526
0
1527
0
  if (headerValue.IsEmpty()) {
1528
0
    return;
1529
0
  }
1530
0
1531
0
  net::ReferrerPolicy policy =
1532
0
    nsContentUtils::GetReferrerPolicyFromHeader(headerValue);
1533
0
  if (policy == net::RP_Unset) {
1534
0
    return;
1535
0
  }
1536
0
1537
0
  SetReferrerPolicy(policy);
1538
0
}
1539
1540
void
1541
WorkerPrivate::Traverse(nsCycleCollectionTraversalCallback& aCb)
1542
0
{
1543
0
  AssertIsOnParentThread();
1544
0
1545
0
  // The WorkerPrivate::mParentEventTargetRef has a reference to the exposed
1546
0
  // Worker object, which is really held by the worker thread.  We traverse this
1547
0
  // reference if and only if our busy count is zero and we have not released
1548
0
  // the main thread reference.  We do not unlink it.  This allows the CC to
1549
0
  // break cycles involving the Worker and begin shutting it down (which does
1550
0
  // happen in unlink) but ensures that the WorkerPrivate won't be deleted
1551
0
  // before we're done shutting down the thread.
1552
0
  if (!mBusyCount && !mMainThreadObjectsForgotten) {
1553
0
    nsCycleCollectionTraversalCallback& cb = aCb;
1554
0
    WorkerPrivate* tmp = this;
1555
0
    NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParentEventTargetRef);
1556
0
  }
1557
0
}
1558
1559
nsresult
1560
WorkerPrivate::Dispatch(already_AddRefed<WorkerRunnable> aRunnable,
1561
                        nsIEventTarget* aSyncLoopTarget)
1562
0
{
1563
0
  // May be called on any thread!
1564
0
  RefPtr<WorkerRunnable> runnable(aRunnable);
1565
0
1566
0
  {
1567
0
    MutexAutoLock lock(mMutex);
1568
0
1569
0
    MOZ_ASSERT_IF(aSyncLoopTarget, mThread);
1570
0
1571
0
    if (!mThread) {
1572
0
      if (ParentStatus() == Pending || mStatus == Pending) {
1573
0
        mPreStartRunnables.AppendElement(runnable);
1574
0
        return NS_OK;
1575
0
      }
1576
0
1577
0
      NS_WARNING("Using a worker event target after the thread has already"
1578
0
                 "been released!");
1579
0
      return NS_ERROR_UNEXPECTED;
1580
0
    }
1581
0
1582
0
    if (mStatus == Dead ||
1583
0
        (!aSyncLoopTarget && ParentStatus() > Running)) {
1584
0
      NS_WARNING("A runnable was posted to a worker that is already shutting "
1585
0
                 "down!");
1586
0
      return NS_ERROR_UNEXPECTED;
1587
0
    }
1588
0
1589
0
    nsresult rv;
1590
0
    if (aSyncLoopTarget) {
1591
0
      rv = aSyncLoopTarget->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
1592
0
    } else {
1593
0
      rv = mThread->DispatchAnyThread(WorkerThreadFriendKey(), runnable.forget());
1594
0
    }
1595
0
1596
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
1597
0
      return rv;
1598
0
    }
1599
0
1600
0
    mCondVar.Notify();
1601
0
  }
1602
0
1603
0
  return NS_OK;
1604
0
}
1605
1606
void
1607
WorkerPrivate::EnableDebugger()
1608
0
{
1609
0
  AssertIsOnParentThread();
1610
0
1611
0
  if (NS_FAILED(RegisterWorkerDebugger(this))) {
1612
0
    NS_WARNING("Failed to register worker debugger!");
1613
0
    return;
1614
0
  }
1615
0
}
1616
1617
void
1618
WorkerPrivate::DisableDebugger()
1619
0
{
1620
0
  AssertIsOnParentThread();
1621
0
1622
0
  if (NS_FAILED(UnregisterWorkerDebugger(this))) {
1623
0
    NS_WARNING("Failed to unregister worker debugger!");
1624
0
  }
1625
0
}
1626
1627
nsresult
1628
WorkerPrivate::DispatchControlRunnable(already_AddRefed<WorkerControlRunnable> aWorkerControlRunnable)
1629
0
{
1630
0
  // May be called on any thread!
1631
0
  RefPtr<WorkerControlRunnable> runnable(aWorkerControlRunnable);
1632
0
  MOZ_ASSERT(runnable);
1633
0
1634
0
  {
1635
0
    MutexAutoLock lock(mMutex);
1636
0
1637
0
    if (mStatus == Dead) {
1638
0
      return NS_ERROR_UNEXPECTED;
1639
0
    }
1640
0
1641
0
    // Transfer ownership to the control queue.
1642
0
    mControlQueue.Push(runnable.forget().take());
1643
0
1644
0
    if (JSContext* cx = mJSContext) {
1645
0
      MOZ_ASSERT(mThread);
1646
0
      JS_RequestInterruptCallback(cx);
1647
0
    }
1648
0
1649
0
    mCondVar.Notify();
1650
0
  }
1651
0
1652
0
  return NS_OK;
1653
0
}
1654
1655
nsresult
1656
WorkerPrivate::DispatchDebuggerRunnable(already_AddRefed<WorkerRunnable> aDebuggerRunnable)
1657
0
{
1658
0
  // May be called on any thread!
1659
0
1660
0
  RefPtr<WorkerRunnable> runnable(aDebuggerRunnable);
1661
0
1662
0
  MOZ_ASSERT(runnable);
1663
0
1664
0
  {
1665
0
    MutexAutoLock lock(mMutex);
1666
0
1667
0
    if (mStatus == Dead) {
1668
0
      NS_WARNING("A debugger runnable was posted to a worker that is already "
1669
0
                 "shutting down!");
1670
0
      return NS_ERROR_UNEXPECTED;
1671
0
    }
1672
0
1673
0
    // Transfer ownership to the debugger queue.
1674
0
    mDebuggerQueue.Push(runnable.forget().take());
1675
0
1676
0
    mCondVar.Notify();
1677
0
  }
1678
0
1679
0
  return NS_OK;
1680
0
}
1681
1682
already_AddRefed<WorkerRunnable>
1683
WorkerPrivate::MaybeWrapAsWorkerRunnable(already_AddRefed<nsIRunnable> aRunnable)
1684
0
{
1685
0
  // May be called on any thread!
1686
0
1687
0
  nsCOMPtr<nsIRunnable> runnable(aRunnable);
1688
0
  MOZ_ASSERT(runnable);
1689
0
1690
0
  RefPtr<WorkerRunnable> workerRunnable =
1691
0
    WorkerRunnable::FromRunnable(runnable);
1692
0
  if (workerRunnable) {
1693
0
    return workerRunnable.forget();
1694
0
  }
1695
0
1696
0
  nsCOMPtr<nsICancelableRunnable> cancelable = do_QueryInterface(runnable);
1697
0
  if (!cancelable) {
1698
0
    MOZ_CRASH("All runnables destined for a worker thread must be cancelable!");
1699
0
  }
1700
0
1701
0
  workerRunnable = new ExternalRunnableWrapper(this, runnable);
1702
0
  return workerRunnable.forget();
1703
0
}
1704
1705
bool
1706
WorkerPrivate::Start()
1707
0
{
1708
0
  // May be called on any thread!
1709
0
  {
1710
0
    MutexAutoLock lock(mMutex);
1711
0
    NS_ASSERTION(mParentStatus != Running, "How can this be?!");
1712
0
1713
0
    if (mParentStatus == Pending) {
1714
0
      mParentStatus = Running;
1715
0
      return true;
1716
0
    }
1717
0
  }
1718
0
1719
0
  return false;
1720
0
}
1721
1722
// aCx is null when called from the finalizer
1723
bool
1724
WorkerPrivate::Notify(WorkerStatus aStatus)
1725
0
{
1726
0
  AssertIsOnParentThread();
1727
0
1728
0
  bool pending;
1729
0
  {
1730
0
    MutexAutoLock lock(mMutex);
1731
0
1732
0
    if (mParentStatus >= aStatus) {
1733
0
      return true;
1734
0
    }
1735
0
1736
0
    pending = mParentStatus == Pending;
1737
0
    mParentStatus = aStatus;
1738
0
  }
1739
0
1740
0
  if (IsSharedWorker()) {
1741
0
    RuntimeService* runtime = RuntimeService::GetService();
1742
0
    MOZ_ASSERT(runtime);
1743
0
1744
0
    runtime->ForgetSharedWorker(this);
1745
0
  }
1746
0
1747
0
  if (pending) {
1748
#ifdef DEBUG
1749
    {
1750
      // Fake a thread here just so that our assertions don't go off for no
1751
      // reason.
1752
      nsIThread* currentThread = NS_GetCurrentThread();
1753
      MOZ_ASSERT(currentThread);
1754
1755
      MOZ_ASSERT(!mPRThread);
1756
      mPRThread = PRThreadFromThread(currentThread);
1757
      MOZ_ASSERT(mPRThread);
1758
    }
1759
#endif
1760
1761
0
    // Worker never got a chance to run, go ahead and delete it.
1762
0
    ScheduleDeletion(WorkerPrivate::WorkerNeverRan);
1763
0
    return true;
1764
0
  }
1765
0
1766
0
  NS_ASSERTION(aStatus != Canceling || mQueuedRunnables.IsEmpty(),
1767
0
               "Shouldn't have anything queued!");
1768
0
1769
0
  // Anything queued will be discarded.
1770
0
  mQueuedRunnables.Clear();
1771
0
1772
0
  // No Canceling timeout is needed.
1773
0
  if (mCancelingTimer) {
1774
0
    mCancelingTimer->Cancel();
1775
0
    mCancelingTimer = nullptr;
1776
0
  }
1777
0
1778
0
  RefPtr<NotifyRunnable> runnable = new NotifyRunnable(this, aStatus);
1779
0
  return runnable->Dispatch();
1780
0
}
1781
1782
bool
1783
WorkerPrivate::Freeze(nsPIDOMWindowInner* aWindow)
1784
0
{
1785
0
  AssertIsOnParentThread();
1786
0
1787
0
  // Shared workers are only frozen if all of their owning documents are
1788
0
  // frozen. It can happen that mSharedWorkers is empty but this thread has
1789
0
  // not been unregistered yet.
1790
0
  if ((IsSharedWorker() || IsServiceWorker()) && !mSharedWorkers.IsEmpty()) {
1791
0
    AssertIsOnMainThread();
1792
0
1793
0
    bool allFrozen = true;
1794
0
1795
0
    for (uint32_t i = 0; i < mSharedWorkers.Length(); ++i) {
1796
0
      if (aWindow && mSharedWorkers[i]->GetOwner() == aWindow) {
1797
0
        // Calling Freeze() may change the refcount, ensure that the worker
1798
0
        // outlives this call.
1799
0
        RefPtr<SharedWorker> kungFuDeathGrip = mSharedWorkers[i];
1800
0
1801
0
        kungFuDeathGrip->Freeze();
1802
0
      } else {
1803
0
        MOZ_ASSERT_IF(mSharedWorkers[i]->GetOwner() && aWindow,
1804
0
                      !SameCOMIdentity(mSharedWorkers[i]->GetOwner(), aWindow));
1805
0
        if (!mSharedWorkers[i]->IsFrozen()) {
1806
0
          allFrozen = false;
1807
0
        }
1808
0
      }
1809
0
    }
1810
0
1811
0
    if (!allFrozen || mParentFrozen) {
1812
0
      return true;
1813
0
    }
1814
0
  }
1815
0
1816
0
  mParentFrozen = true;
1817
0
1818
0
  {
1819
0
    MutexAutoLock lock(mMutex);
1820
0
1821
0
    if (mParentStatus >= Canceling) {
1822
0
      return true;
1823
0
    }
1824
0
  }
1825
0
1826
0
  DisableDebugger();
1827
0
1828
0
  RefPtr<FreezeRunnable> runnable = new FreezeRunnable(this);
1829
0
  if (!runnable->Dispatch()) {
1830
0
    return false;
1831
0
  }
1832
0
1833
0
  return true;
1834
0
}
1835
1836
bool
1837
WorkerPrivate::Thaw(nsPIDOMWindowInner* aWindow)
1838
0
{
1839
0
  AssertIsOnParentThread();
1840
0
1841
0
  // Shared workers are resumed if any of their owning documents are thawed.
1842
0
  // It can happen that mSharedWorkers is empty but this thread has not been
1843
0
  // unregistered yet.
1844
0
  if ((IsSharedWorker() || IsServiceWorker()) && !mSharedWorkers.IsEmpty()) {
1845
0
    AssertIsOnMainThread();
1846
0
1847
0
    bool anyRunning = false;
1848
0
1849
0
    for (uint32_t i = 0; i < mSharedWorkers.Length(); ++i) {
1850
0
      if (aWindow && mSharedWorkers[i]->GetOwner() == aWindow) {
1851
0
        // Calling Thaw() may change the refcount, ensure that the worker
1852
0
        // outlives this call.
1853
0
        RefPtr<SharedWorker> kungFuDeathGrip = mSharedWorkers[i];
1854
0
1855
0
        kungFuDeathGrip->Thaw();
1856
0
        anyRunning = true;
1857
0
      } else {
1858
0
        MOZ_ASSERT_IF(mSharedWorkers[i]->GetOwner() && aWindow,
1859
0
                      !SameCOMIdentity(mSharedWorkers[i]->GetOwner(), aWindow));
1860
0
        if (!mSharedWorkers[i]->IsFrozen()) {
1861
0
          anyRunning = true;
1862
0
        }
1863
0
      }
1864
0
    }
1865
0
1866
0
    if (!anyRunning || !mParentFrozen) {
1867
0
      return true;
1868
0
    }
1869
0
  }
1870
0
1871
0
  MOZ_ASSERT(mParentFrozen);
1872
0
1873
0
  mParentFrozen = false;
1874
0
1875
0
  {
1876
0
    MutexAutoLock lock(mMutex);
1877
0
1878
0
    if (mParentStatus >= Canceling) {
1879
0
      return true;
1880
0
    }
1881
0
  }
1882
0
1883
0
  EnableDebugger();
1884
0
1885
0
  // Execute queued runnables before waking up the worker, otherwise the worker
1886
0
  // could post new messages before we run those that have been queued.
1887
0
  if (!IsParentWindowPaused() && !mQueuedRunnables.IsEmpty()) {
1888
0
    MOZ_ASSERT(IsDedicatedWorker());
1889
0
1890
0
    nsTArray<nsCOMPtr<nsIRunnable>> runnables;
1891
0
    mQueuedRunnables.SwapElements(runnables);
1892
0
1893
0
    for (uint32_t index = 0; index < runnables.Length(); index++) {
1894
0
      runnables[index]->Run();
1895
0
    }
1896
0
  }
1897
0
1898
0
  RefPtr<ThawRunnable> runnable = new ThawRunnable(this);
1899
0
  if (!runnable->Dispatch()) {
1900
0
    return false;
1901
0
  }
1902
0
1903
0
  return true;
1904
0
}
1905
1906
void
1907
WorkerPrivate::ParentWindowPaused()
1908
0
{
1909
0
  AssertIsOnMainThread();
1910
0
  MOZ_ASSERT_IF(IsDedicatedWorker(), mParentWindowPausedDepth == 0);
1911
0
  mParentWindowPausedDepth += 1;
1912
0
}
1913
1914
void
1915
WorkerPrivate::ParentWindowResumed()
1916
0
{
1917
0
  AssertIsOnMainThread();
1918
0
1919
0
  MOZ_ASSERT(mParentWindowPausedDepth > 0);
1920
0
  MOZ_ASSERT_IF(IsDedicatedWorker(), mParentWindowPausedDepth == 1);
1921
0
  mParentWindowPausedDepth -= 1;
1922
0
  if (mParentWindowPausedDepth > 0) {
1923
0
    return;
1924
0
  }
1925
0
1926
0
  {
1927
0
    MutexAutoLock lock(mMutex);
1928
0
1929
0
    if (mParentStatus >= Canceling) {
1930
0
      return;
1931
0
    }
1932
0
  }
1933
0
1934
0
  // Execute queued runnables before waking up, otherwise the worker could post
1935
0
  // new messages before we run those that have been queued.
1936
0
  if (!IsFrozen() && !mQueuedRunnables.IsEmpty()) {
1937
0
    MOZ_ASSERT(IsDedicatedWorker());
1938
0
1939
0
    nsTArray<nsCOMPtr<nsIRunnable>> runnables;
1940
0
    mQueuedRunnables.SwapElements(runnables);
1941
0
1942
0
    for (uint32_t index = 0; index < runnables.Length(); index++) {
1943
0
      runnables[index]->Run();
1944
0
    }
1945
0
  }
1946
0
}
1947
1948
void
1949
WorkerPrivate::PropagateFirstPartyStorageAccessGranted()
1950
0
{
1951
0
  AssertIsOnParentThread();
1952
0
1953
0
  {
1954
0
    MutexAutoLock lock(mMutex);
1955
0
1956
0
    if (mParentStatus >= Canceling) {
1957
0
      return;
1958
0
    }
1959
0
  }
1960
0
1961
0
  RefPtr<PropagateFirstPartyStorageAccessGrantedRunnable> runnable =
1962
0
    new PropagateFirstPartyStorageAccessGrantedRunnable(this);
1963
0
  Unused << NS_WARN_IF(!runnable->Dispatch());
1964
0
}
1965
1966
bool
1967
WorkerPrivate::Close()
1968
0
{
1969
0
  mMutex.AssertCurrentThreadOwns();
1970
0
  if (mParentStatus < Closing) {
1971
0
    mParentStatus = Closing;
1972
0
  }
1973
0
1974
0
  return true;
1975
0
}
1976
1977
bool
1978
WorkerPrivate::ModifyBusyCount(bool aIncrease)
1979
0
{
1980
0
  AssertIsOnParentThread();
1981
0
1982
0
  NS_ASSERTION(aIncrease || mBusyCount, "Mismatched busy count mods!");
1983
0
1984
0
  if (aIncrease) {
1985
0
    mBusyCount++;
1986
0
    return true;
1987
0
  }
1988
0
1989
0
  if (--mBusyCount == 0) {
1990
0
1991
0
    bool shouldCancel;
1992
0
    {
1993
0
      MutexAutoLock lock(mMutex);
1994
0
      shouldCancel = mParentStatus == Canceling;
1995
0
    }
1996
0
1997
0
    if (shouldCancel && !Cancel()) {
1998
0
      return false;
1999
0
    }
2000
0
  }
2001
0
2002
0
  return true;
2003
0
}
2004
2005
bool
2006
WorkerPrivate::ProxyReleaseMainThreadObjects()
2007
0
{
2008
0
  AssertIsOnParentThread();
2009
0
  MOZ_ASSERT(!mMainThreadObjectsForgotten);
2010
0
2011
0
  nsCOMPtr<nsILoadGroup> loadGroupToCancel;
2012
0
  // If we're not overriden, then do nothing here.  Let the load group get
2013
0
  // handled in ForgetMainThreadObjects().
2014
0
  if (mLoadInfo.mInterfaceRequestor) {
2015
0
    mLoadInfo.mLoadGroup.swap(loadGroupToCancel);
2016
0
  }
2017
0
2018
0
  bool result = mLoadInfo.ProxyReleaseMainThreadObjects(this, loadGroupToCancel);
2019
0
2020
0
  mMainThreadObjectsForgotten = true;
2021
0
2022
0
  return result;
2023
0
}
2024
2025
void
2026
WorkerPrivate::UpdateContextOptions(const JS::ContextOptions& aContextOptions)
2027
0
{
2028
0
  AssertIsOnParentThread();
2029
0
2030
0
  {
2031
0
    MutexAutoLock lock(mMutex);
2032
0
    mJSSettings.contextOptions = aContextOptions;
2033
0
  }
2034
0
2035
0
  RefPtr<UpdateContextOptionsRunnable> runnable =
2036
0
    new UpdateContextOptionsRunnable(this, aContextOptions);
2037
0
  if (!runnable->Dispatch()) {
2038
0
    NS_WARNING("Failed to update worker context options!");
2039
0
  }
2040
0
}
2041
2042
void
2043
WorkerPrivate::UpdateLanguages(const nsTArray<nsString>& aLanguages)
2044
0
{
2045
0
  AssertIsOnParentThread();
2046
0
2047
0
  RefPtr<UpdateLanguagesRunnable> runnable =
2048
0
    new UpdateLanguagesRunnable(this, aLanguages);
2049
0
  if (!runnable->Dispatch()) {
2050
0
    NS_WARNING("Failed to update worker languages!");
2051
0
  }
2052
0
}
2053
2054
void
2055
WorkerPrivate::UpdateJSWorkerMemoryParameter(JSGCParamKey aKey, uint32_t aValue)
2056
0
{
2057
0
  AssertIsOnParentThread();
2058
0
2059
0
  bool found = false;
2060
0
2061
0
  {
2062
0
    MutexAutoLock lock(mMutex);
2063
0
    found = mJSSettings.ApplyGCSetting(aKey, aValue);
2064
0
  }
2065
0
2066
0
  if (found) {
2067
0
    RefPtr<UpdateJSWorkerMemoryParameterRunnable> runnable =
2068
0
      new UpdateJSWorkerMemoryParameterRunnable(this, aKey, aValue);
2069
0
    if (!runnable->Dispatch()) {
2070
0
      NS_WARNING("Failed to update memory parameter!");
2071
0
    }
2072
0
  }
2073
0
}
2074
2075
#ifdef JS_GC_ZEAL
2076
void
2077
WorkerPrivate::UpdateGCZeal(uint8_t aGCZeal, uint32_t aFrequency)
2078
{
2079
  AssertIsOnParentThread();
2080
2081
  {
2082
    MutexAutoLock lock(mMutex);
2083
    mJSSettings.gcZeal = aGCZeal;
2084
    mJSSettings.gcZealFrequency = aFrequency;
2085
  }
2086
2087
  RefPtr<UpdateGCZealRunnable> runnable =
2088
    new UpdateGCZealRunnable(this, aGCZeal, aFrequency);
2089
  if (!runnable->Dispatch()) {
2090
    NS_WARNING("Failed to update worker gczeal!");
2091
  }
2092
}
2093
#endif
2094
2095
void
2096
WorkerPrivate::GarbageCollect(bool aShrinking)
2097
0
{
2098
0
  AssertIsOnParentThread();
2099
0
2100
0
  RefPtr<GarbageCollectRunnable> runnable =
2101
0
    new GarbageCollectRunnable(this, aShrinking, /* collectChildren = */ true);
2102
0
  if (!runnable->Dispatch()) {
2103
0
    NS_WARNING("Failed to GC worker!");
2104
0
  }
2105
0
}
2106
2107
void
2108
WorkerPrivate::CycleCollect(bool aDummy)
2109
0
{
2110
0
  AssertIsOnParentThread();
2111
0
2112
0
  RefPtr<CycleCollectRunnable> runnable =
2113
0
    new CycleCollectRunnable(this, /* collectChildren = */ true);
2114
0
  if (!runnable->Dispatch()) {
2115
0
    NS_WARNING("Failed to CC worker!");
2116
0
  }
2117
0
}
2118
2119
void
2120
WorkerPrivate::OfflineStatusChangeEvent(bool aIsOffline)
2121
0
{
2122
0
  AssertIsOnParentThread();
2123
0
2124
0
  RefPtr<OfflineStatusChangeRunnable> runnable =
2125
0
    new OfflineStatusChangeRunnable(this, aIsOffline);
2126
0
  if (!runnable->Dispatch()) {
2127
0
    NS_WARNING("Failed to dispatch offline status change event!");
2128
0
  }
2129
0
}
2130
2131
void
2132
WorkerPrivate::OfflineStatusChangeEventInternal(bool aIsOffline)
2133
0
{
2134
0
  AssertIsOnWorkerThread();
2135
0
2136
0
  // The worker is already in this state. No need to dispatch an event.
2137
0
  if (mOnLine == !aIsOffline) {
2138
0
    return;
2139
0
  }
2140
0
2141
0
  for (uint32_t index = 0; index < mChildWorkers.Length(); ++index) {
2142
0
    mChildWorkers[index]->OfflineStatusChangeEvent(aIsOffline);
2143
0
  }
2144
0
2145
0
  mOnLine = !aIsOffline;
2146
0
  WorkerGlobalScope* globalScope = GlobalScope();
2147
0
  RefPtr<WorkerNavigator> nav = globalScope->GetExistingNavigator();
2148
0
  if (nav) {
2149
0
    nav->SetOnLine(mOnLine);
2150
0
  }
2151
0
2152
0
  nsString eventType;
2153
0
  if (aIsOffline) {
2154
0
    eventType.AssignLiteral("offline");
2155
0
  } else {
2156
0
    eventType.AssignLiteral("online");
2157
0
  }
2158
0
2159
0
  RefPtr<Event> event = NS_NewDOMEvent(globalScope, nullptr, nullptr);
2160
0
2161
0
  event->InitEvent(eventType, false, false);
2162
0
  event->SetTrusted(true);
2163
0
2164
0
  globalScope->DispatchEvent(*event);
2165
0
}
2166
2167
void
2168
WorkerPrivate::MemoryPressure(bool aDummy)
2169
0
{
2170
0
  AssertIsOnParentThread();
2171
0
2172
0
  RefPtr<MemoryPressureRunnable> runnable = new MemoryPressureRunnable(this);
2173
0
  Unused << NS_WARN_IF(!runnable->Dispatch());
2174
0
}
2175
2176
bool
2177
WorkerPrivate::RegisterSharedWorker(SharedWorker* aSharedWorker,
2178
                                    MessagePort* aPort)
2179
0
{
2180
0
  AssertIsOnMainThread();
2181
0
  MOZ_ASSERT(aSharedWorker);
2182
0
  MOZ_ASSERT(IsSharedWorker());
2183
0
  MOZ_ASSERT(!mSharedWorkers.Contains(aSharedWorker));
2184
0
2185
0
  if (IsSharedWorker()) {
2186
0
    RefPtr<MessagePortRunnable> runnable = new MessagePortRunnable(this, aPort);
2187
0
    if (!runnable->Dispatch()) {
2188
0
      return false;
2189
0
    }
2190
0
  }
2191
0
2192
0
  mSharedWorkers.AppendElement(aSharedWorker);
2193
0
2194
0
  // If there were other SharedWorker objects attached to this worker then they
2195
0
  // may all have been frozen and this worker would need to be thawed.
2196
0
  if (mSharedWorkers.Length() > 1 && IsFrozen() && !Thaw(nullptr)) {
2197
0
    return false;
2198
0
  }
2199
0
2200
0
  return true;
2201
0
}
2202
2203
void
2204
WorkerPrivate::BroadcastErrorToSharedWorkers(
2205
                                     JSContext* aCx,
2206
                                     const WorkerErrorReport* aReport,
2207
                                     bool aIsErrorEvent)
2208
0
{
2209
0
  AssertIsOnMainThread();
2210
0
2211
0
  if (aIsErrorEvent && JSREPORT_IS_WARNING(aReport->mFlags)) {
2212
0
    // Don't fire any events anywhere.  Just log to console.
2213
0
    // XXXbz should we log to all the consoles of all the relevant windows?
2214
0
    MOZ_ASSERT(aReport);
2215
0
    WorkerErrorReport::LogErrorToConsole(*aReport, 0);
2216
0
    return;
2217
0
  }
2218
0
2219
0
  AutoTArray<RefPtr<SharedWorker>, 10> sharedWorkers;
2220
0
  GetAllSharedWorkers(sharedWorkers);
2221
0
2222
0
  if (sharedWorkers.IsEmpty()) {
2223
0
    return;
2224
0
  }
2225
0
2226
0
  AutoTArray<WindowAction, 10> windowActions;
2227
0
2228
0
  // First fire the error event at all SharedWorker objects. This may include
2229
0
  // multiple objects in a single window as well as objects in different
2230
0
  // windows.
2231
0
  for (size_t index = 0; index < sharedWorkers.Length(); index++) {
2232
0
    RefPtr<SharedWorker>& sharedWorker = sharedWorkers[index];
2233
0
2234
0
    // May be null.
2235
0
    nsPIDOMWindowInner* window = sharedWorker->GetOwner();
2236
0
2237
0
    RefPtr<Event> event;
2238
0
2239
0
    if (aIsErrorEvent) {
2240
0
      RootedDictionary<ErrorEventInit> errorInit(aCx);
2241
0
      errorInit.mBubbles = false;
2242
0
      errorInit.mCancelable = true;
2243
0
      errorInit.mMessage = aReport->mMessage;
2244
0
      errorInit.mFilename = aReport->mFilename;
2245
0
      errorInit.mLineno = aReport->mLineNumber;
2246
0
      errorInit.mColno = aReport->mColumnNumber;
2247
0
2248
0
      event = ErrorEvent::Constructor(sharedWorker, NS_LITERAL_STRING("error"),
2249
0
                                      errorInit);
2250
0
    } else {
2251
0
      event = Event::Constructor(sharedWorker, NS_LITERAL_STRING("error"),
2252
0
                                 EventInit());
2253
0
    }
2254
0
2255
0
    if (!event) {
2256
0
      ThrowAndReport(window, NS_ERROR_UNEXPECTED);
2257
0
      continue;
2258
0
    }
2259
0
2260
0
    event->SetTrusted(true);
2261
0
2262
0
    ErrorResult res;
2263
0
    bool defaultActionEnabled =
2264
0
      sharedWorker->DispatchEvent(*event, CallerType::System, res);
2265
0
    if (res.Failed()) {
2266
0
      ThrowAndReport(window, res.StealNSResult());
2267
0
      continue;
2268
0
    }
2269
0
2270
0
    if (!aIsErrorEvent) {
2271
0
      continue;
2272
0
    }
2273
0
2274
0
    if (defaultActionEnabled) {
2275
0
      // Add the owning window to our list so that we will fire an error event
2276
0
      // at it later.
2277
0
      if (!windowActions.Contains(window)) {
2278
0
        windowActions.AppendElement(WindowAction(window));
2279
0
      }
2280
0
    } else {
2281
0
      size_t actionsIndex = windowActions.LastIndexOf(WindowAction(window));
2282
0
      if (actionsIndex != windowActions.NoIndex) {
2283
0
        // Any listener that calls preventDefault() will prevent the window from
2284
0
        // receiving the error event.
2285
0
        windowActions[actionsIndex].mDefaultAction = false;
2286
0
      }
2287
0
    }
2288
0
  }
2289
0
2290
0
  // If there are no windows to consider further then we're done.
2291
0
  if (windowActions.IsEmpty()) {
2292
0
    return;
2293
0
  }
2294
0
2295
0
  bool shouldLogErrorToConsole = true;
2296
0
2297
0
  // Now fire error events at all the windows remaining.
2298
0
  for (uint32_t index = 0; index < windowActions.Length(); index++) {
2299
0
    WindowAction& windowAction = windowActions[index];
2300
0
2301
0
    // If there is no window or the script already called preventDefault then
2302
0
    // skip this window.
2303
0
    if (!windowAction.mWindow || !windowAction.mDefaultAction) {
2304
0
      continue;
2305
0
    }
2306
0
2307
0
    nsCOMPtr<nsIScriptGlobalObject> sgo =
2308
0
      do_QueryInterface(windowAction.mWindow);
2309
0
    MOZ_ASSERT(sgo);
2310
0
2311
0
    MOZ_ASSERT(NS_IsMainThread());
2312
0
    RootedDictionary<ErrorEventInit> init(aCx);
2313
0
    init.mLineno = aReport->mLineNumber;
2314
0
    init.mFilename = aReport->mFilename;
2315
0
    init.mMessage = aReport->mMessage;
2316
0
    init.mCancelable = true;
2317
0
    init.mBubbles = true;
2318
0
2319
0
    nsEventStatus status = nsEventStatus_eIgnore;
2320
0
    if (!sgo->HandleScriptError(init, &status)) {
2321
0
      ThrowAndReport(windowAction.mWindow, NS_ERROR_UNEXPECTED);
2322
0
      continue;
2323
0
    }
2324
0
2325
0
    if (status == nsEventStatus_eConsumeNoDefault) {
2326
0
      shouldLogErrorToConsole = false;
2327
0
    }
2328
0
  }
2329
0
2330
0
  // Finally log a warning in the console if no window tried to prevent it.
2331
0
  if (shouldLogErrorToConsole) {
2332
0
    MOZ_ASSERT(aReport);
2333
0
    WorkerErrorReport::LogErrorToConsole(*aReport, 0);
2334
0
  }
2335
0
}
2336
2337
void
2338
WorkerPrivate::GetAllSharedWorkers(nsTArray<RefPtr<SharedWorker>>& aSharedWorkers)
2339
0
{
2340
0
  AssertIsOnMainThread();
2341
0
  MOZ_ASSERT(IsSharedWorker() || IsServiceWorker());
2342
0
2343
0
  if (!aSharedWorkers.IsEmpty()) {
2344
0
    aSharedWorkers.Clear();
2345
0
  }
2346
0
2347
0
  for (uint32_t i = 0; i < mSharedWorkers.Length(); ++i) {
2348
0
    aSharedWorkers.AppendElement(mSharedWorkers[i]);
2349
0
  }
2350
0
}
2351
2352
void
2353
WorkerPrivate::CloseSharedWorkersForWindow(nsPIDOMWindowInner* aWindow)
2354
0
{
2355
0
  AssertIsOnMainThread();
2356
0
  MOZ_ASSERT(IsSharedWorker() || IsServiceWorker());
2357
0
  MOZ_ASSERT(aWindow);
2358
0
2359
0
  bool someRemoved = false;
2360
0
2361
0
  for (uint32_t i = 0; i < mSharedWorkers.Length();) {
2362
0
    if (mSharedWorkers[i]->GetOwner() == aWindow) {
2363
0
      mSharedWorkers[i]->Close();
2364
0
      mSharedWorkers.RemoveElementAt(i);
2365
0
      someRemoved = true;
2366
0
    } else {
2367
0
      MOZ_ASSERT(!SameCOMIdentity(mSharedWorkers[i]->GetOwner(), aWindow));
2368
0
      ++i;
2369
0
    }
2370
0
  }
2371
0
2372
0
  if (!someRemoved) {
2373
0
    return;
2374
0
  }
2375
0
2376
0
  // If there are still SharedWorker objects attached to this worker then they
2377
0
  // may all be frozen and this worker would need to be frozen. Otherwise,
2378
0
  // if that was the last SharedWorker then it's time to cancel this worker.
2379
0
2380
0
  if (!mSharedWorkers.IsEmpty()) {
2381
0
    Freeze(nullptr);
2382
0
  } else {
2383
0
    Cancel();
2384
0
  }
2385
0
}
2386
2387
void
2388
WorkerPrivate::CloseAllSharedWorkers()
2389
0
{
2390
0
  AssertIsOnMainThread();
2391
0
  MOZ_ASSERT(IsSharedWorker() || IsServiceWorker());
2392
0
2393
0
  for (uint32_t i = 0; i < mSharedWorkers.Length(); ++i) {
2394
0
    mSharedWorkers[i]->Close();
2395
0
  }
2396
0
2397
0
  mSharedWorkers.Clear();
2398
0
2399
0
  Cancel();
2400
0
}
2401
2402
void
2403
WorkerPrivate::WorkerScriptLoaded()
2404
0
{
2405
0
  AssertIsOnMainThread();
2406
0
2407
0
  if (IsSharedWorker() || IsServiceWorker()) {
2408
0
    // No longer need to hold references to the window or document we came from.
2409
0
    mLoadInfo.mWindow = nullptr;
2410
0
    mLoadInfo.mScriptContext = nullptr;
2411
0
  }
2412
0
}
2413
2414
void
2415
WorkerPrivate::SetBaseURI(nsIURI* aBaseURI)
2416
0
{
2417
0
  AssertIsOnMainThread();
2418
0
2419
0
  if (!mLoadInfo.mBaseURI) {
2420
0
    NS_ASSERTION(GetParent(), "Shouldn't happen without a parent!");
2421
0
    mLoadInfo.mResolvedScriptURI = aBaseURI;
2422
0
  }
2423
0
2424
0
  mLoadInfo.mBaseURI = aBaseURI;
2425
0
2426
0
  if (NS_FAILED(aBaseURI->GetSpec(mLocationInfo.mHref))) {
2427
0
    mLocationInfo.mHref.Truncate();
2428
0
  }
2429
0
2430
0
  mLocationInfo.mHostname.Truncate();
2431
0
  nsContentUtils::GetHostOrIPv6WithBrackets(aBaseURI, mLocationInfo.mHostname);
2432
0
2433
0
  nsCOMPtr<nsIURL> url(do_QueryInterface(aBaseURI));
2434
0
  if (!url || NS_FAILED(url->GetFilePath(mLocationInfo.mPathname))) {
2435
0
    mLocationInfo.mPathname.Truncate();
2436
0
  }
2437
0
2438
0
  nsCString temp;
2439
0
2440
0
  if (url && NS_SUCCEEDED(url->GetQuery(temp)) && !temp.IsEmpty()) {
2441
0
    mLocationInfo.mSearch.Assign('?');
2442
0
    mLocationInfo.mSearch.Append(temp);
2443
0
  }
2444
0
2445
0
  if (NS_SUCCEEDED(aBaseURI->GetRef(temp)) && !temp.IsEmpty()) {
2446
0
    if (mLocationInfo.mHash.IsEmpty()) {
2447
0
      mLocationInfo.mHash.Assign('#');
2448
0
      mLocationInfo.mHash.Append(temp);
2449
0
    }
2450
0
  }
2451
0
2452
0
  if (NS_SUCCEEDED(aBaseURI->GetScheme(mLocationInfo.mProtocol))) {
2453
0
    mLocationInfo.mProtocol.Append(':');
2454
0
  }
2455
0
  else {
2456
0
    mLocationInfo.mProtocol.Truncate();
2457
0
  }
2458
0
2459
0
  int32_t port;
2460
0
  if (NS_SUCCEEDED(aBaseURI->GetPort(&port)) && port != -1) {
2461
0
    mLocationInfo.mPort.AppendInt(port);
2462
0
2463
0
    nsAutoCString host(mLocationInfo.mHostname);
2464
0
    host.Append(':');
2465
0
    host.Append(mLocationInfo.mPort);
2466
0
2467
0
    mLocationInfo.mHost.Assign(host);
2468
0
  }
2469
0
  else {
2470
0
    mLocationInfo.mHost.Assign(mLocationInfo.mHostname);
2471
0
  }
2472
0
2473
0
  nsContentUtils::GetUTFOrigin(aBaseURI, mLocationInfo.mOrigin);
2474
0
}
2475
2476
nsresult
2477
WorkerPrivate::SetPrincipalOnMainThread(nsIPrincipal* aPrincipal,
2478
                                        nsILoadGroup* aLoadGroup)
2479
0
{
2480
0
  return mLoadInfo.SetPrincipalOnMainThread(aPrincipal, aLoadGroup);
2481
0
}
2482
2483
nsresult
2484
WorkerPrivate::SetPrincipalFromChannel(nsIChannel* aChannel)
2485
0
{
2486
0
  return mLoadInfo.SetPrincipalFromChannel(aChannel);
2487
0
}
2488
2489
bool
2490
WorkerPrivate::FinalChannelPrincipalIsValid(nsIChannel* aChannel)
2491
0
{
2492
0
  return mLoadInfo.FinalChannelPrincipalIsValid(aChannel);
2493
0
}
2494
2495
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
2496
bool
2497
WorkerPrivate::PrincipalURIMatchesScriptURL()
2498
0
{
2499
0
  return mLoadInfo.PrincipalURIMatchesScriptURL();
2500
0
}
2501
#endif
2502
2503
void
2504
WorkerPrivate::UpdateOverridenLoadGroup(nsILoadGroup* aBaseLoadGroup)
2505
0
{
2506
0
  AssertIsOnMainThread();
2507
0
2508
0
  // The load group should have been overriden at init time.
2509
0
  mLoadInfo.mInterfaceRequestor->MaybeAddTabChild(aBaseLoadGroup);
2510
0
}
2511
2512
void
2513
WorkerPrivate::FlushReportsToSharedWorkers(nsIConsoleReportCollector* aReporter)
2514
0
{
2515
0
  AssertIsOnMainThread();
2516
0
2517
0
  AutoTArray<RefPtr<SharedWorker>, 10> sharedWorkers;
2518
0
  AutoTArray<WindowAction, 10> windowActions;
2519
0
  GetAllSharedWorkers(sharedWorkers);
2520
0
2521
0
  // First find out all the shared workers' window.
2522
0
  for (size_t index = 0; index < sharedWorkers.Length(); index++) {
2523
0
    RefPtr<SharedWorker>& sharedWorker = sharedWorkers[index];
2524
0
2525
0
    // May be null.
2526
0
    nsPIDOMWindowInner* window = sharedWorker->GetOwner();
2527
0
2528
0
    // Add the owning window to our list so that we will flush the reports later.
2529
0
    if (window && !windowActions.Contains(window)) {
2530
0
      windowActions.AppendElement(WindowAction(window));
2531
0
    }
2532
0
  }
2533
0
2534
0
  bool reportErrorToBrowserConsole = true;
2535
0
2536
0
  // Flush the reports.
2537
0
  for (uint32_t index = 0; index < windowActions.Length(); index++) {
2538
0
    WindowAction& windowAction = windowActions[index];
2539
0
2540
0
    aReporter->FlushReportsToConsole(
2541
0
      windowAction.mWindow->WindowID(),
2542
0
      nsIConsoleReportCollector::ReportAction::Save);
2543
0
    reportErrorToBrowserConsole = false;
2544
0
  }
2545
0
2546
0
  // Finally report to browser console if there is no any window or shared
2547
0
  // worker.
2548
0
  if (reportErrorToBrowserConsole) {
2549
0
    aReporter->FlushReportsToConsole(0);
2550
0
    return;
2551
0
  }
2552
0
2553
0
  aReporter->ClearConsoleReports();
2554
0
}
2555
2556
#ifdef DEBUG
2557
2558
void
2559
WorkerPrivate::AssertIsOnParentThread() const
2560
{
2561
  if (GetParent()) {
2562
    GetParent()->AssertIsOnWorkerThread();
2563
  } else {
2564
    AssertIsOnMainThread();
2565
  }
2566
}
2567
2568
void
2569
WorkerPrivate::AssertInnerWindowIsCorrect() const
2570
{
2571
  AssertIsOnParentThread();
2572
2573
  // Only care about top level workers from windows.
2574
  if (mParent || !mLoadInfo.mWindow) {
2575
    return;
2576
  }
2577
2578
  AssertIsOnMainThread();
2579
2580
  nsPIDOMWindowOuter* outer = mLoadInfo.mWindow->GetOuterWindow();
2581
  NS_ASSERTION(outer && outer->GetCurrentInnerWindow() == mLoadInfo.mWindow,
2582
               "Inner window no longer correct!");
2583
}
2584
2585
#endif
2586
2587
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
2588
bool
2589
WorkerPrivate::PrincipalIsValid() const
2590
0
{
2591
0
  return mLoadInfo.PrincipalIsValid();
2592
0
}
2593
#endif
2594
2595
WorkerPrivate::WorkerPrivate(WorkerPrivate* aParent,
2596
                             const nsAString& aScriptURL,
2597
                             bool aIsChromeWorker, WorkerType aWorkerType,
2598
                             const nsAString& aWorkerName,
2599
                             const nsACString& aServiceWorkerScope,
2600
                             WorkerLoadInfo& aLoadInfo)
2601
  : mMutex("WorkerPrivate Mutex")
2602
  , mCondVar(mMutex, "WorkerPrivate CondVar")
2603
  , mParent(aParent)
2604
  , mScriptURL(aScriptURL)
2605
  , mWorkerName(aWorkerName)
2606
  , mWorkerType(aWorkerType)
2607
  , mDebugger(nullptr)
2608
  , mJSContext(nullptr)
2609
  , mPRThread(nullptr)
2610
  , mMainThreadEventTarget(GetMainThreadEventTarget())
2611
  , mWorkerControlEventTarget(new WorkerEventTarget(this,
2612
                                                    WorkerEventTarget::Behavior::ControlOnly))
2613
  , mWorkerHybridEventTarget(new WorkerEventTarget(this,
2614
                                                   WorkerEventTarget::Behavior::Hybrid))
2615
  , mParentStatus(Pending)
2616
  , mStatus(Pending)
2617
  , mBusyCount(0)
2618
  , mLoadingWorkerScript(false)
2619
  , mCreationTimeStamp(TimeStamp::Now())
2620
  , mCreationTimeHighRes((double)PR_Now() / PR_USEC_PER_MSEC)
2621
  , mNumHoldersPreventingShutdownStart(0)
2622
  , mDebuggerEventLoopLevel(0)
2623
  , mErrorHandlerRecursionCount(0)
2624
  , mNextTimeoutId(1)
2625
  , mParentWindowPausedDepth(0)
2626
  , mFrozen(false)
2627
  , mTimerRunning(false)
2628
  , mRunningExpiredTimeouts(false)
2629
  , mPendingEventQueueClearing(false)
2630
  , mCancelAllPendingRunnables(false)
2631
  , mPeriodicGCTimerRunning(false)
2632
  , mIdleGCTimerRunning(false)
2633
  , mWorkerScriptExecutedSuccessfully(false)
2634
  , mFetchHandlerWasAdded(false)
2635
  , mOnLine(false)
2636
  , mMainThreadObjectsForgotten(false)
2637
  , mIsChromeWorker(aIsChromeWorker)
2638
  , mParentFrozen(false)
2639
  , mIsSecureContext(false)
2640
  , mDebuggerRegistered(false)
2641
  , mIsInAutomation(false)
2642
  , mPerformanceCounter(nullptr)
2643
0
{
2644
0
  MOZ_ASSERT_IF(!IsDedicatedWorker(), NS_IsMainThread());
2645
0
  mLoadInfo.StealFrom(aLoadInfo);
2646
0
2647
0
  if (aParent) {
2648
0
    aParent->AssertIsOnWorkerThread();
2649
0
2650
0
    // Note that this copies our parent's secure context state into mJSSettings.
2651
0
    aParent->CopyJSSettings(mJSSettings);
2652
0
2653
0
    // And manually set our mIsSecureContext, though it's not really relevant to
2654
0
    // dedicated workers...
2655
0
    mIsSecureContext = aParent->IsSecureContext();
2656
0
    MOZ_ASSERT_IF(mIsChromeWorker, mIsSecureContext);
2657
0
2658
0
    mIsInAutomation = aParent->IsInAutomation();
2659
0
2660
0
    MOZ_ASSERT(IsDedicatedWorker());
2661
0
2662
0
    if (aParent->mParentFrozen) {
2663
0
      Freeze(nullptr);
2664
0
    }
2665
0
2666
0
    mOnLine = aParent->OnLine();
2667
0
  }
2668
0
  else {
2669
0
    AssertIsOnMainThread();
2670
0
2671
0
    RuntimeService::GetDefaultJSSettings(mJSSettings);
2672
0
2673
0
    // Our secure context state depends on the kind of worker we have.
2674
0
    if (UsesSystemPrincipal() || IsServiceWorker()) {
2675
0
      mIsSecureContext = true;
2676
0
    } else if (mLoadInfo.mWindow) {
2677
0
      // Shared and dedicated workers both inherit the loading window's secure
2678
0
      // context state.  Shared workers then prevent windows with a different
2679
0
      // secure context state from attaching to them.
2680
0
      mIsSecureContext = mLoadInfo.mWindow->IsSecureContext();
2681
0
    } else {
2682
0
      MOZ_ASSERT_UNREACHABLE("non-chrome worker that is not a service worker "
2683
0
                             "that has no parent and no associated window");
2684
0
    }
2685
0
2686
0
    if (mIsSecureContext) {
2687
0
      mJSSettings.chrome.realmOptions
2688
0
                 .creationOptions().setSecureContext(true);
2689
0
      mJSSettings.chrome.realmOptions
2690
0
                 .creationOptions().setClampAndJitterTime(false);
2691
0
      mJSSettings.content.realmOptions
2692
0
                 .creationOptions().setSecureContext(true);
2693
0
      mJSSettings.content.realmOptions
2694
0
                 .creationOptions().setClampAndJitterTime(false);
2695
0
    }
2696
0
2697
0
    mIsInAutomation = xpc::IsInAutomation();
2698
0
2699
0
    // Our parent can get suspended after it initiates the async creation
2700
0
    // of a new worker thread.  In this case suspend the new worker as well.
2701
0
    if (mLoadInfo.mWindow && mLoadInfo.mWindow->IsSuspended()) {
2702
0
      ParentWindowPaused();
2703
0
    }
2704
0
2705
0
    if (mLoadInfo.mWindow && mLoadInfo.mWindow->IsFrozen()) {
2706
0
      Freeze(mLoadInfo.mWindow);
2707
0
    }
2708
0
2709
0
    mOnLine = !NS_IsOffline();
2710
0
  }
2711
0
2712
0
  nsCOMPtr<nsISerialEventTarget> target;
2713
0
2714
0
  // A child worker just inherits the parent workers ThrottledEventQueue
2715
0
  // and main thread target for now.  This is mainly due to the restriction
2716
0
  // that ThrottledEventQueue can only be created on the main thread at the
2717
0
  // moment.
2718
0
  if (aParent) {
2719
0
    mMainThreadThrottledEventQueue = aParent->mMainThreadThrottledEventQueue;
2720
0
    mMainThreadEventTarget = aParent->mMainThreadEventTarget;
2721
0
    return;
2722
0
  }
2723
0
2724
0
  MOZ_ASSERT(NS_IsMainThread());
2725
0
  target = GetWindow() ? GetWindow()->EventTargetFor(TaskCategory::Worker) : nullptr;
2726
0
2727
0
  if (!target) {
2728
0
    target = GetMainThreadSerialEventTarget();
2729
0
    MOZ_DIAGNOSTIC_ASSERT(target);
2730
0
  }
2731
0
2732
0
  // Throttle events to the main thread using a ThrottledEventQueue specific to
2733
0
  // this worker thread.  This may return nullptr during shutdown.
2734
0
  mMainThreadThrottledEventQueue = ThrottledEventQueue::Create(target);
2735
0
2736
0
  // If we were able to creat the throttled event queue, then use it for
2737
0
  // dispatching our main thread runnables.  Otherwise use our underlying
2738
0
  // base target.
2739
0
  if (mMainThreadThrottledEventQueue) {
2740
0
    mMainThreadEventTarget = mMainThreadThrottledEventQueue;
2741
0
  } else {
2742
0
    mMainThreadEventTarget = target.forget();
2743
0
  }
2744
0
}
2745
2746
WorkerPrivate::~WorkerPrivate()
2747
0
{
2748
0
  DropJSObjects(this);
2749
0
2750
0
  mWorkerControlEventTarget->ForgetWorkerPrivate(this);
2751
0
2752
0
  // We force the hybrid event target to forget the thread when we
2753
0
  // enter the Killing state, but we do it again here to be safe.
2754
0
  // Its possible that we may be created and destroyed without progressing
2755
0
  // to Killing via some obscure code path.
2756
0
  mWorkerHybridEventTarget->ForgetWorkerPrivate(this);
2757
0
}
2758
2759
// static
2760
already_AddRefed<WorkerPrivate>
2761
WorkerPrivate::Constructor(JSContext* aCx,
2762
                           const nsAString& aScriptURL,
2763
                           bool aIsChromeWorker, WorkerType aWorkerType,
2764
                           const nsAString& aWorkerName,
2765
                           const nsACString& aServiceWorkerScope,
2766
                           WorkerLoadInfo* aLoadInfo, ErrorResult& aRv)
2767
0
{
2768
0
  WorkerPrivate* parent = NS_IsMainThread() ?
2769
0
                          nullptr :
2770
0
                          GetCurrentThreadWorkerPrivate();
2771
0
2772
0
  // If this is a sub-worker, we need to keep the parent worker alive until this
2773
0
  // one is registered.
2774
0
  RefPtr<StrongWorkerRef> workerRef;
2775
0
  if (parent) {
2776
0
    parent->AssertIsOnWorkerThread();
2777
0
2778
0
    workerRef =
2779
0
      StrongWorkerRef::Create(parent, "WorkerPrivate::Constructor");
2780
0
    if (NS_WARN_IF(!workerRef)) {
2781
0
      aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
2782
0
      return nullptr;
2783
0
    }
2784
0
  } else {
2785
0
    AssertIsOnMainThread();
2786
0
  }
2787
0
2788
0
  Maybe<WorkerLoadInfo> stackLoadInfo;
2789
0
  if (!aLoadInfo) {
2790
0
    stackLoadInfo.emplace();
2791
0
2792
0
    nsresult rv = GetLoadInfo(aCx, nullptr, parent, aScriptURL,
2793
0
                              aIsChromeWorker, InheritLoadGroup,
2794
0
                              aWorkerType, stackLoadInfo.ptr());
2795
0
    aRv.MightThrowJSException();
2796
0
    if (NS_FAILED(rv)) {
2797
0
      workerinternals::ReportLoadError(aRv, rv, aScriptURL);
2798
0
      return nullptr;
2799
0
    }
2800
0
2801
0
    aLoadInfo = stackLoadInfo.ptr();
2802
0
  }
2803
0
2804
0
  // NB: This has to be done before creating the WorkerPrivate, because it will
2805
0
  // attempt to use static variables that are initialized in the RuntimeService
2806
0
  // constructor.
2807
0
  RuntimeService* runtimeService;
2808
0
2809
0
  if (!parent) {
2810
0
    runtimeService = RuntimeService::GetOrCreateService();
2811
0
    if (!runtimeService) {
2812
0
      aRv.Throw(NS_ERROR_FAILURE);
2813
0
      return nullptr;
2814
0
    }
2815
0
  }
2816
0
  else {
2817
0
    runtimeService = RuntimeService::GetService();
2818
0
  }
2819
0
2820
0
  MOZ_ASSERT(runtimeService);
2821
0
2822
0
  RefPtr<WorkerPrivate> worker =
2823
0
    new WorkerPrivate(parent, aScriptURL, aIsChromeWorker,
2824
0
                      aWorkerType, aWorkerName, aServiceWorkerScope,
2825
0
                      *aLoadInfo);
2826
0
2827
0
  // Gecko contexts always have an explicitly-set default locale (set by
2828
0
  // XPJSRuntime::Initialize for the main thread, set by
2829
0
  // WorkerThreadPrimaryRunnable::Run for workers just before running worker
2830
0
  // code), so this is never SpiderMonkey's builtin default locale.
2831
0
  JS::UniqueChars defaultLocale = JS_GetDefaultLocale(aCx);
2832
0
  if (NS_WARN_IF(!defaultLocale)) {
2833
0
    aRv.Throw(NS_ERROR_UNEXPECTED);
2834
0
    return nullptr;
2835
0
  }
2836
0
2837
0
  worker->mDefaultLocale = std::move(defaultLocale);
2838
0
2839
0
  if (!runtimeService->RegisterWorker(worker)) {
2840
0
    aRv.Throw(NS_ERROR_UNEXPECTED);
2841
0
    return nullptr;
2842
0
  }
2843
0
2844
0
  worker->EnableDebugger();
2845
0
2846
0
  MOZ_DIAGNOSTIC_ASSERT(worker->PrincipalIsValid());
2847
0
2848
0
  RefPtr<CompileScriptRunnable> compiler =
2849
0
    new CompileScriptRunnable(worker, aScriptURL);
2850
0
  if (!compiler->Dispatch()) {
2851
0
    aRv.Throw(NS_ERROR_UNEXPECTED);
2852
0
    return nullptr;
2853
0
  }
2854
0
2855
0
  worker->mSelfRef = worker;
2856
0
2857
0
  return worker.forget();
2858
0
}
2859
2860
// static
2861
nsresult
2862
WorkerPrivate::GetLoadInfo(JSContext* aCx, nsPIDOMWindowInner* aWindow,
2863
                           WorkerPrivate* aParent, const nsAString& aScriptURL,
2864
                           bool aIsChromeWorker,
2865
                           LoadGroupBehavior aLoadGroupBehavior,
2866
                           WorkerType aWorkerType,
2867
                           WorkerLoadInfo* aLoadInfo)
2868
0
{
2869
0
  using namespace mozilla::dom::workerinternals;
2870
0
2871
0
  MOZ_ASSERT(aCx);
2872
0
  MOZ_ASSERT_IF(NS_IsMainThread(), aCx == nsContentUtils::GetCurrentJSContext());
2873
0
2874
0
  if (aWindow) {
2875
0
    AssertIsOnMainThread();
2876
0
  }
2877
0
2878
0
  WorkerLoadInfo loadInfo;
2879
0
  nsresult rv;
2880
0
2881
0
  if (aParent) {
2882
0
    aParent->AssertIsOnWorkerThread();
2883
0
2884
0
    // If the parent is going away give up now.
2885
0
    WorkerStatus parentStatus;
2886
0
    {
2887
0
      MutexAutoLock lock(aParent->mMutex);
2888
0
      parentStatus = aParent->mStatus;
2889
0
    }
2890
0
2891
0
    if (parentStatus > Running) {
2892
0
      return NS_ERROR_FAILURE;
2893
0
    }
2894
0
2895
0
    // Passing a pointer to our stack loadInfo is safe here because this
2896
0
    // method uses a sync runnable to get the channel from the main thread.
2897
0
    rv = ChannelFromScriptURLWorkerThread(aCx, aParent, aScriptURL,
2898
0
                                          loadInfo);
2899
0
    if (NS_FAILED(rv)) {
2900
0
      MOZ_ALWAYS_TRUE(loadInfo.ProxyReleaseMainThreadObjects(aParent));
2901
0
      return rv;
2902
0
    }
2903
0
2904
0
    // Now that we've spun the loop there's no guarantee that our parent is
2905
0
    // still alive.  We may have received control messages initiating shutdown.
2906
0
    {
2907
0
      MutexAutoLock lock(aParent->mMutex);
2908
0
      parentStatus = aParent->mStatus;
2909
0
    }
2910
0
2911
0
    if (parentStatus > Running) {
2912
0
      MOZ_ALWAYS_TRUE(loadInfo.ProxyReleaseMainThreadObjects(aParent));
2913
0
      return NS_ERROR_FAILURE;
2914
0
    }
2915
0
2916
0
    loadInfo.mDomain = aParent->Domain();
2917
0
    loadInfo.mFromWindow = aParent->IsFromWindow();
2918
0
    loadInfo.mWindowID = aParent->WindowID();
2919
0
    loadInfo.mStorageAllowed = aParent->IsStorageAllowed();
2920
0
    loadInfo.mOriginAttributes = aParent->GetOriginAttributes();
2921
0
    loadInfo.mServiceWorkersTestingInWindow =
2922
0
      aParent->ServiceWorkersTestingInWindow();
2923
0
    loadInfo.mParentController = aParent->GetController();
2924
0
  } else {
2925
0
    AssertIsOnMainThread();
2926
0
2927
0
    // Make sure that the IndexedDatabaseManager is set up
2928
0
    Unused << NS_WARN_IF(!IndexedDatabaseManager::GetOrCreate());
2929
0
2930
0
    nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
2931
0
    MOZ_ASSERT(ssm);
2932
0
2933
0
    bool isChrome = nsContentUtils::IsSystemCaller(aCx);
2934
0
2935
0
    // First check to make sure the caller has permission to make a privileged
2936
0
    // worker if they called the ChromeWorker/ChromeSharedWorker constructor.
2937
0
    if (aIsChromeWorker && !isChrome) {
2938
0
      return NS_ERROR_DOM_SECURITY_ERR;
2939
0
    }
2940
0
2941
0
    // Chrome callers (whether creating a ChromeWorker or Worker) always get the
2942
0
    // system principal here as they're allowed to load anything. The script
2943
0
    // loader will refuse to run any script that does not also have the system
2944
0
    // principal.
2945
0
    if (isChrome) {
2946
0
      rv = ssm->GetSystemPrincipal(getter_AddRefs(loadInfo.mLoadingPrincipal));
2947
0
      NS_ENSURE_SUCCESS(rv, rv);
2948
0
2949
0
      loadInfo.mPrincipalIsSystem = true;
2950
0
    }
2951
0
2952
0
    // See if we're being called from a window.
2953
0
    nsCOMPtr<nsPIDOMWindowInner> globalWindow = aWindow;
2954
0
    if (!globalWindow) {
2955
0
      globalWindow = xpc::CurrentWindowOrNull(aCx);
2956
0
    }
2957
0
2958
0
    nsCOMPtr<nsIDocument> document;
2959
0
    Maybe<ClientInfo> clientInfo;
2960
0
2961
0
    if (globalWindow) {
2962
0
      // Only use the current inner window, and only use it if the caller can
2963
0
      // access it.
2964
0
      if (nsPIDOMWindowOuter* outerWindow = globalWindow->GetOuterWindow()) {
2965
0
        loadInfo.mWindow = outerWindow->GetCurrentInnerWindow();
2966
0
        // TODO: fix this for SharedWorkers with multiple documents (bug 1177935)
2967
0
        loadInfo.mServiceWorkersTestingInWindow =
2968
0
          outerWindow->GetServiceWorkersTestingEnabled();
2969
0
      }
2970
0
2971
0
      if (!loadInfo.mWindow ||
2972
0
          (globalWindow != loadInfo.mWindow &&
2973
0
            !nsContentUtils::CanCallerAccess(loadInfo.mWindow))) {
2974
0
        return NS_ERROR_DOM_SECURITY_ERR;
2975
0
      }
2976
0
2977
0
      nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(loadInfo.mWindow);
2978
0
      MOZ_ASSERT(sgo);
2979
0
2980
0
      loadInfo.mScriptContext = sgo->GetContext();
2981
0
      NS_ENSURE_TRUE(loadInfo.mScriptContext, NS_ERROR_FAILURE);
2982
0
2983
0
      // If we're called from a window then we can dig out the principal and URI
2984
0
      // from the document.
2985
0
      document = loadInfo.mWindow->GetExtantDoc();
2986
0
      NS_ENSURE_TRUE(document, NS_ERROR_FAILURE);
2987
0
2988
0
      loadInfo.mBaseURI = document->GetDocBaseURI();
2989
0
      loadInfo.mLoadGroup = document->GetDocumentLoadGroup();
2990
0
      NS_ENSURE_TRUE(loadInfo.mLoadGroup, NS_ERROR_FAILURE);
2991
0
2992
0
      clientInfo = globalWindow->GetClientInfo();
2993
0
2994
0
      // Use the document's NodePrincipal as loading principal if we're not being
2995
0
      // called from chrome.
2996
0
      if (!loadInfo.mLoadingPrincipal) {
2997
0
        loadInfo.mLoadingPrincipal = document->NodePrincipal();
2998
0
        NS_ENSURE_TRUE(loadInfo.mLoadingPrincipal, NS_ERROR_FAILURE);
2999
0
3000
0
        // We use the document's base domain to limit the number of workers
3001
0
        // each domain can create. For sandboxed documents, we use the domain
3002
0
        // of their first non-sandboxed document, walking up until we find
3003
0
        // one. If we can't find one, we fall back to using the GUID of the
3004
0
        // null principal as the base domain.
3005
0
        if (document->GetSandboxFlags() & SANDBOXED_ORIGIN) {
3006
0
          nsCOMPtr<nsIDocument> tmpDoc = document;
3007
0
          do {
3008
0
            tmpDoc = tmpDoc->GetParentDocument();
3009
0
          } while (tmpDoc && tmpDoc->GetSandboxFlags() & SANDBOXED_ORIGIN);
3010
0
3011
0
          if (tmpDoc) {
3012
0
            // There was an unsandboxed ancestor, yay!
3013
0
            nsCOMPtr<nsIPrincipal> tmpPrincipal = tmpDoc->NodePrincipal();
3014
0
            rv = tmpPrincipal->GetBaseDomain(loadInfo.mDomain);
3015
0
            NS_ENSURE_SUCCESS(rv, rv);
3016
0
          } else {
3017
0
            // No unsandboxed ancestor, use our GUID.
3018
0
            rv = loadInfo.mLoadingPrincipal->GetBaseDomain(loadInfo.mDomain);
3019
0
            NS_ENSURE_SUCCESS(rv, rv);
3020
0
          }
3021
0
        } else {
3022
0
          // Document creating the worker is not sandboxed.
3023
0
          rv = loadInfo.mLoadingPrincipal->GetBaseDomain(loadInfo.mDomain);
3024
0
          NS_ENSURE_SUCCESS(rv, rv);
3025
0
        }
3026
0
      }
3027
0
3028
0
      NS_ENSURE_TRUE(NS_LoadGroupMatchesPrincipal(loadInfo.mLoadGroup,
3029
0
                                                  loadInfo.mLoadingPrincipal),
3030
0
                     NS_ERROR_FAILURE);
3031
0
3032
0
      nsCOMPtr<nsIPermissionManager> permMgr =
3033
0
        do_GetService(NS_PERMISSIONMANAGER_CONTRACTID, &rv);
3034
0
      NS_ENSURE_SUCCESS(rv, rv);
3035
0
3036
0
      uint32_t perm;
3037
0
      rv = permMgr->TestPermissionFromPrincipal(loadInfo.mLoadingPrincipal,
3038
0
                                                "systemXHR", &perm);
3039
0
      NS_ENSURE_SUCCESS(rv, rv);
3040
0
3041
0
      loadInfo.mXHRParamsAllowed = perm == nsIPermissionManager::ALLOW_ACTION;
3042
0
3043
0
      loadInfo.mFromWindow = true;
3044
0
      loadInfo.mWindowID = globalWindow->WindowID();
3045
0
      nsContentUtils::StorageAccess access =
3046
0
        nsContentUtils::StorageAllowedForWindow(globalWindow);
3047
0
      loadInfo.mStorageAllowed = access > nsContentUtils::StorageAccess::eDeny;
3048
0
      loadInfo.mOriginAttributes = nsContentUtils::GetOriginAttributes(document);
3049
0
      loadInfo.mParentController = globalWindow->GetController();
3050
0
    } else {
3051
0
      // Not a window
3052
0
      MOZ_ASSERT(isChrome);
3053
0
3054
0
      // We're being created outside of a window. Need to figure out the script
3055
0
      // that is creating us in order for us to use relative URIs later on.
3056
0
      JS::AutoFilename fileName;
3057
0
      if (JS::DescribeScriptedCaller(aCx, &fileName)) {
3058
0
        // In most cases, fileName is URI. In a few other cases
3059
0
        // (e.g. xpcshell), fileName is a file path. Ideally, we would
3060
0
        // prefer testing whether fileName parses as an URI and fallback
3061
0
        // to file path in case of error, but Windows file paths have
3062
0
        // the interesting property that they can be parsed as bogus
3063
0
        // URIs (e.g. C:/Windows/Tmp is interpreted as scheme "C",
3064
0
        // hostname "Windows", path "Tmp"), which defeats this algorithm.
3065
0
        // Therefore, we adopt the opposite convention.
3066
0
        nsCOMPtr<nsIFile> scriptFile =
3067
0
          do_CreateInstance("@mozilla.org/file/local;1", &rv);
3068
0
        if (NS_FAILED(rv)) {
3069
0
          return rv;
3070
0
        }
3071
0
3072
0
        rv = scriptFile->InitWithPath(NS_ConvertUTF8toUTF16(fileName.get()));
3073
0
        if (NS_SUCCEEDED(rv)) {
3074
0
          rv = NS_NewFileURI(getter_AddRefs(loadInfo.mBaseURI),
3075
0
                             scriptFile);
3076
0
        }
3077
0
        if (NS_FAILED(rv)) {
3078
0
          // As expected, fileName is not a path, so proceed with
3079
0
          // a uri.
3080
0
          rv = NS_NewURI(getter_AddRefs(loadInfo.mBaseURI),
3081
0
                         fileName.get());
3082
0
        }
3083
0
        if (NS_FAILED(rv)) {
3084
0
          return rv;
3085
0
        }
3086
0
      }
3087
0
      loadInfo.mXHRParamsAllowed = true;
3088
0
      loadInfo.mFromWindow = false;
3089
0
      loadInfo.mWindowID = UINT64_MAX;
3090
0
      loadInfo.mStorageAllowed = true;
3091
0
      loadInfo.mOriginAttributes = OriginAttributes();
3092
0
    }
3093
0
3094
0
    MOZ_ASSERT(loadInfo.mLoadingPrincipal);
3095
0
    MOZ_ASSERT(isChrome || !loadInfo.mDomain.IsEmpty());
3096
0
3097
0
    if (!loadInfo.mLoadGroup || aLoadGroupBehavior == OverrideLoadGroup) {
3098
0
      OverrideLoadInfoLoadGroup(loadInfo, loadInfo.mLoadingPrincipal);
3099
0
    }
3100
0
    MOZ_ASSERT(NS_LoadGroupMatchesPrincipal(loadInfo.mLoadGroup,
3101
0
                                            loadInfo.mLoadingPrincipal));
3102
0
3103
0
    // Top level workers' main script use the document charset for the script
3104
0
    // uri encoding.
3105
0
    bool useDefaultEncoding = false;
3106
0
    rv = ChannelFromScriptURLMainThread(loadInfo.mLoadingPrincipal,
3107
0
                                        loadInfo.mBaseURI,
3108
0
                                        document, loadInfo.mLoadGroup,
3109
0
                                        aScriptURL,
3110
0
                                        clientInfo,
3111
0
                                        ContentPolicyType(aWorkerType),
3112
0
                                        useDefaultEncoding,
3113
0
                                        getter_AddRefs(loadInfo.mChannel));
3114
0
    NS_ENSURE_SUCCESS(rv, rv);
3115
0
3116
0
    rv = NS_GetFinalChannelURI(loadInfo.mChannel,
3117
0
                               getter_AddRefs(loadInfo.mResolvedScriptURI));
3118
0
    NS_ENSURE_SUCCESS(rv, rv);
3119
0
3120
0
    rv = loadInfo.SetPrincipalFromChannel(loadInfo.mChannel);
3121
0
    NS_ENSURE_SUCCESS(rv, rv);
3122
0
  }
3123
0
3124
0
  MOZ_DIAGNOSTIC_ASSERT(loadInfo.mLoadingPrincipal);
3125
0
  MOZ_DIAGNOSTIC_ASSERT(loadInfo.PrincipalIsValid());
3126
0
3127
0
  aLoadInfo->StealFrom(loadInfo);
3128
0
  return NS_OK;
3129
0
}
3130
3131
// static
3132
void
3133
WorkerPrivate::OverrideLoadInfoLoadGroup(WorkerLoadInfo& aLoadInfo,
3134
                                         nsIPrincipal* aPrincipal)
3135
0
{
3136
0
  MOZ_ASSERT(!aLoadInfo.mInterfaceRequestor);
3137
0
  MOZ_ASSERT(aLoadInfo.mLoadingPrincipal == aPrincipal);
3138
0
3139
0
  aLoadInfo.mInterfaceRequestor =
3140
0
    new WorkerLoadInfo::InterfaceRequestor(aPrincipal, aLoadInfo.mLoadGroup);
3141
0
  aLoadInfo.mInterfaceRequestor->MaybeAddTabChild(aLoadInfo.mLoadGroup);
3142
0
3143
0
  // NOTE: this defaults the load context to:
3144
0
  //  - private browsing = false
3145
0
  //  - content = true
3146
0
  //  - use remote tabs = false
3147
0
  nsCOMPtr<nsILoadGroup> loadGroup =
3148
0
    do_CreateInstance(NS_LOADGROUP_CONTRACTID);
3149
0
3150
0
  nsresult rv =
3151
0
    loadGroup->SetNotificationCallbacks(aLoadInfo.mInterfaceRequestor);
3152
0
  MOZ_ALWAYS_SUCCEEDS(rv);
3153
0
3154
0
  aLoadInfo.mLoadGroup = loadGroup.forget();
3155
0
3156
0
  MOZ_ASSERT(NS_LoadGroupMatchesPrincipal(aLoadInfo.mLoadGroup, aPrincipal));
3157
0
}
3158
3159
void
3160
WorkerPrivate::DoRunLoop(JSContext* aCx)
3161
0
{
3162
0
  AssertIsOnWorkerThread();
3163
0
  MOZ_ASSERT(mThread);
3164
0
3165
0
  {
3166
0
    MutexAutoLock lock(mMutex);
3167
0
    mJSContext = aCx;
3168
0
3169
0
    MOZ_ASSERT(mStatus == Pending);
3170
0
    mStatus = Running;
3171
0
  }
3172
0
3173
0
  // Now that we've done that, we can go ahead and set up our AutoJSAPI.  We
3174
0
  // can't before this point, because it can't find the right JSContext before
3175
0
  // then, since it gets it from our mJSContext.
3176
0
  AutoJSAPI jsapi;
3177
0
  jsapi.Init();
3178
0
  MOZ_ASSERT(jsapi.cx() == aCx);
3179
0
3180
0
  EnableMemoryReporter();
3181
0
3182
0
  InitializeGCTimers();
3183
0
3184
0
  for (;;) {
3185
0
    WorkerStatus currentStatus;
3186
0
    bool debuggerRunnablesPending = false;
3187
0
    bool normalRunnablesPending = false;
3188
0
3189
0
    {
3190
0
      MutexAutoLock lock(mMutex);
3191
0
3192
0
      while (mControlQueue.IsEmpty() &&
3193
0
             !(debuggerRunnablesPending = !mDebuggerQueue.IsEmpty()) &&
3194
0
             !(normalRunnablesPending = NS_HasPendingEvents(mThread))) {
3195
0
        WaitForWorkerEvents();
3196
0
      }
3197
0
3198
0
      auto result = ProcessAllControlRunnablesLocked();
3199
0
      if (result != ProcessAllControlRunnablesResult::Nothing) {
3200
0
        // NB: There's no JS on the stack here, so Abort vs MayContinue is
3201
0
        // irrelevant
3202
0
3203
0
        // The state of the world may have changed, recheck it.
3204
0
        normalRunnablesPending = NS_HasPendingEvents(mThread);
3205
0
        // The debugger queue doesn't get cleared, so we can ignore that.
3206
0
      }
3207
0
3208
0
      currentStatus = mStatus;
3209
0
    }
3210
0
3211
0
    // if all holders are done then we can kill this thread.
3212
0
    if (currentStatus != Running && !HasActiveHolders()) {
3213
0
3214
0
      // Now we are ready to kill the worker thread.
3215
0
      if (currentStatus == Canceling) {
3216
0
        NotifyInternal(Killing);
3217
0
3218
#ifdef DEBUG
3219
        {
3220
          MutexAutoLock lock(mMutex);
3221
          currentStatus = mStatus;
3222
        }
3223
        MOZ_ASSERT(currentStatus == Killing);
3224
#else
3225
        currentStatus = Killing;
3226
0
#endif
3227
0
      }
3228
0
3229
0
      // If we're supposed to die then we should exit the loop.
3230
0
      if (currentStatus == Killing) {
3231
0
        // The ClientSource should be cleared in NotifyInternal() when we reach
3232
0
        // or pass Canceling.
3233
0
        MOZ_DIAGNOSTIC_ASSERT(!mClientSource);
3234
0
3235
0
        // Flush uncaught rejections immediately, without
3236
0
        // waiting for a next tick.
3237
0
        PromiseDebugging::FlushUncaughtRejections();
3238
0
3239
0
        ShutdownGCTimers();
3240
0
3241
0
        DisableMemoryReporter();
3242
0
3243
0
        {
3244
0
          MutexAutoLock lock(mMutex);
3245
0
3246
0
          mStatus = Dead;
3247
0
          mJSContext = nullptr;
3248
0
        }
3249
0
3250
0
        // After mStatus is set to Dead there can be no more
3251
0
        // WorkerControlRunnables so no need to lock here.
3252
0
        if (!mControlQueue.IsEmpty()) {
3253
0
          WorkerControlRunnable* runnable = nullptr;
3254
0
          while (mControlQueue.Pop(runnable)) {
3255
0
            runnable->Cancel();
3256
0
            runnable->Release();
3257
0
          }
3258
0
        }
3259
0
3260
0
        // Unroot the globals
3261
0
        mScope = nullptr;
3262
0
        mDebuggerScope = nullptr;
3263
0
3264
0
        return;
3265
0
      }
3266
0
    }
3267
0
3268
0
    if (debuggerRunnablesPending || normalRunnablesPending) {
3269
0
      // Start the periodic GC timer if it is not already running.
3270
0
      SetGCTimerMode(PeriodicTimer);
3271
0
    }
3272
0
3273
0
    if (debuggerRunnablesPending) {
3274
0
      WorkerRunnable* runnable = nullptr;
3275
0
3276
0
      {
3277
0
        MutexAutoLock lock(mMutex);
3278
0
3279
0
        mDebuggerQueue.Pop(runnable);
3280
0
        debuggerRunnablesPending = !mDebuggerQueue.IsEmpty();
3281
0
      }
3282
0
3283
0
      MOZ_ASSERT(runnable);
3284
0
      static_cast<nsIRunnable*>(runnable)->Run();
3285
0
      runnable->Release();
3286
0
3287
0
      CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get();
3288
0
      ccjs->PerformDebuggerMicroTaskCheckpoint();
3289
0
3290
0
      if (debuggerRunnablesPending) {
3291
0
        WorkerDebuggerGlobalScope* globalScope = DebuggerGlobalScope();
3292
0
        MOZ_ASSERT(globalScope);
3293
0
3294
0
        // Now *might* be a good time to GC. Let the JS engine make the decision.
3295
0
        JSAutoRealm ar(aCx, globalScope->GetGlobalJSObject());
3296
0
        JS_MaybeGC(aCx);
3297
0
      }
3298
0
    } else if (normalRunnablesPending) {
3299
0
      // Process a single runnable from the main queue.
3300
0
      NS_ProcessNextEvent(mThread, false);
3301
0
3302
0
      normalRunnablesPending = NS_HasPendingEvents(mThread);
3303
0
      if (normalRunnablesPending && GlobalScope()) {
3304
0
        // Now *might* be a good time to GC. Let the JS engine make the decision.
3305
0
        JSAutoRealm ar(aCx, GlobalScope()->GetGlobalJSObject());
3306
0
        JS_MaybeGC(aCx);
3307
0
      }
3308
0
    }
3309
0
3310
0
    if (!debuggerRunnablesPending && !normalRunnablesPending) {
3311
0
      // Both the debugger event queue and the normal event queue has been
3312
0
      // exhausted, cancel the periodic GC timer and schedule the idle GC timer.
3313
0
      SetGCTimerMode(IdleTimer);
3314
0
    }
3315
0
3316
0
    // If the worker thread is spamming the main thread faster than it can
3317
0
    // process the work, then pause the worker thread until the MT catches
3318
0
    // up.
3319
0
    if (mMainThreadThrottledEventQueue &&
3320
0
        mMainThreadThrottledEventQueue->Length() > 5000) {
3321
0
      mMainThreadThrottledEventQueue->AwaitIdle();
3322
0
    }
3323
0
  }
3324
0
3325
0
  MOZ_CRASH("Shouldn't get here!");
3326
0
}
3327
3328
void
3329
WorkerPrivate::OnProcessNextEvent()
3330
0
{
3331
0
  AssertIsOnWorkerThread();
3332
0
3333
0
  uint32_t recursionDepth = CycleCollectedJSContext::Get()->RecursionDepth();
3334
0
  MOZ_ASSERT(recursionDepth);
3335
0
3336
0
  // Normally we process control runnables in DoRunLoop or RunCurrentSyncLoop.
3337
0
  // However, it's possible that non-worker C++ could spin its own nested event
3338
0
  // loop, and in that case we must ensure that we continue to process control
3339
0
  // runnables here.
3340
0
  if (recursionDepth > 1 &&
3341
0
      mSyncLoopStack.Length() < recursionDepth - 1) {
3342
0
    Unused << ProcessAllControlRunnables();
3343
0
    // There's no running JS, and no state to revalidate, so we can ignore the
3344
0
    // return value.
3345
0
  }
3346
0
}
3347
3348
void
3349
WorkerPrivate::AfterProcessNextEvent()
3350
0
{
3351
0
  AssertIsOnWorkerThread();
3352
0
  MOZ_ASSERT(CycleCollectedJSContext::Get()->RecursionDepth());
3353
0
}
3354
3355
nsIEventTarget*
3356
WorkerPrivate::MainThreadEventTarget()
3357
0
{
3358
0
  return mMainThreadEventTarget;
3359
0
}
3360
3361
nsresult
3362
WorkerPrivate::DispatchToMainThread(nsIRunnable* aRunnable, uint32_t aFlags)
3363
0
{
3364
0
  nsCOMPtr<nsIRunnable> r = aRunnable;
3365
0
  return DispatchToMainThread(r.forget(), aFlags);
3366
0
}
3367
3368
nsresult
3369
WorkerPrivate::DispatchToMainThread(already_AddRefed<nsIRunnable> aRunnable,
3370
                                    uint32_t aFlags)
3371
0
{
3372
0
  return mMainThreadEventTarget->Dispatch(std::move(aRunnable), aFlags);
3373
0
}
3374
3375
nsISerialEventTarget*
3376
WorkerPrivate::ControlEventTarget()
3377
0
{
3378
0
  return mWorkerControlEventTarget;
3379
0
}
3380
3381
nsISerialEventTarget*
3382
WorkerPrivate::HybridEventTarget()
3383
0
{
3384
0
  return mWorkerHybridEventTarget;
3385
0
}
3386
3387
bool
3388
WorkerPrivate::EnsureClientSource()
3389
0
{
3390
0
  AssertIsOnWorkerThread();
3391
0
3392
0
  if (mClientSource) {
3393
0
    return true;
3394
0
  }
3395
0
3396
0
  ClientType type;
3397
0
  switch(Type()) {
3398
0
    case WorkerTypeDedicated:
3399
0
      type = ClientType::Worker;
3400
0
      break;
3401
0
    case WorkerTypeShared:
3402
0
      type = ClientType::Sharedworker;
3403
0
      break;
3404
0
    case WorkerTypeService:
3405
0
      type = ClientType::Serviceworker;
3406
0
      break;
3407
0
    default:
3408
0
      MOZ_CRASH("unknown worker type!");
3409
0
  }
3410
0
3411
0
  mClientSource = ClientManager::CreateSource(type, mWorkerHybridEventTarget,
3412
0
                                              GetPrincipalInfo());
3413
0
  MOZ_DIAGNOSTIC_ASSERT(mClientSource);
3414
0
3415
0
  if (mFrozen) {
3416
0
    mClientSource->Freeze();
3417
0
  }
3418
0
3419
0
  // Shortly after the client is reserved we will try loading the main script
3420
0
  // for the worker.  This may get intercepted by the ServiceWorkerManager
3421
0
  // which will then try to create a ClientHandle.  Its actually possible for
3422
0
  // the main thread to create this ClientHandle before our IPC message creating
3423
0
  // the ClientSource completes.  To avoid this race we synchronously ping our
3424
0
  // parent Client actor here.  This ensure the worker ClientSource is created
3425
0
  // in the parent before the main thread might try reaching it with a
3426
0
  // ClientHandle.
3427
0
  //
3428
0
  // An alternative solution would have been to handle the out-of-order operations
3429
0
  // on the parent side.  We could have created a small window where we allow
3430
0
  // ClientHandle objects to exist without a ClientSource.  We would then time
3431
0
  // out these handles if they stayed orphaned for too long.  This approach would
3432
0
  // be much more complex, but also avoid this extra bit of latency when starting
3433
0
  // workers.
3434
0
  //
3435
0
  // Note, we only have to do this for workers that can be controlled by a
3436
0
  // service worker.  So avoid the sync overhead here if we are starting a
3437
0
  // service worker or a chrome worker.
3438
0
  if (Type() != WorkerTypeService && !IsChromeWorker()) {
3439
0
    mClientSource->WorkerSyncPing(this);
3440
0
  }
3441
0
3442
0
  return true;
3443
0
}
3444
3445
bool
3446
WorkerPrivate::EnsureCSPEventListener()
3447
0
{
3448
0
  mCSPEventListener = WorkerCSPEventListener::Create(this);
3449
0
  if (NS_WARN_IF(!mCSPEventListener)) {
3450
0
    return false;
3451
0
  }
3452
0
3453
0
  if (mLoadInfo.mCSP) {
3454
0
    mLoadInfo.mCSP->SetEventListener(mCSPEventListener);
3455
0
  }
3456
0
3457
0
  return true;
3458
0
}
3459
3460
void
3461
WorkerPrivate::EnsurePerformanceStorage()
3462
0
{
3463
0
  AssertIsOnWorkerThread();
3464
0
3465
0
  if (!mPerformanceStorage) {
3466
0
    mPerformanceStorage = PerformanceStorageWorker::Create(this);
3467
0
  }
3468
0
}
3469
3470
Maybe<ClientInfo>
3471
WorkerPrivate::GetClientInfo() const
3472
0
{
3473
0
  AssertIsOnWorkerThread();
3474
0
  Maybe<ClientInfo> clientInfo;
3475
0
  if (!mClientSource) {
3476
0
    MOZ_DIAGNOSTIC_ASSERT(mStatus >= Canceling);
3477
0
    return clientInfo;
3478
0
  }
3479
0
  clientInfo.emplace(mClientSource->Info());
3480
0
  return clientInfo;
3481
0
}
3482
3483
const ClientState
3484
WorkerPrivate::GetClientState() const
3485
0
{
3486
0
  AssertIsOnWorkerThread();
3487
0
  MOZ_DIAGNOSTIC_ASSERT(mClientSource);
3488
0
  ClientState state;
3489
0
  mClientSource->SnapshotState(&state);
3490
0
  return state;
3491
0
}
3492
3493
const Maybe<ServiceWorkerDescriptor>
3494
WorkerPrivate::GetController()
3495
0
{
3496
0
  AssertIsOnWorkerThread();
3497
0
  {
3498
0
    MutexAutoLock lock(mMutex);
3499
0
    if (mStatus >= Canceling) {
3500
0
      return Maybe<ServiceWorkerDescriptor>();
3501
0
    }
3502
0
  }
3503
0
  MOZ_DIAGNOSTIC_ASSERT(mClientSource);
3504
0
  return mClientSource->GetController();
3505
0
}
3506
3507
void
3508
WorkerPrivate::Control(const ServiceWorkerDescriptor& aServiceWorker)
3509
0
{
3510
0
  AssertIsOnWorkerThread();
3511
0
  MOZ_DIAGNOSTIC_ASSERT(!IsChromeWorker());
3512
0
  MOZ_DIAGNOSTIC_ASSERT(Type() != WorkerTypeService);
3513
0
  {
3514
0
    MutexAutoLock lock(mMutex);
3515
0
    if (mStatus >= Canceling) {
3516
0
      return;
3517
0
    }
3518
0
  }
3519
0
  MOZ_DIAGNOSTIC_ASSERT(mClientSource);
3520
0
3521
0
  if (IsBlobURI(mLoadInfo.mBaseURI)) {
3522
0
    // Blob URL workers can only become controlled by inheriting from
3523
0
    // their parent.  Make sure to note this properly.
3524
0
    mClientSource->InheritController(aServiceWorker);
3525
0
  } else {
3526
0
    // Otherwise this is a normal interception and we simply record the
3527
0
    // controller locally.
3528
0
    mClientSource->SetController(aServiceWorker);
3529
0
  }
3530
0
}
3531
3532
void
3533
WorkerPrivate::ExecutionReady()
3534
0
{
3535
0
  AssertIsOnWorkerThread();
3536
0
  {
3537
0
    MutexAutoLock lock(mMutex);
3538
0
    if (mStatus >= Canceling) {
3539
0
      return;
3540
0
    }
3541
0
  }
3542
0
  MOZ_DIAGNOSTIC_ASSERT(mClientSource);
3543
0
  mClientSource->WorkerExecutionReady(this);
3544
0
}
3545
3546
void
3547
WorkerPrivate::InitializeGCTimers()
3548
0
{
3549
0
  AssertIsOnWorkerThread();
3550
0
3551
0
  // We need a timer for GC. The basic plan is to run a non-shrinking GC
3552
0
  // periodically (PERIODIC_GC_TIMER_DELAY_SEC) while the worker is running.
3553
0
  // Once the worker goes idle we set a short (IDLE_GC_TIMER_DELAY_SEC) timer to
3554
0
  // run a shrinking GC. If the worker receives more messages then the short
3555
0
  // timer is canceled and the periodic timer resumes.
3556
0
  mGCTimer = NS_NewTimer();
3557
0
  MOZ_ASSERT(mGCTimer);
3558
0
3559
0
  mPeriodicGCTimerRunning = false;
3560
0
  mIdleGCTimerRunning = false;
3561
0
}
3562
3563
void
3564
WorkerPrivate::SetGCTimerMode(GCTimerMode aMode)
3565
0
{
3566
0
  AssertIsOnWorkerThread();
3567
0
  MOZ_ASSERT(mGCTimer);
3568
0
3569
0
  if ((aMode == PeriodicTimer && mPeriodicGCTimerRunning) ||
3570
0
      (aMode == IdleTimer && mIdleGCTimerRunning)) {
3571
0
    return;
3572
0
  }
3573
0
3574
0
  MOZ_ALWAYS_SUCCEEDS(mGCTimer->Cancel());
3575
0
3576
0
  mPeriodicGCTimerRunning = false;
3577
0
  mIdleGCTimerRunning = false;
3578
0
  LOG(WorkerLog(),
3579
0
      ("Worker %p canceled GC timer because %s\n", this,
3580
0
       aMode == PeriodicTimer ?
3581
0
       "periodic" :
3582
0
       aMode == IdleTimer ? "idle" : "none"));
3583
0
3584
0
  if (aMode == NoTimer) {
3585
0
    return;
3586
0
  }
3587
0
3588
0
  MOZ_ASSERT(aMode == PeriodicTimer || aMode == IdleTimer);
3589
0
3590
0
  uint32_t delay = 0;
3591
0
  int16_t type = nsITimer::TYPE_ONE_SHOT;
3592
0
  nsTimerCallbackFunc callback = nullptr;
3593
0
  const char* name = nullptr;
3594
0
3595
0
  if (aMode == PeriodicTimer) {
3596
0
    delay = PERIODIC_GC_TIMER_DELAY_SEC * 1000;
3597
0
    type = nsITimer::TYPE_REPEATING_SLACK;
3598
0
    callback = PeriodicGCTimerCallback;
3599
0
    name = "dom::PeriodicGCTimerCallback";
3600
0
  }
3601
0
  else {
3602
0
    delay = IDLE_GC_TIMER_DELAY_SEC * 1000;
3603
0
    type = nsITimer::TYPE_ONE_SHOT;
3604
0
    callback = IdleGCTimerCallback;
3605
0
    name = "dom::IdleGCTimerCallback";
3606
0
  }
3607
0
3608
0
  MOZ_ALWAYS_SUCCEEDS(mGCTimer->SetTarget(mWorkerControlEventTarget));
3609
0
  MOZ_ALWAYS_SUCCEEDS(
3610
0
    mGCTimer->InitWithNamedFuncCallback(callback, this, delay, type, name));
3611
0
3612
0
  if (aMode == PeriodicTimer) {
3613
0
    LOG(WorkerLog(), ("Worker %p scheduled periodic GC timer\n", this));
3614
0
    mPeriodicGCTimerRunning = true;
3615
0
  }
3616
0
  else {
3617
0
    LOG(WorkerLog(), ("Worker %p scheduled idle GC timer\n", this));
3618
0
    mIdleGCTimerRunning = true;
3619
0
  }
3620
0
}
3621
3622
void
3623
WorkerPrivate::ShutdownGCTimers()
3624
0
{
3625
0
  AssertIsOnWorkerThread();
3626
0
3627
0
  MOZ_ASSERT(mGCTimer);
3628
0
3629
0
  // Always make sure the timer is canceled.
3630
0
  MOZ_ALWAYS_SUCCEEDS(mGCTimer->Cancel());
3631
0
3632
0
  LOG(WorkerLog(), ("Worker %p killed the GC timer\n", this));
3633
0
3634
0
  mGCTimer = nullptr;
3635
0
  mPeriodicGCTimerRunning = false;
3636
0
  mIdleGCTimerRunning = false;
3637
0
}
3638
3639
bool
3640
WorkerPrivate::InterruptCallback(JSContext* aCx)
3641
0
{
3642
0
  AssertIsOnWorkerThread();
3643
0
3644
0
  MOZ_ASSERT(!JS_IsExceptionPending(aCx));
3645
0
3646
0
  bool mayContinue = true;
3647
0
  bool scheduledIdleGC = false;
3648
0
3649
0
  for (;;) {
3650
0
    // Run all control events now.
3651
0
    auto result = ProcessAllControlRunnables();
3652
0
    if (result == ProcessAllControlRunnablesResult::Abort) {
3653
0
      mayContinue = false;
3654
0
    }
3655
0
3656
0
    bool mayFreeze = mFrozen;
3657
0
    if (mayFreeze) {
3658
0
      MutexAutoLock lock(mMutex);
3659
0
      mayFreeze = mStatus <= Running;
3660
0
    }
3661
0
3662
0
    if (!mayContinue || !mayFreeze) {
3663
0
      break;
3664
0
    }
3665
0
3666
0
    // Cancel the periodic GC timer here before freezing. The idle GC timer
3667
0
    // will clean everything up once it runs.
3668
0
    if (!scheduledIdleGC) {
3669
0
      SetGCTimerMode(IdleTimer);
3670
0
      scheduledIdleGC = true;
3671
0
    }
3672
0
3673
0
    while ((mayContinue = MayContinueRunning())) {
3674
0
      MutexAutoLock lock(mMutex);
3675
0
      if (!mControlQueue.IsEmpty()) {
3676
0
        break;
3677
0
      }
3678
0
3679
0
      WaitForWorkerEvents();
3680
0
    }
3681
0
  }
3682
0
3683
0
  if (!mayContinue) {
3684
0
    // We want only uncatchable exceptions here.
3685
0
    NS_ASSERTION(!JS_IsExceptionPending(aCx),
3686
0
                 "Should not have an exception set here!");
3687
0
    return false;
3688
0
  }
3689
0
3690
0
  // Make sure the periodic timer gets turned back on here.
3691
0
  SetGCTimerMode(PeriodicTimer);
3692
0
3693
0
  return true;
3694
0
}
3695
3696
void
3697
WorkerPrivate::CloseInternal()
3698
0
{
3699
0
  AssertIsOnWorkerThread();
3700
0
  NotifyInternal(Closing);
3701
0
}
3702
3703
bool
3704
WorkerPrivate::IsOnCurrentThread()
3705
0
{
3706
0
  // May be called on any thread!
3707
0
3708
0
  MOZ_ASSERT(mPRThread);
3709
0
  return PR_GetCurrentThread() == mPRThread;
3710
0
}
3711
3712
void
3713
WorkerPrivate::ScheduleDeletion(WorkerRanOrNot aRanOrNot)
3714
0
{
3715
0
  AssertIsOnWorkerThread();
3716
0
  MOZ_ASSERT(mChildWorkers.IsEmpty());
3717
0
  MOZ_ASSERT(mSyncLoopStack.IsEmpty());
3718
0
  MOZ_ASSERT(!mPendingEventQueueClearing);
3719
0
3720
0
  ClearMainEventQueue(aRanOrNot);
3721
#ifdef DEBUG
3722
  if (WorkerRan == aRanOrNot) {
3723
    nsIThread* currentThread = NS_GetCurrentThread();
3724
    MOZ_ASSERT(currentThread);
3725
    MOZ_ASSERT(!NS_HasPendingEvents(currentThread));
3726
  }
3727
#endif
3728
3729
0
  if (WorkerPrivate* parent = GetParent()) {
3730
0
    RefPtr<WorkerFinishedRunnable> runnable =
3731
0
      new WorkerFinishedRunnable(parent, this);
3732
0
    if (!runnable->Dispatch()) {
3733
0
      NS_WARNING("Failed to dispatch runnable!");
3734
0
    }
3735
0
  }
3736
0
  else {
3737
0
    RefPtr<TopLevelWorkerFinishedRunnable> runnable =
3738
0
      new TopLevelWorkerFinishedRunnable(this);
3739
0
    if (NS_FAILED(DispatchToMainThread(runnable.forget()))) {
3740
0
      NS_WARNING("Failed to dispatch runnable!");
3741
0
    }
3742
0
  }
3743
0
}
3744
3745
bool
3746
WorkerPrivate::CollectRuntimeStats(JS::RuntimeStats* aRtStats,
3747
                                   bool aAnonymize)
3748
0
{
3749
0
  AssertIsOnWorkerThread();
3750
0
  NS_ASSERTION(aRtStats, "Null RuntimeStats!");
3751
0
  NS_ASSERTION(mJSContext, "This must never be null!");
3752
0
3753
0
  return JS::CollectRuntimeStats(mJSContext, aRtStats, nullptr, aAnonymize);
3754
0
}
3755
3756
void
3757
WorkerPrivate::EnableMemoryReporter()
3758
0
{
3759
0
  AssertIsOnWorkerThread();
3760
0
  MOZ_ASSERT(!mMemoryReporter);
3761
0
3762
0
  // No need to lock here since the main thread can't race until we've
3763
0
  // successfully registered the reporter.
3764
0
  mMemoryReporter = new MemoryReporter(this);
3765
0
3766
0
  if (NS_FAILED(RegisterWeakAsyncMemoryReporter(mMemoryReporter))) {
3767
0
    NS_WARNING("Failed to register memory reporter!");
3768
0
    // No need to lock here since a failed registration means our memory
3769
0
    // reporter can't start running. Just clean up.
3770
0
    mMemoryReporter = nullptr;
3771
0
  }
3772
0
}
3773
3774
void
3775
WorkerPrivate::DisableMemoryReporter()
3776
0
{
3777
0
  AssertIsOnWorkerThread();
3778
0
3779
0
  RefPtr<MemoryReporter> memoryReporter;
3780
0
  {
3781
0
    // Mutex protectes MemoryReporter::mWorkerPrivate which is cleared by
3782
0
    // MemoryReporter::Disable() below.
3783
0
    MutexAutoLock lock(mMutex);
3784
0
3785
0
    // There is nothing to do here if the memory reporter was never successfully
3786
0
    // registered.
3787
0
    if (!mMemoryReporter) {
3788
0
      return;
3789
0
    }
3790
0
3791
0
    // We don't need this set any longer. Swap it out so that we can unregister
3792
0
    // below.
3793
0
    mMemoryReporter.swap(memoryReporter);
3794
0
3795
0
    // Next disable the memory reporter so that the main thread stops trying to
3796
0
    // signal us.
3797
0
    memoryReporter->Disable();
3798
0
  }
3799
0
3800
0
  // Finally unregister the memory reporter.
3801
0
  if (NS_FAILED(UnregisterWeakMemoryReporter(memoryReporter))) {
3802
0
    NS_WARNING("Failed to unregister memory reporter!");
3803
0
  }
3804
0
}
3805
3806
void
3807
WorkerPrivate::WaitForWorkerEvents()
3808
0
{
3809
0
  AUTO_PROFILER_LABEL("WorkerPrivate::WaitForWorkerEvents", IDLE);
3810
0
3811
0
  AssertIsOnWorkerThread();
3812
0
  mMutex.AssertCurrentThreadOwns();
3813
0
3814
0
  // Wait for a worker event.
3815
0
  mCondVar.Wait();
3816
0
}
3817
3818
WorkerPrivate::ProcessAllControlRunnablesResult
3819
WorkerPrivate::ProcessAllControlRunnablesLocked()
3820
0
{
3821
0
  AssertIsOnWorkerThread();
3822
0
  mMutex.AssertCurrentThreadOwns();
3823
0
3824
0
  auto result = ProcessAllControlRunnablesResult::Nothing;
3825
0
3826
0
  for (;;) {
3827
0
    WorkerControlRunnable* event;
3828
0
    if (!mControlQueue.Pop(event)) {
3829
0
      break;
3830
0
    }
3831
0
3832
0
    MutexAutoUnlock unlock(mMutex);
3833
0
3834
0
    MOZ_ASSERT(event);
3835
0
    if (NS_FAILED(static_cast<nsIRunnable*>(event)->Run())) {
3836
0
      result = ProcessAllControlRunnablesResult::Abort;
3837
0
    }
3838
0
3839
0
    if (result == ProcessAllControlRunnablesResult::Nothing) {
3840
0
      // We ran at least one thing.
3841
0
      result = ProcessAllControlRunnablesResult::MayContinue;
3842
0
    }
3843
0
    event->Release();
3844
0
  }
3845
0
3846
0
  return result;
3847
0
}
3848
3849
void
3850
WorkerPrivate::ClearMainEventQueue(WorkerRanOrNot aRanOrNot)
3851
0
{
3852
0
  AssertIsOnWorkerThread();
3853
0
3854
0
  MOZ_ASSERT(mSyncLoopStack.IsEmpty());
3855
0
  MOZ_ASSERT(!mCancelAllPendingRunnables);
3856
0
  mCancelAllPendingRunnables = true;
3857
0
3858
0
  if (WorkerNeverRan == aRanOrNot) {
3859
0
    for (uint32_t count = mPreStartRunnables.Length(), index = 0;
3860
0
         index < count;
3861
0
         index++) {
3862
0
      RefPtr<WorkerRunnable> runnable = mPreStartRunnables[index].forget();
3863
0
      static_cast<nsIRunnable*>(runnable.get())->Run();
3864
0
    }
3865
0
  } else {
3866
0
    nsIThread* currentThread = NS_GetCurrentThread();
3867
0
    MOZ_ASSERT(currentThread);
3868
0
3869
0
    NS_ProcessPendingEvents(currentThread);
3870
0
  }
3871
0
3872
0
  MOZ_ASSERT(mCancelAllPendingRunnables);
3873
0
  mCancelAllPendingRunnables = false;
3874
0
}
3875
3876
void
3877
WorkerPrivate::ClearDebuggerEventQueue()
3878
0
{
3879
0
  while (!mDebuggerQueue.IsEmpty()) {
3880
0
    WorkerRunnable* runnable = nullptr;
3881
0
    mDebuggerQueue.Pop(runnable);
3882
0
    // It should be ok to simply release the runnable, without running it.
3883
0
    runnable->Release();
3884
0
  }
3885
0
}
3886
3887
bool
3888
WorkerPrivate::FreezeInternal()
3889
0
{
3890
0
  AssertIsOnWorkerThread();
3891
0
3892
0
  NS_ASSERTION(!mFrozen, "Already frozen!");
3893
0
3894
0
  if (mClientSource) {
3895
0
    mClientSource->Freeze();
3896
0
  }
3897
0
3898
0
  mFrozen = true;
3899
0
3900
0
  for (uint32_t index = 0; index < mChildWorkers.Length(); index++) {
3901
0
    mChildWorkers[index]->Freeze(nullptr);
3902
0
  }
3903
0
3904
0
  return true;
3905
0
}
3906
3907
bool
3908
WorkerPrivate::ThawInternal()
3909
0
{
3910
0
  AssertIsOnWorkerThread();
3911
0
3912
0
  NS_ASSERTION(mFrozen, "Not yet frozen!");
3913
0
3914
0
  for (uint32_t index = 0; index < mChildWorkers.Length(); index++) {
3915
0
    mChildWorkers[index]->Thaw(nullptr);
3916
0
  }
3917
0
3918
0
  mFrozen = false;
3919
0
3920
0
  if (mClientSource) {
3921
0
    mClientSource->Thaw();
3922
0
  }
3923
0
3924
0
  return true;
3925
0
}
3926
3927
void
3928
WorkerPrivate::PropagateFirstPartyStorageAccessGrantedInternal()
3929
0
{
3930
0
  AssertIsOnWorkerThread();
3931
0
3932
0
  mLoadInfo.mFirstPartyStorageAccessGranted = true;
3933
0
3934
0
  for (uint32_t index = 0; index < mChildWorkers.Length(); index++) {
3935
0
    mChildWorkers[index]->PropagateFirstPartyStorageAccessGranted();
3936
0
  }
3937
0
}
3938
3939
void
3940
WorkerPrivate::TraverseTimeouts(nsCycleCollectionTraversalCallback& cb)
3941
0
{
3942
0
  for (uint32_t i = 0; i < mTimeouts.Length(); ++i) {
3943
0
    TimeoutInfo* tmp = mTimeouts[i];
3944
0
    NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mHandler)
3945
0
  }
3946
0
}
3947
3948
void
3949
WorkerPrivate::UnlinkTimeouts()
3950
0
{
3951
0
  mTimeouts.Clear();
3952
0
}
3953
3954
bool
3955
WorkerPrivate::ModifyBusyCountFromWorker(bool aIncrease)
3956
0
{
3957
0
  AssertIsOnWorkerThread();
3958
0
3959
0
  {
3960
0
    MutexAutoLock lock(mMutex);
3961
0
3962
0
    // If we're in shutdown then the busy count is no longer being considered so
3963
0
    // just return now.
3964
0
    if (mStatus >= Killing) {
3965
0
      return true;
3966
0
    }
3967
0
  }
3968
0
3969
0
  RefPtr<ModifyBusyCountRunnable> runnable =
3970
0
    new ModifyBusyCountRunnable(this, aIncrease);
3971
0
  return runnable->Dispatch();
3972
0
}
3973
3974
bool
3975
WorkerPrivate::AddChildWorker(WorkerPrivate* aChildWorker)
3976
0
{
3977
0
  AssertIsOnWorkerThread();
3978
0
3979
#ifdef DEBUG
3980
  {
3981
    WorkerStatus currentStatus;
3982
    {
3983
      MutexAutoLock lock(mMutex);
3984
      currentStatus = mStatus;
3985
    }
3986
3987
    MOZ_ASSERT(currentStatus == Running);
3988
  }
3989
#endif
3990
3991
0
  NS_ASSERTION(!mChildWorkers.Contains(aChildWorker),
3992
0
               "Already know about this one!");
3993
0
  mChildWorkers.AppendElement(aChildWorker);
3994
0
3995
0
  return mChildWorkers.Length() == 1 ?
3996
0
         ModifyBusyCountFromWorker(true) :
3997
0
         true;
3998
0
}
3999
4000
void
4001
WorkerPrivate::RemoveChildWorker(WorkerPrivate* aChildWorker)
4002
0
{
4003
0
  AssertIsOnWorkerThread();
4004
0
4005
0
  NS_ASSERTION(mChildWorkers.Contains(aChildWorker),
4006
0
               "Didn't know about this one!");
4007
0
  mChildWorkers.RemoveElement(aChildWorker);
4008
0
4009
0
  if (mChildWorkers.IsEmpty() && !ModifyBusyCountFromWorker(false)) {
4010
0
    NS_WARNING("Failed to modify busy count!");
4011
0
  }
4012
0
}
4013
4014
bool
4015
WorkerPrivate::AddHolder(WorkerHolder* aHolder, WorkerStatus aFailStatus)
4016
0
{
4017
0
  AssertIsOnWorkerThread();
4018
0
4019
0
  {
4020
0
    MutexAutoLock lock(mMutex);
4021
0
4022
0
    if (mStatus >= aFailStatus) {
4023
0
      return false;
4024
0
    }
4025
0
  }
4026
0
4027
0
  MOZ_ASSERT(!mHolders.Contains(aHolder), "Already know about this one!");
4028
0
4029
0
  if (aHolder->GetBehavior() == WorkerHolder::PreventIdleShutdownStart) {
4030
0
    if (!mNumHoldersPreventingShutdownStart && !ModifyBusyCountFromWorker(true)) {
4031
0
      return false;
4032
0
    }
4033
0
    mNumHoldersPreventingShutdownStart += 1;
4034
0
  }
4035
0
4036
0
  mHolders.AppendElement(aHolder);
4037
0
  return true;
4038
0
}
4039
4040
void
4041
WorkerPrivate::RemoveHolder(WorkerHolder* aHolder)
4042
0
{
4043
0
  AssertIsOnWorkerThread();
4044
0
4045
0
  MOZ_ASSERT(mHolders.Contains(aHolder), "Didn't know about this one!");
4046
0
  mHolders.RemoveElement(aHolder);
4047
0
4048
0
  if (aHolder->GetBehavior() == WorkerHolder::PreventIdleShutdownStart) {
4049
0
    mNumHoldersPreventingShutdownStart -= 1;
4050
0
    if (!mNumHoldersPreventingShutdownStart && !ModifyBusyCountFromWorker(false)) {
4051
0
      NS_WARNING("Failed to modify busy count!");
4052
0
    }
4053
0
  }
4054
0
}
4055
4056
void
4057
WorkerPrivate::NotifyHolders(WorkerStatus aStatus)
4058
0
{
4059
0
  AssertIsOnWorkerThread();
4060
0
4061
0
  NS_ASSERTION(aStatus > Closing, "Bad status!");
4062
0
4063
0
  nsTObserverArray<WorkerHolder*>::ForwardIterator iter(mHolders);
4064
0
  while (iter.HasMore()) {
4065
0
    WorkerHolder* holder = iter.GetNext();
4066
0
    if (!holder->Notify(aStatus)) {
4067
0
      NS_WARNING("Failed to notify holder!");
4068
0
    }
4069
0
  }
4070
0
4071
0
  AutoTArray<WorkerPrivate*, 10> children;
4072
0
  children.AppendElements(mChildWorkers);
4073
0
4074
0
  for (uint32_t index = 0; index < children.Length(); index++) {
4075
0
    if (!children[index]->Notify(aStatus)) {
4076
0
      NS_WARNING("Failed to notify child worker!");
4077
0
    }
4078
0
  }
4079
0
}
4080
4081
void
4082
WorkerPrivate::CancelAllTimeouts()
4083
0
{
4084
0
  AssertIsOnWorkerThread();
4085
0
4086
0
  LOG(TimeoutsLog(), ("Worker %p CancelAllTimeouts.\n", this));
4087
0
4088
0
  if (mTimerRunning) {
4089
0
    NS_ASSERTION(mTimer && mTimerRunnable, "Huh?!");
4090
0
    NS_ASSERTION(!mTimeouts.IsEmpty(), "Huh?!");
4091
0
4092
0
    if (NS_FAILED(mTimer->Cancel())) {
4093
0
      NS_WARNING("Failed to cancel timer!");
4094
0
    }
4095
0
4096
0
    for (uint32_t index = 0; index < mTimeouts.Length(); index++) {
4097
0
      mTimeouts[index]->mCanceled = true;
4098
0
    }
4099
0
4100
0
    // If mRunningExpiredTimeouts, then the fact that they are all canceled now
4101
0
    // means that the currently executing RunExpiredTimeouts will deal with
4102
0
    // them.  Otherwise, we need to clean them up ourselves.
4103
0
    if (!mRunningExpiredTimeouts) {
4104
0
      mTimeouts.Clear();
4105
0
      ModifyBusyCountFromWorker(false);
4106
0
    }
4107
0
4108
0
    // Set mTimerRunning false even if mRunningExpiredTimeouts is true, so that
4109
0
    // if we get reentered under this same RunExpiredTimeouts call we don't
4110
0
    // assert above that !mTimeouts().IsEmpty(), because that's clearly false
4111
0
    // now.
4112
0
    mTimerRunning = false;
4113
0
  }
4114
#ifdef DEBUG
4115
  else if (!mRunningExpiredTimeouts) {
4116
    NS_ASSERTION(mTimeouts.IsEmpty(), "Huh?!");
4117
  }
4118
#endif
4119
4120
0
  mTimer = nullptr;
4121
0
  mTimerRunnable = nullptr;
4122
0
}
4123
4124
already_AddRefed<nsIEventTarget>
4125
WorkerPrivate::CreateNewSyncLoop(WorkerStatus aFailStatus)
4126
0
{
4127
0
  AssertIsOnWorkerThread();
4128
0
  MOZ_ASSERT(aFailStatus >= Canceling,
4129
0
             "Sync loops can be created when the worker is in Running/Closing state!");
4130
0
4131
0
  {
4132
0
    MutexAutoLock lock(mMutex);
4133
0
4134
0
    if (mStatus >= aFailStatus) {
4135
0
      return nullptr;
4136
0
    }
4137
0
  }
4138
0
4139
0
  auto queue = static_cast<ThreadEventQueue<EventQueue>*>(mThread->EventQueue());
4140
0
  nsCOMPtr<nsISerialEventTarget> realEventTarget = queue->PushEventQueue();
4141
0
  MOZ_ASSERT(realEventTarget);
4142
0
4143
0
  RefPtr<EventTarget> workerEventTarget =
4144
0
    new EventTarget(this, realEventTarget);
4145
0
4146
0
  {
4147
0
    // Modifications must be protected by mMutex in DEBUG builds, see comment
4148
0
    // about mSyncLoopStack in WorkerPrivate.h.
4149
#ifdef DEBUG
4150
    MutexAutoLock lock(mMutex);
4151
#endif
4152
4153
0
    mSyncLoopStack.AppendElement(new SyncLoopInfo(workerEventTarget));
4154
0
  }
4155
0
4156
0
  return workerEventTarget.forget();
4157
0
}
4158
4159
bool
4160
WorkerPrivate::RunCurrentSyncLoop()
4161
0
{
4162
0
  AssertIsOnWorkerThread();
4163
0
4164
0
  JSContext* cx = GetJSContext();
4165
0
  MOZ_ASSERT(cx);
4166
0
4167
0
  // This should not change between now and the time we finish running this sync
4168
0
  // loop.
4169
0
  uint32_t currentLoopIndex = mSyncLoopStack.Length() - 1;
4170
0
4171
0
  SyncLoopInfo* loopInfo = mSyncLoopStack[currentLoopIndex];
4172
0
4173
0
  MOZ_ASSERT(loopInfo);
4174
0
  MOZ_ASSERT(!loopInfo->mHasRun);
4175
0
  MOZ_ASSERT(!loopInfo->mCompleted);
4176
0
4177
#ifdef DEBUG
4178
  loopInfo->mHasRun = true;
4179
#endif
4180
4181
0
  while (!loopInfo->mCompleted) {
4182
0
    bool normalRunnablesPending = false;
4183
0
4184
0
    // Don't block with the periodic GC timer running.
4185
0
    if (!NS_HasPendingEvents(mThread)) {
4186
0
      SetGCTimerMode(IdleTimer);
4187
0
    }
4188
0
4189
0
    // Wait for something to do.
4190
0
    {
4191
0
      MutexAutoLock lock(mMutex);
4192
0
4193
0
      for (;;) {
4194
0
        while (mControlQueue.IsEmpty() &&
4195
0
               !normalRunnablesPending &&
4196
0
               !(normalRunnablesPending = NS_HasPendingEvents(mThread))) {
4197
0
          WaitForWorkerEvents();
4198
0
        }
4199
0
4200
0
        auto result = ProcessAllControlRunnablesLocked();
4201
0
        if (result != ProcessAllControlRunnablesResult::Nothing) {
4202
0
          // XXXkhuey how should we handle Abort here? See Bug 1003730.
4203
0
4204
0
          // The state of the world may have changed. Recheck it.
4205
0
          normalRunnablesPending = NS_HasPendingEvents(mThread);
4206
0
4207
0
          // NB: If we processed a NotifyRunnable, we might have run
4208
0
          // non-control runnables, one of which may have shut down the
4209
0
          // sync loop.
4210
0
          if (loopInfo->mCompleted) {
4211
0
            break;
4212
0
          }
4213
0
        }
4214
0
4215
0
        // If we *didn't* run any control runnables, this should be unchanged.
4216
0
        MOZ_ASSERT(!loopInfo->mCompleted);
4217
0
4218
0
        if (normalRunnablesPending) {
4219
0
          break;
4220
0
        }
4221
0
      }
4222
0
    }
4223
0
4224
0
    if (normalRunnablesPending) {
4225
0
      // Make sure the periodic timer is running before we continue.
4226
0
      SetGCTimerMode(PeriodicTimer);
4227
0
4228
0
      MOZ_ALWAYS_TRUE(NS_ProcessNextEvent(mThread, false));
4229
0
4230
0
      // Now *might* be a good time to GC. Let the JS engine make the decision.
4231
0
      if (JS::CurrentGlobalOrNull(cx)) {
4232
0
        JS_MaybeGC(cx);
4233
0
      }
4234
0
    }
4235
0
  }
4236
0
4237
0
  // Make sure that the stack didn't change underneath us.
4238
0
  MOZ_ASSERT(mSyncLoopStack[currentLoopIndex] == loopInfo);
4239
0
4240
0
  return DestroySyncLoop(currentLoopIndex);
4241
0
}
4242
4243
bool
4244
WorkerPrivate::DestroySyncLoop(uint32_t aLoopIndex)
4245
0
{
4246
0
  MOZ_ASSERT(!mSyncLoopStack.IsEmpty());
4247
0
  MOZ_ASSERT(mSyncLoopStack.Length() - 1 == aLoopIndex);
4248
0
4249
0
  // We're about to delete the loop, stash its event target and result.
4250
0
  SyncLoopInfo* loopInfo = mSyncLoopStack[aLoopIndex];
4251
0
  nsIEventTarget* nestedEventTarget =
4252
0
    loopInfo->mEventTarget->GetWeakNestedEventTarget();
4253
0
  MOZ_ASSERT(nestedEventTarget);
4254
0
4255
0
  bool result = loopInfo->mResult;
4256
0
4257
0
  {
4258
0
    // Modifications must be protected by mMutex in DEBUG builds, see comment
4259
0
    // about mSyncLoopStack in WorkerPrivate.h.
4260
#ifdef DEBUG
4261
    MutexAutoLock lock(mMutex);
4262
#endif
4263
4264
0
    // This will delete |loopInfo|!
4265
0
    mSyncLoopStack.RemoveElementAt(aLoopIndex);
4266
0
  }
4267
0
4268
0
  auto queue = static_cast<ThreadEventQueue<EventQueue>*>(mThread->EventQueue());
4269
0
  queue->PopEventQueue(nestedEventTarget);
4270
0
4271
0
  if (mSyncLoopStack.IsEmpty() && mPendingEventQueueClearing) {
4272
0
    mPendingEventQueueClearing = false;
4273
0
    ClearMainEventQueue(WorkerRan);
4274
0
  }
4275
0
4276
0
  return result;
4277
0
}
4278
4279
void
4280
WorkerPrivate::StopSyncLoop(nsIEventTarget* aSyncLoopTarget, bool aResult)
4281
0
{
4282
0
  AssertIsOnWorkerThread();
4283
0
  AssertValidSyncLoop(aSyncLoopTarget);
4284
0
4285
0
  MOZ_ASSERT(!mSyncLoopStack.IsEmpty());
4286
0
4287
0
  for (uint32_t index = mSyncLoopStack.Length(); index > 0; index--) {
4288
0
    nsAutoPtr<SyncLoopInfo>& loopInfo = mSyncLoopStack[index - 1];
4289
0
    MOZ_ASSERT(loopInfo);
4290
0
    MOZ_ASSERT(loopInfo->mEventTarget);
4291
0
4292
0
    if (loopInfo->mEventTarget == aSyncLoopTarget) {
4293
0
      // Can't assert |loop->mHasRun| here because dispatch failures can cause
4294
0
      // us to bail out early.
4295
0
      MOZ_ASSERT(!loopInfo->mCompleted);
4296
0
4297
0
      loopInfo->mResult = aResult;
4298
0
      loopInfo->mCompleted = true;
4299
0
4300
0
      loopInfo->mEventTarget->Disable();
4301
0
4302
0
      return;
4303
0
    }
4304
0
4305
0
    MOZ_ASSERT(!SameCOMIdentity(loopInfo->mEventTarget, aSyncLoopTarget));
4306
0
  }
4307
0
4308
0
  MOZ_CRASH("Unknown sync loop!");
4309
0
}
4310
4311
#ifdef DEBUG
4312
void
4313
WorkerPrivate::AssertValidSyncLoop(nsIEventTarget* aSyncLoopTarget)
4314
{
4315
  MOZ_ASSERT(aSyncLoopTarget);
4316
4317
  EventTarget* workerTarget;
4318
  nsresult rv =
4319
    aSyncLoopTarget->QueryInterface(kDEBUGWorkerEventTargetIID,
4320
                                    reinterpret_cast<void**>(&workerTarget));
4321
  MOZ_ASSERT(NS_SUCCEEDED(rv));
4322
  MOZ_ASSERT(workerTarget);
4323
4324
  bool valid = false;
4325
4326
  {
4327
    MutexAutoLock lock(mMutex);
4328
4329
    for (uint32_t index = 0; index < mSyncLoopStack.Length(); index++) {
4330
      nsAutoPtr<SyncLoopInfo>& loopInfo = mSyncLoopStack[index];
4331
      MOZ_ASSERT(loopInfo);
4332
      MOZ_ASSERT(loopInfo->mEventTarget);
4333
4334
      if (loopInfo->mEventTarget == aSyncLoopTarget) {
4335
        valid = true;
4336
        break;
4337
      }
4338
4339
      MOZ_ASSERT(!SameCOMIdentity(loopInfo->mEventTarget, aSyncLoopTarget));
4340
    }
4341
  }
4342
4343
  MOZ_ASSERT(valid);
4344
}
4345
#endif
4346
4347
void
4348
WorkerPrivate::PostMessageToParent(
4349
                            JSContext* aCx,
4350
                            JS::Handle<JS::Value> aMessage,
4351
                            const Sequence<JSObject*>& aTransferable,
4352
                            ErrorResult& aRv)
4353
0
{
4354
0
  AssertIsOnWorkerThread();
4355
0
4356
0
  JS::Rooted<JS::Value> transferable(aCx, JS::UndefinedValue());
4357
0
4358
0
  aRv = nsContentUtils::CreateJSValueFromSequenceOfObject(aCx, aTransferable,
4359
0
                                                          &transferable);
4360
0
  if (NS_WARN_IF(aRv.Failed())) {
4361
0
    return;
4362
0
  }
4363
0
4364
0
  RefPtr<MessageEventRunnable> runnable =
4365
0
    new MessageEventRunnable(this,
4366
0
                             WorkerRunnable::ParentThreadUnchangedBusyCount);
4367
0
4368
0
  UniquePtr<AbstractTimelineMarker> start;
4369
0
  UniquePtr<AbstractTimelineMarker> end;
4370
0
  RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
4371
0
  bool isTimelineRecording = timelines && !timelines->IsEmpty();
4372
0
4373
0
  if (isTimelineRecording) {
4374
0
    start = MakeUnique<WorkerTimelineMarker>(NS_IsMainThread()
4375
0
      ? ProfileTimelineWorkerOperationType::SerializeDataOnMainThread
4376
0
      : ProfileTimelineWorkerOperationType::SerializeDataOffMainThread,
4377
0
      MarkerTracingType::START);
4378
0
  }
4379
0
4380
0
  runnable->Write(aCx, aMessage, transferable, JS::CloneDataPolicy(), aRv);
4381
0
4382
0
  if (isTimelineRecording) {
4383
0
    end = MakeUnique<WorkerTimelineMarker>(NS_IsMainThread()
4384
0
      ? ProfileTimelineWorkerOperationType::SerializeDataOnMainThread
4385
0
      : ProfileTimelineWorkerOperationType::SerializeDataOffMainThread,
4386
0
      MarkerTracingType::END);
4387
0
    timelines->AddMarkerForAllObservedDocShells(start);
4388
0
    timelines->AddMarkerForAllObservedDocShells(end);
4389
0
  }
4390
0
4391
0
  if (NS_WARN_IF(aRv.Failed())) {
4392
0
    return;
4393
0
  }
4394
0
4395
0
  if (!runnable->Dispatch()) {
4396
0
    aRv = NS_ERROR_FAILURE;
4397
0
  }
4398
0
}
4399
4400
void
4401
WorkerPrivate::EnterDebuggerEventLoop()
4402
0
{
4403
0
  AssertIsOnWorkerThread();
4404
0
4405
0
  JSContext* cx = GetJSContext();
4406
0
  MOZ_ASSERT(cx);
4407
0
  CycleCollectedJSContext* ccjscx = CycleCollectedJSContext::Get();
4408
0
4409
0
  uint32_t currentEventLoopLevel = ++mDebuggerEventLoopLevel;
4410
0
4411
0
  while (currentEventLoopLevel <= mDebuggerEventLoopLevel) {
4412
0
4413
0
    bool debuggerRunnablesPending = false;
4414
0
4415
0
    {
4416
0
      MutexAutoLock lock(mMutex);
4417
0
4418
0
      debuggerRunnablesPending = !mDebuggerQueue.IsEmpty();
4419
0
    }
4420
0
4421
0
    // Don't block with the periodic GC timer running.
4422
0
    if (!debuggerRunnablesPending) {
4423
0
      SetGCTimerMode(IdleTimer);
4424
0
    }
4425
0
4426
0
    // Wait for something to do
4427
0
    {
4428
0
      MutexAutoLock lock(mMutex);
4429
0
4430
0
      std::queue<RefPtr<MicroTaskRunnable>>& debuggerMtQueue =
4431
0
        ccjscx->GetDebuggerMicroTaskQueue();
4432
0
      while (mControlQueue.IsEmpty() &&
4433
0
             !(debuggerRunnablesPending = !mDebuggerQueue.IsEmpty()) &&
4434
0
             debuggerMtQueue.empty()) {
4435
0
        WaitForWorkerEvents();
4436
0
      }
4437
0
4438
0
      ProcessAllControlRunnablesLocked();
4439
0
4440
0
      // XXXkhuey should we abort JS on the stack here if we got Abort above?
4441
0
    }
4442
0
    ccjscx->PerformDebuggerMicroTaskCheckpoint();
4443
0
    if (debuggerRunnablesPending) {
4444
0
      // Start the periodic GC timer if it is not already running.
4445
0
      SetGCTimerMode(PeriodicTimer);
4446
0
4447
0
      WorkerRunnable* runnable = nullptr;
4448
0
4449
0
      {
4450
0
        MutexAutoLock lock(mMutex);
4451
0
4452
0
        mDebuggerQueue.Pop(runnable);
4453
0
      }
4454
0
4455
0
      MOZ_ASSERT(runnable);
4456
0
      static_cast<nsIRunnable*>(runnable)->Run();
4457
0
      runnable->Release();
4458
0
4459
0
      ccjscx->PerformDebuggerMicroTaskCheckpoint();
4460
0
4461
0
      // Now *might* be a good time to GC. Let the JS engine make the decision.
4462
0
      if (JS::CurrentGlobalOrNull(cx)) {
4463
0
        JS_MaybeGC(cx);
4464
0
      }
4465
0
    }
4466
0
  }
4467
0
}
4468
4469
void
4470
WorkerPrivate::LeaveDebuggerEventLoop()
4471
0
{
4472
0
  AssertIsOnWorkerThread();
4473
0
4474
0
  MutexAutoLock lock(mMutex);
4475
0
4476
0
  if (mDebuggerEventLoopLevel > 0) {
4477
0
    --mDebuggerEventLoopLevel;
4478
0
  }
4479
0
}
4480
4481
void
4482
WorkerPrivate::PostMessageToDebugger(const nsAString& aMessage)
4483
0
{
4484
0
  mDebugger->PostMessageToDebugger(aMessage);
4485
0
}
4486
4487
void
4488
WorkerPrivate::SetDebuggerImmediate(dom::Function& aHandler, ErrorResult& aRv)
4489
0
{
4490
0
  AssertIsOnWorkerThread();
4491
0
4492
0
  RefPtr<DebuggerImmediateRunnable> runnable =
4493
0
    new DebuggerImmediateRunnable(this, aHandler);
4494
0
  if (!runnable->Dispatch()) {
4495
0
    aRv.Throw(NS_ERROR_FAILURE);
4496
0
  }
4497
0
}
4498
4499
void
4500
WorkerPrivate::ReportErrorToDebugger(const nsAString& aFilename,
4501
                                     uint32_t aLineno,
4502
                                     const nsAString& aMessage)
4503
0
{
4504
0
  mDebugger->ReportErrorToDebugger(aFilename, aLineno, aMessage);
4505
0
}
4506
4507
bool
4508
WorkerPrivate::NotifyInternal(WorkerStatus aStatus)
4509
0
{
4510
0
  AssertIsOnWorkerThread();
4511
0
4512
0
  NS_ASSERTION(aStatus > Running && aStatus < Dead, "Bad status!");
4513
0
4514
0
  RefPtr<EventTarget> eventTarget;
4515
0
4516
0
  // Save the old status and set the new status.
4517
0
  WorkerStatus previousStatus;
4518
0
  {
4519
0
    MutexAutoLock lock(mMutex);
4520
0
4521
0
    if (mStatus >= aStatus) {
4522
0
      return true;
4523
0
    }
4524
0
4525
0
    if (aStatus >= Canceling) {
4526
0
      MutexAutoUnlock unlock(mMutex);
4527
0
      mClientSource.reset();
4528
0
      if (mScope) {
4529
0
        mScope->NoteTerminating();
4530
0
      }
4531
0
    }
4532
0
4533
0
    // Make sure the hybrid event target stops dispatching runnables
4534
0
    // once we reaching the killing state.
4535
0
    if (aStatus == Killing) {
4536
0
      // To avoid deadlock we always acquire the event target mutex before the
4537
0
      // worker private mutex.  (We do it in this order because this is what
4538
0
      // workers best for event dispatching.)  To enforce that order here we
4539
0
      // need to unlock the worker private mutex before we lock the event target
4540
0
      // mutex in ForgetWorkerPrivate.
4541
0
      {
4542
0
        MutexAutoUnlock unlock(mMutex);
4543
0
        mWorkerHybridEventTarget->ForgetWorkerPrivate(this);
4544
0
      }
4545
0
4546
0
      // Check the status code again in case another NotifyInternal came in
4547
0
      // while we were unlocked above.
4548
0
      if (mStatus >= aStatus) {
4549
0
        return true;
4550
0
      }
4551
0
    }
4552
0
4553
0
    previousStatus = mStatus;
4554
0
    mStatus = aStatus;
4555
0
4556
0
    // Mark parent status as closing immediately to avoid new events being
4557
0
    // dispatched after we clear the queue below.
4558
0
    if (aStatus == Closing) {
4559
0
      Close();
4560
0
    }
4561
0
  }
4562
0
4563
0
  MOZ_ASSERT(previousStatus != Pending);
4564
0
4565
0
  if (aStatus >= Closing) {
4566
0
    CancelAllTimeouts();
4567
0
  }
4568
0
4569
0
  // Let all our holders know the new status.
4570
0
  if (aStatus > Closing) {
4571
0
    NotifyHolders(aStatus);
4572
0
  }
4573
0
4574
0
  // If this is the first time our status has changed then we need to clear the
4575
0
  // main event queue.
4576
0
  if (previousStatus == Running) {
4577
0
    // NB: If we're in a sync loop, we can't clear the queue immediately,
4578
0
    // because this is the wrong queue. So we have to defer it until later.
4579
0
    if (!mSyncLoopStack.IsEmpty()) {
4580
0
      mPendingEventQueueClearing = true;
4581
0
    } else {
4582
0
      ClearMainEventQueue(WorkerRan);
4583
0
    }
4584
0
  }
4585
0
4586
0
  // If the worker script never ran, or failed to compile, we don't need to do
4587
0
  // anything else.
4588
0
  if (!GlobalScope()) {
4589
0
    return true;
4590
0
  }
4591
0
4592
0
  // Don't abort the script now, but we dispatch a runnable to do it when the
4593
0
  // current JS frame is executed.
4594
0
  if (aStatus == Closing) {
4595
0
    if (mSyncLoopStack.IsEmpty()) {
4596
0
      // Here we use a normal runnable to know when the current JS chunk of code
4597
0
      // is finished. We cannot use a WorkerRunnable because they are not
4598
0
      // accepted any more by the worker, and we do not want to use a
4599
0
      // WorkerControlRunnable because they are immediately executed.
4600
0
      RefPtr<CancelingRunnable> r = new CancelingRunnable();
4601
0
      mThread->nsThread::Dispatch(r.forget(), NS_DISPATCH_NORMAL);
4602
0
4603
0
      // At the same time, we want to be sure that we interrupt infinite loops.
4604
0
      // The following runnable starts a timer that cancel the worker, from the
4605
0
      // parent thread, after CANCELING_TIMEOUT millseconds.
4606
0
      RefPtr<CancelingWithTimeoutOnParentRunnable> rr =
4607
0
        new CancelingWithTimeoutOnParentRunnable(this);
4608
0
      rr->Dispatch();
4609
0
    }
4610
0
    return true;
4611
0
  }
4612
0
4613
0
  MOZ_ASSERT(aStatus == Canceling || aStatus == Killing);
4614
0
4615
0
  // Always abort the script.
4616
0
  return false;
4617
0
}
4618
4619
void
4620
WorkerPrivate::ReportError(JSContext* aCx, JS::ConstUTF8CharsZ aToStringResult,
4621
                           JSErrorReport* aReport)
4622
0
{
4623
0
  AssertIsOnWorkerThread();
4624
0
4625
0
  if (!MayContinueRunning() || mErrorHandlerRecursionCount == 2) {
4626
0
    return;
4627
0
  }
4628
0
4629
0
  NS_ASSERTION(mErrorHandlerRecursionCount == 0 ||
4630
0
               mErrorHandlerRecursionCount == 1,
4631
0
               "Bad recursion logic!");
4632
0
4633
0
  JS::Rooted<JS::Value> exn(aCx);
4634
0
  if (!JS_GetPendingException(aCx, &exn)) {
4635
0
    // Probably shouldn't actually happen?  But let's go ahead and just use null
4636
0
    // for lack of anything better.
4637
0
    exn.setNull();
4638
0
  }
4639
0
  JS_ClearPendingException(aCx);
4640
0
4641
0
  WorkerErrorReport report;
4642
0
  if (aReport) {
4643
0
    report.AssignErrorReport(aReport);
4644
0
  }
4645
0
  else {
4646
0
    report.mFlags = nsIScriptError::errorFlag | nsIScriptError::exceptionFlag;
4647
0
  }
4648
0
4649
0
  if (report.mMessage.IsEmpty() && aToStringResult) {
4650
0
    nsDependentCString toStringResult(aToStringResult.c_str());
4651
0
    if (!AppendUTF8toUTF16(toStringResult, report.mMessage, mozilla::fallible)) {
4652
0
      // Try again, with only a 1 KB string. Do this infallibly this time.
4653
0
      // If the user doesn't have 1 KB to spare we're done anyways.
4654
0
      uint32_t index = std::min(uint32_t(1024), toStringResult.Length());
4655
0
4656
0
      // Drop the last code point that may be cropped.
4657
0
      index = RewindToPriorUTF8Codepoint(toStringResult.BeginReading(), index);
4658
0
4659
0
      nsDependentCString truncatedToStringResult(aToStringResult.c_str(),
4660
0
                                                 index);
4661
0
      AppendUTF8toUTF16(truncatedToStringResult, report.mMessage);
4662
0
    }
4663
0
  }
4664
0
4665
0
  mErrorHandlerRecursionCount++;
4666
0
4667
0
  // Don't want to run the scope's error handler if this is a recursive error or
4668
0
  // if we ran out of memory.
4669
0
  bool fireAtScope = mErrorHandlerRecursionCount == 1 &&
4670
0
                     report.mErrorNumber != JSMSG_OUT_OF_MEMORY &&
4671
0
                     JS::CurrentGlobalOrNull(aCx);
4672
0
4673
0
  WorkerErrorReport::ReportError(aCx, this, fireAtScope, nullptr, report, 0,
4674
0
                                 exn);
4675
0
4676
0
  mErrorHandlerRecursionCount--;
4677
0
}
4678
4679
// static
4680
void
4681
WorkerPrivate::ReportErrorToConsole(const char* aMessage)
4682
0
{
4683
0
  nsTArray<nsString> emptyParams;
4684
0
  WorkerPrivate::ReportErrorToConsole(aMessage, emptyParams);
4685
0
}
4686
4687
// static
4688
void
4689
WorkerPrivate::ReportErrorToConsole(const char* aMessage,
4690
                                    const nsTArray<nsString>& aParams)
4691
0
{
4692
0
  WorkerPrivate* wp = nullptr;
4693
0
  if (!NS_IsMainThread()) {
4694
0
    wp = GetCurrentThreadWorkerPrivate();
4695
0
  }
4696
0
4697
0
  ReportErrorToConsoleRunnable::Report(wp, aMessage, aParams);
4698
0
}
4699
4700
int32_t
4701
WorkerPrivate::SetTimeout(JSContext* aCx,
4702
                          nsIScriptTimeoutHandler* aHandler,
4703
                          int32_t aTimeout, bool aIsInterval,
4704
                          ErrorResult& aRv)
4705
0
{
4706
0
  AssertIsOnWorkerThread();
4707
0
  MOZ_ASSERT(aHandler);
4708
0
4709
0
  const int32_t timerId = mNextTimeoutId++;
4710
0
4711
0
  WorkerStatus currentStatus;
4712
0
  {
4713
0
    MutexAutoLock lock(mMutex);
4714
0
    currentStatus = mStatus;
4715
0
  }
4716
0
4717
0
  // If the worker is trying to call setTimeout/setInterval and the parent
4718
0
  // thread has initiated the close process then just silently fail.
4719
0
  if (currentStatus >= Closing) {
4720
0
    return timerId;
4721
0
  }
4722
0
4723
0
  nsAutoPtr<TimeoutInfo> newInfo(new TimeoutInfo());
4724
0
  newInfo->mIsInterval = aIsInterval;
4725
0
  newInfo->mId = timerId;
4726
0
4727
0
  if (MOZ_UNLIKELY(timerId == INT32_MAX)) {
4728
0
    NS_WARNING("Timeout ids overflowed!");
4729
0
    mNextTimeoutId = 1;
4730
0
  }
4731
0
4732
0
  newInfo->mHandler = aHandler;
4733
0
4734
0
  // See if any of the optional arguments were passed.
4735
0
  aTimeout = std::max(0, aTimeout);
4736
0
  newInfo->mInterval = TimeDuration::FromMilliseconds(aTimeout);
4737
0
4738
0
  newInfo->mTargetTime = TimeStamp::Now() + newInfo->mInterval;
4739
0
4740
0
  nsAutoPtr<TimeoutInfo>* insertedInfo =
4741
0
    mTimeouts.InsertElementSorted(newInfo.forget(), GetAutoPtrComparator(mTimeouts));
4742
0
4743
0
  LOG(TimeoutsLog(), ("Worker %p has new timeout: delay=%d interval=%s\n",
4744
0
                      this, aTimeout, aIsInterval ? "yes" : "no"));
4745
0
4746
0
  // If the timeout we just made is set to fire next then we need to update the
4747
0
  // timer, unless we're currently running timeouts.
4748
0
  if (insertedInfo == mTimeouts.Elements() && !mRunningExpiredTimeouts) {
4749
0
    if (!mTimer) {
4750
0
      mTimer = NS_NewTimer();
4751
0
      if (!mTimer) {
4752
0
        aRv.Throw(NS_ERROR_UNEXPECTED);
4753
0
        return 0;
4754
0
      }
4755
0
4756
0
      mTimerRunnable = new TimerRunnable(this);
4757
0
    }
4758
0
4759
0
    if (!mTimerRunning) {
4760
0
      if (!ModifyBusyCountFromWorker(true)) {
4761
0
        aRv.Throw(NS_ERROR_FAILURE);
4762
0
        return 0;
4763
0
      }
4764
0
      mTimerRunning = true;
4765
0
    }
4766
0
4767
0
    if (!RescheduleTimeoutTimer(aCx)) {
4768
0
      aRv.Throw(NS_ERROR_FAILURE);
4769
0
      return 0;
4770
0
    }
4771
0
  }
4772
0
4773
0
  return timerId;
4774
0
}
4775
4776
void
4777
WorkerPrivate::ClearTimeout(int32_t aId)
4778
0
{
4779
0
  AssertIsOnWorkerThread();
4780
0
4781
0
  if (!mTimeouts.IsEmpty()) {
4782
0
    NS_ASSERTION(mTimerRunning, "Huh?!");
4783
0
4784
0
    for (uint32_t index = 0; index < mTimeouts.Length(); index++) {
4785
0
      nsAutoPtr<TimeoutInfo>& info = mTimeouts[index];
4786
0
      if (info->mId == aId) {
4787
0
        info->mCanceled = true;
4788
0
        break;
4789
0
      }
4790
0
    }
4791
0
  }
4792
0
}
4793
4794
bool
4795
WorkerPrivate::RunExpiredTimeouts(JSContext* aCx)
4796
0
{
4797
0
  AssertIsOnWorkerThread();
4798
0
4799
0
  // We may be called recursively (e.g. close() inside a timeout) or we could
4800
0
  // have been canceled while this event was pending, bail out if there is
4801
0
  // nothing to do.
4802
0
  if (mRunningExpiredTimeouts || !mTimerRunning) {
4803
0
    return true;
4804
0
  }
4805
0
4806
0
  NS_ASSERTION(mTimer && mTimerRunnable, "Must have a timer!");
4807
0
  NS_ASSERTION(!mTimeouts.IsEmpty(), "Should have some work to do!");
4808
0
4809
0
  bool retval = true;
4810
0
4811
0
  AutoPtrComparator<TimeoutInfo> comparator = GetAutoPtrComparator(mTimeouts);
4812
0
  JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx));
4813
0
4814
0
  // We want to make sure to run *something*, even if the timer fired a little
4815
0
  // early. Fudge the value of now to at least include the first timeout.
4816
0
  const TimeStamp actual_now = TimeStamp::Now();
4817
0
  const TimeStamp now = std::max(actual_now, mTimeouts[0]->mTargetTime);
4818
0
4819
0
  if (now != actual_now) {
4820
0
    LOG(TimeoutsLog(), ("Worker %p fudged timeout by %f ms.\n", this,
4821
0
                        (now - actual_now).ToMilliseconds()));
4822
0
  }
4823
0
4824
0
  AutoTArray<TimeoutInfo*, 10> expiredTimeouts;
4825
0
  for (uint32_t index = 0; index < mTimeouts.Length(); index++) {
4826
0
    nsAutoPtr<TimeoutInfo>& info = mTimeouts[index];
4827
0
    if (info->mTargetTime > now) {
4828
0
      break;
4829
0
    }
4830
0
    expiredTimeouts.AppendElement(info);
4831
0
  }
4832
0
4833
0
  // Guard against recursion.
4834
0
  mRunningExpiredTimeouts = true;
4835
0
4836
0
  // Run expired timeouts.
4837
0
  for (uint32_t index = 0; index < expiredTimeouts.Length(); index++) {
4838
0
    TimeoutInfo*& info = expiredTimeouts[index];
4839
0
4840
0
    if (info->mCanceled) {
4841
0
      continue;
4842
0
    }
4843
0
4844
0
    LOG(TimeoutsLog(), ("Worker %p executing timeout with original delay %f ms.\n",
4845
0
                        this, info->mInterval.ToMilliseconds()));
4846
0
4847
0
    // Always check JS_IsExceptionPending if something fails, and if
4848
0
    // JS_IsExceptionPending returns false (i.e. uncatchable exception) then
4849
0
    // break out of the loop.
4850
0
    const char *reason;
4851
0
    if (info->mIsInterval) {
4852
0
      reason = "setInterval handler";
4853
0
    } else {
4854
0
      reason = "setTimeout handler";
4855
0
    }
4856
0
4857
0
    RefPtr<Function> callback = info->mHandler->GetCallback();
4858
0
    if (!callback) {
4859
0
      nsAutoMicroTask mt;
4860
0
4861
0
      AutoEntryScript aes(global, reason, false);
4862
0
4863
0
      // Evaluate the timeout expression.
4864
0
      const nsAString& script = info->mHandler->GetHandlerText();
4865
0
4866
0
      const char* filename = nullptr;
4867
0
      uint32_t lineNo = 0, dummyColumn = 0;
4868
0
      info->mHandler->GetLocation(&filename, &lineNo, &dummyColumn);
4869
0
4870
0
      JS::CompileOptions options(aes.cx());
4871
0
      options.setFileAndLine(filename, lineNo).setNoScriptRval(true);
4872
0
4873
0
      JS::Rooted<JS::Value> unused(aes.cx());
4874
0
4875
0
      JS::SourceBufferHolder srcBuf(script.BeginReading(), script.Length(),
4876
0
                                    JS::SourceBufferHolder::NoOwnership);
4877
0
      if (!JS::Evaluate(aes.cx(), options, srcBuf, &unused) &&
4878
0
          !JS_IsExceptionPending(aCx)) {
4879
0
        retval = false;
4880
0
        break;
4881
0
      }
4882
0
    } else {
4883
0
      ErrorResult rv;
4884
0
      JS::Rooted<JS::Value> ignoredVal(aCx);
4885
0
      callback->Call(GlobalScope(), info->mHandler->GetArgs(), &ignoredVal, rv,
4886
0
                     reason);
4887
0
      if (rv.IsUncatchableException()) {
4888
0
        rv.SuppressException();
4889
0
        retval = false;
4890
0
        break;
4891
0
      }
4892
0
4893
0
      rv.SuppressException();
4894
0
    }
4895
0
4896
0
    NS_ASSERTION(mRunningExpiredTimeouts, "Someone changed this!");
4897
0
  }
4898
0
4899
0
  // No longer possible to be called recursively.
4900
0
  mRunningExpiredTimeouts = false;
4901
0
4902
0
  // Now remove canceled and expired timeouts from the main list.
4903
0
  // NB: The timeouts present in expiredTimeouts must have the same order
4904
0
  // with respect to each other in mTimeouts.  That is, mTimeouts is just
4905
0
  // expiredTimeouts with extra elements inserted.  There may be unexpired
4906
0
  // timeouts that have been inserted between the expired timeouts if the
4907
0
  // timeout event handler called setTimeout/setInterval.
4908
0
  for (uint32_t index = 0, expiredTimeoutIndex = 0,
4909
0
       expiredTimeoutLength = expiredTimeouts.Length();
4910
0
       index < mTimeouts.Length(); ) {
4911
0
    nsAutoPtr<TimeoutInfo>& info = mTimeouts[index];
4912
0
    if ((expiredTimeoutIndex < expiredTimeoutLength &&
4913
0
         info == expiredTimeouts[expiredTimeoutIndex] &&
4914
0
         ++expiredTimeoutIndex) ||
4915
0
        info->mCanceled) {
4916
0
      if (info->mIsInterval && !info->mCanceled) {
4917
0
        // Reschedule intervals.
4918
0
        info->mTargetTime = info->mTargetTime + info->mInterval;
4919
0
        // Don't resort the list here, we'll do that at the end.
4920
0
        ++index;
4921
0
      }
4922
0
      else {
4923
0
        mTimeouts.RemoveElement(info);
4924
0
      }
4925
0
    }
4926
0
    else {
4927
0
      // If info did not match the current entry in expiredTimeouts, it
4928
0
      // shouldn't be there at all.
4929
0
      NS_ASSERTION(!expiredTimeouts.Contains(info),
4930
0
                   "Our timeouts are out of order!");
4931
0
      ++index;
4932
0
    }
4933
0
  }
4934
0
4935
0
  mTimeouts.Sort(comparator);
4936
0
4937
0
  // Either signal the parent that we're no longer using timeouts or reschedule
4938
0
  // the timer.
4939
0
  if (mTimeouts.IsEmpty()) {
4940
0
    if (!ModifyBusyCountFromWorker(false)) {
4941
0
      retval = false;
4942
0
    }
4943
0
    mTimerRunning = false;
4944
0
  }
4945
0
  else if (retval && !RescheduleTimeoutTimer(aCx)) {
4946
0
    retval = false;
4947
0
  }
4948
0
4949
0
  return retval;
4950
0
}
4951
4952
bool
4953
WorkerPrivate::RescheduleTimeoutTimer(JSContext* aCx)
4954
0
{
4955
0
  AssertIsOnWorkerThread();
4956
0
  MOZ_ASSERT(!mRunningExpiredTimeouts);
4957
0
  NS_ASSERTION(!mTimeouts.IsEmpty(), "Should have some timeouts!");
4958
0
  NS_ASSERTION(mTimer && mTimerRunnable, "Should have a timer!");
4959
0
4960
0
  // NB: This is important! The timer may have already fired, e.g. if a timeout
4961
0
  // callback itself calls setTimeout for a short duration and then takes longer
4962
0
  // than that to finish executing. If that has happened, it's very important
4963
0
  // that we don't execute the event that is now pending in our event queue, or
4964
0
  // our code in RunExpiredTimeouts to "fudge" the timeout value will unleash an
4965
0
  // early timeout when we execute the event we're about to queue.
4966
0
  mTimer->Cancel();
4967
0
4968
0
  double delta =
4969
0
    (mTimeouts[0]->mTargetTime - TimeStamp::Now()).ToMilliseconds();
4970
0
  uint32_t delay = delta > 0 ? std::min(delta, double(UINT32_MAX)) : 0;
4971
0
4972
0
  LOG(TimeoutsLog(), ("Worker %p scheduled timer for %d ms, %zu pending timeouts\n",
4973
0
                      this, delay, mTimeouts.Length()));
4974
0
4975
0
  nsresult rv = mTimer->InitWithCallback(mTimerRunnable, delay, nsITimer::TYPE_ONE_SHOT);
4976
0
  if (NS_FAILED(rv)) {
4977
0
    JS_ReportErrorASCII(aCx, "Failed to start timer!");
4978
0
    return false;
4979
0
  }
4980
0
4981
0
  return true;
4982
0
}
4983
4984
void
4985
WorkerPrivate::StartCancelingTimer()
4986
0
{
4987
0
  AssertIsOnParentThread();
4988
0
4989
0
  auto errorCleanup = MakeScopeExit([&] {
4990
0
    mCancelingTimer = nullptr;
4991
0
  });
4992
0
4993
0
  MOZ_ASSERT(!mCancelingTimer);
4994
0
4995
0
  if (WorkerPrivate* parent = GetParent()) {
4996
0
    mCancelingTimer = NS_NewTimer(parent->ControlEventTarget());
4997
0
  } else {
4998
0
    mCancelingTimer = NS_NewTimer();
4999
0
  }
5000
0
5001
0
  if (NS_WARN_IF(!mCancelingTimer)) {
5002
0
    return;
5003
0
  }
5004
0
5005
0
  // This is not needed if we are already in an advanced shutdown state.
5006
0
  {
5007
0
    MutexAutoLock lock(mMutex);
5008
0
    if (ParentStatus() >= Canceling) {
5009
0
      return;
5010
0
    }
5011
0
  }
5012
0
5013
0
  uint32_t cancelingTimeoutMillis =
5014
0
    StaticPrefs::dom_worker_canceling_timeoutMilliseconds();
5015
0
5016
0
  RefPtr<CancelingTimerCallback> callback = new CancelingTimerCallback(this);
5017
0
  nsresult rv = mCancelingTimer->InitWithCallback(callback,
5018
0
                                                  cancelingTimeoutMillis,
5019
0
                                                  nsITimer::TYPE_ONE_SHOT);
5020
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
5021
0
    return;
5022
0
  }
5023
0
5024
0
  errorCleanup.release();
5025
0
}
5026
5027
void
5028
WorkerPrivate::UpdateContextOptionsInternal(
5029
                                    JSContext* aCx,
5030
                                    const JS::ContextOptions& aContextOptions)
5031
0
{
5032
0
  AssertIsOnWorkerThread();
5033
0
5034
0
  JS::ContextOptionsRef(aCx) = aContextOptions;
5035
0
5036
0
  for (uint32_t index = 0; index < mChildWorkers.Length(); index++) {
5037
0
    mChildWorkers[index]->UpdateContextOptions(aContextOptions);
5038
0
  }
5039
0
}
5040
5041
void
5042
WorkerPrivate::UpdateLanguagesInternal(const nsTArray<nsString>& aLanguages)
5043
0
{
5044
0
  WorkerGlobalScope* globalScope = GlobalScope();
5045
0
  if (globalScope) {
5046
0
    RefPtr<WorkerNavigator> nav = globalScope->GetExistingNavigator();
5047
0
    if (nav) {
5048
0
      nav->SetLanguages(aLanguages);
5049
0
    }
5050
0
  }
5051
0
5052
0
  for (uint32_t index = 0; index < mChildWorkers.Length(); index++) {
5053
0
    mChildWorkers[index]->UpdateLanguages(aLanguages);
5054
0
  }
5055
0
}
5056
5057
void
5058
WorkerPrivate::UpdateJSWorkerMemoryParameterInternal(JSContext* aCx,
5059
                                                     JSGCParamKey aKey,
5060
                                                     uint32_t aValue)
5061
0
{
5062
0
  AssertIsOnWorkerThread();
5063
0
5064
0
  // XXX aValue might be 0 here (telling us to unset a previous value for child
5065
0
  // workers). Calling JS_SetGCParameter with a value of 0 isn't actually
5066
0
  // supported though. We really need some way to revert to a default value
5067
0
  // here.
5068
0
  if (aValue) {
5069
0
    JS_SetGCParameter(aCx, aKey, aValue);
5070
0
  }
5071
0
5072
0
  for (uint32_t index = 0; index < mChildWorkers.Length(); index++) {
5073
0
    mChildWorkers[index]->UpdateJSWorkerMemoryParameter(aKey, aValue);
5074
0
  }
5075
0
}
5076
5077
#ifdef JS_GC_ZEAL
5078
void
5079
WorkerPrivate::UpdateGCZealInternal(JSContext* aCx, uint8_t aGCZeal,
5080
                                    uint32_t aFrequency)
5081
{
5082
  AssertIsOnWorkerThread();
5083
5084
  JS_SetGCZeal(aCx, aGCZeal, aFrequency);
5085
5086
  for (uint32_t index = 0; index < mChildWorkers.Length(); index++) {
5087
    mChildWorkers[index]->UpdateGCZeal(aGCZeal, aFrequency);
5088
  }
5089
}
5090
#endif
5091
5092
void
5093
WorkerPrivate::GarbageCollectInternal(JSContext* aCx, bool aShrinking,
5094
                                      bool aCollectChildren)
5095
0
{
5096
0
  AssertIsOnWorkerThread();
5097
0
5098
0
  if (!GlobalScope()) {
5099
0
    // We haven't compiled anything yet. Just bail out.
5100
0
    return;
5101
0
  }
5102
0
5103
0
  if (aShrinking || aCollectChildren) {
5104
0
    JS::PrepareForFullGC(aCx);
5105
0
5106
0
    if (aShrinking) {
5107
0
      JS::NonIncrementalGC(aCx, GC_SHRINK, JS::gcreason::DOM_WORKER);
5108
0
5109
0
      if (!aCollectChildren) {
5110
0
        LOG(WorkerLog(), ("Worker %p collected idle garbage\n", this));
5111
0
      }
5112
0
    }
5113
0
    else {
5114
0
      JS::NonIncrementalGC(aCx, GC_NORMAL, JS::gcreason::DOM_WORKER);
5115
0
      LOG(WorkerLog(), ("Worker %p collected garbage\n", this));
5116
0
    }
5117
0
  }
5118
0
  else {
5119
0
    JS_MaybeGC(aCx);
5120
0
    LOG(WorkerLog(), ("Worker %p collected periodic garbage\n", this));
5121
0
  }
5122
0
5123
0
  if (aCollectChildren) {
5124
0
    for (uint32_t index = 0; index < mChildWorkers.Length(); index++) {
5125
0
      mChildWorkers[index]->GarbageCollect(aShrinking);
5126
0
    }
5127
0
  }
5128
0
}
5129
5130
void
5131
WorkerPrivate::CycleCollectInternal(bool aCollectChildren)
5132
0
{
5133
0
  AssertIsOnWorkerThread();
5134
0
5135
0
  nsCycleCollector_collect(nullptr);
5136
0
5137
0
  if (aCollectChildren) {
5138
0
    for (uint32_t index = 0; index < mChildWorkers.Length(); index++) {
5139
0
      mChildWorkers[index]->CycleCollect(/* dummy = */ false);
5140
0
    }
5141
0
  }
5142
0
}
5143
5144
void
5145
WorkerPrivate::MemoryPressureInternal()
5146
0
{
5147
0
  AssertIsOnWorkerThread();
5148
0
5149
0
  if (mScope) {
5150
0
    RefPtr<Console> console = mScope->GetConsoleIfExists();
5151
0
    if (console) {
5152
0
      console->ClearStorage();
5153
0
    }
5154
0
5155
0
    RefPtr<Performance> performance = mScope->GetPerformanceIfExists();
5156
0
    if (performance) {
5157
0
      performance->MemoryPressure();
5158
0
    }
5159
0
  }
5160
0
5161
0
  if (mDebuggerScope) {
5162
0
    RefPtr<Console> console = mDebuggerScope->GetConsoleIfExists();
5163
0
    if (console) {
5164
0
      console->ClearStorage();
5165
0
    }
5166
0
  }
5167
0
5168
0
  for (uint32_t index = 0; index < mChildWorkers.Length(); index++) {
5169
0
    mChildWorkers[index]->MemoryPressure(false);
5170
0
  }
5171
0
}
5172
5173
void
5174
WorkerPrivate::SetThread(WorkerThread* aThread)
5175
0
{
5176
0
  if (aThread) {
5177
#ifdef DEBUG
5178
    {
5179
      bool isOnCurrentThread;
5180
      MOZ_ASSERT(NS_SUCCEEDED(aThread->IsOnCurrentThread(&isOnCurrentThread)));
5181
      MOZ_ASSERT(isOnCurrentThread);
5182
    }
5183
#endif
5184
5185
0
    MOZ_ASSERT(!mPRThread);
5186
0
    mPRThread = PRThreadFromThread(aThread);
5187
0
    MOZ_ASSERT(mPRThread);
5188
0
  }
5189
0
  else {
5190
0
    MOZ_ASSERT(mPRThread);
5191
0
  }
5192
0
5193
0
  const WorkerThreadFriendKey friendKey;
5194
0
5195
0
  RefPtr<WorkerThread> doomedThread;
5196
0
5197
0
  { // Scope so that |doomedThread| is released without holding the lock.
5198
0
    MutexAutoLock lock(mMutex);
5199
0
5200
0
    if (aThread) {
5201
0
      MOZ_ASSERT(!mThread);
5202
0
      MOZ_ASSERT(mStatus == Pending);
5203
0
5204
0
      mThread = aThread;
5205
0
      mThread->SetWorker(friendKey, this);
5206
0
5207
0
      if (!mPreStartRunnables.IsEmpty()) {
5208
0
        for (uint32_t index = 0; index < mPreStartRunnables.Length(); index++) {
5209
0
          MOZ_ALWAYS_SUCCEEDS(
5210
0
            mThread->DispatchAnyThread(friendKey, mPreStartRunnables[index].forget()));
5211
0
        }
5212
0
        mPreStartRunnables.Clear();
5213
0
      }
5214
0
    }
5215
0
    else {
5216
0
      MOZ_ASSERT(mThread);
5217
0
5218
0
      mThread->SetWorker(friendKey, nullptr);
5219
0
5220
0
      mThread.swap(doomedThread);
5221
0
    }
5222
0
  }
5223
0
}
5224
5225
void
5226
WorkerPrivate::BeginCTypesCall()
5227
0
{
5228
0
  AssertIsOnWorkerThread();
5229
0
5230
0
  // Don't try to GC while we're blocked in a ctypes call.
5231
0
  SetGCTimerMode(NoTimer);
5232
0
}
5233
5234
void
5235
WorkerPrivate::EndCTypesCall()
5236
0
{
5237
0
  AssertIsOnWorkerThread();
5238
0
5239
0
  // Make sure the periodic timer is running before we start running JS again.
5240
0
  SetGCTimerMode(PeriodicTimer);
5241
0
}
5242
5243
bool
5244
WorkerPrivate::ConnectMessagePort(JSContext* aCx,
5245
                                  MessagePortIdentifier& aIdentifier)
5246
0
{
5247
0
  AssertIsOnWorkerThread();
5248
0
5249
0
  WorkerGlobalScope* globalScope = GlobalScope();
5250
0
5251
0
  JS::Rooted<JSObject*> jsGlobal(aCx, globalScope->GetWrapper());
5252
0
  MOZ_ASSERT(jsGlobal);
5253
0
5254
0
  // This MessagePortIdentifier is used to create a new port, still connected
5255
0
  // with the other one, but in the worker thread.
5256
0
  ErrorResult rv;
5257
0
  RefPtr<MessagePort> port = MessagePort::Create(globalScope, aIdentifier, rv);
5258
0
  if (NS_WARN_IF(rv.Failed())) {
5259
0
    rv.SuppressException();
5260
0
    return false;
5261
0
  }
5262
0
5263
0
  GlobalObject globalObject(aCx, jsGlobal);
5264
0
  if (globalObject.Failed()) {
5265
0
    return false;
5266
0
  }
5267
0
5268
0
  RootedDictionary<MessageEventInit> init(aCx);
5269
0
  init.mBubbles = false;
5270
0
  init.mCancelable = false;
5271
0
  init.mSource.SetValue().SetAsMessagePort() = port;
5272
0
  if (!init.mPorts.AppendElement(port.forget(), fallible)) {
5273
0
    return false;
5274
0
  }
5275
0
5276
0
  RefPtr<MessageEvent> event =
5277
0
    MessageEvent::Constructor(globalObject,
5278
0
                              NS_LITERAL_STRING("connect"), init, rv);
5279
0
5280
0
  event->SetTrusted(true);
5281
0
5282
0
  globalScope->DispatchEvent(*event);
5283
0
5284
0
  return true;
5285
0
}
5286
5287
WorkerGlobalScope*
5288
WorkerPrivate::GetOrCreateGlobalScope(JSContext* aCx)
5289
0
{
5290
0
  AssertIsOnWorkerThread();
5291
0
5292
0
  if (!mScope) {
5293
0
    RefPtr<WorkerGlobalScope> globalScope;
5294
0
    if (IsSharedWorker()) {
5295
0
      globalScope = new SharedWorkerGlobalScope(this, WorkerName());
5296
0
    } else if (IsServiceWorker()) {
5297
0
      globalScope =
5298
0
        new ServiceWorkerGlobalScope(this,
5299
0
                                     GetServiceWorkerRegistrationDescriptor());
5300
0
    } else {
5301
0
      globalScope = new DedicatedWorkerGlobalScope(this, WorkerName());
5302
0
    }
5303
0
5304
0
    JS::Rooted<JSObject*> global(aCx);
5305
0
    NS_ENSURE_TRUE(globalScope->WrapGlobalObject(aCx, &global), nullptr);
5306
0
5307
0
    JSAutoRealm ar(aCx, global);
5308
0
5309
0
    // RegisterBindings() can spin a nested event loop so we have to set mScope
5310
0
    // before calling it, and we have to make sure to unset mScope if it fails.
5311
0
    mScope = std::move(globalScope);
5312
0
5313
0
    if (!RegisterBindings(aCx, global)) {
5314
0
      mScope = nullptr;
5315
0
      return nullptr;
5316
0
    }
5317
0
5318
0
    JS_FireOnNewGlobalObject(aCx, global);
5319
0
  }
5320
0
5321
0
  return mScope;
5322
0
}
5323
5324
WorkerDebuggerGlobalScope*
5325
WorkerPrivate::CreateDebuggerGlobalScope(JSContext* aCx)
5326
0
{
5327
0
  AssertIsOnWorkerThread();
5328
0
5329
0
  MOZ_ASSERT(!mDebuggerScope);
5330
0
5331
0
  RefPtr<WorkerDebuggerGlobalScope> globalScope =
5332
0
    new WorkerDebuggerGlobalScope(this);
5333
0
5334
0
  JS::Rooted<JSObject*> global(aCx);
5335
0
  NS_ENSURE_TRUE(globalScope->WrapGlobalObject(aCx, &global), nullptr);
5336
0
5337
0
  JSAutoRealm ar(aCx, global);
5338
0
5339
0
  // RegisterDebuggerBindings() can spin a nested event loop so we have to set
5340
0
  // mDebuggerScope before calling it, and we have to make sure to unset
5341
0
  // mDebuggerScope if it fails.
5342
0
  mDebuggerScope = std::move(globalScope);
5343
0
5344
0
  if (!RegisterDebuggerBindings(aCx, global)) {
5345
0
    mDebuggerScope = nullptr;
5346
0
    return nullptr;
5347
0
  }
5348
0
5349
0
  JS_FireOnNewGlobalObject(aCx, global);
5350
0
5351
0
  return mDebuggerScope;
5352
0
}
5353
5354
bool
5355
WorkerPrivate::IsOnWorkerThread() const
5356
0
{
5357
0
  // This is much more complicated than it needs to be but we can't use mThread
5358
0
  // because it must be protected by mMutex and sometimes this method is called
5359
0
  // when mMutex is already locked. This method should always work.
5360
0
  MOZ_ASSERT(mPRThread,
5361
0
             "AssertIsOnWorkerThread() called before a thread was assigned!");
5362
0
5363
0
  nsCOMPtr<nsIThread> thread;
5364
0
  nsresult rv =
5365
0
    nsThreadManager::get().GetThreadFromPRThread(mPRThread,
5366
0
                                                 getter_AddRefs(thread));
5367
0
  MOZ_ASSERT(NS_SUCCEEDED(rv));
5368
0
  MOZ_ASSERT(thread);
5369
0
5370
0
  bool current;
5371
0
  rv = thread->IsOnCurrentThread(&current);
5372
0
  return NS_SUCCEEDED(rv) && current;
5373
0
}
5374
5375
#ifdef DEBUG
5376
void
5377
WorkerPrivate::AssertIsOnWorkerThread() const
5378
{
5379
  MOZ_ASSERT(IsOnWorkerThread());
5380
}
5381
#endif // DEBUG
5382
5383
void
5384
WorkerPrivate::DumpCrashInformation(nsACString& aString)
5385
0
{
5386
0
  AssertIsOnWorkerThread();
5387
0
5388
0
  nsTObserverArray<WorkerHolder*>::ForwardIterator iter(mHolders);
5389
0
  while (iter.HasMore()) {
5390
0
    WorkerHolder* holder = iter.GetNext();
5391
0
    aString.Append("|");
5392
0
    aString.Append(holder->Name());
5393
0
  }
5394
0
}
5395
5396
void
5397
WorkerPrivate::EnsurePerformanceCounter()
5398
0
{
5399
0
  AssertIsOnWorkerThread();
5400
0
  MOZ_ASSERT(mozilla::StaticPrefs::dom_performance_enable_scheduler_timing());
5401
0
  if (!mPerformanceCounter) {
5402
0
    nsPrintfCString workerName("Worker:%s", NS_ConvertUTF16toUTF8(mWorkerName).get());
5403
0
    mPerformanceCounter = new PerformanceCounter(workerName);
5404
0
  }
5405
0
}
5406
5407
PerformanceCounter*
5408
WorkerPrivate::GetPerformanceCounter()
5409
0
{
5410
0
  return mPerformanceCounter;
5411
0
}
5412
5413
PerformanceStorage*
5414
WorkerPrivate::GetPerformanceStorage()
5415
0
{
5416
0
  AssertIsOnMainThread();
5417
0
  MOZ_ASSERT(mPerformanceStorage);
5418
0
  return mPerformanceStorage;
5419
0
}
5420
5421
NS_IMPL_ADDREF(WorkerPrivate::EventTarget)
5422
NS_IMPL_RELEASE(WorkerPrivate::EventTarget)
5423
5424
0
NS_INTERFACE_MAP_BEGIN(WorkerPrivate::EventTarget)
5425
0
  NS_INTERFACE_MAP_ENTRY(nsISerialEventTarget)
5426
0
  NS_INTERFACE_MAP_ENTRY(nsIEventTarget)
5427
0
  NS_INTERFACE_MAP_ENTRY(nsISupports)
5428
#ifdef DEBUG
5429
  // kDEBUGWorkerEventTargetIID is special in that it does not AddRef its
5430
  // result.
5431
  if (aIID.Equals(kDEBUGWorkerEventTargetIID)) {
5432
    *aInstancePtr = this;
5433
    return NS_OK;
5434
  }
5435
  else
5436
#endif
5437
0
NS_INTERFACE_MAP_END
5438
5439
NS_IMETHODIMP
5440
WorkerPrivate::EventTarget::DispatchFromScript(nsIRunnable* aRunnable,
5441
                                               uint32_t aFlags)
5442
0
{
5443
0
  nsCOMPtr<nsIRunnable> event(aRunnable);
5444
0
  return Dispatch(event.forget(), aFlags);
5445
0
}
5446
5447
NS_IMETHODIMP
5448
WorkerPrivate::EventTarget::Dispatch(already_AddRefed<nsIRunnable> aRunnable,
5449
                                     uint32_t aFlags)
5450
0
{
5451
0
  // May be called on any thread!
5452
0
  nsCOMPtr<nsIRunnable> event(aRunnable);
5453
0
5454
0
  // Workers only support asynchronous dispatch for now.
5455
0
  if (NS_WARN_IF(aFlags != NS_DISPATCH_NORMAL)) {
5456
0
    return NS_ERROR_UNEXPECTED;
5457
0
  }
5458
0
5459
0
  RefPtr<WorkerRunnable> workerRunnable;
5460
0
5461
0
  MutexAutoLock lock(mMutex);
5462
0
5463
0
  if (!mWorkerPrivate) {
5464
0
    NS_WARNING("A runnable was posted to a worker that is already shutting "
5465
0
               "down!");
5466
0
    return NS_ERROR_UNEXPECTED;
5467
0
  }
5468
0
5469
0
  if (event) {
5470
0
    workerRunnable = mWorkerPrivate->MaybeWrapAsWorkerRunnable(event.forget());
5471
0
  }
5472
0
5473
0
  nsresult rv =
5474
0
    mWorkerPrivate->Dispatch(workerRunnable.forget(), mNestedEventTarget);
5475
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
5476
0
    return rv;
5477
0
  }
5478
0
5479
0
  return NS_OK;
5480
0
}
5481
5482
NS_IMETHODIMP
5483
WorkerPrivate::EventTarget::DelayedDispatch(already_AddRefed<nsIRunnable>,
5484
                                            uint32_t)
5485
5486
0
{
5487
0
  return NS_ERROR_NOT_IMPLEMENTED;
5488
0
}
5489
5490
NS_IMETHODIMP
5491
WorkerPrivate::EventTarget::IsOnCurrentThread(bool* aIsOnCurrentThread)
5492
0
{
5493
0
  // May be called on any thread!
5494
0
5495
0
  MOZ_ASSERT(aIsOnCurrentThread);
5496
0
5497
0
  MutexAutoLock lock(mMutex);
5498
0
5499
0
  if (!mWorkerPrivate) {
5500
0
    NS_WARNING("A worker's event target was used after the worker has !");
5501
0
    return NS_ERROR_UNEXPECTED;
5502
0
  }
5503
0
5504
0
  *aIsOnCurrentThread = mWorkerPrivate->IsOnCurrentThread();
5505
0
  return NS_OK;
5506
0
}
5507
5508
NS_IMETHODIMP_(bool)
5509
WorkerPrivate::EventTarget::IsOnCurrentThreadInfallible()
5510
0
{
5511
0
  // May be called on any thread!
5512
0
5513
0
  MutexAutoLock lock(mMutex);
5514
0
5515
0
  if (!mWorkerPrivate) {
5516
0
    NS_WARNING("A worker's event target was used after the worker has !");
5517
0
    return false;
5518
0
  }
5519
0
5520
0
  return mWorkerPrivate->IsOnCurrentThread();
5521
0
}
5522
5523
} // dom namespace
5524
} // mozilla namespace