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"
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"
25 #include "nsIXULRuntime.h"
26 #include "nsPIDOMWindow.h"
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"
70 #include "nsXPCOMPrivate.h"
71 #include "OSFileConstants.h"
72 #include "xpcpublic.h"
74 #if defined(XP_MACOSX)
75 # include "nsMacUtilsImpl.h"
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"
88 #define WORKERS_SHUTDOWN_TOPIC "web-workers-shutdown"
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
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
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, ...) \
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__); \
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
);
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
{
185 void RebindLiteral(const char (&aStr
)[N
]) {
190 template <typename T
>
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
;
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
;
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());
241 prefName
.AssignLiteral(PREF_JS_OPTIONS_PREFIX
);
242 prefName
.Append(aPref
);
244 if (PrefHelper::Exists(prefName
.get())) {
245 result
= PrefHelper::Get(prefName
.get());
254 void LoadContextOptions(const char* aPrefName
, void* /* aClosure */) {
255 AssertIsOnMainThread();
257 RuntimeService
* rts
= RuntimeService::GetService();
259 // May be shutting down, just bail.
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(
269 NS_LITERAL_CSTRING(PREF_JS_OPTIONS_PREFIX PREF_MEM_OPTIONS_PREFIX
)) ||
271 prefName
, NS_LITERAL_CSTRING(
272 PREF_WORKERS_OPTIONS_PREFIX PREF_MEM_OPTIONS_PREFIX
))) {
277 if (prefName
.EqualsLiteral(PREF_JS_OPTIONS_PREFIX PREF_GCZEAL
) ||
278 prefName
.EqualsLiteral(PREF_WORKERS_OPTIONS_PREFIX PREF_GCZEAL
)) {
284 JS::ContextOptions contextOptions
;
285 contextOptions
.setAsmJS(GetWorkerPref
<bool>(NS_LITERAL_CSTRING("asmjs")))
286 .setWasm(GetWorkerPref
<bool>(NS_LITERAL_CSTRING("wasm")))
288 GetWorkerPref
<bool>(NS_LITERAL_CSTRING("wasm_baselinejit")))
289 .setWasmIon(GetWorkerPref
<bool>(NS_LITERAL_CSTRING("wasm_ionjit")))
290 #ifdef ENABLE_WASM_CRANELIFT
292 GetWorkerPref
<bool>(NS_LITERAL_CSTRING("wasm_cranelift")))
294 #ifdef ENABLE_WASM_REFTYPES
295 .setWasmGc(GetWorkerPref
<bool>(NS_LITERAL_CSTRING("wasm_gc")))
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")))
306 .setFuzzing(GetWorkerPref
<bool>(NS_LITERAL_CSTRING("fuzzing.enabled")))
308 .setExtraWarnings(GetWorkerPref
<bool>(NS_LITERAL_CSTRING("strict")));
310 nsCOMPtr
<nsIXULRuntime
> xr
= do_GetService("@mozilla.org/xre/runtime;1");
312 bool safeMode
= false;
313 xr
->GetInSafeMode(&safeMode
);
315 contextOptions
.disableOptionsForSafeMode();
319 RuntimeService::SetDefaultContextOptions(contextOptions
);
322 rts
->UpdateAllWorkerContextOptions();
327 void LoadGCZealOptions(const char* /* aPrefName */, void* /* aClosure */) {
328 AssertIsOnMainThread();
330 RuntimeService
* rts
= RuntimeService::GetService();
332 // May be shutting down, just bail.
336 int32_t gczeal
= GetWorkerPref
<int32_t>(NS_LITERAL_CSTRING(PREF_GCZEAL
), -1);
342 GetWorkerPref
<int32_t>(NS_LITERAL_CSTRING("gcZeal.frequency"), -1);
344 frequency
= JS_DEFAULT_ZEAL_FREQ
;
347 RuntimeService::SetDefaultGCZeal(uint8_t(gczeal
), uint32_t(frequency
));
350 rts
->UpdateAllWorkerGCZeal();
355 void UpdateCommonJSGCMemoryOption(RuntimeService
* aRuntimeService
,
356 const nsACString
& aPrefName
,
358 AssertIsOnMainThread();
359 NS_ASSERTION(!aPrefName
.IsEmpty(), "Empty pref name!");
361 int32_t prefValue
= GetWorkerPref(aPrefName
, -1);
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();
389 // May be shutting down, just bail.
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
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());
406 NS_ERROR("Unknown pref name!");
411 // During Init() we get called back with a branch string here, so there should
412 // be no just a "mem." pref here.
414 NS_ASSERTION(memPrefName
.EqualsLiteral(PREF_MEM_OPTIONS_PREFIX
), "Huh?!");
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
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)
431 : uint32_t(prefValue
) * 1024 * 1024;
432 UpdateOtherJSGCMemoryOption(rts
, JSGC_MAX_BYTES
, value
);
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);
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
);
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
);
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
);
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
);
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
);
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
);
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
);
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);
503 (prefValue
<= 0 || prefValue
>= 100000) ? 0 : uint32_t(prefValue
);
504 UpdateOtherJSGCMemoryOption(rts
, JSGC_SLICE_TIME_BUDGET
, value
);
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
,
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
,
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
);
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
);
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
,
550 nsAutoCString
message("Workers don't support the 'mem.");
551 message
.Append(memPrefName
);
552 message
.AppendLiteral("' preference!");
553 NS_WARNING(message
.get());
558 bool InterruptCallback(JSContext
* aCx
) {
559 WorkerPrivate
* worker
= GetWorkerPrivateFromContext(aCx
);
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()) {
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
{
579 nsString mScriptSample
;
582 LogViolationDetailsRunnable(WorkerPrivate
* aWorker
, const nsString
& aFileName
,
583 uint32_t aLineNum
, uint32_t aColumnNum
,
584 const nsAString
& aScriptSample
)
585 : WorkerMainThreadRunnable(
587 NS_LITERAL_CSTRING("RuntimeService :: LogViolationDetails")),
588 mFileName(aFileName
),
590 mColumnNum(aColumnNum
),
591 mScriptSample(aScriptSample
) {
595 virtual bool MainThreadRun() override
;
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
);
612 nsAutoJSString scriptSample
;
613 if (NS_WARN_IF(!scriptSample
.init(aCx
, jsString
))) {
614 JS_ClearPendingException(aCx
);
619 uint32_t lineNum
= 0;
620 uint32_t columnNum
= 0;
622 JS::AutoFilename file
;
623 if (JS::DescribeScriptedCaller(aCx
, &file
, &lineNum
, &columnNum
) &&
625 fileName
= NS_ConvertUTF8toUTF16(file
.get());
627 MOZ_ASSERT(!JS_IsExceptionPending(aCx
));
630 RefPtr
<LogViolationDetailsRunnable
> runnable
=
631 new LogViolationDetailsRunnable(worker
, fileName
, lineNum
, columnNum
,
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();
649 case js::CTYPES_CALL_BEGIN
:
650 worker
->BeginCTypesCall();
653 case js::CTYPES_CALL_END
:
654 worker
->EndCTypesCall();
657 case js::CTYPES_CALLBACK_BEGIN
:
658 worker
->BeginCTypesCallback();
661 case js::CTYPES_CALLBACK_END
:
662 worker
->EndCTypesCallback();
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
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;
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
);
716 mDispatchable
->run(mWorkerPrivate
->GetJSContext(),
717 JS::Dispatchable::NotShuttingDown
);
718 mDispatchable
= nullptr; // mDispatchable may delete itself
723 nsresult
Cancel() override
{
724 MOZ_ASSERT(mDispatchable
);
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
);
758 JS_ReportErrorNumberASCII(aCx
, js::GetErrorMessage
, nullptr,
759 JSMSG_ERROR_CONSUMING_RESPONSE
);
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!");
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
);
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!");
807 JS_AddInterruptCallback(aWorkerCx
, InterruptCallback
);
809 js::SetCTypesActivityCallback(aWorkerCx
, CTypesActivityCallback
);
812 JS_SetGCZeal(aWorkerCx
, settings
.gcZeal
, settings
.gcZealFrequency
);
818 static bool PreserveWrapper(JSContext
* cx
, JS::HandleObject 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
)) {
831 cx
, "There should be no edges from the debuggee to the debugger.");
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
;
843 wrapper
= &js::OpaqueCrossCompartmentWrapper::singleton
;
847 js::Wrapper::Renew(existing
, obj
, wrapper
);
849 return js::Wrapper::New(cx
, obj
, wrapper
);
852 static const JSWrapObjectCallbacks WrapObjectCallbacks
= {
857 class WorkerJSRuntime final
: public mozilla::CycleCollectedJSRuntime
{
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
);
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.
910 mWorkerPrivate
->AssertIsOnWorkerThread();
912 if (aStatus
== JSGC_END
) {
913 nsCycleCollector_collect(nullptr);
918 WorkerPrivate
* mWorkerPrivate
;
921 } // anonymous namespace
923 } // namespace workerinternals
925 class WorkerJSContext final
: public mozilla::CycleCollectedJSContext
{
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
942 MOZ_CAN_RUN_SCRIPT_BOUNDARY
~WorkerJSContext() {
943 MOZ_COUNT_DTOR_INHERITED(WorkerJSContext
, CycleCollectedJSContext
);
944 JSContext
* cx
= MaybeContext();
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
))) {
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
);
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();
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
; }
1025 WorkerPrivate
* mWorkerPrivate
;
1028 namespace workerinternals
{
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
;
1041 explicit FinishedRunnable(already_AddRefed
<WorkerThread
> aThread
)
1042 : Runnable("WorkerThreadPrimaryRunnable::FinishedRunnable"),
1044 MOZ_ASSERT(mThread
);
1047 NS_INLINE_DECL_REFCOUNTING_INHERITED(FinishedRunnable
, Runnable
)
1050 ~FinishedRunnable() {}
1056 WorkerThreadPrimaryRunnable(WorkerPrivate
* aWorkerPrivate
,
1057 WorkerThread
* aThread
, JSRuntime
* aParentRuntime
)
1058 : mozilla::Runnable("WorkerThreadPrimaryRunnable"),
1059 mWorkerPrivate(aWorkerPrivate
),
1061 mParentRuntime(aParentRuntime
) {
1062 MOZ_ASSERT(aWorkerPrivate
);
1063 MOZ_ASSERT(aThread
);
1066 NS_INLINE_DECL_REFCOUNTING_INHERITED(WorkerThreadPrimaryRunnable
, Runnable
)
1069 ~WorkerThreadPrimaryRunnable() {}
1074 void PrefLanguagesChanged(const char* /* aPrefName */, void* /* aClosure */) {
1075 AssertIsOnMainThread();
1077 nsTArray
<nsString
> languages
;
1078 Navigator::GetAcceptLanguages(languages
);
1080 RuntimeService
* runtime
= RuntimeService::GetService();
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();
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();
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();
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"),
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;
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;
1168 return gRuntimeService
;
1172 RuntimeService
* RuntimeService::GetService() { return gRuntimeService
; }
1174 bool RuntimeService::RegisterWorker(WorkerPrivate
* aWorkerPrivate
) {
1175 aWorkerPrivate
->AssertIsOnParentThread();
1177 WorkerPrivate
* parent
= aWorkerPrivate
->GetParent();
1179 AssertIsOnMainThread();
1181 if (mShuttingDown
) {
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?!");
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
;
1232 queued
= gMaxWorkersPerDomain
&&
1233 domainInfo
->ActiveWorkerCount() >= gMaxWorkersPerDomain
&&
1234 !domain
.IsEmpty() && !exemptFromPerDomainMax
;
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
);
1255 domainInfo
->mActiveWorkers
.AppendElement(aWorkerPrivate
);
1259 // From here on out we must call UnregisterWorker if something fails!
1261 if (!parent
->AddChildWorker(aWorkerPrivate
)) {
1262 UnregisterWorker(aWorkerPrivate
);
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
);
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
);
1297 MOZ_ASSERT(aWorkerPrivate
->IsSharedWorker());
1302 if (!queued
&& !ScheduleWorker(aWorkerPrivate
)) {
1306 if (isServiceWorker
) {
1307 AssertIsOnMainThread();
1308 Telemetry::Accumulate(Telemetry::SERVICE_WORKER_WAS_SPAWNED
, 1);
1313 void RuntimeService::UnregisterWorker(WorkerPrivate
* aWorkerPrivate
) {
1314 aWorkerPrivate
->AssertIsOnParentThread();
1316 WorkerPrivate
* parent
= aWorkerPrivate
->GetParent();
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
);
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
);
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.
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()) {
1400 } else if (aWorkerPrivate
->IsDedicatedWorker()) {
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()) {
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.
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
;
1437 thread
= WorkerThread::Create(friendKey
);
1439 UnregisterWorker(aWorkerPrivate
);
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
));
1457 thread
->DispatchPrimaryRunnable(friendKey
, runnable
.forget()))) {
1458 UnregisterWorker(aWorkerPrivate
);
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();
1486 IdleThreadInfo
& info
= runtime
->mIdleThreadArray
[index
];
1487 if (info
.mExpirationTime
> now
) {
1488 nextExpiration
= info
.mExpirationTime
;
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
;
1529 sDefaultJSSettings
.gcZealFrequency
= JS_DEFAULT_ZEAL_FREQ
;
1530 sDefaultJSSettings
.gcZeal
= 0;
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.
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
);
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!");
1567 obs
->AddObserver(this, MEMORY_PRESSURE_OBSERVER_TOPIC
, false))) {
1568 NS_WARNING("Failed to register for memory pressure notifications!");
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
)) ||
1586 NS_FAILED(Preferences::RegisterCallback(
1587 LoadGCZealOptions
, PREF_JS_OPTIONS_PREFIX PREF_GCZEAL
)) ||
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
)
1597 WORKER_PREF("dom.workers.options.gcZeal", LoadGCZealOptions
)
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();
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
,
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!");
1686 class CrashIfHangingRunnable
: public WorkerControlRunnable
{
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
);
1700 nsresult
Cancel() override
{
1701 mMsg
.Assign("Canceled");
1703 MonitorAutoLock
lock(mMonitor
);
1709 void DispatchAndWait() {
1710 MonitorAutoLock
lock(mMonitor
);
1713 mMsg
.Assign("Dispatch Error");
1720 const nsCString
& MsgData() const { return mMsg
; }
1723 bool PreDispatch(WorkerPrivate
* aWorkerPrivate
) override
{ return true; }
1725 void PostDispatch(WorkerPrivate
* aWorkerPrivate
,
1726 bool aDispatchResult
) override
{}
1734 void RuntimeService::CrashIfHanging() {
1735 MutexAutoLock
lock(mMutex
);
1737 if (mDomainMap
.IsEmpty()) {
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()) {
1765 // We must have something pending...
1766 MOZ_DIAGNOSTIC_ASSERT(activeWorkers
+ activeServiceWorkers
+ inactiveWorkers
);
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
];
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
) {
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
1846 while (mDomainMap
.Count()) {
1847 MutexAutoUnlock
unlock(mMutex
);
1849 if (!NS_ProcessNextEvent(currentThread
)) {
1850 NS_WARNING("Something bad happened!");
1857 NS_ASSERTION(!mWindowMap
.Count(), "All windows should have been released!");
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
)
1872 WORKER_PREF("dom.workers.options.gcZeal", LoadGCZealOptions
)
1877 NS_FAILED(Preferences::UnregisterCallback(
1879 PREF_JS_OPTIONS_PREFIX PREF_GCZEAL
)) ||
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!");
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!");
1900 obs
->RemoveObserver(this, MEMORY_PRESSURE_OBSERVER_TOPIC
))) {
1901 NS_WARNING("Failed to unregister for memory pressure notifications!");
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
);
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();
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();
1929 MOZ_ASSERT(!aData
->mActiveServiceWorkers
[index
]->GetParent(),
1930 "Shouldn't have a parent in this list!");
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
);
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());
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
);
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;
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
,
2116 BROADCAST_ALL_WORKERS(UpdateJSWorkerMemoryParameter
, aKey
, aValue
);
2120 void RuntimeService::UpdateAllWorkerGCZeal() {
2121 BROADCAST_ALL_WORKERS(UpdateGCZeal
, sDefaultJSSettings
.gcZeal
,
2122 sDefaultJSSettings
.gcZealFrequency
);
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())) {
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();
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
;
2180 NS_IMPL_ISUPPORTS(RuntimeService
, nsIObserver
)
2184 RuntimeService::Observe(nsISupports
* aSubject
, const char* aTopic
,
2185 const char16_t
* aData
) {
2186 AssertIsOnMainThread();
2188 if (!strcmp(aTopic
, NS_XPCOM_SHUTDOWN_OBSERVER_ID
)) {
2192 if (!strcmp(aTopic
, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID
)) {
2196 if (!strcmp(aTopic
, GC_REQUEST_OBSERVER_TOPIC
)) {
2197 GarbageCollectAllWorkers(/* shrinking = */ false);
2200 if (!strcmp(aTopic
, CC_REQUEST_OBSERVER_TOPIC
)) {
2201 CycleCollectAllWorkers();
2204 if (!strcmp(aTopic
, MEMORY_PRESSURE_OBSERVER_TOPIC
)) {
2205 GarbageCollectAllWorkers(/* shrinking = */ true);
2206 CycleCollectAllWorkers();
2207 MemoryPressureAllWorkers();
2210 if (!strcmp(aTopic
, NS_IOSERVICE_OFFLINE_STATUS_TOPIC
)) {
2211 SendOfflineStatusChangeEventToAllWorkers(NS_IsOffline());
2215 MOZ_ASSERT_UNREACHABLE("Unknown observer topic!");
2219 bool LogViolationDetailsRunnable::MainThreadRun() {
2220 AssertIsOnMainThread();
2222 nsIContentSecurityPolicy
* csp
= mWorkerPrivate
->GetCSP();
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());
2236 // MOZ_CAN_RUN_SCRIPT_BOUNDARY until Runnable::Run is MOZ_CAN_RUN_SCRIPT. See
2238 MOZ_CAN_RUN_SCRIPT_BOUNDARY
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().
2255 class MOZ_STACK_CLASS SetThreadHelper final
{
2256 // Raw pointer: this class is on the stack.
2257 WorkerPrivate
* mWorkerPrivate
;
2258 RefPtr
<AbstractThread
> mAbstractThread
;
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();
2278 MOZ_ASSERT(mWorkerPrivate
);
2279 mWorkerPrivate
->ResetWorkerPrivateInWorkerThread();
2280 mWorkerPrivate
= nullptr;
2284 SetThreadHelper
threadHelper(mWorkerPrivate
, mThread
);
2286 mWorkerPrivate
->AssertIsOnWorkerThread();
2289 WorkerErrorReport::CreateAndDispatchGenericErrorRunnableToParent(
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
))) {
2303 JSContext
* cx
= context
->Context();
2305 if (!InitJSContextForWorker(mWorkerPrivate
, cx
)) {
2306 WorkerErrorReport::CreateAndDispatchGenericErrorRunnableToParent(
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
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
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
));
2371 WorkerThreadPrimaryRunnable::FinishedRunnable::Run() {
2372 AssertIsOnMainThread();
2374 RefPtr
<WorkerThread
> thread
;
2375 mThread
.swap(thread
);
2377 RuntimeService
* rts
= RuntimeService::GetService();
2379 rts
->NoteIdleThread(thread
);
2380 } else if (thread
->ShutdownRequired()) {
2381 MOZ_ALWAYS_SUCCEEDS(thread
->Shutdown());
2387 } // namespace workerinternals
2389 void CancelWorkersForWindow(nsPIDOMWindowInner
* aWindow
) {
2390 AssertIsOnMainThread();
2391 RuntimeService
* runtime
= RuntimeService::GetService();
2393 runtime
->CancelWorkersForWindow(aWindow
);
2397 void FreezeWorkersForWindow(nsPIDOMWindowInner
* aWindow
) {
2398 AssertIsOnMainThread();
2399 RuntimeService
* runtime
= RuntimeService::GetService();
2401 runtime
->FreezeWorkersForWindow(aWindow
);
2405 void ThawWorkersForWindow(nsPIDOMWindowInner
* aWindow
) {
2406 AssertIsOnMainThread();
2407 RuntimeService
* runtime
= RuntimeService::GetService();
2409 runtime
->ThawWorkersForWindow(aWindow
);
2413 void SuspendWorkersForWindow(nsPIDOMWindowInner
* aWindow
) {
2414 AssertIsOnMainThread();
2415 RuntimeService
* runtime
= RuntimeService::GetService();
2417 runtime
->SuspendWorkersForWindow(aWindow
);
2421 void ResumeWorkersForWindow(nsPIDOMWindowInner
* aWindow
) {
2422 AssertIsOnMainThread();
2423 RuntimeService
* runtime
= RuntimeService::GetService();
2425 runtime
->ResumeWorkersForWindow(aWindow
);
2429 void PropagateFirstPartyStorageAccessGrantedToWorkers(
2430 nsPIDOMWindowInner
* aWindow
) {
2431 AssertIsOnMainThread();
2433 aWindow
->GetExtantDoc(),
2434 aWindow
->GetExtantDoc()->CookieSettings()->GetCookieBehavior() ==
2435 nsICookieService::BEHAVIOR_REJECT_TRACKER
);
2437 RuntimeService
* runtime
= RuntimeService::GetService();
2439 runtime
->PropagateFirstPartyStorageAccessGranted(aWindow
);
2443 WorkerPrivate
* GetWorkerPrivateFromContext(JSContext
* aCx
) {
2444 MOZ_ASSERT(!NS_IsMainThread());
2447 CycleCollectedJSContext
* ccjscx
= CycleCollectedJSContext::GetFor(aCx
);
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
2457 MOZ_ASSERT(workerjscx
);
2458 return workerjscx
->GetWorkerPrivate();
2461 WorkerPrivate
* GetCurrentThreadWorkerPrivate() {
2462 MOZ_ASSERT(!NS_IsMainThread());
2464 CycleCollectedJSContext
* ccjscx
= CycleCollectedJSContext::Get();
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().
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();
2493 return wp
->GetJSContext();
2496 JSObject
* GetCurrentThreadWorkerGlobal() {
2497 WorkerPrivate
* wp
= GetCurrentThreadWorkerPrivate();
2501 WorkerGlobalScope
* scope
= wp
->GlobalScope();
2505 return scope
->GetGlobalJSObject();
2508 JSObject
* GetCurrentThreadWorkerDebuggerGlobal() {
2509 WorkerPrivate
* wp
= GetCurrentThreadWorkerPrivate();
2513 WorkerDebuggerGlobalScope
* scope
= wp
->DebuggerGlobalScope();
2517 return scope
->GetGlobalJSObject();
2521 } // namespace mozilla