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