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 "RuntimeService.h"
8 
9 #include "nsContentSecurityUtils.h"
10 #include "nsIContentSecurityPolicy.h"
11 #include "mozilla/dom/Document.h"
12 #include "nsIObserverService.h"
13 #include "nsIScriptContext.h"
14 #include "nsIStreamTransportService.h"
15 #include "nsISupportsPriority.h"
16 #include "nsITimer.h"
17 #include "nsIURI.h"
18 #include "nsIXULRuntime.h"
19 #include "nsPIDOMWindow.h"
20 
21 #include <algorithm>
22 #include "mozilla/ipc/BackgroundChild.h"
23 #include "GeckoProfiler.h"
24 #include "js/experimental/CTypes.h"  // JS::CTypesActivityType, JS::SetCTypesActivityCallback
25 #include "jsfriendapi.h"
26 #include "js/friend/ErrorMessages.h"  // js::GetErrorMessage, JSMSG_*
27 #include "js/ContextOptions.h"
28 #include "js/Initialization.h"
29 #include "js/LocaleSensitive.h"
30 #include "js/WasmFeatures.h"
31 #include "mozilla/ArrayUtils.h"
32 #include "mozilla/Atomics.h"
33 #include "mozilla/Attributes.h"
34 #include "mozilla/CycleCollectedJSContext.h"
35 #include "mozilla/CycleCollectedJSRuntime.h"
36 #include "mozilla/Telemetry.h"
37 #include "mozilla/TimeStamp.h"
38 #include "mozilla/dom/AtomList.h"
39 #include "mozilla/dom/BindingUtils.h"
40 #include "mozilla/dom/ErrorEventBinding.h"
41 #include "mozilla/dom/EventTargetBinding.h"
42 #include "mozilla/dom/FetchUtil.h"
43 #include "mozilla/dom/MessageChannel.h"
44 #include "mozilla/dom/MessageEventBinding.h"
45 #include "mozilla/dom/PerformanceService.h"
46 #include "mozilla/dom/RemoteWorkerChild.h"
47 #include "mozilla/dom/WorkerBinding.h"
48 #include "mozilla/dom/ScriptSettings.h"
49 #include "mozilla/dom/IndexedDatabaseManager.h"
50 #include "mozilla/DebugOnly.h"
51 #include "mozilla/Preferences.h"
52 #include "mozilla/ScopeExit.h"
53 #include "mozilla/dom/Navigator.h"
54 #include "mozilla/Monitor.h"
55 #include "nsContentUtils.h"
56 #include "nsCycleCollector.h"
57 #include "nsDOMJSUtils.h"
58 #include "nsISupportsImpl.h"
59 #include "nsLayoutStatics.h"
60 #include "nsNetUtil.h"
61 #include "nsServiceManagerUtils.h"
62 #include "nsThreadUtils.h"
63 #include "nsXPCOM.h"
64 #include "nsXPCOMPrivate.h"
65 #include "OSFileConstants.h"
66 #include "xpcpublic.h"
67 #include "XPCSelfHostedShmem.h"
68 
69 #if defined(XP_MACOSX)
70 #  include "nsMacUtilsImpl.h"
71 #endif
72 
73 #include "Principal.h"
74 #include "WorkerDebuggerManager.h"
75 #include "WorkerError.h"
76 #include "WorkerLoadInfo.h"
77 #include "WorkerRunnable.h"
78 #include "WorkerScope.h"
79 #include "WorkerThread.h"
80 #include "prsystem.h"
81 
82 #ifdef DEBUG
83 #  include "nsICookieJarSettings.h"
84 #endif
85 
86 #define WORKERS_SHUTDOWN_TOPIC "web-workers-shutdown"
87 
88 namespace mozilla {
89 
90 using namespace ipc;
91 
92 namespace dom {
93 
94 using namespace workerinternals;
95 
96 namespace workerinternals {
97 
98 // The size of the worker runtime heaps in bytes. May be changed via pref.
99 #define WORKER_DEFAULT_RUNTIME_HEAPSIZE 32 * 1024 * 1024
100 
101 // The size of the worker JS allocation threshold in MB. May be changed via
102 // pref.
103 #define WORKER_DEFAULT_ALLOCATION_THRESHOLD 30
104 
105 // Half the size of the actual C stack, to be safe.
106 #define WORKER_CONTEXT_NATIVE_STACK_LIMIT 128 * sizeof(size_t) * 1024
107 
108 // The maximum number of threads to use for workers, overridable via pref.
109 #define MAX_WORKERS_PER_DOMAIN 512
110 
111 static_assert(MAX_WORKERS_PER_DOMAIN >= 1,
112               "We should allow at least one worker per domain.");
113 
114 // The number of seconds that idle threads can hang around before being killed.
115 #define IDLE_THREAD_TIMEOUT_SEC 30
116 
117 // The maximum number of threads that can be idle at one time.
118 #define MAX_IDLE_THREADS 20
119 
120 #define PREF_WORKERS_PREFIX "dom.workers."
121 #define PREF_WORKERS_MAX_PER_DOMAIN PREF_WORKERS_PREFIX "maxPerDomain"
122 
123 #define GC_REQUEST_OBSERVER_TOPIC "child-gc-request"
124 #define CC_REQUEST_OBSERVER_TOPIC "child-cc-request"
125 #define MEMORY_PRESSURE_OBSERVER_TOPIC "memory-pressure"
126 #define LOW_MEMORY_DATA "low-memory"
127 #define LOW_MEMORY_ONGOING_DATA "low-memory-ongoing"
128 #define MEMORY_PRESSURE_STOP_OBSERVER_TOPIC "memory-pressure-stop"
129 
130 // Prefixes for observing preference changes.
131 #define PREF_JS_OPTIONS_PREFIX "javascript.options."
132 #define PREF_MEM_OPTIONS_PREFIX "mem."
133 #define PREF_GCZEAL "gczeal"
134 
135 static NS_DEFINE_CID(kStreamTransportServiceCID, NS_STREAMTRANSPORTSERVICE_CID);
136 
137 namespace {
138 
139 const uint32_t kNoIndex = uint32_t(-1);
140 
141 uint32_t gMaxWorkersPerDomain = MAX_WORKERS_PER_DOMAIN;
142 
143 // Does not hold an owning reference.
144 Atomic<RuntimeService*> gRuntimeService(nullptr);
145 
146 // Only true during the call to Init.
147 bool gRuntimeServiceDuringInit = false;
148 
149 class LiteralRebindingCString : public nsDependentCString {
150  public:
151   template <int N>
RebindLiteral(const char (& aStr)[N])152   void RebindLiteral(const char (&aStr)[N]) {
153     Rebind(aStr, N - 1);
154   }
155 };
156 
157 template <typename T>
158 struct PrefTraits;
159 
160 template <>
161 struct PrefTraits<bool> {
162   using PrefValueType = bool;
163 
Getmozilla::dom::workerinternals::__anon3fcc0c900111::PrefTraits164   static inline PrefValueType Get(const char* aPref) {
165     AssertIsOnMainThread();
166     return Preferences::GetBool(aPref);
167   }
168 
Existsmozilla::dom::workerinternals::__anon3fcc0c900111::PrefTraits169   static inline bool Exists(const char* aPref) {
170     AssertIsOnMainThread();
171     return Preferences::GetType(aPref) == nsIPrefBranch::PREF_BOOL;
172   }
173 };
174 
175 template <>
176 struct PrefTraits<int32_t> {
177   using PrefValueType = int32_t;
178 
Getmozilla::dom::workerinternals::__anon3fcc0c900111::PrefTraits179   static inline PrefValueType Get(const char* aPref) {
180     AssertIsOnMainThread();
181     return Preferences::GetInt(aPref);
182   }
183 
Existsmozilla::dom::workerinternals::__anon3fcc0c900111::PrefTraits184   static inline bool Exists(const char* aPref) {
185     AssertIsOnMainThread();
186     return Preferences::GetType(aPref) == nsIPrefBranch::PREF_INT;
187   }
188 };
189 
190 template <typename T>
GetPref(const char * aFullPref,const T aDefault,bool * aPresent=nullptr)191 T GetPref(const char* aFullPref, const T aDefault, bool* aPresent = nullptr) {
192   AssertIsOnMainThread();
193 
194   using PrefHelper = PrefTraits<T>;
195 
196   T result;
197   bool present = true;
198 
199   if (PrefHelper::Exists(aFullPref)) {
200     result = PrefHelper::Get(aFullPref);
201   } else {
202     result = aDefault;
203     present = false;
204   }
205 
206   if (aPresent) {
207     *aPresent = present;
208   }
209   return result;
210 }
211 
LoadContextOptions(const char * aPrefName,void *)212 void LoadContextOptions(const char* aPrefName, void* /* aClosure */) {
213   AssertIsOnMainThread();
214 
215   RuntimeService* rts = RuntimeService::GetService();
216   if (!rts) {
217     // May be shutting down, just bail.
218     return;
219   }
220 
221   const nsDependentCString prefName(aPrefName);
222 
223   // Several other pref branches will get included here so bail out if there is
224   // another callback that will handle this change.
225   if (StringBeginsWith(
226           prefName,
227           nsLiteralCString(PREF_JS_OPTIONS_PREFIX PREF_MEM_OPTIONS_PREFIX))) {
228     return;
229   }
230 
231 #ifdef JS_GC_ZEAL
232   if (prefName.EqualsLiteral(PREF_JS_OPTIONS_PREFIX PREF_GCZEAL)) {
233     return;
234   }
235 #endif
236 
237   JS::ContextOptions contextOptions;
238   xpc::SetPrefableContextOptions(contextOptions);
239 
240   nsCOMPtr<nsIXULRuntime> xr = do_GetService("@mozilla.org/xre/runtime;1");
241   if (xr) {
242     bool safeMode = false;
243     xr->GetInSafeMode(&safeMode);
244     if (safeMode) {
245       contextOptions.disableOptionsForSafeMode();
246     }
247   }
248 
249   RuntimeService::SetDefaultContextOptions(contextOptions);
250 
251   if (rts) {
252     rts->UpdateAllWorkerContextOptions();
253   }
254 }
255 
256 #ifdef JS_GC_ZEAL
LoadGCZealOptions(const char *,void *)257 void LoadGCZealOptions(const char* /* aPrefName */, void* /* aClosure */) {
258   AssertIsOnMainThread();
259 
260   RuntimeService* rts = RuntimeService::GetService();
261   if (!rts) {
262     // May be shutting down, just bail.
263     return;
264   }
265 
266   int32_t gczeal = GetPref<int32_t>(PREF_JS_OPTIONS_PREFIX PREF_GCZEAL, -1);
267   if (gczeal < 0) {
268     gczeal = 0;
269   }
270 
271   int32_t frequency =
272       GetPref<int32_t>(PREF_JS_OPTIONS_PREFIX PREF_GCZEAL ".frequency", -1);
273   if (frequency < 0) {
274     frequency = JS_DEFAULT_ZEAL_FREQ;
275   }
276 
277   RuntimeService::SetDefaultGCZeal(uint8_t(gczeal), uint32_t(frequency));
278 
279   if (rts) {
280     rts->UpdateAllWorkerGCZeal();
281   }
282 }
283 #endif
284 
UpdateCommonJSGCMemoryOption(RuntimeService * aRuntimeService,const char * aPrefName,JSGCParamKey aKey)285 void UpdateCommonJSGCMemoryOption(RuntimeService* aRuntimeService,
286                                   const char* aPrefName, JSGCParamKey aKey) {
287   AssertIsOnMainThread();
288   NS_ASSERTION(aPrefName, "Null pref name!");
289 
290   int32_t prefValue = GetPref(aPrefName, -1);
291   Maybe<uint32_t> value = (prefValue < 0 || prefValue >= 10000)
292                               ? Nothing()
293                               : Some(uint32_t(prefValue));
294 
295   RuntimeService::SetDefaultJSGCSettings(aKey, value);
296 
297   if (aRuntimeService) {
298     aRuntimeService->UpdateAllWorkerMemoryParameter(aKey, value);
299   }
300 }
301 
UpdateOtherJSGCMemoryOption(RuntimeService * aRuntimeService,JSGCParamKey aKey,Maybe<uint32_t> aValue)302 void UpdateOtherJSGCMemoryOption(RuntimeService* aRuntimeService,
303                                  JSGCParamKey aKey, Maybe<uint32_t> aValue) {
304   AssertIsOnMainThread();
305 
306   RuntimeService::SetDefaultJSGCSettings(aKey, aValue);
307 
308   if (aRuntimeService) {
309     aRuntimeService->UpdateAllWorkerMemoryParameter(aKey, aValue);
310   }
311 }
312 
LoadJSGCMemoryOptions(const char * aPrefName,void *)313 void LoadJSGCMemoryOptions(const char* aPrefName, void* /* aClosure */) {
314   AssertIsOnMainThread();
315 
316   RuntimeService* rts = RuntimeService::GetService();
317 
318   if (!rts) {
319     // May be shutting down, just bail.
320     return;
321   }
322 
323   constexpr auto memPrefix =
324       nsLiteralCString{PREF_JS_OPTIONS_PREFIX PREF_MEM_OPTIONS_PREFIX};
325   const nsDependentCString fullPrefName(aPrefName);
326 
327   // Pull out the string that actually distinguishes the parameter we need to
328   // change.
329   nsDependentCSubstring memPrefName;
330   if (StringBeginsWith(fullPrefName, memPrefix)) {
331     memPrefName.Rebind(fullPrefName, memPrefix.Length());
332   } else {
333     NS_ERROR("Unknown pref name!");
334     return;
335   }
336 
337   struct WorkerGCPref {
338     nsLiteralCString memName;
339     const char* fullName;
340     JSGCParamKey key;
341   };
342 
343 #define PREF(suffix_, key_)                                          \
344   {                                                                  \
345     nsLiteralCString(PREF_MEM_OPTIONS_PREFIX suffix_),               \
346         PREF_JS_OPTIONS_PREFIX PREF_MEM_OPTIONS_PREFIX suffix_, key_ \
347   }
348   constexpr WorkerGCPref kWorkerPrefs[] = {
349       PREF("max", JSGC_MAX_BYTES),
350       PREF("gc_high_frequency_time_limit_ms", JSGC_HIGH_FREQUENCY_TIME_LIMIT),
351       PREF("gc_low_frequency_heap_growth", JSGC_LOW_FREQUENCY_HEAP_GROWTH),
352       PREF("gc_high_frequency_large_heap_growth",
353            JSGC_HIGH_FREQUENCY_LARGE_HEAP_GROWTH),
354       PREF("gc_high_frequency_small_heap_growth",
355            JSGC_HIGH_FREQUENCY_SMALL_HEAP_GROWTH),
356       PREF("gc_small_heap_size_max_mb", JSGC_SMALL_HEAP_SIZE_MAX),
357       PREF("gc_large_heap_size_min_mb", JSGC_LARGE_HEAP_SIZE_MIN),
358       PREF("gc_allocation_threshold_mb", JSGC_ALLOCATION_THRESHOLD),
359       PREF("gc_malloc_threshold_base_mb", JSGC_MALLOC_THRESHOLD_BASE),
360       PREF("gc_small_heap_incremental_limit",
361            JSGC_SMALL_HEAP_INCREMENTAL_LIMIT),
362       PREF("gc_large_heap_incremental_limit",
363            JSGC_LARGE_HEAP_INCREMENTAL_LIMIT),
364       PREF("gc_urgent_threshold_mb", JSGC_URGENT_THRESHOLD_MB),
365       PREF("gc_incremental_slice_ms", JSGC_SLICE_TIME_BUDGET_MS),
366       PREF("gc_min_empty_chunk_count", JSGC_MIN_EMPTY_CHUNK_COUNT),
367       PREF("gc_max_empty_chunk_count", JSGC_MAX_EMPTY_CHUNK_COUNT),
368       PREF("gc_compacting", JSGC_COMPACTING_ENABLED),
369   };
370 #undef PREF
371 
372   auto pref = kWorkerPrefs;
373   auto end = kWorkerPrefs + ArrayLength(kWorkerPrefs);
374 
375   if (gRuntimeServiceDuringInit) {
376     // During init, we want to update every pref in kWorkerPrefs.
377     MOZ_ASSERT(memPrefName.IsEmpty(),
378                "Pref branch prefix only expected during init");
379   } else {
380     // Otherwise, find the single pref that changed.
381     while (pref != end) {
382       if (pref->memName == memPrefName) {
383         end = pref + 1;
384         break;
385       }
386       ++pref;
387     }
388 #ifdef DEBUG
389     if (pref == end) {
390       nsAutoCString message("Workers don't support the '");
391       message.Append(memPrefName);
392       message.AppendLiteral("' preference!");
393       NS_WARNING(message.get());
394     }
395 #endif
396   }
397 
398   while (pref != end) {
399     switch (pref->key) {
400       case JSGC_MAX_BYTES: {
401         int32_t prefValue = GetPref(pref->fullName, -1);
402         Maybe<uint32_t> value = (prefValue <= 0 || prefValue >= 0x1000)
403                                     ? Nothing()
404                                     : Some(uint32_t(prefValue) * 1024 * 1024);
405         UpdateOtherJSGCMemoryOption(rts, pref->key, value);
406         break;
407       }
408       case JSGC_SLICE_TIME_BUDGET_MS: {
409         int32_t prefValue = GetPref(pref->fullName, -1);
410         Maybe<uint32_t> value = (prefValue <= 0 || prefValue >= 100000)
411                                     ? Nothing()
412                                     : Some(uint32_t(prefValue));
413         UpdateOtherJSGCMemoryOption(rts, pref->key, value);
414         break;
415       }
416       case JSGC_COMPACTING_ENABLED: {
417         bool present;
418         bool prefValue = GetPref(pref->fullName, false, &present);
419         Maybe<uint32_t> value = present ? Some(prefValue ? 1 : 0) : Nothing();
420         UpdateOtherJSGCMemoryOption(rts, pref->key, value);
421         break;
422       }
423       case JSGC_HIGH_FREQUENCY_TIME_LIMIT:
424       case JSGC_LOW_FREQUENCY_HEAP_GROWTH:
425       case JSGC_HIGH_FREQUENCY_LARGE_HEAP_GROWTH:
426       case JSGC_HIGH_FREQUENCY_SMALL_HEAP_GROWTH:
427       case JSGC_SMALL_HEAP_SIZE_MAX:
428       case JSGC_LARGE_HEAP_SIZE_MIN:
429       case JSGC_ALLOCATION_THRESHOLD:
430       case JSGC_MALLOC_THRESHOLD_BASE:
431       case JSGC_SMALL_HEAP_INCREMENTAL_LIMIT:
432       case JSGC_LARGE_HEAP_INCREMENTAL_LIMIT:
433       case JSGC_URGENT_THRESHOLD_MB:
434       case JSGC_MIN_EMPTY_CHUNK_COUNT:
435       case JSGC_MAX_EMPTY_CHUNK_COUNT:
436         UpdateCommonJSGCMemoryOption(rts, pref->fullName, pref->key);
437         break;
438       default:
439         MOZ_ASSERT_UNREACHABLE("Unknown JSGCParamKey value");
440         break;
441     }
442     ++pref;
443   }
444 }
445 
InterruptCallback(JSContext * aCx)446 bool InterruptCallback(JSContext* aCx) {
447   WorkerPrivate* worker = GetWorkerPrivateFromContext(aCx);
448   MOZ_ASSERT(worker);
449 
450   // Now is a good time to turn on profiling if it's pending.
451   PROFILER_JS_INTERRUPT_CALLBACK();
452 
453   return worker->InterruptCallback(aCx);
454 }
455 
456 class LogViolationDetailsRunnable final : public WorkerMainThreadRunnable {
457   nsString mFileName;
458   uint32_t mLineNum;
459   uint32_t mColumnNum;
460   nsString mScriptSample;
461 
462  public:
LogViolationDetailsRunnable(WorkerPrivate * aWorker,const nsString & aFileName,uint32_t aLineNum,uint32_t aColumnNum,const nsAString & aScriptSample)463   LogViolationDetailsRunnable(WorkerPrivate* aWorker, const nsString& aFileName,
464                               uint32_t aLineNum, uint32_t aColumnNum,
465                               const nsAString& aScriptSample)
466       : WorkerMainThreadRunnable(aWorker,
467                                  "RuntimeService :: LogViolationDetails"_ns),
468         mFileName(aFileName),
469         mLineNum(aLineNum),
470         mColumnNum(aColumnNum),
471         mScriptSample(aScriptSample) {
472     MOZ_ASSERT(aWorker);
473   }
474 
475   virtual bool MainThreadRun() override;
476 
477  private:
478   ~LogViolationDetailsRunnable() = default;
479 };
480 
ContentSecurityPolicyAllows(JSContext * aCx,JS::HandleString aCode)481 bool ContentSecurityPolicyAllows(JSContext* aCx, JS::HandleString aCode) {
482   WorkerPrivate* worker = GetWorkerPrivateFromContext(aCx);
483   worker->AssertIsOnWorkerThread();
484 
485   nsAutoJSString scriptSample;
486   if (NS_WARN_IF(!scriptSample.init(aCx, aCode))) {
487     JS_ClearPendingException(aCx);
488     return false;
489   }
490 
491   if (!nsContentSecurityUtils::IsEvalAllowed(aCx, worker->UsesSystemPrincipal(),
492                                              scriptSample)) {
493     return false;
494   }
495 
496   if (worker->GetReportCSPViolations()) {
497     nsString fileName;
498     uint32_t lineNum = 0;
499     uint32_t columnNum = 0;
500 
501     JS::AutoFilename file;
502     if (JS::DescribeScriptedCaller(aCx, &file, &lineNum, &columnNum) &&
503         file.get()) {
504       CopyUTF8toUTF16(MakeStringSpan(file.get()), fileName);
505     } else {
506       MOZ_ASSERT(!JS_IsExceptionPending(aCx));
507     }
508 
509     RefPtr<LogViolationDetailsRunnable> runnable =
510         new LogViolationDetailsRunnable(worker, fileName, lineNum, columnNum,
511                                         scriptSample);
512 
513     ErrorResult rv;
514     runnable->Dispatch(Killing, rv);
515     if (NS_WARN_IF(rv.Failed())) {
516       rv.SuppressException();
517     }
518   }
519 
520   return worker->IsEvalAllowed();
521 }
522 
CTypesActivityCallback(JSContext * aCx,JS::CTypesActivityType aType)523 void CTypesActivityCallback(JSContext* aCx, JS::CTypesActivityType aType) {
524   WorkerPrivate* worker = GetWorkerPrivateFromContext(aCx);
525   worker->AssertIsOnWorkerThread();
526 
527   switch (aType) {
528     case JS::CTypesActivityType::BeginCall:
529       worker->BeginCTypesCall();
530       break;
531 
532     case JS::CTypesActivityType::EndCall:
533       worker->EndCTypesCall();
534       break;
535 
536     case JS::CTypesActivityType::BeginCallback:
537       worker->BeginCTypesCallback();
538       break;
539 
540     case JS::CTypesActivityType::EndCallback:
541       worker->EndCTypesCallback();
542       break;
543 
544     default:
545       MOZ_CRASH("Unknown type flag!");
546   }
547 }
548 
549 // JSDispatchableRunnables are WorkerRunnables used to dispatch JS::Dispatchable
550 // back to their worker thread. A WorkerRunnable is used for two reasons:
551 //
552 // 1. The JS::Dispatchable::run() callback may run JS so we cannot use a control
553 // runnable since they use async interrupts and break JS run-to-completion.
554 //
555 // 2. The DispatchToEventLoopCallback interface is *required* to fail during
556 // shutdown (see jsapi.h) which is exactly what WorkerRunnable::Dispatch() will
557 // do. Moreover, JS_DestroyContext() does *not* block on JS::Dispatchable::run
558 // being called, DispatchToEventLoopCallback failure is expected to happen
559 // during shutdown.
560 class JSDispatchableRunnable final : public WorkerRunnable {
561   JS::Dispatchable* mDispatchable;
562 
~JSDispatchableRunnable()563   ~JSDispatchableRunnable() { MOZ_ASSERT(!mDispatchable); }
564 
565   // Disable the usual pre/post-dispatch thread assertions since we are
566   // dispatching from some random JS engine internal thread:
567 
PreDispatch(WorkerPrivate * aWorkerPrivate)568   bool PreDispatch(WorkerPrivate* aWorkerPrivate) override { return true; }
569 
PostDispatch(WorkerPrivate * aWorkerPrivate,bool aDispatchResult)570   void PostDispatch(WorkerPrivate* aWorkerPrivate,
571                     bool aDispatchResult) override {
572     // For the benefit of the destructor assert.
573     if (!aDispatchResult) {
574       mDispatchable = nullptr;
575     }
576   }
577 
578  public:
JSDispatchableRunnable(WorkerPrivate * aWorkerPrivate,JS::Dispatchable * aDispatchable)579   JSDispatchableRunnable(WorkerPrivate* aWorkerPrivate,
580                          JS::Dispatchable* aDispatchable)
581       : WorkerRunnable(aWorkerPrivate,
582                        WorkerRunnable::WorkerThreadUnchangedBusyCount),
583         mDispatchable(aDispatchable) {
584     MOZ_ASSERT(mDispatchable);
585   }
586 
WorkerRun(JSContext * aCx,WorkerPrivate * aWorkerPrivate)587   bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
588     MOZ_ASSERT(aWorkerPrivate == mWorkerPrivate);
589     MOZ_ASSERT(aCx == mWorkerPrivate->GetJSContext());
590     MOZ_ASSERT(mDispatchable);
591 
592     AutoJSAPI jsapi;
593     jsapi.Init();
594 
595     mDispatchable->run(mWorkerPrivate->GetJSContext(),
596                        JS::Dispatchable::NotShuttingDown);
597     mDispatchable = nullptr;  // mDispatchable may delete itself
598 
599     return true;
600   }
601 
Cancel()602   nsresult Cancel() override {
603     // We need to check first if cancel is called twice
604     nsresult rv = WorkerRunnable::Cancel();
605     NS_ENSURE_SUCCESS(rv, rv);
606 
607     MOZ_ASSERT(mDispatchable);
608 
609     AutoJSAPI jsapi;
610     jsapi.Init();
611 
612     mDispatchable->run(mWorkerPrivate->GetJSContext(),
613                        JS::Dispatchable::ShuttingDown);
614     mDispatchable = nullptr;  // mDispatchable may delete itself
615 
616     return NS_OK;
617   }
618 };
619 
DispatchToEventLoop(void * aClosure,JS::Dispatchable * aDispatchable)620 static bool DispatchToEventLoop(void* aClosure,
621                                 JS::Dispatchable* aDispatchable) {
622   // This callback may execute either on the worker thread or a random
623   // JS-internal helper thread.
624 
625   // See comment at JS::InitDispatchToEventLoop() below for how we know the
626   // WorkerPrivate is alive.
627   WorkerPrivate* workerPrivate = reinterpret_cast<WorkerPrivate*>(aClosure);
628 
629   // Dispatch is expected to fail during shutdown for the reasons outlined in
630   // the JSDispatchableRunnable comment above.
631   RefPtr<JSDispatchableRunnable> r =
632       new JSDispatchableRunnable(workerPrivate, aDispatchable);
633   return r->Dispatch();
634 }
635 
ConsumeStream(JSContext * aCx,JS::HandleObject aObj,JS::MimeType aMimeType,JS::StreamConsumer * aConsumer)636 static bool ConsumeStream(JSContext* aCx, JS::HandleObject aObj,
637                           JS::MimeType aMimeType,
638                           JS::StreamConsumer* aConsumer) {
639   WorkerPrivate* worker = GetWorkerPrivateFromContext(aCx);
640   if (!worker) {
641     JS_ReportErrorNumberASCII(aCx, js::GetErrorMessage, nullptr,
642                               JSMSG_WASM_ERROR_CONSUMING_RESPONSE);
643     return false;
644   }
645 
646   return FetchUtil::StreamResponseToJS(aCx, aObj, aMimeType, aConsumer, worker);
647 }
648 
InitJSContextForWorker(WorkerPrivate * aWorkerPrivate,JSContext * aWorkerCx)649 bool InitJSContextForWorker(WorkerPrivate* aWorkerPrivate,
650                             JSContext* aWorkerCx) {
651   aWorkerPrivate->AssertIsOnWorkerThread();
652   NS_ASSERTION(!aWorkerPrivate->GetJSContext(), "Already has a context!");
653 
654   JSSettings settings;
655   aWorkerPrivate->CopyJSSettings(settings);
656 
657   JS::ContextOptionsRef(aWorkerCx) = settings.contextOptions;
658 
659   // This is the real place where we set the max memory for the runtime.
660   for (const auto& setting : settings.gcSettings) {
661     if (setting.value) {
662       JS_SetGCParameter(aWorkerCx, setting.key, *setting.value);
663     } else {
664       JS_ResetGCParameter(aWorkerCx, setting.key);
665     }
666   }
667 
668   JS_SetNativeStackQuota(aWorkerCx, WORKER_CONTEXT_NATIVE_STACK_LIMIT);
669 
670   // Security policy:
671   static const JSSecurityCallbacks securityCallbacks = {
672       ContentSecurityPolicyAllows};
673   JS_SetSecurityCallbacks(aWorkerCx, &securityCallbacks);
674 
675   // A WorkerPrivate lives strictly longer than its JSRuntime so we can safely
676   // store a raw pointer as the callback's closure argument on the JSRuntime.
677   JS::InitDispatchToEventLoop(aWorkerCx, DispatchToEventLoop,
678                               (void*)aWorkerPrivate);
679 
680   JS::InitConsumeStreamCallback(aWorkerCx, ConsumeStream,
681                                 FetchUtil::ReportJSStreamError);
682 
683   // When available, set the self-hosted shared memory to be read, so that we
684   // can decode the self-hosted content instead of parsing it.
685   auto& shm = xpc::SelfHostedShmem::GetSingleton();
686   JS::SelfHostedCache selfHostedContent = shm.Content();
687 
688   if (!JS::InitSelfHostedCode(aWorkerCx, selfHostedContent)) {
689     NS_WARNING("Could not init self-hosted code!");
690     return false;
691   }
692 
693   JS_AddInterruptCallback(aWorkerCx, InterruptCallback);
694 
695   JS::SetCTypesActivityCallback(aWorkerCx, CTypesActivityCallback);
696 
697 #ifdef JS_GC_ZEAL
698   JS_SetGCZeal(aWorkerCx, settings.gcZeal, settings.gcZealFrequency);
699 #endif
700 
701   return true;
702 }
703 
PreserveWrapper(JSContext * cx,JS::HandleObject obj)704 static bool PreserveWrapper(JSContext* cx, JS::HandleObject obj) {
705   MOZ_ASSERT(cx);
706   MOZ_ASSERT(obj);
707   MOZ_ASSERT(mozilla::dom::IsDOMObject(obj));
708 
709   return mozilla::dom::TryPreserveWrapper(obj);
710 }
711 
IsWorkerDebuggerGlobalOrSandbox(JS::HandleObject aGlobal)712 static bool IsWorkerDebuggerGlobalOrSandbox(JS::HandleObject aGlobal) {
713   return IsWorkerDebuggerGlobal(aGlobal) || IsWorkerDebuggerSandbox(aGlobal);
714 }
715 
Wrap(JSContext * cx,JS::HandleObject existing,JS::HandleObject obj)716 JSObject* Wrap(JSContext* cx, JS::HandleObject existing, JS::HandleObject obj) {
717   JS::RootedObject targetGlobal(cx, JS::CurrentGlobalOrNull(cx));
718 
719   // Note: the JS engine unwraps CCWs before calling this callback.
720   JS::RootedObject originGlobal(cx, JS::GetNonCCWObjectGlobal(obj));
721 
722   const js::Wrapper* wrapper = nullptr;
723   if (IsWorkerDebuggerGlobalOrSandbox(targetGlobal) &&
724       IsWorkerDebuggerGlobalOrSandbox(originGlobal)) {
725     wrapper = &js::CrossCompartmentWrapper::singleton;
726   } else {
727     wrapper = &js::OpaqueCrossCompartmentWrapper::singleton;
728   }
729 
730   if (existing) {
731     js::Wrapper::Renew(existing, obj, wrapper);
732   }
733   return js::Wrapper::New(cx, obj, wrapper);
734 }
735 
736 static const JSWrapObjectCallbacks WrapObjectCallbacks = {
737     Wrap,
738     nullptr,
739 };
740 
741 class WorkerJSRuntime final : public mozilla::CycleCollectedJSRuntime {
742  public:
743   // The heap size passed here doesn't matter, we will change it later in the
744   // call to JS_SetGCParameter inside InitJSContextForWorker.
WorkerJSRuntime(JSContext * aCx,WorkerPrivate * aWorkerPrivate)745   explicit WorkerJSRuntime(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
746       : CycleCollectedJSRuntime(aCx), mWorkerPrivate(aWorkerPrivate) {
747     MOZ_COUNT_CTOR_INHERITED(WorkerJSRuntime, CycleCollectedJSRuntime);
748     MOZ_ASSERT(aWorkerPrivate);
749 
750     {
751       JS::UniqueChars defaultLocale = aWorkerPrivate->AdoptDefaultLocale();
752       MOZ_ASSERT(defaultLocale,
753                  "failure of a WorkerPrivate to have a default locale should "
754                  "have made the worker fail to spawn");
755 
756       if (!JS_SetDefaultLocale(Runtime(), defaultLocale.get())) {
757         NS_WARNING("failed to set workerCx's default locale");
758       }
759     }
760   }
761 
Shutdown(JSContext * cx)762   void Shutdown(JSContext* cx) override {
763     // The CC is shut down, and the superclass destructor will GC, so make sure
764     // we don't try to CC again.
765     mWorkerPrivate = nullptr;
766 
767     CycleCollectedJSRuntime::Shutdown(cx);
768   }
769 
~WorkerJSRuntime()770   ~WorkerJSRuntime() {
771     MOZ_COUNT_DTOR_INHERITED(WorkerJSRuntime, CycleCollectedJSRuntime);
772   }
773 
PrepareForForgetSkippable()774   virtual void PrepareForForgetSkippable() override {}
775 
BeginCycleCollectionCallback(mozilla::CCReason aReason)776   virtual void BeginCycleCollectionCallback(
777       mozilla::CCReason aReason) override {}
778 
EndCycleCollectionCallback(CycleCollectorResults & aResults)779   virtual void EndCycleCollectionCallback(
780       CycleCollectorResults& aResults) override {}
781 
DispatchDeferredDeletion(bool aContinuation,bool aPurge)782   void DispatchDeferredDeletion(bool aContinuation, bool aPurge) override {
783     MOZ_ASSERT(!aContinuation);
784 
785     // Do it immediately, no need for asynchronous behavior here.
786     nsCycleCollector_doDeferredDeletion();
787   }
788 
CustomGCCallback(JSGCStatus aStatus)789   virtual void CustomGCCallback(JSGCStatus aStatus) override {
790     if (!mWorkerPrivate) {
791       // We're shutting down, no need to do anything.
792       return;
793     }
794 
795     mWorkerPrivate->AssertIsOnWorkerThread();
796 
797     if (aStatus == JSGC_END) {
798       bool collectedAnything =
799           nsCycleCollector_collect(CCReason::GC_FINISHED, nullptr);
800       mWorkerPrivate->SetCCCollectedAnything(collectedAnything);
801     }
802   }
803 
804  private:
805   WorkerPrivate* mWorkerPrivate;
806 };
807 
808 }  // anonymous namespace
809 
810 }  // namespace workerinternals
811 
812 class WorkerJSContext final : public mozilla::CycleCollectedJSContext {
813  public:
814   // The heap size passed here doesn't matter, we will change it later in the
815   // call to JS_SetGCParameter inside InitJSContextForWorker.
WorkerJSContext(WorkerPrivate * aWorkerPrivate)816   explicit WorkerJSContext(WorkerPrivate* aWorkerPrivate)
817       : mWorkerPrivate(aWorkerPrivate) {
818     MOZ_COUNT_CTOR_INHERITED(WorkerJSContext, CycleCollectedJSContext);
819     MOZ_ASSERT(aWorkerPrivate);
820     // Magical number 2. Workers have the base recursion depth 1, and normal
821     // runnables run at level 2, and we don't want to process microtasks
822     // at any other level.
823     SetTargetedMicroTaskRecursionDepth(2);
824   }
825 
826   // MOZ_CAN_RUN_SCRIPT_BOUNDARY because otherwise we have to annotate the
827   // SpiderMonkey JS::JobQueue's destructor as MOZ_CAN_RUN_SCRIPT, which is a
828   // bit of a pain.
~WorkerJSContext()829   MOZ_CAN_RUN_SCRIPT_BOUNDARY ~WorkerJSContext() {
830     MOZ_COUNT_DTOR_INHERITED(WorkerJSContext, CycleCollectedJSContext);
831     JSContext* cx = MaybeContext();
832     if (!cx) {
833       return;  // Initialize() must have failed
834     }
835 
836     // The worker global should be unrooted and the shutdown cycle collection
837     // should break all remaining cycles. The superclass destructor will run
838     // the GC one final time and finalize any JSObjects that were participating
839     // in cycles that were broken during CC shutdown.
840     nsCycleCollector_shutdown();
841 
842     // The CC is shut down, and the superclass destructor will GC, so make sure
843     // we don't try to CC again.
844     mWorkerPrivate = nullptr;
845   }
846 
GetAsWorkerJSContext()847   WorkerJSContext* GetAsWorkerJSContext() override { return this; }
848 
CreateRuntime(JSContext * aCx)849   CycleCollectedJSRuntime* CreateRuntime(JSContext* aCx) override {
850     return new WorkerJSRuntime(aCx, mWorkerPrivate);
851   }
852 
Initialize(JSRuntime * aParentRuntime)853   nsresult Initialize(JSRuntime* aParentRuntime) {
854     nsresult rv = CycleCollectedJSContext::Initialize(
855         aParentRuntime, WORKER_DEFAULT_RUNTIME_HEAPSIZE);
856     if (NS_WARN_IF(NS_FAILED(rv))) {
857       return rv;
858     }
859 
860     JSContext* cx = Context();
861 
862     js::SetPreserveWrapperCallbacks(cx, PreserveWrapper, HasReleasedWrapper);
863     JS_InitDestroyPrincipalsCallback(cx, WorkerPrincipal::Destroy);
864     JS_SetWrapObjectCallbacks(cx, &WrapObjectCallbacks);
865     if (mWorkerPrivate->IsDedicatedWorker()) {
866       JS_SetFutexCanWait(cx);
867     }
868 
869     return NS_OK;
870   }
871 
DispatchToMicroTask(already_AddRefed<MicroTaskRunnable> aRunnable)872   virtual void DispatchToMicroTask(
873       already_AddRefed<MicroTaskRunnable> aRunnable) override {
874     RefPtr<MicroTaskRunnable> runnable(aRunnable);
875 
876     MOZ_ASSERT(!NS_IsMainThread());
877     MOZ_ASSERT(runnable);
878 
879     std::deque<RefPtr<MicroTaskRunnable>>* microTaskQueue = nullptr;
880 
881     JSContext* cx = Context();
882     NS_ASSERTION(cx, "This should never be null!");
883 
884     JS::Rooted<JSObject*> global(cx, JS::CurrentGlobalOrNull(cx));
885     NS_ASSERTION(global, "This should never be null!");
886 
887     // On worker threads, if the current global is the worker global, we use the
888     // main micro task queue. Otherwise, the current global must be
889     // either the debugger global or a debugger sandbox, and we use the debugger
890     // micro task queue instead.
891     if (IsWorkerGlobal(global)) {
892       microTaskQueue = &GetMicroTaskQueue();
893     } else {
894       MOZ_ASSERT(IsWorkerDebuggerGlobal(global) ||
895                  IsWorkerDebuggerSandbox(global));
896 
897       microTaskQueue = &GetDebuggerMicroTaskQueue();
898     }
899 
900     JS::JobQueueMayNotBeEmpty(cx);
901     microTaskQueue->push_back(std::move(runnable));
902   }
903 
IsSystemCaller() const904   bool IsSystemCaller() const override {
905     return mWorkerPrivate->UsesSystemPrincipal();
906   }
907 
ReportError(JSErrorReport * aReport,JS::ConstUTF8CharsZ aToStringResult)908   void ReportError(JSErrorReport* aReport,
909                    JS::ConstUTF8CharsZ aToStringResult) override {
910     mWorkerPrivate->ReportError(Context(), aToStringResult, aReport);
911   }
912 
GetWorkerPrivate() const913   WorkerPrivate* GetWorkerPrivate() const { return mWorkerPrivate; }
914 
915  private:
916   WorkerPrivate* mWorkerPrivate;
917 };
918 
919 namespace workerinternals {
920 
921 namespace {
922 
923 class WorkerThreadPrimaryRunnable final : public Runnable {
924   WorkerPrivate* mWorkerPrivate;
925   SafeRefPtr<WorkerThread> mThread;
926   JSRuntime* mParentRuntime;
927 
928   class FinishedRunnable final : public Runnable {
929     SafeRefPtr<WorkerThread> mThread;
930 
931    public:
FinishedRunnable(SafeRefPtr<WorkerThread> aThread)932     explicit FinishedRunnable(SafeRefPtr<WorkerThread> aThread)
933         : Runnable("WorkerThreadPrimaryRunnable::FinishedRunnable"),
934           mThread(std::move(aThread)) {
935       MOZ_ASSERT(mThread);
936     }
937 
938     NS_INLINE_DECL_REFCOUNTING_INHERITED(FinishedRunnable, Runnable)
939 
940    private:
941     ~FinishedRunnable() = default;
942 
943     NS_DECL_NSIRUNNABLE
944   };
945 
946  public:
WorkerThreadPrimaryRunnable(WorkerPrivate * aWorkerPrivate,SafeRefPtr<WorkerThread> aThread,JSRuntime * aParentRuntime)947   WorkerThreadPrimaryRunnable(WorkerPrivate* aWorkerPrivate,
948                               SafeRefPtr<WorkerThread> aThread,
949                               JSRuntime* aParentRuntime)
950       : mozilla::Runnable("WorkerThreadPrimaryRunnable"),
951         mWorkerPrivate(aWorkerPrivate),
952         mThread(std::move(aThread)),
953         mParentRuntime(aParentRuntime) {
954     MOZ_ASSERT(aWorkerPrivate);
955     MOZ_ASSERT(mThread);
956   }
957 
958   NS_INLINE_DECL_REFCOUNTING_INHERITED(WorkerThreadPrimaryRunnable, Runnable)
959 
960  private:
961   ~WorkerThreadPrimaryRunnable() = default;
962 
963   NS_DECL_NSIRUNNABLE
964 };
965 
PrefLanguagesChanged(const char *,void *)966 void PrefLanguagesChanged(const char* /* aPrefName */, void* /* aClosure */) {
967   AssertIsOnMainThread();
968 
969   nsTArray<nsString> languages;
970   Navigator::GetAcceptLanguages(languages);
971 
972   RuntimeService* runtime = RuntimeService::GetService();
973   if (runtime) {
974     runtime->UpdateAllWorkerLanguages(languages);
975   }
976 }
977 
AppNameOverrideChanged(const char *,void *)978 void AppNameOverrideChanged(const char* /* aPrefName */, void* /* aClosure */) {
979   AssertIsOnMainThread();
980 
981   nsAutoString override;
982   Preferences::GetString("general.appname.override", override);
983 
984   RuntimeService* runtime = RuntimeService::GetService();
985   if (runtime) {
986     runtime->UpdateAppNameOverridePreference(override);
987   }
988 }
989 
AppVersionOverrideChanged(const char *,void *)990 void AppVersionOverrideChanged(const char* /* aPrefName */,
991                                void* /* aClosure */) {
992   AssertIsOnMainThread();
993 
994   nsAutoString override;
995   Preferences::GetString("general.appversion.override", override);
996 
997   RuntimeService* runtime = RuntimeService::GetService();
998   if (runtime) {
999     runtime->UpdateAppVersionOverridePreference(override);
1000   }
1001 }
1002 
PlatformOverrideChanged(const char *,void *)1003 void PlatformOverrideChanged(const char* /* aPrefName */,
1004                              void* /* aClosure */) {
1005   AssertIsOnMainThread();
1006 
1007   nsAutoString override;
1008   Preferences::GetString("general.platform.override", override);
1009 
1010   RuntimeService* runtime = RuntimeService::GetService();
1011   if (runtime) {
1012     runtime->UpdatePlatformOverridePreference(override);
1013   }
1014 }
1015 
1016 } /* anonymous namespace */
1017 
1018 // This is only touched on the main thread. Initialized in Init() below.
1019 UniquePtr<JSSettings> RuntimeService::sDefaultJSSettings;
1020 
RuntimeService()1021 RuntimeService::RuntimeService()
1022     : mMutex("RuntimeService::mMutex"),
1023       mObserved(false),
1024       mShuttingDown(false),
1025       mNavigatorPropertiesLoaded(false) {
1026   AssertIsOnMainThread();
1027   MOZ_ASSERT(!GetService(), "More than one service!");
1028 }
1029 
~RuntimeService()1030 RuntimeService::~RuntimeService() {
1031   AssertIsOnMainThread();
1032 
1033   // gRuntimeService can be null if Init() fails.
1034   MOZ_ASSERT(!GetService() || GetService() == this, "More than one service!");
1035 
1036   gRuntimeService = nullptr;
1037 }
1038 
1039 // static
GetOrCreateService()1040 RuntimeService* RuntimeService::GetOrCreateService() {
1041   AssertIsOnMainThread();
1042 
1043   if (!gRuntimeService) {
1044     // The observer service now owns us until shutdown.
1045     gRuntimeService = new RuntimeService();
1046     if (NS_FAILED((*gRuntimeService).Init())) {
1047       NS_WARNING("Failed to initialize!");
1048       (*gRuntimeService).Cleanup();
1049       gRuntimeService = nullptr;
1050       return nullptr;
1051     }
1052   }
1053 
1054   return gRuntimeService;
1055 }
1056 
1057 // static
GetService()1058 RuntimeService* RuntimeService::GetService() { return gRuntimeService; }
1059 
RegisterWorker(WorkerPrivate & aWorkerPrivate)1060 bool RuntimeService::RegisterWorker(WorkerPrivate& aWorkerPrivate) {
1061   aWorkerPrivate.AssertIsOnParentThread();
1062 
1063   WorkerPrivate* parent = aWorkerPrivate.GetParent();
1064   if (!parent) {
1065     AssertIsOnMainThread();
1066 
1067     if (mShuttingDown) {
1068       return false;
1069     }
1070   }
1071 
1072   const bool isServiceWorker = aWorkerPrivate.IsServiceWorker();
1073   const bool isSharedWorker = aWorkerPrivate.IsSharedWorker();
1074   const bool isDedicatedWorker = aWorkerPrivate.IsDedicatedWorker();
1075   if (isServiceWorker) {
1076     AssertIsOnMainThread();
1077     Telemetry::Accumulate(Telemetry::SERVICE_WORKER_SPAWN_ATTEMPTS, 1);
1078   }
1079 
1080   nsCString sharedWorkerScriptSpec;
1081   if (isSharedWorker) {
1082     AssertIsOnMainThread();
1083 
1084     nsCOMPtr<nsIURI> scriptURI = aWorkerPrivate.GetResolvedScriptURI();
1085     NS_ASSERTION(scriptURI, "Null script URI!");
1086 
1087     nsresult rv = scriptURI->GetSpec(sharedWorkerScriptSpec);
1088     if (NS_FAILED(rv)) {
1089       NS_WARNING("GetSpec failed?!");
1090       return false;
1091     }
1092 
1093     NS_ASSERTION(!sharedWorkerScriptSpec.IsEmpty(), "Empty spec!");
1094   }
1095 
1096   bool exemptFromPerDomainMax = false;
1097   if (isServiceWorker) {
1098     AssertIsOnMainThread();
1099     exemptFromPerDomainMax = Preferences::GetBool(
1100         "dom.serviceWorkers.exemptFromPerDomainMax", false);
1101   }
1102 
1103   const nsCString& domain = aWorkerPrivate.Domain();
1104 
1105   bool queued = false;
1106   {
1107     MutexAutoLock lock(mMutex);
1108 
1109     auto* const domainInfo =
1110         mDomainMap
1111             .LookupOrInsertWith(
1112                 domain,
1113                 [&domain, parent] {
1114                   NS_ASSERTION(!parent, "Shouldn't have a parent here!");
1115                   Unused << parent;  // silence clang -Wunused-lambda-capture in
1116                                      // opt builds
1117                   auto wdi = MakeUnique<WorkerDomainInfo>();
1118                   wdi->mDomain = domain;
1119                   return wdi;
1120                 })
1121             .get();
1122 
1123     queued = gMaxWorkersPerDomain &&
1124              domainInfo->ActiveWorkerCount() >= gMaxWorkersPerDomain &&
1125              !domain.IsEmpty() && !exemptFromPerDomainMax;
1126 
1127     if (queued) {
1128       domainInfo->mQueuedWorkers.AppendElement(&aWorkerPrivate);
1129 
1130       // Worker spawn gets queued due to hitting max workers per domain
1131       // limit so let's log a warning.
1132       WorkerPrivate::ReportErrorToConsole("HittingMaxWorkersPerDomain2");
1133 
1134       if (isServiceWorker) {
1135         Telemetry::Accumulate(Telemetry::SERVICE_WORKER_SPAWN_GETS_QUEUED, 1);
1136       } else if (isSharedWorker) {
1137         Telemetry::Accumulate(Telemetry::SHARED_WORKER_SPAWN_GETS_QUEUED, 1);
1138       } else if (isDedicatedWorker) {
1139         Telemetry::Accumulate(Telemetry::DEDICATED_WORKER_SPAWN_GETS_QUEUED, 1);
1140       }
1141     } else if (parent) {
1142       domainInfo->mChildWorkerCount++;
1143     } else if (isServiceWorker) {
1144       domainInfo->mActiveServiceWorkers.AppendElement(&aWorkerPrivate);
1145     } else {
1146       domainInfo->mActiveWorkers.AppendElement(&aWorkerPrivate);
1147     }
1148   }
1149 
1150   // From here on out we must call UnregisterWorker if something fails!
1151   if (parent) {
1152     if (!parent->AddChildWorker(aWorkerPrivate)) {
1153       UnregisterWorker(aWorkerPrivate);
1154       return false;
1155     }
1156   } else {
1157     if (!mNavigatorPropertiesLoaded) {
1158       Navigator::AppName(mNavigatorProperties.mAppName,
1159                          aWorkerPrivate.GetPrincipal(),
1160                          false /* aUsePrefOverriddenValue */);
1161       if (NS_FAILED(Navigator::GetAppVersion(
1162               mNavigatorProperties.mAppVersion, aWorkerPrivate.GetPrincipal(),
1163               false /* aUsePrefOverriddenValue */)) ||
1164           NS_FAILED(Navigator::GetPlatform(
1165               mNavigatorProperties.mPlatform, aWorkerPrivate.GetPrincipal(),
1166               false /* aUsePrefOverriddenValue */))) {
1167         UnregisterWorker(aWorkerPrivate);
1168         return false;
1169       }
1170 
1171       // The navigator overridden properties should have already been read.
1172 
1173       Navigator::GetAcceptLanguages(mNavigatorProperties.mLanguages);
1174       mNavigatorPropertiesLoaded = true;
1175     }
1176 
1177     nsPIDOMWindowInner* window = aWorkerPrivate.GetWindow();
1178 
1179     if (!isServiceWorker) {
1180       // Service workers are excluded since their lifetime is separate from
1181       // that of dom windows.
1182       if (auto* const windowArray = mWindowMap.GetOrInsertNew(window, 1);
1183           !windowArray->Contains(&aWorkerPrivate)) {
1184         windowArray->AppendElement(&aWorkerPrivate);
1185       } else {
1186         MOZ_ASSERT(aWorkerPrivate.IsSharedWorker());
1187       }
1188     }
1189   }
1190 
1191   if (!queued && !ScheduleWorker(aWorkerPrivate)) {
1192     return false;
1193   }
1194 
1195   if (isServiceWorker) {
1196     AssertIsOnMainThread();
1197     Telemetry::Accumulate(Telemetry::SERVICE_WORKER_WAS_SPAWNED, 1);
1198   }
1199   return true;
1200 }
1201 
UnregisterWorker(WorkerPrivate & aWorkerPrivate)1202 void RuntimeService::UnregisterWorker(WorkerPrivate& aWorkerPrivate) {
1203   aWorkerPrivate.AssertIsOnParentThread();
1204 
1205   WorkerPrivate* parent = aWorkerPrivate.GetParent();
1206   if (!parent) {
1207     AssertIsOnMainThread();
1208   }
1209 
1210   const nsCString& domain = aWorkerPrivate.Domain();
1211 
1212   WorkerPrivate* queuedWorker = nullptr;
1213   {
1214     MutexAutoLock lock(mMutex);
1215 
1216     WorkerDomainInfo* domainInfo;
1217     if (!mDomainMap.Get(domain, &domainInfo)) {
1218       NS_ERROR("Don't have an entry for this domain!");
1219     }
1220 
1221     // Remove old worker from everywhere.
1222     uint32_t index = domainInfo->mQueuedWorkers.IndexOf(&aWorkerPrivate);
1223     if (index != kNoIndex) {
1224       // Was queued, remove from the list.
1225       domainInfo->mQueuedWorkers.RemoveElementAt(index);
1226     } else if (parent) {
1227       MOZ_ASSERT(domainInfo->mChildWorkerCount, "Must be non-zero!");
1228       domainInfo->mChildWorkerCount--;
1229     } else if (aWorkerPrivate.IsServiceWorker()) {
1230       MOZ_ASSERT(domainInfo->mActiveServiceWorkers.Contains(&aWorkerPrivate),
1231                  "Don't know about this worker!");
1232       domainInfo->mActiveServiceWorkers.RemoveElement(&aWorkerPrivate);
1233     } else {
1234       MOZ_ASSERT(domainInfo->mActiveWorkers.Contains(&aWorkerPrivate),
1235                  "Don't know about this worker!");
1236       domainInfo->mActiveWorkers.RemoveElement(&aWorkerPrivate);
1237     }
1238 
1239     // See if there's a queued worker we can schedule.
1240     if (domainInfo->ActiveWorkerCount() < gMaxWorkersPerDomain &&
1241         !domainInfo->mQueuedWorkers.IsEmpty()) {
1242       queuedWorker = domainInfo->mQueuedWorkers[0];
1243       domainInfo->mQueuedWorkers.RemoveElementAt(0);
1244 
1245       if (queuedWorker->GetParent()) {
1246         domainInfo->mChildWorkerCount++;
1247       } else if (queuedWorker->IsServiceWorker()) {
1248         domainInfo->mActiveServiceWorkers.AppendElement(queuedWorker);
1249       } else {
1250         domainInfo->mActiveWorkers.AppendElement(queuedWorker);
1251       }
1252     }
1253 
1254     if (domainInfo->HasNoWorkers()) {
1255       MOZ_ASSERT(domainInfo->mQueuedWorkers.IsEmpty());
1256       mDomainMap.Remove(domain);
1257     }
1258   }
1259 
1260   if (aWorkerPrivate.IsServiceWorker()) {
1261     AssertIsOnMainThread();
1262     Telemetry::AccumulateTimeDelta(Telemetry::SERVICE_WORKER_LIFE_TIME,
1263                                    aWorkerPrivate.CreationTimeStamp());
1264   }
1265 
1266   // NB: For Shared Workers we used to call ShutdownOnMainThread on the
1267   // RemoteWorkerController; however, that was redundant because
1268   // RemoteWorkerChild uses a WeakWorkerRef which notifies at about the
1269   // same time as us calling into the code here and would race with us.
1270 
1271   if (parent) {
1272     parent->RemoveChildWorker(aWorkerPrivate);
1273   } else if (aWorkerPrivate.IsSharedWorker()) {
1274     AssertIsOnMainThread();
1275 
1276     mWindowMap.RemoveIf([&aWorkerPrivate](const auto& iter) {
1277       const auto& workers = iter.Data();
1278       MOZ_ASSERT(workers);
1279 
1280       if (workers->RemoveElement(&aWorkerPrivate)) {
1281         MOZ_ASSERT(!workers->Contains(&aWorkerPrivate),
1282                    "Added worker more than once!");
1283 
1284         return workers->IsEmpty();
1285       }
1286 
1287       return false;
1288     });
1289   } else if (aWorkerPrivate.IsDedicatedWorker()) {
1290     // May be null.
1291     nsPIDOMWindowInner* window = aWorkerPrivate.GetWindow();
1292     if (auto entry = mWindowMap.Lookup(window)) {
1293       MOZ_ALWAYS_TRUE(entry.Data()->RemoveElement(&aWorkerPrivate));
1294       if (entry.Data()->IsEmpty()) {
1295         entry.Remove();
1296       }
1297     } else {
1298       MOZ_ASSERT_UNREACHABLE("window is not in mWindowMap");
1299     }
1300   }
1301 
1302   if (queuedWorker && !ScheduleWorker(*queuedWorker)) {
1303     UnregisterWorker(*queuedWorker);
1304   }
1305 }
1306 
ScheduleWorker(WorkerPrivate & aWorkerPrivate)1307 bool RuntimeService::ScheduleWorker(WorkerPrivate& aWorkerPrivate) {
1308   if (!aWorkerPrivate.Start()) {
1309     // This is ok, means that we didn't need to make a thread for this worker.
1310     return true;
1311   }
1312 
1313   SafeRefPtr<WorkerThread> thread;
1314   {
1315     MutexAutoLock lock(mMutex);
1316     if (!mIdleThreadArray.IsEmpty()) {
1317       thread = std::move(mIdleThreadArray.PopLastElement().mThread);
1318     }
1319   }
1320 
1321   const WorkerThreadFriendKey friendKey;
1322 
1323   if (!thread) {
1324     thread = WorkerThread::Create(friendKey);
1325     if (!thread) {
1326       UnregisterWorker(aWorkerPrivate);
1327       return false;
1328     }
1329   }
1330 
1331   if (NS_FAILED(thread->SetPriority(nsISupportsPriority::PRIORITY_NORMAL))) {
1332     NS_WARNING("Could not set the thread's priority!");
1333   }
1334 
1335   aWorkerPrivate.SetThread(thread.unsafeGetRawPtr());
1336   JSContext* cx = CycleCollectedJSContext::Get()->Context();
1337   nsCOMPtr<nsIRunnable> runnable = new WorkerThreadPrimaryRunnable(
1338       &aWorkerPrivate, thread.clonePtr(), JS_GetParentRuntime(cx));
1339   if (NS_FAILED(
1340           thread->DispatchPrimaryRunnable(friendKey, runnable.forget()))) {
1341     UnregisterWorker(aWorkerPrivate);
1342     return false;
1343   }
1344 
1345   return true;
1346 }
1347 
1348 // static
ShutdownIdleThreads(nsITimer * aTimer,void *)1349 void RuntimeService::ShutdownIdleThreads(nsITimer* aTimer,
1350                                          void* /* aClosure */) {
1351   AssertIsOnMainThread();
1352 
1353   RuntimeService* runtime = RuntimeService::GetService();
1354   NS_ASSERTION(runtime, "This should never be null!");
1355 
1356   NS_ASSERTION(aTimer == runtime->mIdleThreadTimer, "Wrong timer!");
1357 
1358   // Cheat a little and grab all threads that expire within one second of now.
1359   const TimeStamp now = TimeStamp::NowLoRes() + TimeDuration::FromSeconds(1);
1360 
1361   TimeStamp nextExpiration;
1362 
1363   AutoTArray<SafeRefPtr<WorkerThread>, 20> expiredThreads;
1364   {
1365     MutexAutoLock lock(runtime->mMutex);
1366 
1367     for (auto& info : runtime->mIdleThreadArray) {
1368       if (info.mExpirationTime > now) {
1369         nextExpiration = info.mExpirationTime;
1370         break;
1371       }
1372 
1373       expiredThreads.AppendElement(std::move(info.mThread));
1374     }
1375 
1376     runtime->mIdleThreadArray.RemoveElementsAt(0, expiredThreads.Length());
1377   }
1378 
1379   if (!nextExpiration.IsNull()) {
1380     const TimeDuration delta = nextExpiration - TimeStamp::NowLoRes();
1381     const uint32_t delay = delta > TimeDuration{} ? delta.ToMilliseconds() : 0;
1382 
1383     // Reschedule the timer.
1384     MOZ_ALWAYS_SUCCEEDS(aTimer->InitWithNamedFuncCallback(
1385         ShutdownIdleThreads, nullptr, delay, nsITimer::TYPE_ONE_SHOT,
1386         "RuntimeService::ShutdownIdleThreads"));
1387   }
1388 
1389   for (const auto& expiredThread : expiredThreads) {
1390     if (NS_FAILED(expiredThread->Shutdown())) {
1391       NS_WARNING("Failed to shutdown thread!");
1392     }
1393   }
1394 }
1395 
Init()1396 nsresult RuntimeService::Init() {
1397   AssertIsOnMainThread();
1398 
1399   nsLayoutStatics::AddRef();
1400 
1401   // Initialize JSSettings.
1402   sDefaultJSSettings = MakeUnique<JSSettings>();
1403   SetDefaultJSGCSettings(JSGC_MAX_BYTES, Some(WORKER_DEFAULT_RUNTIME_HEAPSIZE));
1404   SetDefaultJSGCSettings(JSGC_ALLOCATION_THRESHOLD,
1405                          Some(WORKER_DEFAULT_ALLOCATION_THRESHOLD));
1406 
1407   // nsIStreamTransportService is thread-safe but it must be initialized on the
1408   // main-thread. FileReader needs it, so, let's initialize it now.
1409   nsresult rv;
1410   nsCOMPtr<nsIStreamTransportService> sts =
1411       do_GetService(kStreamTransportServiceCID, &rv);
1412   NS_ENSURE_TRUE(sts, NS_ERROR_FAILURE);
1413 
1414   mIdleThreadTimer = NS_NewTimer();
1415   NS_ENSURE_STATE(mIdleThreadTimer);
1416 
1417   nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
1418   NS_ENSURE_TRUE(obs, NS_ERROR_FAILURE);
1419 
1420   rv = obs->AddObserver(this, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID, false);
1421   NS_ENSURE_SUCCESS(rv, rv);
1422 
1423   rv = obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
1424   NS_ENSURE_SUCCESS(rv, rv);
1425 
1426   mObserved = true;
1427 
1428   if (NS_FAILED(obs->AddObserver(this, GC_REQUEST_OBSERVER_TOPIC, false))) {
1429     NS_WARNING("Failed to register for GC request notifications!");
1430   }
1431 
1432   if (NS_FAILED(obs->AddObserver(this, CC_REQUEST_OBSERVER_TOPIC, false))) {
1433     NS_WARNING("Failed to register for CC request notifications!");
1434   }
1435 
1436   if (NS_FAILED(
1437           obs->AddObserver(this, MEMORY_PRESSURE_OBSERVER_TOPIC, false))) {
1438     NS_WARNING("Failed to register for memory pressure notifications!");
1439   }
1440 
1441   if (NS_FAILED(
1442           obs->AddObserver(this, NS_IOSERVICE_OFFLINE_STATUS_TOPIC, false))) {
1443     NS_WARNING("Failed to register for offline notification event!");
1444   }
1445 
1446   MOZ_ASSERT(!gRuntimeServiceDuringInit, "This should be false!");
1447   gRuntimeServiceDuringInit = true;
1448 
1449 #define WORKER_PREF(name, callback) \
1450   NS_FAILED(Preferences::RegisterCallbackAndCall(callback, name))
1451 
1452   if (NS_FAILED(Preferences::RegisterPrefixCallbackAndCall(
1453           LoadJSGCMemoryOptions,
1454           PREF_JS_OPTIONS_PREFIX PREF_MEM_OPTIONS_PREFIX)) ||
1455 #ifdef JS_GC_ZEAL
1456       NS_FAILED(Preferences::RegisterCallback(
1457           LoadGCZealOptions, PREF_JS_OPTIONS_PREFIX PREF_GCZEAL)) ||
1458 #endif
1459       WORKER_PREF("intl.accept_languages", PrefLanguagesChanged) ||
1460       WORKER_PREF("general.appname.override", AppNameOverrideChanged) ||
1461       WORKER_PREF("general.appversion.override", AppVersionOverrideChanged) ||
1462       WORKER_PREF("general.platform.override", PlatformOverrideChanged) ||
1463       NS_FAILED(Preferences::RegisterPrefixCallbackAndCall(
1464           LoadContextOptions, PREF_JS_OPTIONS_PREFIX))) {
1465     NS_WARNING("Failed to register pref callbacks!");
1466   }
1467 
1468 #undef WORKER_PREF
1469 
1470   MOZ_ASSERT(gRuntimeServiceDuringInit, "Should be true!");
1471   gRuntimeServiceDuringInit = false;
1472 
1473   int32_t maxPerDomain =
1474       Preferences::GetInt(PREF_WORKERS_MAX_PER_DOMAIN, MAX_WORKERS_PER_DOMAIN);
1475   gMaxWorkersPerDomain = std::max(0, maxPerDomain);
1476 
1477   RefPtr<OSFileConstantsService> osFileConstantsService =
1478       OSFileConstantsService::GetOrCreate();
1479   if (NS_WARN_IF(!osFileConstantsService)) {
1480     return NS_ERROR_FAILURE;
1481   }
1482 
1483   if (NS_WARN_IF(!IndexedDatabaseManager::GetOrCreate())) {
1484     return NS_ERROR_UNEXPECTED;
1485   }
1486 
1487   // PerformanceService must be initialized on the main-thread.
1488   PerformanceService::GetOrCreate();
1489 
1490   return NS_OK;
1491 }
1492 
Shutdown()1493 void RuntimeService::Shutdown() {
1494   AssertIsOnMainThread();
1495 
1496   MOZ_ASSERT(!mShuttingDown);
1497   // That's it, no more workers.
1498   mShuttingDown = true;
1499 
1500   nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
1501   NS_WARNING_ASSERTION(obs, "Failed to get observer service?!");
1502 
1503   // Tell anyone that cares that they're about to lose worker support.
1504   if (obs && NS_FAILED(obs->NotifyObservers(nullptr, WORKERS_SHUTDOWN_TOPIC,
1505                                             nullptr))) {
1506     NS_WARNING("NotifyObservers failed!");
1507   }
1508 
1509   {
1510     AutoTArray<WorkerPrivate*, 100> workers;
1511 
1512     {
1513       MutexAutoLock lock(mMutex);
1514 
1515       AddAllTopLevelWorkersToArray(workers);
1516     }
1517 
1518     // Cancel all top-level workers.
1519     for (const auto& worker : workers) {
1520       if (!worker->Cancel()) {
1521         NS_WARNING("Failed to cancel worker!");
1522       }
1523     }
1524   }
1525 
1526   sDefaultJSSettings = nullptr;
1527 }
1528 
1529 namespace {
1530 
1531 class CrashIfHangingRunnable : public WorkerControlRunnable {
1532  public:
CrashIfHangingRunnable(WorkerPrivate * aWorkerPrivate)1533   explicit CrashIfHangingRunnable(WorkerPrivate* aWorkerPrivate)
1534       : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
1535         mMonitor("CrashIfHangingRunnable::mMonitor") {}
1536 
WorkerRun(JSContext * aCx,WorkerPrivate * aWorkerPrivate)1537   bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
1538     MonitorAutoLock lock(mMonitor);
1539     if (!mHasMsg) {
1540       aWorkerPrivate->DumpCrashInformation(mMsg);
1541       mHasMsg.Flip();
1542     }
1543     lock.Notify();
1544     return true;
1545   }
1546 
Cancel()1547   nsresult Cancel() override {
1548     // We need to check first if cancel is called twice
1549     nsresult rv = WorkerRunnable::Cancel();
1550     NS_ENSURE_SUCCESS(rv, rv);
1551 
1552     MonitorAutoLock lock(mMonitor);
1553     if (!mHasMsg) {
1554       mMsg.Assign("Canceled");
1555       mHasMsg.Flip();
1556     }
1557     lock.Notify();
1558 
1559     return NS_OK;
1560   }
1561 
DispatchAndWait()1562   bool DispatchAndWait() {
1563     MonitorAutoLock lock(mMonitor);
1564 
1565     if (!Dispatch()) {
1566       // The worker is already dead but the main thread still didn't remove it
1567       // from RuntimeService's registry.
1568       return false;
1569     }
1570 
1571     // To avoid any possibility of process hangs we never receive reports on
1572     // we give the worker 1sec to react.
1573     lock.Wait(TimeDuration::FromMilliseconds(1000));
1574     if (!mHasMsg) {
1575       mMsg.Append("NoResponse");
1576       mHasMsg.Flip();
1577     }
1578     return true;
1579   }
1580 
MsgData() const1581   const nsCString& MsgData() const { return mMsg; }
1582 
1583  private:
PreDispatch(WorkerPrivate * aWorkerPrivate)1584   bool PreDispatch(WorkerPrivate* aWorkerPrivate) override { return true; }
1585 
PostDispatch(WorkerPrivate * aWorkerPrivate,bool aDispatchResult)1586   void PostDispatch(WorkerPrivate* aWorkerPrivate,
1587                     bool aDispatchResult) override {}
1588 
1589   Monitor mMonitor;
1590   nsCString mMsg;
1591   FlippedOnce<false> mHasMsg;
1592 };
1593 
1594 struct ActiveWorkerStats {
1595   template <uint32_t ActiveWorkerStats::*Category>
Updatemozilla::dom::workerinternals::__anon3fcc0c900511::ActiveWorkerStats1596   void Update(const nsTArray<WorkerPrivate*>& aWorkers) {
1597     for (const auto worker : aWorkers) {
1598       RefPtr<CrashIfHangingRunnable> runnable =
1599           new CrashIfHangingRunnable(worker);
1600       if (runnable->DispatchAndWait()) {
1601         ++(this->*Category);
1602 
1603         // BC: Busy Count
1604         mMessage.AppendPrintf("-BC:%d", worker->BusyCount());
1605         mMessage.Append(runnable->MsgData());
1606       } else {
1607         mMessage.AppendPrintf("-BC:%d DispatchFailed", worker->BusyCount());
1608       }
1609     }
1610   }
1611 
1612   uint32_t mWorkers = 0;
1613   uint32_t mServiceWorkers = 0;
1614   nsCString mMessage;
1615 };
1616 
1617 }  // namespace
1618 
CrashIfHanging()1619 void RuntimeService::CrashIfHanging() {
1620   MutexAutoLock lock(mMutex);
1621 
1622   ActiveWorkerStats activeStats;
1623   uint32_t inactiveWorkers = 0;
1624 
1625   for (const auto& aData : mDomainMap.Values()) {
1626     activeStats.Update<&ActiveWorkerStats::mWorkers>(aData->mActiveWorkers);
1627     activeStats.Update<&ActiveWorkerStats::mServiceWorkers>(
1628         aData->mActiveServiceWorkers);
1629 
1630     // These might not be top-level workers...
1631     inactiveWorkers += std::count_if(
1632         aData->mQueuedWorkers.begin(), aData->mQueuedWorkers.end(),
1633         [](const auto* const worker) { return !worker->GetParent(); });
1634   }
1635 
1636   if (activeStats.mWorkers + activeStats.mServiceWorkers + inactiveWorkers ==
1637       0) {
1638     return;
1639   }
1640 
1641   nsCString msg;
1642 
1643   // A: active Workers | S: active ServiceWorkers | Q: queued Workers
1644   msg.AppendPrintf("Workers Hanging - %d|A:%d|S:%d|Q:%d", mShuttingDown ? 1 : 0,
1645                    activeStats.mWorkers, activeStats.mServiceWorkers,
1646                    inactiveWorkers);
1647   msg.Append(activeStats.mMessage);
1648 
1649   // This string will be leaked.
1650   MOZ_CRASH_UNSAFE(strdup(msg.BeginReading()));
1651 }
1652 
1653 // This spins the event loop until all workers are finished and their threads
1654 // have been joined.
Cleanup()1655 void RuntimeService::Cleanup() {
1656   AssertIsOnMainThread();
1657 
1658   if (!mShuttingDown) {
1659     Shutdown();
1660   }
1661 
1662   nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
1663   NS_WARNING_ASSERTION(obs, "Failed to get observer service?!");
1664 
1665   if (mIdleThreadTimer) {
1666     if (NS_FAILED(mIdleThreadTimer->Cancel())) {
1667       NS_WARNING("Failed to cancel idle timer!");
1668     }
1669     mIdleThreadTimer = nullptr;
1670   }
1671 
1672   {
1673     MutexAutoLock lock(mMutex);
1674 
1675     AutoTArray<WorkerPrivate*, 100> workers;
1676     AddAllTopLevelWorkersToArray(workers);
1677 
1678     if (!workers.IsEmpty()) {
1679       nsIThread* currentThread = NS_GetCurrentThread();
1680       NS_ASSERTION(currentThread, "This should never be null!");
1681 
1682       // Shut down any idle threads.
1683       if (!mIdleThreadArray.IsEmpty()) {
1684         AutoTArray<SafeRefPtr<WorkerThread>, 20> idleThreads;
1685         idleThreads.SetCapacity(mIdleThreadArray.Length());
1686 
1687 #ifdef DEBUG
1688         const bool anyNullThread = std::any_of(
1689             mIdleThreadArray.begin(), mIdleThreadArray.end(),
1690             [](const auto& entry) { return entry.mThread == nullptr; });
1691         MOZ_ASSERT(!anyNullThread);
1692 #endif
1693 
1694         std::transform(mIdleThreadArray.begin(), mIdleThreadArray.end(),
1695                        MakeBackInserter(idleThreads),
1696                        [](auto& entry) { return std::move(entry.mThread); });
1697 
1698         mIdleThreadArray.Clear();
1699 
1700         MutexAutoUnlock unlock(mMutex);
1701 
1702         for (const auto& idleThread : idleThreads) {
1703           if (NS_FAILED(idleThread->Shutdown())) {
1704             NS_WARNING("Failed to shutdown thread!");
1705           }
1706         }
1707       }
1708 
1709       // And make sure all their final messages have run and all their threads
1710       // have joined.
1711       while (mDomainMap.Count()) {
1712         MutexAutoUnlock unlock(mMutex);
1713 
1714         if (!NS_ProcessNextEvent(currentThread)) {
1715           NS_WARNING("Something bad happened!");
1716           break;
1717         }
1718       }
1719     }
1720   }
1721 
1722   NS_ASSERTION(!mWindowMap.Count(), "All windows should have been released!");
1723 
1724 #define WORKER_PREF(name, callback) \
1725   NS_FAILED(Preferences::UnregisterCallback(callback, name))
1726 
1727   if (mObserved) {
1728     if (NS_FAILED(Preferences::UnregisterPrefixCallback(
1729             LoadContextOptions, PREF_JS_OPTIONS_PREFIX)) ||
1730         WORKER_PREF("intl.accept_languages", PrefLanguagesChanged) ||
1731         WORKER_PREF("general.appname.override", AppNameOverrideChanged) ||
1732         WORKER_PREF("general.appversion.override", AppVersionOverrideChanged) ||
1733         WORKER_PREF("general.platform.override", PlatformOverrideChanged) ||
1734 #ifdef JS_GC_ZEAL
1735         NS_FAILED(Preferences::UnregisterCallback(
1736             LoadGCZealOptions, PREF_JS_OPTIONS_PREFIX PREF_GCZEAL)) ||
1737 #endif
1738         NS_FAILED(Preferences::UnregisterPrefixCallback(
1739             LoadJSGCMemoryOptions,
1740             PREF_JS_OPTIONS_PREFIX PREF_MEM_OPTIONS_PREFIX))) {
1741       NS_WARNING("Failed to unregister pref callbacks!");
1742     }
1743 
1744 #undef WORKER_PREF
1745 
1746     if (obs) {
1747       if (NS_FAILED(obs->RemoveObserver(this, GC_REQUEST_OBSERVER_TOPIC))) {
1748         NS_WARNING("Failed to unregister for GC request notifications!");
1749       }
1750 
1751       if (NS_FAILED(obs->RemoveObserver(this, CC_REQUEST_OBSERVER_TOPIC))) {
1752         NS_WARNING("Failed to unregister for CC request notifications!");
1753       }
1754 
1755       if (NS_FAILED(
1756               obs->RemoveObserver(this, MEMORY_PRESSURE_OBSERVER_TOPIC))) {
1757         NS_WARNING("Failed to unregister for memory pressure notifications!");
1758       }
1759 
1760       if (NS_FAILED(
1761               obs->RemoveObserver(this, NS_IOSERVICE_OFFLINE_STATUS_TOPIC))) {
1762         NS_WARNING("Failed to unregister for offline notification event!");
1763       }
1764       obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID);
1765       obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
1766       mObserved = false;
1767     }
1768   }
1769 
1770   nsLayoutStatics::Release();
1771 }
1772 
AddAllTopLevelWorkersToArray(nsTArray<WorkerPrivate * > & aWorkers)1773 void RuntimeService::AddAllTopLevelWorkersToArray(
1774     nsTArray<WorkerPrivate*>& aWorkers) {
1775   for (const auto& aData : mDomainMap.Values()) {
1776 #ifdef DEBUG
1777     for (const auto& activeWorker : aData->mActiveWorkers) {
1778       MOZ_ASSERT(!activeWorker->GetParent(),
1779                  "Shouldn't have a parent in this list!");
1780     }
1781     for (const auto& activeServiceWorker : aData->mActiveServiceWorkers) {
1782       MOZ_ASSERT(!activeServiceWorker->GetParent(),
1783                  "Shouldn't have a parent in this list!");
1784     }
1785 #endif
1786 
1787     aWorkers.AppendElements(aData->mActiveWorkers);
1788     aWorkers.AppendElements(aData->mActiveServiceWorkers);
1789 
1790     // These might not be top-level workers...
1791     std::copy_if(aData->mQueuedWorkers.begin(), aData->mQueuedWorkers.end(),
1792                  MakeBackInserter(aWorkers),
1793                  [](const auto& worker) { return !worker->GetParent(); });
1794   }
1795 }
1796 
GetWorkersForWindow(const nsPIDOMWindowInner & aWindow) const1797 nsTArray<WorkerPrivate*> RuntimeService::GetWorkersForWindow(
1798     const nsPIDOMWindowInner& aWindow) const {
1799   AssertIsOnMainThread();
1800 
1801   nsTArray<WorkerPrivate*> result;
1802   if (nsTArray<WorkerPrivate*>* const workers = mWindowMap.Get(&aWindow)) {
1803     NS_ASSERTION(!workers->IsEmpty(), "Should have been removed!");
1804     result.AppendElements(*workers);
1805   }
1806   return result;
1807 }
1808 
CancelWorkersForWindow(const nsPIDOMWindowInner & aWindow)1809 void RuntimeService::CancelWorkersForWindow(const nsPIDOMWindowInner& aWindow) {
1810   AssertIsOnMainThread();
1811 
1812   for (WorkerPrivate* const worker : GetWorkersForWindow(aWindow)) {
1813     MOZ_ASSERT(!worker->IsSharedWorker());
1814     worker->Cancel();
1815   }
1816 }
1817 
FreezeWorkersForWindow(const nsPIDOMWindowInner & aWindow)1818 void RuntimeService::FreezeWorkersForWindow(const nsPIDOMWindowInner& aWindow) {
1819   AssertIsOnMainThread();
1820 
1821   for (WorkerPrivate* const worker : GetWorkersForWindow(aWindow)) {
1822     MOZ_ASSERT(!worker->IsSharedWorker());
1823     worker->Freeze(&aWindow);
1824   }
1825 }
1826 
ThawWorkersForWindow(const nsPIDOMWindowInner & aWindow)1827 void RuntimeService::ThawWorkersForWindow(const nsPIDOMWindowInner& aWindow) {
1828   AssertIsOnMainThread();
1829 
1830   for (WorkerPrivate* const worker : GetWorkersForWindow(aWindow)) {
1831     MOZ_ASSERT(!worker->IsSharedWorker());
1832     worker->Thaw(&aWindow);
1833   }
1834 }
1835 
SuspendWorkersForWindow(const nsPIDOMWindowInner & aWindow)1836 void RuntimeService::SuspendWorkersForWindow(
1837     const nsPIDOMWindowInner& aWindow) {
1838   AssertIsOnMainThread();
1839 
1840   for (WorkerPrivate* const worker : GetWorkersForWindow(aWindow)) {
1841     MOZ_ASSERT(!worker->IsSharedWorker());
1842     worker->ParentWindowPaused();
1843   }
1844 }
1845 
ResumeWorkersForWindow(const nsPIDOMWindowInner & aWindow)1846 void RuntimeService::ResumeWorkersForWindow(const nsPIDOMWindowInner& aWindow) {
1847   AssertIsOnMainThread();
1848 
1849   for (WorkerPrivate* const worker : GetWorkersForWindow(aWindow)) {
1850     MOZ_ASSERT(!worker->IsSharedWorker());
1851     worker->ParentWindowResumed();
1852   }
1853 }
1854 
PropagateStorageAccessPermissionGranted(const nsPIDOMWindowInner & aWindow)1855 void RuntimeService::PropagateStorageAccessPermissionGranted(
1856     const nsPIDOMWindowInner& aWindow) {
1857   AssertIsOnMainThread();
1858   MOZ_ASSERT_IF(aWindow.GetExtantDoc(), aWindow.GetExtantDoc()
1859                                             ->CookieJarSettings()
1860                                             ->GetRejectThirdPartyContexts());
1861 
1862   for (WorkerPrivate* const worker : GetWorkersForWindow(aWindow)) {
1863     worker->PropagateStorageAccessPermissionGranted();
1864   }
1865 }
1866 
NoteIdleThread(SafeRefPtr<WorkerThread> aThread)1867 void RuntimeService::NoteIdleThread(SafeRefPtr<WorkerThread> aThread) {
1868   AssertIsOnMainThread();
1869   MOZ_ASSERT(aThread);
1870 
1871   bool shutdownThread = mShuttingDown;
1872   bool scheduleTimer = false;
1873 
1874   if (!shutdownThread) {
1875     static TimeDuration timeout =
1876         TimeDuration::FromSeconds(IDLE_THREAD_TIMEOUT_SEC);
1877 
1878     const TimeStamp expirationTime = TimeStamp::NowLoRes() + timeout;
1879 
1880     MutexAutoLock lock(mMutex);
1881 
1882     const uint32_t previousIdleCount = mIdleThreadArray.Length();
1883 
1884     if (previousIdleCount < MAX_IDLE_THREADS) {
1885       IdleThreadInfo* const info = mIdleThreadArray.AppendElement();
1886       info->mThread = std::move(aThread);
1887       info->mExpirationTime = expirationTime;
1888 
1889       scheduleTimer = previousIdleCount == 0;
1890     } else {
1891       shutdownThread = true;
1892     }
1893   }
1894 
1895   MOZ_ASSERT_IF(shutdownThread, !scheduleTimer);
1896   MOZ_ASSERT_IF(scheduleTimer, !shutdownThread);
1897 
1898   // Too many idle threads, just shut this one down.
1899   if (shutdownThread) {
1900     MOZ_ALWAYS_SUCCEEDS(aThread->Shutdown());
1901   } else if (scheduleTimer) {
1902     MOZ_ALWAYS_SUCCEEDS(mIdleThreadTimer->InitWithNamedFuncCallback(
1903         ShutdownIdleThreads, nullptr, IDLE_THREAD_TIMEOUT_SEC * 1000,
1904         nsITimer::TYPE_ONE_SHOT, "RuntimeService::ShutdownIdleThreads"));
1905   }
1906 }
1907 
1908 template <typename Func>
BroadcastAllWorkers(const Func & aFunc)1909 void RuntimeService::BroadcastAllWorkers(const Func& aFunc) {
1910   AssertIsOnMainThread();
1911 
1912   AutoTArray<WorkerPrivate*, 100> workers;
1913   {
1914     MutexAutoLock lock(mMutex);
1915 
1916     AddAllTopLevelWorkersToArray(workers);
1917   }
1918 
1919   for (const auto& worker : workers) {
1920     aFunc(*worker);
1921   }
1922 }
1923 
UpdateAllWorkerContextOptions()1924 void RuntimeService::UpdateAllWorkerContextOptions() {
1925   BroadcastAllWorkers([](auto& worker) {
1926     worker.UpdateContextOptions(sDefaultJSSettings->contextOptions);
1927   });
1928 }
1929 
UpdateAppNameOverridePreference(const nsAString & aValue)1930 void RuntimeService::UpdateAppNameOverridePreference(const nsAString& aValue) {
1931   AssertIsOnMainThread();
1932   mNavigatorProperties.mAppNameOverridden = aValue;
1933 }
1934 
UpdateAppVersionOverridePreference(const nsAString & aValue)1935 void RuntimeService::UpdateAppVersionOverridePreference(
1936     const nsAString& aValue) {
1937   AssertIsOnMainThread();
1938   mNavigatorProperties.mAppVersionOverridden = aValue;
1939 }
1940 
UpdatePlatformOverridePreference(const nsAString & aValue)1941 void RuntimeService::UpdatePlatformOverridePreference(const nsAString& aValue) {
1942   AssertIsOnMainThread();
1943   mNavigatorProperties.mPlatformOverridden = aValue;
1944 }
1945 
UpdateAllWorkerLanguages(const nsTArray<nsString> & aLanguages)1946 void RuntimeService::UpdateAllWorkerLanguages(
1947     const nsTArray<nsString>& aLanguages) {
1948   MOZ_ASSERT(NS_IsMainThread());
1949 
1950   mNavigatorProperties.mLanguages = aLanguages.Clone();
1951   BroadcastAllWorkers(
1952       [&aLanguages](auto& worker) { worker.UpdateLanguages(aLanguages); });
1953 }
1954 
UpdateAllWorkerMemoryParameter(JSGCParamKey aKey,Maybe<uint32_t> aValue)1955 void RuntimeService::UpdateAllWorkerMemoryParameter(JSGCParamKey aKey,
1956                                                     Maybe<uint32_t> aValue) {
1957   BroadcastAllWorkers([aKey, aValue](auto& worker) {
1958     worker.UpdateJSWorkerMemoryParameter(aKey, aValue);
1959   });
1960 }
1961 
1962 #ifdef JS_GC_ZEAL
UpdateAllWorkerGCZeal()1963 void RuntimeService::UpdateAllWorkerGCZeal() {
1964   BroadcastAllWorkers([](auto& worker) {
1965     worker.UpdateGCZeal(sDefaultJSSettings->gcZeal,
1966                         sDefaultJSSettings->gcZealFrequency);
1967   });
1968 }
1969 #endif
1970 
SetLowMemoryStateAllWorkers(bool aState)1971 void RuntimeService::SetLowMemoryStateAllWorkers(bool aState) {
1972   BroadcastAllWorkers(
1973       [aState](auto& worker) { worker.SetLowMemoryState(aState); });
1974 }
1975 
GarbageCollectAllWorkers(bool aShrinking)1976 void RuntimeService::GarbageCollectAllWorkers(bool aShrinking) {
1977   BroadcastAllWorkers(
1978       [aShrinking](auto& worker) { worker.GarbageCollect(aShrinking); });
1979 }
1980 
CycleCollectAllWorkers()1981 void RuntimeService::CycleCollectAllWorkers() {
1982   BroadcastAllWorkers([](auto& worker) { worker.CycleCollect(); });
1983 }
1984 
SendOfflineStatusChangeEventToAllWorkers(bool aIsOffline)1985 void RuntimeService::SendOfflineStatusChangeEventToAllWorkers(bool aIsOffline) {
1986   BroadcastAllWorkers([aIsOffline](auto& worker) {
1987     worker.OfflineStatusChangeEvent(aIsOffline);
1988   });
1989 }
1990 
MemoryPressureAllWorkers()1991 void RuntimeService::MemoryPressureAllWorkers() {
1992   BroadcastAllWorkers([](auto& worker) { worker.MemoryPressure(); });
1993 }
1994 
ClampedHardwareConcurrency() const1995 uint32_t RuntimeService::ClampedHardwareConcurrency() const {
1996   // The Firefox Hardware Report says 70% of Firefox users have exactly 2 cores.
1997   // When the resistFingerprinting pref is set, we want to blend into the crowd
1998   // so spoof navigator.hardwareConcurrency = 2 to reduce user uniqueness.
1999   if (MOZ_UNLIKELY(nsContentUtils::ShouldResistFingerprinting())) {
2000     return 2;
2001   }
2002 
2003   // This needs to be atomic, because multiple workers, and even mainthread,
2004   // could race to initialize it at once.
2005   static Atomic<uint32_t> unclampedHardwareConcurrency;
2006 
2007   // No need to loop here: if compareExchange fails, that just means that some
2008   // other worker has initialized numberOfProcessors, so we're good to go.
2009   if (!unclampedHardwareConcurrency) {
2010     int32_t numberOfProcessors = 0;
2011 #if defined(XP_MACOSX)
2012     if (nsMacUtilsImpl::IsTCSMAvailable()) {
2013       // On failure, zero is returned from GetPhysicalCPUCount()
2014       // and we fallback to PR_GetNumberOfProcessors below.
2015       numberOfProcessors = nsMacUtilsImpl::GetPhysicalCPUCount();
2016     }
2017 #endif
2018     if (numberOfProcessors == 0) {
2019       numberOfProcessors = PR_GetNumberOfProcessors();
2020     }
2021     if (numberOfProcessors <= 0) {
2022       numberOfProcessors = 1;  // Must be one there somewhere
2023     }
2024     Unused << unclampedHardwareConcurrency.compareExchange(0,
2025                                                            numberOfProcessors);
2026   }
2027 
2028   return std::min(uint32_t(unclampedHardwareConcurrency),
2029                   StaticPrefs::dom_maxHardwareConcurrency());
2030 }
2031 
2032 // nsISupports
NS_IMPL_ISUPPORTS(RuntimeService,nsIObserver)2033 NS_IMPL_ISUPPORTS(RuntimeService, nsIObserver)
2034 
2035 // nsIObserver
2036 NS_IMETHODIMP
2037 RuntimeService::Observe(nsISupports* aSubject, const char* aTopic,
2038                         const char16_t* aData) {
2039   AssertIsOnMainThread();
2040 
2041   if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
2042     Shutdown();
2043     return NS_OK;
2044   }
2045   if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID)) {
2046     Cleanup();
2047     return NS_OK;
2048   }
2049   if (!strcmp(aTopic, GC_REQUEST_OBSERVER_TOPIC)) {
2050     GarbageCollectAllWorkers(/* shrinking = */ false);
2051     return NS_OK;
2052   }
2053   if (!strcmp(aTopic, CC_REQUEST_OBSERVER_TOPIC)) {
2054     CycleCollectAllWorkers();
2055     return NS_OK;
2056   }
2057   if (!strcmp(aTopic, MEMORY_PRESSURE_OBSERVER_TOPIC)) {
2058     nsDependentString data(aData);
2059     // Don't continue to GC/CC if we are in an ongoing low-memory state since
2060     // its very slow and it likely won't help us anyway.
2061     if (data.EqualsLiteral(LOW_MEMORY_ONGOING_DATA)) {
2062       return NS_OK;
2063     }
2064     if (data.EqualsLiteral(LOW_MEMORY_DATA)) {
2065       SetLowMemoryStateAllWorkers(true);
2066     }
2067     GarbageCollectAllWorkers(/* shrinking = */ true);
2068     CycleCollectAllWorkers();
2069     MemoryPressureAllWorkers();
2070     return NS_OK;
2071   }
2072   if (!strcmp(aTopic, MEMORY_PRESSURE_STOP_OBSERVER_TOPIC)) {
2073     SetLowMemoryStateAllWorkers(false);
2074     return NS_OK;
2075   }
2076   if (!strcmp(aTopic, NS_IOSERVICE_OFFLINE_STATUS_TOPIC)) {
2077     SendOfflineStatusChangeEventToAllWorkers(NS_IsOffline());
2078     return NS_OK;
2079   }
2080 
2081   MOZ_ASSERT_UNREACHABLE("Unknown observer topic!");
2082   return NS_OK;
2083 }
2084 
MainThreadRun()2085 bool LogViolationDetailsRunnable::MainThreadRun() {
2086   AssertIsOnMainThread();
2087 
2088   nsIContentSecurityPolicy* csp = mWorkerPrivate->GetCSP();
2089   if (csp) {
2090     if (mWorkerPrivate->GetReportCSPViolations()) {
2091       csp->LogViolationDetails(nsIContentSecurityPolicy::VIOLATION_TYPE_EVAL,
2092                                nullptr,  // triggering element
2093                                mWorkerPrivate->CSPEventListener(), mFileName,
2094                                mScriptSample, mLineNum, mColumnNum, u""_ns,
2095                                u""_ns);
2096     }
2097   }
2098 
2099   return true;
2100 }
2101 
2102 // MOZ_CAN_RUN_SCRIPT_BOUNDARY until Runnable::Run is MOZ_CAN_RUN_SCRIPT.  See
2103 // bug 1535398.
2104 MOZ_CAN_RUN_SCRIPT_BOUNDARY
2105 NS_IMETHODIMP
Run()2106 WorkerThreadPrimaryRunnable::Run() {
2107   AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING(
2108       "WorkerThreadPrimaryRunnable::Run", OTHER, mWorkerPrivate->ScriptURL());
2109 
2110   using mozilla::ipc::BackgroundChild;
2111 
2112   {
2113     auto failureCleanup = MakeScopeExit([&]() {
2114       // The creation of threadHelper above is the point at which a worker is
2115       // considered to have run, because the `mPreStartRunnables` are all
2116       // re-dispatched after `mThread` is set.  We need to let the WorkerPrivate
2117       // know so it can clean up the various event loops and delete the worker.
2118       mWorkerPrivate->RunLoopNeverRan();
2119     });
2120 
2121     mWorkerPrivate->SetWorkerPrivateInWorkerThread(mThread.unsafeGetRawPtr());
2122 
2123     const auto threadCleanup = MakeScopeExit([&] {
2124       // This must be called before ScheduleDeletion, which is either called
2125       // from failureCleanup leaving scope, or from the outer scope.
2126       mWorkerPrivate->ResetWorkerPrivateInWorkerThread();
2127     });
2128 
2129     mWorkerPrivate->AssertIsOnWorkerThread();
2130 
2131     // This needs to be initialized on the worker thread before being used on
2132     // the main thread and calling BackgroundChild::GetOrCreateForCurrentThread
2133     // exposes it to the main thread.
2134     mWorkerPrivate->EnsurePerformanceStorage();
2135 
2136     if (NS_WARN_IF(!BackgroundChild::GetOrCreateForCurrentThread())) {
2137       return NS_ERROR_FAILURE;
2138     }
2139 
2140     {
2141       nsCycleCollector_startup();
2142 
2143       auto context = MakeUnique<WorkerJSContext>(mWorkerPrivate);
2144       nsresult rv = context->Initialize(mParentRuntime);
2145       if (NS_WARN_IF(NS_FAILED(rv))) {
2146         return rv;
2147       }
2148 
2149       JSContext* cx = context->Context();
2150 
2151       if (!InitJSContextForWorker(mWorkerPrivate, cx)) {
2152         return NS_ERROR_FAILURE;
2153       }
2154 
2155       failureCleanup.release();
2156 
2157       {
2158         PROFILER_SET_JS_CONTEXT(cx);
2159 
2160         {
2161           // We're on the worker thread here, and WorkerPrivate's refcounting is
2162           // non-threadsafe: you can only do it on the parent thread.  What that
2163           // means in practice is that we're relying on it being kept alive
2164           // while we run.  Hopefully.
2165           MOZ_KnownLive(mWorkerPrivate)->DoRunLoop(cx);
2166           // The AutoJSAPI in DoRunLoop should have reported any exceptions left
2167           // on cx.
2168           MOZ_ASSERT(!JS_IsExceptionPending(cx));
2169         }
2170 
2171         BackgroundChild::CloseForCurrentThread();
2172 
2173         PROFILER_CLEAR_JS_CONTEXT();
2174       }
2175 
2176       // There may still be runnables on the debugger event queue that hold a
2177       // strong reference to the debugger global scope. These runnables are not
2178       // visible to the cycle collector, so we need to make sure to clear the
2179       // debugger event queue before we try to destroy the context. If we don't,
2180       // the garbage collector will crash.
2181       mWorkerPrivate->ClearDebuggerEventQueue();
2182 
2183       // Perform a full GC. This will collect the main worker global and CC,
2184       // which should break all cycles that touch JS.
2185       JS::PrepareForFullGC(cx);
2186       JS::NonIncrementalGC(cx, JS::GCOptions::Shutdown,
2187                            JS::GCReason::WORKER_SHUTDOWN);
2188 
2189       // Before shutting down the cycle collector we need to do one more pass
2190       // through the event loop to clean up any C++ objects that need deferred
2191       // cleanup.
2192       mWorkerPrivate->ClearMainEventQueue(WorkerPrivate::WorkerRan);
2193 
2194       // Now WorkerJSContext goes out of scope and its destructor will shut
2195       // down the cycle collector. This breaks any remaining cycles and collects
2196       // any remaining C++ objects.
2197     }
2198   }
2199 
2200   mWorkerPrivate->ScheduleDeletion(WorkerPrivate::WorkerRan);
2201 
2202   // It is no longer safe to touch mWorkerPrivate.
2203   mWorkerPrivate = nullptr;
2204 
2205   // Now recycle this thread.
2206   nsCOMPtr<nsIEventTarget> mainTarget = GetMainThreadEventTarget();
2207   MOZ_ASSERT(mainTarget);
2208 
2209   RefPtr<FinishedRunnable> finishedRunnable =
2210       new FinishedRunnable(std::move(mThread));
2211   MOZ_ALWAYS_SUCCEEDS(
2212       mainTarget->Dispatch(finishedRunnable, NS_DISPATCH_NORMAL));
2213 
2214   return NS_OK;
2215 }
2216 
2217 NS_IMETHODIMP
Run()2218 WorkerThreadPrimaryRunnable::FinishedRunnable::Run() {
2219   AssertIsOnMainThread();
2220 
2221   SafeRefPtr<WorkerThread> thread = std::move(mThread);
2222 
2223   RuntimeService* rts = RuntimeService::GetService();
2224   if (rts) {
2225     rts->NoteIdleThread(std::move(thread));
2226   } else if (thread->ShutdownRequired()) {
2227     MOZ_ALWAYS_SUCCEEDS(thread->Shutdown());
2228   }
2229 
2230   return NS_OK;
2231 }
2232 
2233 }  // namespace workerinternals
2234 
CancelWorkersForWindow(const nsPIDOMWindowInner & aWindow)2235 void CancelWorkersForWindow(const nsPIDOMWindowInner& aWindow) {
2236   AssertIsOnMainThread();
2237   RuntimeService* runtime = RuntimeService::GetService();
2238   if (runtime) {
2239     runtime->CancelWorkersForWindow(aWindow);
2240   }
2241 }
2242 
FreezeWorkersForWindow(const nsPIDOMWindowInner & aWindow)2243 void FreezeWorkersForWindow(const nsPIDOMWindowInner& aWindow) {
2244   AssertIsOnMainThread();
2245   RuntimeService* runtime = RuntimeService::GetService();
2246   if (runtime) {
2247     runtime->FreezeWorkersForWindow(aWindow);
2248   }
2249 }
2250 
ThawWorkersForWindow(const nsPIDOMWindowInner & aWindow)2251 void ThawWorkersForWindow(const nsPIDOMWindowInner& aWindow) {
2252   AssertIsOnMainThread();
2253   RuntimeService* runtime = RuntimeService::GetService();
2254   if (runtime) {
2255     runtime->ThawWorkersForWindow(aWindow);
2256   }
2257 }
2258 
SuspendWorkersForWindow(const nsPIDOMWindowInner & aWindow)2259 void SuspendWorkersForWindow(const nsPIDOMWindowInner& aWindow) {
2260   AssertIsOnMainThread();
2261   RuntimeService* runtime = RuntimeService::GetService();
2262   if (runtime) {
2263     runtime->SuspendWorkersForWindow(aWindow);
2264   }
2265 }
2266 
ResumeWorkersForWindow(const nsPIDOMWindowInner & aWindow)2267 void ResumeWorkersForWindow(const nsPIDOMWindowInner& aWindow) {
2268   AssertIsOnMainThread();
2269   RuntimeService* runtime = RuntimeService::GetService();
2270   if (runtime) {
2271     runtime->ResumeWorkersForWindow(aWindow);
2272   }
2273 }
2274 
PropagateStorageAccessPermissionGrantedToWorkers(const nsPIDOMWindowInner & aWindow)2275 void PropagateStorageAccessPermissionGrantedToWorkers(
2276     const nsPIDOMWindowInner& aWindow) {
2277   AssertIsOnMainThread();
2278   MOZ_ASSERT_IF(aWindow.GetExtantDoc(), aWindow.GetExtantDoc()
2279                                             ->CookieJarSettings()
2280                                             ->GetRejectThirdPartyContexts());
2281 
2282   RuntimeService* runtime = RuntimeService::GetService();
2283   if (runtime) {
2284     runtime->PropagateStorageAccessPermissionGranted(aWindow);
2285   }
2286 }
2287 
GetWorkerPrivateFromContext(JSContext * aCx)2288 WorkerPrivate* GetWorkerPrivateFromContext(JSContext* aCx) {
2289   MOZ_ASSERT(!NS_IsMainThread());
2290   MOZ_ASSERT(aCx);
2291 
2292   CycleCollectedJSContext* ccjscx = CycleCollectedJSContext::GetFor(aCx);
2293   if (!ccjscx) {
2294     return nullptr;
2295   }
2296 
2297   WorkerJSContext* workerjscx = ccjscx->GetAsWorkerJSContext();
2298   // GetWorkerPrivateFromContext is called only for worker contexts.  The
2299   // context private is cleared early in ~CycleCollectedJSContext() and so
2300   // GetFor() returns null above if called after ccjscx is no longer a
2301   // WorkerJSContext.
2302   MOZ_ASSERT(workerjscx);
2303   return workerjscx->GetWorkerPrivate();
2304 }
2305 
GetCurrentThreadWorkerPrivate()2306 WorkerPrivate* GetCurrentThreadWorkerPrivate() {
2307   if (NS_IsMainThread()) {
2308     return nullptr;
2309   }
2310 
2311   CycleCollectedJSContext* ccjscx = CycleCollectedJSContext::Get();
2312   if (!ccjscx) {
2313     return nullptr;
2314   }
2315 
2316   WorkerJSContext* workerjscx = ccjscx->GetAsWorkerJSContext();
2317   // Even when GetCurrentThreadWorkerPrivate() is called on worker
2318   // threads, the ccjscx will no longer be a WorkerJSContext if called from
2319   // stable state events during ~CycleCollectedJSContext().
2320   if (!workerjscx) {
2321     return nullptr;
2322   }
2323 
2324   return workerjscx->GetWorkerPrivate();
2325 }
2326 
IsCurrentThreadRunningWorker()2327 bool IsCurrentThreadRunningWorker() {
2328   return !NS_IsMainThread() && !!GetCurrentThreadWorkerPrivate();
2329 }
2330 
IsCurrentThreadRunningChromeWorker()2331 bool IsCurrentThreadRunningChromeWorker() {
2332   WorkerPrivate* wp = GetCurrentThreadWorkerPrivate();
2333   return wp && wp->UsesSystemPrincipal();
2334 }
2335 
GetCurrentWorkerThreadJSContext()2336 JSContext* GetCurrentWorkerThreadJSContext() {
2337   WorkerPrivate* wp = GetCurrentThreadWorkerPrivate();
2338   if (!wp) {
2339     return nullptr;
2340   }
2341   return wp->GetJSContext();
2342 }
2343 
GetCurrentThreadWorkerGlobal()2344 JSObject* GetCurrentThreadWorkerGlobal() {
2345   WorkerPrivate* wp = GetCurrentThreadWorkerPrivate();
2346   if (!wp) {
2347     return nullptr;
2348   }
2349   WorkerGlobalScope* scope = wp->GlobalScope();
2350   if (!scope) {
2351     return nullptr;
2352   }
2353   return scope->GetGlobalJSObject();
2354 }
2355 
GetCurrentThreadWorkerDebuggerGlobal()2356 JSObject* GetCurrentThreadWorkerDebuggerGlobal() {
2357   WorkerPrivate* wp = GetCurrentThreadWorkerPrivate();
2358   if (!wp) {
2359     return nullptr;
2360   }
2361   WorkerDebuggerGlobalScope* scope = wp->DebuggerGlobalScope();
2362   if (!scope) {
2363     return nullptr;
2364   }
2365   return scope->GetGlobalJSObject();
2366 }
2367 
2368 }  // namespace dom
2369 }  // namespace mozilla
2370