Coverage Report

Created: 2018-09-25 14:53

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