Bug 1546544 - Reduce navigator.hardwareConcurrency to account for TCSM r=luke
[gecko.git] / dom / workers / RuntimeService.cpp
blob23dbc46c45ea1c5532b03ceaa345d5edfce33399
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/. */
7 #include "RuntimeService.h"
9 #include "nsAutoPtr.h"
10 #include "nsIChannel.h"
11 #include "nsIContentSecurityPolicy.h"
12 #include "nsICookieService.h"
13 #include "mozilla/dom/Document.h"
14 #include "nsIDOMChromeWindow.h"
15 #include "nsIEffectiveTLDService.h"
16 #include "nsIObserverService.h"
17 #include "nsIPrincipal.h"
18 #include "nsIScriptContext.h"
19 #include "nsIScriptError.h"
20 #include "nsIScriptSecurityManager.h"
21 #include "nsIStreamTransportService.h"
22 #include "nsISupportsPriority.h"
23 #include "nsITimer.h"
24 #include "nsIURI.h"
25 #include "nsIXULRuntime.h"
26 #include "nsPIDOMWindow.h"
28 #include <algorithm>
29 #include "mozilla/ipc/BackgroundChild.h"
30 #include "GeckoProfiler.h"
31 #include "jsfriendapi.h"
32 #include "js/ContextOptions.h"
33 #include "js/LocaleSensitive.h"
34 #include "mozilla/AbstractThread.h"
35 #include "mozilla/AntiTrackingCommon.h"
36 #include "mozilla/ArrayUtils.h"
37 #include "mozilla/Atomics.h"
38 #include "mozilla/Attributes.h"
39 #include "mozilla/CycleCollectedJSContext.h"
40 #include "mozilla/CycleCollectedJSRuntime.h"
41 #include "mozilla/Telemetry.h"
42 #include "mozilla/TimeStamp.h"
43 #include "mozilla/dom/AtomList.h"
44 #include "mozilla/dom/BindingUtils.h"
45 #include "mozilla/dom/ErrorEventBinding.h"
46 #include "mozilla/dom/EventTargetBinding.h"
47 #include "mozilla/dom/FetchUtil.h"
48 #include "mozilla/dom/MessageChannel.h"
49 #include "mozilla/dom/MessageEventBinding.h"
50 #include "mozilla/dom/PerformanceService.h"
51 #include "mozilla/dom/RemoteWorkerChild.h"
52 #include "mozilla/dom/WorkerBinding.h"
53 #include "mozilla/dom/ScriptSettings.h"
54 #include "mozilla/dom/IndexedDatabaseManager.h"
55 #include "mozilla/ipc/BackgroundChild.h"
56 #include "mozilla/DebugOnly.h"
57 #include "mozilla/Preferences.h"
58 #include "mozilla/dom/Navigator.h"
59 #include "mozilla/Monitor.h"
60 #include "mozilla/StaticPrefs.h"
61 #include "nsContentUtils.h"
62 #include "nsCycleCollector.h"
63 #include "nsDOMJSUtils.h"
64 #include "nsISupportsImpl.h"
65 #include "nsLayoutStatics.h"
66 #include "nsNetUtil.h"
67 #include "nsServiceManagerUtils.h"
68 #include "nsThreadUtils.h"
69 #include "nsXPCOM.h"
70 #include "nsXPCOMPrivate.h"
71 #include "OSFileConstants.h"
72 #include "xpcpublic.h"
74 #if defined(XP_MACOSX)
75 # include "nsMacUtilsImpl.h"
76 #endif
78 #include "Principal.h"
79 #include "WorkerDebuggerManager.h"
80 #include "WorkerError.h"
81 #include "WorkerLoadInfo.h"
82 #include "WorkerPrivate.h"
83 #include "WorkerRunnable.h"
84 #include "WorkerScope.h"
85 #include "WorkerThread.h"
86 #include "prsystem.h"
88 #define WORKERS_SHUTDOWN_TOPIC "web-workers-shutdown"
90 namespace mozilla {
92 using namespace ipc;
94 namespace dom {
96 using namespace workerinternals;
98 namespace workerinternals {
100 // The size of the worker runtime heaps in bytes. May be changed via pref.
101 #define WORKER_DEFAULT_RUNTIME_HEAPSIZE 32 * 1024 * 1024
103 // The size of the generational GC nursery for workers, in bytes.
104 #define WORKER_DEFAULT_NURSERY_SIZE 1 * 1024 * 1024
106 // The size of the worker JS allocation threshold in MB. May be changed via
107 // pref.
108 #define WORKER_DEFAULT_ALLOCATION_THRESHOLD 30
110 // Half the size of the actual C stack, to be safe.
111 #define WORKER_CONTEXT_NATIVE_STACK_LIMIT 128 * sizeof(size_t) * 1024
113 // The maximum number of hardware concurrency, overridable via pref.
114 #define MAX_HARDWARE_CONCURRENCY 8
116 // The maximum number of threads to use for workers, overridable via pref.
117 #define MAX_WORKERS_PER_DOMAIN 512
119 static_assert(MAX_WORKERS_PER_DOMAIN >= 1,
120 "We should allow at least one worker per domain.");
122 // The default number of seconds that close handlers will be allowed to run for
123 // content workers.
124 #define MAX_SCRIPT_RUN_TIME_SEC 10
126 // The number of seconds that idle threads can hang around before being killed.
127 #define IDLE_THREAD_TIMEOUT_SEC 30
129 // The maximum number of threads that can be idle at one time.
130 #define MAX_IDLE_THREADS 20
132 #define PREF_WORKERS_PREFIX "dom.workers."
133 #define PREF_WORKERS_MAX_PER_DOMAIN PREF_WORKERS_PREFIX "maxPerDomain"
134 #define PREF_WORKERS_MAX_HARDWARE_CONCURRENCY "dom.maxHardwareConcurrency"
136 #define PREF_MAX_SCRIPT_RUN_TIME_CONTENT "dom.max_script_run_time"
137 #define PREF_MAX_SCRIPT_RUN_TIME_CHROME "dom.max_chrome_script_run_time"
139 #define GC_REQUEST_OBSERVER_TOPIC "child-gc-request"
140 #define CC_REQUEST_OBSERVER_TOPIC "child-cc-request"
141 #define MEMORY_PRESSURE_OBSERVER_TOPIC "memory-pressure"
143 #define BROADCAST_ALL_WORKERS(_func, ...) \
144 PR_BEGIN_MACRO \
145 AssertIsOnMainThread(); \
147 AutoTArray<WorkerPrivate*, 100> workers; \
149 MutexAutoLock lock(mMutex); \
151 AddAllTopLevelWorkersToArray(workers); \
154 if (!workers.IsEmpty()) { \
155 for (uint32_t index = 0; index < workers.Length(); index++) { \
156 workers[index]->_func(__VA_ARGS__); \
159 PR_END_MACRO
161 // Prefixes for observing preference changes.
162 #define PREF_JS_OPTIONS_PREFIX "javascript.options."
163 #define PREF_WORKERS_OPTIONS_PREFIX PREF_WORKERS_PREFIX "options."
164 #define PREF_MEM_OPTIONS_PREFIX "mem."
165 #define PREF_GCZEAL "gcZeal"
167 static NS_DEFINE_CID(kStreamTransportServiceCID, NS_STREAMTRANSPORTSERVICE_CID);
169 namespace {
171 const uint32_t kNoIndex = uint32_t(-1);
173 uint32_t gMaxWorkersPerDomain = MAX_WORKERS_PER_DOMAIN;
174 uint32_t gMaxHardwareConcurrency = MAX_HARDWARE_CONCURRENCY;
176 // Does not hold an owning reference.
177 RuntimeService* gRuntimeService = nullptr;
179 // Only true during the call to Init.
180 bool gRuntimeServiceDuringInit = false;
182 class LiteralRebindingCString : public nsDependentCString {
183 public:
184 template <int N>
185 void RebindLiteral(const char (&aStr)[N]) {
186 Rebind(aStr, N - 1);
190 template <typename T>
191 struct PrefTraits;
193 template <>
194 struct PrefTraits<bool> {
195 typedef bool PrefValueType;
197 static const PrefValueType kDefaultValue = false;
199 static inline PrefValueType Get(const char* aPref) {
200 AssertIsOnMainThread();
201 return Preferences::GetBool(aPref);
204 static inline bool Exists(const char* aPref) {
205 AssertIsOnMainThread();
206 return Preferences::GetType(aPref) == nsIPrefBranch::PREF_BOOL;
210 template <>
211 struct PrefTraits<int32_t> {
212 typedef int32_t PrefValueType;
214 static inline PrefValueType Get(const char* aPref) {
215 AssertIsOnMainThread();
216 return Preferences::GetInt(aPref);
219 static inline bool Exists(const char* aPref) {
220 AssertIsOnMainThread();
221 return Preferences::GetType(aPref) == nsIPrefBranch::PREF_INT;
225 template <typename T>
226 T GetWorkerPref(const nsACString& aPref,
227 const T aDefault = PrefTraits<T>::kDefaultValue) {
228 AssertIsOnMainThread();
230 typedef PrefTraits<T> PrefHelper;
232 T result;
234 nsAutoCString prefName;
235 prefName.AssignLiteral(PREF_WORKERS_OPTIONS_PREFIX);
236 prefName.Append(aPref);
238 if (PrefHelper::Exists(prefName.get())) {
239 result = PrefHelper::Get(prefName.get());
240 } else {
241 prefName.AssignLiteral(PREF_JS_OPTIONS_PREFIX);
242 prefName.Append(aPref);
244 if (PrefHelper::Exists(prefName.get())) {
245 result = PrefHelper::Get(prefName.get());
246 } else {
247 result = aDefault;
251 return result;
254 void LoadContextOptions(const char* aPrefName, void* /* aClosure */) {
255 AssertIsOnMainThread();
257 RuntimeService* rts = RuntimeService::GetService();
258 if (!rts) {
259 // May be shutting down, just bail.
260 return;
263 const nsDependentCString prefName(aPrefName);
265 // Several other pref branches will get included here so bail out if there is
266 // another callback that will handle this change.
267 if (StringBeginsWith(
268 prefName,
269 NS_LITERAL_CSTRING(PREF_JS_OPTIONS_PREFIX PREF_MEM_OPTIONS_PREFIX)) ||
270 StringBeginsWith(
271 prefName, NS_LITERAL_CSTRING(
272 PREF_WORKERS_OPTIONS_PREFIX PREF_MEM_OPTIONS_PREFIX))) {
273 return;
276 #ifdef JS_GC_ZEAL
277 if (prefName.EqualsLiteral(PREF_JS_OPTIONS_PREFIX PREF_GCZEAL) ||
278 prefName.EqualsLiteral(PREF_WORKERS_OPTIONS_PREFIX PREF_GCZEAL)) {
279 return;
281 #endif
283 // Context options.
284 JS::ContextOptions contextOptions;
285 contextOptions.setAsmJS(GetWorkerPref<bool>(NS_LITERAL_CSTRING("asmjs")))
286 .setWasm(GetWorkerPref<bool>(NS_LITERAL_CSTRING("wasm")))
287 .setWasmBaseline(
288 GetWorkerPref<bool>(NS_LITERAL_CSTRING("wasm_baselinejit")))
289 .setWasmIon(GetWorkerPref<bool>(NS_LITERAL_CSTRING("wasm_ionjit")))
290 #ifdef ENABLE_WASM_CRANELIFT
291 .setWasmCranelift(
292 GetWorkerPref<bool>(NS_LITERAL_CSTRING("wasm_cranelift")))
293 #endif
294 #ifdef ENABLE_WASM_REFTYPES
295 .setWasmGc(GetWorkerPref<bool>(NS_LITERAL_CSTRING("wasm_gc")))
296 #endif
297 .setWasmVerbose(GetWorkerPref<bool>(NS_LITERAL_CSTRING("wasm_verbose")))
298 .setThrowOnAsmJSValidationFailure(GetWorkerPref<bool>(
299 NS_LITERAL_CSTRING("throw_on_asmjs_validation_failure")))
300 .setBaseline(GetWorkerPref<bool>(NS_LITERAL_CSTRING("baselinejit")))
301 .setIon(GetWorkerPref<bool>(NS_LITERAL_CSTRING("ion")))
302 .setNativeRegExp(GetWorkerPref<bool>(NS_LITERAL_CSTRING("native_regexp")))
303 .setAsyncStack(GetWorkerPref<bool>(NS_LITERAL_CSTRING("asyncstack")))
304 .setWerror(GetWorkerPref<bool>(NS_LITERAL_CSTRING("werror")))
305 #ifdef FUZZING
306 .setFuzzing(GetWorkerPref<bool>(NS_LITERAL_CSTRING("fuzzing.enabled")))
307 #endif
308 .setExtraWarnings(GetWorkerPref<bool>(NS_LITERAL_CSTRING("strict")));
310 nsCOMPtr<nsIXULRuntime> xr = do_GetService("@mozilla.org/xre/runtime;1");
311 if (xr) {
312 bool safeMode = false;
313 xr->GetInSafeMode(&safeMode);
314 if (safeMode) {
315 contextOptions.disableOptionsForSafeMode();
319 RuntimeService::SetDefaultContextOptions(contextOptions);
321 if (rts) {
322 rts->UpdateAllWorkerContextOptions();
326 #ifdef JS_GC_ZEAL
327 void LoadGCZealOptions(const char* /* aPrefName */, void* /* aClosure */) {
328 AssertIsOnMainThread();
330 RuntimeService* rts = RuntimeService::GetService();
331 if (!rts) {
332 // May be shutting down, just bail.
333 return;
336 int32_t gczeal = GetWorkerPref<int32_t>(NS_LITERAL_CSTRING(PREF_GCZEAL), -1);
337 if (gczeal < 0) {
338 gczeal = 0;
341 int32_t frequency =
342 GetWorkerPref<int32_t>(NS_LITERAL_CSTRING("gcZeal.frequency"), -1);
343 if (frequency < 0) {
344 frequency = JS_DEFAULT_ZEAL_FREQ;
347 RuntimeService::SetDefaultGCZeal(uint8_t(gczeal), uint32_t(frequency));
349 if (rts) {
350 rts->UpdateAllWorkerGCZeal();
353 #endif
355 void UpdateCommonJSGCMemoryOption(RuntimeService* aRuntimeService,
356 const nsACString& aPrefName,
357 JSGCParamKey aKey) {
358 AssertIsOnMainThread();
359 NS_ASSERTION(!aPrefName.IsEmpty(), "Empty pref name!");
361 int32_t prefValue = GetWorkerPref(aPrefName, -1);
362 uint32_t value =
363 (prefValue < 0 || prefValue >= 10000) ? 0 : uint32_t(prefValue);
365 RuntimeService::SetDefaultJSGCSettings(aKey, value);
367 if (aRuntimeService) {
368 aRuntimeService->UpdateAllWorkerMemoryParameter(aKey, value);
372 void UpdateOtherJSGCMemoryOption(RuntimeService* aRuntimeService,
373 JSGCParamKey aKey, uint32_t aValue) {
374 AssertIsOnMainThread();
376 RuntimeService::SetDefaultJSGCSettings(aKey, aValue);
378 if (aRuntimeService) {
379 aRuntimeService->UpdateAllWorkerMemoryParameter(aKey, aValue);
383 void LoadJSGCMemoryOptions(const char* aPrefName, void* /* aClosure */) {
384 AssertIsOnMainThread();
386 RuntimeService* rts = RuntimeService::GetService();
388 if (!rts) {
389 // May be shutting down, just bail.
390 return;
393 NS_NAMED_LITERAL_CSTRING(jsPrefix, PREF_JS_OPTIONS_PREFIX);
394 NS_NAMED_LITERAL_CSTRING(workersPrefix, PREF_WORKERS_OPTIONS_PREFIX);
396 const nsDependentCString fullPrefName(aPrefName);
398 // Pull out the string that actually distinguishes the parameter we need to
399 // change.
400 nsDependentCSubstring memPrefName;
401 if (StringBeginsWith(fullPrefName, jsPrefix)) {
402 memPrefName.Rebind(fullPrefName, jsPrefix.Length());
403 } else if (StringBeginsWith(fullPrefName, workersPrefix)) {
404 memPrefName.Rebind(fullPrefName, workersPrefix.Length());
405 } else {
406 NS_ERROR("Unknown pref name!");
407 return;
410 #ifdef DEBUG
411 // During Init() we get called back with a branch string here, so there should
412 // be no just a "mem." pref here.
413 if (!rts) {
414 NS_ASSERTION(memPrefName.EqualsLiteral(PREF_MEM_OPTIONS_PREFIX), "Huh?!");
416 #endif
418 // If we're running in Init() then do this for every pref we care about.
419 // Otherwise we just want to update the parameter that changed.
420 for (uint32_t index = !gRuntimeServiceDuringInit
421 ? JSSettings::kGCSettingsArraySize - 1
422 : 0;
423 index < JSSettings::kGCSettingsArraySize; index++) {
424 LiteralRebindingCString matchName;
426 matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX "max");
427 if (memPrefName == matchName || (gRuntimeServiceDuringInit && index == 0)) {
428 int32_t prefValue = GetWorkerPref(matchName, -1);
429 uint32_t value = (prefValue <= 0 || prefValue >= 0x1000)
430 ? uint32_t(-1)
431 : uint32_t(prefValue) * 1024 * 1024;
432 UpdateOtherJSGCMemoryOption(rts, JSGC_MAX_BYTES, value);
433 continue;
436 matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX "high_water_mark");
437 if (memPrefName == matchName || (gRuntimeServiceDuringInit && index == 1)) {
438 int32_t prefValue = GetWorkerPref(matchName, 128);
439 UpdateOtherJSGCMemoryOption(rts, JSGC_MAX_MALLOC_BYTES,
440 uint32_t(prefValue) * 1024 * 1024);
441 continue;
444 matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX
445 "gc_high_frequency_time_limit_ms");
446 if (memPrefName == matchName || (gRuntimeServiceDuringInit && index == 2)) {
447 UpdateCommonJSGCMemoryOption(rts, matchName,
448 JSGC_HIGH_FREQUENCY_TIME_LIMIT);
449 continue;
452 matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX
453 "gc_low_frequency_heap_growth");
454 if (memPrefName == matchName || (gRuntimeServiceDuringInit && index == 3)) {
455 UpdateCommonJSGCMemoryOption(rts, matchName,
456 JSGC_LOW_FREQUENCY_HEAP_GROWTH);
457 continue;
460 matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX
461 "gc_high_frequency_heap_growth_min");
462 if (memPrefName == matchName || (gRuntimeServiceDuringInit && index == 4)) {
463 UpdateCommonJSGCMemoryOption(rts, matchName,
464 JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MIN);
465 continue;
468 matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX
469 "gc_high_frequency_heap_growth_max");
470 if (memPrefName == matchName || (gRuntimeServiceDuringInit && index == 5)) {
471 UpdateCommonJSGCMemoryOption(rts, matchName,
472 JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MAX);
473 continue;
476 matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX
477 "gc_high_frequency_low_limit_mb");
478 if (memPrefName == matchName || (gRuntimeServiceDuringInit && index == 6)) {
479 UpdateCommonJSGCMemoryOption(rts, matchName,
480 JSGC_HIGH_FREQUENCY_LOW_LIMIT);
481 continue;
484 matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX
485 "gc_high_frequency_high_limit_mb");
486 if (memPrefName == matchName || (gRuntimeServiceDuringInit && index == 7)) {
487 UpdateCommonJSGCMemoryOption(rts, matchName,
488 JSGC_HIGH_FREQUENCY_HIGH_LIMIT);
489 continue;
492 matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX
493 "gc_allocation_threshold_mb");
494 if (memPrefName == matchName || (gRuntimeServiceDuringInit && index == 8)) {
495 UpdateCommonJSGCMemoryOption(rts, matchName, JSGC_ALLOCATION_THRESHOLD);
496 continue;
499 matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX "gc_incremental_slice_ms");
500 if (memPrefName == matchName || (gRuntimeServiceDuringInit && index == 9)) {
501 int32_t prefValue = GetWorkerPref(matchName, -1);
502 uint32_t value =
503 (prefValue <= 0 || prefValue >= 100000) ? 0 : uint32_t(prefValue);
504 UpdateOtherJSGCMemoryOption(rts, JSGC_SLICE_TIME_BUDGET, value);
505 continue;
508 matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX "gc_dynamic_heap_growth");
509 if (memPrefName == matchName ||
510 (gRuntimeServiceDuringInit && index == 10)) {
511 bool prefValue = GetWorkerPref(matchName, false);
512 UpdateOtherJSGCMemoryOption(rts, JSGC_DYNAMIC_HEAP_GROWTH,
513 prefValue ? 0 : 1);
514 continue;
517 matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX "gc_dynamic_mark_slice");
518 if (memPrefName == matchName ||
519 (gRuntimeServiceDuringInit && index == 11)) {
520 bool prefValue = GetWorkerPref(matchName, false);
521 UpdateOtherJSGCMemoryOption(rts, JSGC_DYNAMIC_MARK_SLICE,
522 prefValue ? 0 : 1);
523 continue;
526 matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX "gc_min_empty_chunk_count");
527 if (memPrefName == matchName ||
528 (gRuntimeServiceDuringInit && index == 12)) {
529 UpdateCommonJSGCMemoryOption(rts, matchName, JSGC_MIN_EMPTY_CHUNK_COUNT);
530 continue;
533 matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX "gc_max_empty_chunk_count");
534 if (memPrefName == matchName ||
535 (gRuntimeServiceDuringInit && index == 13)) {
536 UpdateCommonJSGCMemoryOption(rts, matchName, JSGC_MAX_EMPTY_CHUNK_COUNT);
537 continue;
540 matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX "gc_compacting");
541 if (memPrefName == matchName ||
542 (gRuntimeServiceDuringInit && index == 14)) {
543 bool prefValue = GetWorkerPref(matchName, false);
544 UpdateOtherJSGCMemoryOption(rts, JSGC_COMPACTING_ENABLED,
545 prefValue ? 0 : 1);
546 continue;
549 #ifdef DEBUG
550 nsAutoCString message("Workers don't support the 'mem.");
551 message.Append(memPrefName);
552 message.AppendLiteral("' preference!");
553 NS_WARNING(message.get());
554 #endif
558 bool InterruptCallback(JSContext* aCx) {
559 WorkerPrivate* worker = GetWorkerPrivateFromContext(aCx);
560 MOZ_ASSERT(worker);
562 // As with the main thread, the interrupt callback is triggered
563 // non-deterministically when recording/replaying, so return early to avoid
564 // performing any recorded events.
565 if (recordreplay::IsRecordingOrReplaying()) {
566 return true;
569 // Now is a good time to turn on profiling if it's pending.
570 PROFILER_JS_INTERRUPT_CALLBACK();
572 return worker->InterruptCallback(aCx);
575 class LogViolationDetailsRunnable final : public WorkerMainThreadRunnable {
576 nsString mFileName;
577 uint32_t mLineNum;
578 uint32_t mColumnNum;
579 nsString mScriptSample;
581 public:
582 LogViolationDetailsRunnable(WorkerPrivate* aWorker, const nsString& aFileName,
583 uint32_t aLineNum, uint32_t aColumnNum,
584 const nsAString& aScriptSample)
585 : WorkerMainThreadRunnable(
586 aWorker,
587 NS_LITERAL_CSTRING("RuntimeService :: LogViolationDetails")),
588 mFileName(aFileName),
589 mLineNum(aLineNum),
590 mColumnNum(aColumnNum),
591 mScriptSample(aScriptSample) {
592 MOZ_ASSERT(aWorker);
595 virtual bool MainThreadRun() override;
597 private:
598 ~LogViolationDetailsRunnable() {}
601 bool ContentSecurityPolicyAllows(JSContext* aCx, JS::HandleValue aValue) {
602 WorkerPrivate* worker = GetWorkerPrivateFromContext(aCx);
603 worker->AssertIsOnWorkerThread();
605 if (worker->GetReportCSPViolations()) {
606 JS::Rooted<JSString*> jsString(aCx, JS::ToString(aCx, aValue));
607 if (NS_WARN_IF(!jsString)) {
608 JS_ClearPendingException(aCx);
609 return false;
612 nsAutoJSString scriptSample;
613 if (NS_WARN_IF(!scriptSample.init(aCx, jsString))) {
614 JS_ClearPendingException(aCx);
615 return false;
618 nsString fileName;
619 uint32_t lineNum = 0;
620 uint32_t columnNum = 0;
622 JS::AutoFilename file;
623 if (JS::DescribeScriptedCaller(aCx, &file, &lineNum, &columnNum) &&
624 file.get()) {
625 fileName = NS_ConvertUTF8toUTF16(file.get());
626 } else {
627 MOZ_ASSERT(!JS_IsExceptionPending(aCx));
630 RefPtr<LogViolationDetailsRunnable> runnable =
631 new LogViolationDetailsRunnable(worker, fileName, lineNum, columnNum,
632 scriptSample);
634 ErrorResult rv;
635 runnable->Dispatch(Killing, rv);
636 if (NS_WARN_IF(rv.Failed())) {
637 rv.SuppressException();
641 return worker->IsEvalAllowed();
644 void CTypesActivityCallback(JSContext* aCx, js::CTypesActivityType aType) {
645 WorkerPrivate* worker = GetWorkerPrivateFromContext(aCx);
646 worker->AssertIsOnWorkerThread();
648 switch (aType) {
649 case js::CTYPES_CALL_BEGIN:
650 worker->BeginCTypesCall();
651 break;
653 case js::CTYPES_CALL_END:
654 worker->EndCTypesCall();
655 break;
657 case js::CTYPES_CALLBACK_BEGIN:
658 worker->BeginCTypesCallback();
659 break;
661 case js::CTYPES_CALLBACK_END:
662 worker->EndCTypesCallback();
663 break;
665 default:
666 MOZ_CRASH("Unknown type flag!");
670 // JSDispatchableRunnables are WorkerRunnables used to dispatch JS::Dispatchable
671 // back to their worker thread. A WorkerRunnable is used for two reasons:
673 // 1. The JS::Dispatchable::run() callback may run JS so we cannot use a control
674 // runnable since they use async interrupts and break JS run-to-completion.
676 // 2. The DispatchToEventLoopCallback interface is *required* to fail during
677 // shutdown (see jsapi.h) which is exactly what WorkerRunnable::Dispatch() will
678 // do. Moreover, JS_DestroyContext() does *not* block on JS::Dispatchable::run
679 // being called, DispatchToEventLoopCallback failure is expected to happen
680 // during shutdown.
681 class JSDispatchableRunnable final : public WorkerRunnable {
682 JS::Dispatchable* mDispatchable;
684 ~JSDispatchableRunnable() { MOZ_ASSERT(!mDispatchable); }
686 // Disable the usual pre/post-dispatch thread assertions since we are
687 // dispatching from some random JS engine internal thread:
689 bool PreDispatch(WorkerPrivate* aWorkerPrivate) override { return true; }
691 void PostDispatch(WorkerPrivate* aWorkerPrivate,
692 bool aDispatchResult) override {
693 // For the benefit of the destructor assert.
694 if (!aDispatchResult) {
695 mDispatchable = nullptr;
699 public:
700 JSDispatchableRunnable(WorkerPrivate* aWorkerPrivate,
701 JS::Dispatchable* aDispatchable)
702 : WorkerRunnable(aWorkerPrivate,
703 WorkerRunnable::WorkerThreadUnchangedBusyCount),
704 mDispatchable(aDispatchable) {
705 MOZ_ASSERT(mDispatchable);
708 bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
709 MOZ_ASSERT(aWorkerPrivate == mWorkerPrivate);
710 MOZ_ASSERT(aCx == mWorkerPrivate->GetJSContext());
711 MOZ_ASSERT(mDispatchable);
713 AutoJSAPI jsapi;
714 jsapi.Init();
716 mDispatchable->run(mWorkerPrivate->GetJSContext(),
717 JS::Dispatchable::NotShuttingDown);
718 mDispatchable = nullptr; // mDispatchable may delete itself
720 return true;
723 nsresult Cancel() override {
724 MOZ_ASSERT(mDispatchable);
726 AutoJSAPI jsapi;
727 jsapi.Init();
729 mDispatchable->run(mWorkerPrivate->GetJSContext(),
730 JS::Dispatchable::ShuttingDown);
731 mDispatchable = nullptr; // mDispatchable may delete itself
733 return WorkerRunnable::Cancel();
737 static bool DispatchToEventLoop(void* aClosure,
738 JS::Dispatchable* aDispatchable) {
739 // This callback may execute either on the worker thread or a random
740 // JS-internal helper thread.
742 // See comment at JS::InitDispatchToEventLoop() below for how we know the
743 // WorkerPrivate is alive.
744 WorkerPrivate* workerPrivate = reinterpret_cast<WorkerPrivate*>(aClosure);
746 // Dispatch is expected to fail during shutdown for the reasons outlined in
747 // the JSDispatchableRunnable comment above.
748 RefPtr<JSDispatchableRunnable> r =
749 new JSDispatchableRunnable(workerPrivate, aDispatchable);
750 return r->Dispatch();
753 static bool ConsumeStream(JSContext* aCx, JS::HandleObject aObj,
754 JS::MimeType aMimeType,
755 JS::StreamConsumer* aConsumer) {
756 WorkerPrivate* worker = GetWorkerPrivateFromContext(aCx);
757 if (!worker) {
758 JS_ReportErrorNumberASCII(aCx, js::GetErrorMessage, nullptr,
759 JSMSG_ERROR_CONSUMING_RESPONSE);
760 return false;
763 return FetchUtil::StreamResponseToJS(aCx, aObj, aMimeType, aConsumer, worker);
766 bool InitJSContextForWorker(WorkerPrivate* aWorkerPrivate,
767 JSContext* aWorkerCx) {
768 aWorkerPrivate->AssertIsOnWorkerThread();
769 NS_ASSERTION(!aWorkerPrivate->GetJSContext(), "Already has a context!");
771 JSSettings settings;
772 aWorkerPrivate->CopyJSSettings(settings);
774 JS::ContextOptionsRef(aWorkerCx) = settings.contextOptions;
776 JSSettings::JSGCSettingsArray& gcSettings = settings.gcSettings;
778 // This is the real place where we set the max memory for the runtime.
779 for (uint32_t index = 0; index < ArrayLength(gcSettings); index++) {
780 const JSSettings::JSGCSetting& setting = gcSettings[index];
781 if (setting.key.isSome()) {
782 NS_ASSERTION(setting.value, "Can't handle 0 values!");
783 JS_SetGCParameter(aWorkerCx, *setting.key, setting.value);
787 JS_SetNativeStackQuota(aWorkerCx, WORKER_CONTEXT_NATIVE_STACK_LIMIT);
789 // Security policy:
790 static const JSSecurityCallbacks securityCallbacks = {
791 ContentSecurityPolicyAllows};
792 JS_SetSecurityCallbacks(aWorkerCx, &securityCallbacks);
794 // A WorkerPrivate lives strictly longer than its JSRuntime so we can safely
795 // store a raw pointer as the callback's closure argument on the JSRuntime.
796 JS::InitDispatchToEventLoop(aWorkerCx, DispatchToEventLoop,
797 (void*)aWorkerPrivate);
799 JS::InitConsumeStreamCallback(aWorkerCx, ConsumeStream,
800 FetchUtil::ReportJSStreamError);
802 if (!JS::InitSelfHostedCode(aWorkerCx)) {
803 NS_WARNING("Could not init self-hosted code!");
804 return false;
807 JS_AddInterruptCallback(aWorkerCx, InterruptCallback);
809 js::SetCTypesActivityCallback(aWorkerCx, CTypesActivityCallback);
811 #ifdef JS_GC_ZEAL
812 JS_SetGCZeal(aWorkerCx, settings.gcZeal, settings.gcZealFrequency);
813 #endif
815 return true;
818 static bool PreserveWrapper(JSContext* cx, JS::HandleObject obj) {
819 MOZ_ASSERT(cx);
820 MOZ_ASSERT(obj);
821 MOZ_ASSERT(mozilla::dom::IsDOMObject(obj));
823 return mozilla::dom::TryPreserveWrapper(obj);
826 JSObject* Wrap(JSContext* cx, JS::HandleObject existing, JS::HandleObject obj) {
827 JSObject* targetGlobal = JS::CurrentGlobalOrNull(cx);
828 if (!IsWorkerDebuggerGlobal(targetGlobal) &&
829 !IsWorkerDebuggerSandbox(targetGlobal)) {
830 JS_ReportErrorASCII(
831 cx, "There should be no edges from the debuggee to the debugger.");
832 return nullptr;
835 // Note: the JS engine unwraps CCWs before calling this callback.
836 JSObject* originGlobal = JS::GetNonCCWObjectGlobal(obj);
838 const js::Wrapper* wrapper = nullptr;
839 if (IsWorkerDebuggerGlobal(originGlobal) ||
840 IsWorkerDebuggerSandbox(originGlobal)) {
841 wrapper = &js::CrossCompartmentWrapper::singleton;
842 } else {
843 wrapper = &js::OpaqueCrossCompartmentWrapper::singleton;
846 if (existing) {
847 js::Wrapper::Renew(existing, obj, wrapper);
849 return js::Wrapper::New(cx, obj, wrapper);
852 static const JSWrapObjectCallbacks WrapObjectCallbacks = {
853 Wrap,
854 nullptr,
857 class WorkerJSRuntime final : public mozilla::CycleCollectedJSRuntime {
858 public:
859 // The heap size passed here doesn't matter, we will change it later in the
860 // call to JS_SetGCParameter inside InitJSContextForWorker.
861 explicit WorkerJSRuntime(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
862 : CycleCollectedJSRuntime(aCx), mWorkerPrivate(aWorkerPrivate) {
863 MOZ_COUNT_CTOR_INHERITED(WorkerJSRuntime, CycleCollectedJSRuntime);
864 MOZ_ASSERT(aWorkerPrivate);
867 JS::UniqueChars defaultLocale = aWorkerPrivate->AdoptDefaultLocale();
868 MOZ_ASSERT(defaultLocale,
869 "failure of a WorkerPrivate to have a default locale should "
870 "have made the worker fail to spawn");
872 if (!JS_SetDefaultLocale(Runtime(), defaultLocale.get())) {
873 NS_WARNING("failed to set workerCx's default locale");
878 void Shutdown(JSContext* cx) override {
879 // The CC is shut down, and the superclass destructor will GC, so make sure
880 // we don't try to CC again.
881 mWorkerPrivate = nullptr;
883 CycleCollectedJSRuntime::Shutdown(cx);
886 ~WorkerJSRuntime() {
887 MOZ_COUNT_DTOR_INHERITED(WorkerJSRuntime, CycleCollectedJSRuntime);
890 virtual void PrepareForForgetSkippable() override {}
892 virtual void BeginCycleCollectionCallback() override {}
894 virtual void EndCycleCollectionCallback(
895 CycleCollectorResults& aResults) override {}
897 void DispatchDeferredDeletion(bool aContinuation, bool aPurge) override {
898 MOZ_ASSERT(!aContinuation);
900 // Do it immediately, no need for asynchronous behavior here.
901 nsCycleCollector_doDeferredDeletion();
904 virtual void CustomGCCallback(JSGCStatus aStatus) override {
905 if (!mWorkerPrivate) {
906 // We're shutting down, no need to do anything.
907 return;
910 mWorkerPrivate->AssertIsOnWorkerThread();
912 if (aStatus == JSGC_END) {
913 nsCycleCollector_collect(nullptr);
917 private:
918 WorkerPrivate* mWorkerPrivate;
921 } // anonymous namespace
923 } // namespace workerinternals
925 class WorkerJSContext final : public mozilla::CycleCollectedJSContext {
926 public:
927 // The heap size passed here doesn't matter, we will change it later in the
928 // call to JS_SetGCParameter inside InitJSContextForWorker.
929 explicit WorkerJSContext(WorkerPrivate* aWorkerPrivate)
930 : mWorkerPrivate(aWorkerPrivate) {
931 MOZ_COUNT_CTOR_INHERITED(WorkerJSContext, CycleCollectedJSContext);
932 MOZ_ASSERT(aWorkerPrivate);
933 // Magical number 2. Workers have the base recursion depth 1, and normal
934 // runnables run at level 2, and we don't want to process microtasks
935 // at any other level.
936 SetTargetedMicroTaskRecursionDepth(2);
939 // MOZ_CAN_RUN_SCRIPT_BOUNDARY because otherwise we have to annotate the
940 // SpiderMonkey JS::JobQueue's destructor as MOZ_CAN_RUN_SCRIPT, which is a
941 // bit of a pain.
942 MOZ_CAN_RUN_SCRIPT_BOUNDARY ~WorkerJSContext() {
943 MOZ_COUNT_DTOR_INHERITED(WorkerJSContext, CycleCollectedJSContext);
944 JSContext* cx = MaybeContext();
945 if (!cx) {
946 return; // Initialize() must have failed
949 // The worker global should be unrooted and the shutdown cycle collection
950 // should break all remaining cycles. The superclass destructor will run
951 // the GC one final time and finalize any JSObjects that were participating
952 // in cycles that were broken during CC shutdown.
953 nsCycleCollector_shutdown();
955 // The CC is shut down, and the superclass destructor will GC, so make sure
956 // we don't try to CC again.
957 mWorkerPrivate = nullptr;
960 WorkerJSContext* GetAsWorkerJSContext() override { return this; }
962 CycleCollectedJSRuntime* CreateRuntime(JSContext* aCx) override {
963 return new WorkerJSRuntime(aCx, mWorkerPrivate);
966 nsresult Initialize(JSRuntime* aParentRuntime) {
967 nsresult rv = CycleCollectedJSContext::Initialize(
968 aParentRuntime, WORKER_DEFAULT_RUNTIME_HEAPSIZE,
969 WORKER_DEFAULT_NURSERY_SIZE);
970 if (NS_WARN_IF(NS_FAILED(rv))) {
971 return rv;
974 JSContext* cx = Context();
976 js::SetPreserveWrapperCallback(cx, PreserveWrapper);
977 JS_InitDestroyPrincipalsCallback(cx, DestroyWorkerPrincipals);
978 JS_SetWrapObjectCallbacks(cx, &WrapObjectCallbacks);
979 if (mWorkerPrivate->IsDedicatedWorker()) {
980 JS_SetFutexCanWait(cx);
983 return NS_OK;
986 virtual void DispatchToMicroTask(
987 already_AddRefed<MicroTaskRunnable> aRunnable) override {
988 RefPtr<MicroTaskRunnable> runnable(aRunnable);
990 MOZ_ASSERT(!NS_IsMainThread());
991 MOZ_ASSERT(runnable);
993 std::queue<RefPtr<MicroTaskRunnable>>* microTaskQueue = nullptr;
995 JSContext* cx = GetCurrentWorkerThreadJSContext();
996 NS_ASSERTION(cx, "This should never be null!");
998 JS::Rooted<JSObject*> global(cx, JS::CurrentGlobalOrNull(cx));
999 NS_ASSERTION(global, "This should never be null!");
1001 // On worker threads, if the current global is the worker global, we use the
1002 // main micro task queue. Otherwise, the current global must be
1003 // either the debugger global or a debugger sandbox, and we use the debugger
1004 // micro task queue instead.
1005 if (IsWorkerGlobal(global)) {
1006 microTaskQueue = &GetMicroTaskQueue();
1007 } else {
1008 MOZ_ASSERT(IsWorkerDebuggerGlobal(global) ||
1009 IsWorkerDebuggerSandbox(global));
1011 microTaskQueue = &GetDebuggerMicroTaskQueue();
1014 JS::JobQueueMayNotBeEmpty(cx);
1015 microTaskQueue->push(runnable.forget());
1018 bool IsSystemCaller() const override {
1019 return mWorkerPrivate->UsesSystemPrincipal();
1022 WorkerPrivate* GetWorkerPrivate() const { return mWorkerPrivate; }
1024 private:
1025 WorkerPrivate* mWorkerPrivate;
1028 namespace workerinternals {
1030 namespace {
1032 class WorkerThreadPrimaryRunnable final : public Runnable {
1033 WorkerPrivate* mWorkerPrivate;
1034 RefPtr<WorkerThread> mThread;
1035 JSRuntime* mParentRuntime;
1037 class FinishedRunnable final : public Runnable {
1038 RefPtr<WorkerThread> mThread;
1040 public:
1041 explicit FinishedRunnable(already_AddRefed<WorkerThread> aThread)
1042 : Runnable("WorkerThreadPrimaryRunnable::FinishedRunnable"),
1043 mThread(aThread) {
1044 MOZ_ASSERT(mThread);
1047 NS_INLINE_DECL_REFCOUNTING_INHERITED(FinishedRunnable, Runnable)
1049 private:
1050 ~FinishedRunnable() {}
1052 NS_DECL_NSIRUNNABLE
1055 public:
1056 WorkerThreadPrimaryRunnable(WorkerPrivate* aWorkerPrivate,
1057 WorkerThread* aThread, JSRuntime* aParentRuntime)
1058 : mozilla::Runnable("WorkerThreadPrimaryRunnable"),
1059 mWorkerPrivate(aWorkerPrivate),
1060 mThread(aThread),
1061 mParentRuntime(aParentRuntime) {
1062 MOZ_ASSERT(aWorkerPrivate);
1063 MOZ_ASSERT(aThread);
1066 NS_INLINE_DECL_REFCOUNTING_INHERITED(WorkerThreadPrimaryRunnable, Runnable)
1068 private:
1069 ~WorkerThreadPrimaryRunnable() {}
1071 NS_DECL_NSIRUNNABLE
1074 void PrefLanguagesChanged(const char* /* aPrefName */, void* /* aClosure */) {
1075 AssertIsOnMainThread();
1077 nsTArray<nsString> languages;
1078 Navigator::GetAcceptLanguages(languages);
1080 RuntimeService* runtime = RuntimeService::GetService();
1081 if (runtime) {
1082 runtime->UpdateAllWorkerLanguages(languages);
1086 void AppNameOverrideChanged(const char* /* aPrefName */, void* /* aClosure */) {
1087 AssertIsOnMainThread();
1089 nsAutoString override;
1090 Preferences::GetString("general.appname.override", override);
1092 RuntimeService* runtime = RuntimeService::GetService();
1093 if (runtime) {
1094 runtime->UpdateAppNameOverridePreference(override);
1098 void AppVersionOverrideChanged(const char* /* aPrefName */,
1099 void* /* aClosure */) {
1100 AssertIsOnMainThread();
1102 nsAutoString override;
1103 Preferences::GetString("general.appversion.override", override);
1105 RuntimeService* runtime = RuntimeService::GetService();
1106 if (runtime) {
1107 runtime->UpdateAppVersionOverridePreference(override);
1111 void PlatformOverrideChanged(const char* /* aPrefName */,
1112 void* /* aClosure */) {
1113 AssertIsOnMainThread();
1115 nsAutoString override;
1116 Preferences::GetString("general.platform.override", override);
1118 RuntimeService* runtime = RuntimeService::GetService();
1119 if (runtime) {
1120 runtime->UpdatePlatformOverridePreference(override);
1124 } /* anonymous namespace */
1126 struct RuntimeService::IdleThreadInfo {
1127 RefPtr<WorkerThread> mThread;
1128 mozilla::TimeStamp mExpirationTime;
1131 // This is only touched on the main thread. Initialized in Init() below.
1132 JSSettings RuntimeService::sDefaultJSSettings;
1134 RuntimeService::RuntimeService()
1135 : mMutex("RuntimeService::mMutex"),
1136 mObserved(false),
1137 mShuttingDown(false),
1138 mNavigatorPropertiesLoaded(false) {
1139 AssertIsOnMainThread();
1140 NS_ASSERTION(!gRuntimeService, "More than one service!");
1143 RuntimeService::~RuntimeService() {
1144 AssertIsOnMainThread();
1146 // gRuntimeService can be null if Init() fails.
1147 NS_ASSERTION(!gRuntimeService || gRuntimeService == this,
1148 "More than one service!");
1150 gRuntimeService = nullptr;
1153 // static
1154 RuntimeService* RuntimeService::GetOrCreateService() {
1155 AssertIsOnMainThread();
1157 if (!gRuntimeService) {
1158 // The observer service now owns us until shutdown.
1159 gRuntimeService = new RuntimeService();
1160 if (NS_FAILED(gRuntimeService->Init())) {
1161 NS_WARNING("Failed to initialize!");
1162 gRuntimeService->Cleanup();
1163 gRuntimeService = nullptr;
1164 return nullptr;
1168 return gRuntimeService;
1171 // static
1172 RuntimeService* RuntimeService::GetService() { return gRuntimeService; }
1174 bool RuntimeService::RegisterWorker(WorkerPrivate* aWorkerPrivate) {
1175 aWorkerPrivate->AssertIsOnParentThread();
1177 WorkerPrivate* parent = aWorkerPrivate->GetParent();
1178 if (!parent) {
1179 AssertIsOnMainThread();
1181 if (mShuttingDown) {
1182 return false;
1186 const bool isServiceWorker = aWorkerPrivate->IsServiceWorker();
1187 const bool isSharedWorker = aWorkerPrivate->IsSharedWorker();
1188 const bool isDedicatedWorker = aWorkerPrivate->IsDedicatedWorker();
1189 if (isServiceWorker) {
1190 AssertIsOnMainThread();
1191 Telemetry::Accumulate(Telemetry::SERVICE_WORKER_SPAWN_ATTEMPTS, 1);
1194 nsCString sharedWorkerScriptSpec;
1195 if (isSharedWorker) {
1196 AssertIsOnMainThread();
1198 nsCOMPtr<nsIURI> scriptURI = aWorkerPrivate->GetResolvedScriptURI();
1199 NS_ASSERTION(scriptURI, "Null script URI!");
1201 nsresult rv = scriptURI->GetSpec(sharedWorkerScriptSpec);
1202 if (NS_FAILED(rv)) {
1203 NS_WARNING("GetSpec failed?!");
1204 return false;
1207 NS_ASSERTION(!sharedWorkerScriptSpec.IsEmpty(), "Empty spec!");
1210 bool exemptFromPerDomainMax = false;
1211 if (isServiceWorker) {
1212 AssertIsOnMainThread();
1213 exemptFromPerDomainMax = Preferences::GetBool(
1214 "dom.serviceWorkers.exemptFromPerDomainMax", false);
1217 const nsCString& domain = aWorkerPrivate->Domain();
1219 WorkerDomainInfo* domainInfo;
1220 bool queued = false;
1222 MutexAutoLock lock(mMutex);
1224 domainInfo = mDomainMap.LookupForAdd(domain).OrInsert([&domain, parent]() {
1225 NS_ASSERTION(!parent, "Shouldn't have a parent here!");
1226 Unused << parent; // silence clang -Wunused-lambda-capture in opt builds
1227 WorkerDomainInfo* wdi = new WorkerDomainInfo();
1228 wdi->mDomain = domain;
1229 return wdi;
1232 queued = gMaxWorkersPerDomain &&
1233 domainInfo->ActiveWorkerCount() >= gMaxWorkersPerDomain &&
1234 !domain.IsEmpty() && !exemptFromPerDomainMax;
1236 if (queued) {
1237 domainInfo->mQueuedWorkers.AppendElement(aWorkerPrivate);
1239 // Worker spawn gets queued due to hitting max workers per domain
1240 // limit so let's log a warning.
1241 WorkerPrivate::ReportErrorToConsole("HittingMaxWorkersPerDomain2");
1243 if (isServiceWorker) {
1244 Telemetry::Accumulate(Telemetry::SERVICE_WORKER_SPAWN_GETS_QUEUED, 1);
1245 } else if (isSharedWorker) {
1246 Telemetry::Accumulate(Telemetry::SHARED_WORKER_SPAWN_GETS_QUEUED, 1);
1247 } else if (isDedicatedWorker) {
1248 Telemetry::Accumulate(Telemetry::DEDICATED_WORKER_SPAWN_GETS_QUEUED, 1);
1250 } else if (parent) {
1251 domainInfo->mChildWorkerCount++;
1252 } else if (isServiceWorker) {
1253 domainInfo->mActiveServiceWorkers.AppendElement(aWorkerPrivate);
1254 } else {
1255 domainInfo->mActiveWorkers.AppendElement(aWorkerPrivate);
1259 // From here on out we must call UnregisterWorker if something fails!
1260 if (parent) {
1261 if (!parent->AddChildWorker(aWorkerPrivate)) {
1262 UnregisterWorker(aWorkerPrivate);
1263 return false;
1265 } else {
1266 if (!mNavigatorPropertiesLoaded) {
1267 Navigator::AppName(mNavigatorProperties.mAppName,
1268 aWorkerPrivate->GetPrincipal(),
1269 false /* aUsePrefOverriddenValue */);
1270 if (NS_FAILED(Navigator::GetAppVersion(
1271 mNavigatorProperties.mAppVersion, aWorkerPrivate->GetPrincipal(),
1272 false /* aUsePrefOverriddenValue */)) ||
1273 NS_FAILED(Navigator::GetPlatform(
1274 mNavigatorProperties.mPlatform, aWorkerPrivate->GetPrincipal(),
1275 false /* aUsePrefOverriddenValue */))) {
1276 UnregisterWorker(aWorkerPrivate);
1277 return false;
1280 // The navigator overridden properties should have already been read.
1282 Navigator::GetAcceptLanguages(mNavigatorProperties.mLanguages);
1283 mNavigatorPropertiesLoaded = true;
1286 nsPIDOMWindowInner* window = aWorkerPrivate->GetWindow();
1288 if (!isServiceWorker) {
1289 // Service workers are excluded since their lifetime is separate from
1290 // that of dom windows.
1291 nsTArray<WorkerPrivate*>* windowArray =
1292 mWindowMap.LookupForAdd(window).OrInsert(
1293 []() { return new nsTArray<WorkerPrivate*>(1); });
1294 if (!windowArray->Contains(aWorkerPrivate)) {
1295 windowArray->AppendElement(aWorkerPrivate);
1296 } else {
1297 MOZ_ASSERT(aWorkerPrivate->IsSharedWorker());
1302 if (!queued && !ScheduleWorker(aWorkerPrivate)) {
1303 return false;
1306 if (isServiceWorker) {
1307 AssertIsOnMainThread();
1308 Telemetry::Accumulate(Telemetry::SERVICE_WORKER_WAS_SPAWNED, 1);
1310 return true;
1313 void RuntimeService::UnregisterWorker(WorkerPrivate* aWorkerPrivate) {
1314 aWorkerPrivate->AssertIsOnParentThread();
1316 WorkerPrivate* parent = aWorkerPrivate->GetParent();
1317 if (!parent) {
1318 AssertIsOnMainThread();
1321 const nsCString& domain = aWorkerPrivate->Domain();
1323 WorkerPrivate* queuedWorker = nullptr;
1325 MutexAutoLock lock(mMutex);
1327 WorkerDomainInfo* domainInfo;
1328 if (!mDomainMap.Get(domain, &domainInfo)) {
1329 NS_ERROR("Don't have an entry for this domain!");
1332 // Remove old worker from everywhere.
1333 uint32_t index = domainInfo->mQueuedWorkers.IndexOf(aWorkerPrivate);
1334 if (index != kNoIndex) {
1335 // Was queued, remove from the list.
1336 domainInfo->mQueuedWorkers.RemoveElementAt(index);
1337 } else if (parent) {
1338 MOZ_ASSERT(domainInfo->mChildWorkerCount, "Must be non-zero!");
1339 domainInfo->mChildWorkerCount--;
1340 } else if (aWorkerPrivate->IsServiceWorker()) {
1341 MOZ_ASSERT(domainInfo->mActiveServiceWorkers.Contains(aWorkerPrivate),
1342 "Don't know about this worker!");
1343 domainInfo->mActiveServiceWorkers.RemoveElement(aWorkerPrivate);
1344 } else {
1345 MOZ_ASSERT(domainInfo->mActiveWorkers.Contains(aWorkerPrivate),
1346 "Don't know about this worker!");
1347 domainInfo->mActiveWorkers.RemoveElement(aWorkerPrivate);
1350 // See if there's a queued worker we can schedule.
1351 if (domainInfo->ActiveWorkerCount() < gMaxWorkersPerDomain &&
1352 !domainInfo->mQueuedWorkers.IsEmpty()) {
1353 queuedWorker = domainInfo->mQueuedWorkers[0];
1354 domainInfo->mQueuedWorkers.RemoveElementAt(0);
1356 if (queuedWorker->GetParent()) {
1357 domainInfo->mChildWorkerCount++;
1358 } else if (queuedWorker->IsServiceWorker()) {
1359 domainInfo->mActiveServiceWorkers.AppendElement(queuedWorker);
1360 } else {
1361 domainInfo->mActiveWorkers.AppendElement(queuedWorker);
1365 if (domainInfo->HasNoWorkers()) {
1366 MOZ_ASSERT(domainInfo->mQueuedWorkers.IsEmpty());
1367 mDomainMap.Remove(domain);
1371 if (aWorkerPrivate->IsServiceWorker()) {
1372 AssertIsOnMainThread();
1373 Telemetry::AccumulateTimeDelta(Telemetry::SERVICE_WORKER_LIFE_TIME,
1374 aWorkerPrivate->CreationTimeStamp());
1377 // NB: For Shared Workers we used to call ShutdownOnMainThread on the
1378 // RemoteWorkerController; however, that was redundant because
1379 // RemoteWorkerChild uses a WeakWorkerRef which notifies at about the
1380 // same time as us calling into the code here and would race with us.
1382 if (parent) {
1383 parent->RemoveChildWorker(aWorkerPrivate);
1384 } else if (aWorkerPrivate->IsSharedWorker()) {
1385 AssertIsOnMainThread();
1387 for (auto iter = mWindowMap.Iter(); !iter.Done(); iter.Next()) {
1388 nsAutoPtr<nsTArray<WorkerPrivate*>>& workers = iter.Data();
1389 MOZ_ASSERT(workers.get());
1391 if (workers->RemoveElement(aWorkerPrivate)) {
1392 MOZ_ASSERT(!workers->Contains(aWorkerPrivate),
1393 "Added worker more than once!");
1395 if (workers->IsEmpty()) {
1396 iter.Remove();
1400 } else if (aWorkerPrivate->IsDedicatedWorker()) {
1401 // May be null.
1402 nsPIDOMWindowInner* window = aWorkerPrivate->GetWindow();
1403 if (auto entry = mWindowMap.Lookup(window)) {
1404 MOZ_ALWAYS_TRUE(entry.Data()->RemoveElement(aWorkerPrivate));
1405 if (entry.Data()->IsEmpty()) {
1406 entry.Remove();
1408 } else {
1409 MOZ_ASSERT_UNREACHABLE("window is not in mWindowMap");
1413 if (queuedWorker && !ScheduleWorker(queuedWorker)) {
1414 UnregisterWorker(queuedWorker);
1418 bool RuntimeService::ScheduleWorker(WorkerPrivate* aWorkerPrivate) {
1419 if (!aWorkerPrivate->Start()) {
1420 // This is ok, means that we didn't need to make a thread for this worker.
1421 return true;
1424 RefPtr<WorkerThread> thread;
1426 MutexAutoLock lock(mMutex);
1427 if (!mIdleThreadArray.IsEmpty()) {
1428 uint32_t index = mIdleThreadArray.Length() - 1;
1429 mIdleThreadArray[index].mThread.swap(thread);
1430 mIdleThreadArray.RemoveElementAt(index);
1434 const WorkerThreadFriendKey friendKey;
1436 if (!thread) {
1437 thread = WorkerThread::Create(friendKey);
1438 if (!thread) {
1439 UnregisterWorker(aWorkerPrivate);
1440 return false;
1444 int32_t priority = aWorkerPrivate->IsChromeWorker()
1445 ? nsISupportsPriority::PRIORITY_NORMAL
1446 : nsISupportsPriority::PRIORITY_LOW;
1448 if (NS_FAILED(thread->SetPriority(priority))) {
1449 NS_WARNING("Could not set the thread's priority!");
1452 aWorkerPrivate->SetThread(thread);
1453 JSContext* cx = CycleCollectedJSContext::Get()->Context();
1454 nsCOMPtr<nsIRunnable> runnable = new WorkerThreadPrimaryRunnable(
1455 aWorkerPrivate, thread, JS_GetParentRuntime(cx));
1456 if (NS_FAILED(
1457 thread->DispatchPrimaryRunnable(friendKey, runnable.forget()))) {
1458 UnregisterWorker(aWorkerPrivate);
1459 return false;
1462 return true;
1465 // static
1466 void RuntimeService::ShutdownIdleThreads(nsITimer* aTimer,
1467 void* /* aClosure */) {
1468 AssertIsOnMainThread();
1470 RuntimeService* runtime = RuntimeService::GetService();
1471 NS_ASSERTION(runtime, "This should never be null!");
1473 NS_ASSERTION(aTimer == runtime->mIdleThreadTimer, "Wrong timer!");
1475 // Cheat a little and grab all threads that expire within one second of now.
1476 TimeStamp now = TimeStamp::NowLoRes() + TimeDuration::FromSeconds(1);
1478 TimeStamp nextExpiration;
1480 AutoTArray<RefPtr<WorkerThread>, 20> expiredThreads;
1482 MutexAutoLock lock(runtime->mMutex);
1484 for (uint32_t index = 0; index < runtime->mIdleThreadArray.Length();
1485 index++) {
1486 IdleThreadInfo& info = runtime->mIdleThreadArray[index];
1487 if (info.mExpirationTime > now) {
1488 nextExpiration = info.mExpirationTime;
1489 break;
1492 RefPtr<WorkerThread>* thread = expiredThreads.AppendElement();
1493 thread->swap(info.mThread);
1496 if (!expiredThreads.IsEmpty()) {
1497 runtime->mIdleThreadArray.RemoveElementsAt(0, expiredThreads.Length());
1501 if (!nextExpiration.IsNull()) {
1502 TimeDuration delta = nextExpiration - TimeStamp::NowLoRes();
1503 uint32_t delay(delta > TimeDuration(0) ? delta.ToMilliseconds() : 0);
1505 // Reschedule the timer.
1506 MOZ_ALWAYS_SUCCEEDS(aTimer->InitWithNamedFuncCallback(
1507 ShutdownIdleThreads, nullptr, delay, nsITimer::TYPE_ONE_SHOT,
1508 "RuntimeService::ShutdownIdleThreads"));
1511 for (uint32_t index = 0; index < expiredThreads.Length(); index++) {
1512 if (NS_FAILED(expiredThreads[index]->Shutdown())) {
1513 NS_WARNING("Failed to shutdown thread!");
1518 nsresult RuntimeService::Init() {
1519 AssertIsOnMainThread();
1521 nsLayoutStatics::AddRef();
1523 // Initialize JSSettings.
1524 if (sDefaultJSSettings.gcSettings[0].key.isNothing()) {
1525 sDefaultJSSettings.contextOptions = JS::ContextOptions();
1526 sDefaultJSSettings.chrome.maxScriptRuntime = -1;
1527 sDefaultJSSettings.content.maxScriptRuntime = MAX_SCRIPT_RUN_TIME_SEC;
1528 #ifdef JS_GC_ZEAL
1529 sDefaultJSSettings.gcZealFrequency = JS_DEFAULT_ZEAL_FREQ;
1530 sDefaultJSSettings.gcZeal = 0;
1531 #endif
1532 SetDefaultJSGCSettings(JSGC_MAX_BYTES, WORKER_DEFAULT_RUNTIME_HEAPSIZE);
1533 SetDefaultJSGCSettings(JSGC_ALLOCATION_THRESHOLD,
1534 WORKER_DEFAULT_ALLOCATION_THRESHOLD);
1537 // nsIStreamTransportService is thread-safe but it must be initialized on the
1538 // main-thread. FileReader needs it, so, let's initialize it now.
1539 nsresult rv;
1540 nsCOMPtr<nsIStreamTransportService> sts =
1541 do_GetService(kStreamTransportServiceCID, &rv);
1542 NS_ENSURE_TRUE(sts, NS_ERROR_FAILURE);
1544 mIdleThreadTimer = NS_NewTimer();
1545 NS_ENSURE_STATE(mIdleThreadTimer);
1547 nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
1548 NS_ENSURE_TRUE(obs, NS_ERROR_FAILURE);
1550 rv = obs->AddObserver(this, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID, false);
1551 NS_ENSURE_SUCCESS(rv, rv);
1553 rv = obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
1554 NS_ENSURE_SUCCESS(rv, rv);
1556 mObserved = true;
1558 if (NS_FAILED(obs->AddObserver(this, GC_REQUEST_OBSERVER_TOPIC, false))) {
1559 NS_WARNING("Failed to register for GC request notifications!");
1562 if (NS_FAILED(obs->AddObserver(this, CC_REQUEST_OBSERVER_TOPIC, false))) {
1563 NS_WARNING("Failed to register for CC request notifications!");
1566 if (NS_FAILED(
1567 obs->AddObserver(this, MEMORY_PRESSURE_OBSERVER_TOPIC, false))) {
1568 NS_WARNING("Failed to register for memory pressure notifications!");
1571 if (NS_FAILED(
1572 obs->AddObserver(this, NS_IOSERVICE_OFFLINE_STATUS_TOPIC, false))) {
1573 NS_WARNING("Failed to register for offline notification event!");
1576 MOZ_ASSERT(!gRuntimeServiceDuringInit, "This should be false!");
1577 gRuntimeServiceDuringInit = true;
1579 if (NS_FAILED(Preferences::RegisterPrefixCallback(
1580 LoadJSGCMemoryOptions,
1581 PREF_JS_OPTIONS_PREFIX PREF_MEM_OPTIONS_PREFIX)) ||
1582 NS_FAILED(Preferences::RegisterPrefixCallbackAndCall(
1583 LoadJSGCMemoryOptions,
1584 PREF_WORKERS_OPTIONS_PREFIX PREF_MEM_OPTIONS_PREFIX)) ||
1585 #ifdef JS_GC_ZEAL
1586 NS_FAILED(Preferences::RegisterCallback(
1587 LoadGCZealOptions, PREF_JS_OPTIONS_PREFIX PREF_GCZEAL)) ||
1588 #endif
1590 #define WORKER_PREF(name, callback) \
1591 NS_FAILED(Preferences::RegisterCallbackAndCall(callback, name)) ||
1592 WORKER_PREF("intl.accept_languages", PrefLanguagesChanged) WORKER_PREF(
1593 "general.appname.override", AppNameOverrideChanged)
1594 WORKER_PREF("general.appversion.override", AppVersionOverrideChanged)
1595 WORKER_PREF("general.platform.override", PlatformOverrideChanged)
1596 #ifdef JS_GC_ZEAL
1597 WORKER_PREF("dom.workers.options.gcZeal", LoadGCZealOptions)
1598 #endif
1599 #undef WORKER_PREF
1601 NS_FAILED(Preferences::RegisterPrefixCallbackAndCall(
1602 LoadContextOptions, PREF_WORKERS_OPTIONS_PREFIX)) ||
1603 NS_FAILED(Preferences::RegisterPrefixCallback(LoadContextOptions,
1604 PREF_JS_OPTIONS_PREFIX))) {
1605 NS_WARNING("Failed to register pref callbacks!");
1608 MOZ_ASSERT(gRuntimeServiceDuringInit, "Should be true!");
1609 gRuntimeServiceDuringInit = false;
1611 // We assume atomic 32bit reads/writes. If this assumption doesn't hold on
1612 // some wacky platform then the worst that could happen is that the close
1613 // handler will run for a slightly different amount of time.
1614 if (NS_FAILED(Preferences::AddIntVarCache(
1615 &sDefaultJSSettings.content.maxScriptRuntime,
1616 PREF_MAX_SCRIPT_RUN_TIME_CONTENT, MAX_SCRIPT_RUN_TIME_SEC)) ||
1617 NS_FAILED(Preferences::AddIntVarCache(
1618 &sDefaultJSSettings.chrome.maxScriptRuntime,
1619 PREF_MAX_SCRIPT_RUN_TIME_CHROME, -1))) {
1620 NS_WARNING("Failed to register timeout cache!");
1623 int32_t maxPerDomain =
1624 Preferences::GetInt(PREF_WORKERS_MAX_PER_DOMAIN, MAX_WORKERS_PER_DOMAIN);
1625 gMaxWorkersPerDomain = std::max(0, maxPerDomain);
1627 int32_t maxHardwareConcurrency = Preferences::GetInt(
1628 PREF_WORKERS_MAX_HARDWARE_CONCURRENCY, MAX_HARDWARE_CONCURRENCY);
1629 gMaxHardwareConcurrency = std::max(0, maxHardwareConcurrency);
1631 RefPtr<OSFileConstantsService> osFileConstantsService =
1632 OSFileConstantsService::GetOrCreate();
1633 if (NS_WARN_IF(!osFileConstantsService)) {
1634 return NS_ERROR_FAILURE;
1637 if (NS_WARN_IF(!IndexedDatabaseManager::GetOrCreate())) {
1638 return NS_ERROR_UNEXPECTED;
1641 // PerformanceService must be initialized on the main-thread.
1642 PerformanceService::GetOrCreate();
1644 return NS_OK;
1647 void RuntimeService::Shutdown() {
1648 AssertIsOnMainThread();
1650 MOZ_ASSERT(!mShuttingDown);
1651 // That's it, no more workers.
1652 mShuttingDown = true;
1654 nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
1655 NS_WARNING_ASSERTION(obs, "Failed to get observer service?!");
1657 // Tell anyone that cares that they're about to lose worker support.
1658 if (obs && NS_FAILED(obs->NotifyObservers(nullptr, WORKERS_SHUTDOWN_TOPIC,
1659 nullptr))) {
1660 NS_WARNING("NotifyObservers failed!");
1664 MutexAutoLock lock(mMutex);
1666 AutoTArray<WorkerPrivate*, 100> workers;
1667 AddAllTopLevelWorkersToArray(workers);
1669 if (!workers.IsEmpty()) {
1670 // Cancel all top-level workers.
1672 MutexAutoUnlock unlock(mMutex);
1674 for (uint32_t index = 0; index < workers.Length(); index++) {
1675 if (!workers[index]->Cancel()) {
1676 NS_WARNING("Failed to cancel worker!");
1684 namespace {
1686 class CrashIfHangingRunnable : public WorkerControlRunnable {
1687 public:
1688 explicit CrashIfHangingRunnable(WorkerPrivate* aWorkerPrivate)
1689 : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
1690 mMonitor("CrashIfHangingRunnable::mMonitor") {}
1692 bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
1693 aWorkerPrivate->DumpCrashInformation(mMsg);
1695 MonitorAutoLock lock(mMonitor);
1696 lock.Notify();
1697 return true;
1700 nsresult Cancel() override {
1701 mMsg.Assign("Canceled");
1703 MonitorAutoLock lock(mMonitor);
1704 lock.Notify();
1706 return NS_OK;
1709 void DispatchAndWait() {
1710 MonitorAutoLock lock(mMonitor);
1712 if (!Dispatch()) {
1713 mMsg.Assign("Dispatch Error");
1714 return;
1717 lock.Wait();
1720 const nsCString& MsgData() const { return mMsg; }
1722 private:
1723 bool PreDispatch(WorkerPrivate* aWorkerPrivate) override { return true; }
1725 void PostDispatch(WorkerPrivate* aWorkerPrivate,
1726 bool aDispatchResult) override {}
1728 Monitor mMonitor;
1729 nsCString mMsg;
1732 } // namespace
1734 void RuntimeService::CrashIfHanging() {
1735 MutexAutoLock lock(mMutex);
1737 if (mDomainMap.IsEmpty()) {
1738 return;
1741 uint32_t activeWorkers = 0;
1742 uint32_t activeServiceWorkers = 0;
1743 uint32_t inactiveWorkers = 0;
1745 nsTArray<WorkerPrivate*> workers;
1747 for (auto iter = mDomainMap.Iter(); !iter.Done(); iter.Next()) {
1748 WorkerDomainInfo* aData = iter.UserData();
1750 activeWorkers += aData->mActiveWorkers.Length();
1751 activeServiceWorkers += aData->mActiveServiceWorkers.Length();
1753 workers.AppendElements(aData->mActiveWorkers);
1754 workers.AppendElements(aData->mActiveServiceWorkers);
1756 // These might not be top-level workers...
1757 for (uint32_t index = 0; index < aData->mQueuedWorkers.Length(); index++) {
1758 WorkerPrivate* worker = aData->mQueuedWorkers[index];
1759 if (!worker->GetParent()) {
1760 ++inactiveWorkers;
1765 // We must have something pending...
1766 MOZ_DIAGNOSTIC_ASSERT(activeWorkers + activeServiceWorkers + inactiveWorkers);
1768 nsCString msg;
1770 // A: active Workers | S: active ServiceWorkers | Q: queued Workers
1771 msg.AppendPrintf("Workers Hanging - %d|A:%d|S:%d|Q:%d", mShuttingDown ? 1 : 0,
1772 activeWorkers, activeServiceWorkers, inactiveWorkers);
1774 // For each thread, let's print some data to know what is going wrong.
1775 for (uint32_t i = 0; i < workers.Length(); ++i) {
1776 WorkerPrivate* workerPrivate = workers[i];
1778 // BC: Busy Count
1779 msg.AppendPrintf("-BC:%d", workerPrivate->BusyCount());
1781 RefPtr<CrashIfHangingRunnable> runnable =
1782 new CrashIfHangingRunnable(workerPrivate);
1783 runnable->DispatchAndWait();
1785 msg.Append(runnable->MsgData());
1788 // This string will be leaked.
1789 MOZ_CRASH_UNSAFE(strdup(msg.BeginReading()));
1792 // This spins the event loop until all workers are finished and their threads
1793 // have been joined.
1794 void RuntimeService::Cleanup() {
1795 AssertIsOnMainThread();
1797 if (!mShuttingDown) {
1798 Shutdown();
1801 nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
1802 NS_WARNING_ASSERTION(obs, "Failed to get observer service?!");
1804 if (mIdleThreadTimer) {
1805 if (NS_FAILED(mIdleThreadTimer->Cancel())) {
1806 NS_WARNING("Failed to cancel idle timer!");
1808 mIdleThreadTimer = nullptr;
1812 MutexAutoLock lock(mMutex);
1814 AutoTArray<WorkerPrivate*, 100> workers;
1815 AddAllTopLevelWorkersToArray(workers);
1817 if (!workers.IsEmpty()) {
1818 nsIThread* currentThread = NS_GetCurrentThread();
1819 NS_ASSERTION(currentThread, "This should never be null!");
1821 // Shut down any idle threads.
1822 if (!mIdleThreadArray.IsEmpty()) {
1823 AutoTArray<RefPtr<WorkerThread>, 20> idleThreads;
1825 uint32_t idleThreadCount = mIdleThreadArray.Length();
1826 idleThreads.SetLength(idleThreadCount);
1828 for (uint32_t index = 0; index < idleThreadCount; index++) {
1829 NS_ASSERTION(mIdleThreadArray[index].mThread, "Null thread!");
1830 idleThreads[index].swap(mIdleThreadArray[index].mThread);
1833 mIdleThreadArray.Clear();
1835 MutexAutoUnlock unlock(mMutex);
1837 for (uint32_t index = 0; index < idleThreadCount; index++) {
1838 if (NS_FAILED(idleThreads[index]->Shutdown())) {
1839 NS_WARNING("Failed to shutdown thread!");
1844 // And make sure all their final messages have run and all their threads
1845 // have joined.
1846 while (mDomainMap.Count()) {
1847 MutexAutoUnlock unlock(mMutex);
1849 if (!NS_ProcessNextEvent(currentThread)) {
1850 NS_WARNING("Something bad happened!");
1851 break;
1857 NS_ASSERTION(!mWindowMap.Count(), "All windows should have been released!");
1859 if (mObserved) {
1860 if (NS_FAILED(Preferences::UnregisterPrefixCallback(
1861 LoadContextOptions, PREF_JS_OPTIONS_PREFIX)) ||
1862 NS_FAILED(Preferences::UnregisterPrefixCallback(
1863 LoadContextOptions, PREF_WORKERS_OPTIONS_PREFIX)) ||
1864 #define WORKER_PREF(name, callback) \
1865 NS_FAILED(Preferences::UnregisterCallback(callback, name)) ||
1866 WORKER_PREF("intl.accept_languages", PrefLanguagesChanged) WORKER_PREF(
1867 "general.appname.override",
1868 AppNameOverrideChanged) WORKER_PREF("general.appversion.override",
1869 AppVersionOverrideChanged)
1870 WORKER_PREF("general.platform.override", PlatformOverrideChanged)
1871 #ifdef JS_GC_ZEAL
1872 WORKER_PREF("dom.workers.options.gcZeal", LoadGCZealOptions)
1873 #endif
1874 #undef WORKER_PREF
1876 #ifdef JS_GC_ZEAL
1877 NS_FAILED(Preferences::UnregisterCallback(
1878 LoadGCZealOptions,
1879 PREF_JS_OPTIONS_PREFIX PREF_GCZEAL)) ||
1880 #endif
1881 NS_FAILED(Preferences::UnregisterPrefixCallback(
1882 LoadJSGCMemoryOptions,
1883 PREF_JS_OPTIONS_PREFIX PREF_MEM_OPTIONS_PREFIX)) ||
1884 NS_FAILED(Preferences::UnregisterPrefixCallback(
1885 LoadJSGCMemoryOptions,
1886 PREF_WORKERS_OPTIONS_PREFIX PREF_MEM_OPTIONS_PREFIX))) {
1887 NS_WARNING("Failed to unregister pref callbacks!");
1890 if (obs) {
1891 if (NS_FAILED(obs->RemoveObserver(this, GC_REQUEST_OBSERVER_TOPIC))) {
1892 NS_WARNING("Failed to unregister for GC request notifications!");
1895 if (NS_FAILED(obs->RemoveObserver(this, CC_REQUEST_OBSERVER_TOPIC))) {
1896 NS_WARNING("Failed to unregister for CC request notifications!");
1899 if (NS_FAILED(
1900 obs->RemoveObserver(this, MEMORY_PRESSURE_OBSERVER_TOPIC))) {
1901 NS_WARNING("Failed to unregister for memory pressure notifications!");
1904 if (NS_FAILED(
1905 obs->RemoveObserver(this, NS_IOSERVICE_OFFLINE_STATUS_TOPIC))) {
1906 NS_WARNING("Failed to unregister for offline notification event!");
1908 obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID);
1909 obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
1910 mObserved = false;
1914 nsLayoutStatics::Release();
1917 void RuntimeService::AddAllTopLevelWorkersToArray(
1918 nsTArray<WorkerPrivate*>& aWorkers) {
1919 for (auto iter = mDomainMap.Iter(); !iter.Done(); iter.Next()) {
1920 WorkerDomainInfo* aData = iter.UserData();
1922 #ifdef DEBUG
1923 for (uint32_t index = 0; index < aData->mActiveWorkers.Length(); index++) {
1924 MOZ_ASSERT(!aData->mActiveWorkers[index]->GetParent(),
1925 "Shouldn't have a parent in this list!");
1927 for (uint32_t index = 0; index < aData->mActiveServiceWorkers.Length();
1928 index++) {
1929 MOZ_ASSERT(!aData->mActiveServiceWorkers[index]->GetParent(),
1930 "Shouldn't have a parent in this list!");
1932 #endif
1934 aWorkers.AppendElements(aData->mActiveWorkers);
1935 aWorkers.AppendElements(aData->mActiveServiceWorkers);
1937 // These might not be top-level workers...
1938 for (uint32_t index = 0; index < aData->mQueuedWorkers.Length(); index++) {
1939 WorkerPrivate* worker = aData->mQueuedWorkers[index];
1940 if (!worker->GetParent()) {
1941 aWorkers.AppendElement(worker);
1947 void RuntimeService::GetWorkersForWindow(nsPIDOMWindowInner* aWindow,
1948 nsTArray<WorkerPrivate*>& aWorkers) {
1949 AssertIsOnMainThread();
1951 nsTArray<WorkerPrivate*>* workers;
1952 if (mWindowMap.Get(aWindow, &workers)) {
1953 NS_ASSERTION(!workers->IsEmpty(), "Should have been removed!");
1954 aWorkers.AppendElements(*workers);
1955 } else {
1956 NS_ASSERTION(aWorkers.IsEmpty(), "Should be empty!");
1960 void RuntimeService::CancelWorkersForWindow(nsPIDOMWindowInner* aWindow) {
1961 AssertIsOnMainThread();
1963 nsTArray<WorkerPrivate*> workers;
1964 GetWorkersForWindow(aWindow, workers);
1966 if (!workers.IsEmpty()) {
1967 for (uint32_t index = 0; index < workers.Length(); index++) {
1968 WorkerPrivate*& worker = workers[index];
1969 MOZ_ASSERT(!worker->IsSharedWorker());
1970 worker->Cancel();
1975 void RuntimeService::FreezeWorkersForWindow(nsPIDOMWindowInner* aWindow) {
1976 AssertIsOnMainThread();
1977 MOZ_ASSERT(aWindow);
1979 nsTArray<WorkerPrivate*> workers;
1980 GetWorkersForWindow(aWindow, workers);
1982 for (uint32_t index = 0; index < workers.Length(); index++) {
1983 MOZ_ASSERT(!workers[index]->IsSharedWorker());
1984 workers[index]->Freeze(aWindow);
1988 void RuntimeService::ThawWorkersForWindow(nsPIDOMWindowInner* aWindow) {
1989 AssertIsOnMainThread();
1990 MOZ_ASSERT(aWindow);
1992 nsTArray<WorkerPrivate*> workers;
1993 GetWorkersForWindow(aWindow, workers);
1995 for (uint32_t index = 0; index < workers.Length(); index++) {
1996 MOZ_ASSERT(!workers[index]->IsSharedWorker());
1997 workers[index]->Thaw(aWindow);
2001 void RuntimeService::SuspendWorkersForWindow(nsPIDOMWindowInner* aWindow) {
2002 AssertIsOnMainThread();
2003 MOZ_ASSERT(aWindow);
2005 nsTArray<WorkerPrivate*> workers;
2006 GetWorkersForWindow(aWindow, workers);
2008 for (uint32_t index = 0; index < workers.Length(); index++) {
2009 MOZ_ASSERT(!workers[index]->IsSharedWorker());
2010 workers[index]->ParentWindowPaused();
2014 void RuntimeService::ResumeWorkersForWindow(nsPIDOMWindowInner* aWindow) {
2015 AssertIsOnMainThread();
2016 MOZ_ASSERT(aWindow);
2018 nsTArray<WorkerPrivate*> workers;
2019 GetWorkersForWindow(aWindow, workers);
2021 for (uint32_t index = 0; index < workers.Length(); index++) {
2022 MOZ_ASSERT(!workers[index]->IsSharedWorker());
2023 workers[index]->ParentWindowResumed();
2027 void RuntimeService::PropagateFirstPartyStorageAccessGranted(
2028 nsPIDOMWindowInner* aWindow) {
2029 AssertIsOnMainThread();
2030 MOZ_ASSERT(aWindow);
2031 MOZ_ASSERT_IF(
2032 aWindow->GetExtantDoc(),
2033 aWindow->GetExtantDoc()->CookieSettings()->GetCookieBehavior() ==
2034 nsICookieService::BEHAVIOR_REJECT_TRACKER);
2036 nsTArray<WorkerPrivate*> workers;
2037 GetWorkersForWindow(aWindow, workers);
2039 for (uint32_t index = 0; index < workers.Length(); index++) {
2040 workers[index]->PropagateFirstPartyStorageAccessGranted();
2044 void RuntimeService::NoteIdleThread(WorkerThread* aThread) {
2045 AssertIsOnMainThread();
2046 MOZ_ASSERT(aThread);
2048 bool shutdownThread = mShuttingDown;
2049 bool scheduleTimer = false;
2051 if (!shutdownThread) {
2052 static TimeDuration timeout =
2053 TimeDuration::FromSeconds(IDLE_THREAD_TIMEOUT_SEC);
2055 TimeStamp expirationTime = TimeStamp::NowLoRes() + timeout;
2057 MutexAutoLock lock(mMutex);
2059 uint32_t previousIdleCount = mIdleThreadArray.Length();
2061 if (previousIdleCount < MAX_IDLE_THREADS) {
2062 IdleThreadInfo* info = mIdleThreadArray.AppendElement();
2063 info->mThread = aThread;
2064 info->mExpirationTime = expirationTime;
2066 scheduleTimer = previousIdleCount == 0;
2067 } else {
2068 shutdownThread = true;
2072 MOZ_ASSERT_IF(shutdownThread, !scheduleTimer);
2073 MOZ_ASSERT_IF(scheduleTimer, !shutdownThread);
2075 // Too many idle threads, just shut this one down.
2076 if (shutdownThread) {
2077 MOZ_ALWAYS_SUCCEEDS(aThread->Shutdown());
2078 } else if (scheduleTimer) {
2079 MOZ_ALWAYS_SUCCEEDS(mIdleThreadTimer->InitWithNamedFuncCallback(
2080 ShutdownIdleThreads, nullptr, IDLE_THREAD_TIMEOUT_SEC * 1000,
2081 nsITimer::TYPE_ONE_SHOT, "RuntimeService::ShutdownIdleThreads"));
2085 void RuntimeService::UpdateAllWorkerContextOptions() {
2086 BROADCAST_ALL_WORKERS(UpdateContextOptions,
2087 sDefaultJSSettings.contextOptions);
2090 void RuntimeService::UpdateAppNameOverridePreference(const nsAString& aValue) {
2091 AssertIsOnMainThread();
2092 mNavigatorProperties.mAppNameOverridden = aValue;
2095 void RuntimeService::UpdateAppVersionOverridePreference(
2096 const nsAString& aValue) {
2097 AssertIsOnMainThread();
2098 mNavigatorProperties.mAppVersionOverridden = aValue;
2101 void RuntimeService::UpdatePlatformOverridePreference(const nsAString& aValue) {
2102 AssertIsOnMainThread();
2103 mNavigatorProperties.mPlatformOverridden = aValue;
2106 void RuntimeService::UpdateAllWorkerLanguages(
2107 const nsTArray<nsString>& aLanguages) {
2108 MOZ_ASSERT(NS_IsMainThread());
2110 mNavigatorProperties.mLanguages = aLanguages;
2111 BROADCAST_ALL_WORKERS(UpdateLanguages, aLanguages);
2114 void RuntimeService::UpdateAllWorkerMemoryParameter(JSGCParamKey aKey,
2115 uint32_t aValue) {
2116 BROADCAST_ALL_WORKERS(UpdateJSWorkerMemoryParameter, aKey, aValue);
2119 #ifdef JS_GC_ZEAL
2120 void RuntimeService::UpdateAllWorkerGCZeal() {
2121 BROADCAST_ALL_WORKERS(UpdateGCZeal, sDefaultJSSettings.gcZeal,
2122 sDefaultJSSettings.gcZealFrequency);
2124 #endif
2126 void RuntimeService::GarbageCollectAllWorkers(bool aShrinking) {
2127 BROADCAST_ALL_WORKERS(GarbageCollect, aShrinking);
2130 void RuntimeService::CycleCollectAllWorkers() {
2131 BROADCAST_ALL_WORKERS(CycleCollect, /* dummy = */ false);
2134 void RuntimeService::SendOfflineStatusChangeEventToAllWorkers(bool aIsOffline) {
2135 BROADCAST_ALL_WORKERS(OfflineStatusChangeEvent, aIsOffline);
2138 void RuntimeService::MemoryPressureAllWorkers() {
2139 BROADCAST_ALL_WORKERS(MemoryPressure, /* dummy = */ false);
2142 uint32_t RuntimeService::ClampedHardwareConcurrency() const {
2143 // The Firefox Hardware Report says 70% of Firefox users have exactly 2 cores.
2144 // When the resistFingerprinting pref is set, we want to blend into the crowd
2145 // so spoof navigator.hardwareConcurrency = 2 to reduce user uniqueness.
2146 if (MOZ_UNLIKELY(nsContentUtils::ShouldResistFingerprinting())) {
2147 return 2;
2150 // This needs to be atomic, because multiple workers, and even mainthread,
2151 // could race to initialize it at once.
2152 static Atomic<uint32_t> clampedHardwareConcurrency;
2154 // No need to loop here: if compareExchange fails, that just means that some
2155 // other worker has initialized numberOfProcessors, so we're good to go.
2156 if (!clampedHardwareConcurrency) {
2157 int32_t numberOfProcessors = 0;
2158 #if defined(XP_MACOSX)
2159 if (nsMacUtilsImpl::IsTCSMAvailable()) {
2160 // On failure, zero is returned from GetPhysicalCPUCount()
2161 // and we fallback to PR_GetNumberOfProcessors below.
2162 numberOfProcessors = nsMacUtilsImpl::GetPhysicalCPUCount();
2164 #endif
2165 if (numberOfProcessors == 0) {
2166 numberOfProcessors = PR_GetNumberOfProcessors();
2168 if (numberOfProcessors <= 0) {
2169 numberOfProcessors = 1; // Must be one there somewhere
2171 uint32_t clampedValue =
2172 std::min(uint32_t(numberOfProcessors), gMaxHardwareConcurrency);
2173 Unused << clampedHardwareConcurrency.compareExchange(0, clampedValue);
2176 return clampedHardwareConcurrency;
2179 // nsISupports
2180 NS_IMPL_ISUPPORTS(RuntimeService, nsIObserver)
2182 // nsIObserver
2183 NS_IMETHODIMP
2184 RuntimeService::Observe(nsISupports* aSubject, const char* aTopic,
2185 const char16_t* aData) {
2186 AssertIsOnMainThread();
2188 if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
2189 Shutdown();
2190 return NS_OK;
2192 if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID)) {
2193 Cleanup();
2194 return NS_OK;
2196 if (!strcmp(aTopic, GC_REQUEST_OBSERVER_TOPIC)) {
2197 GarbageCollectAllWorkers(/* shrinking = */ false);
2198 return NS_OK;
2200 if (!strcmp(aTopic, CC_REQUEST_OBSERVER_TOPIC)) {
2201 CycleCollectAllWorkers();
2202 return NS_OK;
2204 if (!strcmp(aTopic, MEMORY_PRESSURE_OBSERVER_TOPIC)) {
2205 GarbageCollectAllWorkers(/* shrinking = */ true);
2206 CycleCollectAllWorkers();
2207 MemoryPressureAllWorkers();
2208 return NS_OK;
2210 if (!strcmp(aTopic, NS_IOSERVICE_OFFLINE_STATUS_TOPIC)) {
2211 SendOfflineStatusChangeEventToAllWorkers(NS_IsOffline());
2212 return NS_OK;
2215 MOZ_ASSERT_UNREACHABLE("Unknown observer topic!");
2216 return NS_OK;
2219 bool LogViolationDetailsRunnable::MainThreadRun() {
2220 AssertIsOnMainThread();
2222 nsIContentSecurityPolicy* csp = mWorkerPrivate->GetCSP();
2223 if (csp) {
2224 if (mWorkerPrivate->GetReportCSPViolations()) {
2225 csp->LogViolationDetails(nsIContentSecurityPolicy::VIOLATION_TYPE_EVAL,
2226 nullptr, // triggering element
2227 mWorkerPrivate->CSPEventListener(), mFileName,
2228 mScriptSample, mLineNum, mColumnNum,
2229 EmptyString(), EmptyString());
2233 return true;
2236 // MOZ_CAN_RUN_SCRIPT_BOUNDARY until Runnable::Run is MOZ_CAN_RUN_SCRIPT. See
2237 // bug 1535398.
2238 MOZ_CAN_RUN_SCRIPT_BOUNDARY
2239 NS_IMETHODIMP
2240 WorkerThreadPrimaryRunnable::Run() {
2241 AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING(
2242 "WorkerThreadPrimaryRunnable::Run", OTHER, mWorkerPrivate->ScriptURL());
2244 using mozilla::ipc::BackgroundChild;
2246 // Note: GetOrCreateForCurrentThread() must be called prior to
2247 // mWorkerPrivate->SetThread() in order to avoid accidentally consuming
2248 // worker messages here.
2249 bool ipcReady = true;
2250 if (NS_WARN_IF(!BackgroundChild::GetOrCreateForCurrentThread())) {
2251 // Let's report the error only after SetThread().
2252 ipcReady = false;
2255 class MOZ_STACK_CLASS SetThreadHelper final {
2256 // Raw pointer: this class is on the stack.
2257 WorkerPrivate* mWorkerPrivate;
2258 RefPtr<AbstractThread> mAbstractThread;
2260 public:
2261 SetThreadHelper(WorkerPrivate* aWorkerPrivate, WorkerThread* aThread)
2262 : mWorkerPrivate(aWorkerPrivate),
2263 mAbstractThread(AbstractThread::CreateXPCOMThreadWrapper(
2264 NS_GetCurrentThread(), false)) {
2265 MOZ_ASSERT(aWorkerPrivate);
2266 MOZ_ASSERT(aThread);
2268 mWorkerPrivate->SetWorkerPrivateInWorkerThread(aThread);
2271 ~SetThreadHelper() {
2272 if (mWorkerPrivate) {
2273 mWorkerPrivate->ResetWorkerPrivateInWorkerThread();
2277 void Nullify() {
2278 MOZ_ASSERT(mWorkerPrivate);
2279 mWorkerPrivate->ResetWorkerPrivateInWorkerThread();
2280 mWorkerPrivate = nullptr;
2284 SetThreadHelper threadHelper(mWorkerPrivate, mThread);
2286 mWorkerPrivate->AssertIsOnWorkerThread();
2288 if (!ipcReady) {
2289 WorkerErrorReport::CreateAndDispatchGenericErrorRunnableToParent(
2290 mWorkerPrivate);
2291 return NS_ERROR_FAILURE;
2295 nsCycleCollector_startup();
2297 auto context = MakeUnique<WorkerJSContext>(mWorkerPrivate);
2298 nsresult rv = context->Initialize(mParentRuntime);
2299 if (NS_WARN_IF(NS_FAILED(rv))) {
2300 return rv;
2303 JSContext* cx = context->Context();
2305 if (!InitJSContextForWorker(mWorkerPrivate, cx)) {
2306 WorkerErrorReport::CreateAndDispatchGenericErrorRunnableToParent(
2307 mWorkerPrivate);
2308 return NS_ERROR_FAILURE;
2312 PROFILER_SET_JS_CONTEXT(cx);
2315 // We're on the worker thread here, and WorkerPrivate's refcounting is
2316 // non-threadsafe: you can only do it on the parent thread. What that
2317 // means in practice is that we're relying on it being kept alive while
2318 // we run. Hopefully.
2319 MOZ_KnownLive(mWorkerPrivate)->DoRunLoop(cx);
2320 // The AutoJSAPI in DoRunLoop should have reported any exceptions left
2321 // on cx.
2322 MOZ_ASSERT(!JS_IsExceptionPending(cx));
2325 BackgroundChild::CloseForCurrentThread();
2327 PROFILER_CLEAR_JS_CONTEXT();
2330 // There may still be runnables on the debugger event queue that hold a
2331 // strong reference to the debugger global scope. These runnables are not
2332 // visible to the cycle collector, so we need to make sure to clear the
2333 // debugger event queue before we try to destroy the context. If we don't,
2334 // the garbage collector will crash.
2335 mWorkerPrivate->ClearDebuggerEventQueue();
2337 // Perform a full GC. This will collect the main worker global and CC,
2338 // which should break all cycles that touch JS.
2339 JS_GC(cx, JS::GCReason::WORKER_SHUTDOWN);
2341 // Before shutting down the cycle collector we need to do one more pass
2342 // through the event loop to clean up any C++ objects that need deferred
2343 // cleanup.
2344 mWorkerPrivate->ClearMainEventQueue(WorkerPrivate::WorkerRan);
2346 // Now WorkerJSContext goes out of scope and its destructor will shut
2347 // down the cycle collector. This breaks any remaining cycles and collects
2348 // any remaining C++ objects.
2351 threadHelper.Nullify();
2353 mWorkerPrivate->ScheduleDeletion(WorkerPrivate::WorkerRan);
2355 // It is no longer safe to touch mWorkerPrivate.
2356 mWorkerPrivate = nullptr;
2358 // Now recycle this thread.
2359 nsCOMPtr<nsIEventTarget> mainTarget = GetMainThreadEventTarget();
2360 MOZ_ASSERT(mainTarget);
2362 RefPtr<FinishedRunnable> finishedRunnable =
2363 new FinishedRunnable(mThread.forget());
2364 MOZ_ALWAYS_SUCCEEDS(
2365 mainTarget->Dispatch(finishedRunnable, NS_DISPATCH_NORMAL));
2367 return NS_OK;
2370 NS_IMETHODIMP
2371 WorkerThreadPrimaryRunnable::FinishedRunnable::Run() {
2372 AssertIsOnMainThread();
2374 RefPtr<WorkerThread> thread;
2375 mThread.swap(thread);
2377 RuntimeService* rts = RuntimeService::GetService();
2378 if (rts) {
2379 rts->NoteIdleThread(thread);
2380 } else if (thread->ShutdownRequired()) {
2381 MOZ_ALWAYS_SUCCEEDS(thread->Shutdown());
2384 return NS_OK;
2387 } // namespace workerinternals
2389 void CancelWorkersForWindow(nsPIDOMWindowInner* aWindow) {
2390 AssertIsOnMainThread();
2391 RuntimeService* runtime = RuntimeService::GetService();
2392 if (runtime) {
2393 runtime->CancelWorkersForWindow(aWindow);
2397 void FreezeWorkersForWindow(nsPIDOMWindowInner* aWindow) {
2398 AssertIsOnMainThread();
2399 RuntimeService* runtime = RuntimeService::GetService();
2400 if (runtime) {
2401 runtime->FreezeWorkersForWindow(aWindow);
2405 void ThawWorkersForWindow(nsPIDOMWindowInner* aWindow) {
2406 AssertIsOnMainThread();
2407 RuntimeService* runtime = RuntimeService::GetService();
2408 if (runtime) {
2409 runtime->ThawWorkersForWindow(aWindow);
2413 void SuspendWorkersForWindow(nsPIDOMWindowInner* aWindow) {
2414 AssertIsOnMainThread();
2415 RuntimeService* runtime = RuntimeService::GetService();
2416 if (runtime) {
2417 runtime->SuspendWorkersForWindow(aWindow);
2421 void ResumeWorkersForWindow(nsPIDOMWindowInner* aWindow) {
2422 AssertIsOnMainThread();
2423 RuntimeService* runtime = RuntimeService::GetService();
2424 if (runtime) {
2425 runtime->ResumeWorkersForWindow(aWindow);
2429 void PropagateFirstPartyStorageAccessGrantedToWorkers(
2430 nsPIDOMWindowInner* aWindow) {
2431 AssertIsOnMainThread();
2432 MOZ_ASSERT_IF(
2433 aWindow->GetExtantDoc(),
2434 aWindow->GetExtantDoc()->CookieSettings()->GetCookieBehavior() ==
2435 nsICookieService::BEHAVIOR_REJECT_TRACKER);
2437 RuntimeService* runtime = RuntimeService::GetService();
2438 if (runtime) {
2439 runtime->PropagateFirstPartyStorageAccessGranted(aWindow);
2443 WorkerPrivate* GetWorkerPrivateFromContext(JSContext* aCx) {
2444 MOZ_ASSERT(!NS_IsMainThread());
2445 MOZ_ASSERT(aCx);
2447 CycleCollectedJSContext* ccjscx = CycleCollectedJSContext::GetFor(aCx);
2448 if (!ccjscx) {
2449 return nullptr;
2452 WorkerJSContext* workerjscx = ccjscx->GetAsWorkerJSContext();
2453 // GetWorkerPrivateFromContext is called only for worker contexts. The
2454 // context private is cleared early in ~CycleCollectedJSContext() and so
2455 // GetFor() returns null above if called after ccjscx is no longer a
2456 // WorkerJSContext.
2457 MOZ_ASSERT(workerjscx);
2458 return workerjscx->GetWorkerPrivate();
2461 WorkerPrivate* GetCurrentThreadWorkerPrivate() {
2462 MOZ_ASSERT(!NS_IsMainThread());
2464 CycleCollectedJSContext* ccjscx = CycleCollectedJSContext::Get();
2465 if (!ccjscx) {
2466 return nullptr;
2469 WorkerJSContext* workerjscx = ccjscx->GetAsWorkerJSContext();
2470 // Although GetCurrentThreadWorkerPrivate() is called only for worker
2471 // threads, the ccjscx will no longer be a WorkerJSContext if called from
2472 // stable state events during ~CycleCollectedJSContext().
2473 if (!workerjscx) {
2474 return nullptr;
2477 return workerjscx->GetWorkerPrivate();
2480 bool IsCurrentThreadRunningWorker() {
2481 return !NS_IsMainThread() && !!GetCurrentThreadWorkerPrivate();
2484 bool IsCurrentThreadRunningChromeWorker() {
2485 return GetCurrentThreadWorkerPrivate()->UsesSystemPrincipal();
2488 JSContext* GetCurrentWorkerThreadJSContext() {
2489 WorkerPrivate* wp = GetCurrentThreadWorkerPrivate();
2490 if (!wp) {
2491 return nullptr;
2493 return wp->GetJSContext();
2496 JSObject* GetCurrentThreadWorkerGlobal() {
2497 WorkerPrivate* wp = GetCurrentThreadWorkerPrivate();
2498 if (!wp) {
2499 return nullptr;
2501 WorkerGlobalScope* scope = wp->GlobalScope();
2502 if (!scope) {
2503 return nullptr;
2505 return scope->GetGlobalJSObject();
2508 JSObject* GetCurrentThreadWorkerDebuggerGlobal() {
2509 WorkerPrivate* wp = GetCurrentThreadWorkerPrivate();
2510 if (!wp) {
2511 return nullptr;
2513 WorkerDebuggerGlobalScope* scope = wp->DebuggerGlobalScope();
2514 if (!scope) {
2515 return nullptr;
2517 return scope->GetGlobalJSObject();
2520 } // namespace dom
2521 } // namespace mozilla