dom/workers/RuntimeService.cpp

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

mercurial