tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

RuntimeService.cpp (84938B)


      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 <algorithm>
     10 
     11 #include "GeckoProfiler.h"
     12 #include "XPCSelfHostedShmem.h"
     13 #include "js/ColumnNumber.h"  // JS::ColumnNumberOneOrigin
     14 #include "js/ContextOptions.h"
     15 #include "js/GCVector.h"
     16 #include "js/Initialization.h"
     17 #include "js/LocaleSensitive.h"
     18 #include "js/Value.h"
     19 #include "js/WasmFeatures.h"
     20 #include "js/experimental/CTypes.h"  // JS::CTypesActivityType, JS::SetCTypesActivityCallback
     21 #include "js/friend/ErrorMessages.h"  // js::GetErrorMessage, JSMSG_*
     22 #include "jsfriendapi.h"
     23 #include "mozilla/Atomics.h"
     24 #include "mozilla/Attributes.h"
     25 #include "mozilla/CycleCollectedJSContext.h"
     26 #include "mozilla/CycleCollectedJSRuntime.h"
     27 #include "mozilla/FlowMarkers.h"
     28 #include "mozilla/Monitor.h"
     29 #include "mozilla/Preferences.h"
     30 #include "mozilla/ScopeExit.h"
     31 #include "mozilla/StaticPrefs_javascript.h"
     32 #include "mozilla/TimeStamp.h"
     33 #include "mozilla/dom/AtomList.h"
     34 #include "mozilla/dom/BindingUtils.h"
     35 #include "mozilla/dom/Document.h"
     36 #include "mozilla/dom/ErrorEventBinding.h"
     37 #include "mozilla/dom/EventTargetBinding.h"
     38 #include "mozilla/dom/FetchUtil.h"
     39 #include "mozilla/dom/IndexedDatabaseManager.h"
     40 #include "mozilla/dom/MessageChannel.h"
     41 #include "mozilla/dom/MessageEventBinding.h"
     42 #include "mozilla/dom/Navigator.h"
     43 #include "mozilla/dom/PerformanceService.h"
     44 #include "mozilla/dom/RemoteWorkerChild.h"
     45 #include "mozilla/dom/ScriptSettings.h"
     46 #include "mozilla/dom/ShadowRealmGlobalScope.h"
     47 #include "mozilla/dom/TimeoutHandler.h"
     48 #include "mozilla/dom/TrustedTypeUtils.h"
     49 #include "mozilla/dom/WorkerBinding.h"
     50 #include "mozilla/extensions/WebExtensionPolicy.h"
     51 #include "mozilla/glean/DomServiceworkersMetrics.h"
     52 #include "mozilla/glean/DomWorkersMetrics.h"
     53 #include "mozilla/ipc/BackgroundChild.h"
     54 #include "nsContentSecurityUtils.h"
     55 #include "nsContentUtils.h"
     56 #include "nsCycleCollector.h"
     57 #include "nsDOMJSUtils.h"
     58 #include "nsGlobalWindowInner.h"
     59 #include "nsIContentSecurityPolicy.h"
     60 #include "nsIObserverService.h"
     61 #include "nsIScriptContext.h"
     62 #include "nsIStreamTransportService.h"
     63 #include "nsISupportsImpl.h"
     64 #include "nsISupportsPriority.h"
     65 #include "nsITimer.h"
     66 #include "nsIURI.h"
     67 #include "nsIXULRuntime.h"
     68 #include "nsLayoutStatics.h"
     69 #include "nsNetUtil.h"
     70 #include "nsPIDOMWindow.h"
     71 #include "nsServiceManagerUtils.h"
     72 #include "nsThreadUtils.h"
     73 #include "nsXPCOM.h"
     74 #include "nsXPCOMPrivate.h"
     75 #include "xpcpublic.h"
     76 
     77 #if defined(XP_MACOSX)
     78 #  include "nsMacUtilsImpl.h"
     79 #endif
     80 
     81 #include "WorkerDebuggerManager.h"
     82 #include "WorkerError.h"
     83 #include "WorkerLoadInfo.h"
     84 #include "WorkerRunnable.h"
     85 #include "WorkerScope.h"
     86 #include "WorkerThread.h"
     87 #include "prsystem.h"
     88 
     89 #ifdef DEBUG
     90 #  include "nsICookieJarSettings.h"
     91 #endif
     92 
     93 #define WORKERS_SHUTDOWN_TOPIC "web-workers-shutdown"
     94 
     95 static mozilla::LazyLogModule gWorkerShutdownDumpLog("WorkerShutdownDump");
     96 
     97 #ifdef SHUTDOWN_LOG
     98 #  undef SHUTDOWN_LOG
     99 #endif
    100 #define SHUTDOWN_LOG(msg) MOZ_LOG(gWorkerShutdownDumpLog, LogLevel::Debug, msg);
    101 
    102 namespace mozilla {
    103 
    104 using namespace ipc;
    105 
    106 namespace dom {
    107 
    108 using namespace workerinternals;
    109 
    110 namespace workerinternals {
    111 
    112 // The size of the worker runtime heaps in bytes. May be changed via pref.
    113 #define WORKER_DEFAULT_RUNTIME_HEAPSIZE 32 * 1024 * 1024
    114 
    115 // The size of the worker JS allocation threshold in MB. May be changed via
    116 // pref.
    117 #define WORKER_DEFAULT_ALLOCATION_THRESHOLD 30
    118 
    119 // Half the size of the actual C stack, to be safe.
    120 #define WORKER_CONTEXT_NATIVE_STACK_LIMIT 128 * sizeof(size_t) * 1024
    121 
    122 // The maximum number of threads to use for workers, overridable via pref.
    123 #define MAX_WORKERS_PER_DOMAIN 512
    124 
    125 static_assert(MAX_WORKERS_PER_DOMAIN >= 1,
    126              "We should allow at least one worker per domain.");
    127 
    128 #define PREF_WORKERS_PREFIX "dom.workers."
    129 #define PREF_WORKERS_MAX_PER_DOMAIN PREF_WORKERS_PREFIX "maxPerDomain"
    130 
    131 #define GC_REQUEST_OBSERVER_TOPIC "child-gc-request"
    132 #define CC_REQUEST_OBSERVER_TOPIC "child-cc-request"
    133 #define MEMORY_PRESSURE_OBSERVER_TOPIC "memory-pressure"
    134 #define LOW_MEMORY_DATA "low-memory"
    135 #define LOW_MEMORY_ONGOING_DATA "low-memory-ongoing"
    136 #define MEMORY_PRESSURE_STOP_OBSERVER_TOPIC "memory-pressure-stop"
    137 
    138 // Prefixes for observing preference changes.
    139 #define PREF_JS_OPTIONS_PREFIX "javascript.options."
    140 #define PREF_MEM_OPTIONS_PREFIX "mem."
    141 #define PREF_GCZEAL_OPTIONS_PREFIX "mem.gc_zeal."
    142 #define PREF_MODE "mode"
    143 #define PREF_FREQUENCY "frequency"
    144 
    145 static NS_DEFINE_CID(kStreamTransportServiceCID, NS_STREAMTRANSPORTSERVICE_CID);
    146 
    147 namespace {
    148 
    149 const uint32_t kNoIndex = uint32_t(-1);
    150 
    151 uint32_t gMaxWorkersPerDomain = MAX_WORKERS_PER_DOMAIN;
    152 
    153 // Does not hold an owning reference.
    154 Atomic<RuntimeService*> gRuntimeService(nullptr);
    155 
    156 // Only true during the call to Init.
    157 bool gRuntimeServiceDuringInit = false;
    158 
    159 class LiteralRebindingCString : public nsDependentCString {
    160 public:
    161  template <int N>
    162  void RebindLiteral(const char (&aStr)[N]) {
    163    Rebind(aStr, N - 1);
    164  }
    165 };
    166 
    167 template <typename T>
    168 struct PrefTraits;
    169 
    170 template <>
    171 struct PrefTraits<bool> {
    172  using PrefValueType = bool;
    173 
    174  static inline PrefValueType Get(const char* aPref) {
    175    AssertIsOnMainThread();
    176    return Preferences::GetBool(aPref);
    177  }
    178 
    179  static inline bool Exists(const char* aPref) {
    180    AssertIsOnMainThread();
    181    return Preferences::GetType(aPref) == nsIPrefBranch::PREF_BOOL;
    182  }
    183 };
    184 
    185 template <>
    186 struct PrefTraits<int32_t> {
    187  using PrefValueType = int32_t;
    188 
    189  static inline PrefValueType Get(const char* aPref) {
    190    AssertIsOnMainThread();
    191    return Preferences::GetInt(aPref);
    192  }
    193 
    194  static inline bool Exists(const char* aPref) {
    195    AssertIsOnMainThread();
    196    return Preferences::GetType(aPref) == nsIPrefBranch::PREF_INT;
    197  }
    198 };
    199 
    200 template <typename T>
    201 T GetPref(const char* aFullPref, const T aDefault, bool* aPresent = nullptr) {
    202  AssertIsOnMainThread();
    203 
    204  using PrefHelper = PrefTraits<T>;
    205 
    206  T result;
    207  bool present = true;
    208 
    209  if (PrefHelper::Exists(aFullPref)) {
    210    result = PrefHelper::Get(aFullPref);
    211  } else {
    212    result = aDefault;
    213    present = false;
    214  }
    215 
    216  if (aPresent) {
    217    *aPresent = present;
    218  }
    219  return result;
    220 }
    221 
    222 void LoadContextOptions(const char* aPrefName, void* /* aClosure */) {
    223  AssertIsOnMainThread();
    224 
    225  RuntimeService* rts = RuntimeService::GetService();
    226  if (!rts) {
    227    // May be shutting down, just bail.
    228    return;
    229  }
    230 
    231  const nsDependentCString prefName(aPrefName);
    232 
    233  // Several other pref branches will get included here so bail out if there is
    234  // another callback that will handle this change.
    235  if (StringBeginsWith(
    236          prefName,
    237          nsLiteralCString(PREF_JS_OPTIONS_PREFIX PREF_MEM_OPTIONS_PREFIX))) {
    238    return;
    239  }
    240 
    241  JS::ContextOptions contextOptions;
    242  xpc::SetPrefableContextOptions(contextOptions);
    243 
    244  nsCOMPtr<nsIXULRuntime> xr = do_GetService("@mozilla.org/xre/runtime;1");
    245  if (xr) {
    246    bool safeMode = false;
    247    xr->GetInSafeMode(&safeMode);
    248    if (safeMode) {
    249      contextOptions.disableOptionsForSafeMode();
    250    }
    251  }
    252 
    253  RuntimeService::SetDefaultContextOptions(contextOptions);
    254 
    255  if (rts) {
    256    rts->UpdateAllWorkerContextOptions();
    257  }
    258 }
    259 
    260 #ifdef JS_GC_ZEAL
    261 void LoadGCZealOptions(const char* /* aPrefName */, void* /* aClosure */) {
    262  AssertIsOnMainThread();
    263 
    264  RuntimeService* rts = RuntimeService::GetService();
    265  if (!rts) {
    266    // May be shutting down, just bail.
    267    return;
    268  }
    269 
    270  int32_t mode = GetPref<int32_t>(
    271      PREF_JS_OPTIONS_PREFIX PREF_GCZEAL_OPTIONS_PREFIX PREF_MODE, -1);
    272  if (mode < 0) {
    273    mode = 0;
    274  }
    275 
    276  int32_t frequency = GetPref<int32_t>(
    277      PREF_JS_OPTIONS_PREFIX PREF_GCZEAL_OPTIONS_PREFIX PREF_FREQUENCY, -1);
    278  if (frequency < 0) {
    279    frequency = JS::BrowserDefaultGCZealFrequency;
    280  }
    281 
    282  RuntimeService::SetDefaultGCZeal(uint8_t(mode), uint32_t(frequency));
    283 
    284  if (rts) {
    285    rts->UpdateAllWorkerGCZeal();
    286  }
    287 }
    288 #endif
    289 
    290 void UpdateCommonJSGCMemoryOption(RuntimeService* aRuntimeService,
    291                                  const char* aPrefName, JSGCParamKey aKey) {
    292  AssertIsOnMainThread();
    293  NS_ASSERTION(aPrefName, "Null pref name!");
    294 
    295  int32_t prefValue = GetPref(aPrefName, -1);
    296  Maybe<uint32_t> value = (prefValue < 0 || prefValue >= 10000)
    297                              ? Nothing()
    298                              : Some(uint32_t(prefValue));
    299 
    300  RuntimeService::SetDefaultJSGCSettings(aKey, value);
    301 
    302  if (aRuntimeService) {
    303    aRuntimeService->UpdateAllWorkerMemoryParameter(aKey, value);
    304  }
    305 }
    306 
    307 void UpdateOtherJSGCMemoryOption(RuntimeService* aRuntimeService,
    308                                 JSGCParamKey aKey, Maybe<uint32_t> aValue) {
    309  AssertIsOnMainThread();
    310 
    311  RuntimeService::SetDefaultJSGCSettings(aKey, aValue);
    312 
    313  if (aRuntimeService) {
    314    aRuntimeService->UpdateAllWorkerMemoryParameter(aKey, aValue);
    315  }
    316 }
    317 
    318 void LoadJSGCMemoryOptions(const char* aPrefName, void* /* aClosure */) {
    319  AssertIsOnMainThread();
    320 
    321  RuntimeService* rts = RuntimeService::GetService();
    322 
    323  if (!rts) {
    324    // May be shutting down, just bail.
    325    return;
    326  }
    327 
    328  constexpr auto memPrefix =
    329      nsLiteralCString{PREF_JS_OPTIONS_PREFIX PREF_MEM_OPTIONS_PREFIX};
    330  const nsDependentCString fullPrefName(aPrefName);
    331 
    332  // Pull out the string that actually distinguishes the parameter we need to
    333  // change.
    334  nsDependentCSubstring memPrefName;
    335  if (StringBeginsWith(fullPrefName, memPrefix)) {
    336    memPrefName.Rebind(fullPrefName, memPrefix.Length());
    337  } else {
    338    NS_ERROR("Unknown pref name!");
    339    return;
    340  }
    341 
    342  struct WorkerGCPref {
    343    nsLiteralCString memName;
    344    const char* fullName;
    345    JSGCParamKey key;
    346  };
    347 
    348 #define PREF(suffix_, key_)                                          \
    349  {                                                                  \
    350    nsLiteralCString(suffix_),                                       \
    351        PREF_JS_OPTIONS_PREFIX PREF_MEM_OPTIONS_PREFIX suffix_, key_ \
    352  }
    353  constexpr WorkerGCPref kWorkerPrefs[] = {
    354      PREF("max", JSGC_MAX_BYTES),
    355      PREF("gc_high_frequency_time_limit_ms", JSGC_HIGH_FREQUENCY_TIME_LIMIT),
    356      PREF("gc_low_frequency_heap_growth", JSGC_LOW_FREQUENCY_HEAP_GROWTH),
    357      PREF("gc_high_frequency_large_heap_growth",
    358           JSGC_HIGH_FREQUENCY_LARGE_HEAP_GROWTH),
    359      PREF("gc_high_frequency_small_heap_growth",
    360           JSGC_HIGH_FREQUENCY_SMALL_HEAP_GROWTH),
    361      PREF("gc_small_heap_size_max_mb", JSGC_SMALL_HEAP_SIZE_MAX),
    362      PREF("gc_large_heap_size_min_mb", JSGC_LARGE_HEAP_SIZE_MIN),
    363      PREF("gc_balanced_heap_limits", JSGC_BALANCED_HEAP_LIMITS_ENABLED),
    364      PREF("gc_heap_growth_factor", JSGC_HEAP_GROWTH_FACTOR),
    365      PREF("gc_allocation_threshold_mb", JSGC_ALLOCATION_THRESHOLD),
    366      PREF("gc_malloc_threshold_base_mb", JSGC_MALLOC_THRESHOLD_BASE),
    367      PREF("gc_small_heap_incremental_limit",
    368           JSGC_SMALL_HEAP_INCREMENTAL_LIMIT),
    369      PREF("gc_large_heap_incremental_limit",
    370           JSGC_LARGE_HEAP_INCREMENTAL_LIMIT),
    371      PREF("gc_urgent_threshold_mb", JSGC_URGENT_THRESHOLD_MB),
    372      PREF("gc_incremental_slice_ms", JSGC_SLICE_TIME_BUDGET_MS),
    373      PREF("gc_min_empty_chunk_count", JSGC_MIN_EMPTY_CHUNK_COUNT),
    374      PREF("gc_compacting", JSGC_COMPACTING_ENABLED),
    375      PREF("gc_parallel_marking", JSGC_PARALLEL_MARKING_ENABLED),
    376      PREF("gc_parallel_marking_threshold_mb",
    377           JSGC_PARALLEL_MARKING_THRESHOLD_MB),
    378      PREF("gc_max_parallel_marking_threads", JSGC_MAX_MARKING_THREADS),
    379 #ifdef NIGHTLY_BUILD
    380      PREF("gc_experimental_semispace_nursery", JSGC_SEMISPACE_NURSERY_ENABLED),
    381 #endif
    382      PREF("nursery_max_time_goal_ms", JSGC_NURSERY_MAX_TIME_GOAL_MS),
    383      // Note: Workers do not currently trigger eager minor GC, but if that is
    384      // desired the following parameters should be added:
    385      // javascript.options.mem.nursery_eager_collection_threshold_kb
    386      // javascript.options.mem.nursery_eager_collection_threshold_percent
    387      // javascript.options.mem.nursery_eager_collection_timeout_ms
    388  };
    389 #undef PREF
    390 
    391  auto pref = kWorkerPrefs;
    392  auto end = kWorkerPrefs + std::size(kWorkerPrefs);
    393 
    394  if (gRuntimeServiceDuringInit) {
    395    // During init, we want to update every pref in kWorkerPrefs.
    396    MOZ_ASSERT(memPrefName.IsEmpty(),
    397               "Pref branch prefix only expected during init");
    398  } else {
    399    // Otherwise, find the single pref that changed.
    400    while (pref != end) {
    401      if (pref->memName == memPrefName) {
    402        end = pref + 1;
    403        break;
    404      }
    405      ++pref;
    406    }
    407 #ifdef DEBUG
    408    if (pref == end) {
    409      nsAutoCString message("Workers don't support the '");
    410      message.Append(memPrefName);
    411      message.AppendLiteral("' preference!");
    412      NS_WARNING(message.get());
    413    }
    414 #endif
    415  }
    416 
    417  while (pref != end) {
    418    switch (pref->key) {
    419      case JSGC_MAX_BYTES: {
    420        int32_t prefValue = GetPref(pref->fullName, -1);
    421        Maybe<uint32_t> value = (prefValue <= 0 || prefValue >= 0x1000)
    422                                    ? Nothing()
    423                                    : Some(uint32_t(prefValue) * 1024 * 1024);
    424        UpdateOtherJSGCMemoryOption(rts, pref->key, value);
    425        break;
    426      }
    427      case JSGC_SLICE_TIME_BUDGET_MS: {
    428        int32_t prefValue = GetPref(pref->fullName, -1);
    429        Maybe<uint32_t> value = (prefValue <= 0 || prefValue >= 100000)
    430                                    ? Nothing()
    431                                    : Some(uint32_t(prefValue));
    432        UpdateOtherJSGCMemoryOption(rts, pref->key, value);
    433        break;
    434      }
    435      case JSGC_COMPACTING_ENABLED:
    436      case JSGC_PARALLEL_MARKING_ENABLED:
    437 #ifdef NIGHTLY_BUILD
    438      case JSGC_SEMISPACE_NURSERY_ENABLED:
    439 #endif
    440      case JSGC_BALANCED_HEAP_LIMITS_ENABLED: {
    441        bool present;
    442        bool prefValue = GetPref(pref->fullName, false, &present);
    443        Maybe<uint32_t> value = present ? Some(prefValue ? 1 : 0) : Nothing();
    444        UpdateOtherJSGCMemoryOption(rts, pref->key, value);
    445        break;
    446      }
    447      case JSGC_HIGH_FREQUENCY_TIME_LIMIT:
    448      case JSGC_LOW_FREQUENCY_HEAP_GROWTH:
    449      case JSGC_HIGH_FREQUENCY_LARGE_HEAP_GROWTH:
    450      case JSGC_HIGH_FREQUENCY_SMALL_HEAP_GROWTH:
    451      case JSGC_SMALL_HEAP_SIZE_MAX:
    452      case JSGC_LARGE_HEAP_SIZE_MIN:
    453      case JSGC_ALLOCATION_THRESHOLD:
    454      case JSGC_MALLOC_THRESHOLD_BASE:
    455      case JSGC_SMALL_HEAP_INCREMENTAL_LIMIT:
    456      case JSGC_LARGE_HEAP_INCREMENTAL_LIMIT:
    457      case JSGC_URGENT_THRESHOLD_MB:
    458      case JSGC_MIN_EMPTY_CHUNK_COUNT:
    459      case JSGC_HEAP_GROWTH_FACTOR:
    460      case JSGC_PARALLEL_MARKING_THRESHOLD_MB:
    461      case JSGC_MAX_MARKING_THREADS:
    462      case JSGC_NURSERY_MAX_TIME_GOAL_MS:
    463        UpdateCommonJSGCMemoryOption(rts, pref->fullName, pref->key);
    464        break;
    465      default:
    466        MOZ_ASSERT_UNREACHABLE("Unknown JSGCParamKey value");
    467        break;
    468    }
    469    ++pref;
    470  }
    471 }
    472 
    473 MOZ_CAN_RUN_SCRIPT bool InterruptCallback(JSContext* aCx) {
    474  WorkerPrivate* worker = GetWorkerPrivateFromContext(aCx);
    475  MOZ_ASSERT(worker);
    476 
    477  // Now is a good time to turn on profiling if it's pending.
    478  PROFILER_JS_INTERRUPT_CALLBACK();
    479 
    480  return MOZ_KnownLive(worker)->InterruptCallback(aCx);
    481 }
    482 
    483 class LogViolationDetailsRunnable final : public WorkerMainThreadRunnable {
    484  uint16_t mViolationType;
    485  nsCString mFileName;
    486  uint32_t mLineNum;
    487  uint32_t mColumnNum;
    488  nsString mScriptSample;
    489 
    490 public:
    491  LogViolationDetailsRunnable(WorkerPrivate* aWorker, uint16_t aViolationType,
    492                              const nsCString& aFileName, uint32_t aLineNum,
    493                              uint32_t aColumnNum,
    494                              const nsAString& aScriptSample)
    495      : WorkerMainThreadRunnable(aWorker,
    496                                 "RuntimeService :: LogViolationDetails"_ns),
    497        mViolationType(aViolationType),
    498        mFileName(aFileName),
    499        mLineNum(aLineNum),
    500        mColumnNum(aColumnNum),
    501        mScriptSample(aScriptSample) {
    502    MOZ_ASSERT(aWorker);
    503  }
    504 
    505  virtual bool MainThreadRun() override;
    506 
    507 private:
    508  ~LogViolationDetailsRunnable() = default;
    509 };
    510 
    511 MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION bool ContentSecurityPolicyAllows(
    512    JSContext* aCx, JS::RuntimeCode aKind, JS::Handle<JSString*> aCodeString,
    513    JS::CompilationType aCompilationType,
    514    JS::Handle<JS::StackGCVector<JSString*>> aParameterStrings,
    515    JS::Handle<JSString*> aBodyString,
    516    JS::Handle<JS::StackGCVector<JS::Value>> aParameterArgs,
    517    JS::Handle<JS::Value> aBodyArg, bool* aOutCanCompileStrings) {
    518  WorkerPrivate* worker = GetWorkerPrivateFromContext(aCx);
    519  worker->AssertIsOnWorkerThread();
    520 
    521  // Allow eval by default without a CSP.
    522  bool evalOK = true;
    523  bool reportViolation = false;
    524  uint16_t violationType;
    525  nsAutoJSString scriptSample;
    526  if (aKind == JS::RuntimeCode::JS) {
    527    ErrorResult error;
    528    // FIXME(Bug 1990732): Need to pass a principal here to skip TT enforcement
    529    // when this code is run from a WebExtension content script.
    530    bool areArgumentsTrusted = TrustedTypeUtils::
    531        AreArgumentsTrustedForEnsureCSPDoesNotBlockStringCompilation(
    532            aCx, aCodeString, aCompilationType, aParameterStrings, aBodyString,
    533            aParameterArgs, aBodyArg, nullptr, error);
    534    if (error.MaybeSetPendingException(aCx)) {
    535      return false;
    536    }
    537    if (!areArgumentsTrusted) {
    538      *aOutCanCompileStrings = false;
    539      return true;
    540    }
    541 
    542    if (NS_WARN_IF(!scriptSample.init(aCx, aCodeString))) {
    543      return false;
    544    }
    545 
    546    if (!nsContentSecurityUtils::IsEvalAllowed(
    547            aCx, worker->UsesSystemPrincipal(), scriptSample)) {
    548      *aOutCanCompileStrings = false;
    549      return true;
    550    }
    551 
    552    if (WorkerCSPContext* ctx = worker->GetCSPContext()) {
    553      evalOK = ctx->IsEvalAllowed(reportViolation);
    554    }
    555    violationType = nsIContentSecurityPolicy::VIOLATION_TYPE_EVAL;
    556  } else {
    557    if (WorkerCSPContext* ctx = worker->GetCSPContext()) {
    558      evalOK = ctx->IsWasmEvalAllowed(reportViolation);
    559    }
    560 
    561    // As for nsScriptSecurityManager::ContentSecurityPolicyPermitsJSAction,
    562    // for MV2 extensions we have to allow wasm by default and report violations
    563    // for historical reasons.
    564    // TODO bug 1770909: remove this exception.
    565    auto* principal = BasePrincipal::Cast(worker->GetPrincipal());
    566    RefPtr<extensions::WebExtensionPolicyCore> policy =
    567        principal ? principal->AddonPolicyCore() : nullptr;
    568    if (!evalOK && policy && policy->ManifestVersion() == 2) {
    569      evalOK = true;
    570      reportViolation = true;
    571    }
    572 
    573    violationType = nsIContentSecurityPolicy::VIOLATION_TYPE_WASM_EVAL;
    574  }
    575 
    576  if (reportViolation) {
    577    auto caller = JSCallingLocation::Get(aCx);
    578    RefPtr<LogViolationDetailsRunnable> runnable =
    579        new LogViolationDetailsRunnable(worker, violationType,
    580                                        caller.FileName(), caller.mLine,
    581                                        caller.mColumn, scriptSample);
    582 
    583    ErrorResult rv;
    584    runnable->Dispatch(worker, Killing, rv);
    585    if (NS_WARN_IF(rv.Failed())) {
    586      rv.SuppressException();
    587    }
    588  }
    589 
    590  *aOutCanCompileStrings = evalOK;
    591  return true;
    592 }
    593 
    594 void CTypesActivityCallback(JSContext* aCx, JS::CTypesActivityType aType) {
    595  WorkerPrivate* worker = GetWorkerPrivateFromContext(aCx);
    596  worker->AssertIsOnWorkerThread();
    597 
    598  switch (aType) {
    599    case JS::CTypesActivityType::BeginCall:
    600      worker->BeginCTypesCall();
    601      break;
    602 
    603    case JS::CTypesActivityType::EndCall:
    604      worker->EndCTypesCall();
    605      break;
    606 
    607    case JS::CTypesActivityType::BeginCallback:
    608      worker->BeginCTypesCallback();
    609      break;
    610 
    611    case JS::CTypesActivityType::EndCallback:
    612      worker->EndCTypesCallback();
    613      break;
    614 
    615    default:
    616      MOZ_CRASH("Unknown type flag!");
    617  }
    618 }
    619 
    620 // JSDispatchableRunnables are WorkerRunnables used to dispatch JS::Dispatchable
    621 // back to their worker thread. A WorkerRunnable is used for two reasons:
    622 //
    623 // 1. The JS::Dispatchable::run() callback may run JS so we cannot use a control
    624 // runnable since they use async interrupts and break JS run-to-completion.
    625 //
    626 // 2. The DispatchToEventLoopCallback interface is *required* to fail during
    627 // shutdown (see jsapi.h) which is exactly what WorkerRunnable::Dispatch() will
    628 // do. Moreover, JS_DestroyContext() does *not* block on JS::Dispatchable::run
    629 // being called, DispatchToEventLoopCallback failure is expected to happen
    630 // during shutdown.
    631 class JSDispatchableRunnable final : public WorkerThreadRunnable {
    632  js::UniquePtr<JS::Dispatchable> mDispatchable;
    633 
    634  ~JSDispatchableRunnable() { MOZ_ASSERT(!mDispatchable); }
    635 
    636  // Disable the usual pre/post-dispatch thread assertions since we are
    637  // dispatching from some random JS engine internal thread:
    638 
    639  bool PreDispatch(WorkerPrivate* aWorkerPrivate) override { return true; }
    640 
    641  void PostDispatch(WorkerPrivate* aWorkerPrivate,
    642                    bool aDispatchResult) override {
    643    if (!aDispatchResult) {
    644      // It is possible (for example in WASM failed compilation) that a
    645      // worker will not run, and in this case we need to
    646      // release the task as a failed task for deletion by the JS runtime.
    647      JS::Dispatchable::ReleaseFailedTask(std::move(mDispatchable));
    648    }
    649  }
    650 
    651 public:
    652  JSDispatchableRunnable(WorkerPrivate* aWorkerPrivate,
    653                         js::UniquePtr<JS::Dispatchable>&& aDispatchable)
    654      : WorkerThreadRunnable("JSDispatchableRunnable"),
    655        mDispatchable(std::move(aDispatchable)) {
    656    MOZ_ASSERT(mDispatchable);
    657  }
    658 
    659  bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
    660    MOZ_ASSERT(aCx == aWorkerPrivate->GetJSContext());
    661    MOZ_ASSERT(mDispatchable);
    662 
    663    AutoJSAPI jsapi;
    664    jsapi.Init();
    665 
    666    JS::Dispatchable::Run(aWorkerPrivate->GetJSContext(),
    667                          std::move(mDispatchable),
    668                          JS::Dispatchable::NotShuttingDown);
    669    // mDispatchable is no longer valid after this point.
    670    // The delete has been handled on the JS engine side
    671 
    672    return true;
    673  }
    674 
    675  nsresult Cancel() override {
    676    MOZ_ASSERT(mDispatchable);
    677 
    678    AutoJSAPI jsapi;
    679    jsapi.Init();
    680 
    681    // TODO: Make this make more sense
    682    // Why are we calling Run here? Because the way the API was designed
    683    // is so that once control is passed to the runnable, then both cancellation
    684    // and running are handled through `Run` by either passing NotShuttingDown
    685    // or ShuttingDown (for cancellation).
    686    JS::Dispatchable::Run(GetCurrentThreadWorkerPrivate()->GetJSContext(),
    687                          std::move(mDispatchable),
    688                          JS::Dispatchable::ShuttingDown);
    689    // mDispatchable is no longer valid after this point.
    690    // The delete has been handled on the JS engine side
    691 
    692    return NS_OK;
    693  }
    694 };
    695 
    696 static bool DispatchToEventLoop(
    697    void* aClosure, js::UniquePtr<JS::Dispatchable>&& aDispatchable) {
    698  // This callback may execute either on the worker thread or a random
    699  // JS-internal helper thread.
    700 
    701  // See comment at JS::InitDispatchToEventLoop() below for how we know the
    702  // WorkerPrivate is alive.
    703  WorkerPrivate* workerPrivate = reinterpret_cast<WorkerPrivate*>(aClosure);
    704 
    705  // Dispatch is expected to fail during shutdown for the reasons outlined in
    706  // the JSDispatchableRunnable comment above.
    707  RefPtr<JSDispatchableRunnable> r =
    708      new JSDispatchableRunnable(workerPrivate, std::move(aDispatchable));
    709  return r->Dispatch(workerPrivate);
    710 }
    711 
    712 static bool DelayedDispatchToEventLoop(
    713    void* aClosure, js::UniquePtr<JS::Dispatchable>&& aDispatchable,
    714    uint32_t delay) {
    715  // See comment at JS::InitDispatchsToEventLoop() below for how we know the
    716  // WorkerPrivate is alive.
    717  WorkerPrivate* workerPrivate = reinterpret_cast<WorkerPrivate*>(aClosure);
    718 
    719  workerPrivate->AssertIsOnWorkerThread();
    720 
    721  JSContext* cx = workerPrivate->GetJSContext();
    722  TimeoutHandler* handler =
    723      new DelayedJSDispatchableHandler(cx, std::move(aDispatchable));
    724  workerPrivate->SetTimeout(cx, handler, delay, /* aIsInterval */ false,
    725                            Timeout::Reason::eJSTimeout, IgnoreErrors());
    726 
    727  return true;
    728 }
    729 
    730 static void AsyncTaskStarted(void* aClosure, JS::Dispatchable* aDispatchable) {
    731  // See comment at JS::InitDispatchsToEventLoop() below for how we know the
    732  // WorkerPrivate is alive.
    733  WorkerPrivate* workerPrivate = reinterpret_cast<WorkerPrivate*>(aClosure);
    734  workerPrivate->AssertIsOnWorkerThread();
    735  workerPrivate->JSAsyncTaskStarted(aDispatchable);
    736 }
    737 
    738 static void AsyncTaskFinished(void* aClosure, JS::Dispatchable* aDispatchable) {
    739  // See comment at JS::InitDispatchsToEventLoop() below for how we know the
    740  // WorkerPrivate is alive.
    741  WorkerPrivate* workerPrivate = reinterpret_cast<WorkerPrivate*>(aClosure);
    742  workerPrivate->AssertIsOnWorkerThread();
    743  workerPrivate->JSAsyncTaskFinished(aDispatchable);
    744 }
    745 
    746 static bool ConsumeStream(JSContext* aCx, JS::Handle<JSObject*> aObj,
    747                          JS::MimeType aMimeType,
    748                          JS::StreamConsumer* aConsumer) {
    749  WorkerPrivate* worker = GetWorkerPrivateFromContext(aCx);
    750  if (!worker) {
    751    JS_ReportErrorNumberASCII(aCx, js::GetErrorMessage, nullptr,
    752                              JSMSG_WASM_ERROR_CONSUMING_RESPONSE);
    753    return false;
    754  }
    755 
    756  return FetchUtil::StreamResponseToJS(aCx, aObj, aMimeType, aConsumer, worker);
    757 }
    758 
    759 bool InitJSContextForWorker(WorkerPrivate* aWorkerPrivate,
    760                            JSContext* aWorkerCx) {
    761  aWorkerPrivate->AssertIsOnWorkerThread();
    762  NS_ASSERTION(!aWorkerPrivate->GetJSContext(), "Already has a context!");
    763 
    764  JSSettings settings;
    765  aWorkerPrivate->CopyJSSettings(settings);
    766 
    767  JS::ContextOptionsRef(aWorkerCx) = settings.contextOptions;
    768 
    769  // This is the real place where we set the max memory for the runtime.
    770  for (const auto& setting : settings.gcSettings) {
    771    if (setting.value) {
    772      JS_SetGCParameter(aWorkerCx, setting.key, *setting.value);
    773    } else {
    774      JS_ResetGCParameter(aWorkerCx, setting.key);
    775    }
    776  }
    777 
    778  JS_SetNativeStackQuota(aWorkerCx, WORKER_CONTEXT_NATIVE_STACK_LIMIT);
    779 
    780  // Security policy:
    781  static const JSSecurityCallbacks securityCallbacks = {
    782      ContentSecurityPolicyAllows, TrustedTypeUtils::HostGetCodeForEval};
    783  JS_SetSecurityCallbacks(aWorkerCx, &securityCallbacks);
    784 
    785  // A WorkerPrivate lives strictly longer than its JSRuntime so we can safely
    786  // store a raw pointer as the callback's closure argument on the JSRuntime.
    787  JS::InitAsyncTaskCallbacks(aWorkerCx, DispatchToEventLoop,
    788                             DelayedDispatchToEventLoop, AsyncTaskStarted,
    789                             AsyncTaskFinished, (void*)aWorkerPrivate);
    790 
    791  JS::InitConsumeStreamCallback(aWorkerCx, ConsumeStream,
    792                                FetchUtil::ReportJSStreamError);
    793 
    794  // When available, set the self-hosted shared memory to be read, so that we
    795  // can decode the self-hosted content instead of parsing it.
    796  auto& shm = xpc::SelfHostedShmem::GetSingleton();
    797  JS::SelfHostedCache selfHostedContent = shm.Content();
    798 
    799  if (!JS::InitSelfHostedCode(aWorkerCx, selfHostedContent)) {
    800    NS_WARNING("Could not init self-hosted code!");
    801    return false;
    802  }
    803 
    804  JS_AddInterruptCallback(aWorkerCx, InterruptCallback);
    805 
    806  JS::SetCTypesActivityCallback(aWorkerCx, CTypesActivityCallback);
    807 
    808 #ifdef JS_GC_ZEAL
    809  JS::SetGCZeal(aWorkerCx, settings.gcZeal, settings.gcZealFrequency);
    810 #endif
    811 
    812  return true;
    813 }
    814 
    815 static bool PreserveWrapper(JSContext* cx, JS::Handle<JSObject*> obj) {
    816  MOZ_ASSERT(cx);
    817  MOZ_ASSERT(obj);
    818  MOZ_ASSERT(mozilla::dom::IsDOMObject(obj));
    819 
    820  return mozilla::dom::TryPreserveWrapper(obj);
    821 }
    822 
    823 static bool IsWorkerDebuggerGlobalOrSandbox(JS::Handle<JSObject*> aGlobal) {
    824  return IsWorkerDebuggerGlobal(aGlobal) || IsWorkerDebuggerSandbox(aGlobal);
    825 }
    826 
    827 JSObject* Wrap(JSContext* cx, JS::Handle<JSObject*> existing,
    828               JS::Handle<JSObject*> obj) {
    829  JS::Rooted<JSObject*> targetGlobal(cx, JS::CurrentGlobalOrNull(cx));
    830 
    831  // Note: the JS engine unwraps CCWs before calling this callback.
    832  JS::Rooted<JSObject*> originGlobal(cx, JS::GetNonCCWObjectGlobal(obj));
    833 
    834  const js::Wrapper* wrapper = nullptr;
    835  if (IsWorkerDebuggerGlobalOrSandbox(targetGlobal) &&
    836      IsWorkerDebuggerGlobalOrSandbox(originGlobal)) {
    837    wrapper = &js::CrossCompartmentWrapper::singleton;
    838  } else {
    839    wrapper = &js::OpaqueCrossCompartmentWrapper::singleton;
    840  }
    841 
    842  if (existing) {
    843    js::Wrapper::Renew(existing, obj, wrapper);
    844  }
    845  return js::Wrapper::New(cx, obj, wrapper);
    846 }
    847 
    848 static const JSWrapObjectCallbacks WrapObjectCallbacks = {
    849    Wrap,
    850    nullptr,
    851 };
    852 
    853 class WorkerJSRuntime final : public mozilla::CycleCollectedJSRuntime {
    854 public:
    855  // The heap size passed here doesn't matter, we will change it later in the
    856  // call to JS_SetGCParameter inside InitJSContextForWorker.
    857  explicit WorkerJSRuntime(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
    858      : CycleCollectedJSRuntime(aCx), mWorkerPrivate(aWorkerPrivate) {
    859    MOZ_COUNT_CTOR_INHERITED(WorkerJSRuntime, CycleCollectedJSRuntime);
    860    MOZ_ASSERT(aWorkerPrivate);
    861 
    862    {
    863      JS::UniqueChars defaultLocale = aWorkerPrivate->AdoptDefaultLocale();
    864      MOZ_ASSERT(defaultLocale,
    865                 "failure of a WorkerPrivate to have a default locale should "
    866                 "have made the worker fail to spawn");
    867 
    868      if (!JS_SetDefaultLocale(Runtime(), defaultLocale.get())) {
    869        NS_WARNING("failed to set workerCx's default locale");
    870      }
    871    }
    872  }
    873 
    874  void Shutdown(JSContext* cx) override {
    875    // The CC is shut down, and the superclass destructor will GC, so make sure
    876    // we don't try to CC again.
    877    mWorkerPrivate = nullptr;
    878 
    879    CycleCollectedJSRuntime::Shutdown(cx);
    880  }
    881 
    882  ~WorkerJSRuntime() {
    883    MOZ_COUNT_DTOR_INHERITED(WorkerJSRuntime, CycleCollectedJSRuntime);
    884  }
    885 
    886  virtual void PrepareForForgetSkippable() override {}
    887 
    888  virtual void BeginCycleCollectionCallback(
    889      mozilla::CCReason aReason) override {}
    890 
    891  virtual void EndCycleCollectionCallback(
    892      CycleCollectorResults& aResults) override {}
    893 
    894  void DispatchDeferredDeletion(bool aContinuation, bool aPurge) override {
    895    MOZ_ASSERT(!aContinuation);
    896 
    897    // Do it immediately, no need for asynchronous behavior here.
    898    nsCycleCollector_doDeferredDeletion();
    899  }
    900 
    901  virtual void CustomGCCallback(JSGCStatus aStatus) override {
    902    if (!mWorkerPrivate) {
    903      // We're shutting down, no need to do anything.
    904      return;
    905    }
    906 
    907    mWorkerPrivate->AssertIsOnWorkerThread();
    908 
    909    if (aStatus == JSGC_END) {
    910      bool collectedAnything =
    911          nsCycleCollector_collect(CCReason::GC_FINISHED, nullptr);
    912      mWorkerPrivate->SetCCCollectedAnything(collectedAnything);
    913    }
    914  }
    915 
    916  void TraceAdditionalNativeBlackRoots(JSTracer* aTracer) override {
    917    if (!mWorkerPrivate || !mWorkerPrivate->MayContinueRunning()) {
    918      return;
    919    }
    920 
    921    if (WorkerGlobalScope* scope = mWorkerPrivate->GlobalScope()) {
    922      if (EventListenerManager* elm = scope->GetExistingListenerManager()) {
    923        elm->TraceListeners(aTracer);
    924      }
    925    }
    926 
    927    if (WorkerDebuggerGlobalScope* debuggerScope =
    928            mWorkerPrivate->DebuggerGlobalScope()) {
    929      if (EventListenerManager* elm =
    930              debuggerScope->GetExistingListenerManager()) {
    931        elm->TraceListeners(aTracer);
    932      }
    933    }
    934  };
    935 
    936 private:
    937  WorkerPrivate* mWorkerPrivate;
    938 };
    939 
    940 }  // anonymous namespace
    941 
    942 }  // namespace workerinternals
    943 
    944 class WorkerJSContext final : public mozilla::CycleCollectedJSContext {
    945 public:
    946  // The heap size passed here doesn't matter, we will change it later in the
    947  // call to JS_SetGCParameter inside InitJSContextForWorker.
    948  explicit WorkerJSContext(WorkerPrivate* aWorkerPrivate)
    949      : mWorkerPrivate(aWorkerPrivate) {
    950    MOZ_COUNT_CTOR_INHERITED(WorkerJSContext, CycleCollectedJSContext);
    951    MOZ_ASSERT(aWorkerPrivate);
    952    // Magical number 2. Workers have the base recursion depth 1, and normal
    953    // runnables run at level 2, and we don't want to process microtasks
    954    // at any other level.
    955    SetTargetedMicroTaskRecursionDepth(2);
    956  }
    957 
    958  // MOZ_CAN_RUN_SCRIPT_BOUNDARY because otherwise we have to annotate the
    959  // SpiderMonkey JS::JobQueue's destructor as MOZ_CAN_RUN_SCRIPT, which is a
    960  // bit of a pain.
    961  MOZ_CAN_RUN_SCRIPT_BOUNDARY ~WorkerJSContext() {
    962    MOZ_COUNT_DTOR_INHERITED(WorkerJSContext, CycleCollectedJSContext);
    963    JSContext* cx = MaybeContext();
    964    if (!cx) {
    965      return;  // Initialize() must have failed
    966    }
    967 
    968    // We expect to come here with the cycle collector already shut down.
    969    // The superclass destructor will run the GC one final time and finalize any
    970    // JSObjects that were participating in cycles that were broken during CC
    971    // shutdown.
    972    // Make sure we don't try to CC again.
    973    mWorkerPrivate = nullptr;
    974  }
    975 
    976  WorkerJSContext* GetAsWorkerJSContext() override { return this; }
    977 
    978  CycleCollectedJSRuntime* CreateRuntime(JSContext* aCx) override {
    979    return new WorkerJSRuntime(aCx, mWorkerPrivate);
    980  }
    981 
    982  nsresult Initialize(JSRuntime* aParentRuntime) {
    983    nsresult rv = CycleCollectedJSContext::Initialize(
    984        aParentRuntime, WORKER_DEFAULT_RUNTIME_HEAPSIZE);
    985    if (NS_WARN_IF(NS_FAILED(rv))) {
    986      return rv;
    987    }
    988 
    989    JSContext* cx = Context();
    990 
    991    js::SetPreserveWrapperCallbacks(cx, PreserveWrapper, HasReleasedWrapper);
    992    JS_InitDestroyPrincipalsCallback(cx, nsJSPrincipals::Destroy);
    993    JS_InitReadPrincipalsCallback(cx, nsJSPrincipals::ReadPrincipals);
    994    JS_SetWrapObjectCallbacks(cx, &WrapObjectCallbacks);
    995    if (mWorkerPrivate->IsDedicatedWorker()) {
    996      JS_SetFutexCanWait(cx);
    997    }
    998 
    999    return NS_OK;
   1000  }
   1001 
   1002  virtual bool useDebugQueue(JS::Handle<JSObject*> global) const override {
   1003    MOZ_ASSERT(!NS_IsMainThread());
   1004 
   1005    return !(IsWorkerGlobal(global) || IsShadowRealmGlobal(global));
   1006  }
   1007 
   1008  virtual void DispatchToMicroTask(
   1009      already_AddRefed<MicroTaskRunnable> aRunnable) override {
   1010    RefPtr<MicroTaskRunnable> runnable(aRunnable);
   1011 
   1012    MOZ_ASSERT(!NS_IsMainThread());
   1013    MOZ_ASSERT(runnable);
   1014 
   1015    JSContext* cx = Context();
   1016    NS_ASSERTION(cx, "This should never be null!");
   1017 
   1018    JS::Rooted<JSObject*> global(cx, JS::CurrentGlobalOrNull(cx));
   1019    NS_ASSERTION(global, "This should never be null!");
   1020 
   1021    JS::JobQueueMayNotBeEmpty(cx);
   1022    if (StaticPrefs::javascript_options_use_js_microtask_queue()) {
   1023      PROFILER_MARKER_FLOW_ONLY("WorkerJSContext::DispatchToMicroTask", OTHER,
   1024                                {}, FlowMarker,
   1025                                Flow::FromPointer(runnable.get()));
   1026 
   1027      // On worker threads, if the current global is the worker global or
   1028      // ShadowRealm global, we use the main micro task queue. Otherwise, the
   1029      // current global must be either the debugger global or a debugger
   1030      // sandbox, and we use the debugger micro task queue instead.
   1031      if (IsWorkerGlobal(global) || IsShadowRealmGlobal(global)) {
   1032        if (!EnqueueMicroTask(cx, runnable.forget())) {
   1033          // This should never fail, but if it does, we have no choice but to
   1034          // crash. This is always an OOM.
   1035          NS_ABORT_OOM(0);
   1036        }
   1037      } else {
   1038        MOZ_ASSERT(IsWorkerDebuggerGlobal(global) ||
   1039                   IsWorkerDebuggerSandbox(global));
   1040        if (!EnqueueDebugMicroTask(cx, runnable.forget())) {
   1041          // This should never fail, but if it does, we have no choice but to
   1042          // crash. This is always an OOM.
   1043          NS_ABORT_OOM(0);
   1044        }
   1045      }
   1046    } else {
   1047      std::deque<RefPtr<MicroTaskRunnable>>* microTaskQueue = nullptr;
   1048      // On worker threads, if the current global is the worker global or
   1049      // ShadowRealm global, we use the main micro task queue. Otherwise, the
   1050      // current global must be either the debugger global or a debugger
   1051      // sandbox, and we use the debugger micro task queue instead.
   1052      if (IsWorkerGlobal(global) || IsShadowRealmGlobal(global)) {
   1053        microTaskQueue = &GetMicroTaskQueue();
   1054      } else {
   1055        MOZ_ASSERT(IsWorkerDebuggerGlobal(global) ||
   1056                   IsWorkerDebuggerSandbox(global));
   1057 
   1058        microTaskQueue = &GetDebuggerMicroTaskQueue();
   1059      }
   1060 
   1061      if (!runnable->isInList()) {
   1062        // A recycled object may be in the list already.
   1063        mMicrotasksToTrace.insertBack(runnable);
   1064      }
   1065      PROFILER_MARKER_FLOW_ONLY("WorkerJSContext::DispatchToMicroTask", OTHER,
   1066                                {}, FlowMarker,
   1067                                Flow::FromPointer(runnable.get()));
   1068      microTaskQueue->push_back(std::move(runnable));
   1069    }
   1070  }
   1071 
   1072  bool IsSystemCaller() const override {
   1073    return mWorkerPrivate->UsesSystemPrincipal();
   1074  }
   1075 
   1076  void ReportError(JSErrorReport* aReport,
   1077                   JS::ConstUTF8CharsZ aToStringResult) override {
   1078    mWorkerPrivate->ReportError(Context(), aToStringResult, aReport);
   1079  }
   1080 
   1081  WorkerPrivate* GetWorkerPrivate() const { return mWorkerPrivate; }
   1082 
   1083 private:
   1084  WorkerPrivate* mWorkerPrivate;
   1085 };
   1086 
   1087 namespace workerinternals {
   1088 
   1089 namespace {
   1090 
   1091 class WorkerThreadPrimaryRunnable final : public Runnable {
   1092  WorkerPrivate* mWorkerPrivate;
   1093  SafeRefPtr<WorkerThread> mThread;
   1094  JSRuntime* mParentRuntime;
   1095 
   1096  class FinishedRunnable final : public Runnable {
   1097    SafeRefPtr<WorkerThread> mThread;
   1098 
   1099   public:
   1100    explicit FinishedRunnable(SafeRefPtr<WorkerThread> aThread)
   1101        : Runnable("WorkerThreadPrimaryRunnable::FinishedRunnable"),
   1102          mThread(std::move(aThread)) {
   1103      MOZ_ASSERT(mThread);
   1104    }
   1105 
   1106    NS_INLINE_DECL_REFCOUNTING_INHERITED(FinishedRunnable, Runnable)
   1107 
   1108   private:
   1109    ~FinishedRunnable() = default;
   1110 
   1111    NS_DECL_NSIRUNNABLE
   1112  };
   1113 
   1114 public:
   1115  WorkerThreadPrimaryRunnable(WorkerPrivate* aWorkerPrivate,
   1116                              SafeRefPtr<WorkerThread> aThread,
   1117                              JSRuntime* aParentRuntime)
   1118      : mozilla::Runnable("WorkerThreadPrimaryRunnable"),
   1119        mWorkerPrivate(aWorkerPrivate),
   1120        mThread(std::move(aThread)),
   1121        mParentRuntime(aParentRuntime) {
   1122    MOZ_ASSERT(aWorkerPrivate);
   1123    MOZ_ASSERT(mThread);
   1124  }
   1125 
   1126  NS_INLINE_DECL_REFCOUNTING_INHERITED(WorkerThreadPrimaryRunnable, Runnable)
   1127 
   1128 private:
   1129  ~WorkerThreadPrimaryRunnable() = default;
   1130 
   1131  NS_DECL_NSIRUNNABLE
   1132 };
   1133 
   1134 void PrefLanguagesChanged(const char* /* aPrefName */, void* /* aClosure */) {
   1135  AssertIsOnMainThread();
   1136 
   1137  nsTArray<nsString> languages;
   1138  Navigator::GetAcceptLanguages(languages, nullptr);
   1139 
   1140  RuntimeService* runtime = RuntimeService::GetService();
   1141  if (runtime) {
   1142    runtime->UpdateAllWorkerLanguages(languages);
   1143  }
   1144 }
   1145 
   1146 void AppVersionOverrideChanged(const char* /* aPrefName */,
   1147                               void* /* aClosure */) {
   1148  AssertIsOnMainThread();
   1149 
   1150  nsAutoString override;
   1151  Preferences::GetString("general.appversion.override", override);
   1152 
   1153  RuntimeService* runtime = RuntimeService::GetService();
   1154  if (runtime) {
   1155    runtime->UpdateAppVersionOverridePreference(override);
   1156  }
   1157 }
   1158 
   1159 void PlatformOverrideChanged(const char* /* aPrefName */,
   1160                             void* /* aClosure */) {
   1161  AssertIsOnMainThread();
   1162 
   1163  nsAutoString override;
   1164  Preferences::GetString("general.platform.override", override);
   1165 
   1166  RuntimeService* runtime = RuntimeService::GetService();
   1167  if (runtime) {
   1168    runtime->UpdatePlatformOverridePreference(override);
   1169  }
   1170 }
   1171 
   1172 } /* anonymous namespace */
   1173 
   1174 // This is only touched on the main thread. Initialized in Init() below.
   1175 StaticAutoPtr<JSSettings> RuntimeService::sDefaultJSSettings;
   1176 
   1177 RuntimeService::RuntimeService()
   1178    : mMutex("RuntimeService::mMutex"),
   1179      mObserved(false),
   1180      mShuttingDown(false),
   1181      mNavigatorPropertiesLoaded(false) {
   1182  AssertIsOnMainThread();
   1183  MOZ_ASSERT(!GetService(), "More than one service!");
   1184 }
   1185 
   1186 RuntimeService::~RuntimeService() {
   1187  AssertIsOnMainThread();
   1188 
   1189  // gRuntimeService can be null if Init() fails.
   1190  MOZ_ASSERT(!GetService() || GetService() == this, "More than one service!");
   1191 
   1192  gRuntimeService = nullptr;
   1193 }
   1194 
   1195 // static
   1196 RuntimeService* RuntimeService::GetOrCreateService() {
   1197  AssertIsOnMainThread();
   1198 
   1199  if (!gRuntimeService) {
   1200    // The observer service now owns us until shutdown.
   1201    gRuntimeService = new RuntimeService();
   1202    if (NS_FAILED((*gRuntimeService).Init())) {
   1203      NS_WARNING("Failed to initialize!");
   1204      (*gRuntimeService).Cleanup();
   1205      gRuntimeService = nullptr;
   1206      return nullptr;
   1207    }
   1208  }
   1209 
   1210  return gRuntimeService;
   1211 }
   1212 
   1213 // static
   1214 RuntimeService* RuntimeService::GetService() { return gRuntimeService; }
   1215 
   1216 bool RuntimeService::RegisterWorker(WorkerPrivate& aWorkerPrivate) {
   1217  aWorkerPrivate.AssertIsOnParentThread();
   1218 
   1219  WorkerPrivate* parent = aWorkerPrivate.GetParent();
   1220  if (!parent) {
   1221    AssertIsOnMainThread();
   1222 
   1223    if (mShuttingDown) {
   1224      return false;
   1225    }
   1226  }
   1227 
   1228  const bool isServiceWorker = aWorkerPrivate.IsServiceWorker();
   1229  const bool isSharedWorker = aWorkerPrivate.IsSharedWorker();
   1230  const bool isDedicatedWorker = aWorkerPrivate.IsDedicatedWorker();
   1231  if (isServiceWorker) {
   1232    AssertIsOnMainThread();
   1233  }
   1234 
   1235  nsCString sharedWorkerScriptSpec;
   1236  if (isSharedWorker) {
   1237    AssertIsOnMainThread();
   1238 
   1239    nsCOMPtr<nsIURI> scriptURI = aWorkerPrivate.GetResolvedScriptURI();
   1240    NS_ASSERTION(scriptURI, "Null script URI!");
   1241 
   1242    nsresult rv = scriptURI->GetSpec(sharedWorkerScriptSpec);
   1243    if (NS_FAILED(rv)) {
   1244      NS_WARNING("GetSpec failed?!");
   1245      return false;
   1246    }
   1247 
   1248    NS_ASSERTION(!sharedWorkerScriptSpec.IsEmpty(), "Empty spec!");
   1249  }
   1250 
   1251  bool exemptFromPerDomainMax = false;
   1252  if (isServiceWorker) {
   1253    AssertIsOnMainThread();
   1254    exemptFromPerDomainMax = Preferences::GetBool(
   1255        "dom.serviceWorkers.exemptFromPerDomainMax", false);
   1256  }
   1257 
   1258  const nsCString& domain = aWorkerPrivate.Domain();
   1259 
   1260  bool queued = false;
   1261  {
   1262    MutexAutoLock lock(mMutex);
   1263 
   1264    auto* const domainInfo =
   1265        mDomainMap
   1266            .LookupOrInsertWith(
   1267                domain,
   1268                [&domain, parent] {
   1269                  NS_ASSERTION(!parent, "Shouldn't have a parent here!");
   1270                  (void)parent;  // silence clang -Wunused-lambda-capture in
   1271                                 // opt builds
   1272                  auto wdi = MakeUnique<WorkerDomainInfo>();
   1273                  wdi->mDomain = domain;
   1274                  return wdi;
   1275                })
   1276            .get();
   1277 
   1278    queued = gMaxWorkersPerDomain &&
   1279             domainInfo->ActiveWorkerCount() >= gMaxWorkersPerDomain &&
   1280             !domain.IsEmpty() && !exemptFromPerDomainMax;
   1281 
   1282    if (queued) {
   1283      domainInfo->mQueuedWorkers.AppendElement(&aWorkerPrivate);
   1284 
   1285      // Worker spawn gets queued due to hitting max workers per domain
   1286      // limit so let's log a warning.
   1287      WorkerPrivate::ReportErrorToConsole(nsIScriptError::warningFlag, "DOM"_ns,
   1288                                          nsContentUtils::eDOM_PROPERTIES,
   1289                                          "HittingMaxWorkersPerDomain2"_ns);
   1290 
   1291      if (isServiceWorker) {
   1292        glean::workers::service_worker_spawn_gets_queued.Add(1);
   1293      } else if (isSharedWorker) {
   1294        glean::workers::shared_worker_spawn_gets_queued.Add(1);
   1295      } else if (isDedicatedWorker) {
   1296        glean::workers::dedicated_worker_spawn_gets_queued.Add(1);
   1297      }
   1298    } else if (parent) {
   1299      domainInfo->mChildWorkerCount++;
   1300    } else if (isServiceWorker) {
   1301      domainInfo->mActiveServiceWorkers.AppendElement(&aWorkerPrivate);
   1302    } else {
   1303      domainInfo->mActiveWorkers.AppendElement(&aWorkerPrivate);
   1304    }
   1305  }
   1306 
   1307  aWorkerPrivate.SetIsQueued(queued);
   1308 
   1309  // From here on out we must call UnregisterWorker if something fails!
   1310  if (parent) {
   1311    if (!parent->AddChildWorker(aWorkerPrivate)) {
   1312      UnregisterWorker(aWorkerPrivate);
   1313      return false;
   1314    }
   1315  } else {
   1316    if (!mNavigatorPropertiesLoaded) {
   1317      if (NS_FAILED(Navigator::GetAppVersion(
   1318              mNavigatorProperties.mAppVersion, aWorkerPrivate.GetDocument(),
   1319              false /* aUsePrefOverriddenValue */)) ||
   1320          NS_FAILED(Navigator::GetPlatform(
   1321              mNavigatorProperties.mPlatform, aWorkerPrivate.GetDocument(),
   1322              false /* aUsePrefOverriddenValue */))) {
   1323        UnregisterWorker(aWorkerPrivate);
   1324        return false;
   1325      }
   1326 
   1327      // The navigator overridden properties should have already been read.
   1328 
   1329      Navigator::GetAcceptLanguages(mNavigatorProperties.mLanguages, nullptr);
   1330      mNavigatorPropertiesLoaded = true;
   1331    }
   1332 
   1333    nsPIDOMWindowInner* window = aWorkerPrivate.GetWindow();
   1334 
   1335    if (!isServiceWorker) {
   1336      // Service workers are excluded since their lifetime is separate from
   1337      // that of dom windows.
   1338      if (auto* const windowArray = mWindowMap.GetOrInsertNew(window, 1);
   1339          !windowArray->Contains(&aWorkerPrivate)) {
   1340        windowArray->AppendElement(&aWorkerPrivate);
   1341      } else {
   1342        MOZ_ASSERT(aWorkerPrivate.IsSharedWorker());
   1343      }
   1344    }
   1345  }
   1346 
   1347  if (!queued && !ScheduleWorker(aWorkerPrivate)) {
   1348    return false;
   1349  }
   1350 
   1351  if (isServiceWorker) {
   1352    AssertIsOnMainThread();
   1353  }
   1354  return true;
   1355 }
   1356 
   1357 void RuntimeService::UnregisterWorker(WorkerPrivate& aWorkerPrivate) {
   1358  aWorkerPrivate.AssertIsOnParentThread();
   1359 
   1360  WorkerPrivate* parent = aWorkerPrivate.GetParent();
   1361  if (!parent) {
   1362    AssertIsOnMainThread();
   1363  }
   1364 
   1365  const nsCString& domain = aWorkerPrivate.Domain();
   1366 
   1367  WorkerPrivate* queuedWorker = nullptr;
   1368  {
   1369    MutexAutoLock lock(mMutex);
   1370 
   1371    WorkerDomainInfo* domainInfo;
   1372    if (!mDomainMap.Get(domain, &domainInfo)) {
   1373      NS_ERROR("Don't have an entry for this domain!");
   1374    }
   1375 
   1376    // Remove old worker from everywhere.
   1377    uint32_t index = domainInfo->mQueuedWorkers.IndexOf(&aWorkerPrivate);
   1378    if (index != kNoIndex) {
   1379      // Was queued, remove from the list.
   1380      domainInfo->mQueuedWorkers.RemoveElementAt(index);
   1381    } else if (parent) {
   1382      MOZ_ASSERT(domainInfo->mChildWorkerCount, "Must be non-zero!");
   1383      domainInfo->mChildWorkerCount--;
   1384    } else if (aWorkerPrivate.IsServiceWorker()) {
   1385      MOZ_ASSERT(domainInfo->mActiveServiceWorkers.Contains(&aWorkerPrivate),
   1386                 "Don't know about this worker!");
   1387      domainInfo->mActiveServiceWorkers.RemoveElement(&aWorkerPrivate);
   1388    } else {
   1389      MOZ_ASSERT(domainInfo->mActiveWorkers.Contains(&aWorkerPrivate),
   1390                 "Don't know about this worker!");
   1391      domainInfo->mActiveWorkers.RemoveElement(&aWorkerPrivate);
   1392    }
   1393 
   1394    // See if there's a queued worker we can schedule.
   1395    if (domainInfo->ActiveWorkerCount() < gMaxWorkersPerDomain &&
   1396        !domainInfo->mQueuedWorkers.IsEmpty()) {
   1397      queuedWorker = domainInfo->mQueuedWorkers[0];
   1398      domainInfo->mQueuedWorkers.RemoveElementAt(0);
   1399 
   1400      if (queuedWorker->GetParent()) {
   1401        domainInfo->mChildWorkerCount++;
   1402      } else if (queuedWorker->IsServiceWorker()) {
   1403        domainInfo->mActiveServiceWorkers.AppendElement(queuedWorker);
   1404      } else {
   1405        domainInfo->mActiveWorkers.AppendElement(queuedWorker);
   1406      }
   1407    }
   1408 
   1409    if (domainInfo->HasNoWorkers()) {
   1410      MOZ_ASSERT(domainInfo->mQueuedWorkers.IsEmpty());
   1411      mDomainMap.Remove(domain);
   1412    }
   1413  }
   1414 
   1415  // NB: For Shared Workers we used to call ShutdownOnMainThread on the
   1416  // RemoteWorkerController; however, that was redundant because
   1417  // RemoteWorkerChild uses a WeakWorkerRef which notifies at about the
   1418  // same time as us calling into the code here and would race with us.
   1419 
   1420  if (parent) {
   1421    parent->RemoveChildWorker(aWorkerPrivate);
   1422  } else if (aWorkerPrivate.IsSharedWorker()) {
   1423    AssertIsOnMainThread();
   1424 
   1425    mWindowMap.RemoveIf([&aWorkerPrivate](const auto& iter) {
   1426      const auto& workers = iter.Data();
   1427      MOZ_ASSERT(workers);
   1428 
   1429      if (workers->RemoveElement(&aWorkerPrivate)) {
   1430        MOZ_ASSERT(!workers->Contains(&aWorkerPrivate),
   1431                   "Added worker more than once!");
   1432 
   1433        return workers->IsEmpty();
   1434      }
   1435 
   1436      return false;
   1437    });
   1438  } else if (aWorkerPrivate.IsDedicatedWorker()) {
   1439    // May be null.
   1440    nsPIDOMWindowInner* window = aWorkerPrivate.GetWindow();
   1441    if (auto entry = mWindowMap.Lookup(window)) {
   1442      MOZ_ALWAYS_TRUE(entry.Data()->RemoveElement(&aWorkerPrivate));
   1443      if (entry.Data()->IsEmpty()) {
   1444        entry.Remove();
   1445      }
   1446    } else {
   1447      MOZ_ASSERT_UNREACHABLE("window is not in mWindowMap");
   1448    }
   1449  }
   1450 
   1451  if (queuedWorker && !ScheduleWorker(*queuedWorker)) {
   1452    UnregisterWorker(*queuedWorker);
   1453  }
   1454 }
   1455 
   1456 bool RuntimeService::ScheduleWorker(WorkerPrivate& aWorkerPrivate) {
   1457  if (!aWorkerPrivate.Start()) {
   1458    // This is ok, means that we didn't need to make a thread for this worker.
   1459    return true;
   1460  }
   1461 
   1462  const WorkerThreadFriendKey friendKey;
   1463 
   1464  SafeRefPtr<WorkerThread> thread = WorkerThread::Create(friendKey);
   1465  if (!thread) {
   1466    UnregisterWorker(aWorkerPrivate);
   1467    return false;
   1468  }
   1469 
   1470  if (NS_FAILED(thread->SetPriority(nsISupportsPriority::PRIORITY_NORMAL))) {
   1471    NS_WARNING("Could not set the thread's priority!");
   1472  }
   1473 
   1474  aWorkerPrivate.SetThread(thread.unsafeGetRawPtr());
   1475  JSContext* cx = CycleCollectedJSContext::Get()->Context();
   1476 
   1477  nsCOMPtr<nsIRunnable> runnable = new WorkerThreadPrimaryRunnable(
   1478      &aWorkerPrivate, thread.clonePtr(), JS_GetParentRuntime(cx));
   1479  if (NS_FAILED(
   1480          thread->DispatchPrimaryRunnable(friendKey, runnable.forget()))) {
   1481    UnregisterWorker(aWorkerPrivate);
   1482    return false;
   1483  }
   1484 
   1485  // The worker was queued when creating, so enable remote debugger now.
   1486  if (aWorkerPrivate.IsQueued()) {
   1487    aWorkerPrivate.SetIsQueued(false);
   1488    aWorkerPrivate.EnableRemoteDebugger();
   1489  }
   1490 
   1491  return true;
   1492 }
   1493 
   1494 nsresult RuntimeService::Init() {
   1495  AssertIsOnMainThread();
   1496 
   1497  nsLayoutStatics::AddRef();
   1498 
   1499  // Initialize JSSettings.
   1500  sDefaultJSSettings = new JSSettings();
   1501  SetDefaultJSGCSettings(JSGC_MAX_BYTES, Some(WORKER_DEFAULT_RUNTIME_HEAPSIZE));
   1502  SetDefaultJSGCSettings(JSGC_ALLOCATION_THRESHOLD,
   1503                         Some(WORKER_DEFAULT_ALLOCATION_THRESHOLD));
   1504 
   1505  // nsIStreamTransportService is thread-safe but it must be initialized on the
   1506  // main-thread. FileReader needs it, so, let's initialize it now.
   1507  nsresult rv;
   1508  nsCOMPtr<nsIStreamTransportService> sts =
   1509      do_GetService(kStreamTransportServiceCID, &rv);
   1510  NS_ENSURE_TRUE(sts, NS_ERROR_FAILURE);
   1511 
   1512  nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
   1513  NS_ENSURE_TRUE(obs, NS_ERROR_FAILURE);
   1514 
   1515  rv = obs->AddObserver(this, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID, false);
   1516  NS_ENSURE_SUCCESS(rv, rv);
   1517 
   1518  rv = obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
   1519  NS_ENSURE_SUCCESS(rv, rv);
   1520 
   1521  mObserved = true;
   1522 
   1523  if (NS_FAILED(obs->AddObserver(this, GC_REQUEST_OBSERVER_TOPIC, false))) {
   1524    NS_WARNING("Failed to register for GC request notifications!");
   1525  }
   1526 
   1527  if (NS_FAILED(obs->AddObserver(this, CC_REQUEST_OBSERVER_TOPIC, false))) {
   1528    NS_WARNING("Failed to register for CC request notifications!");
   1529  }
   1530 
   1531  if (NS_FAILED(
   1532          obs->AddObserver(this, MEMORY_PRESSURE_OBSERVER_TOPIC, false))) {
   1533    NS_WARNING("Failed to register for memory pressure notifications!");
   1534  }
   1535 
   1536  if (NS_FAILED(
   1537          obs->AddObserver(this, NS_IOSERVICE_OFFLINE_STATUS_TOPIC, false))) {
   1538    NS_WARNING("Failed to register for offline notification event!");
   1539  }
   1540 
   1541  MOZ_ASSERT(!gRuntimeServiceDuringInit, "This should be false!");
   1542  gRuntimeServiceDuringInit = true;
   1543 
   1544 #define WORKER_PREF(name, callback) \
   1545  NS_FAILED(Preferences::RegisterCallbackAndCall(callback, name))
   1546 
   1547  if (NS_FAILED(Preferences::RegisterPrefixCallbackAndCall(
   1548          LoadJSGCMemoryOptions,
   1549          PREF_JS_OPTIONS_PREFIX PREF_MEM_OPTIONS_PREFIX)) ||
   1550 #ifdef JS_GC_ZEAL
   1551      NS_FAILED(Preferences::RegisterCallback(
   1552          LoadGCZealOptions,
   1553          PREF_JS_OPTIONS_PREFIX PREF_GCZEAL_OPTIONS_PREFIX)) ||
   1554 #endif
   1555      WORKER_PREF("intl.accept_languages", PrefLanguagesChanged) ||
   1556      WORKER_PREF("general.appversion.override", AppVersionOverrideChanged) ||
   1557      WORKER_PREF("general.platform.override", PlatformOverrideChanged) ||
   1558      NS_FAILED(Preferences::RegisterPrefixCallbackAndCall(
   1559          LoadContextOptions, PREF_JS_OPTIONS_PREFIX))) {
   1560    NS_WARNING("Failed to register pref callbacks!");
   1561  }
   1562 
   1563 #undef WORKER_PREF
   1564 
   1565  MOZ_ASSERT(gRuntimeServiceDuringInit, "Should be true!");
   1566  gRuntimeServiceDuringInit = false;
   1567 
   1568  int32_t maxPerDomain =
   1569      Preferences::GetInt(PREF_WORKERS_MAX_PER_DOMAIN, MAX_WORKERS_PER_DOMAIN);
   1570  gMaxWorkersPerDomain = std::max(0, maxPerDomain);
   1571 
   1572  IndexedDatabaseManager* idm = IndexedDatabaseManager::GetOrCreate();
   1573  if (NS_WARN_IF(!idm)) {
   1574    return NS_ERROR_UNEXPECTED;
   1575  }
   1576 
   1577  rv = idm->EnsureLocale();
   1578  if (NS_WARN_IF(NS_FAILED(rv))) {
   1579    return rv;
   1580  }
   1581 
   1582  // PerformanceService must be initialized on the main-thread.
   1583  PerformanceService::GetOrCreate();
   1584 
   1585  return NS_OK;
   1586 }
   1587 
   1588 void RuntimeService::Shutdown() {
   1589  AssertIsOnMainThread();
   1590 
   1591  MOZ_ASSERT(!mShuttingDown);
   1592  // That's it, no more workers.
   1593  mShuttingDown = true;
   1594 
   1595  nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
   1596  NS_WARNING_ASSERTION(obs, "Failed to get observer service?!");
   1597 
   1598  // Tell anyone that cares that they're about to lose worker support.
   1599  if (obs && NS_FAILED(obs->NotifyObservers(nullptr, WORKERS_SHUTDOWN_TOPIC,
   1600                                            nullptr))) {
   1601    NS_WARNING("NotifyObservers failed!");
   1602  }
   1603 
   1604  {
   1605    AutoTArray<WorkerPrivate*, 100> workers;
   1606 
   1607    {
   1608      MutexAutoLock lock(mMutex);
   1609 
   1610      AddAllTopLevelWorkersToArray(workers);
   1611    }
   1612 
   1613    // Cancel all top-level workers.
   1614    for (const auto& worker : workers) {
   1615      if (!worker->Cancel()) {
   1616        NS_WARNING("Failed to cancel worker!");
   1617      }
   1618    }
   1619  }
   1620 
   1621  sDefaultJSSettings = nullptr;
   1622 }
   1623 
   1624 namespace {
   1625 
   1626 class DumpCrashInfoRunnable final : public WorkerControlRunnable {
   1627 public:
   1628  explicit DumpCrashInfoRunnable(WorkerPrivate* aWorkerPrivate)
   1629      : WorkerControlRunnable("DumpCrashInfoRunnable"),
   1630        mMonitor("DumpCrashInfoRunnable::mMonitor"),
   1631        mWorkerPrivate(aWorkerPrivate) {}
   1632 
   1633  bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
   1634    MonitorAutoLock lock(mMonitor);
   1635    if (!mHasMsg) {
   1636      aWorkerPrivate->DumpCrashInformation(mMsg);
   1637      mHasMsg.Flip();
   1638    }
   1639    lock.Notify();
   1640    return true;
   1641  }
   1642 
   1643  nsresult Cancel() override {
   1644    MonitorAutoLock lock(mMonitor);
   1645    if (!mHasMsg) {
   1646      mMsg.Assign("Canceled");
   1647      mHasMsg.Flip();
   1648    }
   1649    lock.Notify();
   1650 
   1651    return NS_OK;
   1652  }
   1653 
   1654  bool DispatchAndWait() {
   1655    MonitorAutoLock lock(mMonitor);
   1656 
   1657    if (!Dispatch(mWorkerPrivate)) {
   1658      // The worker is already dead but the main thread still didn't remove it
   1659      // from RuntimeService's registry.
   1660      return false;
   1661    }
   1662 
   1663    // To avoid any possibility of process hangs we never receive reports on
   1664    // we give the worker 1sec to react.
   1665    lock.Wait(TimeDuration::FromMilliseconds(1000));
   1666    if (!mHasMsg) {
   1667      mMsg.Append("NoResponse");
   1668      mHasMsg.Flip();
   1669    }
   1670    return true;
   1671  }
   1672 
   1673  const nsCString& MsgData() const { return mMsg; }
   1674 
   1675 private:
   1676  bool PreDispatch(WorkerPrivate* aWorkerPrivate) override { return true; }
   1677 
   1678  void PostDispatch(WorkerPrivate* aWorkerPrivate,
   1679                    bool aDispatchResult) override {}
   1680 
   1681  Monitor mMonitor MOZ_UNANNOTATED;
   1682  nsCString mMsg;
   1683  FlippedOnce<false> mHasMsg;
   1684  WorkerPrivate* mWorkerPrivate;
   1685 };
   1686 
   1687 struct ActiveWorkerStats {
   1688  template <uint32_t ActiveWorkerStats::* Category>
   1689  void Update(const nsTArray<WorkerPrivate*>& aWorkers) {
   1690    for (const auto worker : aWorkers) {
   1691      RefPtr<DumpCrashInfoRunnable> runnable =
   1692          new DumpCrashInfoRunnable(worker);
   1693      if (runnable->DispatchAndWait()) {
   1694        ++(this->*Category);
   1695        mMessage.Append(runnable->MsgData());
   1696      }
   1697    }
   1698  }
   1699 
   1700  uint32_t mWorkers = 0;
   1701  uint32_t mServiceWorkers = 0;
   1702  nsCString mMessage;
   1703 };
   1704 
   1705 }  // namespace
   1706 
   1707 void RuntimeService::CrashIfHanging() {
   1708  MutexAutoLock lock(mMutex);
   1709 
   1710  // If we never wanted to shut down we cannot hang.
   1711  if (!mShuttingDown) {
   1712    return;
   1713  }
   1714 
   1715  ActiveWorkerStats activeStats;
   1716  uint32_t inactiveWorkers = 0;
   1717 
   1718  for (const auto& aData : mDomainMap.Values()) {
   1719    activeStats.Update<&ActiveWorkerStats::mWorkers>(aData->mActiveWorkers);
   1720    activeStats.Update<&ActiveWorkerStats::mServiceWorkers>(
   1721        aData->mActiveServiceWorkers);
   1722 
   1723    // These might not be top-level workers...
   1724    inactiveWorkers += std::count_if(
   1725        aData->mQueuedWorkers.begin(), aData->mQueuedWorkers.end(),
   1726        [](const auto* const worker) { return !worker->GetParent(); });
   1727  }
   1728 
   1729  if (activeStats.mWorkers + activeStats.mServiceWorkers + inactiveWorkers ==
   1730      0) {
   1731    return;
   1732  }
   1733 
   1734  nsCString msg;
   1735 
   1736  // A: active Workers | S: active ServiceWorkers | Q: queued Workers
   1737  msg.AppendPrintf("Workers Hanging - %d|A:%d|S:%d|Q:%d", mShuttingDown ? 1 : 0,
   1738                   activeStats.mWorkers, activeStats.mServiceWorkers,
   1739                   inactiveWorkers);
   1740  msg.Append(activeStats.mMessage);
   1741 
   1742  // This string will be leaked.
   1743  MOZ_CRASH_UNSAFE(strdup(msg.BeginReading()));
   1744 }
   1745 
   1746 // This spins the event loop until all workers are finished and their threads
   1747 // have been joined.
   1748 void RuntimeService::Cleanup() {
   1749  AssertIsOnMainThread();
   1750 
   1751  if (!mShuttingDown) {
   1752    Shutdown();
   1753  }
   1754 
   1755  nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
   1756  NS_WARNING_ASSERTION(obs, "Failed to get observer service?!");
   1757 
   1758  {
   1759    MutexAutoLock lock(mMutex);
   1760 
   1761    AutoTArray<WorkerPrivate*, 100> workers;
   1762    AddAllTopLevelWorkersToArray(workers);
   1763 
   1764    if (!workers.IsEmpty()) {
   1765      nsIThread* currentThread = NS_GetCurrentThread();
   1766      NS_ASSERTION(currentThread, "This should never be null!");
   1767 
   1768      // If the loop below takes too long, we probably have a problematic
   1769      // worker.  MOZ_LOG some info before the parent process forcibly
   1770      // terminates us so that in the event we are a content process, the log
   1771      // output can provide useful context about the workers that did not
   1772      // cleanly shut down.
   1773      nsCOMPtr<nsITimer> timer;
   1774      RefPtr<RuntimeService> self = this;
   1775      nsresult rv = NS_NewTimerWithCallback(
   1776          getter_AddRefs(timer),
   1777          [self](nsITimer*) { self->DumpRunningWorkers(); },
   1778          TimeDuration::FromSeconds(1), nsITimer::TYPE_ONE_SHOT,
   1779          "RuntimeService::WorkerShutdownDump"_ns);
   1780      (void)NS_WARN_IF(NS_FAILED(rv));
   1781 
   1782      // And make sure all their final messages have run and all their threads
   1783      // have joined.
   1784      while (mDomainMap.Count()) {
   1785        MutexAutoUnlock unlock(mMutex);
   1786 
   1787        if (!NS_ProcessNextEvent(currentThread)) {
   1788          NS_WARNING("Something bad happened!");
   1789          break;
   1790        }
   1791      }
   1792 
   1793      if (NS_SUCCEEDED(rv)) {
   1794        timer->Cancel();
   1795      }
   1796    }
   1797  }
   1798 
   1799  NS_ASSERTION(!mWindowMap.Count(), "All windows should have been released!");
   1800 
   1801 #define WORKER_PREF(name, callback) \
   1802  NS_FAILED(Preferences::UnregisterCallback(callback, name))
   1803 
   1804  if (mObserved) {
   1805    if (NS_FAILED(Preferences::UnregisterPrefixCallback(
   1806            LoadContextOptions, PREF_JS_OPTIONS_PREFIX)) ||
   1807        WORKER_PREF("intl.accept_languages", PrefLanguagesChanged) ||
   1808        WORKER_PREF("general.appversion.override", AppVersionOverrideChanged) ||
   1809        WORKER_PREF("general.platform.override", PlatformOverrideChanged) ||
   1810 #ifdef JS_GC_ZEAL
   1811        NS_FAILED(Preferences::UnregisterCallback(
   1812            LoadGCZealOptions,
   1813            PREF_JS_OPTIONS_PREFIX PREF_GCZEAL_OPTIONS_PREFIX)) ||
   1814 #endif
   1815        NS_FAILED(Preferences::UnregisterPrefixCallback(
   1816            LoadJSGCMemoryOptions,
   1817            PREF_JS_OPTIONS_PREFIX PREF_MEM_OPTIONS_PREFIX))) {
   1818      NS_WARNING("Failed to unregister pref callbacks!");
   1819    }
   1820 
   1821 #undef WORKER_PREF
   1822 
   1823    if (obs) {
   1824      if (NS_FAILED(obs->RemoveObserver(this, GC_REQUEST_OBSERVER_TOPIC))) {
   1825        NS_WARNING("Failed to unregister for GC request notifications!");
   1826      }
   1827 
   1828      if (NS_FAILED(obs->RemoveObserver(this, CC_REQUEST_OBSERVER_TOPIC))) {
   1829        NS_WARNING("Failed to unregister for CC request notifications!");
   1830      }
   1831 
   1832      if (NS_FAILED(
   1833              obs->RemoveObserver(this, MEMORY_PRESSURE_OBSERVER_TOPIC))) {
   1834        NS_WARNING("Failed to unregister for memory pressure notifications!");
   1835      }
   1836 
   1837      if (NS_FAILED(
   1838              obs->RemoveObserver(this, NS_IOSERVICE_OFFLINE_STATUS_TOPIC))) {
   1839        NS_WARNING("Failed to unregister for offline notification event!");
   1840      }
   1841      obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID);
   1842      obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
   1843      mObserved = false;
   1844    }
   1845  }
   1846 
   1847  nsLayoutStatics::Release();
   1848 }
   1849 
   1850 void RuntimeService::AddAllTopLevelWorkersToArray(
   1851    nsTArray<WorkerPrivate*>& aWorkers) {
   1852  for (const auto& aData : mDomainMap.Values()) {
   1853 #ifdef DEBUG
   1854    for (const auto& activeWorker : aData->mActiveWorkers) {
   1855      MOZ_ASSERT(!activeWorker->GetParent(),
   1856                 "Shouldn't have a parent in this list!");
   1857    }
   1858    for (const auto& activeServiceWorker : aData->mActiveServiceWorkers) {
   1859      MOZ_ASSERT(!activeServiceWorker->GetParent(),
   1860                 "Shouldn't have a parent in this list!");
   1861    }
   1862 #endif
   1863 
   1864    aWorkers.AppendElements(aData->mActiveWorkers);
   1865    aWorkers.AppendElements(aData->mActiveServiceWorkers);
   1866 
   1867    // These might not be top-level workers...
   1868    std::copy_if(aData->mQueuedWorkers.begin(), aData->mQueuedWorkers.end(),
   1869                 MakeBackInserter(aWorkers),
   1870                 [](const auto& worker) { return !worker->GetParent(); });
   1871  }
   1872 }
   1873 
   1874 nsTArray<WorkerPrivate*> RuntimeService::GetWorkersForWindow(
   1875    const nsPIDOMWindowInner& aWindow) const {
   1876  AssertIsOnMainThread();
   1877 
   1878  nsTArray<WorkerPrivate*> result;
   1879  if (nsTArray<WorkerPrivate*>* const workers = mWindowMap.Get(&aWindow)) {
   1880    NS_ASSERTION(!workers->IsEmpty(), "Should have been removed!");
   1881    result.AppendElements(*workers);
   1882  }
   1883  return result;
   1884 }
   1885 
   1886 void RuntimeService::CancelWorkersForWindow(const nsPIDOMWindowInner& aWindow) {
   1887  AssertIsOnMainThread();
   1888 
   1889  for (WorkerPrivate* const worker : GetWorkersForWindow(aWindow)) {
   1890    MOZ_ASSERT(!worker->IsSharedWorker());
   1891    worker->Cancel();
   1892  }
   1893 }
   1894 
   1895 void RuntimeService::UpdateWorkersBackgroundState(
   1896    const nsPIDOMWindowInner& aWindow, bool aIsBackground) {
   1897  AssertIsOnMainThread();
   1898  for (WorkerPrivate* const worker : GetWorkersForWindow(aWindow)) {
   1899    MOZ_ASSERT(!worker->IsSharedWorker());
   1900    if (aIsBackground) {
   1901      worker->SetIsRunningInBackground();
   1902    } else {
   1903      worker->SetIsRunningInForeground();
   1904    }
   1905  }
   1906 }
   1907 
   1908 void RuntimeService::FreezeWorkersForWindow(const nsPIDOMWindowInner& aWindow) {
   1909  AssertIsOnMainThread();
   1910 
   1911  for (WorkerPrivate* const worker : GetWorkersForWindow(aWindow)) {
   1912    MOZ_ASSERT(!worker->IsSharedWorker());
   1913    worker->Freeze(&aWindow);
   1914  }
   1915 }
   1916 
   1917 void RuntimeService::ThawWorkersForWindow(const nsPIDOMWindowInner& aWindow) {
   1918  AssertIsOnMainThread();
   1919 
   1920  for (WorkerPrivate* const worker : GetWorkersForWindow(aWindow)) {
   1921    MOZ_ASSERT(!worker->IsSharedWorker());
   1922    worker->Thaw(&aWindow);
   1923  }
   1924 }
   1925 
   1926 void RuntimeService::SuspendWorkersForWindow(
   1927    const nsPIDOMWindowInner& aWindow) {
   1928  AssertIsOnMainThread();
   1929 
   1930  for (WorkerPrivate* const worker : GetWorkersForWindow(aWindow)) {
   1931    MOZ_ASSERT(!worker->IsSharedWorker());
   1932    worker->ParentWindowPaused();
   1933  }
   1934 }
   1935 
   1936 void RuntimeService::ResumeWorkersForWindow(const nsPIDOMWindowInner& aWindow) {
   1937  AssertIsOnMainThread();
   1938 
   1939  for (WorkerPrivate* const worker : GetWorkersForWindow(aWindow)) {
   1940    MOZ_ASSERT(!worker->IsSharedWorker());
   1941    worker->ParentWindowResumed();
   1942  }
   1943 }
   1944 
   1945 void RuntimeService::PropagateStorageAccessPermissionGranted(
   1946    const nsPIDOMWindowInner& aWindow) {
   1947  AssertIsOnMainThread();
   1948  MOZ_ASSERT_IF(aWindow.GetExtantDoc(), aWindow.GetExtantDoc()
   1949                                            ->CookieJarSettings()
   1950                                            ->GetRejectThirdPartyContexts());
   1951 
   1952  for (WorkerPrivate* const worker : GetWorkersForWindow(aWindow)) {
   1953    worker->PropagateStorageAccessPermissionGranted();
   1954  }
   1955 }
   1956 
   1957 template <typename Func>
   1958 void RuntimeService::BroadcastAllWorkers(const Func& aFunc) {
   1959  AssertIsOnMainThread();
   1960 
   1961  AutoTArray<WorkerPrivate*, 100> workers;
   1962  {
   1963    MutexAutoLock lock(mMutex);
   1964 
   1965    AddAllTopLevelWorkersToArray(workers);
   1966  }
   1967 
   1968  for (const auto& worker : workers) {
   1969    aFunc(*worker);
   1970  }
   1971 }
   1972 
   1973 void RuntimeService::UpdateAllWorkerContextOptions() {
   1974  BroadcastAllWorkers([](auto& worker) {
   1975    worker.UpdateContextOptions(sDefaultJSSettings->contextOptions);
   1976  });
   1977 }
   1978 
   1979 void RuntimeService::UpdateAppVersionOverridePreference(
   1980    const nsAString& aValue) {
   1981  AssertIsOnMainThread();
   1982  mNavigatorProperties.mAppVersionOverridden = aValue;
   1983 }
   1984 
   1985 void RuntimeService::UpdatePlatformOverridePreference(const nsAString& aValue) {
   1986  AssertIsOnMainThread();
   1987  mNavigatorProperties.mPlatformOverridden = aValue;
   1988 }
   1989 
   1990 void RuntimeService::UpdateAllWorkerLanguages(
   1991    const nsTArray<nsString>& aLanguages) {
   1992  MOZ_ASSERT(NS_IsMainThread());
   1993 
   1994  mNavigatorProperties.mLanguages = aLanguages.Clone();
   1995  BroadcastAllWorkers(
   1996      [&aLanguages](auto& worker) { worker.UpdateLanguages(aLanguages); });
   1997 }
   1998 
   1999 void RuntimeService::UpdateAllWorkerMemoryParameter(JSGCParamKey aKey,
   2000                                                    Maybe<uint32_t> aValue) {
   2001  BroadcastAllWorkers([aKey, aValue](auto& worker) {
   2002    worker.UpdateJSWorkerMemoryParameter(aKey, aValue);
   2003  });
   2004 }
   2005 
   2006 #ifdef JS_GC_ZEAL
   2007 void RuntimeService::UpdateAllWorkerGCZeal() {
   2008  BroadcastAllWorkers([](auto& worker) {
   2009    worker.UpdateGCZeal(sDefaultJSSettings->gcZeal,
   2010                        sDefaultJSSettings->gcZealFrequency);
   2011  });
   2012 }
   2013 #endif
   2014 
   2015 void RuntimeService::SetLowMemoryStateAllWorkers(bool aState) {
   2016  BroadcastAllWorkers(
   2017      [aState](auto& worker) { worker.SetLowMemoryState(aState); });
   2018 }
   2019 
   2020 void RuntimeService::GarbageCollectAllWorkers(bool aShrinking) {
   2021  BroadcastAllWorkers(
   2022      [aShrinking](auto& worker) { worker.GarbageCollect(aShrinking); });
   2023 }
   2024 
   2025 void RuntimeService::CycleCollectAllWorkers() {
   2026  BroadcastAllWorkers([](auto& worker) { worker.CycleCollect(); });
   2027 }
   2028 
   2029 void RuntimeService::SendOfflineStatusChangeEventToAllWorkers(bool aIsOffline) {
   2030  BroadcastAllWorkers([aIsOffline](auto& worker) {
   2031    worker.OfflineStatusChangeEvent(aIsOffline);
   2032  });
   2033 }
   2034 
   2035 void RuntimeService::MemoryPressureAllWorkers() {
   2036  BroadcastAllWorkers([](auto& worker) { worker.MemoryPressure(); });
   2037 }
   2038 
   2039 uint32_t RuntimeService::ClampedHardwareConcurrency(bool aRFPHardcoded,
   2040                                                    bool aRFPTiered) const {
   2041  // The Firefox Hardware Report says 34% of Firefox users have exactly 4 cores.
   2042  // When the resistFingerprinting pref is set, we want to blend into the crowd
   2043  // so spoof navigator.hardwareConcurrency = 4 to reduce user uniqueness. On
   2044  // OSX, the majority of Macs have 8 cores.
   2045  if (MOZ_UNLIKELY(aRFPHardcoded)) {
   2046 #ifdef XP_MACOSX
   2047    return 8;
   2048 #else
   2049    return 4;
   2050 #endif
   2051  }
   2052 
   2053  // This needs to be atomic, because multiple workers, and even mainthread,
   2054  // could race to initialize it at once.
   2055  static Atomic<uint32_t> unclampedHardwareConcurrency;
   2056 
   2057  // No need to loop here: if compareExchange fails, that just means that some
   2058  // other worker has initialized numberOfProcessors, so we're good to go.
   2059  if (!unclampedHardwareConcurrency) {
   2060    int32_t numberOfProcessors = 0;
   2061 #if defined(XP_MACOSX)
   2062    if (nsMacUtilsImpl::IsTCSMAvailable()) {
   2063      // On failure, zero is returned from GetPhysicalCPUCount()
   2064      // and we fallback to PR_GetNumberOfProcessors below.
   2065      numberOfProcessors = nsMacUtilsImpl::GetPhysicalCPUCount();
   2066    }
   2067 #endif
   2068    if (numberOfProcessors == 0) {
   2069      numberOfProcessors = PR_GetNumberOfProcessors();
   2070    }
   2071    if (numberOfProcessors <= 0) {
   2072      numberOfProcessors = 1;  // Must be one there somewhere
   2073    }
   2074    (void)unclampedHardwareConcurrency.compareExchange(0, numberOfProcessors);
   2075  }
   2076 
   2077  if (MOZ_UNLIKELY(aRFPTiered)) {
   2078    if (unclampedHardwareConcurrency >= 8) {
   2079      return 8;
   2080    }
   2081    return 4;
   2082  }
   2083 
   2084  return std::min(uint32_t(unclampedHardwareConcurrency),
   2085                  StaticPrefs::dom_maxHardwareConcurrency());
   2086 }
   2087 
   2088 // nsISupports
   2089 NS_IMPL_ISUPPORTS(RuntimeService, nsIObserver)
   2090 
   2091 // nsIObserver
   2092 NS_IMETHODIMP
   2093 RuntimeService::Observe(nsISupports* aSubject, const char* aTopic,
   2094                        const char16_t* aData) {
   2095  AssertIsOnMainThread();
   2096 
   2097  if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
   2098    Shutdown();
   2099    return NS_OK;
   2100  }
   2101  if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID)) {
   2102    Cleanup();
   2103    return NS_OK;
   2104  }
   2105  if (!strcmp(aTopic, GC_REQUEST_OBSERVER_TOPIC)) {
   2106    GarbageCollectAllWorkers(/* shrinking = */ false);
   2107    return NS_OK;
   2108  }
   2109  if (!strcmp(aTopic, CC_REQUEST_OBSERVER_TOPIC)) {
   2110    CycleCollectAllWorkers();
   2111    return NS_OK;
   2112  }
   2113  if (!strcmp(aTopic, MEMORY_PRESSURE_OBSERVER_TOPIC)) {
   2114    nsDependentString data(aData);
   2115    // Don't continue to GC/CC if we are in an ongoing low-memory state since
   2116    // its very slow and it likely won't help us anyway.
   2117    if (data.EqualsLiteral(LOW_MEMORY_ONGOING_DATA)) {
   2118      return NS_OK;
   2119    }
   2120    if (data.EqualsLiteral(LOW_MEMORY_DATA)) {
   2121      SetLowMemoryStateAllWorkers(true);
   2122    }
   2123    GarbageCollectAllWorkers(/* shrinking = */ true);
   2124    CycleCollectAllWorkers();
   2125    MemoryPressureAllWorkers();
   2126    return NS_OK;
   2127  }
   2128  if (!strcmp(aTopic, MEMORY_PRESSURE_STOP_OBSERVER_TOPIC)) {
   2129    SetLowMemoryStateAllWorkers(false);
   2130    return NS_OK;
   2131  }
   2132  if (!strcmp(aTopic, NS_IOSERVICE_OFFLINE_STATUS_TOPIC)) {
   2133    SendOfflineStatusChangeEventToAllWorkers(NS_IsOffline());
   2134    return NS_OK;
   2135  }
   2136 
   2137  MOZ_ASSERT_UNREACHABLE("Unknown observer topic!");
   2138  return NS_OK;
   2139 }
   2140 
   2141 namespace {
   2142 const char* WorkerKindToString(WorkerKind kind) {
   2143  switch (kind) {
   2144    case WorkerKindDedicated:
   2145      return "dedicated";
   2146    case WorkerKindShared:
   2147      return "shared";
   2148    case WorkerKindService:
   2149      return "service";
   2150    default:
   2151      NS_WARNING("Unknown worker type");
   2152      return "unknown worker type";
   2153  }
   2154 }
   2155 
   2156 void LogWorker(WorkerPrivate* worker, const char* category) {
   2157  AssertIsOnMainThread();
   2158 
   2159  SHUTDOWN_LOG(("Found %s (%s): %s", category,
   2160                WorkerKindToString(worker->Kind()),
   2161                NS_ConvertUTF16toUTF8(worker->ScriptURL()).get()));
   2162 
   2163  if (worker->Kind() == WorkerKindService) {
   2164    SHUTDOWN_LOG(("Scope: %s", worker->ServiceWorkerScope().get()));
   2165  }
   2166 
   2167  nsCString origin;
   2168  worker->GetPrincipal()->GetOrigin(origin);
   2169  SHUTDOWN_LOG(("Principal: %s", origin.get()));
   2170 
   2171  nsCString loadingOrigin;
   2172  worker->GetLoadingPrincipal()->GetOrigin(loadingOrigin);
   2173  SHUTDOWN_LOG(("LoadingPrincipal: %s", loadingOrigin.get()));
   2174 
   2175  RefPtr<DumpCrashInfoRunnable> runnable = new DumpCrashInfoRunnable(worker);
   2176  if (runnable->DispatchAndWait()) {
   2177    SHUTDOWN_LOG(("CrashInfo: %s", runnable->MsgData().get()));
   2178  } else {
   2179    SHUTDOWN_LOG(("CrashInfo: dispatch failed"));
   2180  }
   2181 }
   2182 }  // namespace
   2183 
   2184 void RuntimeService::DumpRunningWorkers() {
   2185  // Temporarily set the LogLevel high enough to be certain the messages are
   2186  // visible.
   2187  LogModule* module = gWorkerShutdownDumpLog;
   2188  LogLevel prevLevel = module->Level();
   2189 
   2190  const auto cleanup =
   2191      MakeScopeExit([module, prevLevel] { module->SetLevel(prevLevel); });
   2192 
   2193  if (prevLevel < LogLevel::Debug) {
   2194    module->SetLevel(LogLevel::Debug);
   2195  }
   2196 
   2197  MutexAutoLock lock(mMutex);
   2198 
   2199  for (const auto& info : mDomainMap.Values()) {
   2200    for (WorkerPrivate* worker : info->mActiveWorkers) {
   2201      LogWorker(worker, "ActiveWorker");
   2202    }
   2203 
   2204    for (WorkerPrivate* worker : info->mActiveServiceWorkers) {
   2205      LogWorker(worker, "ActiveServiceWorker");
   2206    }
   2207 
   2208    for (WorkerPrivate* worker : info->mQueuedWorkers) {
   2209      LogWorker(worker, "QueuedWorker");
   2210    }
   2211  }
   2212 }
   2213 
   2214 void RuntimeService::UpdateWorkersPlaybackState(
   2215    const nsPIDOMWindowInner& aWindow, bool aIsPlayingAudio) {
   2216  AssertIsOnMainThread();
   2217 
   2218  for (WorkerPrivate* const worker : GetWorkersForWindow(aWindow)) {
   2219    MOZ_ASSERT(!worker->IsSharedWorker());
   2220    worker->SetIsPlayingAudio(aIsPlayingAudio);
   2221  }
   2222 }
   2223 
   2224 void RuntimeService::UpdateWorkersPeerConnections(
   2225    const nsPIDOMWindowInner& aWindow, bool aHasPeerConnections) {
   2226  AssertIsOnMainThread();
   2227 
   2228  for (WorkerPrivate* const worker : GetWorkersForWindow(aWindow)) {
   2229    MOZ_ASSERT(!worker->IsSharedWorker());
   2230    worker->SetActivePeerConnections(aHasPeerConnections);
   2231  }
   2232 }
   2233 
   2234 bool LogViolationDetailsRunnable::MainThreadRun() {
   2235  AssertIsOnMainThread();
   2236  MOZ_ASSERT(mWorkerRef);
   2237 
   2238  nsIContentSecurityPolicy* csp = mWorkerRef->Private()->GetCsp();
   2239  if (csp) {
   2240    csp->LogViolationDetails(mViolationType,
   2241                             nullptr,  // triggering element
   2242                             mWorkerRef->Private()->CSPEventListener(),
   2243                             mFileName, mScriptSample, mLineNum, mColumnNum,
   2244                             u""_ns, u""_ns);
   2245  }
   2246 
   2247  return true;
   2248 }
   2249 
   2250 // MOZ_CAN_RUN_SCRIPT_BOUNDARY until Runnable::Run is MOZ_CAN_RUN_SCRIPT.  See
   2251 // bug 1535398.
   2252 MOZ_CAN_RUN_SCRIPT_BOUNDARY
   2253 NS_IMETHODIMP
   2254 WorkerThreadPrimaryRunnable::Run() {
   2255  NS_ConvertUTF16toUTF8 url(mWorkerPrivate->ScriptURL());
   2256  AUTO_PROFILER_LABEL_DYNAMIC_CSTR("WorkerThreadPrimaryRunnable::Run", OTHER,
   2257                                   url.get());
   2258 
   2259  using mozilla::ipc::BackgroundChild;
   2260  {
   2261    bool runLoopRan = false;
   2262    auto failureCleanup = MakeScopeExit([&]() {
   2263      // If Worker initialization fails, call WorkerPrivate::ScheduleDeletion()
   2264      // to release the WorkerPrivate in the parent thread.
   2265      mWorkerPrivate->ScheduleDeletion(WorkerPrivate::WorkerRan);
   2266    });
   2267 
   2268    mWorkerPrivate->SetWorkerPrivateInWorkerThread(mThread.unsafeGetRawPtr());
   2269 
   2270    const auto threadCleanup = MakeScopeExit([&] {
   2271      // If Worker initialization fails, such as creating a BackgroundChild or
   2272      // the worker's JSContext initialization failing, call
   2273      // WorkerPrivate::RunLoopNeverRan() to set the Worker to the correct
   2274      // status, which means "Dead," to forbid WorkerThreadRunnable dispatching.
   2275      if (!runLoopRan) {
   2276        mWorkerPrivate->RunLoopNeverRan();
   2277      }
   2278      mWorkerPrivate->ResetWorkerPrivateInWorkerThread();
   2279    });
   2280 
   2281    mWorkerPrivate->AssertIsOnWorkerThread();
   2282 
   2283    // This needs to be initialized on the worker thread before being used on
   2284    // the main thread and calling BackgroundChild::GetOrCreateForCurrentThread
   2285    // exposes it to the main thread.
   2286    mWorkerPrivate->EnsurePerformanceStorage();
   2287 
   2288    if (NS_WARN_IF(!BackgroundChild::GetOrCreateForCurrentThread())) {
   2289      return NS_ERROR_FAILURE;
   2290    }
   2291 
   2292    nsWeakPtr globalScopeSentinel;
   2293    nsWeakPtr debuggerScopeSentinel;
   2294    // Never use the following pointers without checking their corresponding
   2295    // nsWeakPtr sentinel, defined above and initialized after DoRunLoop ends.
   2296    WorkerGlobalScopeBase* globalScopeRawPtr = nullptr;
   2297    WorkerGlobalScopeBase* debuggerScopeRawPtr = nullptr;
   2298    {
   2299      nsCycleCollector_startup();
   2300 
   2301      auto context = MakeUnique<WorkerJSContext>(mWorkerPrivate);
   2302      nsresult rv = context->Initialize(mParentRuntime);
   2303      if (NS_WARN_IF(NS_FAILED(rv))) {
   2304        return rv;
   2305      }
   2306 
   2307      JSContext* cx = context->Context();
   2308 
   2309      if (!InitJSContextForWorker(mWorkerPrivate, cx)) {
   2310        return NS_ERROR_FAILURE;
   2311      }
   2312 
   2313      failureCleanup.release();
   2314 
   2315      // Binding the RemoteWorkerDebugger child endpoint after initailzation
   2316      // successfully.
   2317      // mWorkerPrivate->BindRemoteWorkerDebuggerChild();
   2318 
   2319      runLoopRan = true;
   2320 
   2321      {
   2322        PROFILER_SET_JS_CONTEXT(context.get());
   2323 
   2324        {
   2325          // We're on the worker thread here, and WorkerPrivate's refcounting is
   2326          // non-threadsafe: you can only do it on the parent thread.  What that
   2327          // means in practice is that we're relying on it being kept alive
   2328          // while we run.  Hopefully.
   2329          MOZ_KnownLive(mWorkerPrivate)->DoRunLoop(cx);
   2330          // The AutoJSAPI in DoRunLoop should have reported any exceptions left
   2331          // on cx.
   2332          MOZ_ASSERT(!JS_IsExceptionPending(cx));
   2333        }
   2334 
   2335        mWorkerPrivate->ShutdownModuleLoader();
   2336 
   2337        mWorkerPrivate->RunShutdownTasks();
   2338 
   2339        BackgroundChild::CloseForCurrentThread();
   2340 
   2341        PROFILER_CLEAR_JS_CONTEXT();
   2342      }
   2343 
   2344      // There may still be runnables on the debugger event queue that hold a
   2345      // strong reference to the debugger global scope. These runnables are not
   2346      // visible to the cycle collector, so we need to make sure to clear the
   2347      // debugger event queue before we try to destroy the context. If we don't,
   2348      // the garbage collector will crash.
   2349      // Note that this just releases the runnables and does not execute them.
   2350      mWorkerPrivate->ClearDebuggerEventQueue();
   2351 
   2352      // Before shutting down the cycle collector we need to do one more pass
   2353      // through the event loop to clean up any C++ objects that need deferred
   2354      // cleanup.
   2355      NS_ProcessPendingEvents(nullptr);
   2356 
   2357      // At this point we expect the scopes to be alive if they were ever
   2358      // created successfully, keep weak references and set up the sentinels.
   2359      globalScopeRawPtr = mWorkerPrivate->GlobalScope();
   2360      if (globalScopeRawPtr) {
   2361        globalScopeSentinel = do_GetWeakReference(globalScopeRawPtr);
   2362      }
   2363      MOZ_ASSERT(!globalScopeRawPtr || globalScopeSentinel);
   2364      debuggerScopeRawPtr = mWorkerPrivate->DebuggerGlobalScope();
   2365      if (debuggerScopeRawPtr) {
   2366        debuggerScopeSentinel = do_GetWeakReference(debuggerScopeRawPtr);
   2367      }
   2368      MOZ_ASSERT(!debuggerScopeRawPtr || debuggerScopeSentinel);
   2369 
   2370      // To our best knowledge nobody should need a reference to our globals
   2371      // now (NS_ProcessPendingEvents is the last expected potential usage)
   2372      // and we can unroot them.
   2373      mWorkerPrivate->UnrootGlobalScopes();
   2374 
   2375      // Perform a full GC until we collect the main worker global and CC,
   2376      // which should break all cycles that touch JS.
   2377      bool repeatGCCC = true;
   2378      while (repeatGCCC) {
   2379        JS::PrepareForFullGC(cx);
   2380        JS::NonIncrementalGC(cx, JS::GCOptions::Shutdown,
   2381                             JS::GCReason::WORKER_SHUTDOWN);
   2382 
   2383        // If we CCed something or got new events as a side effect, repeat.
   2384        repeatGCCC = mWorkerPrivate->isLastCCCollectedAnything() ||
   2385                     NS_HasPendingEvents(nullptr);
   2386        NS_ProcessPendingEvents(nullptr);
   2387      }
   2388 
   2389      // The worker global should be unrooted and the shutdown of cycle
   2390      // collection should break all the remaining cycles.
   2391      nsCycleCollector_shutdown();
   2392 
   2393      // If ever the CC shutdown run caused side effects, process them.
   2394      NS_ProcessPendingEvents(nullptr);
   2395 
   2396      // Now WorkerJSContext goes out of scope. Do not use any cycle
   2397      // collectable objects nor JS after this point!
   2398    }
   2399 
   2400    // Check sentinels if we actually removed all global scope references.
   2401    // In case use the earlier set-aside raw pointers to not mess with the
   2402    // ref counting after the cycle collector has gone away.
   2403    if (NS_WARN_IF(globalScopeSentinel && globalScopeSentinel->IsAlive())) {
   2404      MOZ_ASSERT_UNREACHABLE("WorkerGlobalScope alive after worker shutdown");
   2405      globalScopeRawPtr->NoteWorkerTerminated();
   2406      globalScopeRawPtr = nullptr;
   2407    }
   2408    if (NS_WARN_IF(debuggerScopeSentinel && debuggerScopeSentinel->IsAlive())) {
   2409      MOZ_ASSERT_UNREACHABLE("Debugger global alive after worker shutdown");
   2410      debuggerScopeRawPtr->NoteWorkerTerminated();
   2411      debuggerScopeRawPtr = nullptr;
   2412    }
   2413  }
   2414 
   2415  mWorkerPrivate->ScheduleDeletion(WorkerPrivate::WorkerRan);
   2416 
   2417  // It is no longer safe to touch mWorkerPrivate.
   2418  mWorkerPrivate = nullptr;
   2419 
   2420  // Now recycle this thread.
   2421  nsCOMPtr<nsIEventTarget> mainTarget = GetMainThreadSerialEventTarget();
   2422  MOZ_ASSERT(mainTarget);
   2423 
   2424  RefPtr<FinishedRunnable> finishedRunnable =
   2425      new FinishedRunnable(std::move(mThread));
   2426  MOZ_ALWAYS_SUCCEEDS(
   2427      mainTarget->Dispatch(finishedRunnable, NS_DISPATCH_NORMAL));
   2428 
   2429  return NS_OK;
   2430 }
   2431 
   2432 NS_IMETHODIMP
   2433 WorkerThreadPrimaryRunnable::FinishedRunnable::Run() {
   2434  AssertIsOnMainThread();
   2435 
   2436  SafeRefPtr<WorkerThread> thread = std::move(mThread);
   2437  if (thread->ShutdownRequired()) {
   2438    MOZ_ALWAYS_SUCCEEDS(thread->Shutdown());
   2439  }
   2440 
   2441  return NS_OK;
   2442 }
   2443 
   2444 }  // namespace workerinternals
   2445 
   2446 // This is mostly for invoking within a debugger.
   2447 void DumpRunningWorkers() {
   2448  RuntimeService* runtimeService = RuntimeService::GetService();
   2449  if (runtimeService) {
   2450    runtimeService->DumpRunningWorkers();
   2451  } else {
   2452    NS_WARNING("RuntimeService not found");
   2453  }
   2454 }
   2455 
   2456 void CancelWorkersForWindow(const nsPIDOMWindowInner& aWindow) {
   2457  AssertIsOnMainThread();
   2458  RuntimeService* runtime = RuntimeService::GetService();
   2459  if (runtime) {
   2460    runtime->CancelWorkersForWindow(aWindow);
   2461  }
   2462 }
   2463 
   2464 void UpdateWorkersBackgroundState(const nsPIDOMWindowInner& aWindow,
   2465                                  bool aIsBackground) {
   2466  AssertIsOnMainThread();
   2467  RuntimeService* runtime = RuntimeService::GetService();
   2468  if (runtime) {
   2469    runtime->UpdateWorkersBackgroundState(aWindow, aIsBackground);
   2470  }
   2471 }
   2472 
   2473 void FreezeWorkersForWindow(const nsPIDOMWindowInner& aWindow) {
   2474  AssertIsOnMainThread();
   2475  RuntimeService* runtime = RuntimeService::GetService();
   2476  if (runtime) {
   2477    runtime->FreezeWorkersForWindow(aWindow);
   2478  }
   2479 }
   2480 
   2481 void ThawWorkersForWindow(const nsPIDOMWindowInner& aWindow) {
   2482  AssertIsOnMainThread();
   2483  RuntimeService* runtime = RuntimeService::GetService();
   2484  if (runtime) {
   2485    runtime->ThawWorkersForWindow(aWindow);
   2486  }
   2487 }
   2488 
   2489 void SuspendWorkersForWindow(const nsPIDOMWindowInner& aWindow) {
   2490  AssertIsOnMainThread();
   2491  RuntimeService* runtime = RuntimeService::GetService();
   2492  if (runtime) {
   2493    runtime->SuspendWorkersForWindow(aWindow);
   2494  }
   2495 }
   2496 
   2497 void ResumeWorkersForWindow(const nsPIDOMWindowInner& aWindow) {
   2498  AssertIsOnMainThread();
   2499  RuntimeService* runtime = RuntimeService::GetService();
   2500  if (runtime) {
   2501    runtime->ResumeWorkersForWindow(aWindow);
   2502  }
   2503 }
   2504 
   2505 void PropagateStorageAccessPermissionGrantedToWorkers(
   2506    const nsPIDOMWindowInner& aWindow) {
   2507  AssertIsOnMainThread();
   2508  MOZ_ASSERT_IF(aWindow.GetExtantDoc(), aWindow.GetExtantDoc()
   2509                                            ->CookieJarSettings()
   2510                                            ->GetRejectThirdPartyContexts());
   2511 
   2512  RuntimeService* runtime = RuntimeService::GetService();
   2513  if (runtime) {
   2514    runtime->PropagateStorageAccessPermissionGranted(aWindow);
   2515  }
   2516 }
   2517 
   2518 WorkerPrivate* GetWorkerPrivateFromContext(JSContext* aCx) {
   2519  MOZ_ASSERT(!NS_IsMainThread());
   2520  MOZ_ASSERT(aCx);
   2521 
   2522  CycleCollectedJSContext* ccjscx = CycleCollectedJSContext::GetFor(aCx);
   2523  if (!ccjscx) {
   2524    return nullptr;
   2525  }
   2526 
   2527  WorkerJSContext* workerjscx = ccjscx->GetAsWorkerJSContext();
   2528  // GetWorkerPrivateFromContext is called only for worker contexts.  The
   2529  // context private is cleared early in ~CycleCollectedJSContext() and so
   2530  // GetFor() returns null above if called after ccjscx is no longer a
   2531  // WorkerJSContext.
   2532  MOZ_ASSERT(workerjscx);
   2533  return workerjscx->GetWorkerPrivate();
   2534 }
   2535 
   2536 WorkerPrivate* GetCurrentThreadWorkerPrivate() {
   2537  if (NS_IsMainThread()) {
   2538    return nullptr;
   2539  }
   2540 
   2541  CycleCollectedJSContext* ccjscx = CycleCollectedJSContext::Get();
   2542  if (!ccjscx) {
   2543    return nullptr;
   2544  }
   2545 
   2546  WorkerJSContext* workerjscx = ccjscx->GetAsWorkerJSContext();
   2547  // Even when GetCurrentThreadWorkerPrivate() is called on worker
   2548  // threads, the ccjscx will no longer be a WorkerJSContext if called from
   2549  // stable state events during ~CycleCollectedJSContext().
   2550  if (!workerjscx) {
   2551    return nullptr;
   2552  }
   2553 
   2554  return workerjscx->GetWorkerPrivate();
   2555 }
   2556 
   2557 bool IsCurrentThreadRunningWorker() {
   2558  return !NS_IsMainThread() && !!GetCurrentThreadWorkerPrivate();
   2559 }
   2560 
   2561 bool IsCurrentThreadRunningChromeWorker() {
   2562  WorkerPrivate* wp = GetCurrentThreadWorkerPrivate();
   2563  return wp && wp->UsesSystemPrincipal();
   2564 }
   2565 
   2566 JSContext* GetCurrentWorkerThreadJSContext() {
   2567  WorkerPrivate* wp = GetCurrentThreadWorkerPrivate();
   2568  if (!wp) {
   2569    return nullptr;
   2570  }
   2571  return wp->GetJSContext();
   2572 }
   2573 
   2574 JSObject* GetCurrentThreadWorkerGlobal() {
   2575  WorkerPrivate* wp = GetCurrentThreadWorkerPrivate();
   2576  if (!wp) {
   2577    return nullptr;
   2578  }
   2579  WorkerGlobalScope* scope = wp->GlobalScope();
   2580  if (!scope) {
   2581    return nullptr;
   2582  }
   2583  return scope->GetGlobalJSObject();
   2584 }
   2585 
   2586 JSObject* GetCurrentThreadWorkerDebuggerGlobal() {
   2587  WorkerPrivate* wp = GetCurrentThreadWorkerPrivate();
   2588  if (!wp) {
   2589    return nullptr;
   2590  }
   2591  WorkerDebuggerGlobalScope* scope = wp->DebuggerGlobalScope();
   2592  if (!scope) {
   2593    return nullptr;
   2594  }
   2595  return scope->GetGlobalJSObject();
   2596 }
   2597 
   2598 void UpdateWorkersPlaybackState(const nsPIDOMWindowInner& aWindow,
   2599                                bool aIsPlayingAudio) {
   2600  AssertIsOnMainThread();
   2601  RuntimeService* runtime = RuntimeService::GetService();
   2602  if (runtime) {
   2603    runtime->UpdateWorkersPlaybackState(aWindow, aIsPlayingAudio);
   2604  }
   2605 }
   2606 
   2607 void UpdateWorkersPeerConnections(const nsPIDOMWindowInner& aWindow,
   2608                                  bool aHasPeerConnections) {
   2609  AssertIsOnMainThread();
   2610  RuntimeService* runtime = RuntimeService::GetService();
   2611  if (runtime) {
   2612    runtime->UpdateWorkersPeerConnections(aWindow, aHasPeerConnections);
   2613  }
   2614 }
   2615 
   2616 }  // namespace dom
   2617 }  // namespace mozilla