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