dom/workers/RuntimeService.cpp

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

mercurial