dom/workers/RuntimeService.cpp

Fri, 16 Jan 2015 18:13:44 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 18:13:44 +0100
branch
TOR_BUG_9701
changeset 14
925c144e1f1f
permissions
-rw-r--r--

Integrate suggestion from review to improve consistency with existing code.

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

mercurial