dom/workers/RuntimeService.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial