1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/workers/RuntimeService.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,2719 @@ 1.4 +/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ 1.5 +/* vim: set ts=2 et sw=2 tw=80: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "RuntimeService.h" 1.11 + 1.12 +#include "nsIChannel.h" 1.13 +#include "nsIContentSecurityPolicy.h" 1.14 +#include "nsIDocument.h" 1.15 +#include "nsIDOMChromeWindow.h" 1.16 +#include "nsIEffectiveTLDService.h" 1.17 +#include "nsIObserverService.h" 1.18 +#include "nsIPrincipal.h" 1.19 +#include "nsIScriptContext.h" 1.20 +#include "nsIScriptSecurityManager.h" 1.21 +#include "nsISupportsPriority.h" 1.22 +#include "nsITimer.h" 1.23 +#include "nsIURI.h" 1.24 +#include "nsPIDOMWindow.h" 1.25 + 1.26 +#include <algorithm> 1.27 +#include "GeckoProfiler.h" 1.28 +#include "js/OldDebugAPI.h" 1.29 +#include "jsfriendapi.h" 1.30 +#include "mozilla/ArrayUtils.h" 1.31 +#include "mozilla/CycleCollectedJSRuntime.h" 1.32 +#include "mozilla/dom/asmjscache/AsmJSCache.h" 1.33 +#include "mozilla/dom/AtomList.h" 1.34 +#include "mozilla/dom/BindingUtils.h" 1.35 +#include "mozilla/dom/ErrorEventBinding.h" 1.36 +#include "mozilla/dom/EventTargetBinding.h" 1.37 +#include "mozilla/dom/MessageEventBinding.h" 1.38 +#include "mozilla/dom/WorkerBinding.h" 1.39 +#include "mozilla/DebugOnly.h" 1.40 +#include "mozilla/Preferences.h" 1.41 +#include "mozilla/dom/Navigator.h" 1.42 +#include "nsContentUtils.h" 1.43 +#include "nsCycleCollector.h" 1.44 +#include "nsDOMJSUtils.h" 1.45 +#include "nsISupportsImpl.h" 1.46 +#include "nsLayoutStatics.h" 1.47 +#include "nsNetUtil.h" 1.48 +#include "nsServiceManagerUtils.h" 1.49 +#include "nsThread.h" 1.50 +#include "nsThreadUtils.h" 1.51 +#include "nsXPCOM.h" 1.52 +#include "nsXPCOMPrivate.h" 1.53 +#include "OSFileConstants.h" 1.54 +#include "xpcpublic.h" 1.55 + 1.56 +#ifdef MOZ_NUWA_PROCESS 1.57 +#include "ipc/Nuwa.h" 1.58 +#endif 1.59 + 1.60 +#ifdef DEBUG 1.61 +#include "nsThreadManager.h" 1.62 +#endif 1.63 + 1.64 +#include "SharedWorker.h" 1.65 +#include "WorkerPrivate.h" 1.66 +#include "WorkerRunnable.h" 1.67 + 1.68 +using namespace mozilla; 1.69 +using namespace mozilla::dom; 1.70 + 1.71 +USING_WORKERS_NAMESPACE 1.72 + 1.73 +using mozilla::MutexAutoLock; 1.74 +using mozilla::MutexAutoUnlock; 1.75 +using mozilla::Preferences; 1.76 + 1.77 +// The size of the worker runtime heaps in bytes. May be changed via pref. 1.78 +#define WORKER_DEFAULT_RUNTIME_HEAPSIZE 32 * 1024 * 1024 1.79 + 1.80 +// The size of the worker JS allocation threshold in MB. May be changed via pref. 1.81 +#define WORKER_DEFAULT_ALLOCATION_THRESHOLD 30 1.82 + 1.83 +// The C stack size. We use the same stack size on all platforms for 1.84 +// consistency. 1.85 +#define WORKER_STACK_SIZE 256 * sizeof(size_t) * 1024 1.86 + 1.87 +// Half the size of the actual C stack, to be safe. 1.88 +#define WORKER_CONTEXT_NATIVE_STACK_LIMIT 128 * sizeof(size_t) * 1024 1.89 + 1.90 +// The maximum number of threads to use for workers, overridable via pref. 1.91 +#define MAX_WORKERS_PER_DOMAIN 10 1.92 + 1.93 +static_assert(MAX_WORKERS_PER_DOMAIN >= 1, 1.94 + "We should allow at least one worker per domain."); 1.95 + 1.96 +// The default number of seconds that close handlers will be allowed to run for 1.97 +// content workers. 1.98 +#define MAX_SCRIPT_RUN_TIME_SEC 10 1.99 + 1.100 +// The number of seconds that idle threads can hang around before being killed. 1.101 +#define IDLE_THREAD_TIMEOUT_SEC 30 1.102 + 1.103 +// The maximum number of threads that can be idle at one time. 1.104 +#define MAX_IDLE_THREADS 20 1.105 + 1.106 +#define PREF_WORKERS_PREFIX "dom.workers." 1.107 +#define PREF_WORKERS_MAX_PER_DOMAIN PREF_WORKERS_PREFIX "maxPerDomain" 1.108 + 1.109 +#define PREF_MAX_SCRIPT_RUN_TIME_CONTENT "dom.max_script_run_time" 1.110 +#define PREF_MAX_SCRIPT_RUN_TIME_CHROME "dom.max_chrome_script_run_time" 1.111 + 1.112 +#define GC_REQUEST_OBSERVER_TOPIC "child-gc-request" 1.113 +#define CC_REQUEST_OBSERVER_TOPIC "child-cc-request" 1.114 +#define MEMORY_PRESSURE_OBSERVER_TOPIC "memory-pressure" 1.115 + 1.116 +#define PREF_GENERAL_APPNAME_OVERRIDE "general.appname.override" 1.117 +#define PREF_GENERAL_APPVERSION_OVERRIDE "general.appversion.override" 1.118 +#define PREF_GENERAL_PLATFORM_OVERRIDE "general.platform.override" 1.119 + 1.120 +#define BROADCAST_ALL_WORKERS(_func, ...) \ 1.121 + PR_BEGIN_MACRO \ 1.122 + AssertIsOnMainThread(); \ 1.123 + \ 1.124 + nsAutoTArray<WorkerPrivate*, 100> workers; \ 1.125 + { \ 1.126 + MutexAutoLock lock(mMutex); \ 1.127 + \ 1.128 + mDomainMap.EnumerateRead(AddAllTopLevelWorkersToArray, &workers); \ 1.129 + } \ 1.130 + \ 1.131 + if (!workers.IsEmpty()) { \ 1.132 + AutoSafeJSContext cx; \ 1.133 + JSAutoRequest ar(cx); \ 1.134 + for (uint32_t index = 0; index < workers.Length(); index++) { \ 1.135 + workers[index]-> _func (cx, __VA_ARGS__); \ 1.136 + } \ 1.137 + } \ 1.138 + PR_END_MACRO 1.139 + 1.140 +// Prefixes for observing preference changes. 1.141 +#define PREF_JS_OPTIONS_PREFIX "javascript.options." 1.142 +#define PREF_WORKERS_OPTIONS_PREFIX PREF_WORKERS_PREFIX "options." 1.143 +#define PREF_MEM_OPTIONS_PREFIX "mem." 1.144 +#define PREF_GCZEAL "gcZeal" 1.145 + 1.146 +#if !(defined(DEBUG) || defined(MOZ_ENABLE_JS_DUMP)) 1.147 +#define DUMP_CONTROLLED_BY_PREF 1 1.148 +#define PREF_DOM_WINDOW_DUMP_ENABLED "browser.dom.window.dump.enabled" 1.149 +#endif 1.150 + 1.151 +#define PREF_WORKERS_LATEST_JS_VERSION "dom.workers.latestJSVersion" 1.152 + 1.153 +namespace { 1.154 + 1.155 +const uint32_t kNoIndex = uint32_t(-1); 1.156 + 1.157 +const JS::ContextOptions kRequiredContextOptions = 1.158 + JS::ContextOptions().setDontReportUncaught(true) 1.159 + .setNoScriptRval(true); 1.160 + 1.161 +uint32_t gMaxWorkersPerDomain = MAX_WORKERS_PER_DOMAIN; 1.162 + 1.163 +// Does not hold an owning reference. 1.164 +RuntimeService* gRuntimeService = nullptr; 1.165 + 1.166 +// Only non-null during the call to Init. 1.167 +RuntimeService* gRuntimeServiceDuringInit = nullptr; 1.168 + 1.169 +enum { 1.170 + ID_Worker = 0, 1.171 + ID_ChromeWorker, 1.172 + ID_Event, 1.173 + ID_MessageEvent, 1.174 + ID_ErrorEvent, 1.175 + 1.176 + ID_COUNT 1.177 +}; 1.178 + 1.179 +// These are jsids for the main runtime. Only touched on the main thread. 1.180 +jsid gStringIDs[ID_COUNT] = { JSID_VOID }; 1.181 + 1.182 +const char* gStringChars[] = { 1.183 + "Worker", 1.184 + "ChromeWorker", 1.185 + "Event", 1.186 + "MessageEvent", 1.187 + "ErrorEvent" 1.188 + 1.189 + // XXX Don't care about ProgressEvent since it should never leak to the main 1.190 + // thread. 1.191 +}; 1.192 + 1.193 +static_assert(MOZ_ARRAY_LENGTH(gStringChars) == ID_COUNT, 1.194 + "gStringChars should have the right length."); 1.195 + 1.196 +class LiteralRebindingCString : public nsDependentCString 1.197 +{ 1.198 +public: 1.199 + template<int N> 1.200 + void RebindLiteral(const char (&aStr)[N]) 1.201 + { 1.202 + Rebind(aStr, N-1); 1.203 + } 1.204 +}; 1.205 + 1.206 +template <typename T> 1.207 +struct PrefTraits; 1.208 + 1.209 +template <> 1.210 +struct PrefTraits<bool> 1.211 +{ 1.212 + typedef bool PrefValueType; 1.213 + 1.214 + static const PrefValueType kDefaultValue = false; 1.215 + 1.216 + static inline PrefValueType 1.217 + Get(const char* aPref) 1.218 + { 1.219 + AssertIsOnMainThread(); 1.220 + return Preferences::GetBool(aPref); 1.221 + } 1.222 + 1.223 + static inline bool 1.224 + Exists(const char* aPref) 1.225 + { 1.226 + AssertIsOnMainThread(); 1.227 + return Preferences::GetType(aPref) == nsIPrefBranch::PREF_BOOL; 1.228 + } 1.229 +}; 1.230 + 1.231 +template <> 1.232 +struct PrefTraits<int32_t> 1.233 +{ 1.234 + typedef int32_t PrefValueType; 1.235 + 1.236 + static inline PrefValueType 1.237 + Get(const char* aPref) 1.238 + { 1.239 + AssertIsOnMainThread(); 1.240 + return Preferences::GetInt(aPref); 1.241 + } 1.242 + 1.243 + static inline bool 1.244 + Exists(const char* aPref) 1.245 + { 1.246 + AssertIsOnMainThread(); 1.247 + return Preferences::GetType(aPref) == nsIPrefBranch::PREF_INT; 1.248 + } 1.249 +}; 1.250 + 1.251 +template <typename T> 1.252 +T 1.253 +GetWorkerPref(const nsACString& aPref, 1.254 + const T aDefault = PrefTraits<T>::kDefaultValue) 1.255 +{ 1.256 + AssertIsOnMainThread(); 1.257 + 1.258 + typedef PrefTraits<T> PrefHelper; 1.259 + 1.260 + T result; 1.261 + 1.262 + nsAutoCString prefName; 1.263 + prefName.AssignLiteral(PREF_WORKERS_OPTIONS_PREFIX); 1.264 + prefName.Append(aPref); 1.265 + 1.266 + if (PrefHelper::Exists(prefName.get())) { 1.267 + result = PrefHelper::Get(prefName.get()); 1.268 + } 1.269 + else { 1.270 + prefName.AssignLiteral(PREF_JS_OPTIONS_PREFIX); 1.271 + prefName.Append(aPref); 1.272 + 1.273 + if (PrefHelper::Exists(prefName.get())) { 1.274 + result = PrefHelper::Get(prefName.get()); 1.275 + } 1.276 + else { 1.277 + result = aDefault; 1.278 + } 1.279 + } 1.280 + 1.281 + return result; 1.282 +} 1.283 + 1.284 +// This function creates a key for a SharedWorker composed by "name|scriptSpec". 1.285 +// If the name contains a '|', this will be replaced by '||'. 1.286 +void 1.287 +GenerateSharedWorkerKey(const nsACString& aScriptSpec, const nsACString& aName, 1.288 + nsCString& aKey) 1.289 +{ 1.290 + aKey.Truncate(); 1.291 + aKey.SetCapacity(aScriptSpec.Length() + aName.Length() + 1); 1.292 + 1.293 + nsACString::const_iterator start, end; 1.294 + aName.BeginReading(start); 1.295 + aName.EndReading(end); 1.296 + for (; start != end; ++start) { 1.297 + if (*start == '|') { 1.298 + aKey.AppendASCII("||"); 1.299 + } else { 1.300 + aKey.Append(*start); 1.301 + } 1.302 + } 1.303 + 1.304 + aKey.Append('|'); 1.305 + aKey.Append(aScriptSpec); 1.306 +} 1.307 + 1.308 +void 1.309 +LoadRuntimeAndContextOptions(const char* aPrefName, void* /* aClosure */) 1.310 +{ 1.311 + AssertIsOnMainThread(); 1.312 + 1.313 + RuntimeService* rts = RuntimeService::GetService(); 1.314 + if (!rts && !gRuntimeServiceDuringInit) { 1.315 + // May be shutting down, just bail. 1.316 + return; 1.317 + } 1.318 + 1.319 + const nsDependentCString prefName(aPrefName); 1.320 + 1.321 + // Several other pref branches will get included here so bail out if there is 1.322 + // another callback that will handle this change. 1.323 + if (StringBeginsWith(prefName, 1.324 + NS_LITERAL_CSTRING(PREF_JS_OPTIONS_PREFIX 1.325 + PREF_MEM_OPTIONS_PREFIX)) || 1.326 + StringBeginsWith(prefName, 1.327 + NS_LITERAL_CSTRING(PREF_WORKERS_OPTIONS_PREFIX 1.328 + PREF_MEM_OPTIONS_PREFIX))) { 1.329 + return; 1.330 + } 1.331 + 1.332 +#ifdef JS_GC_ZEAL 1.333 + if (prefName.EqualsLiteral(PREF_JS_OPTIONS_PREFIX PREF_GCZEAL) || 1.334 + prefName.EqualsLiteral(PREF_WORKERS_OPTIONS_PREFIX PREF_GCZEAL)) { 1.335 + return; 1.336 + } 1.337 +#endif 1.338 + 1.339 + // Runtime options. 1.340 + JS::RuntimeOptions runtimeOptions; 1.341 + if (GetWorkerPref<bool>(NS_LITERAL_CSTRING("asmjs"))) { 1.342 + runtimeOptions.setAsmJS(true); 1.343 + } 1.344 + if (GetWorkerPref<bool>(NS_LITERAL_CSTRING("baselinejit"))) { 1.345 + runtimeOptions.setBaseline(true); 1.346 + } 1.347 + if (GetWorkerPref<bool>(NS_LITERAL_CSTRING("ion"))) { 1.348 + runtimeOptions.setIon(true); 1.349 + } 1.350 + 1.351 + // Common options. 1.352 + JS::ContextOptions commonContextOptions = kRequiredContextOptions; 1.353 + if (GetWorkerPref<bool>(NS_LITERAL_CSTRING("strict"))) { 1.354 + commonContextOptions.setExtraWarnings(true); 1.355 + } 1.356 + if (GetWorkerPref<bool>(NS_LITERAL_CSTRING("werror"))) { 1.357 + commonContextOptions.setWerror(true); 1.358 + } 1.359 + 1.360 + // Content options. 1.361 + JS::ContextOptions contentContextOptions = commonContextOptions; 1.362 + 1.363 + // Chrome options. 1.364 + JS::ContextOptions chromeContextOptions = commonContextOptions; 1.365 +#ifdef DEBUG 1.366 + if (GetWorkerPref<bool>(NS_LITERAL_CSTRING("strict.debug"))) { 1.367 + chromeContextOptions.setExtraWarnings(true); 1.368 + } 1.369 +#endif 1.370 + 1.371 + RuntimeService::SetDefaultRuntimeAndContextOptions(runtimeOptions, 1.372 + contentContextOptions, 1.373 + chromeContextOptions); 1.374 + 1.375 + if (rts) { 1.376 + rts->UpdateAllWorkerRuntimeAndContextOptions(); 1.377 + } 1.378 +} 1.379 + 1.380 +#ifdef JS_GC_ZEAL 1.381 +void 1.382 +LoadGCZealOptions(const char* /* aPrefName */, void* /* aClosure */) 1.383 +{ 1.384 + AssertIsOnMainThread(); 1.385 + 1.386 + RuntimeService* rts = RuntimeService::GetService(); 1.387 + if (!rts && !gRuntimeServiceDuringInit) { 1.388 + // May be shutting down, just bail. 1.389 + return; 1.390 + } 1.391 + 1.392 + int32_t gczeal = GetWorkerPref<int32_t>(NS_LITERAL_CSTRING(PREF_GCZEAL), -1); 1.393 + if (gczeal < 0) { 1.394 + gczeal = 0; 1.395 + } 1.396 + 1.397 + int32_t frequency = 1.398 + GetWorkerPref<int32_t>(NS_LITERAL_CSTRING("gcZeal.frequency"), -1); 1.399 + if (frequency < 0) { 1.400 + frequency = JS_DEFAULT_ZEAL_FREQ; 1.401 + } 1.402 + 1.403 + RuntimeService::SetDefaultGCZeal(uint8_t(gczeal), uint32_t(frequency)); 1.404 + 1.405 + if (rts) { 1.406 + rts->UpdateAllWorkerGCZeal(); 1.407 + } 1.408 +} 1.409 +#endif 1.410 + 1.411 +void 1.412 +UpdateCommonJSGCMemoryOption(RuntimeService* aRuntimeService, 1.413 + const nsACString& aPrefName, JSGCParamKey aKey) 1.414 +{ 1.415 + AssertIsOnMainThread(); 1.416 + NS_ASSERTION(!aPrefName.IsEmpty(), "Empty pref name!"); 1.417 + 1.418 + int32_t prefValue = GetWorkerPref(aPrefName, -1); 1.419 + uint32_t value = 1.420 + (prefValue < 0 || prefValue >= 10000) ? 0 : uint32_t(prefValue); 1.421 + 1.422 + RuntimeService::SetDefaultJSGCSettings(aKey, value); 1.423 + 1.424 + if (aRuntimeService) { 1.425 + aRuntimeService->UpdateAllWorkerMemoryParameter(aKey, value); 1.426 + } 1.427 +} 1.428 + 1.429 +void 1.430 +UpdatOtherJSGCMemoryOption(RuntimeService* aRuntimeService, 1.431 + JSGCParamKey aKey, uint32_t aValue) 1.432 +{ 1.433 + AssertIsOnMainThread(); 1.434 + 1.435 + RuntimeService::SetDefaultJSGCSettings(aKey, aValue); 1.436 + 1.437 + if (aRuntimeService) { 1.438 + aRuntimeService->UpdateAllWorkerMemoryParameter(aKey, aValue); 1.439 + } 1.440 +} 1.441 + 1.442 + 1.443 +void 1.444 +LoadJSGCMemoryOptions(const char* aPrefName, void* /* aClosure */) 1.445 +{ 1.446 + AssertIsOnMainThread(); 1.447 + 1.448 + RuntimeService* rts = RuntimeService::GetService(); 1.449 + 1.450 + if (!rts && !gRuntimeServiceDuringInit) { 1.451 + // May be shutting down, just bail. 1.452 + return; 1.453 + } 1.454 + 1.455 + NS_NAMED_LITERAL_CSTRING(jsPrefix, PREF_JS_OPTIONS_PREFIX); 1.456 + NS_NAMED_LITERAL_CSTRING(workersPrefix, PREF_WORKERS_OPTIONS_PREFIX); 1.457 + 1.458 + const nsDependentCString fullPrefName(aPrefName); 1.459 + 1.460 + // Pull out the string that actually distinguishes the parameter we need to 1.461 + // change. 1.462 + nsDependentCSubstring memPrefName; 1.463 + if (StringBeginsWith(fullPrefName, jsPrefix)) { 1.464 + memPrefName.Rebind(fullPrefName, jsPrefix.Length()); 1.465 + } 1.466 + else if (StringBeginsWith(fullPrefName, workersPrefix)) { 1.467 + memPrefName.Rebind(fullPrefName, workersPrefix.Length()); 1.468 + } 1.469 + else { 1.470 + NS_ERROR("Unknown pref name!"); 1.471 + return; 1.472 + } 1.473 + 1.474 +#ifdef DEBUG 1.475 + // During Init() we get called back with a branch string here, so there should 1.476 + // be no just a "mem." pref here. 1.477 + if (!rts) { 1.478 + NS_ASSERTION(memPrefName.EqualsLiteral(PREF_MEM_OPTIONS_PREFIX), "Huh?!"); 1.479 + } 1.480 +#endif 1.481 + 1.482 + // If we're running in Init() then do this for every pref we care about. 1.483 + // Otherwise we just want to update the parameter that changed. 1.484 + for (uint32_t index = rts ? JSSettings::kGCSettingsArraySize - 1 : 0; 1.485 + index < JSSettings::kGCSettingsArraySize; 1.486 + index++) { 1.487 + LiteralRebindingCString matchName; 1.488 + 1.489 + matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX "max"); 1.490 + if (memPrefName == matchName || (!rts && index == 0)) { 1.491 + int32_t prefValue = GetWorkerPref(matchName, -1); 1.492 + uint32_t value = (prefValue <= 0 || prefValue >= 0x1000) ? 1.493 + uint32_t(-1) : 1.494 + uint32_t(prefValue) * 1024 * 1024; 1.495 + UpdatOtherJSGCMemoryOption(rts, JSGC_MAX_BYTES, value); 1.496 + continue; 1.497 + } 1.498 + 1.499 + matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX "high_water_mark"); 1.500 + if (memPrefName == matchName || (!rts && index == 1)) { 1.501 + int32_t prefValue = GetWorkerPref(matchName, 128); 1.502 + UpdatOtherJSGCMemoryOption(rts, JSGC_MAX_MALLOC_BYTES, 1.503 + uint32_t(prefValue) * 1024 * 1024); 1.504 + continue; 1.505 + } 1.506 + 1.507 + matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX 1.508 + "gc_high_frequency_time_limit_ms"); 1.509 + if (memPrefName == matchName || (!rts && index == 2)) { 1.510 + UpdateCommonJSGCMemoryOption(rts, matchName, 1.511 + JSGC_HIGH_FREQUENCY_TIME_LIMIT); 1.512 + continue; 1.513 + } 1.514 + 1.515 + matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX 1.516 + "gc_low_frequency_heap_growth"); 1.517 + if (memPrefName == matchName || (!rts && index == 3)) { 1.518 + UpdateCommonJSGCMemoryOption(rts, matchName, 1.519 + JSGC_LOW_FREQUENCY_HEAP_GROWTH); 1.520 + continue; 1.521 + } 1.522 + 1.523 + matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX 1.524 + "gc_high_frequency_heap_growth_min"); 1.525 + if (memPrefName == matchName || (!rts && index == 4)) { 1.526 + UpdateCommonJSGCMemoryOption(rts, matchName, 1.527 + JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MIN); 1.528 + continue; 1.529 + } 1.530 + 1.531 + matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX 1.532 + "gc_high_frequency_heap_growth_max"); 1.533 + if (memPrefName == matchName || (!rts && index == 5)) { 1.534 + UpdateCommonJSGCMemoryOption(rts, matchName, 1.535 + JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MAX); 1.536 + continue; 1.537 + } 1.538 + 1.539 + matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX 1.540 + "gc_high_frequency_low_limit_mb"); 1.541 + if (memPrefName == matchName || (!rts && index == 6)) { 1.542 + UpdateCommonJSGCMemoryOption(rts, matchName, 1.543 + JSGC_HIGH_FREQUENCY_LOW_LIMIT); 1.544 + continue; 1.545 + } 1.546 + 1.547 + matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX 1.548 + "gc_high_frequency_high_limit_mb"); 1.549 + if (memPrefName == matchName || (!rts && index == 7)) { 1.550 + UpdateCommonJSGCMemoryOption(rts, matchName, 1.551 + JSGC_HIGH_FREQUENCY_HIGH_LIMIT); 1.552 + continue; 1.553 + } 1.554 + 1.555 + matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX 1.556 + "gc_allocation_threshold_mb"); 1.557 + if (memPrefName == matchName || (!rts && index == 8)) { 1.558 + UpdateCommonJSGCMemoryOption(rts, matchName, JSGC_ALLOCATION_THRESHOLD); 1.559 + continue; 1.560 + } 1.561 + 1.562 + matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX "gc_incremental_slice_ms"); 1.563 + if (memPrefName == matchName || (!rts && index == 9)) { 1.564 + int32_t prefValue = GetWorkerPref(matchName, -1); 1.565 + uint32_t value = 1.566 + (prefValue <= 0 || prefValue >= 100000) ? 0 : uint32_t(prefValue); 1.567 + UpdatOtherJSGCMemoryOption(rts, JSGC_SLICE_TIME_BUDGET, value); 1.568 + continue; 1.569 + } 1.570 + 1.571 + matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX "gc_dynamic_heap_growth"); 1.572 + if (memPrefName == matchName || (!rts && index == 10)) { 1.573 + bool prefValue = GetWorkerPref(matchName, false); 1.574 + UpdatOtherJSGCMemoryOption(rts, JSGC_DYNAMIC_HEAP_GROWTH, 1.575 + prefValue ? 0 : 1); 1.576 + continue; 1.577 + } 1.578 + 1.579 + matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX "gc_dynamic_mark_slice"); 1.580 + if (memPrefName == matchName || (!rts && index == 11)) { 1.581 + bool prefValue = GetWorkerPref(matchName, false); 1.582 + UpdatOtherJSGCMemoryOption(rts, JSGC_DYNAMIC_MARK_SLICE, 1.583 + prefValue ? 0 : 1); 1.584 + continue; 1.585 + } 1.586 + 1.587 +#ifdef DEBUG 1.588 + nsAutoCString message("Workers don't support the 'mem."); 1.589 + message.Append(memPrefName); 1.590 + message.AppendLiteral("' preference!"); 1.591 + NS_WARNING(message.get()); 1.592 +#endif 1.593 + } 1.594 +} 1.595 + 1.596 +void 1.597 +ErrorReporter(JSContext* aCx, const char* aMessage, JSErrorReport* aReport) 1.598 +{ 1.599 + WorkerPrivate* worker = GetWorkerPrivateFromContext(aCx); 1.600 + MOZ_ASSERT(worker); 1.601 + 1.602 + return worker->ReportError(aCx, aMessage, aReport); 1.603 +} 1.604 + 1.605 +bool 1.606 +InterruptCallback(JSContext* aCx) 1.607 +{ 1.608 + WorkerPrivate* worker = GetWorkerPrivateFromContext(aCx); 1.609 + MOZ_ASSERT(worker); 1.610 + 1.611 + // Now is a good time to turn on profiling if it's pending. 1.612 + profiler_js_operation_callback(); 1.613 + 1.614 + return worker->InterruptCallback(aCx); 1.615 +} 1.616 + 1.617 +class LogViolationDetailsRunnable MOZ_FINAL : public nsRunnable 1.618 +{ 1.619 + WorkerPrivate* mWorkerPrivate; 1.620 + nsCOMPtr<nsIEventTarget> mSyncLoopTarget; 1.621 + nsString mFileName; 1.622 + uint32_t mLineNum; 1.623 + 1.624 +public: 1.625 + LogViolationDetailsRunnable(WorkerPrivate* aWorker, 1.626 + const nsString& aFileName, 1.627 + uint32_t aLineNum) 1.628 + : mWorkerPrivate(aWorker), mFileName(aFileName), mLineNum(aLineNum) 1.629 + { 1.630 + MOZ_ASSERT(aWorker); 1.631 + } 1.632 + 1.633 + NS_DECL_ISUPPORTS_INHERITED 1.634 + 1.635 + bool 1.636 + Dispatch(JSContext* aCx) 1.637 + { 1.638 + AutoSyncLoopHolder syncLoop(mWorkerPrivate); 1.639 + 1.640 + mSyncLoopTarget = syncLoop.EventTarget(); 1.641 + MOZ_ASSERT(mSyncLoopTarget); 1.642 + 1.643 + if (NS_FAILED(NS_DispatchToMainThread(this, NS_DISPATCH_NORMAL))) { 1.644 + JS_ReportError(aCx, "Failed to dispatch to main thread!"); 1.645 + return false; 1.646 + } 1.647 + 1.648 + return syncLoop.Run(); 1.649 + } 1.650 + 1.651 +private: 1.652 + NS_DECL_NSIRUNNABLE 1.653 +}; 1.654 + 1.655 +bool 1.656 +ContentSecurityPolicyAllows(JSContext* aCx) 1.657 +{ 1.658 + WorkerPrivate* worker = GetWorkerPrivateFromContext(aCx); 1.659 + worker->AssertIsOnWorkerThread(); 1.660 + 1.661 + if (worker->GetReportCSPViolations()) { 1.662 + nsString fileName; 1.663 + uint32_t lineNum = 0; 1.664 + 1.665 + JS::AutoFilename file; 1.666 + if (JS::DescribeScriptedCaller(aCx, &file, &lineNum) && file.get()) { 1.667 + fileName = NS_ConvertUTF8toUTF16(file.get()); 1.668 + } else { 1.669 + JS_ReportPendingException(aCx); 1.670 + } 1.671 + 1.672 + nsRefPtr<LogViolationDetailsRunnable> runnable = 1.673 + new LogViolationDetailsRunnable(worker, fileName, lineNum); 1.674 + 1.675 + if (!runnable->Dispatch(aCx)) { 1.676 + JS_ReportPendingException(aCx); 1.677 + } 1.678 + } 1.679 + 1.680 + return worker->IsEvalAllowed(); 1.681 +} 1.682 + 1.683 +void 1.684 +CTypesActivityCallback(JSContext* aCx, 1.685 + js::CTypesActivityType aType) 1.686 +{ 1.687 + WorkerPrivate* worker = GetWorkerPrivateFromContext(aCx); 1.688 + worker->AssertIsOnWorkerThread(); 1.689 + 1.690 + switch (aType) { 1.691 + case js::CTYPES_CALL_BEGIN: 1.692 + worker->BeginCTypesCall(); 1.693 + break; 1.694 + 1.695 + case js::CTYPES_CALL_END: 1.696 + worker->EndCTypesCall(); 1.697 + break; 1.698 + 1.699 + case js::CTYPES_CALLBACK_BEGIN: 1.700 + worker->BeginCTypesCallback(); 1.701 + break; 1.702 + 1.703 + case js::CTYPES_CALLBACK_END: 1.704 + worker->EndCTypesCallback(); 1.705 + break; 1.706 + 1.707 + default: 1.708 + MOZ_CRASH("Unknown type flag!"); 1.709 + } 1.710 +} 1.711 + 1.712 +static nsIPrincipal* 1.713 +GetPrincipalForAsmJSCacheOp() 1.714 +{ 1.715 + WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate(); 1.716 + if (!workerPrivate) { 1.717 + return nullptr; 1.718 + } 1.719 + 1.720 + // asmjscache::OpenEntryForX guarnatee to only access the given nsIPrincipal 1.721 + // from the main thread. 1.722 + return workerPrivate->GetPrincipalDontAssertMainThread(); 1.723 +} 1.724 + 1.725 +static bool 1.726 +AsmJSCacheOpenEntryForRead(JS::Handle<JSObject*> aGlobal, 1.727 + const jschar* aBegin, 1.728 + const jschar* aLimit, 1.729 + size_t* aSize, 1.730 + const uint8_t** aMemory, 1.731 + intptr_t *aHandle) 1.732 +{ 1.733 + nsIPrincipal* principal = GetPrincipalForAsmJSCacheOp(); 1.734 + if (!principal) { 1.735 + return false; 1.736 + } 1.737 + 1.738 + return asmjscache::OpenEntryForRead(principal, aBegin, aLimit, aSize, aMemory, 1.739 + aHandle); 1.740 +} 1.741 + 1.742 +static bool 1.743 +AsmJSCacheOpenEntryForWrite(JS::Handle<JSObject*> aGlobal, 1.744 + bool aInstalled, 1.745 + const jschar* aBegin, 1.746 + const jschar* aEnd, 1.747 + size_t aSize, 1.748 + uint8_t** aMemory, 1.749 + intptr_t* aHandle) 1.750 +{ 1.751 + nsIPrincipal* principal = GetPrincipalForAsmJSCacheOp(); 1.752 + if (!principal) { 1.753 + return false; 1.754 + } 1.755 + 1.756 + return asmjscache::OpenEntryForWrite(principal, aInstalled, aBegin, aEnd, 1.757 + aSize, aMemory, aHandle); 1.758 +} 1.759 + 1.760 +struct WorkerThreadRuntimePrivate : public PerThreadAtomCache 1.761 +{ 1.762 + WorkerPrivate* mWorkerPrivate; 1.763 +}; 1.764 + 1.765 +JSContext* 1.766 +CreateJSContextForWorker(WorkerPrivate* aWorkerPrivate, JSRuntime* aRuntime) 1.767 +{ 1.768 + aWorkerPrivate->AssertIsOnWorkerThread(); 1.769 + NS_ASSERTION(!aWorkerPrivate->GetJSContext(), "Already has a context!"); 1.770 + 1.771 + JSSettings settings; 1.772 + aWorkerPrivate->CopyJSSettings(settings); 1.773 + 1.774 + JS::RuntimeOptionsRef(aRuntime) = settings.runtimeOptions; 1.775 + 1.776 + JSSettings::JSGCSettingsArray& gcSettings = settings.gcSettings; 1.777 + 1.778 + // This is the real place where we set the max memory for the runtime. 1.779 + for (uint32_t index = 0; index < ArrayLength(gcSettings); index++) { 1.780 + const JSSettings::JSGCSetting& setting = gcSettings[index]; 1.781 + if (setting.IsSet()) { 1.782 + NS_ASSERTION(setting.value, "Can't handle 0 values!"); 1.783 + JS_SetGCParameter(aRuntime, setting.key, setting.value); 1.784 + } 1.785 + } 1.786 + 1.787 + JS_SetIsWorkerRuntime(aRuntime); 1.788 + 1.789 + JS_SetNativeStackQuota(aRuntime, WORKER_CONTEXT_NATIVE_STACK_LIMIT); 1.790 + 1.791 + // Security policy: 1.792 + static JSSecurityCallbacks securityCallbacks = { 1.793 + ContentSecurityPolicyAllows 1.794 + }; 1.795 + JS_SetSecurityCallbacks(aRuntime, &securityCallbacks); 1.796 + 1.797 + // DOM helpers: 1.798 + static js::DOMCallbacks DOMCallbacks = { 1.799 + InstanceClassHasProtoAtDepth 1.800 + }; 1.801 + SetDOMCallbacks(aRuntime, &DOMCallbacks); 1.802 + 1.803 + // Set up the asm.js cache callbacks 1.804 + static JS::AsmJSCacheOps asmJSCacheOps = { 1.805 + AsmJSCacheOpenEntryForRead, 1.806 + asmjscache::CloseEntryForRead, 1.807 + AsmJSCacheOpenEntryForWrite, 1.808 + asmjscache::CloseEntryForWrite, 1.809 + asmjscache::GetBuildId 1.810 + }; 1.811 + JS::SetAsmJSCacheOps(aRuntime, &asmJSCacheOps); 1.812 + 1.813 + JSContext* workerCx = JS_NewContext(aRuntime, 0); 1.814 + if (!workerCx) { 1.815 + NS_WARNING("Could not create new context!"); 1.816 + return nullptr; 1.817 + } 1.818 + 1.819 + auto rtPrivate = new WorkerThreadRuntimePrivate(); 1.820 + memset(rtPrivate, 0, sizeof(WorkerThreadRuntimePrivate)); 1.821 + rtPrivate->mWorkerPrivate = aWorkerPrivate; 1.822 + JS_SetRuntimePrivate(aRuntime, rtPrivate); 1.823 + 1.824 + JS_SetErrorReporter(workerCx, ErrorReporter); 1.825 + 1.826 + JS_SetInterruptCallback(aRuntime, InterruptCallback); 1.827 + 1.828 + js::SetCTypesActivityCallback(aRuntime, CTypesActivityCallback); 1.829 + 1.830 + JS::ContextOptionsRef(workerCx) = 1.831 + aWorkerPrivate->IsChromeWorker() ? settings.chrome.contextOptions 1.832 + : settings.content.contextOptions; 1.833 + 1.834 +#ifdef JS_GC_ZEAL 1.835 + JS_SetGCZeal(workerCx, settings.gcZeal, settings.gcZealFrequency); 1.836 +#endif 1.837 + 1.838 + return workerCx; 1.839 +} 1.840 + 1.841 +class WorkerJSRuntime : public mozilla::CycleCollectedJSRuntime 1.842 +{ 1.843 +public: 1.844 + // The heap size passed here doesn't matter, we will change it later in the 1.845 + // call to JS_SetGCParameter inside CreateJSContextForWorker. 1.846 + WorkerJSRuntime(JSRuntime* aParentRuntime, WorkerPrivate* aWorkerPrivate) 1.847 + : CycleCollectedJSRuntime(aParentRuntime, 1.848 + WORKER_DEFAULT_RUNTIME_HEAPSIZE, 1.849 + JS_NO_HELPER_THREADS), 1.850 + mWorkerPrivate(aWorkerPrivate) 1.851 + { 1.852 + } 1.853 + 1.854 + ~WorkerJSRuntime() 1.855 + { 1.856 + auto rtPrivate = static_cast<WorkerThreadRuntimePrivate*>(JS_GetRuntimePrivate(Runtime())); 1.857 + delete rtPrivate; 1.858 + JS_SetRuntimePrivate(Runtime(), nullptr); 1.859 + 1.860 + // The worker global should be unrooted and the shutdown cycle collection 1.861 + // should break all remaining cycles. The superclass destructor will run 1.862 + // the GC one final time and finalize any JSObjects that were participating 1.863 + // in cycles that were broken during CC shutdown. 1.864 + nsCycleCollector_shutdown(); 1.865 + 1.866 + // The CC is shut down, and the superclass destructor will GC, so make sure 1.867 + // we don't try to CC again. 1.868 + mWorkerPrivate = nullptr; 1.869 + } 1.870 + 1.871 + virtual void 1.872 + PrepareForForgetSkippable() MOZ_OVERRIDE 1.873 + { 1.874 + } 1.875 + 1.876 + virtual void 1.877 + BeginCycleCollectionCallback() MOZ_OVERRIDE 1.878 + { 1.879 + } 1.880 + 1.881 + virtual void 1.882 + EndCycleCollectionCallback(CycleCollectorResults &aResults) MOZ_OVERRIDE 1.883 + { 1.884 + } 1.885 + 1.886 + void 1.887 + DispatchDeferredDeletion(bool aContinuation) MOZ_OVERRIDE 1.888 + { 1.889 + MOZ_ASSERT(!aContinuation); 1.890 + 1.891 + // Do it immediately, no need for asynchronous behavior here. 1.892 + nsCycleCollector_doDeferredDeletion(); 1.893 + } 1.894 + 1.895 + virtual void CustomGCCallback(JSGCStatus aStatus) MOZ_OVERRIDE 1.896 + { 1.897 + if (!mWorkerPrivate) { 1.898 + // We're shutting down, no need to do anything. 1.899 + return; 1.900 + } 1.901 + 1.902 + mWorkerPrivate->AssertIsOnWorkerThread(); 1.903 + 1.904 + if (aStatus == JSGC_END) { 1.905 + nsCycleCollector_collect(nullptr); 1.906 + } 1.907 + } 1.908 + 1.909 +private: 1.910 + WorkerPrivate* mWorkerPrivate; 1.911 +}; 1.912 + 1.913 +class WorkerThreadPrimaryRunnable MOZ_FINAL : public nsRunnable 1.914 +{ 1.915 + WorkerPrivate* mWorkerPrivate; 1.916 + nsRefPtr<RuntimeService::WorkerThread> mThread; 1.917 + JSRuntime* mParentRuntime; 1.918 + 1.919 + class FinishedRunnable MOZ_FINAL : public nsRunnable 1.920 + { 1.921 + nsRefPtr<RuntimeService::WorkerThread> mThread; 1.922 + 1.923 + public: 1.924 + FinishedRunnable(already_AddRefed<RuntimeService::WorkerThread> aThread) 1.925 + : mThread(aThread) 1.926 + { 1.927 + MOZ_ASSERT(mThread); 1.928 + } 1.929 + 1.930 + NS_DECL_ISUPPORTS_INHERITED 1.931 + 1.932 + private: 1.933 + ~FinishedRunnable() 1.934 + { } 1.935 + 1.936 + NS_DECL_NSIRUNNABLE 1.937 + }; 1.938 + 1.939 +public: 1.940 + WorkerThreadPrimaryRunnable(WorkerPrivate* aWorkerPrivate, 1.941 + RuntimeService::WorkerThread* aThread, 1.942 + JSRuntime* aParentRuntime) 1.943 + : mWorkerPrivate(aWorkerPrivate), mThread(aThread), mParentRuntime(aParentRuntime) 1.944 + { 1.945 + MOZ_ASSERT(aWorkerPrivate); 1.946 + MOZ_ASSERT(aThread); 1.947 + } 1.948 + 1.949 + NS_DECL_ISUPPORTS_INHERITED 1.950 + 1.951 +private: 1.952 + ~WorkerThreadPrimaryRunnable() 1.953 + { } 1.954 + 1.955 + NS_DECL_NSIRUNNABLE 1.956 +}; 1.957 + 1.958 +class WorkerTaskRunnable MOZ_FINAL : public WorkerRunnable 1.959 +{ 1.960 + nsRefPtr<WorkerTask> mTask; 1.961 + 1.962 +public: 1.963 + WorkerTaskRunnable(WorkerPrivate* aWorkerPrivate, WorkerTask* aTask) 1.964 + : WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount), mTask(aTask) 1.965 + { 1.966 + MOZ_ASSERT(aTask); 1.967 + } 1.968 + 1.969 +private: 1.970 + virtual bool 1.971 + PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE 1.972 + { 1.973 + // May be called on any thread! 1.974 + return true; 1.975 + } 1.976 + 1.977 + virtual void 1.978 + PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate, 1.979 + bool aDispatchResult) MOZ_OVERRIDE 1.980 + { 1.981 + // May be called on any thread! 1.982 + } 1.983 + 1.984 + virtual bool 1.985 + WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE 1.986 + { 1.987 + return mTask->RunTask(aCx); 1.988 + } 1.989 +}; 1.990 + 1.991 +void 1.992 +AppNameOverrideChanged(const char* /* aPrefName */, void* /* aClosure */) 1.993 +{ 1.994 + AssertIsOnMainThread(); 1.995 + 1.996 + const nsAdoptingString& override = 1.997 + mozilla::Preferences::GetString(PREF_GENERAL_APPNAME_OVERRIDE); 1.998 + 1.999 + RuntimeService* runtime = RuntimeService::GetService(); 1.1000 + if (runtime) { 1.1001 + runtime->UpdateAppNameOverridePreference(override); 1.1002 + } 1.1003 +} 1.1004 + 1.1005 +void 1.1006 +AppVersionOverrideChanged(const char* /* aPrefName */, void* /* aClosure */) 1.1007 +{ 1.1008 + AssertIsOnMainThread(); 1.1009 + 1.1010 + const nsAdoptingString& override = 1.1011 + mozilla::Preferences::GetString(PREF_GENERAL_APPVERSION_OVERRIDE); 1.1012 + 1.1013 + RuntimeService* runtime = RuntimeService::GetService(); 1.1014 + if (runtime) { 1.1015 + runtime->UpdateAppVersionOverridePreference(override); 1.1016 + } 1.1017 +} 1.1018 + 1.1019 +void 1.1020 +PlatformOverrideChanged(const char* /* aPrefName */, void* /* aClosure */) 1.1021 +{ 1.1022 + AssertIsOnMainThread(); 1.1023 + 1.1024 + const nsAdoptingString& override = 1.1025 + mozilla::Preferences::GetString(PREF_GENERAL_PLATFORM_OVERRIDE); 1.1026 + 1.1027 + RuntimeService* runtime = RuntimeService::GetService(); 1.1028 + if (runtime) { 1.1029 + runtime->UpdatePlatformOverridePreference(override); 1.1030 + } 1.1031 +} 1.1032 + 1.1033 +} /* anonymous namespace */ 1.1034 + 1.1035 +class RuntimeService::WorkerThread MOZ_FINAL : public nsThread 1.1036 +{ 1.1037 + class Observer MOZ_FINAL : public nsIThreadObserver 1.1038 + { 1.1039 + WorkerPrivate* mWorkerPrivate; 1.1040 + 1.1041 + public: 1.1042 + Observer(WorkerPrivate* aWorkerPrivate) 1.1043 + : mWorkerPrivate(aWorkerPrivate) 1.1044 + { 1.1045 + MOZ_ASSERT(aWorkerPrivate); 1.1046 + aWorkerPrivate->AssertIsOnWorkerThread(); 1.1047 + } 1.1048 + 1.1049 + NS_DECL_THREADSAFE_ISUPPORTS 1.1050 + 1.1051 + private: 1.1052 + ~Observer() 1.1053 + { 1.1054 + mWorkerPrivate->AssertIsOnWorkerThread(); 1.1055 + } 1.1056 + 1.1057 + NS_DECL_NSITHREADOBSERVER 1.1058 + }; 1.1059 + 1.1060 + WorkerPrivate* mWorkerPrivate; 1.1061 + nsRefPtr<Observer> mObserver; 1.1062 + 1.1063 +#ifdef DEBUG 1.1064 + // Protected by nsThread::mLock. 1.1065 + bool mAcceptingNonWorkerRunnables; 1.1066 +#endif 1.1067 + 1.1068 +public: 1.1069 + static already_AddRefed<WorkerThread> 1.1070 + Create(); 1.1071 + 1.1072 + void 1.1073 + SetWorker(WorkerPrivate* aWorkerPrivate); 1.1074 + 1.1075 + NS_DECL_ISUPPORTS_INHERITED 1.1076 + 1.1077 + NS_IMETHOD 1.1078 + Dispatch(nsIRunnable* aRunnable, uint32_t aFlags) MOZ_OVERRIDE; 1.1079 + 1.1080 +#ifdef DEBUG 1.1081 + bool 1.1082 + IsAcceptingNonWorkerRunnables() 1.1083 + { 1.1084 + MutexAutoLock lock(mLock); 1.1085 + return mAcceptingNonWorkerRunnables; 1.1086 + } 1.1087 + 1.1088 + void 1.1089 + SetAcceptingNonWorkerRunnables(bool aAcceptingNonWorkerRunnables) 1.1090 + { 1.1091 + MutexAutoLock lock(mLock); 1.1092 + mAcceptingNonWorkerRunnables = aAcceptingNonWorkerRunnables; 1.1093 + } 1.1094 +#endif 1.1095 + 1.1096 +private: 1.1097 + WorkerThread() 1.1098 + : nsThread(nsThread::NOT_MAIN_THREAD, WORKER_STACK_SIZE), 1.1099 + mWorkerPrivate(nullptr) 1.1100 +#ifdef DEBUG 1.1101 + , mAcceptingNonWorkerRunnables(true) 1.1102 +#endif 1.1103 + { } 1.1104 + 1.1105 + ~WorkerThread() 1.1106 + { } 1.1107 +}; 1.1108 + 1.1109 +BEGIN_WORKERS_NAMESPACE 1.1110 + 1.1111 +// Entry point for main thread non-window globals. 1.1112 +bool 1.1113 +ResolveWorkerClasses(JSContext* aCx, JS::Handle<JSObject*> aObj, JS::Handle<jsid> aId, 1.1114 + JS::MutableHandle<JSObject*> aObjp) 1.1115 +{ 1.1116 + AssertIsOnMainThread(); 1.1117 + MOZ_ASSERT(nsContentUtils::IsCallerChrome()); 1.1118 + 1.1119 + // Make sure our strings are interned. 1.1120 + if (JSID_IS_VOID(gStringIDs[0])) { 1.1121 + for (uint32_t i = 0; i < ID_COUNT; i++) { 1.1122 + JSString* str = JS_InternString(aCx, gStringChars[i]); 1.1123 + if (!str) { 1.1124 + while (i) { 1.1125 + gStringIDs[--i] = JSID_VOID; 1.1126 + } 1.1127 + return false; 1.1128 + } 1.1129 + gStringIDs[i] = INTERNED_STRING_TO_JSID(aCx, str); 1.1130 + } 1.1131 + } 1.1132 + 1.1133 + bool shouldResolve = false; 1.1134 + 1.1135 + for (uint32_t i = 0; i < ID_COUNT; i++) { 1.1136 + if (gStringIDs[i] == aId) { 1.1137 + shouldResolve = true; 1.1138 + break; 1.1139 + } 1.1140 + } 1.1141 + 1.1142 + if (!shouldResolve) { 1.1143 + aObjp.set(nullptr); 1.1144 + return true; 1.1145 + } 1.1146 + 1.1147 + if (!WorkerBinding::GetConstructorObject(aCx, aObj) || 1.1148 + !ChromeWorkerBinding::GetConstructorObject(aCx, aObj) || 1.1149 + !ErrorEventBinding::GetConstructorObject(aCx, aObj) || 1.1150 + !MessageEventBinding::GetConstructorObject(aCx, aObj)) { 1.1151 + return false; 1.1152 + } 1.1153 + 1.1154 + aObjp.set(aObj); 1.1155 + return true; 1.1156 +} 1.1157 + 1.1158 +void 1.1159 +CancelWorkersForWindow(nsPIDOMWindow* aWindow) 1.1160 +{ 1.1161 + AssertIsOnMainThread(); 1.1162 + RuntimeService* runtime = RuntimeService::GetService(); 1.1163 + if (runtime) { 1.1164 + runtime->CancelWorkersForWindow(aWindow); 1.1165 + } 1.1166 +} 1.1167 + 1.1168 +void 1.1169 +SuspendWorkersForWindow(nsPIDOMWindow* aWindow) 1.1170 +{ 1.1171 + AssertIsOnMainThread(); 1.1172 + RuntimeService* runtime = RuntimeService::GetService(); 1.1173 + if (runtime) { 1.1174 + runtime->SuspendWorkersForWindow(aWindow); 1.1175 + } 1.1176 +} 1.1177 + 1.1178 +void 1.1179 +ResumeWorkersForWindow(nsPIDOMWindow* aWindow) 1.1180 +{ 1.1181 + AssertIsOnMainThread(); 1.1182 + RuntimeService* runtime = RuntimeService::GetService(); 1.1183 + if (runtime) { 1.1184 + runtime->ResumeWorkersForWindow(aWindow); 1.1185 + } 1.1186 +} 1.1187 + 1.1188 +WorkerCrossThreadDispatcher::WorkerCrossThreadDispatcher( 1.1189 + WorkerPrivate* aWorkerPrivate) 1.1190 +: mMutex("WorkerCrossThreadDispatcher::mMutex"), 1.1191 + mWorkerPrivate(aWorkerPrivate) 1.1192 +{ 1.1193 + MOZ_ASSERT(aWorkerPrivate); 1.1194 +} 1.1195 + 1.1196 +bool 1.1197 +WorkerCrossThreadDispatcher::PostTask(WorkerTask* aTask) 1.1198 +{ 1.1199 + MOZ_ASSERT(aTask); 1.1200 + 1.1201 + MutexAutoLock lock(mMutex); 1.1202 + 1.1203 + if (!mWorkerPrivate) { 1.1204 + NS_WARNING("Posted a task to a WorkerCrossThreadDispatcher that is no " 1.1205 + "longer accepting tasks!"); 1.1206 + return false; 1.1207 + } 1.1208 + 1.1209 + nsRefPtr<WorkerTaskRunnable> runnable = 1.1210 + new WorkerTaskRunnable(mWorkerPrivate, aTask); 1.1211 + return runnable->Dispatch(nullptr); 1.1212 +} 1.1213 + 1.1214 +WorkerPrivate* 1.1215 +GetWorkerPrivateFromContext(JSContext* aCx) 1.1216 +{ 1.1217 + MOZ_ASSERT(!NS_IsMainThread()); 1.1218 + MOZ_ASSERT(aCx); 1.1219 + 1.1220 + JSRuntime* rt = JS_GetRuntime(aCx); 1.1221 + MOZ_ASSERT(rt); 1.1222 + 1.1223 + void* rtPrivate = JS_GetRuntimePrivate(rt); 1.1224 + MOZ_ASSERT(rtPrivate); 1.1225 + 1.1226 + return static_cast<WorkerThreadRuntimePrivate*>(rtPrivate)->mWorkerPrivate; 1.1227 +} 1.1228 + 1.1229 +WorkerPrivate* 1.1230 +GetCurrentThreadWorkerPrivate() 1.1231 +{ 1.1232 + MOZ_ASSERT(!NS_IsMainThread()); 1.1233 + 1.1234 + CycleCollectedJSRuntime* ccrt = CycleCollectedJSRuntime::Get(); 1.1235 + if (!ccrt) { 1.1236 + return nullptr; 1.1237 + } 1.1238 + 1.1239 + JSRuntime* rt = ccrt->Runtime(); 1.1240 + MOZ_ASSERT(rt); 1.1241 + 1.1242 + void* rtPrivate = JS_GetRuntimePrivate(rt); 1.1243 + MOZ_ASSERT(rtPrivate); 1.1244 + 1.1245 + return static_cast<WorkerThreadRuntimePrivate*>(rtPrivate)->mWorkerPrivate; 1.1246 +} 1.1247 + 1.1248 +bool 1.1249 +IsCurrentThreadRunningChromeWorker() 1.1250 +{ 1.1251 + return GetCurrentThreadWorkerPrivate()->UsesSystemPrincipal(); 1.1252 +} 1.1253 + 1.1254 +JSContext* 1.1255 +GetCurrentThreadJSContext() 1.1256 +{ 1.1257 + return GetCurrentThreadWorkerPrivate()->GetJSContext(); 1.1258 +} 1.1259 + 1.1260 +END_WORKERS_NAMESPACE 1.1261 + 1.1262 +// This is only touched on the main thread. Initialized in Init() below. 1.1263 +JSSettings RuntimeService::sDefaultJSSettings; 1.1264 +bool RuntimeService::sDefaultPreferences[WORKERPREF_COUNT] = { false }; 1.1265 + 1.1266 +RuntimeService::RuntimeService() 1.1267 +: mMutex("RuntimeService::mMutex"), mObserved(false), 1.1268 + mShuttingDown(false), mNavigatorPropertiesLoaded(false) 1.1269 +{ 1.1270 + AssertIsOnMainThread(); 1.1271 + NS_ASSERTION(!gRuntimeService, "More than one service!"); 1.1272 +} 1.1273 + 1.1274 +RuntimeService::~RuntimeService() 1.1275 +{ 1.1276 + AssertIsOnMainThread(); 1.1277 + 1.1278 + // gRuntimeService can be null if Init() fails. 1.1279 + NS_ASSERTION(!gRuntimeService || gRuntimeService == this, 1.1280 + "More than one service!"); 1.1281 + 1.1282 + gRuntimeService = nullptr; 1.1283 +} 1.1284 + 1.1285 +// static 1.1286 +RuntimeService* 1.1287 +RuntimeService::GetOrCreateService() 1.1288 +{ 1.1289 + AssertIsOnMainThread(); 1.1290 + 1.1291 + if (!gRuntimeService) { 1.1292 + nsRefPtr<RuntimeService> service = new RuntimeService(); 1.1293 + if (NS_FAILED(service->Init())) { 1.1294 + NS_WARNING("Failed to initialize!"); 1.1295 + service->Cleanup(); 1.1296 + return nullptr; 1.1297 + } 1.1298 + 1.1299 + // The observer service now owns us until shutdown. 1.1300 + gRuntimeService = service; 1.1301 + } 1.1302 + 1.1303 + return gRuntimeService; 1.1304 +} 1.1305 + 1.1306 +// static 1.1307 +RuntimeService* 1.1308 +RuntimeService::GetService() 1.1309 +{ 1.1310 + return gRuntimeService; 1.1311 +} 1.1312 + 1.1313 +bool 1.1314 +RuntimeService::RegisterWorker(JSContext* aCx, WorkerPrivate* aWorkerPrivate) 1.1315 +{ 1.1316 + aWorkerPrivate->AssertIsOnParentThread(); 1.1317 + 1.1318 + WorkerPrivate* parent = aWorkerPrivate->GetParent(); 1.1319 + if (!parent) { 1.1320 + AssertIsOnMainThread(); 1.1321 + 1.1322 + if (mShuttingDown) { 1.1323 + JS_ReportError(aCx, "Cannot create worker during shutdown!"); 1.1324 + return false; 1.1325 + } 1.1326 + } 1.1327 + 1.1328 + bool isSharedWorker = aWorkerPrivate->IsSharedWorker(); 1.1329 + 1.1330 + const nsCString& sharedWorkerName = aWorkerPrivate->SharedWorkerName(); 1.1331 + nsCString sharedWorkerScriptSpec; 1.1332 + 1.1333 + if (isSharedWorker) { 1.1334 + AssertIsOnMainThread(); 1.1335 + 1.1336 + nsCOMPtr<nsIURI> scriptURI = aWorkerPrivate->GetResolvedScriptURI(); 1.1337 + NS_ASSERTION(scriptURI, "Null script URI!"); 1.1338 + 1.1339 + nsresult rv = scriptURI->GetSpec(sharedWorkerScriptSpec); 1.1340 + if (NS_FAILED(rv)) { 1.1341 + NS_WARNING("GetSpec failed?!"); 1.1342 + xpc::Throw(aCx, rv); 1.1343 + return false; 1.1344 + } 1.1345 + 1.1346 + NS_ASSERTION(!sharedWorkerScriptSpec.IsEmpty(), "Empty spec!"); 1.1347 + } 1.1348 + 1.1349 + const nsCString& domain = aWorkerPrivate->Domain(); 1.1350 + 1.1351 + WorkerDomainInfo* domainInfo; 1.1352 + bool queued = false; 1.1353 + { 1.1354 + MutexAutoLock lock(mMutex); 1.1355 + 1.1356 + if (!mDomainMap.Get(domain, &domainInfo)) { 1.1357 + NS_ASSERTION(!parent, "Shouldn't have a parent here!"); 1.1358 + 1.1359 + domainInfo = new WorkerDomainInfo(); 1.1360 + domainInfo->mDomain = domain; 1.1361 + mDomainMap.Put(domain, domainInfo); 1.1362 + } 1.1363 + 1.1364 + queued = gMaxWorkersPerDomain && 1.1365 + domainInfo->ActiveWorkerCount() >= gMaxWorkersPerDomain && 1.1366 + !domain.IsEmpty(); 1.1367 + 1.1368 + if (queued) { 1.1369 + domainInfo->mQueuedWorkers.AppendElement(aWorkerPrivate); 1.1370 + } 1.1371 + else if (parent) { 1.1372 + domainInfo->mChildWorkerCount++; 1.1373 + } 1.1374 + else { 1.1375 + domainInfo->mActiveWorkers.AppendElement(aWorkerPrivate); 1.1376 + } 1.1377 + 1.1378 + if (isSharedWorker) { 1.1379 + nsAutoCString key; 1.1380 + GenerateSharedWorkerKey(sharedWorkerScriptSpec, sharedWorkerName, key); 1.1381 + MOZ_ASSERT(!domainInfo->mSharedWorkerInfos.Get(key)); 1.1382 + 1.1383 + SharedWorkerInfo* sharedWorkerInfo = 1.1384 + new SharedWorkerInfo(aWorkerPrivate, sharedWorkerScriptSpec, 1.1385 + sharedWorkerName); 1.1386 + domainInfo->mSharedWorkerInfos.Put(key, sharedWorkerInfo); 1.1387 + } 1.1388 + } 1.1389 + 1.1390 + // From here on out we must call UnregisterWorker if something fails! 1.1391 + if (parent) { 1.1392 + if (!parent->AddChildWorker(aCx, aWorkerPrivate)) { 1.1393 + UnregisterWorker(aCx, aWorkerPrivate); 1.1394 + return false; 1.1395 + } 1.1396 + } 1.1397 + else { 1.1398 + if (!mNavigatorPropertiesLoaded) { 1.1399 + Navigator::AppName(mNavigatorProperties.mAppName, 1.1400 + false /* aUsePrefOverriddenValue */); 1.1401 + if (NS_FAILED(Navigator::GetAppVersion(mNavigatorProperties.mAppVersion, 1.1402 + false /* aUsePrefOverriddenValue */)) || 1.1403 + NS_FAILED(Navigator::GetPlatform(mNavigatorProperties.mPlatform, 1.1404 + false /* aUsePrefOverriddenValue */)) || 1.1405 + NS_FAILED(NS_GetNavigatorUserAgent(mNavigatorProperties.mUserAgent))) { 1.1406 + JS_ReportError(aCx, "Failed to load navigator strings!"); 1.1407 + UnregisterWorker(aCx, aWorkerPrivate); 1.1408 + return false; 1.1409 + } 1.1410 + 1.1411 + mNavigatorProperties.mAppNameOverridden = 1.1412 + mozilla::Preferences::GetString(PREF_GENERAL_APPNAME_OVERRIDE); 1.1413 + mNavigatorProperties.mAppVersionOverridden = 1.1414 + mozilla::Preferences::GetString(PREF_GENERAL_APPVERSION_OVERRIDE); 1.1415 + mNavigatorProperties.mPlatformOverridden = 1.1416 + mozilla::Preferences::GetString(PREF_GENERAL_PLATFORM_OVERRIDE); 1.1417 + 1.1418 + mNavigatorPropertiesLoaded = true; 1.1419 + } 1.1420 + 1.1421 + nsPIDOMWindow* window = aWorkerPrivate->GetWindow(); 1.1422 + 1.1423 + nsTArray<WorkerPrivate*>* windowArray; 1.1424 + if (!mWindowMap.Get(window, &windowArray)) { 1.1425 + windowArray = new nsTArray<WorkerPrivate*>(1); 1.1426 + mWindowMap.Put(window, windowArray); 1.1427 + } 1.1428 + 1.1429 + if (!windowArray->Contains(aWorkerPrivate)) { 1.1430 + windowArray->AppendElement(aWorkerPrivate); 1.1431 + } else { 1.1432 + MOZ_ASSERT(aWorkerPrivate->IsSharedWorker()); 1.1433 + } 1.1434 + } 1.1435 + 1.1436 + if (!queued && !ScheduleWorker(aCx, aWorkerPrivate)) { 1.1437 + return false; 1.1438 + } 1.1439 + 1.1440 + return true; 1.1441 +} 1.1442 + 1.1443 +void 1.1444 +RuntimeService::UnregisterWorker(JSContext* aCx, WorkerPrivate* aWorkerPrivate) 1.1445 +{ 1.1446 + aWorkerPrivate->AssertIsOnParentThread(); 1.1447 + 1.1448 + WorkerPrivate* parent = aWorkerPrivate->GetParent(); 1.1449 + if (!parent) { 1.1450 + AssertIsOnMainThread(); 1.1451 + } 1.1452 + 1.1453 + const nsCString& domain = aWorkerPrivate->Domain(); 1.1454 + 1.1455 + WorkerPrivate* queuedWorker = nullptr; 1.1456 + { 1.1457 + MutexAutoLock lock(mMutex); 1.1458 + 1.1459 + WorkerDomainInfo* domainInfo; 1.1460 + if (!mDomainMap.Get(domain, &domainInfo)) { 1.1461 + NS_ERROR("Don't have an entry for this domain!"); 1.1462 + } 1.1463 + 1.1464 + // Remove old worker from everywhere. 1.1465 + uint32_t index = domainInfo->mQueuedWorkers.IndexOf(aWorkerPrivate); 1.1466 + if (index != kNoIndex) { 1.1467 + // Was queued, remove from the list. 1.1468 + domainInfo->mQueuedWorkers.RemoveElementAt(index); 1.1469 + } 1.1470 + else if (parent) { 1.1471 + NS_ASSERTION(domainInfo->mChildWorkerCount, "Must be non-zero!"); 1.1472 + domainInfo->mChildWorkerCount--; 1.1473 + } 1.1474 + else { 1.1475 + NS_ASSERTION(domainInfo->mActiveWorkers.Contains(aWorkerPrivate), 1.1476 + "Don't know about this worker!"); 1.1477 + domainInfo->mActiveWorkers.RemoveElement(aWorkerPrivate); 1.1478 + } 1.1479 + 1.1480 + if (aWorkerPrivate->IsSharedWorker()) { 1.1481 + MatchSharedWorkerInfo match(aWorkerPrivate); 1.1482 + domainInfo->mSharedWorkerInfos.EnumerateRead(FindSharedWorkerInfo, 1.1483 + &match); 1.1484 + 1.1485 + if (match.mSharedWorkerInfo) { 1.1486 + nsAutoCString key; 1.1487 + GenerateSharedWorkerKey(match.mSharedWorkerInfo->mScriptSpec, 1.1488 + match.mSharedWorkerInfo->mName, key); 1.1489 + domainInfo->mSharedWorkerInfos.Remove(key); 1.1490 + } 1.1491 + } 1.1492 + 1.1493 + // See if there's a queued worker we can schedule. 1.1494 + if (domainInfo->ActiveWorkerCount() < gMaxWorkersPerDomain && 1.1495 + !domainInfo->mQueuedWorkers.IsEmpty()) { 1.1496 + queuedWorker = domainInfo->mQueuedWorkers[0]; 1.1497 + domainInfo->mQueuedWorkers.RemoveElementAt(0); 1.1498 + 1.1499 + if (queuedWorker->GetParent()) { 1.1500 + domainInfo->mChildWorkerCount++; 1.1501 + } 1.1502 + else { 1.1503 + domainInfo->mActiveWorkers.AppendElement(queuedWorker); 1.1504 + } 1.1505 + } 1.1506 + 1.1507 + if (!domainInfo->ActiveWorkerCount()) { 1.1508 + MOZ_ASSERT(domainInfo->mQueuedWorkers.IsEmpty()); 1.1509 + mDomainMap.Remove(domain); 1.1510 + } 1.1511 + } 1.1512 + 1.1513 + if (aWorkerPrivate->IsSharedWorker()) { 1.1514 + AssertIsOnMainThread(); 1.1515 + 1.1516 + nsAutoTArray<nsRefPtr<SharedWorker>, 5> sharedWorkersToNotify; 1.1517 + aWorkerPrivate->GetAllSharedWorkers(sharedWorkersToNotify); 1.1518 + 1.1519 + for (uint32_t index = 0; index < sharedWorkersToNotify.Length(); index++) { 1.1520 + MOZ_ASSERT(sharedWorkersToNotify[index]); 1.1521 + sharedWorkersToNotify[index]->NoteDeadWorker(aCx); 1.1522 + } 1.1523 + } 1.1524 + 1.1525 + if (parent) { 1.1526 + parent->RemoveChildWorker(aCx, aWorkerPrivate); 1.1527 + } 1.1528 + else if (aWorkerPrivate->IsSharedWorker()) { 1.1529 + mWindowMap.Enumerate(RemoveSharedWorkerFromWindowMap, aWorkerPrivate); 1.1530 + } 1.1531 + else { 1.1532 + // May be null. 1.1533 + nsPIDOMWindow* window = aWorkerPrivate->GetWindow(); 1.1534 + 1.1535 + nsTArray<WorkerPrivate*>* windowArray; 1.1536 + MOZ_ALWAYS_TRUE(mWindowMap.Get(window, &windowArray)); 1.1537 + 1.1538 + MOZ_ALWAYS_TRUE(windowArray->RemoveElement(aWorkerPrivate)); 1.1539 + 1.1540 + if (windowArray->IsEmpty()) { 1.1541 + mWindowMap.Remove(window); 1.1542 + } 1.1543 + } 1.1544 + 1.1545 + if (queuedWorker && !ScheduleWorker(aCx, queuedWorker)) { 1.1546 + UnregisterWorker(aCx, queuedWorker); 1.1547 + } 1.1548 +} 1.1549 + 1.1550 +bool 1.1551 +RuntimeService::ScheduleWorker(JSContext* aCx, WorkerPrivate* aWorkerPrivate) 1.1552 +{ 1.1553 + if (!aWorkerPrivate->Start()) { 1.1554 + // This is ok, means that we didn't need to make a thread for this worker. 1.1555 + return true; 1.1556 + } 1.1557 + 1.1558 + nsRefPtr<WorkerThread> thread; 1.1559 + { 1.1560 + MutexAutoLock lock(mMutex); 1.1561 + if (!mIdleThreadArray.IsEmpty()) { 1.1562 + uint32_t index = mIdleThreadArray.Length() - 1; 1.1563 + mIdleThreadArray[index].mThread.swap(thread); 1.1564 + mIdleThreadArray.RemoveElementAt(index); 1.1565 + } 1.1566 + } 1.1567 + 1.1568 + if (!thread) { 1.1569 + thread = WorkerThread::Create(); 1.1570 + if (!thread) { 1.1571 + UnregisterWorker(aCx, aWorkerPrivate); 1.1572 + JS_ReportError(aCx, "Could not create new thread!"); 1.1573 + return false; 1.1574 + } 1.1575 + } 1.1576 + 1.1577 + MOZ_ASSERT(thread->IsAcceptingNonWorkerRunnables()); 1.1578 + 1.1579 + int32_t priority = aWorkerPrivate->IsChromeWorker() ? 1.1580 + nsISupportsPriority::PRIORITY_NORMAL : 1.1581 + nsISupportsPriority::PRIORITY_LOW; 1.1582 + 1.1583 + if (NS_FAILED(thread->SetPriority(priority))) { 1.1584 + NS_WARNING("Could not set the thread's priority!"); 1.1585 + } 1.1586 + 1.1587 + nsCOMPtr<nsIRunnable> runnable = 1.1588 + new WorkerThreadPrimaryRunnable(aWorkerPrivate, thread, JS_GetParentRuntime(aCx)); 1.1589 + if (NS_FAILED(thread->Dispatch(runnable, NS_DISPATCH_NORMAL))) { 1.1590 + UnregisterWorker(aCx, aWorkerPrivate); 1.1591 + JS_ReportError(aCx, "Could not dispatch to thread!"); 1.1592 + return false; 1.1593 + } 1.1594 + 1.1595 +#ifdef DEBUG 1.1596 + thread->SetAcceptingNonWorkerRunnables(false); 1.1597 +#endif 1.1598 + 1.1599 + return true; 1.1600 +} 1.1601 + 1.1602 +// static 1.1603 +void 1.1604 +RuntimeService::ShutdownIdleThreads(nsITimer* aTimer, void* /* aClosure */) 1.1605 +{ 1.1606 + AssertIsOnMainThread(); 1.1607 + 1.1608 + RuntimeService* runtime = RuntimeService::GetService(); 1.1609 + NS_ASSERTION(runtime, "This should never be null!"); 1.1610 + 1.1611 + NS_ASSERTION(aTimer == runtime->mIdleThreadTimer, "Wrong timer!"); 1.1612 + 1.1613 + // Cheat a little and grab all threads that expire within one second of now. 1.1614 + TimeStamp now = TimeStamp::Now() + TimeDuration::FromSeconds(1); 1.1615 + 1.1616 + TimeStamp nextExpiration; 1.1617 + 1.1618 + nsAutoTArray<nsRefPtr<WorkerThread>, 20> expiredThreads; 1.1619 + { 1.1620 + MutexAutoLock lock(runtime->mMutex); 1.1621 + 1.1622 + for (uint32_t index = 0; index < runtime->mIdleThreadArray.Length(); 1.1623 + index++) { 1.1624 + IdleThreadInfo& info = runtime->mIdleThreadArray[index]; 1.1625 + if (info.mExpirationTime > now) { 1.1626 + nextExpiration = info.mExpirationTime; 1.1627 + break; 1.1628 + } 1.1629 + 1.1630 + nsRefPtr<WorkerThread>* thread = expiredThreads.AppendElement(); 1.1631 + thread->swap(info.mThread); 1.1632 + } 1.1633 + 1.1634 + if (!expiredThreads.IsEmpty()) { 1.1635 + runtime->mIdleThreadArray.RemoveElementsAt(0, expiredThreads.Length()); 1.1636 + } 1.1637 + } 1.1638 + 1.1639 + NS_ASSERTION(nextExpiration.IsNull() || !expiredThreads.IsEmpty(), 1.1640 + "Should have a new time or there should be some threads to shut " 1.1641 + "down"); 1.1642 + 1.1643 + for (uint32_t index = 0; index < expiredThreads.Length(); index++) { 1.1644 + if (NS_FAILED(expiredThreads[index]->Shutdown())) { 1.1645 + NS_WARNING("Failed to shutdown thread!"); 1.1646 + } 1.1647 + } 1.1648 + 1.1649 + if (!nextExpiration.IsNull()) { 1.1650 + TimeDuration delta = nextExpiration - TimeStamp::Now(); 1.1651 + uint32_t delay(delta > TimeDuration(0) ? delta.ToMilliseconds() : 0); 1.1652 + 1.1653 + // Reschedule the timer. 1.1654 + if (NS_FAILED(aTimer->InitWithFuncCallback(ShutdownIdleThreads, nullptr, 1.1655 + delay, 1.1656 + nsITimer::TYPE_ONE_SHOT))) { 1.1657 + NS_ERROR("Can't schedule timer!"); 1.1658 + } 1.1659 + } 1.1660 +} 1.1661 + 1.1662 +nsresult 1.1663 +RuntimeService::Init() 1.1664 +{ 1.1665 + AssertIsOnMainThread(); 1.1666 + 1.1667 + nsLayoutStatics::AddRef(); 1.1668 + 1.1669 + // Initialize JSSettings. 1.1670 + if (!sDefaultJSSettings.gcSettings[0].IsSet()) { 1.1671 + sDefaultJSSettings.runtimeOptions = JS::RuntimeOptions(); 1.1672 + sDefaultJSSettings.chrome.contextOptions = kRequiredContextOptions; 1.1673 + sDefaultJSSettings.chrome.maxScriptRuntime = -1; 1.1674 + sDefaultJSSettings.chrome.compartmentOptions.setVersion(JSVERSION_LATEST); 1.1675 + sDefaultJSSettings.content.contextOptions = kRequiredContextOptions; 1.1676 + sDefaultJSSettings.content.maxScriptRuntime = MAX_SCRIPT_RUN_TIME_SEC; 1.1677 +#ifdef JS_GC_ZEAL 1.1678 + sDefaultJSSettings.gcZealFrequency = JS_DEFAULT_ZEAL_FREQ; 1.1679 + sDefaultJSSettings.gcZeal = 0; 1.1680 +#endif 1.1681 + SetDefaultJSGCSettings(JSGC_MAX_BYTES, WORKER_DEFAULT_RUNTIME_HEAPSIZE); 1.1682 + SetDefaultJSGCSettings(JSGC_ALLOCATION_THRESHOLD, 1.1683 + WORKER_DEFAULT_ALLOCATION_THRESHOLD); 1.1684 + } 1.1685 + 1.1686 +// If dump is not controlled by pref, it's set to true. 1.1687 +#ifndef DUMP_CONTROLLED_BY_PREF 1.1688 + sDefaultPreferences[WORKERPREF_DUMP] = true; 1.1689 +#endif 1.1690 + 1.1691 + mIdleThreadTimer = do_CreateInstance(NS_TIMER_CONTRACTID); 1.1692 + NS_ENSURE_STATE(mIdleThreadTimer); 1.1693 + 1.1694 + nsCOMPtr<nsIObserverService> obs = services::GetObserverService(); 1.1695 + NS_ENSURE_TRUE(obs, NS_ERROR_FAILURE); 1.1696 + 1.1697 + nsresult rv = 1.1698 + obs->AddObserver(this, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID, false); 1.1699 + NS_ENSURE_SUCCESS(rv, rv); 1.1700 + 1.1701 + rv = obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false); 1.1702 + NS_ENSURE_SUCCESS(rv, rv); 1.1703 + 1.1704 + mObserved = true; 1.1705 + 1.1706 + if (NS_FAILED(obs->AddObserver(this, GC_REQUEST_OBSERVER_TOPIC, false))) { 1.1707 + NS_WARNING("Failed to register for GC request notifications!"); 1.1708 + } 1.1709 + 1.1710 + if (NS_FAILED(obs->AddObserver(this, CC_REQUEST_OBSERVER_TOPIC, false))) { 1.1711 + NS_WARNING("Failed to register for CC request notifications!"); 1.1712 + } 1.1713 + 1.1714 + if (NS_FAILED(obs->AddObserver(this, MEMORY_PRESSURE_OBSERVER_TOPIC, 1.1715 + false))) { 1.1716 + NS_WARNING("Failed to register for memory pressure notifications!"); 1.1717 + } 1.1718 + 1.1719 + if (NS_FAILED(obs->AddObserver(this, NS_IOSERVICE_OFFLINE_STATUS_TOPIC, false))) { 1.1720 + NS_WARNING("Failed to register for offline notification event!"); 1.1721 + } 1.1722 + 1.1723 + NS_ASSERTION(!gRuntimeServiceDuringInit, "This should be null!"); 1.1724 + gRuntimeServiceDuringInit = this; 1.1725 + 1.1726 + if (NS_FAILED(Preferences::RegisterCallback( 1.1727 + LoadJSGCMemoryOptions, 1.1728 + PREF_JS_OPTIONS_PREFIX PREF_MEM_OPTIONS_PREFIX, 1.1729 + nullptr)) || 1.1730 + NS_FAILED(Preferences::RegisterCallbackAndCall( 1.1731 + LoadJSGCMemoryOptions, 1.1732 + PREF_WORKERS_OPTIONS_PREFIX PREF_MEM_OPTIONS_PREFIX, 1.1733 + nullptr)) || 1.1734 +#ifdef JS_GC_ZEAL 1.1735 + NS_FAILED(Preferences::RegisterCallback( 1.1736 + LoadGCZealOptions, 1.1737 + PREF_JS_OPTIONS_PREFIX PREF_GCZEAL, 1.1738 + nullptr)) || 1.1739 + NS_FAILED(Preferences::RegisterCallbackAndCall( 1.1740 + LoadGCZealOptions, 1.1741 + PREF_WORKERS_OPTIONS_PREFIX PREF_GCZEAL, 1.1742 + nullptr)) || 1.1743 +#endif 1.1744 +#if DUMP_CONTROLLED_BY_PREF 1.1745 + NS_FAILED(Preferences::RegisterCallbackAndCall( 1.1746 + WorkerPrefChanged, 1.1747 + PREF_DOM_WINDOW_DUMP_ENABLED, 1.1748 + reinterpret_cast<void *>(WORKERPREF_DUMP))) || 1.1749 +#endif 1.1750 + NS_FAILED(Preferences::RegisterCallback(LoadRuntimeAndContextOptions, 1.1751 + PREF_JS_OPTIONS_PREFIX, 1.1752 + nullptr)) || 1.1753 + NS_FAILED(Preferences::RegisterCallbackAndCall( 1.1754 + LoadRuntimeAndContextOptions, 1.1755 + PREF_WORKERS_OPTIONS_PREFIX, 1.1756 + nullptr)) || 1.1757 + NS_FAILED(Preferences::RegisterCallbackAndCall( 1.1758 + AppNameOverrideChanged, 1.1759 + PREF_GENERAL_APPNAME_OVERRIDE, 1.1760 + nullptr)) || 1.1761 + NS_FAILED(Preferences::RegisterCallbackAndCall( 1.1762 + AppVersionOverrideChanged, 1.1763 + PREF_GENERAL_APPVERSION_OVERRIDE, 1.1764 + nullptr)) || 1.1765 + NS_FAILED(Preferences::RegisterCallbackAndCall( 1.1766 + PlatformOverrideChanged, 1.1767 + PREF_GENERAL_PLATFORM_OVERRIDE, 1.1768 + nullptr)) || 1.1769 + NS_FAILED(Preferences::RegisterCallbackAndCall( 1.1770 + JSVersionChanged, 1.1771 + PREF_WORKERS_LATEST_JS_VERSION, 1.1772 + nullptr))) { 1.1773 + NS_WARNING("Failed to register pref callbacks!"); 1.1774 + } 1.1775 + 1.1776 + NS_ASSERTION(gRuntimeServiceDuringInit == this, "Should be 'this'!"); 1.1777 + gRuntimeServiceDuringInit = nullptr; 1.1778 + 1.1779 + // We assume atomic 32bit reads/writes. If this assumption doesn't hold on 1.1780 + // some wacky platform then the worst that could happen is that the close 1.1781 + // handler will run for a slightly different amount of time. 1.1782 + if (NS_FAILED(Preferences::AddIntVarCache( 1.1783 + &sDefaultJSSettings.content.maxScriptRuntime, 1.1784 + PREF_MAX_SCRIPT_RUN_TIME_CONTENT, 1.1785 + MAX_SCRIPT_RUN_TIME_SEC)) || 1.1786 + NS_FAILED(Preferences::AddIntVarCache( 1.1787 + &sDefaultJSSettings.chrome.maxScriptRuntime, 1.1788 + PREF_MAX_SCRIPT_RUN_TIME_CHROME, -1))) { 1.1789 + NS_WARNING("Failed to register timeout cache!"); 1.1790 + } 1.1791 + 1.1792 + int32_t maxPerDomain = Preferences::GetInt(PREF_WORKERS_MAX_PER_DOMAIN, 1.1793 + MAX_WORKERS_PER_DOMAIN); 1.1794 + gMaxWorkersPerDomain = std::max(0, maxPerDomain); 1.1795 + 1.1796 + rv = InitOSFileConstants(); 1.1797 + if (NS_FAILED(rv)) { 1.1798 + return rv; 1.1799 + } 1.1800 + 1.1801 + return NS_OK; 1.1802 +} 1.1803 + 1.1804 +void 1.1805 +RuntimeService::Shutdown() 1.1806 +{ 1.1807 + AssertIsOnMainThread(); 1.1808 + 1.1809 + MOZ_ASSERT(!mShuttingDown); 1.1810 + // That's it, no more workers. 1.1811 + mShuttingDown = true; 1.1812 + 1.1813 + nsCOMPtr<nsIObserverService> obs = services::GetObserverService(); 1.1814 + NS_WARN_IF_FALSE(obs, "Failed to get observer service?!"); 1.1815 + 1.1816 + // Tell anyone that cares that they're about to lose worker support. 1.1817 + if (obs && NS_FAILED(obs->NotifyObservers(nullptr, WORKERS_SHUTDOWN_TOPIC, 1.1818 + nullptr))) { 1.1819 + NS_WARNING("NotifyObservers failed!"); 1.1820 + } 1.1821 + 1.1822 + { 1.1823 + MutexAutoLock lock(mMutex); 1.1824 + 1.1825 + nsAutoTArray<WorkerPrivate*, 100> workers; 1.1826 + mDomainMap.EnumerateRead(AddAllTopLevelWorkersToArray, &workers); 1.1827 + 1.1828 + if (!workers.IsEmpty()) { 1.1829 + // Cancel all top-level workers. 1.1830 + { 1.1831 + MutexAutoUnlock unlock(mMutex); 1.1832 + 1.1833 + AutoSafeJSContext cx; 1.1834 + JSAutoRequest ar(cx); 1.1835 + 1.1836 + for (uint32_t index = 0; index < workers.Length(); index++) { 1.1837 + if (!workers[index]->Kill(cx)) { 1.1838 + NS_WARNING("Failed to cancel worker!"); 1.1839 + } 1.1840 + } 1.1841 + } 1.1842 + } 1.1843 + } 1.1844 +} 1.1845 + 1.1846 +// This spins the event loop until all workers are finished and their threads 1.1847 +// have been joined. 1.1848 +void 1.1849 +RuntimeService::Cleanup() 1.1850 +{ 1.1851 + AssertIsOnMainThread(); 1.1852 + 1.1853 + nsCOMPtr<nsIObserverService> obs = services::GetObserverService(); 1.1854 + NS_WARN_IF_FALSE(obs, "Failed to get observer service?!"); 1.1855 + 1.1856 + if (mIdleThreadTimer) { 1.1857 + if (NS_FAILED(mIdleThreadTimer->Cancel())) { 1.1858 + NS_WARNING("Failed to cancel idle timer!"); 1.1859 + } 1.1860 + mIdleThreadTimer = nullptr; 1.1861 + } 1.1862 + 1.1863 + { 1.1864 + MutexAutoLock lock(mMutex); 1.1865 + 1.1866 + nsAutoTArray<WorkerPrivate*, 100> workers; 1.1867 + mDomainMap.EnumerateRead(AddAllTopLevelWorkersToArray, &workers); 1.1868 + 1.1869 + if (!workers.IsEmpty()) { 1.1870 + nsIThread* currentThread = NS_GetCurrentThread(); 1.1871 + NS_ASSERTION(currentThread, "This should never be null!"); 1.1872 + 1.1873 + // Shut down any idle threads. 1.1874 + if (!mIdleThreadArray.IsEmpty()) { 1.1875 + nsAutoTArray<nsRefPtr<WorkerThread>, 20> idleThreads; 1.1876 + 1.1877 + uint32_t idleThreadCount = mIdleThreadArray.Length(); 1.1878 + idleThreads.SetLength(idleThreadCount); 1.1879 + 1.1880 + for (uint32_t index = 0; index < idleThreadCount; index++) { 1.1881 + NS_ASSERTION(mIdleThreadArray[index].mThread, "Null thread!"); 1.1882 + idleThreads[index].swap(mIdleThreadArray[index].mThread); 1.1883 + } 1.1884 + 1.1885 + mIdleThreadArray.Clear(); 1.1886 + 1.1887 + MutexAutoUnlock unlock(mMutex); 1.1888 + 1.1889 + for (uint32_t index = 0; index < idleThreadCount; index++) { 1.1890 + if (NS_FAILED(idleThreads[index]->Shutdown())) { 1.1891 + NS_WARNING("Failed to shutdown thread!"); 1.1892 + } 1.1893 + } 1.1894 + } 1.1895 + 1.1896 + // And make sure all their final messages have run and all their threads 1.1897 + // have joined. 1.1898 + while (mDomainMap.Count()) { 1.1899 + MutexAutoUnlock unlock(mMutex); 1.1900 + 1.1901 + if (!NS_ProcessNextEvent(currentThread)) { 1.1902 + NS_WARNING("Something bad happened!"); 1.1903 + break; 1.1904 + } 1.1905 + } 1.1906 + } 1.1907 + } 1.1908 + 1.1909 + NS_ASSERTION(!mWindowMap.Count(), "All windows should have been released!"); 1.1910 + 1.1911 + if (mObserved) { 1.1912 + if (NS_FAILED(Preferences::UnregisterCallback(JSVersionChanged, 1.1913 + PREF_WORKERS_LATEST_JS_VERSION, 1.1914 + nullptr)) || 1.1915 + NS_FAILED(Preferences::UnregisterCallback(AppNameOverrideChanged, 1.1916 + PREF_GENERAL_APPNAME_OVERRIDE, 1.1917 + nullptr)) || 1.1918 + NS_FAILED(Preferences::UnregisterCallback(AppVersionOverrideChanged, 1.1919 + PREF_GENERAL_APPVERSION_OVERRIDE, 1.1920 + nullptr)) || 1.1921 + NS_FAILED(Preferences::UnregisterCallback(PlatformOverrideChanged, 1.1922 + PREF_GENERAL_PLATFORM_OVERRIDE, 1.1923 + nullptr)) || 1.1924 + NS_FAILED(Preferences::UnregisterCallback(LoadRuntimeAndContextOptions, 1.1925 + PREF_JS_OPTIONS_PREFIX, 1.1926 + nullptr)) || 1.1927 + NS_FAILED(Preferences::UnregisterCallback(LoadRuntimeAndContextOptions, 1.1928 + PREF_WORKERS_OPTIONS_PREFIX, 1.1929 + nullptr)) || 1.1930 +#if DUMP_CONTROLLED_BY_PREF 1.1931 + NS_FAILED(Preferences::UnregisterCallback( 1.1932 + WorkerPrefChanged, 1.1933 + PREF_DOM_WINDOW_DUMP_ENABLED, 1.1934 + reinterpret_cast<void *>(WORKERPREF_DUMP))) || 1.1935 +#endif 1.1936 +#ifdef JS_GC_ZEAL 1.1937 + NS_FAILED(Preferences::UnregisterCallback( 1.1938 + LoadGCZealOptions, 1.1939 + PREF_JS_OPTIONS_PREFIX PREF_GCZEAL, 1.1940 + nullptr)) || 1.1941 + NS_FAILED(Preferences::UnregisterCallback( 1.1942 + LoadGCZealOptions, 1.1943 + PREF_WORKERS_OPTIONS_PREFIX PREF_GCZEAL, 1.1944 + nullptr)) || 1.1945 +#endif 1.1946 + NS_FAILED(Preferences::UnregisterCallback( 1.1947 + LoadJSGCMemoryOptions, 1.1948 + PREF_JS_OPTIONS_PREFIX PREF_MEM_OPTIONS_PREFIX, 1.1949 + nullptr)) || 1.1950 + NS_FAILED(Preferences::UnregisterCallback( 1.1951 + LoadJSGCMemoryOptions, 1.1952 + PREF_WORKERS_OPTIONS_PREFIX PREF_MEM_OPTIONS_PREFIX, 1.1953 + nullptr))) { 1.1954 + NS_WARNING("Failed to unregister pref callbacks!"); 1.1955 + } 1.1956 + 1.1957 + if (obs) { 1.1958 + if (NS_FAILED(obs->RemoveObserver(this, GC_REQUEST_OBSERVER_TOPIC))) { 1.1959 + NS_WARNING("Failed to unregister for GC request notifications!"); 1.1960 + } 1.1961 + 1.1962 + if (NS_FAILED(obs->RemoveObserver(this, CC_REQUEST_OBSERVER_TOPIC))) { 1.1963 + NS_WARNING("Failed to unregister for CC request notifications!"); 1.1964 + } 1.1965 + 1.1966 + if (NS_FAILED(obs->RemoveObserver(this, 1.1967 + MEMORY_PRESSURE_OBSERVER_TOPIC))) { 1.1968 + NS_WARNING("Failed to unregister for memory pressure notifications!"); 1.1969 + } 1.1970 + 1.1971 + if (NS_FAILED(obs->RemoveObserver(this, 1.1972 + NS_IOSERVICE_OFFLINE_STATUS_TOPIC))) { 1.1973 + NS_WARNING("Failed to unregister for offline notification event!"); 1.1974 + } 1.1975 + obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID); 1.1976 + obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID); 1.1977 + mObserved = false; 1.1978 + } 1.1979 + } 1.1980 + 1.1981 + CleanupOSFileConstants(); 1.1982 + nsLayoutStatics::Release(); 1.1983 +} 1.1984 + 1.1985 +// static 1.1986 +PLDHashOperator 1.1987 +RuntimeService::AddAllTopLevelWorkersToArray(const nsACString& aKey, 1.1988 + WorkerDomainInfo* aData, 1.1989 + void* aUserArg) 1.1990 +{ 1.1991 + nsTArray<WorkerPrivate*>* array = 1.1992 + static_cast<nsTArray<WorkerPrivate*>*>(aUserArg); 1.1993 + 1.1994 +#ifdef DEBUG 1.1995 + for (uint32_t index = 0; index < aData->mActiveWorkers.Length(); index++) { 1.1996 + NS_ASSERTION(!aData->mActiveWorkers[index]->GetParent(), 1.1997 + "Shouldn't have a parent in this list!"); 1.1998 + } 1.1999 +#endif 1.2000 + 1.2001 + array->AppendElements(aData->mActiveWorkers); 1.2002 + 1.2003 + // These might not be top-level workers... 1.2004 + for (uint32_t index = 0; index < aData->mQueuedWorkers.Length(); index++) { 1.2005 + WorkerPrivate* worker = aData->mQueuedWorkers[index]; 1.2006 + if (!worker->GetParent()) { 1.2007 + array->AppendElement(worker); 1.2008 + } 1.2009 + } 1.2010 + 1.2011 + return PL_DHASH_NEXT; 1.2012 +} 1.2013 + 1.2014 +// static 1.2015 +PLDHashOperator 1.2016 +RuntimeService::RemoveSharedWorkerFromWindowMap( 1.2017 + nsPIDOMWindow* aKey, 1.2018 + nsAutoPtr<nsTArray<WorkerPrivate*> >& aData, 1.2019 + void* aUserArg) 1.2020 +{ 1.2021 + AssertIsOnMainThread(); 1.2022 + MOZ_ASSERT(aData.get()); 1.2023 + MOZ_ASSERT(aUserArg); 1.2024 + 1.2025 + auto workerPrivate = static_cast<WorkerPrivate*>(aUserArg); 1.2026 + 1.2027 + MOZ_ASSERT(workerPrivate->IsSharedWorker()); 1.2028 + 1.2029 + if (aData->RemoveElement(workerPrivate)) { 1.2030 + MOZ_ASSERT(!aData->Contains(workerPrivate), "Added worker more than once!"); 1.2031 + 1.2032 + if (aData->IsEmpty()) { 1.2033 + return PL_DHASH_REMOVE; 1.2034 + } 1.2035 + } 1.2036 + 1.2037 + return PL_DHASH_NEXT; 1.2038 +} 1.2039 + 1.2040 +// static 1.2041 +PLDHashOperator 1.2042 +RuntimeService::FindSharedWorkerInfo(const nsACString& aKey, 1.2043 + SharedWorkerInfo* aData, 1.2044 + void* aUserArg) 1.2045 +{ 1.2046 + auto match = static_cast<MatchSharedWorkerInfo*>(aUserArg); 1.2047 + 1.2048 + if (aData->mWorkerPrivate == match->mWorkerPrivate) { 1.2049 + match->mSharedWorkerInfo = aData; 1.2050 + return PL_DHASH_STOP; 1.2051 + } 1.2052 + 1.2053 + return PL_DHASH_NEXT; 1.2054 +} 1.2055 + 1.2056 +void 1.2057 +RuntimeService::GetWorkersForWindow(nsPIDOMWindow* aWindow, 1.2058 + nsTArray<WorkerPrivate*>& aWorkers) 1.2059 +{ 1.2060 + AssertIsOnMainThread(); 1.2061 + 1.2062 + nsTArray<WorkerPrivate*>* workers; 1.2063 + if (mWindowMap.Get(aWindow, &workers)) { 1.2064 + NS_ASSERTION(!workers->IsEmpty(), "Should have been removed!"); 1.2065 + aWorkers.AppendElements(*workers); 1.2066 + } 1.2067 + else { 1.2068 + NS_ASSERTION(aWorkers.IsEmpty(), "Should be empty!"); 1.2069 + } 1.2070 +} 1.2071 + 1.2072 +void 1.2073 +RuntimeService::CancelWorkersForWindow(nsPIDOMWindow* aWindow) 1.2074 +{ 1.2075 + AssertIsOnMainThread(); 1.2076 + 1.2077 + nsAutoTArray<WorkerPrivate*, MAX_WORKERS_PER_DOMAIN> workers; 1.2078 + GetWorkersForWindow(aWindow, workers); 1.2079 + 1.2080 + if (!workers.IsEmpty()) { 1.2081 + nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(aWindow); 1.2082 + MOZ_ASSERT(sgo); 1.2083 + 1.2084 + nsIScriptContext* scx = sgo->GetContext(); 1.2085 + 1.2086 + AutoPushJSContext cx(scx ? 1.2087 + scx->GetNativeContext() : 1.2088 + nsContentUtils::GetSafeJSContext()); 1.2089 + 1.2090 + for (uint32_t index = 0; index < workers.Length(); index++) { 1.2091 + WorkerPrivate*& worker = workers[index]; 1.2092 + 1.2093 + if (worker->IsSharedWorker()) { 1.2094 + worker->CloseSharedWorkersForWindow(aWindow); 1.2095 + } else if (!worker->Cancel(cx)) { 1.2096 + JS_ReportPendingException(cx); 1.2097 + } 1.2098 + } 1.2099 + } 1.2100 +} 1.2101 + 1.2102 +void 1.2103 +RuntimeService::SuspendWorkersForWindow(nsPIDOMWindow* aWindow) 1.2104 +{ 1.2105 + AssertIsOnMainThread(); 1.2106 + MOZ_ASSERT(aWindow); 1.2107 + 1.2108 + nsAutoTArray<WorkerPrivate*, MAX_WORKERS_PER_DOMAIN> workers; 1.2109 + GetWorkersForWindow(aWindow, workers); 1.2110 + 1.2111 + if (!workers.IsEmpty()) { 1.2112 + nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(aWindow); 1.2113 + MOZ_ASSERT(sgo); 1.2114 + 1.2115 + nsIScriptContext* scx = sgo->GetContext(); 1.2116 + 1.2117 + AutoPushJSContext cx(scx ? 1.2118 + scx->GetNativeContext() : 1.2119 + nsContentUtils::GetSafeJSContext()); 1.2120 + 1.2121 + for (uint32_t index = 0; index < workers.Length(); index++) { 1.2122 + if (!workers[index]->Suspend(cx, aWindow)) { 1.2123 + JS_ReportPendingException(cx); 1.2124 + } 1.2125 + } 1.2126 + } 1.2127 +} 1.2128 + 1.2129 +void 1.2130 +RuntimeService::ResumeWorkersForWindow(nsPIDOMWindow* aWindow) 1.2131 +{ 1.2132 + AssertIsOnMainThread(); 1.2133 + MOZ_ASSERT(aWindow); 1.2134 + 1.2135 + nsAutoTArray<WorkerPrivate*, MAX_WORKERS_PER_DOMAIN> workers; 1.2136 + GetWorkersForWindow(aWindow, workers); 1.2137 + 1.2138 + if (!workers.IsEmpty()) { 1.2139 + nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(aWindow); 1.2140 + MOZ_ASSERT(sgo); 1.2141 + 1.2142 + nsIScriptContext* scx = sgo->GetContext(); 1.2143 + 1.2144 + AutoPushJSContext cx(scx ? 1.2145 + scx->GetNativeContext() : 1.2146 + nsContentUtils::GetSafeJSContext()); 1.2147 + 1.2148 + for (uint32_t index = 0; index < workers.Length(); index++) { 1.2149 + if (!workers[index]->SynchronizeAndResume(cx, aWindow, scx)) { 1.2150 + JS_ReportPendingException(cx); 1.2151 + } 1.2152 + } 1.2153 + } 1.2154 +} 1.2155 + 1.2156 +nsresult 1.2157 +RuntimeService::CreateSharedWorker(const GlobalObject& aGlobal, 1.2158 + const nsAString& aScriptURL, 1.2159 + const nsACString& aName, 1.2160 + SharedWorker** aSharedWorker) 1.2161 +{ 1.2162 + AssertIsOnMainThread(); 1.2163 + 1.2164 + nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.GetAsSupports()); 1.2165 + MOZ_ASSERT(window); 1.2166 + 1.2167 + JSContext* cx = aGlobal.GetContext(); 1.2168 + 1.2169 + WorkerPrivate::LoadInfo loadInfo; 1.2170 + nsresult rv = WorkerPrivate::GetLoadInfo(cx, window, nullptr, aScriptURL, 1.2171 + false, &loadInfo); 1.2172 + NS_ENSURE_SUCCESS(rv, rv); 1.2173 + 1.2174 + MOZ_ASSERT(loadInfo.mResolvedScriptURI); 1.2175 + 1.2176 + nsCString scriptSpec; 1.2177 + rv = loadInfo.mResolvedScriptURI->GetSpec(scriptSpec); 1.2178 + NS_ENSURE_SUCCESS(rv, rv); 1.2179 + 1.2180 + nsRefPtr<WorkerPrivate> workerPrivate; 1.2181 + { 1.2182 + MutexAutoLock lock(mMutex); 1.2183 + 1.2184 + WorkerDomainInfo* domainInfo; 1.2185 + SharedWorkerInfo* sharedWorkerInfo; 1.2186 + 1.2187 + nsAutoCString key; 1.2188 + GenerateSharedWorkerKey(scriptSpec, aName, key); 1.2189 + 1.2190 + if (mDomainMap.Get(loadInfo.mDomain, &domainInfo) && 1.2191 + domainInfo->mSharedWorkerInfos.Get(key, &sharedWorkerInfo)) { 1.2192 + workerPrivate = sharedWorkerInfo->mWorkerPrivate; 1.2193 + } 1.2194 + } 1.2195 + 1.2196 + bool created = false; 1.2197 + 1.2198 + if (!workerPrivate) { 1.2199 + ErrorResult rv; 1.2200 + workerPrivate = 1.2201 + WorkerPrivate::Constructor(aGlobal, aScriptURL, false, 1.2202 + WorkerPrivate::WorkerTypeShared, aName, 1.2203 + &loadInfo, rv); 1.2204 + NS_ENSURE_TRUE(workerPrivate, rv.ErrorCode()); 1.2205 + 1.2206 + created = true; 1.2207 + } 1.2208 + 1.2209 + MOZ_ASSERT(workerPrivate->IsSharedWorker()); 1.2210 + 1.2211 + nsRefPtr<SharedWorker> sharedWorker = 1.2212 + new SharedWorker(window, workerPrivate); 1.2213 + 1.2214 + if (!workerPrivate->RegisterSharedWorker(cx, sharedWorker)) { 1.2215 + NS_WARNING("Worker is unreachable, this shouldn't happen!"); 1.2216 + sharedWorker->Close(); 1.2217 + return NS_ERROR_FAILURE; 1.2218 + } 1.2219 + 1.2220 + // This is normally handled in RegisterWorker, but that wasn't called if the 1.2221 + // worker already existed. 1.2222 + if (!created) { 1.2223 + nsTArray<WorkerPrivate*>* windowArray; 1.2224 + if (!mWindowMap.Get(window, &windowArray)) { 1.2225 + windowArray = new nsTArray<WorkerPrivate*>(1); 1.2226 + mWindowMap.Put(window, windowArray); 1.2227 + } 1.2228 + 1.2229 + if (!windowArray->Contains(workerPrivate)) { 1.2230 + windowArray->AppendElement(workerPrivate); 1.2231 + } 1.2232 + } 1.2233 + 1.2234 + sharedWorker.forget(aSharedWorker); 1.2235 + return NS_OK; 1.2236 +} 1.2237 + 1.2238 +void 1.2239 +RuntimeService::ForgetSharedWorker(WorkerPrivate* aWorkerPrivate) 1.2240 +{ 1.2241 + AssertIsOnMainThread(); 1.2242 + MOZ_ASSERT(aWorkerPrivate); 1.2243 + MOZ_ASSERT(aWorkerPrivate->IsSharedWorker()); 1.2244 + 1.2245 + MutexAutoLock lock(mMutex); 1.2246 + 1.2247 + WorkerDomainInfo* domainInfo; 1.2248 + if (mDomainMap.Get(aWorkerPrivate->Domain(), &domainInfo)) { 1.2249 + MatchSharedWorkerInfo match(aWorkerPrivate); 1.2250 + domainInfo->mSharedWorkerInfos.EnumerateRead(FindSharedWorkerInfo, 1.2251 + &match); 1.2252 + 1.2253 + if (match.mSharedWorkerInfo) { 1.2254 + nsAutoCString key; 1.2255 + GenerateSharedWorkerKey(match.mSharedWorkerInfo->mScriptSpec, 1.2256 + match.mSharedWorkerInfo->mName, key); 1.2257 + domainInfo->mSharedWorkerInfos.Remove(key); 1.2258 + } 1.2259 + } 1.2260 +} 1.2261 + 1.2262 +void 1.2263 +RuntimeService::NoteIdleThread(WorkerThread* aThread) 1.2264 +{ 1.2265 + AssertIsOnMainThread(); 1.2266 + MOZ_ASSERT(aThread); 1.2267 + 1.2268 +#ifdef DEBUG 1.2269 + aThread->SetAcceptingNonWorkerRunnables(true); 1.2270 +#endif 1.2271 + 1.2272 + static TimeDuration timeout = 1.2273 + TimeDuration::FromSeconds(IDLE_THREAD_TIMEOUT_SEC); 1.2274 + 1.2275 + TimeStamp expirationTime = TimeStamp::Now() + timeout; 1.2276 + 1.2277 + bool shutdown; 1.2278 + if (mShuttingDown) { 1.2279 + shutdown = true; 1.2280 + } 1.2281 + else { 1.2282 + MutexAutoLock lock(mMutex); 1.2283 + 1.2284 + if (mIdleThreadArray.Length() < MAX_IDLE_THREADS) { 1.2285 + IdleThreadInfo* info = mIdleThreadArray.AppendElement(); 1.2286 + info->mThread = aThread; 1.2287 + info->mExpirationTime = expirationTime; 1.2288 + shutdown = false; 1.2289 + } 1.2290 + else { 1.2291 + shutdown = true; 1.2292 + } 1.2293 + } 1.2294 + 1.2295 + // Too many idle threads, just shut this one down. 1.2296 + if (shutdown) { 1.2297 + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(aThread->Shutdown())); 1.2298 + return; 1.2299 + } 1.2300 + 1.2301 + // Schedule timer. 1.2302 + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mIdleThreadTimer->InitWithFuncCallback( 1.2303 + ShutdownIdleThreads, nullptr, 1.2304 + IDLE_THREAD_TIMEOUT_SEC * 1000, 1.2305 + nsITimer::TYPE_ONE_SHOT))); 1.2306 +} 1.2307 + 1.2308 +void 1.2309 +RuntimeService::UpdateAllWorkerRuntimeAndContextOptions() 1.2310 +{ 1.2311 + BROADCAST_ALL_WORKERS(UpdateRuntimeAndContextOptions, 1.2312 + sDefaultJSSettings.runtimeOptions, 1.2313 + sDefaultJSSettings.content.contextOptions, 1.2314 + sDefaultJSSettings.chrome.contextOptions); 1.2315 +} 1.2316 + 1.2317 +void 1.2318 +RuntimeService::UpdateAppNameOverridePreference(const nsAString& aValue) 1.2319 +{ 1.2320 + AssertIsOnMainThread(); 1.2321 + mNavigatorProperties.mAppNameOverridden = aValue; 1.2322 +} 1.2323 + 1.2324 +void 1.2325 +RuntimeService::UpdateAppVersionOverridePreference(const nsAString& aValue) 1.2326 +{ 1.2327 + AssertIsOnMainThread(); 1.2328 + mNavigatorProperties.mAppVersionOverridden = aValue; 1.2329 +} 1.2330 + 1.2331 +void 1.2332 +RuntimeService::UpdatePlatformOverridePreference(const nsAString& aValue) 1.2333 +{ 1.2334 + AssertIsOnMainThread(); 1.2335 + mNavigatorProperties.mPlatformOverridden = aValue; 1.2336 +} 1.2337 + 1.2338 +void 1.2339 +RuntimeService::UpdateAllWorkerPreference(WorkerPreference aPref, bool aValue) 1.2340 +{ 1.2341 + BROADCAST_ALL_WORKERS(UpdatePreference, aPref, aValue); 1.2342 +} 1.2343 + 1.2344 +void 1.2345 +RuntimeService::UpdateAllWorkerMemoryParameter(JSGCParamKey aKey, 1.2346 + uint32_t aValue) 1.2347 +{ 1.2348 + BROADCAST_ALL_WORKERS(UpdateJSWorkerMemoryParameter, aKey, aValue); 1.2349 +} 1.2350 + 1.2351 +#ifdef JS_GC_ZEAL 1.2352 +void 1.2353 +RuntimeService::UpdateAllWorkerGCZeal() 1.2354 +{ 1.2355 + BROADCAST_ALL_WORKERS(UpdateGCZeal, sDefaultJSSettings.gcZeal, 1.2356 + sDefaultJSSettings.gcZealFrequency); 1.2357 +} 1.2358 +#endif 1.2359 + 1.2360 +void 1.2361 +RuntimeService::GarbageCollectAllWorkers(bool aShrinking) 1.2362 +{ 1.2363 + BROADCAST_ALL_WORKERS(GarbageCollect, aShrinking); 1.2364 +} 1.2365 + 1.2366 +void 1.2367 +RuntimeService::CycleCollectAllWorkers() 1.2368 +{ 1.2369 + BROADCAST_ALL_WORKERS(CycleCollect, /* dummy = */ false); 1.2370 +} 1.2371 + 1.2372 +void 1.2373 +RuntimeService::SendOfflineStatusChangeEventToAllWorkers(bool aIsOffline) 1.2374 +{ 1.2375 + BROADCAST_ALL_WORKERS(OfflineStatusChangeEvent, aIsOffline); 1.2376 +} 1.2377 + 1.2378 +// nsISupports 1.2379 +NS_IMPL_ISUPPORTS(RuntimeService, nsIObserver) 1.2380 + 1.2381 +// nsIObserver 1.2382 +NS_IMETHODIMP 1.2383 +RuntimeService::Observe(nsISupports* aSubject, const char* aTopic, 1.2384 + const char16_t* aData) 1.2385 +{ 1.2386 + AssertIsOnMainThread(); 1.2387 + 1.2388 + if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) { 1.2389 + Shutdown(); 1.2390 + return NS_OK; 1.2391 + } 1.2392 + if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID)) { 1.2393 + Cleanup(); 1.2394 + return NS_OK; 1.2395 + } 1.2396 + if (!strcmp(aTopic, GC_REQUEST_OBSERVER_TOPIC)) { 1.2397 + GarbageCollectAllWorkers(/* shrinking = */ false); 1.2398 + return NS_OK; 1.2399 + } 1.2400 + if (!strcmp(aTopic, CC_REQUEST_OBSERVER_TOPIC)) { 1.2401 + CycleCollectAllWorkers(); 1.2402 + return NS_OK; 1.2403 + } 1.2404 + if (!strcmp(aTopic, MEMORY_PRESSURE_OBSERVER_TOPIC)) { 1.2405 + GarbageCollectAllWorkers(/* shrinking = */ true); 1.2406 + CycleCollectAllWorkers(); 1.2407 + return NS_OK; 1.2408 + } 1.2409 + if (!strcmp(aTopic, NS_IOSERVICE_OFFLINE_STATUS_TOPIC)) { 1.2410 + SendOfflineStatusChangeEventToAllWorkers(NS_IsOffline()); 1.2411 + return NS_OK; 1.2412 + } 1.2413 + 1.2414 + NS_NOTREACHED("Unknown observer topic!"); 1.2415 + return NS_OK; 1.2416 +} 1.2417 + 1.2418 +/* static */ void 1.2419 +RuntimeService::WorkerPrefChanged(const char* aPrefName, void* aClosure) 1.2420 +{ 1.2421 + AssertIsOnMainThread(); 1.2422 + 1.2423 + uintptr_t tmp = reinterpret_cast<uintptr_t>(aClosure); 1.2424 + MOZ_ASSERT(tmp < WORKERPREF_COUNT); 1.2425 + WorkerPreference key = static_cast<WorkerPreference>(tmp); 1.2426 + 1.2427 +#ifdef DUMP_CONTROLLED_BY_PREF 1.2428 + if (key == WORKERPREF_DUMP) { 1.2429 + key = WORKERPREF_DUMP; 1.2430 + sDefaultPreferences[WORKERPREF_DUMP] = 1.2431 + Preferences::GetBool(PREF_DOM_WINDOW_DUMP_ENABLED, false); 1.2432 + } 1.2433 +#endif 1.2434 + 1.2435 + // This function should never be registered as a callback for a preference it 1.2436 + // does not handle. 1.2437 + MOZ_ASSERT(key != WORKERPREF_COUNT); 1.2438 + 1.2439 + RuntimeService* rts = RuntimeService::GetService(); 1.2440 + if (rts) { 1.2441 + rts->UpdateAllWorkerPreference(key, sDefaultPreferences[key]); 1.2442 + } 1.2443 +} 1.2444 + 1.2445 +void 1.2446 +RuntimeService::JSVersionChanged(const char* /* aPrefName */, void* /* aClosure */) 1.2447 +{ 1.2448 + AssertIsOnMainThread(); 1.2449 + 1.2450 + bool useLatest = Preferences::GetBool(PREF_WORKERS_LATEST_JS_VERSION, false); 1.2451 + JS::CompartmentOptions& options = sDefaultJSSettings.content.compartmentOptions; 1.2452 + options.setVersion(useLatest ? JSVERSION_LATEST : JSVERSION_DEFAULT); 1.2453 +} 1.2454 + 1.2455 +// static 1.2456 +already_AddRefed<RuntimeService::WorkerThread> 1.2457 +RuntimeService::WorkerThread::Create() 1.2458 +{ 1.2459 + MOZ_ASSERT(nsThreadManager::get()); 1.2460 + 1.2461 + nsRefPtr<WorkerThread> thread = new WorkerThread(); 1.2462 + if (NS_FAILED(thread->Init())) { 1.2463 + NS_WARNING("Failed to create new thread!"); 1.2464 + return nullptr; 1.2465 + } 1.2466 + 1.2467 + NS_SetThreadName(thread, "DOM Worker"); 1.2468 + 1.2469 + return thread.forget(); 1.2470 +} 1.2471 + 1.2472 +void 1.2473 +RuntimeService::WorkerThread::SetWorker(WorkerPrivate* aWorkerPrivate) 1.2474 +{ 1.2475 + MOZ_ASSERT(PR_GetCurrentThread() == mThread); 1.2476 + MOZ_ASSERT_IF(aWorkerPrivate, !mWorkerPrivate); 1.2477 + MOZ_ASSERT_IF(!aWorkerPrivate, mWorkerPrivate); 1.2478 + 1.2479 + // No need to lock here because mWorkerPrivate is only modified on mThread. 1.2480 + 1.2481 + if (mWorkerPrivate) { 1.2482 + MOZ_ASSERT(mObserver); 1.2483 + 1.2484 + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(RemoveObserver(mObserver))); 1.2485 + 1.2486 + mObserver = nullptr; 1.2487 + mWorkerPrivate->SetThread(nullptr); 1.2488 + } 1.2489 + 1.2490 + mWorkerPrivate = aWorkerPrivate; 1.2491 + 1.2492 + if (mWorkerPrivate) { 1.2493 + mWorkerPrivate->SetThread(this); 1.2494 + 1.2495 + nsRefPtr<Observer> observer = new Observer(mWorkerPrivate); 1.2496 + 1.2497 + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(AddObserver(observer))); 1.2498 + 1.2499 + mObserver.swap(observer); 1.2500 + } 1.2501 +} 1.2502 + 1.2503 +NS_IMPL_ISUPPORTS_INHERITED0(RuntimeService::WorkerThread, nsThread) 1.2504 + 1.2505 +NS_IMETHODIMP 1.2506 +RuntimeService::WorkerThread::Dispatch(nsIRunnable* aRunnable, uint32_t aFlags) 1.2507 +{ 1.2508 + // May be called on any thread! 1.2509 + 1.2510 +#ifdef DEBUG 1.2511 + if (PR_GetCurrentThread() == mThread) { 1.2512 + MOZ_ASSERT(mWorkerPrivate); 1.2513 + mWorkerPrivate->AssertIsOnWorkerThread(); 1.2514 + } 1.2515 + else if (aRunnable && !IsAcceptingNonWorkerRunnables()) { 1.2516 + // Only enforce cancelable runnables after we've started the worker loop. 1.2517 + nsCOMPtr<nsICancelableRunnable> cancelable = do_QueryInterface(aRunnable); 1.2518 + MOZ_ASSERT(cancelable, 1.2519 + "Should have been wrapped by the worker's event target!"); 1.2520 + } 1.2521 +#endif 1.2522 + 1.2523 + // Workers only support asynchronous dispatch for now. 1.2524 + if (NS_WARN_IF(aFlags != NS_DISPATCH_NORMAL)) { 1.2525 + return NS_ERROR_UNEXPECTED; 1.2526 + } 1.2527 + 1.2528 + nsIRunnable* runnableToDispatch; 1.2529 + nsRefPtr<WorkerRunnable> workerRunnable; 1.2530 + 1.2531 + if (aRunnable && PR_GetCurrentThread() == mThread) { 1.2532 + // No need to lock here because mWorkerPrivate is only modified on mThread. 1.2533 + workerRunnable = mWorkerPrivate->MaybeWrapAsWorkerRunnable(aRunnable); 1.2534 + runnableToDispatch = workerRunnable; 1.2535 + } 1.2536 + else { 1.2537 + runnableToDispatch = aRunnable; 1.2538 + } 1.2539 + 1.2540 + nsresult rv = nsThread::Dispatch(runnableToDispatch, NS_DISPATCH_NORMAL); 1.2541 + if (NS_WARN_IF(NS_FAILED(rv))) { 1.2542 + return rv; 1.2543 + } 1.2544 + 1.2545 + return NS_OK; 1.2546 +} 1.2547 + 1.2548 +NS_IMPL_ISUPPORTS(RuntimeService::WorkerThread::Observer, nsIThreadObserver) 1.2549 + 1.2550 +NS_IMETHODIMP 1.2551 +RuntimeService::WorkerThread::Observer::OnDispatchedEvent( 1.2552 + nsIThreadInternal* /*aThread */) 1.2553 +{ 1.2554 + MOZ_ASSUME_UNREACHABLE("This should never be called!"); 1.2555 +} 1.2556 + 1.2557 +NS_IMETHODIMP 1.2558 +RuntimeService::WorkerThread::Observer::OnProcessNextEvent( 1.2559 + nsIThreadInternal* /* aThread */, 1.2560 + bool aMayWait, 1.2561 + uint32_t aRecursionDepth) 1.2562 +{ 1.2563 + mWorkerPrivate->AssertIsOnWorkerThread(); 1.2564 + MOZ_ASSERT(!aMayWait); 1.2565 + 1.2566 + mWorkerPrivate->OnProcessNextEvent(aRecursionDepth); 1.2567 + return NS_OK; 1.2568 +} 1.2569 + 1.2570 +NS_IMETHODIMP 1.2571 +RuntimeService::WorkerThread::Observer::AfterProcessNextEvent( 1.2572 + nsIThreadInternal* /* aThread */, 1.2573 + uint32_t aRecursionDepth, 1.2574 + bool /* aEventWasProcessed */) 1.2575 +{ 1.2576 + mWorkerPrivate->AssertIsOnWorkerThread(); 1.2577 + 1.2578 + mWorkerPrivate->AfterProcessNextEvent(aRecursionDepth); 1.2579 + return NS_OK; 1.2580 +} 1.2581 + 1.2582 +NS_IMPL_ISUPPORTS_INHERITED0(LogViolationDetailsRunnable, nsRunnable) 1.2583 + 1.2584 +NS_IMETHODIMP 1.2585 +LogViolationDetailsRunnable::Run() 1.2586 +{ 1.2587 + AssertIsOnMainThread(); 1.2588 + 1.2589 + nsIContentSecurityPolicy* csp = mWorkerPrivate->GetCSP(); 1.2590 + if (csp) { 1.2591 + NS_NAMED_LITERAL_STRING(scriptSample, 1.2592 + "Call to eval() or related function blocked by CSP."); 1.2593 + if (mWorkerPrivate->GetReportCSPViolations()) { 1.2594 + csp->LogViolationDetails(nsIContentSecurityPolicy::VIOLATION_TYPE_EVAL, 1.2595 + mFileName, scriptSample, mLineNum, 1.2596 + EmptyString(), EmptyString()); 1.2597 + } 1.2598 + } 1.2599 + 1.2600 + nsRefPtr<MainThreadStopSyncLoopRunnable> response = 1.2601 + new MainThreadStopSyncLoopRunnable(mWorkerPrivate, mSyncLoopTarget.forget(), 1.2602 + true); 1.2603 + MOZ_ALWAYS_TRUE(response->Dispatch(nullptr)); 1.2604 + 1.2605 + return NS_OK; 1.2606 +} 1.2607 + 1.2608 +NS_IMPL_ISUPPORTS_INHERITED0(WorkerThreadPrimaryRunnable, nsRunnable) 1.2609 + 1.2610 +NS_IMETHODIMP 1.2611 +WorkerThreadPrimaryRunnable::Run() 1.2612 +{ 1.2613 +#ifdef MOZ_NUWA_PROCESS 1.2614 + if (IsNuwaProcess()) { 1.2615 + NS_ASSERTION(NuwaMarkCurrentThread != nullptr, 1.2616 + "NuwaMarkCurrentThread is undefined!"); 1.2617 + NuwaMarkCurrentThread(nullptr, nullptr); 1.2618 + NuwaFreezeCurrentThread(); 1.2619 + } 1.2620 +#endif 1.2621 + 1.2622 + char stackBaseGuess; 1.2623 + 1.2624 + nsAutoCString threadName; 1.2625 + threadName.AssignLiteral("WebWorker '"); 1.2626 + threadName.Append(NS_LossyConvertUTF16toASCII(mWorkerPrivate->ScriptURL())); 1.2627 + threadName.Append('\''); 1.2628 + 1.2629 + profiler_register_thread(threadName.get(), &stackBaseGuess); 1.2630 + 1.2631 + mThread->SetWorker(mWorkerPrivate); 1.2632 + 1.2633 + mWorkerPrivate->AssertIsOnWorkerThread(); 1.2634 + 1.2635 + { 1.2636 + nsCycleCollector_startup(); 1.2637 + 1.2638 + WorkerJSRuntime runtime(mParentRuntime, mWorkerPrivate); 1.2639 + JSRuntime* rt = runtime.Runtime(); 1.2640 + 1.2641 + JSContext* cx = CreateJSContextForWorker(mWorkerPrivate, rt); 1.2642 + if (!cx) { 1.2643 + // XXX need to fire an error at parent. 1.2644 + NS_ERROR("Failed to create runtime and context!"); 1.2645 + return NS_ERROR_FAILURE; 1.2646 + } 1.2647 + 1.2648 + { 1.2649 +#ifdef MOZ_ENABLE_PROFILER_SPS 1.2650 + PseudoStack* stack = mozilla_get_pseudo_stack(); 1.2651 + if (stack) { 1.2652 + stack->sampleRuntime(rt); 1.2653 + } 1.2654 +#endif 1.2655 + 1.2656 + { 1.2657 + JSAutoRequest ar(cx); 1.2658 + 1.2659 + mWorkerPrivate->DoRunLoop(cx); 1.2660 + 1.2661 + JS_ReportPendingException(cx); 1.2662 + } 1.2663 + 1.2664 +#ifdef MOZ_ENABLE_PROFILER_SPS 1.2665 + if (stack) { 1.2666 + stack->sampleRuntime(nullptr); 1.2667 + } 1.2668 +#endif 1.2669 + } 1.2670 + 1.2671 + // Destroy the main context. This will unroot the main worker global and 1.2672 + // GC. This is not the last JSContext (WorkerJSRuntime maintains an 1.2673 + // internal JSContext). 1.2674 + JS_DestroyContext(cx); 1.2675 + 1.2676 + // Now WorkerJSRuntime goes out of scope and its destructor will shut 1.2677 + // down the cycle collector and destroy the final JSContext. This 1.2678 + // breaks any remaining cycles and collects the C++ and JS objects 1.2679 + // participating. 1.2680 + } 1.2681 + 1.2682 + mThread->SetWorker(nullptr); 1.2683 + 1.2684 + mWorkerPrivate->ScheduleDeletion(WorkerPrivate::WorkerRan); 1.2685 + 1.2686 + // It is no longer safe to touch mWorkerPrivate. 1.2687 + mWorkerPrivate = nullptr; 1.2688 + 1.2689 + // Now recycle this thread. 1.2690 + nsCOMPtr<nsIThread> mainThread = do_GetMainThread(); 1.2691 + MOZ_ASSERT(mainThread); 1.2692 + 1.2693 + nsRefPtr<FinishedRunnable> finishedRunnable = 1.2694 + new FinishedRunnable(mThread.forget()); 1.2695 + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mainThread->Dispatch(finishedRunnable, 1.2696 + NS_DISPATCH_NORMAL))); 1.2697 + 1.2698 + profiler_unregister_thread(); 1.2699 + return NS_OK; 1.2700 +} 1.2701 + 1.2702 +NS_IMPL_ISUPPORTS_INHERITED0(WorkerThreadPrimaryRunnable::FinishedRunnable, 1.2703 + nsRunnable) 1.2704 + 1.2705 +NS_IMETHODIMP 1.2706 +WorkerThreadPrimaryRunnable::FinishedRunnable::Run() 1.2707 +{ 1.2708 + AssertIsOnMainThread(); 1.2709 + 1.2710 + nsRefPtr<RuntimeService::WorkerThread> thread; 1.2711 + mThread.swap(thread); 1.2712 + 1.2713 + RuntimeService* rts = RuntimeService::GetService(); 1.2714 + if (rts) { 1.2715 + rts->NoteIdleThread(thread); 1.2716 + } 1.2717 + else if (thread->ShutdownRequired()) { 1.2718 + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(thread->Shutdown())); 1.2719 + } 1.2720 + 1.2721 + return NS_OK; 1.2722 +}