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