|
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 } |