LCOV - code coverage report
Current view: top level - dom/workers - RuntimeService.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 527 1332 39.6 %
Date: 2017-07-14 16:53:18 Functions: 75 177 42.4 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13