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 "nsAutoPtr.h"
10 #include "nsIChannel.h"
11 #include "nsIContentSecurityPolicy.h"
12 #include "nsIDocument.h"
13 #include "nsIDOMChromeWindow.h"
14 #include "nsIEffectiveTLDService.h"
15 #include "nsIObserverService.h"
16 #include "nsIPrincipal.h"
17 #include "nsIScriptContext.h"
18 #include "nsIScriptError.h"
19 #include "nsIScriptSecurityManager.h"
20 #include "nsIStreamTransportService.h"
21 #include "nsISupportsPriority.h"
22 #include "nsITimer.h"
23 #include "nsIURI.h"
24 #include "nsIXULRuntime.h"
25 #include "nsPIDOMWindow.h"
26 
27 #include <algorithm>
28 #include "mozilla/ipc/BackgroundChild.h"
29 #include "GeckoProfiler.h"
30 #include "jsfriendapi.h"
31 #include "mozilla/AbstractThread.h"
32 #include "mozilla/ArrayUtils.h"
33 #include "mozilla/AsyncEventDispatcher.h"
34 #include "mozilla/Atomics.h"
35 #include "mozilla/CycleCollectedJSContext.h"
36 #include "mozilla/CycleCollectedJSRuntime.h"
37 #include "mozilla/Telemetry.h"
38 #include "mozilla/TimeStamp.h"
39 #include "mozilla/dom/asmjscache/AsmJSCache.h"
40 #include "mozilla/dom/AtomList.h"
41 #include "mozilla/dom/BindingUtils.h"
42 #include "mozilla/dom/ErrorEventBinding.h"
43 #include "mozilla/dom/EventTargetBinding.h"
44 #include "mozilla/dom/FetchUtil.h"
45 #include "mozilla/dom/MessageChannel.h"
46 #include "mozilla/dom/MessageEventBinding.h"
47 #include "mozilla/dom/PerformanceService.h"
48 #include "mozilla/dom/WorkerBinding.h"
49 #include "mozilla/dom/ScriptSettings.h"
50 #include "mozilla/dom/IndexedDatabaseManager.h"
51 #include "mozilla/ipc/BackgroundChild.h"
52 #include "mozilla/DebugOnly.h"
53 #include "mozilla/Preferences.h"
54 #include "mozilla/dom/Navigator.h"
55 #include "mozilla/Monitor.h"
56 #include "nsContentUtils.h"
57 #include "nsCycleCollector.h"
58 #include "nsDOMJSUtils.h"
59 #include "nsISupportsImpl.h"
60 #include "nsLayoutStatics.h"
61 #include "nsNetUtil.h"
62 #include "nsServiceManagerUtils.h"
63 #include "nsThreadUtils.h"
64 #include "nsXPCOM.h"
65 #include "nsXPCOMPrivate.h"
66 #include "OSFileConstants.h"
67 #include "xpcpublic.h"
68 
69 #include "Principal.h"
70 #include "SharedWorker.h"
71 #include "WorkerDebuggerManager.h"
72 #include "WorkerLoadInfo.h"
73 #include "WorkerPrivate.h"
74 #include "WorkerRunnable.h"
75 #include "WorkerScope.h"
76 #include "WorkerThread.h"
77 #include "prsystem.h"
78 
79 #define WORKERS_SHUTDOWN_TOPIC "web-workers-shutdown"
80 
81 namespace mozilla {
82 
83 using namespace ipc;
84 
85 namespace dom {
86 
87 using namespace workerinternals;
88 
89 namespace workerinternals {
90 
91 // The size of the worker runtime heaps in bytes. May be changed via pref.
92 #define WORKER_DEFAULT_RUNTIME_HEAPSIZE 32 * 1024 * 1024
93 
94 // The size of the generational GC nursery for workers, in bytes.
95 #define WORKER_DEFAULT_NURSERY_SIZE 1 * 1024 * 1024
96 
97 // The size of the worker JS allocation threshold in MB. May be changed via
98 // pref.
99 #define WORKER_DEFAULT_ALLOCATION_THRESHOLD 30
100 
101 // Half the size of the actual C stack, to be safe.
102 #define WORKER_CONTEXT_NATIVE_STACK_LIMIT 128 * sizeof(size_t) * 1024
103 
104 // The maximum number of hardware concurrency, overridable via pref.
105 #define MAX_HARDWARE_CONCURRENCY 8
106 
107 // The maximum number of threads to use for workers, overridable via pref.
108 #define MAX_WORKERS_PER_DOMAIN 512
109 
110 static_assert(MAX_WORKERS_PER_DOMAIN >= 1,
111               "We should allow at least one worker per domain.");
112 
113 // The default number of seconds that close handlers will be allowed to run for
114 // content workers.
115 #define MAX_SCRIPT_RUN_TIME_SEC 10
116 
117 // The number of seconds that idle threads can hang around before being killed.
118 #define IDLE_THREAD_TIMEOUT_SEC 30
119 
120 // The maximum number of threads that can be idle at one time.
121 #define MAX_IDLE_THREADS 20
122 
123 #define PREF_WORKERS_PREFIX "dom.workers."
124 #define PREF_WORKERS_MAX_PER_DOMAIN PREF_WORKERS_PREFIX "maxPerDomain"
125 #define PREF_WORKERS_MAX_HARDWARE_CONCURRENCY "dom.maxHardwareConcurrency"
126 
127 #define PREF_MAX_SCRIPT_RUN_TIME_CONTENT "dom.max_script_run_time"
128 #define PREF_MAX_SCRIPT_RUN_TIME_CHROME "dom.max_chrome_script_run_time"
129 
130 #define GC_REQUEST_OBSERVER_TOPIC "child-gc-request"
131 #define CC_REQUEST_OBSERVER_TOPIC "child-cc-request"
132 #define MEMORY_PRESSURE_OBSERVER_TOPIC "memory-pressure"
133 
134 #define BROADCAST_ALL_WORKERS(_func, ...)                         \
135   PR_BEGIN_MACRO                                                  \
136   AssertIsOnMainThread();                                         \
137                                                                   \
138   AutoTArray<WorkerPrivate*, 100> workers;                        \
139   {                                                               \
140     MutexAutoLock lock(mMutex);                                   \
141                                                                   \
142     AddAllTopLevelWorkersToArray(workers);                        \
143   }                                                               \
144                                                                   \
145   if (!workers.IsEmpty()) {                                       \
146     for (uint32_t index = 0; index < workers.Length(); index++) { \
147       workers[index]->_func(__VA_ARGS__);                         \
148     }                                                             \
149   }                                                               \
150   PR_END_MACRO
151 
152 // Prefixes for observing preference changes.
153 #define PREF_JS_OPTIONS_PREFIX "javascript.options."
154 #define PREF_WORKERS_OPTIONS_PREFIX PREF_WORKERS_PREFIX "options."
155 #define PREF_MEM_OPTIONS_PREFIX "mem."
156 #define PREF_GCZEAL "gcZeal"
157 
158 static NS_DEFINE_CID(kStreamTransportServiceCID, NS_STREAMTRANSPORTSERVICE_CID);
159 
160 namespace {
161 
162 const uint32_t kNoIndex = uint32_t(-1);
163 
164 uint32_t gMaxWorkersPerDomain = MAX_WORKERS_PER_DOMAIN;
165 uint32_t gMaxHardwareConcurrency = MAX_HARDWARE_CONCURRENCY;
166 
167 // Does not hold an owning reference.
168 RuntimeService* gRuntimeService = nullptr;
169 
170 // Only true during the call to Init.
171 bool gRuntimeServiceDuringInit = false;
172 
173 class LiteralRebindingCString : public nsDependentCString {
174  public:
175   template <int N>
RebindLiteral(const char (& aStr)[N])176   void RebindLiteral(const char (&aStr)[N]) {
177     Rebind(aStr, N - 1);
178   }
179 };
180 
181 template <typename T>
182 struct PrefTraits;
183 
184 template <>
185 struct PrefTraits<bool> {
186   typedef bool PrefValueType;
187 
188   static const PrefValueType kDefaultValue = false;
189 
Getmozilla::dom::workerinternals::__anonca25b2750111::PrefTraits190   static inline PrefValueType Get(const char* aPref) {
191     AssertIsOnMainThread();
192     return Preferences::GetBool(aPref);
193   }
194 
Existsmozilla::dom::workerinternals::__anonca25b2750111::PrefTraits195   static inline bool Exists(const char* aPref) {
196     AssertIsOnMainThread();
197     return Preferences::GetType(aPref) == nsIPrefBranch::PREF_BOOL;
198   }
199 };
200 
201 template <>
202 struct PrefTraits<int32_t> {
203   typedef int32_t PrefValueType;
204 
Getmozilla::dom::workerinternals::__anonca25b2750111::PrefTraits205   static inline PrefValueType Get(const char* aPref) {
206     AssertIsOnMainThread();
207     return Preferences::GetInt(aPref);
208   }
209 
Existsmozilla::dom::workerinternals::__anonca25b2750111::PrefTraits210   static inline bool Exists(const char* aPref) {
211     AssertIsOnMainThread();
212     return Preferences::GetType(aPref) == nsIPrefBranch::PREF_INT;
213   }
214 };
215 
216 template <typename T>
GetWorkerPref(const nsACString & aPref,const T aDefault=PrefTraits<T>::kDefaultValue)217 T GetWorkerPref(const nsACString& aPref,
218                 const T aDefault = PrefTraits<T>::kDefaultValue) {
219   AssertIsOnMainThread();
220 
221   typedef PrefTraits<T> PrefHelper;
222 
223   T result;
224 
225   nsAutoCString prefName;
226   prefName.AssignLiteral(PREF_WORKERS_OPTIONS_PREFIX);
227   prefName.Append(aPref);
228 
229   if (PrefHelper::Exists(prefName.get())) {
230     result = PrefHelper::Get(prefName.get());
231   } else {
232     prefName.AssignLiteral(PREF_JS_OPTIONS_PREFIX);
233     prefName.Append(aPref);
234 
235     if (PrefHelper::Exists(prefName.get())) {
236       result = PrefHelper::Get(prefName.get());
237     } else {
238       result = aDefault;
239     }
240   }
241 
242   return result;
243 }
244 
LoadContextOptions(const char * aPrefName,void *)245 void LoadContextOptions(const char* aPrefName, void* /* aClosure */) {
246   AssertIsOnMainThread();
247 
248   RuntimeService* rts = RuntimeService::GetService();
249   if (!rts) {
250     // May be shutting down, just bail.
251     return;
252   }
253 
254   const nsDependentCString prefName(aPrefName);
255 
256   // Several other pref branches will get included here so bail out if there is
257   // another callback that will handle this change.
258   if (StringBeginsWith(
259           prefName,
260           NS_LITERAL_CSTRING(PREF_JS_OPTIONS_PREFIX PREF_MEM_OPTIONS_PREFIX)) ||
261       StringBeginsWith(
262           prefName, NS_LITERAL_CSTRING(
263                         PREF_WORKERS_OPTIONS_PREFIX PREF_MEM_OPTIONS_PREFIX))) {
264     return;
265   }
266 
267 #ifdef JS_GC_ZEAL
268   if (prefName.EqualsLiteral(PREF_JS_OPTIONS_PREFIX PREF_GCZEAL) ||
269       prefName.EqualsLiteral(PREF_WORKERS_OPTIONS_PREFIX PREF_GCZEAL)) {
270     return;
271   }
272 #endif
273 
274   // Context options.
275   JS::ContextOptions contextOptions;
276   contextOptions.setAsmJS(GetWorkerPref<bool>(NS_LITERAL_CSTRING("asmjs")))
277       .setWasm(GetWorkerPref<bool>(NS_LITERAL_CSTRING("wasm")))
278       .setWasmBaseline(
279           GetWorkerPref<bool>(NS_LITERAL_CSTRING("wasm_baselinejit")))
280       .setWasmIon(GetWorkerPref<bool>(NS_LITERAL_CSTRING("wasm_ionjit")))
281       .setThrowOnAsmJSValidationFailure(GetWorkerPref<bool>(
282           NS_LITERAL_CSTRING("throw_on_asmjs_validation_failure")))
283       .setBaseline(GetWorkerPref<bool>(NS_LITERAL_CSTRING("baselinejit")))
284       .setIon(GetWorkerPref<bool>(NS_LITERAL_CSTRING("ion")))
285       .setNativeRegExp(GetWorkerPref<bool>(NS_LITERAL_CSTRING("native_regexp")))
286       .setAsyncStack(GetWorkerPref<bool>(NS_LITERAL_CSTRING("asyncstack")))
287       .setWerror(GetWorkerPref<bool>(NS_LITERAL_CSTRING("werror")))
288 #ifdef FUZZING
289       .setFuzzing(GetWorkerPref<bool>(NS_LITERAL_CSTRING("fuzzing.enabled")))
290 #endif
291       .setStreams(GetWorkerPref<bool>(NS_LITERAL_CSTRING("streams")))
292       .setExtraWarnings(GetWorkerPref<bool>(NS_LITERAL_CSTRING("strict")))
293       .setArrayProtoValues(
294           GetWorkerPref<bool>(NS_LITERAL_CSTRING("array_prototype_values")));
295 
296   nsCOMPtr<nsIXULRuntime> xr = do_GetService("@mozilla.org/xre/runtime;1");
297   if (xr) {
298     bool safeMode = false;
299     xr->GetInSafeMode(&safeMode);
300     if (safeMode) {
301       contextOptions.disableOptionsForSafeMode();
302     }
303   }
304 
305   RuntimeService::SetDefaultContextOptions(contextOptions);
306 
307   if (rts) {
308     rts->UpdateAllWorkerContextOptions();
309   }
310 }
311 
312 #ifdef JS_GC_ZEAL
LoadGCZealOptions(const char *,void *)313 void LoadGCZealOptions(const char* /* aPrefName */, void* /* aClosure */) {
314   AssertIsOnMainThread();
315 
316   RuntimeService* rts = RuntimeService::GetService();
317   if (!rts) {
318     // May be shutting down, just bail.
319     return;
320   }
321 
322   int32_t gczeal = GetWorkerPref<int32_t>(NS_LITERAL_CSTRING(PREF_GCZEAL), -1);
323   if (gczeal < 0) {
324     gczeal = 0;
325   }
326 
327   int32_t frequency =
328       GetWorkerPref<int32_t>(NS_LITERAL_CSTRING("gcZeal.frequency"), -1);
329   if (frequency < 0) {
330     frequency = JS_DEFAULT_ZEAL_FREQ;
331   }
332 
333   RuntimeService::SetDefaultGCZeal(uint8_t(gczeal), uint32_t(frequency));
334 
335   if (rts) {
336     rts->UpdateAllWorkerGCZeal();
337   }
338 }
339 #endif
340 
UpdateCommonJSGCMemoryOption(RuntimeService * aRuntimeService,const nsACString & aPrefName,JSGCParamKey aKey)341 void UpdateCommonJSGCMemoryOption(RuntimeService* aRuntimeService,
342                                   const nsACString& aPrefName,
343                                   JSGCParamKey aKey) {
344   AssertIsOnMainThread();
345   NS_ASSERTION(!aPrefName.IsEmpty(), "Empty pref name!");
346 
347   int32_t prefValue = GetWorkerPref(aPrefName, -1);
348   uint32_t value =
349       (prefValue < 0 || prefValue >= 10000) ? 0 : uint32_t(prefValue);
350 
351   RuntimeService::SetDefaultJSGCSettings(aKey, value);
352 
353   if (aRuntimeService) {
354     aRuntimeService->UpdateAllWorkerMemoryParameter(aKey, value);
355   }
356 }
357 
UpdateOtherJSGCMemoryOption(RuntimeService * aRuntimeService,JSGCParamKey aKey,uint32_t aValue)358 void UpdateOtherJSGCMemoryOption(RuntimeService* aRuntimeService,
359                                  JSGCParamKey aKey, uint32_t aValue) {
360   AssertIsOnMainThread();
361 
362   RuntimeService::SetDefaultJSGCSettings(aKey, aValue);
363 
364   if (aRuntimeService) {
365     aRuntimeService->UpdateAllWorkerMemoryParameter(aKey, aValue);
366   }
367 }
368 
LoadJSGCMemoryOptions(const char * aPrefName,void *)369 void LoadJSGCMemoryOptions(const char* aPrefName, void* /* aClosure */) {
370   AssertIsOnMainThread();
371 
372   RuntimeService* rts = RuntimeService::GetService();
373 
374   if (!rts) {
375     // May be shutting down, just bail.
376     return;
377   }
378 
379   NS_NAMED_LITERAL_CSTRING(jsPrefix, PREF_JS_OPTIONS_PREFIX);
380   NS_NAMED_LITERAL_CSTRING(workersPrefix, PREF_WORKERS_OPTIONS_PREFIX);
381 
382   const nsDependentCString fullPrefName(aPrefName);
383 
384   // Pull out the string that actually distinguishes the parameter we need to
385   // change.
386   nsDependentCSubstring memPrefName;
387   if (StringBeginsWith(fullPrefName, jsPrefix)) {
388     memPrefName.Rebind(fullPrefName, jsPrefix.Length());
389   } else if (StringBeginsWith(fullPrefName, workersPrefix)) {
390     memPrefName.Rebind(fullPrefName, workersPrefix.Length());
391   } else {
392     NS_ERROR("Unknown pref name!");
393     return;
394   }
395 
396 #ifdef DEBUG
397   // During Init() we get called back with a branch string here, so there should
398   // be no just a "mem." pref here.
399   if (!rts) {
400     NS_ASSERTION(memPrefName.EqualsLiteral(PREF_MEM_OPTIONS_PREFIX), "Huh?!");
401   }
402 #endif
403 
404   // If we're running in Init() then do this for every pref we care about.
405   // Otherwise we just want to update the parameter that changed.
406   for (uint32_t index = !gRuntimeServiceDuringInit
407                             ? JSSettings::kGCSettingsArraySize - 1
408                             : 0;
409        index < JSSettings::kGCSettingsArraySize; index++) {
410     LiteralRebindingCString matchName;
411 
412     matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX "max");
413     if (memPrefName == matchName || (gRuntimeServiceDuringInit && index == 0)) {
414       int32_t prefValue = GetWorkerPref(matchName, -1);
415       uint32_t value = (prefValue <= 0 || prefValue >= 0x1000)
416                            ? uint32_t(-1)
417                            : uint32_t(prefValue) * 1024 * 1024;
418       UpdateOtherJSGCMemoryOption(rts, JSGC_MAX_BYTES, value);
419       continue;
420     }
421 
422     matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX "high_water_mark");
423     if (memPrefName == matchName || (gRuntimeServiceDuringInit && index == 1)) {
424       int32_t prefValue = GetWorkerPref(matchName, 128);
425       UpdateOtherJSGCMemoryOption(rts, JSGC_MAX_MALLOC_BYTES,
426                                   uint32_t(prefValue) * 1024 * 1024);
427       continue;
428     }
429 
430     matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX
431                             "gc_high_frequency_time_limit_ms");
432     if (memPrefName == matchName || (gRuntimeServiceDuringInit && index == 2)) {
433       UpdateCommonJSGCMemoryOption(rts, matchName,
434                                    JSGC_HIGH_FREQUENCY_TIME_LIMIT);
435       continue;
436     }
437 
438     matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX
439                             "gc_low_frequency_heap_growth");
440     if (memPrefName == matchName || (gRuntimeServiceDuringInit && index == 3)) {
441       UpdateCommonJSGCMemoryOption(rts, matchName,
442                                    JSGC_LOW_FREQUENCY_HEAP_GROWTH);
443       continue;
444     }
445 
446     matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX
447                             "gc_high_frequency_heap_growth_min");
448     if (memPrefName == matchName || (gRuntimeServiceDuringInit && index == 4)) {
449       UpdateCommonJSGCMemoryOption(rts, matchName,
450                                    JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MIN);
451       continue;
452     }
453 
454     matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX
455                             "gc_high_frequency_heap_growth_max");
456     if (memPrefName == matchName || (gRuntimeServiceDuringInit && index == 5)) {
457       UpdateCommonJSGCMemoryOption(rts, matchName,
458                                    JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MAX);
459       continue;
460     }
461 
462     matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX
463                             "gc_high_frequency_low_limit_mb");
464     if (memPrefName == matchName || (gRuntimeServiceDuringInit && index == 6)) {
465       UpdateCommonJSGCMemoryOption(rts, matchName,
466                                    JSGC_HIGH_FREQUENCY_LOW_LIMIT);
467       continue;
468     }
469 
470     matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX
471                             "gc_high_frequency_high_limit_mb");
472     if (memPrefName == matchName || (gRuntimeServiceDuringInit && index == 7)) {
473       UpdateCommonJSGCMemoryOption(rts, matchName,
474                                    JSGC_HIGH_FREQUENCY_HIGH_LIMIT);
475       continue;
476     }
477 
478     matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX
479                             "gc_allocation_threshold_mb");
480     if (memPrefName == matchName || (gRuntimeServiceDuringInit && index == 8)) {
481       UpdateCommonJSGCMemoryOption(rts, matchName, JSGC_ALLOCATION_THRESHOLD);
482       continue;
483     }
484 
485     matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX "gc_incremental_slice_ms");
486     if (memPrefName == matchName || (gRuntimeServiceDuringInit && index == 9)) {
487       int32_t prefValue = GetWorkerPref(matchName, -1);
488       uint32_t value =
489           (prefValue <= 0 || prefValue >= 100000) ? 0 : uint32_t(prefValue);
490       UpdateOtherJSGCMemoryOption(rts, JSGC_SLICE_TIME_BUDGET, value);
491       continue;
492     }
493 
494     matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX "gc_dynamic_heap_growth");
495     if (memPrefName == matchName ||
496         (gRuntimeServiceDuringInit && index == 10)) {
497       bool prefValue = GetWorkerPref(matchName, false);
498       UpdateOtherJSGCMemoryOption(rts, JSGC_DYNAMIC_HEAP_GROWTH,
499                                   prefValue ? 0 : 1);
500       continue;
501     }
502 
503     matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX "gc_dynamic_mark_slice");
504     if (memPrefName == matchName ||
505         (gRuntimeServiceDuringInit && index == 11)) {
506       bool prefValue = GetWorkerPref(matchName, false);
507       UpdateOtherJSGCMemoryOption(rts, JSGC_DYNAMIC_MARK_SLICE,
508                                   prefValue ? 0 : 1);
509       continue;
510     }
511 
512     matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX "gc_min_empty_chunk_count");
513     if (memPrefName == matchName ||
514         (gRuntimeServiceDuringInit && index == 12)) {
515       UpdateCommonJSGCMemoryOption(rts, matchName, JSGC_MIN_EMPTY_CHUNK_COUNT);
516       continue;
517     }
518 
519     matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX "gc_max_empty_chunk_count");
520     if (memPrefName == matchName ||
521         (gRuntimeServiceDuringInit && index == 13)) {
522       UpdateCommonJSGCMemoryOption(rts, matchName, JSGC_MAX_EMPTY_CHUNK_COUNT);
523       continue;
524     }
525 
526     matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX "gc_compacting");
527     if (memPrefName == matchName ||
528         (gRuntimeServiceDuringInit && index == 14)) {
529       bool prefValue = GetWorkerPref(matchName, false);
530       UpdateOtherJSGCMemoryOption(rts, JSGC_COMPACTING_ENABLED,
531                                   prefValue ? 0 : 1);
532       continue;
533     }
534 
535 #ifdef DEBUG
536     nsAutoCString message("Workers don't support the 'mem.");
537     message.Append(memPrefName);
538     message.AppendLiteral("' preference!");
539     NS_WARNING(message.get());
540 #endif
541   }
542 }
543 
InterruptCallback(JSContext * aCx)544 bool InterruptCallback(JSContext* aCx) {
545   WorkerPrivate* worker = GetWorkerPrivateFromContext(aCx);
546   MOZ_ASSERT(worker);
547 
548   // Now is a good time to turn on profiling if it's pending.
549   PROFILER_JS_INTERRUPT_CALLBACK();
550 
551   return worker->InterruptCallback(aCx);
552 }
553 
554 class LogViolationDetailsRunnable final : public WorkerMainThreadRunnable {
555   nsString mFileName;
556   uint32_t mLineNum;
557 
558  public:
LogViolationDetailsRunnable(WorkerPrivate * aWorker,const nsString & aFileName,uint32_t aLineNum)559   LogViolationDetailsRunnable(WorkerPrivate* aWorker, const nsString& aFileName,
560                               uint32_t aLineNum)
561       : WorkerMainThreadRunnable(
562             aWorker,
563             NS_LITERAL_CSTRING("RuntimeService :: LogViolationDetails")),
564         mFileName(aFileName),
565         mLineNum(aLineNum) {
566     MOZ_ASSERT(aWorker);
567   }
568 
569   virtual bool MainThreadRun() override;
570 
571  private:
~LogViolationDetailsRunnable()572   ~LogViolationDetailsRunnable() {}
573 };
574 
ContentSecurityPolicyAllows(JSContext * aCx)575 bool ContentSecurityPolicyAllows(JSContext* aCx) {
576   WorkerPrivate* worker = GetWorkerPrivateFromContext(aCx);
577   worker->AssertIsOnWorkerThread();
578 
579   if (worker->GetReportCSPViolations()) {
580     nsString fileName;
581     uint32_t lineNum = 0;
582 
583     JS::AutoFilename file;
584     if (JS::DescribeScriptedCaller(aCx, &file, &lineNum) && file.get()) {
585       fileName = NS_ConvertUTF8toUTF16(file.get());
586     } else {
587       MOZ_ASSERT(!JS_IsExceptionPending(aCx));
588     }
589 
590     RefPtr<LogViolationDetailsRunnable> runnable =
591         new LogViolationDetailsRunnable(worker, fileName, lineNum);
592 
593     ErrorResult rv;
594     runnable->Dispatch(Killing, rv);
595     if (NS_WARN_IF(rv.Failed())) {
596       rv.SuppressException();
597     }
598   }
599 
600   return worker->IsEvalAllowed();
601 }
602 
CTypesActivityCallback(JSContext * aCx,js::CTypesActivityType aType)603 void CTypesActivityCallback(JSContext* aCx, js::CTypesActivityType aType) {
604   WorkerPrivate* worker = GetWorkerPrivateFromContext(aCx);
605   worker->AssertIsOnWorkerThread();
606 
607   switch (aType) {
608     case js::CTYPES_CALL_BEGIN:
609       worker->BeginCTypesCall();
610       break;
611 
612     case js::CTYPES_CALL_END:
613       worker->EndCTypesCall();
614       break;
615 
616     case js::CTYPES_CALLBACK_BEGIN:
617       worker->BeginCTypesCallback();
618       break;
619 
620     case js::CTYPES_CALLBACK_END:
621       worker->EndCTypesCallback();
622       break;
623 
624     default:
625       MOZ_CRASH("Unknown type flag!");
626   }
627 }
628 
GetPrincipalForAsmJSCacheOp()629 static nsIPrincipal* GetPrincipalForAsmJSCacheOp() {
630   WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
631   if (!workerPrivate) {
632     return nullptr;
633   }
634 
635   // asmjscache::OpenEntryForX guarnatee to only access the given nsIPrincipal
636   // from the main thread.
637   return workerPrivate->GetPrincipalDontAssertMainThread();
638 }
639 
AsmJSCacheOpenEntryForRead(JS::Handle<JSObject * > aGlobal,const char16_t * aBegin,const char16_t * aLimit,size_t * aSize,const uint8_t ** aMemory,intptr_t * aHandle)640 static bool AsmJSCacheOpenEntryForRead(JS::Handle<JSObject*> aGlobal,
641                                        const char16_t* aBegin,
642                                        const char16_t* aLimit, size_t* aSize,
643                                        const uint8_t** aMemory,
644                                        intptr_t* aHandle) {
645   nsIPrincipal* principal = GetPrincipalForAsmJSCacheOp();
646   if (!principal) {
647     return false;
648   }
649 
650   return asmjscache::OpenEntryForRead(principal, aBegin, aLimit, aSize, aMemory,
651                                       aHandle);
652 }
653 
AsmJSCacheOpenEntryForWrite(JS::Handle<JSObject * > aGlobal,const char16_t * aBegin,const char16_t * aEnd,size_t aSize,uint8_t ** aMemory,intptr_t * aHandle)654 static JS::AsmJSCacheResult AsmJSCacheOpenEntryForWrite(
655     JS::Handle<JSObject*> aGlobal, const char16_t* aBegin, const char16_t* aEnd,
656     size_t aSize, uint8_t** aMemory, intptr_t* aHandle) {
657   nsIPrincipal* principal = GetPrincipalForAsmJSCacheOp();
658   if (!principal) {
659     return JS::AsmJSCache_InternalError;
660   }
661 
662   return asmjscache::OpenEntryForWrite(principal, aBegin, aEnd, aSize, aMemory,
663                                        aHandle);
664 }
665 
666 // JSDispatchableRunnables are WorkerRunnables used to dispatch JS::Dispatchable
667 // back to their worker thread. A WorkerRunnable is used for two reasons:
668 //
669 // 1. The JS::Dispatchable::run() callback may run JS so we cannot use a control
670 // runnable since they use async interrupts and break JS run-to-completion.
671 //
672 // 2. The DispatchToEventLoopCallback interface is *required* to fail during
673 // shutdown (see jsapi.h) which is exactly what WorkerRunnable::Dispatch() will
674 // do. Moreover, JS_DestroyContext() does *not* block on JS::Dispatchable::run
675 // being called, DispatchToEventLoopCallback failure is expected to happen
676 // during shutdown.
677 class JSDispatchableRunnable final : public WorkerRunnable {
678   JS::Dispatchable* mDispatchable;
679 
~JSDispatchableRunnable()680   ~JSDispatchableRunnable() { MOZ_ASSERT(!mDispatchable); }
681 
682   // Disable the usual pre/post-dispatch thread assertions since we are
683   // dispatching from some random JS engine internal thread:
684 
PreDispatch(WorkerPrivate * aWorkerPrivate)685   bool PreDispatch(WorkerPrivate* aWorkerPrivate) override { return true; }
686 
PostDispatch(WorkerPrivate * aWorkerPrivate,bool aDispatchResult)687   void PostDispatch(WorkerPrivate* aWorkerPrivate,
688                     bool aDispatchResult) override {
689     // For the benefit of the destructor assert.
690     if (!aDispatchResult) {
691       mDispatchable = nullptr;
692     }
693   }
694 
695  public:
JSDispatchableRunnable(WorkerPrivate * aWorkerPrivate,JS::Dispatchable * aDispatchable)696   JSDispatchableRunnable(WorkerPrivate* aWorkerPrivate,
697                          JS::Dispatchable* aDispatchable)
698       : WorkerRunnable(aWorkerPrivate,
699                        WorkerRunnable::WorkerThreadUnchangedBusyCount),
700         mDispatchable(aDispatchable) {
701     MOZ_ASSERT(mDispatchable);
702   }
703 
WorkerRun(JSContext * aCx,WorkerPrivate * aWorkerPrivate)704   bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
705     MOZ_ASSERT(aWorkerPrivate == mWorkerPrivate);
706     MOZ_ASSERT(aCx == mWorkerPrivate->GetJSContext());
707     MOZ_ASSERT(mDispatchable);
708 
709     AutoJSAPI jsapi;
710     jsapi.Init();
711 
712     mDispatchable->run(mWorkerPrivate->GetJSContext(),
713                        JS::Dispatchable::NotShuttingDown);
714     mDispatchable = nullptr;  // mDispatchable may delete itself
715 
716     return true;
717   }
718 
Cancel()719   nsresult Cancel() override {
720     MOZ_ASSERT(mDispatchable);
721 
722     AutoJSAPI jsapi;
723     jsapi.Init();
724 
725     mDispatchable->run(mWorkerPrivate->GetJSContext(),
726                        JS::Dispatchable::ShuttingDown);
727     mDispatchable = nullptr;  // mDispatchable may delete itself
728 
729     return WorkerRunnable::Cancel();
730   }
731 };
732 
DispatchToEventLoop(void * aClosure,JS::Dispatchable * aDispatchable)733 static bool DispatchToEventLoop(void* aClosure,
734                                 JS::Dispatchable* aDispatchable) {
735   // This callback may execute either on the worker thread or a random
736   // JS-internal helper thread.
737 
738   // See comment at JS::InitDispatchToEventLoop() below for how we know the
739   // WorkerPrivate is alive.
740   WorkerPrivate* workerPrivate = reinterpret_cast<WorkerPrivate*>(aClosure);
741 
742   // Dispatch is expected to fail during shutdown for the reasons outlined in
743   // the JSDispatchableRunnable comment above.
744   RefPtr<JSDispatchableRunnable> r =
745       new JSDispatchableRunnable(workerPrivate, aDispatchable);
746   return r->Dispatch();
747 }
748 
ConsumeStream(JSContext * aCx,JS::HandleObject aObj,JS::MimeType aMimeType,JS::StreamConsumer * aConsumer)749 static bool ConsumeStream(JSContext* aCx, JS::HandleObject aObj,
750                           JS::MimeType aMimeType,
751                           JS::StreamConsumer* aConsumer) {
752   WorkerPrivate* worker = GetWorkerPrivateFromContext(aCx);
753   if (!worker) {
754     JS_ReportErrorNumberASCII(aCx, js::GetErrorMessage, nullptr,
755                               JSMSG_ERROR_CONSUMING_RESPONSE);
756     return false;
757   }
758 
759   return FetchUtil::StreamResponseToJS(aCx, aObj, aMimeType, aConsumer, worker);
760 }
761 
762 class WorkerJSContext;
763 
764 class WorkerThreadContextPrivate : private PerThreadAtomCache {
765   friend class WorkerJSContext;
766 
767   WorkerPrivate* mWorkerPrivate;
768 
769  public:
770   // This can't return null, but we can't lose the "Get" prefix in the name or
771   // it will be ambiguous with the WorkerPrivate class name.
GetWorkerPrivate() const772   WorkerPrivate* GetWorkerPrivate() const {
773     MOZ_ASSERT(!NS_IsMainThread());
774     MOZ_ASSERT(mWorkerPrivate);
775 
776     return mWorkerPrivate;
777   }
778 
779  private:
WorkerThreadContextPrivate(WorkerPrivate * aWorkerPrivate)780   explicit WorkerThreadContextPrivate(WorkerPrivate* aWorkerPrivate)
781       : mWorkerPrivate(aWorkerPrivate) {
782     MOZ_ASSERT(!NS_IsMainThread());
783 
784     // Zero out the base class members.
785     memset(this, 0, sizeof(PerThreadAtomCache));
786 
787     MOZ_ASSERT(mWorkerPrivate);
788   }
789 
~WorkerThreadContextPrivate()790   ~WorkerThreadContextPrivate() { MOZ_ASSERT(!NS_IsMainThread()); }
791 
792   WorkerThreadContextPrivate(const WorkerThreadContextPrivate&) = delete;
793 
794   WorkerThreadContextPrivate& operator=(const WorkerThreadContextPrivate&) =
795       delete;
796 };
797 
InitJSContextForWorker(WorkerPrivate * aWorkerPrivate,JSContext * aWorkerCx)798 bool InitJSContextForWorker(WorkerPrivate* aWorkerPrivate,
799                             JSContext* aWorkerCx) {
800   aWorkerPrivate->AssertIsOnWorkerThread();
801   NS_ASSERTION(!aWorkerPrivate->GetJSContext(), "Already has a context!");
802 
803   JSSettings settings;
804   aWorkerPrivate->CopyJSSettings(settings);
805 
806   JS::ContextOptionsRef(aWorkerCx) = settings.contextOptions;
807 
808   JSSettings::JSGCSettingsArray& gcSettings = settings.gcSettings;
809 
810   // This is the real place where we set the max memory for the runtime.
811   for (uint32_t index = 0; index < ArrayLength(gcSettings); index++) {
812     const JSSettings::JSGCSetting& setting = gcSettings[index];
813     if (setting.key.isSome()) {
814       NS_ASSERTION(setting.value, "Can't handle 0 values!");
815       JS_SetGCParameter(aWorkerCx, *setting.key, setting.value);
816     }
817   }
818 
819   JS_SetNativeStackQuota(aWorkerCx, WORKER_CONTEXT_NATIVE_STACK_LIMIT);
820 
821   // Security policy:
822   static const JSSecurityCallbacks securityCallbacks = {
823       ContentSecurityPolicyAllows};
824   JS_SetSecurityCallbacks(aWorkerCx, &securityCallbacks);
825 
826   // Set up the asm.js cache callbacks
827   static const JS::AsmJSCacheOps asmJSCacheOps = {
828       AsmJSCacheOpenEntryForRead, asmjscache::CloseEntryForRead,
829       AsmJSCacheOpenEntryForWrite, asmjscache::CloseEntryForWrite};
830   JS::SetAsmJSCacheOps(aWorkerCx, &asmJSCacheOps);
831 
832   // A WorkerPrivate lives strictly longer than its JSRuntime so we can safely
833   // store a raw pointer as the callback's closure argument on the JSRuntime.
834   JS::InitDispatchToEventLoop(aWorkerCx, DispatchToEventLoop,
835                               (void*)aWorkerPrivate);
836 
837   JS::InitConsumeStreamCallback(aWorkerCx, ConsumeStream);
838 
839   if (!JS::InitSelfHostedCode(aWorkerCx)) {
840     NS_WARNING("Could not init self-hosted code!");
841     return false;
842   }
843 
844   JS_AddInterruptCallback(aWorkerCx, InterruptCallback);
845 
846   js::SetCTypesActivityCallback(aWorkerCx, CTypesActivityCallback);
847 
848 #ifdef JS_GC_ZEAL
849   JS_SetGCZeal(aWorkerCx, settings.gcZeal, settings.gcZealFrequency);
850 #endif
851 
852   return true;
853 }
854 
PreserveWrapper(JSContext * cx,JSObject * obj)855 static bool PreserveWrapper(JSContext* cx, JSObject* obj) {
856   MOZ_ASSERT(cx);
857   MOZ_ASSERT(obj);
858   MOZ_ASSERT(mozilla::dom::IsDOMObject(obj));
859 
860   return mozilla::dom::TryPreserveWrapper(obj);
861 }
862 
Wrap(JSContext * cx,JS::HandleObject existing,JS::HandleObject obj)863 JSObject* Wrap(JSContext* cx, JS::HandleObject existing, JS::HandleObject obj) {
864   JSObject* targetGlobal = JS::CurrentGlobalOrNull(cx);
865   if (!IsWorkerDebuggerGlobal(targetGlobal) &&
866       !IsWorkerDebuggerSandbox(targetGlobal)) {
867     MOZ_CRASH("There should be no edges from the debuggee to the debugger.");
868   }
869 
870   JSObject* originGlobal = js::GetGlobalForObjectCrossCompartment(obj);
871 
872   const js::Wrapper* wrapper = nullptr;
873   if (IsWorkerDebuggerGlobal(originGlobal) ||
874       IsWorkerDebuggerSandbox(originGlobal)) {
875     wrapper = &js::CrossCompartmentWrapper::singleton;
876   } else {
877     wrapper = &js::OpaqueCrossCompartmentWrapper::singleton;
878   }
879 
880   if (existing) {
881     js::Wrapper::Renew(existing, obj, wrapper);
882   }
883   return js::Wrapper::New(cx, obj, wrapper);
884 }
885 
886 static const JSWrapObjectCallbacks WrapObjectCallbacks = {
887     Wrap,
888     nullptr,
889 };
890 
891 class WorkerJSRuntime final : public mozilla::CycleCollectedJSRuntime {
892  public:
893   // The heap size passed here doesn't matter, we will change it later in the
894   // call to JS_SetGCParameter inside InitJSContextForWorker.
WorkerJSRuntime(JSContext * aCx,WorkerPrivate * aWorkerPrivate)895   explicit WorkerJSRuntime(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
896       : CycleCollectedJSRuntime(aCx), mWorkerPrivate(aWorkerPrivate) {
897     MOZ_COUNT_CTOR_INHERITED(WorkerJSRuntime, CycleCollectedJSRuntime);
898     MOZ_ASSERT(aWorkerPrivate);
899 
900     {
901       JS::UniqueChars defaultLocale = aWorkerPrivate->AdoptDefaultLocale();
902       MOZ_ASSERT(defaultLocale,
903                  "failure of a WorkerPrivate to have a default locale should "
904                  "have made the worker fail to spawn");
905 
906       if (!JS_SetDefaultLocale(Runtime(), defaultLocale.get())) {
907         NS_WARNING("failed to set workerCx's default locale");
908       }
909     }
910   }
911 
Shutdown(JSContext * cx)912   void Shutdown(JSContext* cx) override {
913     // The CC is shut down, and the superclass destructor will GC, so make sure
914     // we don't try to CC again.
915     mWorkerPrivate = nullptr;
916 
917     CycleCollectedJSRuntime::Shutdown(cx);
918   }
919 
~WorkerJSRuntime()920   ~WorkerJSRuntime() {
921     MOZ_COUNT_DTOR_INHERITED(WorkerJSRuntime, CycleCollectedJSRuntime);
922   }
923 
PrepareForForgetSkippable()924   virtual void PrepareForForgetSkippable() override {}
925 
BeginCycleCollectionCallback()926   virtual void BeginCycleCollectionCallback() override {}
927 
EndCycleCollectionCallback(CycleCollectorResults & aResults)928   virtual void EndCycleCollectionCallback(
929       CycleCollectorResults& aResults) override {}
930 
DispatchDeferredDeletion(bool aContinuation,bool aPurge)931   void DispatchDeferredDeletion(bool aContinuation, bool aPurge) override {
932     MOZ_ASSERT(!aContinuation);
933 
934     // Do it immediately, no need for asynchronous behavior here.
935     nsCycleCollector_doDeferredDeletion();
936   }
937 
CustomGCCallback(JSGCStatus aStatus)938   virtual void CustomGCCallback(JSGCStatus aStatus) override {
939     if (!mWorkerPrivate) {
940       // We're shutting down, no need to do anything.
941       return;
942     }
943 
944     mWorkerPrivate->AssertIsOnWorkerThread();
945 
946     if (aStatus == JSGC_END) {
947       nsCycleCollector_collect(nullptr);
948     }
949   }
950 
951  private:
952   WorkerPrivate* mWorkerPrivate;
953 };
954 
955 class MOZ_STACK_CLASS WorkerJSContext final
956     : public mozilla::CycleCollectedJSContext {
957  public:
958   // The heap size passed here doesn't matter, we will change it later in the
959   // call to JS_SetGCParameter inside InitJSContextForWorker.
WorkerJSContext(WorkerPrivate * aWorkerPrivate)960   explicit WorkerJSContext(WorkerPrivate* aWorkerPrivate)
961       : mWorkerPrivate(aWorkerPrivate) {
962     MOZ_COUNT_CTOR_INHERITED(WorkerJSContext, CycleCollectedJSContext);
963     MOZ_ASSERT(aWorkerPrivate);
964     // Magical number 2. Workers have the base recursion depth 1, and normal
965     // runnables run at level 2, and we don't want to process microtasks
966     // at any other level.
967     SetTargetedMicroTaskRecursionDepth(2);
968   }
969 
~WorkerJSContext()970   ~WorkerJSContext() {
971     MOZ_COUNT_DTOR_INHERITED(WorkerJSContext, CycleCollectedJSContext);
972     JSContext* cx = MaybeContext();
973     if (!cx) {
974       return;  // Initialize() must have failed
975     }
976 
977     delete static_cast<WorkerThreadContextPrivate*>(JS_GetContextPrivate(cx));
978     JS_SetContextPrivate(cx, nullptr);
979 
980     // The worker global should be unrooted and the shutdown cycle collection
981     // should break all remaining cycles. The superclass destructor will run
982     // the GC one final time and finalize any JSObjects that were participating
983     // in cycles that were broken during CC shutdown.
984     nsCycleCollector_shutdown();
985 
986     // The CC is shut down, and the superclass destructor will GC, so make sure
987     // we don't try to CC again.
988     mWorkerPrivate = nullptr;
989   }
990 
CreateRuntime(JSContext * aCx)991   CycleCollectedJSRuntime* CreateRuntime(JSContext* aCx) override {
992     return new WorkerJSRuntime(aCx, mWorkerPrivate);
993   }
994 
Initialize(JSRuntime * aParentRuntime)995   nsresult Initialize(JSRuntime* aParentRuntime) {
996     nsresult rv = CycleCollectedJSContext::Initialize(
997         aParentRuntime, WORKER_DEFAULT_RUNTIME_HEAPSIZE,
998         WORKER_DEFAULT_NURSERY_SIZE);
999     if (NS_WARN_IF(NS_FAILED(rv))) {
1000       return rv;
1001     }
1002 
1003     JSContext* cx = Context();
1004 
1005     JS_SetContextPrivate(cx, new WorkerThreadContextPrivate(mWorkerPrivate));
1006 
1007     js::SetPreserveWrapperCallback(cx, PreserveWrapper);
1008     JS_InitDestroyPrincipalsCallback(cx, DestroyWorkerPrincipals);
1009     JS_SetWrapObjectCallbacks(cx, &WrapObjectCallbacks);
1010     if (mWorkerPrivate->IsDedicatedWorker()) {
1011       JS_SetFutexCanWait(cx);
1012     }
1013 
1014     return NS_OK;
1015   }
1016 
DispatchToMicroTask(already_AddRefed<MicroTaskRunnable> aRunnable)1017   virtual void DispatchToMicroTask(
1018       already_AddRefed<MicroTaskRunnable> aRunnable) override {
1019     RefPtr<MicroTaskRunnable> runnable(aRunnable);
1020 
1021     MOZ_ASSERT(!NS_IsMainThread());
1022     MOZ_ASSERT(runnable);
1023 
1024     std::queue<RefPtr<MicroTaskRunnable>>* microTaskQueue = nullptr;
1025 
1026     JSContext* cx = GetCurrentWorkerThreadJSContext();
1027     NS_ASSERTION(cx, "This should never be null!");
1028 
1029     JS::Rooted<JSObject*> global(cx, JS::CurrentGlobalOrNull(cx));
1030     NS_ASSERTION(global, "This should never be null!");
1031 
1032     // On worker threads, if the current global is the worker global, we use the
1033     // main micro task queue. Otherwise, the current global must be
1034     // either the debugger global or a debugger sandbox, and we use the debugger
1035     // micro task queue instead.
1036     if (IsWorkerGlobal(global)) {
1037       microTaskQueue = &GetMicroTaskQueue();
1038     } else {
1039       MOZ_ASSERT(IsWorkerDebuggerGlobal(global) ||
1040                  IsWorkerDebuggerSandbox(global));
1041 
1042       microTaskQueue = &GetDebuggerMicroTaskQueue();
1043     }
1044 
1045     microTaskQueue->push(runnable.forget());
1046   }
1047 
1048  private:
1049   WorkerPrivate* mWorkerPrivate;
1050 };
1051 
1052 class WorkerThreadPrimaryRunnable final : public Runnable {
1053   WorkerPrivate* mWorkerPrivate;
1054   RefPtr<WorkerThread> mThread;
1055   JSRuntime* mParentRuntime;
1056 
1057   class FinishedRunnable final : public Runnable {
1058     RefPtr<WorkerThread> mThread;
1059 
1060    public:
FinishedRunnable(already_AddRefed<WorkerThread> aThread)1061     explicit FinishedRunnable(already_AddRefed<WorkerThread> aThread)
1062         : Runnable("WorkerThreadPrimaryRunnable::FinishedRunnable"),
1063           mThread(aThread) {
1064       MOZ_ASSERT(mThread);
1065     }
1066 
1067     NS_INLINE_DECL_REFCOUNTING_INHERITED(FinishedRunnable, Runnable)
1068 
1069    private:
~FinishedRunnable()1070     ~FinishedRunnable() {}
1071 
1072     NS_DECL_NSIRUNNABLE
1073   };
1074 
1075  public:
WorkerThreadPrimaryRunnable(WorkerPrivate * aWorkerPrivate,WorkerThread * aThread,JSRuntime * aParentRuntime)1076   WorkerThreadPrimaryRunnable(WorkerPrivate* aWorkerPrivate,
1077                               WorkerThread* aThread, JSRuntime* aParentRuntime)
1078       : mozilla::Runnable("WorkerThreadPrimaryRunnable"),
1079         mWorkerPrivate(aWorkerPrivate),
1080         mThread(aThread),
1081         mParentRuntime(aParentRuntime) {
1082     MOZ_ASSERT(aWorkerPrivate);
1083     MOZ_ASSERT(aThread);
1084   }
1085 
1086   NS_INLINE_DECL_REFCOUNTING_INHERITED(WorkerThreadPrimaryRunnable, Runnable)
1087 
1088  private:
~WorkerThreadPrimaryRunnable()1089   ~WorkerThreadPrimaryRunnable() {}
1090 
1091   NS_DECL_NSIRUNNABLE
1092 };
1093 
PrefLanguagesChanged(const char *,void *)1094 void PrefLanguagesChanged(const char* /* aPrefName */, void* /* aClosure */) {
1095   AssertIsOnMainThread();
1096 
1097   nsTArray<nsString> languages;
1098   Navigator::GetAcceptLanguages(languages);
1099 
1100   RuntimeService* runtime = RuntimeService::GetService();
1101   if (runtime) {
1102     runtime->UpdateAllWorkerLanguages(languages);
1103   }
1104 }
1105 
AppNameOverrideChanged(const char *,void *)1106 void AppNameOverrideChanged(const char* /* aPrefName */, void* /* aClosure */) {
1107   AssertIsOnMainThread();
1108 
1109   nsAutoString override;
1110   Preferences::GetString("general.appname.override", override);
1111 
1112   RuntimeService* runtime = RuntimeService::GetService();
1113   if (runtime) {
1114     runtime->UpdateAppNameOverridePreference(override);
1115   }
1116 }
1117 
AppVersionOverrideChanged(const char *,void *)1118 void AppVersionOverrideChanged(const char* /* aPrefName */,
1119                                void* /* aClosure */) {
1120   AssertIsOnMainThread();
1121 
1122   nsAutoString override;
1123   Preferences::GetString("general.appversion.override", override);
1124 
1125   RuntimeService* runtime = RuntimeService::GetService();
1126   if (runtime) {
1127     runtime->UpdateAppVersionOverridePreference(override);
1128   }
1129 }
1130 
PlatformOverrideChanged(const char *,void *)1131 void PlatformOverrideChanged(const char* /* aPrefName */,
1132                              void* /* aClosure */) {
1133   AssertIsOnMainThread();
1134 
1135   nsAutoString override;
1136   Preferences::GetString("general.platform.override", override);
1137 
1138   RuntimeService* runtime = RuntimeService::GetService();
1139   if (runtime) {
1140     runtime->UpdatePlatformOverridePreference(override);
1141   }
1142 }
1143 
1144 } /* anonymous namespace */
1145 
1146 struct RuntimeService::IdleThreadInfo {
1147   RefPtr<WorkerThread> mThread;
1148   mozilla::TimeStamp mExpirationTime;
1149 };
1150 
1151 // This is only touched on the main thread. Initialized in Init() below.
1152 JSSettings RuntimeService::sDefaultJSSettings;
1153 
RuntimeService()1154 RuntimeService::RuntimeService()
1155     : mMutex("RuntimeService::mMutex"),
1156       mObserved(false),
1157       mShuttingDown(false),
1158       mNavigatorPropertiesLoaded(false) {
1159   AssertIsOnMainThread();
1160   NS_ASSERTION(!gRuntimeService, "More than one service!");
1161 }
1162 
~RuntimeService()1163 RuntimeService::~RuntimeService() {
1164   AssertIsOnMainThread();
1165 
1166   // gRuntimeService can be null if Init() fails.
1167   NS_ASSERTION(!gRuntimeService || gRuntimeService == this,
1168                "More than one service!");
1169 
1170   gRuntimeService = nullptr;
1171 }
1172 
1173 // static
GetOrCreateService()1174 RuntimeService* RuntimeService::GetOrCreateService() {
1175   AssertIsOnMainThread();
1176 
1177   if (!gRuntimeService) {
1178     // The observer service now owns us until shutdown.
1179     gRuntimeService = new RuntimeService();
1180     if (NS_FAILED(gRuntimeService->Init())) {
1181       NS_WARNING("Failed to initialize!");
1182       gRuntimeService->Cleanup();
1183       gRuntimeService = nullptr;
1184       return nullptr;
1185     }
1186   }
1187 
1188   return gRuntimeService;
1189 }
1190 
1191 // static
GetService()1192 RuntimeService* RuntimeService::GetService() { return gRuntimeService; }
1193 
RegisterWorker(WorkerPrivate * aWorkerPrivate)1194 bool RuntimeService::RegisterWorker(WorkerPrivate* aWorkerPrivate) {
1195   aWorkerPrivate->AssertIsOnParentThread();
1196 
1197   WorkerPrivate* parent = aWorkerPrivate->GetParent();
1198   if (!parent) {
1199     AssertIsOnMainThread();
1200 
1201     if (mShuttingDown) {
1202       return false;
1203     }
1204   }
1205 
1206   const bool isServiceWorker = aWorkerPrivate->IsServiceWorker();
1207   const bool isSharedWorker = aWorkerPrivate->IsSharedWorker();
1208   const bool isDedicatedWorker = aWorkerPrivate->IsDedicatedWorker();
1209   if (isServiceWorker) {
1210     AssertIsOnMainThread();
1211     Telemetry::Accumulate(Telemetry::SERVICE_WORKER_SPAWN_ATTEMPTS, 1);
1212   }
1213 
1214   nsCString sharedWorkerScriptSpec;
1215   if (isSharedWorker) {
1216     AssertIsOnMainThread();
1217 
1218     nsCOMPtr<nsIURI> scriptURI = aWorkerPrivate->GetResolvedScriptURI();
1219     NS_ASSERTION(scriptURI, "Null script URI!");
1220 
1221     nsresult rv = scriptURI->GetSpec(sharedWorkerScriptSpec);
1222     if (NS_FAILED(rv)) {
1223       NS_WARNING("GetSpec failed?!");
1224       return false;
1225     }
1226 
1227     NS_ASSERTION(!sharedWorkerScriptSpec.IsEmpty(), "Empty spec!");
1228   }
1229 
1230   bool exemptFromPerDomainMax = false;
1231   if (isServiceWorker) {
1232     AssertIsOnMainThread();
1233     exemptFromPerDomainMax = Preferences::GetBool(
1234         "dom.serviceWorkers.exemptFromPerDomainMax", false);
1235   }
1236 
1237   const nsCString& domain = aWorkerPrivate->Domain();
1238 
1239   WorkerDomainInfo* domainInfo;
1240   bool queued = false;
1241   {
1242     MutexAutoLock lock(mMutex);
1243 
1244     domainInfo = mDomainMap.LookupForAdd(domain).OrInsert([&domain, parent]() {
1245       NS_ASSERTION(!parent, "Shouldn't have a parent here!");
1246       Unused << parent;  // silence clang -Wunused-lambda-capture in opt builds
1247       WorkerDomainInfo* wdi = new WorkerDomainInfo();
1248       wdi->mDomain = domain;
1249       return wdi;
1250     });
1251 
1252     queued = gMaxWorkersPerDomain &&
1253              domainInfo->ActiveWorkerCount() >= gMaxWorkersPerDomain &&
1254              !domain.IsEmpty() && !exemptFromPerDomainMax;
1255 
1256     if (queued) {
1257       domainInfo->mQueuedWorkers.AppendElement(aWorkerPrivate);
1258 
1259       // Worker spawn gets queued due to hitting max workers per domain
1260       // limit so let's log a warning.
1261       WorkerPrivate::ReportErrorToConsole("HittingMaxWorkersPerDomain2");
1262 
1263       if (isServiceWorker) {
1264         Telemetry::Accumulate(Telemetry::SERVICE_WORKER_SPAWN_GETS_QUEUED, 1);
1265       } else if (isSharedWorker) {
1266         Telemetry::Accumulate(Telemetry::SHARED_WORKER_SPAWN_GETS_QUEUED, 1);
1267       } else if (isDedicatedWorker) {
1268         Telemetry::Accumulate(Telemetry::DEDICATED_WORKER_SPAWN_GETS_QUEUED, 1);
1269       }
1270     } else if (parent) {
1271       domainInfo->mChildWorkerCount++;
1272     } else if (isServiceWorker) {
1273       domainInfo->mActiveServiceWorkers.AppendElement(aWorkerPrivate);
1274     } else {
1275       domainInfo->mActiveWorkers.AppendElement(aWorkerPrivate);
1276     }
1277 
1278     if (isSharedWorker) {
1279 #ifdef DEBUG
1280       for (const UniquePtr<SharedWorkerInfo>& data :
1281            domainInfo->mSharedWorkerInfos) {
1282         if (data->mScriptSpec == sharedWorkerScriptSpec &&
1283             data->mName == aWorkerPrivate->WorkerName() &&
1284             // We want to be sure that the window's principal subsumes the
1285             // SharedWorker's principal and vice versa.
1286             data->mWorkerPrivate->GetPrincipal()->Subsumes(
1287                 aWorkerPrivate->GetPrincipal()) &&
1288             aWorkerPrivate->GetPrincipal()->Subsumes(
1289                 data->mWorkerPrivate->GetPrincipal())) {
1290           MOZ_CRASH("We should not instantiate a new SharedWorker!");
1291         }
1292       }
1293 #endif
1294 
1295       UniquePtr<SharedWorkerInfo> sharedWorkerInfo(
1296           new SharedWorkerInfo(aWorkerPrivate, sharedWorkerScriptSpec,
1297                                aWorkerPrivate->WorkerName()));
1298       domainInfo->mSharedWorkerInfos.AppendElement(Move(sharedWorkerInfo));
1299     }
1300   }
1301 
1302   // From here on out we must call UnregisterWorker if something fails!
1303   if (parent) {
1304     if (!parent->AddChildWorker(aWorkerPrivate)) {
1305       UnregisterWorker(aWorkerPrivate);
1306       return false;
1307     }
1308   } else {
1309     if (!mNavigatorPropertiesLoaded) {
1310       Navigator::AppName(mNavigatorProperties.mAppName,
1311                          false /* aUsePrefOverriddenValue */);
1312       if (NS_FAILED(
1313               Navigator::GetAppVersion(mNavigatorProperties.mAppVersion,
1314                                        false /* aUsePrefOverriddenValue */)) ||
1315           NS_FAILED(
1316               Navigator::GetPlatform(mNavigatorProperties.mPlatform,
1317                                      false /* aUsePrefOverriddenValue */))) {
1318         UnregisterWorker(aWorkerPrivate);
1319         return false;
1320       }
1321 
1322       // The navigator overridden properties should have already been read.
1323 
1324       Navigator::GetAcceptLanguages(mNavigatorProperties.mLanguages);
1325       mNavigatorPropertiesLoaded = true;
1326     }
1327 
1328     nsPIDOMWindowInner* window = aWorkerPrivate->GetWindow();
1329 
1330     if (!isServiceWorker) {
1331       // Service workers are excluded since their lifetime is separate from
1332       // that of dom windows.
1333       nsTArray<WorkerPrivate*>* windowArray =
1334           mWindowMap.LookupForAdd(window).OrInsert(
1335               []() { return new nsTArray<WorkerPrivate*>(1); });
1336       if (!windowArray->Contains(aWorkerPrivate)) {
1337         windowArray->AppendElement(aWorkerPrivate);
1338       } else {
1339         MOZ_ASSERT(aWorkerPrivate->IsSharedWorker());
1340       }
1341     }
1342   }
1343 
1344   if (!queued && !ScheduleWorker(aWorkerPrivate)) {
1345     return false;
1346   }
1347 
1348   if (isServiceWorker) {
1349     AssertIsOnMainThread();
1350     Telemetry::Accumulate(Telemetry::SERVICE_WORKER_WAS_SPAWNED, 1);
1351   }
1352   return true;
1353 }
1354 
RemoveSharedWorker(WorkerDomainInfo * aDomainInfo,WorkerPrivate * aWorkerPrivate)1355 void RuntimeService::RemoveSharedWorker(WorkerDomainInfo* aDomainInfo,
1356                                         WorkerPrivate* aWorkerPrivate) {
1357   for (uint32_t i = 0; i < aDomainInfo->mSharedWorkerInfos.Length(); ++i) {
1358     const UniquePtr<SharedWorkerInfo>& data =
1359         aDomainInfo->mSharedWorkerInfos[i];
1360     if (data->mWorkerPrivate == aWorkerPrivate) {
1361       aDomainInfo->mSharedWorkerInfos.RemoveElementAt(i);
1362       break;
1363     }
1364   }
1365 }
1366 
UnregisterWorker(WorkerPrivate * aWorkerPrivate)1367 void RuntimeService::UnregisterWorker(WorkerPrivate* aWorkerPrivate) {
1368   aWorkerPrivate->AssertIsOnParentThread();
1369 
1370   WorkerPrivate* parent = aWorkerPrivate->GetParent();
1371   if (!parent) {
1372     AssertIsOnMainThread();
1373   }
1374 
1375   const nsCString& domain = aWorkerPrivate->Domain();
1376 
1377   WorkerPrivate* queuedWorker = nullptr;
1378   {
1379     MutexAutoLock lock(mMutex);
1380 
1381     WorkerDomainInfo* domainInfo;
1382     if (!mDomainMap.Get(domain, &domainInfo)) {
1383       NS_ERROR("Don't have an entry for this domain!");
1384     }
1385 
1386     // Remove old worker from everywhere.
1387     uint32_t index = domainInfo->mQueuedWorkers.IndexOf(aWorkerPrivate);
1388     if (index != kNoIndex) {
1389       // Was queued, remove from the list.
1390       domainInfo->mQueuedWorkers.RemoveElementAt(index);
1391     } else if (parent) {
1392       MOZ_ASSERT(domainInfo->mChildWorkerCount, "Must be non-zero!");
1393       domainInfo->mChildWorkerCount--;
1394     } else if (aWorkerPrivate->IsServiceWorker()) {
1395       MOZ_ASSERT(domainInfo->mActiveServiceWorkers.Contains(aWorkerPrivate),
1396                  "Don't know about this worker!");
1397       domainInfo->mActiveServiceWorkers.RemoveElement(aWorkerPrivate);
1398     } else {
1399       MOZ_ASSERT(domainInfo->mActiveWorkers.Contains(aWorkerPrivate),
1400                  "Don't know about this worker!");
1401       domainInfo->mActiveWorkers.RemoveElement(aWorkerPrivate);
1402     }
1403 
1404     if (aWorkerPrivate->IsSharedWorker()) {
1405       RemoveSharedWorker(domainInfo, aWorkerPrivate);
1406     }
1407 
1408     // See if there's a queued worker we can schedule.
1409     if (domainInfo->ActiveWorkerCount() < gMaxWorkersPerDomain &&
1410         !domainInfo->mQueuedWorkers.IsEmpty()) {
1411       queuedWorker = domainInfo->mQueuedWorkers[0];
1412       domainInfo->mQueuedWorkers.RemoveElementAt(0);
1413 
1414       if (queuedWorker->GetParent()) {
1415         domainInfo->mChildWorkerCount++;
1416       } else if (queuedWorker->IsServiceWorker()) {
1417         domainInfo->mActiveServiceWorkers.AppendElement(queuedWorker);
1418       } else {
1419         domainInfo->mActiveWorkers.AppendElement(queuedWorker);
1420       }
1421     }
1422 
1423     if (domainInfo->HasNoWorkers()) {
1424       MOZ_ASSERT(domainInfo->mQueuedWorkers.IsEmpty());
1425       mDomainMap.Remove(domain);
1426     }
1427   }
1428 
1429   if (aWorkerPrivate->IsServiceWorker()) {
1430     AssertIsOnMainThread();
1431     Telemetry::AccumulateTimeDelta(Telemetry::SERVICE_WORKER_LIFE_TIME,
1432                                    aWorkerPrivate->CreationTimeStamp());
1433   }
1434 
1435   if (aWorkerPrivate->IsSharedWorker() || aWorkerPrivate->IsServiceWorker()) {
1436     AssertIsOnMainThread();
1437     aWorkerPrivate->CloseAllSharedWorkers();
1438   }
1439 
1440   if (parent) {
1441     parent->RemoveChildWorker(aWorkerPrivate);
1442   } else if (aWorkerPrivate->IsSharedWorker()) {
1443     AssertIsOnMainThread();
1444 
1445     for (auto iter = mWindowMap.Iter(); !iter.Done(); iter.Next()) {
1446       nsAutoPtr<nsTArray<WorkerPrivate*>>& workers = iter.Data();
1447       MOZ_ASSERT(workers.get());
1448 
1449       if (workers->RemoveElement(aWorkerPrivate)) {
1450         MOZ_ASSERT(!workers->Contains(aWorkerPrivate),
1451                    "Added worker more than once!");
1452 
1453         if (workers->IsEmpty()) {
1454           iter.Remove();
1455         }
1456       }
1457     }
1458   } else if (aWorkerPrivate->IsDedicatedWorker()) {
1459     // May be null.
1460     nsPIDOMWindowInner* window = aWorkerPrivate->GetWindow();
1461     if (auto entry = mWindowMap.Lookup(window)) {
1462       MOZ_ALWAYS_TRUE(entry.Data()->RemoveElement(aWorkerPrivate));
1463       if (entry.Data()->IsEmpty()) {
1464         entry.Remove();
1465       }
1466     } else {
1467       MOZ_ASSERT_UNREACHABLE("window is not in mWindowMap");
1468     }
1469   }
1470 
1471   if (queuedWorker && !ScheduleWorker(queuedWorker)) {
1472     UnregisterWorker(queuedWorker);
1473   }
1474 }
1475 
ScheduleWorker(WorkerPrivate * aWorkerPrivate)1476 bool RuntimeService::ScheduleWorker(WorkerPrivate* aWorkerPrivate) {
1477   if (!aWorkerPrivate->Start()) {
1478     // This is ok, means that we didn't need to make a thread for this worker.
1479     return true;
1480   }
1481 
1482   RefPtr<WorkerThread> thread;
1483   {
1484     MutexAutoLock lock(mMutex);
1485     if (!mIdleThreadArray.IsEmpty()) {
1486       uint32_t index = mIdleThreadArray.Length() - 1;
1487       mIdleThreadArray[index].mThread.swap(thread);
1488       mIdleThreadArray.RemoveElementAt(index);
1489     }
1490   }
1491 
1492   const WorkerThreadFriendKey friendKey;
1493 
1494   if (!thread) {
1495     thread = WorkerThread::Create(friendKey);
1496     if (!thread) {
1497       UnregisterWorker(aWorkerPrivate);
1498       return false;
1499     }
1500   }
1501 
1502   int32_t priority = aWorkerPrivate->IsChromeWorker()
1503                          ? nsISupportsPriority::PRIORITY_NORMAL
1504                          : nsISupportsPriority::PRIORITY_LOW;
1505 
1506   if (NS_FAILED(thread->SetPriority(priority))) {
1507     NS_WARNING("Could not set the thread's priority!");
1508   }
1509 
1510   JSContext* cx = CycleCollectedJSContext::Get()->Context();
1511   nsCOMPtr<nsIRunnable> runnable = new WorkerThreadPrimaryRunnable(
1512       aWorkerPrivate, thread, JS_GetParentRuntime(cx));
1513   if (NS_FAILED(
1514           thread->DispatchPrimaryRunnable(friendKey, runnable.forget()))) {
1515     UnregisterWorker(aWorkerPrivate);
1516     return false;
1517   }
1518 
1519   return true;
1520 }
1521 
1522 // static
ShutdownIdleThreads(nsITimer * aTimer,void *)1523 void RuntimeService::ShutdownIdleThreads(nsITimer* aTimer,
1524                                          void* /* aClosure */) {
1525   AssertIsOnMainThread();
1526 
1527   RuntimeService* runtime = RuntimeService::GetService();
1528   NS_ASSERTION(runtime, "This should never be null!");
1529 
1530   NS_ASSERTION(aTimer == runtime->mIdleThreadTimer, "Wrong timer!");
1531 
1532   // Cheat a little and grab all threads that expire within one second of now.
1533   TimeStamp now = TimeStamp::NowLoRes() + TimeDuration::FromSeconds(1);
1534 
1535   TimeStamp nextExpiration;
1536 
1537   AutoTArray<RefPtr<WorkerThread>, 20> expiredThreads;
1538   {
1539     MutexAutoLock lock(runtime->mMutex);
1540 
1541     for (uint32_t index = 0; index < runtime->mIdleThreadArray.Length();
1542          index++) {
1543       IdleThreadInfo& info = runtime->mIdleThreadArray[index];
1544       if (info.mExpirationTime > now) {
1545         nextExpiration = info.mExpirationTime;
1546         break;
1547       }
1548 
1549       RefPtr<WorkerThread>* thread = expiredThreads.AppendElement();
1550       thread->swap(info.mThread);
1551     }
1552 
1553     if (!expiredThreads.IsEmpty()) {
1554       runtime->mIdleThreadArray.RemoveElementsAt(0, expiredThreads.Length());
1555     }
1556   }
1557 
1558   if (!nextExpiration.IsNull()) {
1559     TimeDuration delta = nextExpiration - TimeStamp::NowLoRes();
1560     uint32_t delay(delta > TimeDuration(0) ? delta.ToMilliseconds() : 0);
1561 
1562     // Reschedule the timer.
1563     MOZ_ALWAYS_SUCCEEDS(aTimer->InitWithNamedFuncCallback(
1564         ShutdownIdleThreads, nullptr, delay, nsITimer::TYPE_ONE_SHOT,
1565         "RuntimeService::ShutdownIdleThreads"));
1566   }
1567 
1568   for (uint32_t index = 0; index < expiredThreads.Length(); index++) {
1569     if (NS_FAILED(expiredThreads[index]->Shutdown())) {
1570       NS_WARNING("Failed to shutdown thread!");
1571     }
1572   }
1573 }
1574 
Init()1575 nsresult RuntimeService::Init() {
1576   AssertIsOnMainThread();
1577 
1578   nsLayoutStatics::AddRef();
1579 
1580   // Initialize JSSettings.
1581   if (sDefaultJSSettings.gcSettings[0].key.isNothing()) {
1582     sDefaultJSSettings.contextOptions = JS::ContextOptions();
1583     sDefaultJSSettings.chrome.maxScriptRuntime = -1;
1584     sDefaultJSSettings.content.maxScriptRuntime = MAX_SCRIPT_RUN_TIME_SEC;
1585 #ifdef JS_GC_ZEAL
1586     sDefaultJSSettings.gcZealFrequency = JS_DEFAULT_ZEAL_FREQ;
1587     sDefaultJSSettings.gcZeal = 0;
1588 #endif
1589     SetDefaultJSGCSettings(JSGC_MAX_BYTES, WORKER_DEFAULT_RUNTIME_HEAPSIZE);
1590     SetDefaultJSGCSettings(JSGC_ALLOCATION_THRESHOLD,
1591                            WORKER_DEFAULT_ALLOCATION_THRESHOLD);
1592   }
1593 
1594   // nsIStreamTransportService is thread-safe but it must be initialized on the
1595   // main-thread. FileReader needs it, so, let's initialize it now.
1596   nsresult rv;
1597   nsCOMPtr<nsIStreamTransportService> sts =
1598       do_GetService(kStreamTransportServiceCID, &rv);
1599   NS_ENSURE_TRUE(sts, NS_ERROR_FAILURE);
1600 
1601   mIdleThreadTimer = NS_NewTimer();
1602   NS_ENSURE_STATE(mIdleThreadTimer);
1603 
1604   nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
1605   NS_ENSURE_TRUE(obs, NS_ERROR_FAILURE);
1606 
1607   rv = obs->AddObserver(this, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID, false);
1608   NS_ENSURE_SUCCESS(rv, rv);
1609 
1610   rv = obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
1611   NS_ENSURE_SUCCESS(rv, rv);
1612 
1613   mObserved = true;
1614 
1615   if (NS_FAILED(obs->AddObserver(this, GC_REQUEST_OBSERVER_TOPIC, false))) {
1616     NS_WARNING("Failed to register for GC request notifications!");
1617   }
1618 
1619   if (NS_FAILED(obs->AddObserver(this, CC_REQUEST_OBSERVER_TOPIC, false))) {
1620     NS_WARNING("Failed to register for CC request notifications!");
1621   }
1622 
1623   if (NS_FAILED(
1624           obs->AddObserver(this, MEMORY_PRESSURE_OBSERVER_TOPIC, false))) {
1625     NS_WARNING("Failed to register for memory pressure notifications!");
1626   }
1627 
1628   if (NS_FAILED(
1629           obs->AddObserver(this, NS_IOSERVICE_OFFLINE_STATUS_TOPIC, false))) {
1630     NS_WARNING("Failed to register for offline notification event!");
1631   }
1632 
1633   MOZ_ASSERT(!gRuntimeServiceDuringInit, "This should be false!");
1634   gRuntimeServiceDuringInit = true;
1635 
1636   if (NS_FAILED(Preferences::RegisterPrefixCallback(
1637           LoadJSGCMemoryOptions,
1638           PREF_JS_OPTIONS_PREFIX PREF_MEM_OPTIONS_PREFIX)) ||
1639       NS_FAILED(Preferences::RegisterPrefixCallbackAndCall(
1640           LoadJSGCMemoryOptions,
1641           PREF_WORKERS_OPTIONS_PREFIX PREF_MEM_OPTIONS_PREFIX)) ||
1642 #ifdef JS_GC_ZEAL
1643       NS_FAILED(Preferences::RegisterCallback(
1644           LoadGCZealOptions, PREF_JS_OPTIONS_PREFIX PREF_GCZEAL)) ||
1645 #endif
1646 
1647 #define WORKER_PREF(name, callback) \
1648   NS_FAILED(Preferences::RegisterCallbackAndCall(callback, name)) ||
1649       WORKER_PREF("intl.accept_languages", PrefLanguagesChanged) WORKER_PREF(
1650           "general.appname.override", AppNameOverrideChanged)
1651           WORKER_PREF("general.appversion.override", AppVersionOverrideChanged)
1652               WORKER_PREF("general.platform.override", PlatformOverrideChanged)
1653 #ifdef JS_GC_ZEAL
1654                   WORKER_PREF("dom.workers.options.gcZeal", LoadGCZealOptions)
1655 #endif
1656 #undef WORKER_PREF
1657 
1658                       NS_FAILED(Preferences::RegisterPrefixCallbackAndCall(
1659                           LoadContextOptions, PREF_WORKERS_OPTIONS_PREFIX)) ||
1660       NS_FAILED(Preferences::RegisterPrefixCallback(LoadContextOptions,
1661                                                     PREF_JS_OPTIONS_PREFIX))) {
1662     NS_WARNING("Failed to register pref callbacks!");
1663   }
1664 
1665   MOZ_ASSERT(gRuntimeServiceDuringInit, "Should be true!");
1666   gRuntimeServiceDuringInit = false;
1667 
1668   // We assume atomic 32bit reads/writes. If this assumption doesn't hold on
1669   // some wacky platform then the worst that could happen is that the close
1670   // handler will run for a slightly different amount of time.
1671   if (NS_FAILED(Preferences::AddIntVarCache(
1672           &sDefaultJSSettings.content.maxScriptRuntime,
1673           PREF_MAX_SCRIPT_RUN_TIME_CONTENT, MAX_SCRIPT_RUN_TIME_SEC)) ||
1674       NS_FAILED(Preferences::AddIntVarCache(
1675           &sDefaultJSSettings.chrome.maxScriptRuntime,
1676           PREF_MAX_SCRIPT_RUN_TIME_CHROME, -1))) {
1677     NS_WARNING("Failed to register timeout cache!");
1678   }
1679 
1680   int32_t maxPerDomain =
1681       Preferences::GetInt(PREF_WORKERS_MAX_PER_DOMAIN, MAX_WORKERS_PER_DOMAIN);
1682   gMaxWorkersPerDomain = std::max(0, maxPerDomain);
1683 
1684   int32_t maxHardwareConcurrency = Preferences::GetInt(
1685       PREF_WORKERS_MAX_HARDWARE_CONCURRENCY, MAX_HARDWARE_CONCURRENCY);
1686   gMaxHardwareConcurrency = std::max(0, maxHardwareConcurrency);
1687 
1688   RefPtr<OSFileConstantsService> osFileConstantsService =
1689       OSFileConstantsService::GetOrCreate();
1690   if (NS_WARN_IF(!osFileConstantsService)) {
1691     return NS_ERROR_FAILURE;
1692   }
1693 
1694   if (NS_WARN_IF(!IndexedDatabaseManager::GetOrCreate())) {
1695     return NS_ERROR_UNEXPECTED;
1696   }
1697 
1698   // PerformanceService must be initialized on the main-thread.
1699   PerformanceService::GetOrCreate();
1700 
1701   return NS_OK;
1702 }
1703 
Shutdown()1704 void RuntimeService::Shutdown() {
1705   AssertIsOnMainThread();
1706 
1707   MOZ_ASSERT(!mShuttingDown);
1708   // That's it, no more workers.
1709   mShuttingDown = true;
1710 
1711   nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
1712   NS_WARNING_ASSERTION(obs, "Failed to get observer service?!");
1713 
1714   // Tell anyone that cares that they're about to lose worker support.
1715   if (obs && NS_FAILED(obs->NotifyObservers(nullptr, WORKERS_SHUTDOWN_TOPIC,
1716                                             nullptr))) {
1717     NS_WARNING("NotifyObservers failed!");
1718   }
1719 
1720   {
1721     MutexAutoLock lock(mMutex);
1722 
1723     AutoTArray<WorkerPrivate*, 100> workers;
1724     AddAllTopLevelWorkersToArray(workers);
1725 
1726     if (!workers.IsEmpty()) {
1727       // Cancel all top-level workers.
1728       {
1729         MutexAutoUnlock unlock(mMutex);
1730 
1731         for (uint32_t index = 0; index < workers.Length(); index++) {
1732           if (!workers[index]->Kill()) {
1733             NS_WARNING("Failed to cancel worker!");
1734           }
1735         }
1736       }
1737     }
1738   }
1739 }
1740 
1741 namespace {
1742 
1743 class CrashIfHangingRunnable : public WorkerControlRunnable {
1744  public:
CrashIfHangingRunnable(WorkerPrivate * aWorkerPrivate)1745   explicit CrashIfHangingRunnable(WorkerPrivate* aWorkerPrivate)
1746       : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
1747         mMonitor("CrashIfHangingRunnable::mMonitor") {}
1748 
WorkerRun(JSContext * aCx,WorkerPrivate * aWorkerPrivate)1749   bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
1750     aWorkerPrivate->DumpCrashInformation(mMsg);
1751 
1752     MonitorAutoLock lock(mMonitor);
1753     lock.Notify();
1754     return true;
1755   }
1756 
Cancel()1757   nsresult Cancel() override {
1758     mMsg.Assign("Canceled");
1759 
1760     MonitorAutoLock lock(mMonitor);
1761     lock.Notify();
1762 
1763     return NS_OK;
1764   }
1765 
DispatchAndWait()1766   void DispatchAndWait() {
1767     MonitorAutoLock lock(mMonitor);
1768 
1769     if (!Dispatch()) {
1770       mMsg.Assign("Dispatch Error");
1771       return;
1772     }
1773 
1774     lock.Wait();
1775   }
1776 
MsgData() const1777   const nsCString& MsgData() const { return mMsg; }
1778 
1779  private:
PreDispatch(WorkerPrivate * aWorkerPrivate)1780   bool PreDispatch(WorkerPrivate* aWorkerPrivate) override { return true; }
1781 
PostDispatch(WorkerPrivate * aWorkerPrivate,bool aDispatchResult)1782   void PostDispatch(WorkerPrivate* aWorkerPrivate,
1783                     bool aDispatchResult) override {}
1784 
1785   Monitor mMonitor;
1786   nsCString mMsg;
1787 };
1788 
1789 }  // namespace
1790 
CrashIfHanging()1791 void RuntimeService::CrashIfHanging() {
1792   MutexAutoLock lock(mMutex);
1793 
1794   if (mDomainMap.IsEmpty()) {
1795     return;
1796   }
1797 
1798   uint32_t activeWorkers = 0;
1799   uint32_t activeServiceWorkers = 0;
1800   uint32_t inactiveWorkers = 0;
1801 
1802   nsTArray<WorkerPrivate*> workers;
1803 
1804   for (auto iter = mDomainMap.Iter(); !iter.Done(); iter.Next()) {
1805     WorkerDomainInfo* aData = iter.UserData();
1806 
1807     activeWorkers += aData->mActiveWorkers.Length();
1808     activeServiceWorkers += aData->mActiveServiceWorkers.Length();
1809 
1810     workers.AppendElements(aData->mActiveWorkers);
1811     workers.AppendElements(aData->mActiveServiceWorkers);
1812 
1813     // These might not be top-level workers...
1814     for (uint32_t index = 0; index < aData->mQueuedWorkers.Length(); index++) {
1815       WorkerPrivate* worker = aData->mQueuedWorkers[index];
1816       if (!worker->GetParent()) {
1817         ++inactiveWorkers;
1818       }
1819     }
1820   }
1821 
1822   // We must have something pending...
1823   MOZ_DIAGNOSTIC_ASSERT(activeWorkers + activeServiceWorkers + inactiveWorkers);
1824 
1825   nsCString msg;
1826 
1827   // A: active Workers | S: active ServiceWorkers | Q: queued Workers
1828   msg.AppendPrintf("Workers Hanging - %d|A:%d|S:%d|Q:%d", mShuttingDown ? 1 : 0,
1829                    activeWorkers, activeServiceWorkers, inactiveWorkers);
1830 
1831   // For each thread, let's print some data to know what is going wrong.
1832   for (uint32_t i = 0; i < workers.Length(); ++i) {
1833     WorkerPrivate* workerPrivate = workers[i];
1834 
1835     // BC: Busy Count
1836     msg.AppendPrintf("-BC:%d", workerPrivate->BusyCount());
1837 
1838     RefPtr<CrashIfHangingRunnable> runnable =
1839         new CrashIfHangingRunnable(workerPrivate);
1840     runnable->DispatchAndWait();
1841 
1842     msg.Append(runnable->MsgData());
1843   }
1844 
1845   // This string will be leaked.
1846   MOZ_CRASH_UNSAFE_OOL(strdup(msg.BeginReading()));
1847 }
1848 
1849 // This spins the event loop until all workers are finished and their threads
1850 // have been joined.
Cleanup()1851 void RuntimeService::Cleanup() {
1852   AssertIsOnMainThread();
1853 
1854   nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
1855   NS_WARNING_ASSERTION(obs, "Failed to get observer service?!");
1856 
1857   if (mIdleThreadTimer) {
1858     if (NS_FAILED(mIdleThreadTimer->Cancel())) {
1859       NS_WARNING("Failed to cancel idle timer!");
1860     }
1861     mIdleThreadTimer = nullptr;
1862   }
1863 
1864   {
1865     MutexAutoLock lock(mMutex);
1866 
1867     AutoTArray<WorkerPrivate*, 100> workers;
1868     AddAllTopLevelWorkersToArray(workers);
1869 
1870     if (!workers.IsEmpty()) {
1871       nsIThread* currentThread = NS_GetCurrentThread();
1872       NS_ASSERTION(currentThread, "This should never be null!");
1873 
1874       // Shut down any idle threads.
1875       if (!mIdleThreadArray.IsEmpty()) {
1876         AutoTArray<RefPtr<WorkerThread>, 20> idleThreads;
1877 
1878         uint32_t idleThreadCount = mIdleThreadArray.Length();
1879         idleThreads.SetLength(idleThreadCount);
1880 
1881         for (uint32_t index = 0; index < idleThreadCount; index++) {
1882           NS_ASSERTION(mIdleThreadArray[index].mThread, "Null thread!");
1883           idleThreads[index].swap(mIdleThreadArray[index].mThread);
1884         }
1885 
1886         mIdleThreadArray.Clear();
1887 
1888         MutexAutoUnlock unlock(mMutex);
1889 
1890         for (uint32_t index = 0; index < idleThreadCount; index++) {
1891           if (NS_FAILED(idleThreads[index]->Shutdown())) {
1892             NS_WARNING("Failed to shutdown thread!");
1893           }
1894         }
1895       }
1896 
1897       // And make sure all their final messages have run and all their threads
1898       // have joined.
1899       while (mDomainMap.Count()) {
1900         MutexAutoUnlock unlock(mMutex);
1901 
1902         if (!NS_ProcessNextEvent(currentThread)) {
1903           NS_WARNING("Something bad happened!");
1904           break;
1905         }
1906       }
1907     }
1908   }
1909 
1910   NS_ASSERTION(!mWindowMap.Count(), "All windows should have been released!");
1911 
1912   if (mObserved) {
1913     if (NS_FAILED(Preferences::UnregisterPrefixCallback(
1914             LoadContextOptions, PREF_JS_OPTIONS_PREFIX)) ||
1915         NS_FAILED(Preferences::UnregisterPrefixCallback(
1916             LoadContextOptions, PREF_WORKERS_OPTIONS_PREFIX)) ||
1917 #define WORKER_PREF(name, callback) \
1918   NS_FAILED(Preferences::UnregisterCallback(callback, name)) ||
1919         WORKER_PREF("intl.accept_languages", PrefLanguagesChanged) WORKER_PREF(
1920             "general.appname.override",
1921             AppNameOverrideChanged) WORKER_PREF("general.appversion.override",
1922                                                 AppVersionOverrideChanged)
1923             WORKER_PREF("general.platform.override", PlatformOverrideChanged)
1924 #ifdef JS_GC_ZEAL
1925                 WORKER_PREF("dom.workers.options.gcZeal", LoadGCZealOptions)
1926 #endif
1927 #undef WORKER_PREF
1928 
1929 #ifdef JS_GC_ZEAL
1930                     NS_FAILED(Preferences::UnregisterCallback(
1931                         LoadGCZealOptions,
1932                         PREF_JS_OPTIONS_PREFIX PREF_GCZEAL)) ||
1933 #endif
1934         NS_FAILED(Preferences::UnregisterPrefixCallback(
1935             LoadJSGCMemoryOptions,
1936             PREF_JS_OPTIONS_PREFIX PREF_MEM_OPTIONS_PREFIX)) ||
1937         NS_FAILED(Preferences::UnregisterPrefixCallback(
1938             LoadJSGCMemoryOptions,
1939             PREF_WORKERS_OPTIONS_PREFIX PREF_MEM_OPTIONS_PREFIX))) {
1940       NS_WARNING("Failed to unregister pref callbacks!");
1941     }
1942 
1943     if (obs) {
1944       if (NS_FAILED(obs->RemoveObserver(this, GC_REQUEST_OBSERVER_TOPIC))) {
1945         NS_WARNING("Failed to unregister for GC request notifications!");
1946       }
1947 
1948       if (NS_FAILED(obs->RemoveObserver(this, CC_REQUEST_OBSERVER_TOPIC))) {
1949         NS_WARNING("Failed to unregister for CC request notifications!");
1950       }
1951 
1952       if (NS_FAILED(
1953               obs->RemoveObserver(this, MEMORY_PRESSURE_OBSERVER_TOPIC))) {
1954         NS_WARNING("Failed to unregister for memory pressure notifications!");
1955       }
1956 
1957       if (NS_FAILED(
1958               obs->RemoveObserver(this, NS_IOSERVICE_OFFLINE_STATUS_TOPIC))) {
1959         NS_WARNING("Failed to unregister for offline notification event!");
1960       }
1961       obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID);
1962       obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
1963       mObserved = false;
1964     }
1965   }
1966 
1967   nsLayoutStatics::Release();
1968 }
1969 
AddAllTopLevelWorkersToArray(nsTArray<WorkerPrivate * > & aWorkers)1970 void RuntimeService::AddAllTopLevelWorkersToArray(
1971     nsTArray<WorkerPrivate*>& aWorkers) {
1972   for (auto iter = mDomainMap.Iter(); !iter.Done(); iter.Next()) {
1973     WorkerDomainInfo* aData = iter.UserData();
1974 
1975 #ifdef DEBUG
1976     for (uint32_t index = 0; index < aData->mActiveWorkers.Length(); index++) {
1977       MOZ_ASSERT(!aData->mActiveWorkers[index]->GetParent(),
1978                  "Shouldn't have a parent in this list!");
1979     }
1980     for (uint32_t index = 0; index < aData->mActiveServiceWorkers.Length();
1981          index++) {
1982       MOZ_ASSERT(!aData->mActiveServiceWorkers[index]->GetParent(),
1983                  "Shouldn't have a parent in this list!");
1984     }
1985 #endif
1986 
1987     aWorkers.AppendElements(aData->mActiveWorkers);
1988     aWorkers.AppendElements(aData->mActiveServiceWorkers);
1989 
1990     // These might not be top-level workers...
1991     for (uint32_t index = 0; index < aData->mQueuedWorkers.Length(); index++) {
1992       WorkerPrivate* worker = aData->mQueuedWorkers[index];
1993       if (!worker->GetParent()) {
1994         aWorkers.AppendElement(worker);
1995       }
1996     }
1997   }
1998 }
1999 
GetWorkersForWindow(nsPIDOMWindowInner * aWindow,nsTArray<WorkerPrivate * > & aWorkers)2000 void RuntimeService::GetWorkersForWindow(nsPIDOMWindowInner* aWindow,
2001                                          nsTArray<WorkerPrivate*>& aWorkers) {
2002   AssertIsOnMainThread();
2003 
2004   nsTArray<WorkerPrivate*>* workers;
2005   if (mWindowMap.Get(aWindow, &workers)) {
2006     NS_ASSERTION(!workers->IsEmpty(), "Should have been removed!");
2007     aWorkers.AppendElements(*workers);
2008   } else {
2009     NS_ASSERTION(aWorkers.IsEmpty(), "Should be empty!");
2010   }
2011 }
2012 
CancelWorkersForWindow(nsPIDOMWindowInner * aWindow)2013 void RuntimeService::CancelWorkersForWindow(nsPIDOMWindowInner* aWindow) {
2014   AssertIsOnMainThread();
2015 
2016   nsTArray<WorkerPrivate*> workers;
2017   GetWorkersForWindow(aWindow, workers);
2018 
2019   if (!workers.IsEmpty()) {
2020     for (uint32_t index = 0; index < workers.Length(); index++) {
2021       WorkerPrivate*& worker = workers[index];
2022 
2023       if (worker->IsSharedWorker()) {
2024         worker->CloseSharedWorkersForWindow(aWindow);
2025       } else {
2026         worker->Cancel();
2027       }
2028     }
2029   }
2030 }
2031 
FreezeWorkersForWindow(nsPIDOMWindowInner * aWindow)2032 void RuntimeService::FreezeWorkersForWindow(nsPIDOMWindowInner* aWindow) {
2033   AssertIsOnMainThread();
2034   MOZ_ASSERT(aWindow);
2035 
2036   nsTArray<WorkerPrivate*> workers;
2037   GetWorkersForWindow(aWindow, workers);
2038 
2039   for (uint32_t index = 0; index < workers.Length(); index++) {
2040     workers[index]->Freeze(aWindow);
2041   }
2042 }
2043 
ThawWorkersForWindow(nsPIDOMWindowInner * aWindow)2044 void RuntimeService::ThawWorkersForWindow(nsPIDOMWindowInner* aWindow) {
2045   AssertIsOnMainThread();
2046   MOZ_ASSERT(aWindow);
2047 
2048   nsTArray<WorkerPrivate*> workers;
2049   GetWorkersForWindow(aWindow, workers);
2050 
2051   for (uint32_t index = 0; index < workers.Length(); index++) {
2052     workers[index]->Thaw(aWindow);
2053   }
2054 }
2055 
SuspendWorkersForWindow(nsPIDOMWindowInner * aWindow)2056 void RuntimeService::SuspendWorkersForWindow(nsPIDOMWindowInner* aWindow) {
2057   AssertIsOnMainThread();
2058   MOZ_ASSERT(aWindow);
2059 
2060   nsTArray<WorkerPrivate*> workers;
2061   GetWorkersForWindow(aWindow, workers);
2062 
2063   for (uint32_t index = 0; index < workers.Length(); index++) {
2064     workers[index]->ParentWindowPaused();
2065   }
2066 }
2067 
ResumeWorkersForWindow(nsPIDOMWindowInner * aWindow)2068 void RuntimeService::ResumeWorkersForWindow(nsPIDOMWindowInner* aWindow) {
2069   AssertIsOnMainThread();
2070   MOZ_ASSERT(aWindow);
2071 
2072   nsTArray<WorkerPrivate*> workers;
2073   GetWorkersForWindow(aWindow, workers);
2074 
2075   for (uint32_t index = 0; index < workers.Length(); index++) {
2076     workers[index]->ParentWindowResumed();
2077   }
2078 }
2079 
CreateSharedWorker(const GlobalObject & aGlobal,const nsAString & aScriptURL,const nsAString & aName,SharedWorker ** aSharedWorker)2080 nsresult RuntimeService::CreateSharedWorker(const GlobalObject& aGlobal,
2081                                             const nsAString& aScriptURL,
2082                                             const nsAString& aName,
2083                                             SharedWorker** aSharedWorker) {
2084   AssertIsOnMainThread();
2085 
2086   nsCOMPtr<nsPIDOMWindowInner> window =
2087       do_QueryInterface(aGlobal.GetAsSupports());
2088   MOZ_ASSERT(window);
2089 
2090   // If the window is blocked from accessing storage, do not allow it
2091   // to connect to a SharedWorker.  This would potentially allow it
2092   // to communicate with other windows that do have storage access.
2093   // Allow private browsing, however, as we handle that isolation
2094   // via the principal.
2095   auto storageAllowed = nsContentUtils::StorageAllowedForWindow(window);
2096   if (storageAllowed != nsContentUtils::StorageAccess::eAllow &&
2097       storageAllowed != nsContentUtils::StorageAccess::ePrivateBrowsing) {
2098     return NS_ERROR_DOM_SECURITY_ERR;
2099   }
2100 
2101     // Assert that the principal private browsing state matches the
2102     // StorageAccess value.
2103 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
2104   if (storageAllowed == nsContentUtils::StorageAccess::ePrivateBrowsing) {
2105     nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
2106     nsCOMPtr<nsIPrincipal> principal = doc ? doc->NodePrincipal() : nullptr;
2107     uint32_t privateBrowsingId = 0;
2108     if (principal) {
2109       MOZ_ALWAYS_SUCCEEDS(principal->GetPrivateBrowsingId(&privateBrowsingId));
2110     }
2111     MOZ_DIAGNOSTIC_ASSERT(privateBrowsingId != 0);
2112   }
2113 #endif  // MOZ_DIAGNOSTIC_ASSERT_ENABLED
2114 
2115   JSContext* cx = aGlobal.Context();
2116 
2117   WorkerLoadInfo loadInfo;
2118   nsresult rv = WorkerPrivate::GetLoadInfo(
2119       cx, window, nullptr, aScriptURL, false, WorkerPrivate::OverrideLoadGroup,
2120       WorkerTypeShared, &loadInfo);
2121   NS_ENSURE_SUCCESS(rv, rv);
2122 
2123   return CreateSharedWorkerFromLoadInfo(cx, &loadInfo, aScriptURL, aName,
2124                                         aSharedWorker);
2125 }
2126 
CreateSharedWorkerFromLoadInfo(JSContext * aCx,WorkerLoadInfo * aLoadInfo,const nsAString & aScriptURL,const nsAString & aName,SharedWorker ** aSharedWorker)2127 nsresult RuntimeService::CreateSharedWorkerFromLoadInfo(
2128     JSContext* aCx, WorkerLoadInfo* aLoadInfo, const nsAString& aScriptURL,
2129     const nsAString& aName, SharedWorker** aSharedWorker) {
2130   AssertIsOnMainThread();
2131   MOZ_ASSERT(aLoadInfo);
2132   MOZ_ASSERT(aLoadInfo->mResolvedScriptURI);
2133 
2134   RefPtr<WorkerPrivate> workerPrivate;
2135   {
2136     MutexAutoLock lock(mMutex);
2137 
2138     nsCString scriptSpec;
2139     nsresult rv = aLoadInfo->mResolvedScriptURI->GetSpec(scriptSpec);
2140     NS_ENSURE_SUCCESS(rv, rv);
2141 
2142     MOZ_DIAGNOSTIC_ASSERT(aLoadInfo->mPrincipal &&
2143                           aLoadInfo->mLoadingPrincipal);
2144 
2145     WorkerDomainInfo* domainInfo;
2146     if (mDomainMap.Get(aLoadInfo->mDomain, &domainInfo)) {
2147       for (const UniquePtr<SharedWorkerInfo>& data :
2148            domainInfo->mSharedWorkerInfos) {
2149         if (data->mScriptSpec == scriptSpec && data->mName == aName &&
2150             // We want to be sure that the window's principal subsumes the
2151             // SharedWorker's loading principal and vice versa.
2152             aLoadInfo->mLoadingPrincipal->Subsumes(
2153                 data->mWorkerPrivate->GetLoadingPrincipal()) &&
2154             data->mWorkerPrivate->GetLoadingPrincipal()->Subsumes(
2155                 aLoadInfo->mLoadingPrincipal)) {
2156           workerPrivate = data->mWorkerPrivate;
2157           break;
2158         }
2159       }
2160     }
2161   }
2162 
2163   // Keep a reference to the window before spawning the worker. If the worker is
2164   // a Shared/Service worker and the worker script loads and executes before
2165   // the SharedWorker object itself is created before then WorkerScriptLoaded()
2166   // will reset the loadInfo's window.
2167   nsCOMPtr<nsPIDOMWindowInner> window = aLoadInfo->mWindow;
2168 
2169   // shouldAttachToWorkerPrivate tracks whether our SharedWorker should actually
2170   // get attached to the WorkerPrivate we're using.  It will become false if the
2171   // WorkerPrivate already exists and its secure context state doesn't match
2172   // what we want for the new SharedWorker.
2173   bool shouldAttachToWorkerPrivate = true;
2174   bool created = false;
2175   ErrorResult rv;
2176   if (!workerPrivate) {
2177     workerPrivate =
2178         WorkerPrivate::Constructor(aCx, aScriptURL, false, WorkerTypeShared,
2179                                    aName, VoidCString(), aLoadInfo, rv);
2180     NS_ENSURE_TRUE(workerPrivate, rv.StealNSResult());
2181 
2182     created = true;
2183   } else {
2184     // Check whether the secure context state matches.  The current compartment
2185     // of aCx is the compartment of the SharedWorker constructor that was
2186     // invoked, which is the compartment of the document that will be hooked up
2187     // to the worker, so that's what we want to check.
2188     shouldAttachToWorkerPrivate =
2189         workerPrivate->IsSecureContext() ==
2190         JS_GetIsSecureContext(js::GetContextCompartment(aCx));
2191 
2192     // If we're attaching to an existing SharedWorker private, then we
2193     // must update the overriden load group to account for our document's
2194     // load group.
2195     if (shouldAttachToWorkerPrivate) {
2196       workerPrivate->UpdateOverridenLoadGroup(aLoadInfo->mLoadGroup);
2197     }
2198   }
2199 
2200   // We don't actually care about this MessageChannel, but we use it to 'steal'
2201   // its 2 connected ports.
2202   RefPtr<MessageChannel> channel =
2203       MessageChannel::Constructor(window->AsGlobal(), rv);
2204   if (NS_WARN_IF(rv.Failed())) {
2205     return rv.StealNSResult();
2206   }
2207 
2208   RefPtr<SharedWorker> sharedWorker =
2209       new SharedWorker(window, workerPrivate, channel->Port1());
2210 
2211   if (!shouldAttachToWorkerPrivate) {
2212     // We're done here.  Just queue up our error event and return our
2213     // dead-on-arrival SharedWorker.
2214     RefPtr<AsyncEventDispatcher> errorEvent = new AsyncEventDispatcher(
2215         sharedWorker, NS_LITERAL_STRING("error"), false);
2216     errorEvent->PostDOMEvent();
2217     sharedWorker.forget(aSharedWorker);
2218     return NS_OK;
2219   }
2220 
2221   if (!workerPrivate->RegisterSharedWorker(sharedWorker, channel->Port2())) {
2222     NS_WARNING("Worker is unreachable, this shouldn't happen!");
2223     sharedWorker->Close();
2224     return NS_ERROR_FAILURE;
2225   }
2226 
2227   // This is normally handled in RegisterWorker, but that wasn't called if the
2228   // worker already existed.
2229   if (!created) {
2230     nsTArray<WorkerPrivate*>* windowArray;
2231     if (!mWindowMap.Get(window, &windowArray)) {
2232       windowArray = new nsTArray<WorkerPrivate*>(1);
2233       mWindowMap.Put(window, windowArray);
2234     }
2235 
2236     if (!windowArray->Contains(workerPrivate)) {
2237       windowArray->AppendElement(workerPrivate);
2238     }
2239   }
2240 
2241   sharedWorker.forget(aSharedWorker);
2242   return NS_OK;
2243 }
2244 
ForgetSharedWorker(WorkerPrivate * aWorkerPrivate)2245 void RuntimeService::ForgetSharedWorker(WorkerPrivate* aWorkerPrivate) {
2246   AssertIsOnMainThread();
2247   MOZ_ASSERT(aWorkerPrivate);
2248   MOZ_ASSERT(aWorkerPrivate->IsSharedWorker());
2249 
2250   MutexAutoLock lock(mMutex);
2251 
2252   WorkerDomainInfo* domainInfo;
2253   if (mDomainMap.Get(aWorkerPrivate->Domain(), &domainInfo)) {
2254     RemoveSharedWorker(domainInfo, aWorkerPrivate);
2255   }
2256 }
2257 
NoteIdleThread(WorkerThread * aThread)2258 void RuntimeService::NoteIdleThread(WorkerThread* aThread) {
2259   AssertIsOnMainThread();
2260   MOZ_ASSERT(aThread);
2261 
2262   bool shutdownThread = mShuttingDown;
2263   bool scheduleTimer = false;
2264 
2265   if (!shutdownThread) {
2266     static TimeDuration timeout =
2267         TimeDuration::FromSeconds(IDLE_THREAD_TIMEOUT_SEC);
2268 
2269     TimeStamp expirationTime = TimeStamp::NowLoRes() + timeout;
2270 
2271     MutexAutoLock lock(mMutex);
2272 
2273     uint32_t previousIdleCount = mIdleThreadArray.Length();
2274 
2275     if (previousIdleCount < MAX_IDLE_THREADS) {
2276       IdleThreadInfo* info = mIdleThreadArray.AppendElement();
2277       info->mThread = aThread;
2278       info->mExpirationTime = expirationTime;
2279 
2280       scheduleTimer = previousIdleCount == 0;
2281     } else {
2282       shutdownThread = true;
2283     }
2284   }
2285 
2286   MOZ_ASSERT_IF(shutdownThread, !scheduleTimer);
2287   MOZ_ASSERT_IF(scheduleTimer, !shutdownThread);
2288 
2289   // Too many idle threads, just shut this one down.
2290   if (shutdownThread) {
2291     MOZ_ALWAYS_SUCCEEDS(aThread->Shutdown());
2292   } else if (scheduleTimer) {
2293     MOZ_ALWAYS_SUCCEEDS(mIdleThreadTimer->InitWithNamedFuncCallback(
2294         ShutdownIdleThreads, nullptr, IDLE_THREAD_TIMEOUT_SEC * 1000,
2295         nsITimer::TYPE_ONE_SHOT, "RuntimeService::ShutdownIdleThreads"));
2296   }
2297 }
2298 
UpdateAllWorkerContextOptions()2299 void RuntimeService::UpdateAllWorkerContextOptions() {
2300   BROADCAST_ALL_WORKERS(UpdateContextOptions,
2301                         sDefaultJSSettings.contextOptions);
2302 }
2303 
UpdateAppNameOverridePreference(const nsAString & aValue)2304 void RuntimeService::UpdateAppNameOverridePreference(const nsAString& aValue) {
2305   AssertIsOnMainThread();
2306   mNavigatorProperties.mAppNameOverridden = aValue;
2307 }
2308 
UpdateAppVersionOverridePreference(const nsAString & aValue)2309 void RuntimeService::UpdateAppVersionOverridePreference(
2310     const nsAString& aValue) {
2311   AssertIsOnMainThread();
2312   mNavigatorProperties.mAppVersionOverridden = aValue;
2313 }
2314 
UpdatePlatformOverridePreference(const nsAString & aValue)2315 void RuntimeService::UpdatePlatformOverridePreference(const nsAString& aValue) {
2316   AssertIsOnMainThread();
2317   mNavigatorProperties.mPlatformOverridden = aValue;
2318 }
2319 
UpdateAllWorkerLanguages(const nsTArray<nsString> & aLanguages)2320 void RuntimeService::UpdateAllWorkerLanguages(
2321     const nsTArray<nsString>& aLanguages) {
2322   MOZ_ASSERT(NS_IsMainThread());
2323 
2324   mNavigatorProperties.mLanguages = aLanguages;
2325   BROADCAST_ALL_WORKERS(UpdateLanguages, aLanguages);
2326 }
2327 
UpdateAllWorkerMemoryParameter(JSGCParamKey aKey,uint32_t aValue)2328 void RuntimeService::UpdateAllWorkerMemoryParameter(JSGCParamKey aKey,
2329                                                     uint32_t aValue) {
2330   BROADCAST_ALL_WORKERS(UpdateJSWorkerMemoryParameter, aKey, aValue);
2331 }
2332 
2333 #ifdef JS_GC_ZEAL
UpdateAllWorkerGCZeal()2334 void RuntimeService::UpdateAllWorkerGCZeal() {
2335   BROADCAST_ALL_WORKERS(UpdateGCZeal, sDefaultJSSettings.gcZeal,
2336                         sDefaultJSSettings.gcZealFrequency);
2337 }
2338 #endif
2339 
GarbageCollectAllWorkers(bool aShrinking)2340 void RuntimeService::GarbageCollectAllWorkers(bool aShrinking) {
2341   BROADCAST_ALL_WORKERS(GarbageCollect, aShrinking);
2342 }
2343 
CycleCollectAllWorkers()2344 void RuntimeService::CycleCollectAllWorkers() {
2345   BROADCAST_ALL_WORKERS(CycleCollect, /* dummy = */ false);
2346 }
2347 
SendOfflineStatusChangeEventToAllWorkers(bool aIsOffline)2348 void RuntimeService::SendOfflineStatusChangeEventToAllWorkers(bool aIsOffline) {
2349   BROADCAST_ALL_WORKERS(OfflineStatusChangeEvent, aIsOffline);
2350 }
2351 
MemoryPressureAllWorkers()2352 void RuntimeService::MemoryPressureAllWorkers() {
2353   BROADCAST_ALL_WORKERS(MemoryPressure, /* dummy = */ false);
2354 }
2355 
ClampedHardwareConcurrency() const2356 uint32_t RuntimeService::ClampedHardwareConcurrency() const {
2357   // The Firefox Hardware Report says 70% of Firefox users have exactly 2 cores.
2358   // When the resistFingerprinting pref is set, we want to blend into the crowd
2359   // so spoof navigator.hardwareConcurrency = 2 to reduce user uniqueness.
2360   if (MOZ_UNLIKELY(nsContentUtils::ShouldResistFingerprinting())) {
2361     return 2;
2362   }
2363 
2364   // This needs to be atomic, because multiple workers, and even mainthread,
2365   // could race to initialize it at once.
2366   static Atomic<uint32_t> clampedHardwareConcurrency;
2367 
2368   // No need to loop here: if compareExchange fails, that just means that some
2369   // other worker has initialized numberOfProcessors, so we're good to go.
2370   if (!clampedHardwareConcurrency) {
2371     int32_t numberOfProcessors = PR_GetNumberOfProcessors();
2372     if (numberOfProcessors <= 0) {
2373       numberOfProcessors = 1;  // Must be one there somewhere
2374     }
2375     uint32_t clampedValue =
2376         std::min(uint32_t(numberOfProcessors), gMaxHardwareConcurrency);
2377     clampedHardwareConcurrency.compareExchange(0, clampedValue);
2378   }
2379 
2380   return clampedHardwareConcurrency;
2381 }
2382 
2383 // nsISupports
NS_IMPL_ISUPPORTS(RuntimeService,nsIObserver)2384 NS_IMPL_ISUPPORTS(RuntimeService, nsIObserver)
2385 
2386 // nsIObserver
2387 NS_IMETHODIMP
2388 RuntimeService::Observe(nsISupports* aSubject, const char* aTopic,
2389                         const char16_t* aData) {
2390   AssertIsOnMainThread();
2391 
2392   if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
2393     Shutdown();
2394     return NS_OK;
2395   }
2396   if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID)) {
2397     Cleanup();
2398     return NS_OK;
2399   }
2400   if (!strcmp(aTopic, GC_REQUEST_OBSERVER_TOPIC)) {
2401     GarbageCollectAllWorkers(/* shrinking = */ false);
2402     return NS_OK;
2403   }
2404   if (!strcmp(aTopic, CC_REQUEST_OBSERVER_TOPIC)) {
2405     CycleCollectAllWorkers();
2406     return NS_OK;
2407   }
2408   if (!strcmp(aTopic, MEMORY_PRESSURE_OBSERVER_TOPIC)) {
2409     GarbageCollectAllWorkers(/* shrinking = */ true);
2410     CycleCollectAllWorkers();
2411     MemoryPressureAllWorkers();
2412     return NS_OK;
2413   }
2414   if (!strcmp(aTopic, NS_IOSERVICE_OFFLINE_STATUS_TOPIC)) {
2415     SendOfflineStatusChangeEventToAllWorkers(NS_IsOffline());
2416     return NS_OK;
2417   }
2418 
2419   NS_NOTREACHED("Unknown observer topic!");
2420   return NS_OK;
2421 }
2422 
MainThreadRun()2423 bool LogViolationDetailsRunnable::MainThreadRun() {
2424   AssertIsOnMainThread();
2425 
2426   nsIContentSecurityPolicy* csp = mWorkerPrivate->GetCSP();
2427   if (csp) {
2428     NS_NAMED_LITERAL_STRING(
2429         scriptSample, "Call to eval() or related function blocked by CSP.");
2430     if (mWorkerPrivate->GetReportCSPViolations()) {
2431       csp->LogViolationDetails(nsIContentSecurityPolicy::VIOLATION_TYPE_EVAL,
2432                                mFileName, scriptSample, mLineNum, EmptyString(),
2433                                EmptyString());
2434     }
2435   }
2436 
2437   return true;
2438 }
2439 
2440 NS_IMETHODIMP
Run()2441 WorkerThreadPrimaryRunnable::Run() {
2442   AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING(
2443       "WorkerThreadPrimaryRunnable::Run", OTHER, mWorkerPrivate->ScriptURL());
2444 
2445   using mozilla::ipc::BackgroundChild;
2446 
2447   // Note: GetOrCreateForCurrentThread() must be called prior to
2448   //       mWorkerPrivate->SetThread() in order to avoid accidentally consuming
2449   //       worker messages here.
2450   if (NS_WARN_IF(!BackgroundChild::GetOrCreateForCurrentThread())) {
2451     // XXX need to fire an error at parent.
2452     // Failed in creating BackgroundChild: probably in shutdown. Continue to run
2453     // without BackgroundChild created.
2454   }
2455 
2456   class MOZ_STACK_CLASS SetThreadHelper final {
2457     // Raw pointer: this class is on the stack.
2458     WorkerPrivate* mWorkerPrivate;
2459     RefPtr<AbstractThread> mAbstractThread;
2460 
2461    public:
2462     SetThreadHelper(WorkerPrivate* aWorkerPrivate, WorkerThread* aThread)
2463         : mWorkerPrivate(aWorkerPrivate),
2464           mAbstractThread(AbstractThread::CreateXPCOMThreadWrapper(
2465               NS_GetCurrentThread(), false)) {
2466       MOZ_ASSERT(aWorkerPrivate);
2467       MOZ_ASSERT(aThread);
2468 
2469       mWorkerPrivate->SetThread(aThread);
2470     }
2471 
2472     ~SetThreadHelper() {
2473       if (mWorkerPrivate) {
2474         mWorkerPrivate->SetThread(nullptr);
2475       }
2476     }
2477 
2478     void Nullify() {
2479       MOZ_ASSERT(mWorkerPrivate);
2480       mWorkerPrivate->SetThread(nullptr);
2481       mWorkerPrivate = nullptr;
2482     }
2483   };
2484 
2485   SetThreadHelper threadHelper(mWorkerPrivate, mThread);
2486 
2487   mWorkerPrivate->AssertIsOnWorkerThread();
2488 
2489   {
2490     nsCycleCollector_startup();
2491 
2492     WorkerJSContext context(mWorkerPrivate);
2493     nsresult rv = context.Initialize(mParentRuntime);
2494     if (NS_WARN_IF(NS_FAILED(rv))) {
2495       return rv;
2496     }
2497 
2498     JSContext* cx = context.Context();
2499 
2500     if (!InitJSContextForWorker(mWorkerPrivate, cx)) {
2501       // XXX need to fire an error at parent.
2502       NS_ERROR("Failed to create context!");
2503       return NS_ERROR_FAILURE;
2504     }
2505 
2506     {
2507       PROFILER_SET_JS_CONTEXT(cx);
2508 
2509       {
2510         JSAutoRequest ar(cx);
2511 
2512         mWorkerPrivate->DoRunLoop(cx);
2513         // The AutoJSAPI in DoRunLoop should have reported any exceptions left
2514         // on cx.  Note that we still need the JSAutoRequest above because
2515         // AutoJSAPI on workers does NOT enter a request!
2516         MOZ_ASSERT(!JS_IsExceptionPending(cx));
2517       }
2518 
2519       BackgroundChild::CloseForCurrentThread();
2520 
2521       PROFILER_CLEAR_JS_CONTEXT();
2522     }
2523 
2524     // There may still be runnables on the debugger event queue that hold a
2525     // strong reference to the debugger global scope. These runnables are not
2526     // visible to the cycle collector, so we need to make sure to clear the
2527     // debugger event queue before we try to destroy the context. If we don't,
2528     // the garbage collector will crash.
2529     mWorkerPrivate->ClearDebuggerEventQueue();
2530 
2531     // Perform a full GC. This will collect the main worker global and CC,
2532     // which should break all cycles that touch JS.
2533     JS_GC(cx);
2534 
2535     // Before shutting down the cycle collector we need to do one more pass
2536     // through the event loop to clean up any C++ objects that need deferred
2537     // cleanup.
2538     mWorkerPrivate->ClearMainEventQueue(WorkerPrivate::WorkerRan);
2539 
2540     // Now WorkerJSContext goes out of scope and its destructor will shut
2541     // down the cycle collector. This breaks any remaining cycles and collects
2542     // any remaining C++ objects.
2543   }
2544 
2545   threadHelper.Nullify();
2546 
2547   mWorkerPrivate->ScheduleDeletion(WorkerPrivate::WorkerRan);
2548 
2549   // It is no longer safe to touch mWorkerPrivate.
2550   mWorkerPrivate = nullptr;
2551 
2552   // Now recycle this thread.
2553   nsCOMPtr<nsIEventTarget> mainTarget = GetMainThreadEventTarget();
2554   MOZ_ASSERT(mainTarget);
2555 
2556   RefPtr<FinishedRunnable> finishedRunnable =
2557       new FinishedRunnable(mThread.forget());
2558   MOZ_ALWAYS_SUCCEEDS(
2559       mainTarget->Dispatch(finishedRunnable, NS_DISPATCH_NORMAL));
2560 
2561   return NS_OK;
2562 }
2563 
2564 NS_IMETHODIMP
Run()2565 WorkerThreadPrimaryRunnable::FinishedRunnable::Run() {
2566   AssertIsOnMainThread();
2567 
2568   RefPtr<WorkerThread> thread;
2569   mThread.swap(thread);
2570 
2571   RuntimeService* rts = RuntimeService::GetService();
2572   if (rts) {
2573     rts->NoteIdleThread(thread);
2574   } else if (thread->ShutdownRequired()) {
2575     MOZ_ALWAYS_SUCCEEDS(thread->Shutdown());
2576   }
2577 
2578   return NS_OK;
2579 }
2580 
2581 }  // workerinternals namespace
2582 
CancelWorkersForWindow(nsPIDOMWindowInner * aWindow)2583 void CancelWorkersForWindow(nsPIDOMWindowInner* aWindow) {
2584   AssertIsOnMainThread();
2585   RuntimeService* runtime = RuntimeService::GetService();
2586   if (runtime) {
2587     runtime->CancelWorkersForWindow(aWindow);
2588   }
2589 }
2590 
FreezeWorkersForWindow(nsPIDOMWindowInner * aWindow)2591 void FreezeWorkersForWindow(nsPIDOMWindowInner* aWindow) {
2592   AssertIsOnMainThread();
2593   RuntimeService* runtime = RuntimeService::GetService();
2594   if (runtime) {
2595     runtime->FreezeWorkersForWindow(aWindow);
2596   }
2597 }
2598 
ThawWorkersForWindow(nsPIDOMWindowInner * aWindow)2599 void ThawWorkersForWindow(nsPIDOMWindowInner* aWindow) {
2600   AssertIsOnMainThread();
2601   RuntimeService* runtime = RuntimeService::GetService();
2602   if (runtime) {
2603     runtime->ThawWorkersForWindow(aWindow);
2604   }
2605 }
2606 
SuspendWorkersForWindow(nsPIDOMWindowInner * aWindow)2607 void SuspendWorkersForWindow(nsPIDOMWindowInner* aWindow) {
2608   AssertIsOnMainThread();
2609   RuntimeService* runtime = RuntimeService::GetService();
2610   if (runtime) {
2611     runtime->SuspendWorkersForWindow(aWindow);
2612   }
2613 }
2614 
ResumeWorkersForWindow(nsPIDOMWindowInner * aWindow)2615 void ResumeWorkersForWindow(nsPIDOMWindowInner* aWindow) {
2616   AssertIsOnMainThread();
2617   RuntimeService* runtime = RuntimeService::GetService();
2618   if (runtime) {
2619     runtime->ResumeWorkersForWindow(aWindow);
2620   }
2621 }
2622 
GetWorkerPrivateFromContext(JSContext * aCx)2623 WorkerPrivate* GetWorkerPrivateFromContext(JSContext* aCx) {
2624   MOZ_ASSERT(!NS_IsMainThread());
2625   MOZ_ASSERT(aCx);
2626 
2627   void* cxPrivate = JS_GetContextPrivate(aCx);
2628   if (!cxPrivate) {
2629     return nullptr;
2630   }
2631 
2632   return static_cast<WorkerThreadContextPrivate*>(cxPrivate)
2633       ->GetWorkerPrivate();
2634 }
2635 
GetCurrentThreadWorkerPrivate()2636 WorkerPrivate* GetCurrentThreadWorkerPrivate() {
2637   MOZ_ASSERT(!NS_IsMainThread());
2638 
2639   CycleCollectedJSContext* ccjscx = CycleCollectedJSContext::Get();
2640   if (!ccjscx) {
2641     return nullptr;
2642   }
2643 
2644   JSContext* cx = ccjscx->Context();
2645   MOZ_ASSERT(cx);
2646 
2647   // Note that we can return nullptr if the nsCycleCollector_shutdown() in
2648   // ~WorkerJSContext() triggers any calls to GetCurrentThreadWorkerPrivate().
2649   // At this stage CycleCollectedJSContext::Get() will still return a context,
2650   // but the context private has already been cleared.
2651   return GetWorkerPrivateFromContext(cx);
2652 }
2653 
IsCurrentThreadRunningChromeWorker()2654 bool IsCurrentThreadRunningChromeWorker() {
2655   return GetCurrentThreadWorkerPrivate()->UsesSystemPrincipal();
2656 }
2657 
GetCurrentWorkerThreadJSContext()2658 JSContext* GetCurrentWorkerThreadJSContext() {
2659   WorkerPrivate* wp = GetCurrentThreadWorkerPrivate();
2660   if (!wp) {
2661     return nullptr;
2662   }
2663   return wp->GetJSContext();
2664 }
2665 
GetCurrentThreadWorkerGlobal()2666 JSObject* GetCurrentThreadWorkerGlobal() {
2667   WorkerPrivate* wp = GetCurrentThreadWorkerPrivate();
2668   if (!wp) {
2669     return nullptr;
2670   }
2671   WorkerGlobalScope* scope = wp->GlobalScope();
2672   if (!scope) {
2673     return nullptr;
2674   }
2675   return scope->GetGlobalJSObject();
2676 }
2677 
2678 }  // namespace dom
2679 }  // namespace mozilla
2680