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

Generated by: LCOV version 1.13-14-ga5dd952