Bug 1610775 [wpt PR 21336] - Update urllib3 to 1.25.8, a=testonly
[gecko.git] / dom / workers / WorkerPrivate.cpp
blob3bfaeefa608c56c79d57d35bd36c6c0fb74f7496
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/. */
7 #include "WorkerPrivate.h"
9 #include <utility>
11 #include "js/CompilationAndEvaluation.h"
12 #include "js/ContextOptions.h"
13 #include "js/LocaleSensitive.h"
14 #include "js/MemoryMetrics.h"
15 #include "js/SourceText.h"
16 #include "MessageEventRunnable.h"
17 #include "mozilla/ScopeExit.h"
18 #include "mozilla/StaticPrefs_dom.h"
19 #include "mozilla/dom/BlobURLProtocolHandler.h"
20 #include "mozilla/dom/CallbackDebuggerNotification.h"
21 #include "mozilla/dom/ClientManager.h"
22 #include "mozilla/dom/ClientSource.h"
23 #include "mozilla/dom/ClientState.h"
24 #include "mozilla/dom/Console.h"
25 #include "mozilla/dom/DOMTypes.h"
26 #include "mozilla/dom/Event.h"
27 #include "mozilla/dom/FunctionBinding.h"
28 #include "mozilla/dom/IndexedDatabaseManager.h"
29 #include "mozilla/dom/MessageEvent.h"
30 #include "mozilla/dom/MessageEventBinding.h"
31 #include "mozilla/dom/MessagePort.h"
32 #include "mozilla/dom/MessagePortBinding.h"
33 #include "mozilla/dom/nsCSPContext.h"
34 #include "mozilla/dom/nsCSPUtils.h"
35 #include "mozilla/dom/Performance.h"
36 #include "mozilla/dom/PerformanceStorageWorker.h"
37 #include "mozilla/dom/PromiseDebugging.h"
38 #include "mozilla/dom/RemoteWorkerChild.h"
39 #include "mozilla/dom/RemoteWorkerService.h"
40 #include "mozilla/dom/TimeoutHandler.h"
41 #include "mozilla/dom/WorkerBinding.h"
42 #include "mozilla/StorageAccess.h"
43 #include "mozilla/ThreadEventQueue.h"
44 #include "mozilla/ThrottledEventQueue.h"
45 #include "mozilla/TimelineConsumers.h"
46 #include "mozilla/WorkerTimelineMarker.h"
47 #include "nsCycleCollector.h"
48 #include "nsGlobalWindowInner.h"
49 #include "nsNetUtil.h"
50 #include "nsIMemoryReporter.h"
51 #include "nsIPermissionManager.h"
52 #include "nsIScriptError.h"
53 #include "nsIURI.h"
54 #include "nsIURL.h"
55 #include "nsIUUIDGenerator.h"
56 #include "nsPrintfCString.h"
57 #include "nsProxyRelease.h"
58 #include "nsQueryObject.h"
59 #include "nsRFPService.h"
60 #include "nsSandboxFlags.h"
61 #include "nsUTF8Utils.h"
63 #include "RuntimeService.h"
64 #include "ScriptLoader.h"
65 #include "mozilla/dom/ServiceWorkerEvents.h"
66 #include "mozilla/dom/ServiceWorkerManager.h"
67 #include "mozilla/net/CookieSettings.h"
68 #include "WorkerCSPEventListener.h"
69 #include "WorkerDebugger.h"
70 #include "WorkerDebuggerManager.h"
71 #include "WorkerError.h"
72 #include "WorkerEventTarget.h"
73 #include "WorkerNavigator.h"
74 #include "WorkerRef.h"
75 #include "WorkerRunnable.h"
76 #include "WorkerScope.h"
77 #include "WorkerThread.h"
79 #include "nsThreadManager.h"
81 #ifdef XP_WIN
82 # undef PostMessage
83 #endif
85 // JS_MaybeGC will run once every second during normal execution.
86 #define PERIODIC_GC_TIMER_DELAY_SEC 1
88 // A shrinking GC will run five seconds after the last event is processed.
89 #define IDLE_GC_TIMER_DELAY_SEC 5
91 static mozilla::LazyLogModule sWorkerPrivateLog("WorkerPrivate");
92 static mozilla::LazyLogModule sWorkerTimeoutsLog("WorkerTimeouts");
94 mozilla::LogModule* WorkerLog() { return sWorkerPrivateLog; }
96 mozilla::LogModule* TimeoutsLog() { return sWorkerTimeoutsLog; }
98 #ifdef LOG
99 # undef LOG
100 #endif
101 #define LOG(log, _args) MOZ_LOG(log, LogLevel::Debug, _args);
103 namespace mozilla {
105 using namespace ipc;
107 namespace dom {
109 using namespace workerinternals;
111 MOZ_DEFINE_MALLOC_SIZE_OF(JsWorkerMallocSizeOf)
113 namespace {
115 #ifdef DEBUG
117 const nsIID kDEBUGWorkerEventTargetIID = {
118 0xccaba3fa,
119 0x5be2,
120 0x4de2,
121 {0xba, 0x87, 0x3b, 0x3b, 0x5b, 0x1d, 0x5, 0xfb}};
123 #endif
125 template <class T>
126 class AutoPtrComparator {
127 typedef nsAutoPtr<T> A;
128 typedef T* B;
130 public:
131 bool Equals(const A& a, const B& b) const {
132 return a && b ? *a == *b : !a && !b ? true : false;
134 bool LessThan(const A& a, const B& b) const {
135 return a && b ? *a < *b : b ? true : false;
139 template <class T>
140 inline AutoPtrComparator<T> GetAutoPtrComparator(
141 const nsTArray<nsAutoPtr<T>>&) {
142 return AutoPtrComparator<T>();
145 // This class is used to wrap any runnables that the worker receives via the
146 // nsIEventTarget::Dispatch() method (either from NS_DispatchToCurrentThread or
147 // from the worker's EventTarget).
148 class ExternalRunnableWrapper final : public WorkerRunnable {
149 nsCOMPtr<nsIRunnable> mWrappedRunnable;
151 public:
152 ExternalRunnableWrapper(WorkerPrivate* aWorkerPrivate,
153 nsIRunnable* aWrappedRunnable)
154 : WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
155 mWrappedRunnable(aWrappedRunnable) {
156 MOZ_ASSERT(aWorkerPrivate);
157 MOZ_ASSERT(aWrappedRunnable);
160 NS_INLINE_DECL_REFCOUNTING_INHERITED(ExternalRunnableWrapper, WorkerRunnable)
162 private:
163 ~ExternalRunnableWrapper() {}
165 virtual bool PreDispatch(WorkerPrivate* aWorkerPrivate) override {
166 // Silence bad assertions.
167 return true;
170 virtual void PostDispatch(WorkerPrivate* aWorkerPrivate,
171 bool aDispatchResult) override {
172 // Silence bad assertions.
175 virtual bool WorkerRun(JSContext* aCx,
176 WorkerPrivate* aWorkerPrivate) override {
177 nsresult rv = mWrappedRunnable->Run();
178 if (NS_FAILED(rv)) {
179 if (!JS_IsExceptionPending(aCx)) {
180 Throw(aCx, rv);
182 return false;
184 return true;
187 nsresult Cancel() override {
188 nsresult rv;
189 nsCOMPtr<nsICancelableRunnable> cancelable =
190 do_QueryInterface(mWrappedRunnable);
191 MOZ_ASSERT(cancelable); // We checked this earlier!
192 rv = cancelable->Cancel();
193 nsresult rv2 = WorkerRunnable::Cancel();
194 return NS_FAILED(rv) ? rv : rv2;
198 struct WindowAction {
199 nsPIDOMWindowInner* mWindow;
200 bool mDefaultAction;
202 MOZ_IMPLICIT WindowAction(nsPIDOMWindowInner* aWindow)
203 : mWindow(aWindow), mDefaultAction(true) {}
205 bool operator==(const WindowAction& aOther) const {
206 return mWindow == aOther.mWindow;
210 class WorkerFinishedRunnable final : public WorkerControlRunnable {
211 WorkerPrivate* mFinishedWorker;
213 public:
214 WorkerFinishedRunnable(WorkerPrivate* aWorkerPrivate,
215 WorkerPrivate* aFinishedWorker)
216 : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
217 mFinishedWorker(aFinishedWorker) {}
219 private:
220 virtual bool PreDispatch(WorkerPrivate* aWorkerPrivate) override {
221 // Silence bad assertions.
222 return true;
225 virtual void PostDispatch(WorkerPrivate* aWorkerPrivate,
226 bool aDispatchResult) override {
227 // Silence bad assertions.
230 virtual bool WorkerRun(JSContext* aCx,
231 WorkerPrivate* aWorkerPrivate) override {
232 if (!mFinishedWorker->ProxyReleaseMainThreadObjects()) {
233 NS_WARNING("Failed to dispatch, going to leak!");
236 RuntimeService* runtime = RuntimeService::GetService();
237 NS_ASSERTION(runtime, "This should never be null!");
239 mFinishedWorker->DisableDebugger();
241 runtime->UnregisterWorker(mFinishedWorker);
243 mFinishedWorker->ClearSelfAndParentEventTargetRef();
244 return true;
248 class TopLevelWorkerFinishedRunnable final : public Runnable {
249 WorkerPrivate* mFinishedWorker;
251 public:
252 explicit TopLevelWorkerFinishedRunnable(WorkerPrivate* aFinishedWorker)
253 : mozilla::Runnable("TopLevelWorkerFinishedRunnable"),
254 mFinishedWorker(aFinishedWorker) {
255 aFinishedWorker->AssertIsOnWorkerThread();
258 NS_INLINE_DECL_REFCOUNTING_INHERITED(TopLevelWorkerFinishedRunnable, Runnable)
260 private:
261 ~TopLevelWorkerFinishedRunnable() {}
263 NS_IMETHOD
264 Run() override {
265 AssertIsOnMainThread();
267 RuntimeService* runtime = RuntimeService::GetService();
268 MOZ_ASSERT(runtime);
270 mFinishedWorker->DisableDebugger();
272 runtime->UnregisterWorker(mFinishedWorker);
274 if (!mFinishedWorker->ProxyReleaseMainThreadObjects()) {
275 NS_WARNING("Failed to dispatch, going to leak!");
278 mFinishedWorker->ClearSelfAndParentEventTargetRef();
279 return NS_OK;
283 class ModifyBusyCountRunnable final : public WorkerControlRunnable {
284 bool mIncrease;
286 public:
287 ModifyBusyCountRunnable(WorkerPrivate* aWorkerPrivate, bool aIncrease)
288 : WorkerControlRunnable(aWorkerPrivate, ParentThreadUnchangedBusyCount),
289 mIncrease(aIncrease) {}
291 private:
292 virtual bool WorkerRun(JSContext* aCx,
293 WorkerPrivate* aWorkerPrivate) override {
294 return aWorkerPrivate->ModifyBusyCount(mIncrease);
297 virtual void PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
298 bool aRunResult) override {
299 if (mIncrease) {
300 WorkerControlRunnable::PostRun(aCx, aWorkerPrivate, aRunResult);
301 return;
303 // Don't do anything here as it's possible that aWorkerPrivate has been
304 // deleted.
308 class CompileScriptRunnable final : public WorkerDebuggeeRunnable {
309 nsString mScriptURL;
310 UniquePtr<SerializedStackHolder> mOriginStack;
312 public:
313 explicit CompileScriptRunnable(WorkerPrivate* aWorkerPrivate,
314 UniquePtr<SerializedStackHolder> aOriginStack,
315 const nsAString& aScriptURL)
316 : WorkerDebuggeeRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount),
317 mScriptURL(aScriptURL),
318 mOriginStack(aOriginStack.release()) {}
320 private:
321 // We can't implement PreRun effectively, because at the point when that would
322 // run we have not yet done our load so don't know things like our final
323 // principal and whatnot.
325 virtual bool WorkerRun(JSContext* aCx,
326 WorkerPrivate* aWorkerPrivate) override {
327 aWorkerPrivate->AssertIsOnWorkerThread();
329 if (NS_WARN_IF(!aWorkerPrivate->EnsureClientSource())) {
330 return false;
333 if (NS_WARN_IF(!aWorkerPrivate->EnsureCSPEventListener())) {
334 return false;
337 // PerformanceStorage & PerformanceCounter both need to be initialized
338 // on the worker thread before being used on main-thread.
339 // Let's be sure that it is created before any
340 // content loading.
341 aWorkerPrivate->EnsurePerformanceStorage();
342 aWorkerPrivate->EnsurePerformanceCounter();
344 ErrorResult rv;
345 workerinternals::LoadMainScript(aWorkerPrivate, std::move(mOriginStack),
346 mScriptURL, WorkerScript, rv);
347 rv.WouldReportJSException();
348 // Explicitly ignore NS_BINDING_ABORTED on rv. Or more precisely, still
349 // return false and don't SetWorkerScriptExecutedSuccessfully() in that
350 // case, but don't throw anything on aCx. The idea is to not dispatch error
351 // events if our load is canceled with that error code.
352 if (rv.ErrorCodeIs(NS_BINDING_ABORTED)) {
353 rv.SuppressException();
354 return false;
357 WorkerGlobalScope* globalScope = aWorkerPrivate->GlobalScope();
358 if (NS_WARN_IF(!globalScope)) {
359 // We never got as far as calling GetOrCreateGlobalScope, or it failed.
360 // We have no way to enter a compartment, hence no sane way to report this
361 // error. :(
362 rv.SuppressException();
363 return false;
366 // Make sure to propagate exceptions from rv onto aCx, so that they will get
367 // reported after we return. We want to propagate just JS exceptions,
368 // because all the other errors are handled when the script is loaded.
369 // See: https://dom.spec.whatwg.org/#concept-event-fire
370 if (rv.Failed() && !rv.IsJSException()) {
371 WorkerErrorReport::CreateAndDispatchGenericErrorRunnableToParent(
372 aWorkerPrivate);
373 rv.SuppressException();
374 return false;
377 // This is a little dumb, but aCx is in the null realm here because we
378 // set it up that way in our Run(), since we had not created the global at
379 // that point yet. So we need to enter the realm of our global,
380 // because setting a pending exception on aCx involves wrapping into its
381 // current compartment. Luckily we have a global now.
382 JSAutoRealm ar(aCx, globalScope->GetGlobalJSObject());
383 if (rv.MaybeSetPendingException(aCx)) {
384 return false;
387 aWorkerPrivate->SetWorkerScriptExecutedSuccessfully();
388 return true;
391 void PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
392 bool aRunResult) override {
393 if (!aRunResult) {
394 aWorkerPrivate->CloseInternal();
396 WorkerRunnable::PostRun(aCx, aWorkerPrivate, aRunResult);
400 class NotifyRunnable final : public WorkerControlRunnable {
401 WorkerStatus mStatus;
403 public:
404 NotifyRunnable(WorkerPrivate* aWorkerPrivate, WorkerStatus aStatus)
405 : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
406 mStatus(aStatus) {
407 MOZ_ASSERT(aStatus == Closing || aStatus == Canceling ||
408 aStatus == Killing);
411 private:
412 virtual bool PreDispatch(WorkerPrivate* aWorkerPrivate) override {
413 aWorkerPrivate->AssertIsOnParentThread();
414 return aWorkerPrivate->ModifyBusyCount(true);
417 virtual void PostDispatch(WorkerPrivate* aWorkerPrivate,
418 bool aDispatchResult) override {
419 aWorkerPrivate->AssertIsOnParentThread();
420 if (!aDispatchResult) {
421 // We couldn't dispatch to the worker, which means it's already dead.
422 // Undo the busy count modification.
423 aWorkerPrivate->ModifyBusyCount(false);
427 virtual void PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
428 bool aRunResult) override {
429 aWorkerPrivate->ModifyBusyCountFromWorker(false);
432 virtual bool WorkerRun(JSContext* aCx,
433 WorkerPrivate* aWorkerPrivate) override {
434 return aWorkerPrivate->NotifyInternal(mStatus);
438 class FreezeRunnable final : public WorkerControlRunnable {
439 public:
440 explicit FreezeRunnable(WorkerPrivate* aWorkerPrivate)
441 : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount) {}
443 private:
444 virtual bool WorkerRun(JSContext* aCx,
445 WorkerPrivate* aWorkerPrivate) override {
446 return aWorkerPrivate->FreezeInternal();
450 class ThawRunnable final : public WorkerControlRunnable {
451 public:
452 explicit ThawRunnable(WorkerPrivate* aWorkerPrivate)
453 : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount) {}
455 private:
456 virtual bool WorkerRun(JSContext* aCx,
457 WorkerPrivate* aWorkerPrivate) override {
458 return aWorkerPrivate->ThawInternal();
462 class PropagateFirstPartyStorageAccessGrantedRunnable final
463 : public WorkerControlRunnable {
464 public:
465 explicit PropagateFirstPartyStorageAccessGrantedRunnable(
466 WorkerPrivate* aWorkerPrivate)
467 : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount) {}
469 private:
470 bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
471 aWorkerPrivate->PropagateFirstPartyStorageAccessGrantedInternal();
472 return true;
476 class ReportErrorToConsoleRunnable final : public WorkerRunnable {
477 const char* mMessage;
478 const nsTArray<nsString> mParams;
480 public:
481 // aWorkerPrivate is the worker thread we're on (or the main thread, if null)
482 static void Report(WorkerPrivate* aWorkerPrivate, const char* aMessage,
483 const nsTArray<nsString>& aParams) {
484 if (aWorkerPrivate) {
485 aWorkerPrivate->AssertIsOnWorkerThread();
486 } else {
487 AssertIsOnMainThread();
490 // Now fire a runnable to do the same on the parent's thread if we can.
491 if (aWorkerPrivate) {
492 RefPtr<ReportErrorToConsoleRunnable> runnable =
493 new ReportErrorToConsoleRunnable(aWorkerPrivate, aMessage, aParams);
494 runnable->Dispatch();
495 return;
498 // Log a warning to the console.
499 nsContentUtils::ReportToConsole(
500 nsIScriptError::warningFlag, NS_LITERAL_CSTRING("DOM"), nullptr,
501 nsContentUtils::eDOM_PROPERTIES, aMessage, aParams);
504 private:
505 ReportErrorToConsoleRunnable(WorkerPrivate* aWorkerPrivate,
506 const char* aMessage,
507 const nsTArray<nsString>& aParams)
508 : WorkerRunnable(aWorkerPrivate, ParentThreadUnchangedBusyCount),
509 mMessage(aMessage),
510 mParams(aParams) {}
512 virtual void PostDispatch(WorkerPrivate* aWorkerPrivate,
513 bool aDispatchResult) override {
514 aWorkerPrivate->AssertIsOnWorkerThread();
516 // Dispatch may fail if the worker was canceled, no need to report that as
517 // an error, so don't call base class PostDispatch.
520 virtual bool WorkerRun(JSContext* aCx,
521 WorkerPrivate* aWorkerPrivate) override {
522 WorkerPrivate* parent = aWorkerPrivate->GetParent();
523 MOZ_ASSERT_IF(!parent, NS_IsMainThread());
524 Report(parent, mMessage, mParams);
525 return true;
529 class TimerRunnable final : public WorkerRunnable,
530 public nsITimerCallback,
531 public nsINamed {
532 public:
533 NS_DECL_ISUPPORTS_INHERITED
535 explicit TimerRunnable(WorkerPrivate* aWorkerPrivate)
536 : WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount) {}
538 private:
539 ~TimerRunnable() {}
541 virtual bool PreDispatch(WorkerPrivate* aWorkerPrivate) override {
542 // Silence bad assertions.
543 return true;
546 virtual void PostDispatch(WorkerPrivate* aWorkerPrivate,
547 bool aDispatchResult) override {
548 // Silence bad assertions.
551 // MOZ_CAN_RUN_SCRIPT_BOUNDARY until worker runnables are generally
552 // MOZ_CAN_RUN_SCRIPT.
553 MOZ_CAN_RUN_SCRIPT_BOUNDARY
554 virtual bool WorkerRun(JSContext* aCx,
555 WorkerPrivate* aWorkerPrivate) override {
556 return aWorkerPrivate->RunExpiredTimeouts(aCx);
559 NS_IMETHOD
560 Notify(nsITimer* aTimer) override { return Run(); }
562 NS_IMETHOD
563 GetName(nsACString& aName) override {
564 aName.AssignLiteral("TimerRunnable");
565 return NS_OK;
569 NS_IMPL_ISUPPORTS_INHERITED(TimerRunnable, WorkerRunnable, nsITimerCallback,
570 nsINamed)
572 class DebuggerImmediateRunnable : public WorkerRunnable {
573 RefPtr<dom::Function> mHandler;
575 public:
576 explicit DebuggerImmediateRunnable(WorkerPrivate* aWorkerPrivate,
577 dom::Function& aHandler)
578 : WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
579 mHandler(&aHandler) {}
581 private:
582 virtual bool IsDebuggerRunnable() const override { return true; }
584 virtual bool PreDispatch(WorkerPrivate* aWorkerPrivate) override {
585 // Silence bad assertions.
586 return true;
589 virtual void PostDispatch(WorkerPrivate* aWorkerPrivate,
590 bool aDispatchResult) override {
591 // Silence bad assertions.
594 virtual bool WorkerRun(JSContext* aCx,
595 WorkerPrivate* aWorkerPrivate) override {
596 JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx));
597 JS::Rooted<JS::Value> callable(
598 aCx, JS::ObjectOrNullValue(mHandler->CallableOrNull()));
599 JS::HandleValueArray args = JS::HandleValueArray::empty();
600 JS::Rooted<JS::Value> rval(aCx);
601 if (!JS_CallFunctionValue(aCx, global, callable, args, &rval)) {
602 // Just return false; WorkerRunnable::Run will report the exception.
603 return false;
606 return true;
610 void PeriodicGCTimerCallback(nsITimer* aTimer, void* aClosure) {
611 auto workerPrivate = static_cast<WorkerPrivate*>(aClosure);
612 MOZ_DIAGNOSTIC_ASSERT(workerPrivate);
613 workerPrivate->AssertIsOnWorkerThread();
614 workerPrivate->GarbageCollectInternal(workerPrivate->GetJSContext(),
615 false /* shrinking */,
616 false /* collect children */);
619 void IdleGCTimerCallback(nsITimer* aTimer, void* aClosure) {
620 auto workerPrivate = static_cast<WorkerPrivate*>(aClosure);
621 MOZ_DIAGNOSTIC_ASSERT(workerPrivate);
622 workerPrivate->AssertIsOnWorkerThread();
623 workerPrivate->GarbageCollectInternal(workerPrivate->GetJSContext(),
624 true /* shrinking */,
625 false /* collect children */);
628 class UpdateContextOptionsRunnable final : public WorkerControlRunnable {
629 JS::ContextOptions mContextOptions;
631 public:
632 UpdateContextOptionsRunnable(WorkerPrivate* aWorkerPrivate,
633 const JS::ContextOptions& aContextOptions)
634 : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
635 mContextOptions(aContextOptions) {}
637 private:
638 virtual bool WorkerRun(JSContext* aCx,
639 WorkerPrivate* aWorkerPrivate) override {
640 aWorkerPrivate->UpdateContextOptionsInternal(aCx, mContextOptions);
641 return true;
645 class UpdateLanguagesRunnable final : public WorkerRunnable {
646 nsTArray<nsString> mLanguages;
648 public:
649 UpdateLanguagesRunnable(WorkerPrivate* aWorkerPrivate,
650 const nsTArray<nsString>& aLanguages)
651 : WorkerRunnable(aWorkerPrivate), mLanguages(aLanguages) {}
653 virtual bool WorkerRun(JSContext* aCx,
654 WorkerPrivate* aWorkerPrivate) override {
655 aWorkerPrivate->UpdateLanguagesInternal(mLanguages);
656 return true;
660 class UpdateJSWorkerMemoryParameterRunnable final
661 : public WorkerControlRunnable {
662 uint32_t mValue;
663 JSGCParamKey mKey;
665 public:
666 UpdateJSWorkerMemoryParameterRunnable(WorkerPrivate* aWorkerPrivate,
667 JSGCParamKey aKey, uint32_t aValue)
668 : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
669 mValue(aValue),
670 mKey(aKey) {}
672 private:
673 virtual bool WorkerRun(JSContext* aCx,
674 WorkerPrivate* aWorkerPrivate) override {
675 aWorkerPrivate->UpdateJSWorkerMemoryParameterInternal(aCx, mKey, mValue);
676 return true;
680 #ifdef JS_GC_ZEAL
681 class UpdateGCZealRunnable final : public WorkerControlRunnable {
682 uint8_t mGCZeal;
683 uint32_t mFrequency;
685 public:
686 UpdateGCZealRunnable(WorkerPrivate* aWorkerPrivate, uint8_t aGCZeal,
687 uint32_t aFrequency)
688 : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
689 mGCZeal(aGCZeal),
690 mFrequency(aFrequency) {}
692 private:
693 virtual bool WorkerRun(JSContext* aCx,
694 WorkerPrivate* aWorkerPrivate) override {
695 aWorkerPrivate->UpdateGCZealInternal(aCx, mGCZeal, mFrequency);
696 return true;
699 #endif
701 class SetLowMemoryStateRunnable final : public WorkerControlRunnable {
702 bool mState;
704 public:
705 SetLowMemoryStateRunnable(WorkerPrivate* aWorkerPrivate, bool aState)
706 : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
707 mState(aState) {}
709 private:
710 virtual bool WorkerRun(JSContext* aCx,
711 WorkerPrivate* aWorkerPrivate) override {
712 aWorkerPrivate->SetLowMemoryStateInternal(aCx, mState);
713 return true;
717 class GarbageCollectRunnable final : public WorkerControlRunnable {
718 bool mShrinking;
719 bool mCollectChildren;
721 public:
722 GarbageCollectRunnable(WorkerPrivate* aWorkerPrivate, bool aShrinking,
723 bool aCollectChildren)
724 : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
725 mShrinking(aShrinking),
726 mCollectChildren(aCollectChildren) {}
728 private:
729 virtual bool PreDispatch(WorkerPrivate* aWorkerPrivate) override {
730 // Silence bad assertions, this can be dispatched from either the main
731 // thread or the timer thread..
732 return true;
735 virtual void PostDispatch(WorkerPrivate* aWorkerPrivate,
736 bool aDispatchResult) override {
737 // Silence bad assertions, this can be dispatched from either the main
738 // thread or the timer thread..
741 virtual bool WorkerRun(JSContext* aCx,
742 WorkerPrivate* aWorkerPrivate) override {
743 aWorkerPrivate->GarbageCollectInternal(aCx, mShrinking, mCollectChildren);
744 return true;
748 class CycleCollectRunnable : public WorkerControlRunnable {
749 bool mCollectChildren;
751 public:
752 CycleCollectRunnable(WorkerPrivate* aWorkerPrivate, bool aCollectChildren)
753 : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
754 mCollectChildren(aCollectChildren) {}
756 bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
757 aWorkerPrivate->CycleCollectInternal(mCollectChildren);
758 return true;
762 class OfflineStatusChangeRunnable : public WorkerRunnable {
763 public:
764 OfflineStatusChangeRunnable(WorkerPrivate* aWorkerPrivate, bool aIsOffline)
765 : WorkerRunnable(aWorkerPrivate), mIsOffline(aIsOffline) {}
767 bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
768 aWorkerPrivate->OfflineStatusChangeEventInternal(mIsOffline);
769 return true;
772 private:
773 bool mIsOffline;
776 class MemoryPressureRunnable : public WorkerControlRunnable {
777 public:
778 explicit MemoryPressureRunnable(WorkerPrivate* aWorkerPrivate)
779 : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount) {}
781 bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
782 aWorkerPrivate->MemoryPressureInternal();
783 return true;
787 #ifdef DEBUG
788 static bool StartsWithExplicit(nsACString& s) {
789 return StringBeginsWith(s, NS_LITERAL_CSTRING("explicit/"));
791 #endif
793 PRThread* PRThreadFromThread(nsIThread* aThread) {
794 MOZ_ASSERT(aThread);
796 PRThread* result;
797 MOZ_ALWAYS_SUCCEEDS(aThread->GetPRThread(&result));
798 MOZ_ASSERT(result);
800 return result;
803 // A runnable to cancel the worker from the parent thread when self.close() is
804 // called. This runnable is executed on the parent process in order to cancel
805 // the current runnable. It uses a normal WorkerDebuggeeRunnable in order to be
806 // sure that all the pending WorkerDebuggeeRunnables are executed before this.
807 class CancelingOnParentRunnable final : public WorkerDebuggeeRunnable {
808 public:
809 explicit CancelingOnParentRunnable(WorkerPrivate* aWorkerPrivate)
810 : WorkerDebuggeeRunnable(aWorkerPrivate, ParentThreadUnchangedBusyCount) {
813 bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
814 aWorkerPrivate->Cancel();
815 return true;
819 // A runnable to cancel the worker from the parent process.
820 class CancelingWithTimeoutOnParentRunnable final
821 : public WorkerControlRunnable {
822 public:
823 explicit CancelingWithTimeoutOnParentRunnable(WorkerPrivate* aWorkerPrivate)
824 : WorkerControlRunnable(aWorkerPrivate, ParentThreadUnchangedBusyCount) {}
826 bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
827 aWorkerPrivate->AssertIsOnParentThread();
828 aWorkerPrivate->StartCancelingTimer();
829 return true;
833 class CancelingTimerCallback final : public nsITimerCallback {
834 public:
835 NS_DECL_ISUPPORTS
837 explicit CancelingTimerCallback(WorkerPrivate* aWorkerPrivate)
838 : mWorkerPrivate(aWorkerPrivate) {}
840 NS_IMETHOD
841 Notify(nsITimer* aTimer) override {
842 mWorkerPrivate->AssertIsOnParentThread();
843 mWorkerPrivate->Cancel();
844 return NS_OK;
847 private:
848 ~CancelingTimerCallback() = default;
850 // Raw pointer here is OK because the timer is canceled during the shutdown
851 // steps.
852 WorkerPrivate* mWorkerPrivate;
855 NS_IMPL_ISUPPORTS(CancelingTimerCallback, nsITimerCallback)
857 // This runnable starts the canceling of a worker after a self.close().
858 class CancelingRunnable final : public Runnable {
859 public:
860 CancelingRunnable() : Runnable("CancelingRunnable") {}
862 NS_IMETHOD
863 Run() override {
864 WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
865 MOZ_ASSERT(workerPrivate);
866 workerPrivate->AssertIsOnWorkerThread();
868 // Now we can cancel the this worker from the parent process.
869 RefPtr<CancelingOnParentRunnable> r =
870 new CancelingOnParentRunnable(workerPrivate);
871 r->Dispatch();
873 return NS_OK;
877 } /* anonymous namespace */
879 nsString ComputeWorkerPrivateId() {
880 nsresult rv;
881 nsCOMPtr<nsIUUIDGenerator> uuidGenerator =
882 do_GetService("@mozilla.org/uuid-generator;1", &rv);
883 MOZ_ASSERT(NS_SUCCEEDED(rv));
885 nsID uuid;
886 rv = uuidGenerator->GenerateUUIDInPlace(&uuid);
887 MOZ_ASSERT(NS_SUCCEEDED(rv));
888 char buffer[NSID_LENGTH];
889 uuid.ToProvidedString(buffer);
891 nsString id;
892 // Remove {} and the null terminator
893 id.AssignASCII(&buffer[1], NSID_LENGTH - 3);
895 return id;
898 class WorkerPrivate::EventTarget final : public nsISerialEventTarget {
899 // This mutex protects mWorkerPrivate and must be acquired *before* the
900 // WorkerPrivate's mutex whenever they must both be held.
901 mozilla::Mutex mMutex;
902 WorkerPrivate* mWorkerPrivate;
903 nsIEventTarget* mWeakNestedEventTarget;
904 nsCOMPtr<nsIEventTarget> mNestedEventTarget;
906 public:
907 explicit EventTarget(WorkerPrivate* aWorkerPrivate)
908 : mMutex("WorkerPrivate::EventTarget::mMutex"),
909 mWorkerPrivate(aWorkerPrivate),
910 mWeakNestedEventTarget(nullptr) {
911 MOZ_ASSERT(aWorkerPrivate);
914 EventTarget(WorkerPrivate* aWorkerPrivate, nsIEventTarget* aNestedEventTarget)
915 : mMutex("WorkerPrivate::EventTarget::mMutex"),
916 mWorkerPrivate(aWorkerPrivate),
917 mWeakNestedEventTarget(aNestedEventTarget),
918 mNestedEventTarget(aNestedEventTarget) {
919 MOZ_ASSERT(aWorkerPrivate);
920 MOZ_ASSERT(aNestedEventTarget);
923 void Disable() {
924 nsCOMPtr<nsIEventTarget> nestedEventTarget;
926 MutexAutoLock lock(mMutex);
928 // Note, Disable() can be called more than once safely.
929 mWorkerPrivate = nullptr;
930 mNestedEventTarget.swap(nestedEventTarget);
934 nsIEventTarget* GetWeakNestedEventTarget() const {
935 MOZ_ASSERT(mWeakNestedEventTarget);
936 return mWeakNestedEventTarget;
939 NS_DECL_THREADSAFE_ISUPPORTS
940 NS_DECL_NSIEVENTTARGET_FULL
942 private:
943 ~EventTarget() {}
946 struct WorkerPrivate::TimeoutInfo {
947 TimeoutInfo() : mId(0), mIsInterval(false), mCanceled(false) {
948 MOZ_COUNT_CTOR(mozilla::dom::WorkerPrivate::TimeoutInfo);
951 ~TimeoutInfo() { MOZ_COUNT_DTOR(mozilla::dom::WorkerPrivate::TimeoutInfo); }
953 bool operator==(const TimeoutInfo& aOther) {
954 return mTargetTime == aOther.mTargetTime;
957 bool operator<(const TimeoutInfo& aOther) {
958 return mTargetTime < aOther.mTargetTime;
961 RefPtr<TimeoutHandler> mHandler;
962 mozilla::TimeStamp mTargetTime;
963 mozilla::TimeDuration mInterval;
964 int32_t mId;
965 bool mIsInterval;
966 bool mCanceled;
969 class WorkerJSContextStats final : public JS::RuntimeStats {
970 const nsCString mRtPath;
972 public:
973 explicit WorkerJSContextStats(const nsACString& aRtPath)
974 : JS::RuntimeStats(JsWorkerMallocSizeOf), mRtPath(aRtPath) {}
976 ~WorkerJSContextStats() {
977 for (size_t i = 0; i != zoneStatsVector.length(); i++) {
978 delete static_cast<xpc::ZoneStatsExtras*>(zoneStatsVector[i].extra);
981 for (size_t i = 0; i != realmStatsVector.length(); i++) {
982 delete static_cast<xpc::RealmStatsExtras*>(realmStatsVector[i].extra);
986 const nsCString& Path() const { return mRtPath; }
988 virtual void initExtraZoneStats(JS::Zone* aZone,
989 JS::ZoneStats* aZoneStats) override {
990 MOZ_ASSERT(!aZoneStats->extra);
992 // ReportJSRuntimeExplicitTreeStats expects that
993 // aZoneStats->extra is a xpc::ZoneStatsExtras pointer.
994 xpc::ZoneStatsExtras* extras = new xpc::ZoneStatsExtras;
995 extras->pathPrefix = mRtPath;
996 extras->pathPrefix += nsPrintfCString("zone(0x%p)/", (void*)aZone);
998 MOZ_ASSERT(StartsWithExplicit(extras->pathPrefix));
1000 aZoneStats->extra = extras;
1003 virtual void initExtraRealmStats(JS::Handle<JS::Realm*> aRealm,
1004 JS::RealmStats* aRealmStats) override {
1005 MOZ_ASSERT(!aRealmStats->extra);
1007 // ReportJSRuntimeExplicitTreeStats expects that
1008 // aRealmStats->extra is a xpc::RealmStatsExtras pointer.
1009 xpc::RealmStatsExtras* extras = new xpc::RealmStatsExtras;
1011 // This is the |jsPathPrefix|. Each worker has exactly one realm.
1012 extras->jsPathPrefix.Assign(mRtPath);
1013 extras->jsPathPrefix +=
1014 nsPrintfCString("zone(0x%p)/", (void*)js::GetRealmZone(aRealm));
1015 extras->jsPathPrefix += NS_LITERAL_CSTRING("realm(web-worker)/");
1017 // This should never be used when reporting with workers (hence the "?!").
1018 extras->domPathPrefix.AssignLiteral("explicit/workers/?!/");
1020 MOZ_ASSERT(StartsWithExplicit(extras->jsPathPrefix));
1021 MOZ_ASSERT(StartsWithExplicit(extras->domPathPrefix));
1023 extras->location = nullptr;
1025 aRealmStats->extra = extras;
1029 class WorkerPrivate::MemoryReporter final : public nsIMemoryReporter {
1030 NS_DECL_THREADSAFE_ISUPPORTS
1032 friend class WorkerPrivate;
1034 SharedMutex mMutex;
1035 WorkerPrivate* mWorkerPrivate;
1037 public:
1038 explicit MemoryReporter(WorkerPrivate* aWorkerPrivate)
1039 : mMutex(aWorkerPrivate->mMutex), mWorkerPrivate(aWorkerPrivate) {
1040 aWorkerPrivate->AssertIsOnWorkerThread();
1043 NS_IMETHOD
1044 CollectReports(nsIHandleReportCallback* aHandleReport, nsISupports* aData,
1045 bool aAnonymize) override;
1047 private:
1048 class FinishCollectRunnable;
1050 class CollectReportsRunnable final : public MainThreadWorkerControlRunnable {
1051 RefPtr<FinishCollectRunnable> mFinishCollectRunnable;
1052 const bool mAnonymize;
1054 public:
1055 CollectReportsRunnable(WorkerPrivate* aWorkerPrivate,
1056 nsIHandleReportCallback* aHandleReport,
1057 nsISupports* aHandlerData, bool aAnonymize,
1058 const nsACString& aPath);
1060 private:
1061 bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override;
1063 ~CollectReportsRunnable() {
1064 if (NS_IsMainThread()) {
1065 mFinishCollectRunnable->Run();
1066 return;
1069 WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
1070 MOZ_ASSERT(workerPrivate);
1071 MOZ_ALWAYS_SUCCEEDS(workerPrivate->DispatchToMainThreadForMessaging(
1072 mFinishCollectRunnable.forget()));
1076 class FinishCollectRunnable final : public Runnable {
1077 nsCOMPtr<nsIHandleReportCallback> mHandleReport;
1078 nsCOMPtr<nsISupports> mHandlerData;
1079 size_t mPerformanceUserEntries;
1080 size_t mPerformanceResourceEntries;
1081 const bool mAnonymize;
1082 bool mSuccess;
1084 public:
1085 WorkerJSContextStats mCxStats;
1087 explicit FinishCollectRunnable(nsIHandleReportCallback* aHandleReport,
1088 nsISupports* aHandlerData, bool aAnonymize,
1089 const nsACString& aPath);
1091 NS_IMETHOD Run() override;
1093 void SetPerformanceSizes(size_t userEntries, size_t resourceEntries) {
1094 mPerformanceUserEntries = userEntries;
1095 mPerformanceResourceEntries = resourceEntries;
1098 void SetSuccess(bool success) { mSuccess = success; }
1100 private:
1101 ~FinishCollectRunnable() {
1102 // mHandleReport and mHandlerData are released on the main thread.
1103 AssertIsOnMainThread();
1106 FinishCollectRunnable(const FinishCollectRunnable&) = delete;
1107 FinishCollectRunnable& operator=(const FinishCollectRunnable&) = delete;
1108 FinishCollectRunnable& operator=(const FinishCollectRunnable&&) = delete;
1111 ~MemoryReporter() {}
1113 void Disable() {
1114 // Called from WorkerPrivate::DisableMemoryReporter.
1115 mMutex.AssertCurrentThreadOwns();
1117 NS_ASSERTION(mWorkerPrivate, "Disabled more than once!");
1118 mWorkerPrivate = nullptr;
1122 NS_IMPL_ISUPPORTS(WorkerPrivate::MemoryReporter, nsIMemoryReporter)
1124 NS_IMETHODIMP
1125 WorkerPrivate::MemoryReporter::CollectReports(
1126 nsIHandleReportCallback* aHandleReport, nsISupports* aData,
1127 bool aAnonymize) {
1128 AssertIsOnMainThread();
1130 RefPtr<CollectReportsRunnable> runnable;
1133 MutexAutoLock lock(mMutex);
1135 if (!mWorkerPrivate) {
1136 // This will effectively report 0 memory.
1137 nsCOMPtr<nsIMemoryReporterManager> manager =
1138 do_GetService("@mozilla.org/memory-reporter-manager;1");
1139 if (manager) {
1140 manager->EndReport();
1142 return NS_OK;
1145 nsAutoCString path;
1146 path.AppendLiteral("explicit/workers/workers(");
1147 if (aAnonymize && !mWorkerPrivate->Domain().IsEmpty()) {
1148 path.AppendLiteral("<anonymized-domain>)/worker(<anonymized-url>");
1149 } else {
1150 nsAutoCString escapedDomain(mWorkerPrivate->Domain());
1151 if (escapedDomain.IsEmpty()) {
1152 escapedDomain += "chrome";
1153 } else {
1154 escapedDomain.ReplaceChar('/', '\\');
1156 path.Append(escapedDomain);
1157 path.AppendLiteral(")/worker(");
1158 NS_ConvertUTF16toUTF8 escapedURL(mWorkerPrivate->ScriptURL());
1159 escapedURL.ReplaceChar('/', '\\');
1160 path.Append(escapedURL);
1162 path.AppendPrintf(", 0x%p)/", static_cast<void*>(mWorkerPrivate));
1164 runnable = new CollectReportsRunnable(mWorkerPrivate, aHandleReport, aData,
1165 aAnonymize, path);
1168 if (!runnable->Dispatch()) {
1169 return NS_ERROR_UNEXPECTED;
1172 return NS_OK;
1175 WorkerPrivate::MemoryReporter::CollectReportsRunnable::CollectReportsRunnable(
1176 WorkerPrivate* aWorkerPrivate, nsIHandleReportCallback* aHandleReport,
1177 nsISupports* aHandlerData, bool aAnonymize, const nsACString& aPath)
1178 : MainThreadWorkerControlRunnable(aWorkerPrivate),
1179 mFinishCollectRunnable(new FinishCollectRunnable(
1180 aHandleReport, aHandlerData, aAnonymize, aPath)),
1181 mAnonymize(aAnonymize) {}
1183 bool WorkerPrivate::MemoryReporter::CollectReportsRunnable::WorkerRun(
1184 JSContext* aCx, WorkerPrivate* aWorkerPrivate) {
1185 aWorkerPrivate->AssertIsOnWorkerThread();
1187 RefPtr<WorkerGlobalScope> scope = aWorkerPrivate->GlobalScope();
1188 RefPtr<Performance> performance =
1189 scope ? scope->GetPerformanceIfExists() : nullptr;
1190 if (performance) {
1191 size_t userEntries = performance->SizeOfUserEntries(JsWorkerMallocSizeOf);
1192 size_t resourceEntries =
1193 performance->SizeOfResourceEntries(JsWorkerMallocSizeOf);
1194 mFinishCollectRunnable->SetPerformanceSizes(userEntries, resourceEntries);
1197 mFinishCollectRunnable->SetSuccess(aWorkerPrivate->CollectRuntimeStats(
1198 &mFinishCollectRunnable->mCxStats, mAnonymize));
1200 return true;
1203 WorkerPrivate::MemoryReporter::FinishCollectRunnable::FinishCollectRunnable(
1204 nsIHandleReportCallback* aHandleReport, nsISupports* aHandlerData,
1205 bool aAnonymize, const nsACString& aPath)
1206 : mozilla::Runnable(
1207 "dom::WorkerPrivate::MemoryReporter::FinishCollectRunnable"),
1208 mHandleReport(aHandleReport),
1209 mHandlerData(aHandlerData),
1210 mPerformanceUserEntries(0),
1211 mPerformanceResourceEntries(0),
1212 mAnonymize(aAnonymize),
1213 mSuccess(false),
1214 mCxStats(aPath) {}
1216 NS_IMETHODIMP
1217 WorkerPrivate::MemoryReporter::FinishCollectRunnable::Run() {
1218 AssertIsOnMainThread();
1220 nsCOMPtr<nsIMemoryReporterManager> manager =
1221 do_GetService("@mozilla.org/memory-reporter-manager;1");
1223 if (!manager) return NS_OK;
1225 if (mSuccess) {
1226 xpc::ReportJSRuntimeExplicitTreeStats(
1227 mCxStats, mCxStats.Path(), mHandleReport, mHandlerData, mAnonymize);
1229 if (mPerformanceUserEntries) {
1230 nsCString path = mCxStats.Path();
1231 path.AppendLiteral("dom/performance/user-entries");
1232 mHandleReport->Callback(
1233 EmptyCString(), path, nsIMemoryReporter::KIND_HEAP,
1234 nsIMemoryReporter::UNITS_BYTES, mPerformanceUserEntries,
1235 NS_LITERAL_CSTRING("Memory used for performance user entries."),
1236 mHandlerData);
1239 if (mPerformanceResourceEntries) {
1240 nsCString path = mCxStats.Path();
1241 path.AppendLiteral("dom/performance/resource-entries");
1242 mHandleReport->Callback(
1243 EmptyCString(), path, nsIMemoryReporter::KIND_HEAP,
1244 nsIMemoryReporter::UNITS_BYTES, mPerformanceResourceEntries,
1245 NS_LITERAL_CSTRING("Memory used for performance resource entries."),
1246 mHandlerData);
1250 manager->EndReport();
1252 return NS_OK;
1255 WorkerPrivate::SyncLoopInfo::SyncLoopInfo(EventTarget* aEventTarget)
1256 : mEventTarget(aEventTarget),
1257 mCompleted(false),
1258 mResult(false)
1259 #ifdef DEBUG
1261 mHasRun(false)
1262 #endif
1266 Document* WorkerPrivate::GetDocument() const {
1267 AssertIsOnMainThread();
1268 if (mLoadInfo.mWindow) {
1269 return mLoadInfo.mWindow->GetExtantDoc();
1271 // if we don't have a document, we should query the document
1272 // from the parent in case of a nested worker
1273 WorkerPrivate* parent = mParent;
1274 while (parent) {
1275 if (parent->mLoadInfo.mWindow) {
1276 return parent->mLoadInfo.mWindow->GetExtantDoc();
1278 parent = parent->GetParent();
1280 // couldn't query a document, give up and return nullptr
1281 return nullptr;
1284 void WorkerPrivate::SetCSP(nsIContentSecurityPolicy* aCSP) {
1285 AssertIsOnMainThread();
1286 if (!aCSP) {
1287 return;
1289 aCSP->EnsureEventTarget(mMainThreadEventTarget);
1291 mLoadInfo.mCSP = aCSP;
1292 mLoadInfo.mCSPInfo = new CSPInfo();
1293 nsresult rv = CSPToCSPInfo(mLoadInfo.mCSP, mLoadInfo.mCSPInfo);
1294 if (NS_WARN_IF(NS_FAILED(rv))) {
1295 return;
1299 nsresult WorkerPrivate::SetCSPFromHeaderValues(
1300 const nsACString& aCSPHeaderValue,
1301 const nsACString& aCSPReportOnlyHeaderValue) {
1302 AssertIsOnMainThread();
1303 MOZ_DIAGNOSTIC_ASSERT(!mLoadInfo.mCSP);
1305 NS_ConvertASCIItoUTF16 cspHeaderValue(aCSPHeaderValue);
1306 NS_ConvertASCIItoUTF16 cspROHeaderValue(aCSPReportOnlyHeaderValue);
1308 nsresult rv;
1309 nsCOMPtr<nsIContentSecurityPolicy> csp = new nsCSPContext();
1311 // First, we try to query the URI from the Principal, but
1312 // in case selfURI remains empty (e.g in case the Principal
1313 // is a SystemPrincipal) then we fall back and use the
1314 // base URI as selfURI for CSP.
1315 nsCOMPtr<nsIURI> selfURI;
1316 mLoadInfo.mPrincipal->GetURI(getter_AddRefs(selfURI));
1317 if (!selfURI) {
1318 selfURI = mLoadInfo.mBaseURI;
1320 MOZ_ASSERT(selfURI, "need a self URI for CSP");
1322 rv = csp->SetRequestContextWithPrincipal(mLoadInfo.mPrincipal, selfURI,
1323 EmptyString(), 0);
1324 NS_ENSURE_SUCCESS(rv, rv);
1326 csp->EnsureEventTarget(mMainThreadEventTarget);
1328 // If there's a CSP header, apply it.
1329 if (!cspHeaderValue.IsEmpty()) {
1330 rv = CSP_AppendCSPFromHeader(csp, cspHeaderValue, false);
1331 NS_ENSURE_SUCCESS(rv, rv);
1333 // If there's a report-only CSP header, apply it.
1334 if (!cspROHeaderValue.IsEmpty()) {
1335 rv = CSP_AppendCSPFromHeader(csp, cspROHeaderValue, true);
1336 NS_ENSURE_SUCCESS(rv, rv);
1339 // Set evalAllowed, default value is set in GetAllowsEval
1340 bool evalAllowed = false;
1341 bool reportEvalViolations = false;
1342 rv = csp->GetAllowsEval(&reportEvalViolations, &evalAllowed);
1343 NS_ENSURE_SUCCESS(rv, rv);
1345 mLoadInfo.mCSP = csp;
1346 mLoadInfo.mEvalAllowed = evalAllowed;
1347 mLoadInfo.mReportCSPViolations = reportEvalViolations;
1349 mLoadInfo.mCSPInfo = new CSPInfo();
1350 rv = CSPToCSPInfo(csp, mLoadInfo.mCSPInfo);
1351 if (NS_WARN_IF(NS_FAILED(rv))) {
1352 return rv;
1354 return NS_OK;
1357 void WorkerPrivate::StoreCSPOnClient() {
1358 MOZ_ACCESS_THREAD_BOUND(mWorkerThreadAccessible, data);
1359 if (data->mClientSource && mLoadInfo.mCSPInfo) {
1360 data->mClientSource->SetCspInfo(*mLoadInfo.mCSPInfo.get());
1364 void WorkerPrivate::UpdateReferrerInfoFromHeader(
1365 const nsACString& aReferrerPolicyHeaderValue) {
1366 NS_ConvertUTF8toUTF16 headerValue(aReferrerPolicyHeaderValue);
1368 if (headerValue.IsEmpty()) {
1369 return;
1372 ReferrerPolicy policy =
1373 ReferrerInfo::ReferrerPolicyFromHeaderString(headerValue);
1374 if (policy == ReferrerPolicy::_empty) {
1375 return;
1378 nsCOMPtr<nsIReferrerInfo> referrerInfo =
1379 static_cast<ReferrerInfo*>(GetReferrerInfo())->CloneWithNewPolicy(policy);
1380 SetReferrerInfo(referrerInfo);
1383 void WorkerPrivate::Traverse(nsCycleCollectionTraversalCallback& aCb) {
1384 AssertIsOnParentThread();
1386 // The WorkerPrivate::mParentEventTargetRef has a reference to the exposed
1387 // Worker object, which is really held by the worker thread. We traverse this
1388 // reference if and only if our busy count is zero and we have not released
1389 // the main thread reference. We do not unlink it. This allows the CC to
1390 // break cycles involving the Worker and begin shutting it down (which does
1391 // happen in unlink) but ensures that the WorkerPrivate won't be deleted
1392 // before we're done shutting down the thread.
1393 if (!mBusyCount && !mMainThreadObjectsForgotten) {
1394 nsCycleCollectionTraversalCallback& cb = aCb;
1395 WorkerPrivate* tmp = this;
1396 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParentEventTargetRef);
1400 nsresult WorkerPrivate::Dispatch(already_AddRefed<WorkerRunnable> aRunnable,
1401 nsIEventTarget* aSyncLoopTarget) {
1402 // May be called on any thread!
1403 MutexAutoLock lock(mMutex);
1404 return DispatchLockHeld(std::move(aRunnable), aSyncLoopTarget, lock);
1407 nsresult WorkerPrivate::DispatchLockHeld(
1408 already_AddRefed<WorkerRunnable> aRunnable, nsIEventTarget* aSyncLoopTarget,
1409 const MutexAutoLock& aProofOfLock) {
1410 // May be called on any thread!
1411 RefPtr<WorkerRunnable> runnable(aRunnable);
1413 MOZ_ASSERT_IF(aSyncLoopTarget, mThread);
1415 if (mStatus == Dead || (!aSyncLoopTarget && ParentStatus() > Running)) {
1416 NS_WARNING(
1417 "A runnable was posted to a worker that is already shutting "
1418 "down!");
1419 return NS_ERROR_UNEXPECTED;
1422 if (runnable->IsDebuggeeRunnable() && !mDebuggerReady) {
1423 MOZ_RELEASE_ASSERT(!aSyncLoopTarget);
1424 mDelayedDebuggeeRunnables.AppendElement(runnable);
1425 return NS_OK;
1428 if (!mThread) {
1429 if (ParentStatus() == Pending || mStatus == Pending) {
1430 mPreStartRunnables.AppendElement(runnable);
1431 return NS_OK;
1434 NS_WARNING(
1435 "Using a worker event target after the thread has already"
1436 "been released!");
1437 return NS_ERROR_UNEXPECTED;
1440 nsresult rv;
1441 if (aSyncLoopTarget) {
1442 rv = aSyncLoopTarget->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
1443 } else {
1444 // WorkerDebuggeeRunnables don't need any special treatment here. True,
1445 // they should not be delivered to a frozen worker. But frozen workers
1446 // aren't drawing from the thread's main event queue anyway, only from
1447 // mControlQueue.
1448 rv = mThread->DispatchAnyThread(WorkerThreadFriendKey(), runnable.forget());
1451 if (NS_WARN_IF(NS_FAILED(rv))) {
1452 return rv;
1455 mCondVar.Notify();
1456 return NS_OK;
1459 void WorkerPrivate::EnableDebugger() {
1460 AssertIsOnParentThread();
1462 if (NS_FAILED(RegisterWorkerDebugger(this))) {
1463 NS_WARNING("Failed to register worker debugger!");
1464 return;
1468 void WorkerPrivate::DisableDebugger() {
1469 AssertIsOnParentThread();
1471 // RegisterDebuggerMainThreadRunnable might be dispatched but not executed.
1472 // Wait for its execution before unregistraion.
1473 if (!NS_IsMainThread()) {
1474 WaitForIsDebuggerRegistered(true);
1477 if (NS_FAILED(UnregisterWorkerDebugger(this))) {
1478 NS_WARNING("Failed to unregister worker debugger!");
1482 nsresult WorkerPrivate::DispatchControlRunnable(
1483 already_AddRefed<WorkerControlRunnable> aWorkerControlRunnable) {
1484 // May be called on any thread!
1485 RefPtr<WorkerControlRunnable> runnable(aWorkerControlRunnable);
1486 MOZ_ASSERT(runnable);
1489 MutexAutoLock lock(mMutex);
1491 if (mStatus == Dead) {
1492 return NS_ERROR_UNEXPECTED;
1495 // Transfer ownership to the control queue.
1496 mControlQueue.Push(runnable.forget().take());
1498 if (JSContext* cx = mJSContext) {
1499 MOZ_ASSERT(mThread);
1500 JS_RequestInterruptCallback(cx);
1503 mCondVar.Notify();
1506 return NS_OK;
1509 nsresult WorkerPrivate::DispatchDebuggerRunnable(
1510 already_AddRefed<WorkerRunnable> aDebuggerRunnable) {
1511 // May be called on any thread!
1513 RefPtr<WorkerRunnable> runnable(aDebuggerRunnable);
1515 MOZ_ASSERT(runnable);
1518 MutexAutoLock lock(mMutex);
1520 if (mStatus == Dead) {
1521 NS_WARNING(
1522 "A debugger runnable was posted to a worker that is already "
1523 "shutting down!");
1524 return NS_ERROR_UNEXPECTED;
1527 // Transfer ownership to the debugger queue.
1528 mDebuggerQueue.Push(runnable.forget().take());
1530 mCondVar.Notify();
1533 return NS_OK;
1536 already_AddRefed<WorkerRunnable> WorkerPrivate::MaybeWrapAsWorkerRunnable(
1537 already_AddRefed<nsIRunnable> aRunnable) {
1538 // May be called on any thread!
1540 nsCOMPtr<nsIRunnable> runnable(aRunnable);
1541 MOZ_ASSERT(runnable);
1543 RefPtr<WorkerRunnable> workerRunnable =
1544 WorkerRunnable::FromRunnable(runnable);
1545 if (workerRunnable) {
1546 return workerRunnable.forget();
1549 nsCOMPtr<nsICancelableRunnable> cancelable = do_QueryInterface(runnable);
1550 if (!cancelable) {
1551 MOZ_CRASH("All runnables destined for a worker thread must be cancelable!");
1554 workerRunnable = new ExternalRunnableWrapper(this, runnable);
1555 return workerRunnable.forget();
1558 bool WorkerPrivate::Start() {
1559 // May be called on any thread!
1561 MutexAutoLock lock(mMutex);
1562 NS_ASSERTION(mParentStatus != Running, "How can this be?!");
1564 if (mParentStatus == Pending) {
1565 mParentStatus = Running;
1566 return true;
1570 return false;
1573 // aCx is null when called from the finalizer
1574 bool WorkerPrivate::Notify(WorkerStatus aStatus) {
1575 AssertIsOnParentThread();
1577 bool pending;
1579 MutexAutoLock lock(mMutex);
1581 if (mParentStatus >= aStatus) {
1582 return true;
1585 pending = mParentStatus == Pending;
1586 mParentStatus = aStatus;
1589 if (pending) {
1590 #ifdef DEBUG
1592 // Fake a thread here just so that our assertions don't go off for no
1593 // reason.
1594 nsIThread* currentThread = NS_GetCurrentThread();
1595 MOZ_ASSERT(currentThread);
1597 MOZ_ASSERT(!mPRThread);
1598 mPRThread = PRThreadFromThread(currentThread);
1599 MOZ_ASSERT(mPRThread);
1601 #endif
1603 // Worker never got a chance to run, go ahead and delete it.
1604 ScheduleDeletion(WorkerPrivate::WorkerNeverRan);
1605 return true;
1608 // No Canceling timeout is needed.
1609 if (mCancelingTimer) {
1610 mCancelingTimer->Cancel();
1611 mCancelingTimer = nullptr;
1614 RefPtr<NotifyRunnable> runnable = new NotifyRunnable(this, aStatus);
1615 return runnable->Dispatch();
1618 bool WorkerPrivate::Freeze(nsPIDOMWindowInner* aWindow) {
1619 AssertIsOnParentThread();
1621 mParentFrozen = true;
1623 // WorkerDebuggeeRunnables sent from a worker to content must not be delivered
1624 // while the worker is frozen.
1626 // Since a top-level worker and all its children share the same
1627 // mMainThreadDebuggeeEventTarget, it's sufficient to do this only in the
1628 // top-level worker.
1629 if (aWindow) {
1630 // This is called from WorkerPrivate construction, and We may not have
1631 // allocated mMainThreadDebuggeeEventTarget yet.
1632 if (mMainThreadDebuggeeEventTarget) {
1633 // Pausing a ThrottledEventQueue is infallible.
1634 MOZ_ALWAYS_SUCCEEDS(mMainThreadDebuggeeEventTarget->SetIsPaused(true));
1639 MutexAutoLock lock(mMutex);
1641 if (mParentStatus >= Canceling) {
1642 return true;
1646 DisableDebugger();
1648 RefPtr<FreezeRunnable> runnable = new FreezeRunnable(this);
1649 if (!runnable->Dispatch()) {
1650 return false;
1653 return true;
1656 bool WorkerPrivate::Thaw(nsPIDOMWindowInner* aWindow) {
1657 AssertIsOnParentThread();
1658 MOZ_ASSERT(mParentFrozen);
1660 mParentFrozen = false;
1662 // Delivery of WorkerDebuggeeRunnables to the window may resume.
1664 // Since a top-level worker and all its children share the same
1665 // mMainThreadDebuggeeEventTarget, it's sufficient to do this only in the
1666 // top-level worker.
1667 if (aWindow) {
1668 // Since the worker is no longer frozen, only a paused parent window should
1669 // require the queue to remain paused.
1671 // This can only fail if the ThrottledEventQueue cannot dispatch its
1672 // executor to the main thread, in which case the main thread was never
1673 // going to draw runnables from it anyway, so the failure doesn't matter.
1674 Unused << mMainThreadDebuggeeEventTarget->SetIsPaused(
1675 IsParentWindowPaused());
1679 MutexAutoLock lock(mMutex);
1681 if (mParentStatus >= Canceling) {
1682 return true;
1686 EnableDebugger();
1688 RefPtr<ThawRunnable> runnable = new ThawRunnable(this);
1689 if (!runnable->Dispatch()) {
1690 return false;
1693 return true;
1696 void WorkerPrivate::ParentWindowPaused() {
1697 AssertIsOnMainThread();
1698 MOZ_ASSERT(!mParentWindowPaused);
1699 mParentWindowPaused = true;
1701 // This is called from WorkerPrivate construction, and we may not have
1702 // allocated mMainThreadDebuggeeEventTarget yet.
1703 if (mMainThreadDebuggeeEventTarget) {
1704 // Pausing a ThrottledEventQueue is infallible.
1705 MOZ_ALWAYS_SUCCEEDS(mMainThreadDebuggeeEventTarget->SetIsPaused(true));
1709 void WorkerPrivate::ParentWindowResumed() {
1710 AssertIsOnMainThread();
1712 MOZ_ASSERT(mParentWindowPaused);
1713 mParentWindowPaused = false;
1716 MutexAutoLock lock(mMutex);
1718 if (mParentStatus >= Canceling) {
1719 return;
1723 // Since the window is no longer paused, the queue should only remain paused
1724 // if the worker is frozen.
1726 // This can only fail if the ThrottledEventQueue cannot dispatch its executor
1727 // to the main thread, in which case the main thread was never going to draw
1728 // runnables from it anyway, so the failure doesn't matter.
1729 Unused << mMainThreadDebuggeeEventTarget->SetIsPaused(IsFrozen());
1732 void WorkerPrivate::PropagateFirstPartyStorageAccessGranted() {
1733 AssertIsOnParentThread();
1736 MutexAutoLock lock(mMutex);
1738 if (mParentStatus >= Canceling) {
1739 return;
1743 RefPtr<PropagateFirstPartyStorageAccessGrantedRunnable> runnable =
1744 new PropagateFirstPartyStorageAccessGrantedRunnable(this);
1745 Unused << NS_WARN_IF(!runnable->Dispatch());
1748 bool WorkerPrivate::Close() {
1749 mMutex.AssertCurrentThreadOwns();
1750 if (mParentStatus < Closing) {
1751 mParentStatus = Closing;
1754 return true;
1757 bool WorkerPrivate::ModifyBusyCount(bool aIncrease) {
1758 AssertIsOnParentThread();
1760 MOZ_ASSERT(aIncrease || mBusyCount, "Mismatched busy count mods!");
1762 if (aIncrease) {
1763 mBusyCount++;
1764 return true;
1767 if (--mBusyCount == 0) {
1768 bool shouldCancel;
1770 MutexAutoLock lock(mMutex);
1771 shouldCancel = mParentStatus == Canceling;
1774 if (shouldCancel && !Cancel()) {
1775 return false;
1779 return true;
1782 bool WorkerPrivate::ProxyReleaseMainThreadObjects() {
1783 AssertIsOnParentThread();
1784 MOZ_ASSERT(!mMainThreadObjectsForgotten);
1786 nsCOMPtr<nsILoadGroup> loadGroupToCancel;
1787 // If we're not overriden, then do nothing here. Let the load group get
1788 // handled in ForgetMainThreadObjects().
1789 if (mLoadInfo.mInterfaceRequestor) {
1790 mLoadInfo.mLoadGroup.swap(loadGroupToCancel);
1793 bool result =
1794 mLoadInfo.ProxyReleaseMainThreadObjects(this, loadGroupToCancel);
1796 mMainThreadObjectsForgotten = true;
1798 return result;
1801 void WorkerPrivate::UpdateContextOptions(
1802 const JS::ContextOptions& aContextOptions) {
1803 AssertIsOnParentThread();
1806 MutexAutoLock lock(mMutex);
1807 mJSSettings.contextOptions = aContextOptions;
1810 RefPtr<UpdateContextOptionsRunnable> runnable =
1811 new UpdateContextOptionsRunnable(this, aContextOptions);
1812 if (!runnable->Dispatch()) {
1813 NS_WARNING("Failed to update worker context options!");
1817 void WorkerPrivate::UpdateLanguages(const nsTArray<nsString>& aLanguages) {
1818 AssertIsOnParentThread();
1820 RefPtr<UpdateLanguagesRunnable> runnable =
1821 new UpdateLanguagesRunnable(this, aLanguages);
1822 if (!runnable->Dispatch()) {
1823 NS_WARNING("Failed to update worker languages!");
1827 void WorkerPrivate::UpdateJSWorkerMemoryParameter(JSGCParamKey aKey,
1828 uint32_t aValue) {
1829 AssertIsOnParentThread();
1831 bool found = false;
1834 MutexAutoLock lock(mMutex);
1835 found = mJSSettings.ApplyGCSetting(aKey, aValue);
1838 if (found) {
1839 RefPtr<UpdateJSWorkerMemoryParameterRunnable> runnable =
1840 new UpdateJSWorkerMemoryParameterRunnable(this, aKey, aValue);
1841 if (!runnable->Dispatch()) {
1842 NS_WARNING("Failed to update memory parameter!");
1847 #ifdef JS_GC_ZEAL
1848 void WorkerPrivate::UpdateGCZeal(uint8_t aGCZeal, uint32_t aFrequency) {
1849 AssertIsOnParentThread();
1852 MutexAutoLock lock(mMutex);
1853 mJSSettings.gcZeal = aGCZeal;
1854 mJSSettings.gcZealFrequency = aFrequency;
1857 RefPtr<UpdateGCZealRunnable> runnable =
1858 new UpdateGCZealRunnable(this, aGCZeal, aFrequency);
1859 if (!runnable->Dispatch()) {
1860 NS_WARNING("Failed to update worker gczeal!");
1863 #endif
1865 void WorkerPrivate::SetLowMemoryState(bool aState) {
1866 AssertIsOnParentThread();
1868 RefPtr<SetLowMemoryStateRunnable> runnable =
1869 new SetLowMemoryStateRunnable(this, aState);
1870 if (!runnable->Dispatch()) {
1871 NS_WARNING("Failed to set low memory state!");
1875 void WorkerPrivate::GarbageCollect(bool aShrinking) {
1876 AssertIsOnParentThread();
1878 RefPtr<GarbageCollectRunnable> runnable = new GarbageCollectRunnable(
1879 this, aShrinking, /* collectChildren = */ true);
1880 if (!runnable->Dispatch()) {
1881 NS_WARNING("Failed to GC worker!");
1885 void WorkerPrivate::CycleCollect(bool aDummy) {
1886 AssertIsOnParentThread();
1888 RefPtr<CycleCollectRunnable> runnable =
1889 new CycleCollectRunnable(this, /* collectChildren = */ true);
1890 if (!runnable->Dispatch()) {
1891 NS_WARNING("Failed to CC worker!");
1895 void WorkerPrivate::OfflineStatusChangeEvent(bool aIsOffline) {
1896 AssertIsOnParentThread();
1898 RefPtr<OfflineStatusChangeRunnable> runnable =
1899 new OfflineStatusChangeRunnable(this, aIsOffline);
1900 if (!runnable->Dispatch()) {
1901 NS_WARNING("Failed to dispatch offline status change event!");
1905 void WorkerPrivate::OfflineStatusChangeEventInternal(bool aIsOffline) {
1906 MOZ_ACCESS_THREAD_BOUND(mWorkerThreadAccessible, data);
1908 // The worker is already in this state. No need to dispatch an event.
1909 if (data->mOnLine == !aIsOffline) {
1910 return;
1913 for (uint32_t index = 0; index < data->mChildWorkers.Length(); ++index) {
1914 data->mChildWorkers[index]->OfflineStatusChangeEvent(aIsOffline);
1917 data->mOnLine = !aIsOffline;
1918 WorkerGlobalScope* globalScope = GlobalScope();
1919 RefPtr<WorkerNavigator> nav = globalScope->GetExistingNavigator();
1920 if (nav) {
1921 nav->SetOnLine(data->mOnLine);
1924 nsString eventType;
1925 if (aIsOffline) {
1926 eventType.AssignLiteral("offline");
1927 } else {
1928 eventType.AssignLiteral("online");
1931 RefPtr<Event> event = NS_NewDOMEvent(globalScope, nullptr, nullptr);
1933 event->InitEvent(eventType, false, false);
1934 event->SetTrusted(true);
1936 globalScope->DispatchEvent(*event);
1939 void WorkerPrivate::MemoryPressure(bool aDummy) {
1940 AssertIsOnParentThread();
1942 RefPtr<MemoryPressureRunnable> runnable = new MemoryPressureRunnable(this);
1943 Unused << NS_WARN_IF(!runnable->Dispatch());
1946 void WorkerPrivate::WorkerScriptLoaded() {
1947 AssertIsOnMainThread();
1949 if (IsSharedWorker() || IsServiceWorker()) {
1950 // No longer need to hold references to the window or document we came from.
1951 mLoadInfo.mWindow = nullptr;
1952 mLoadInfo.mScriptContext = nullptr;
1956 void WorkerPrivate::SetBaseURI(nsIURI* aBaseURI) {
1957 AssertIsOnMainThread();
1959 if (!mLoadInfo.mBaseURI) {
1960 NS_ASSERTION(GetParent(), "Shouldn't happen without a parent!");
1961 mLoadInfo.mResolvedScriptURI = aBaseURI;
1964 mLoadInfo.mBaseURI = aBaseURI;
1966 if (NS_FAILED(aBaseURI->GetSpec(mLocationInfo.mHref))) {
1967 mLocationInfo.mHref.Truncate();
1970 mLocationInfo.mHostname.Truncate();
1971 nsContentUtils::GetHostOrIPv6WithBrackets(aBaseURI, mLocationInfo.mHostname);
1973 nsCOMPtr<nsIURL> url(do_QueryInterface(aBaseURI));
1974 if (!url || NS_FAILED(url->GetFilePath(mLocationInfo.mPathname))) {
1975 mLocationInfo.mPathname.Truncate();
1978 nsCString temp;
1980 if (url && NS_SUCCEEDED(url->GetQuery(temp)) && !temp.IsEmpty()) {
1981 mLocationInfo.mSearch.Assign('?');
1982 mLocationInfo.mSearch.Append(temp);
1985 if (NS_SUCCEEDED(aBaseURI->GetRef(temp)) && !temp.IsEmpty()) {
1986 if (mLocationInfo.mHash.IsEmpty()) {
1987 mLocationInfo.mHash.Assign('#');
1988 mLocationInfo.mHash.Append(temp);
1992 if (NS_SUCCEEDED(aBaseURI->GetScheme(mLocationInfo.mProtocol))) {
1993 mLocationInfo.mProtocol.Append(':');
1994 } else {
1995 mLocationInfo.mProtocol.Truncate();
1998 int32_t port;
1999 if (NS_SUCCEEDED(aBaseURI->GetPort(&port)) && port != -1) {
2000 mLocationInfo.mPort.AppendInt(port);
2002 nsAutoCString host(mLocationInfo.mHostname);
2003 host.Append(':');
2004 host.Append(mLocationInfo.mPort);
2006 mLocationInfo.mHost.Assign(host);
2007 } else {
2008 mLocationInfo.mHost.Assign(mLocationInfo.mHostname);
2011 nsContentUtils::GetUTFOrigin(aBaseURI, mLocationInfo.mOrigin);
2014 nsresult WorkerPrivate::SetPrincipalsAndCSPOnMainThread(
2015 nsIPrincipal* aPrincipal, nsIPrincipal* aStoragePrincipal,
2016 nsILoadGroup* aLoadGroup, nsIContentSecurityPolicy* aCsp) {
2017 return mLoadInfo.SetPrincipalsAndCSPOnMainThread(
2018 aPrincipal, aStoragePrincipal, aLoadGroup, aCsp);
2021 nsresult WorkerPrivate::SetPrincipalsAndCSPFromChannel(nsIChannel* aChannel) {
2022 return mLoadInfo.SetPrincipalsAndCSPFromChannel(aChannel);
2025 bool WorkerPrivate::FinalChannelPrincipalIsValid(nsIChannel* aChannel) {
2026 return mLoadInfo.FinalChannelPrincipalIsValid(aChannel);
2029 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
2030 bool WorkerPrivate::PrincipalURIMatchesScriptURL() {
2031 return mLoadInfo.PrincipalURIMatchesScriptURL();
2033 #endif
2035 void WorkerPrivate::UpdateOverridenLoadGroup(nsILoadGroup* aBaseLoadGroup) {
2036 AssertIsOnMainThread();
2038 // The load group should have been overriden at init time.
2039 mLoadInfo.mInterfaceRequestor->MaybeAddBrowserChild(aBaseLoadGroup);
2042 #ifdef DEBUG
2044 void WorkerPrivate::AssertIsOnParentThread() const {
2045 if (GetParent()) {
2046 GetParent()->AssertIsOnWorkerThread();
2047 } else {
2048 AssertIsOnMainThread();
2052 void WorkerPrivate::AssertInnerWindowIsCorrect() const {
2053 AssertIsOnParentThread();
2055 // Only care about top level workers from windows.
2056 if (mParent || !mLoadInfo.mWindow) {
2057 return;
2060 AssertIsOnMainThread();
2062 nsPIDOMWindowOuter* outer = mLoadInfo.mWindow->GetOuterWindow();
2063 NS_ASSERTION(outer && outer->GetCurrentInnerWindow() == mLoadInfo.mWindow,
2064 "Inner window no longer correct!");
2067 #endif
2069 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
2070 bool WorkerPrivate::PrincipalIsValid() const {
2071 return mLoadInfo.PrincipalIsValid();
2073 #endif
2075 WorkerPrivate::WorkerThreadAccessible::WorkerThreadAccessible(
2076 WorkerPrivate* const aParent)
2077 : mNumWorkerRefsPreventingShutdownStart(0),
2078 mDebuggerEventLoopLevel(0),
2079 mErrorHandlerRecursionCount(0),
2080 mNextTimeoutId(1),
2081 mFrozen(false),
2082 mTimerRunning(false),
2083 mRunningExpiredTimeouts(false),
2084 mPeriodicGCTimerRunning(false),
2085 mIdleGCTimerRunning(false),
2086 mOnLine(aParent ? aParent->OnLine() : !NS_IsOffline()) {}
2088 namespace {
2090 bool IsNewWorkerSecureContext(const WorkerPrivate* const aParent,
2091 const WorkerType aWorkerType,
2092 const WorkerLoadInfo& aLoadInfo) {
2093 if (aParent) {
2094 return aParent->IsSecureContext();
2097 // Our secure context state depends on the kind of worker we have.
2099 if (aLoadInfo.mPrincipalIsSystem) {
2100 return true;
2103 if (aWorkerType == WorkerTypeService) {
2104 return true;
2107 if (aLoadInfo.mSecureContext != WorkerLoadInfo::eNotSet) {
2108 return aLoadInfo.mSecureContext == WorkerLoadInfo::eSecureContext;
2111 MOZ_ASSERT_UNREACHABLE(
2112 "non-chrome worker that is not a service worker "
2113 "that has no parent and no associated window");
2115 return false;
2118 } // namespace
2120 WorkerPrivate::WorkerPrivate(
2121 WorkerPrivate* aParent, const nsAString& aScriptURL, bool aIsChromeWorker,
2122 WorkerType aWorkerType, const nsAString& aWorkerName,
2123 const nsACString& aServiceWorkerScope, WorkerLoadInfo& aLoadInfo,
2124 nsString&& aId, const nsID& aAgentClusterId,
2125 const nsILoadInfo::CrossOriginOpenerPolicy aAgentClusterOpenerPolicy)
2126 : mMutex("WorkerPrivate Mutex"),
2127 mCondVar(mMutex, "WorkerPrivate CondVar"),
2128 mParent(aParent),
2129 mScriptURL(aScriptURL),
2130 mWorkerName(aWorkerName),
2131 mWorkerType(aWorkerType),
2132 mLoadInfo(std::move(aLoadInfo)),
2133 mDebugger(nullptr),
2134 mJSContext(nullptr),
2135 mPRThread(nullptr),
2136 mWorkerControlEventTarget(new WorkerEventTarget(
2137 this, WorkerEventTarget::Behavior::ControlOnly)),
2138 mWorkerHybridEventTarget(
2139 new WorkerEventTarget(this, WorkerEventTarget::Behavior::Hybrid)),
2140 mParentStatus(Pending),
2141 mStatus(Pending),
2142 mBusyCount(0),
2143 mLoadingWorkerScript(false),
2144 mCreationTimeStamp(TimeStamp::Now()),
2145 mCreationTimeHighRes((double)PR_Now() / PR_USEC_PER_MSEC),
2146 mReportedUseCounters(false),
2147 mAgentClusterId(aAgentClusterId),
2148 mWorkerThreadAccessible(aParent),
2149 mPostSyncLoopOperations(0),
2150 mParentWindowPaused(false),
2151 mCancelAllPendingRunnables(false),
2152 mWorkerScriptExecutedSuccessfully(false),
2153 mFetchHandlerWasAdded(false),
2154 mMainThreadObjectsForgotten(false),
2155 mIsChromeWorker(aIsChromeWorker),
2156 mParentFrozen(false),
2157 mIsSecureContext(
2158 IsNewWorkerSecureContext(mParent, mWorkerType, mLoadInfo)),
2159 mDebuggerRegistered(false),
2160 mDebuggerReady(true),
2161 mIsInAutomation(false),
2162 mPerformanceCounter(nullptr),
2163 mId(std::move(aId)),
2164 mAgentClusterOpenerPolicy(aAgentClusterOpenerPolicy) {
2165 MOZ_ASSERT_IF(!IsDedicatedWorker(), NS_IsMainThread());
2167 if (aParent) {
2168 aParent->AssertIsOnWorkerThread();
2170 // Note that this copies our parent's secure context state into mJSSettings.
2171 aParent->CopyJSSettings(mJSSettings);
2173 MOZ_ASSERT_IF(mIsChromeWorker, mIsSecureContext);
2175 mIsInAutomation = aParent->IsInAutomation();
2177 MOZ_ASSERT(IsDedicatedWorker());
2179 if (aParent->mParentFrozen) {
2180 Freeze(nullptr);
2182 } else {
2183 AssertIsOnMainThread();
2185 RuntimeService::GetDefaultJSSettings(mJSSettings);
2187 mJSSettings.chrome.realmOptions.behaviors().setClampAndJitterTime(
2188 !UsesSystemPrincipal());
2189 mJSSettings.content.realmOptions.behaviors().setClampAndJitterTime(
2190 !UsesSystemPrincipal());
2192 mJSSettings.chrome.realmOptions.creationOptions().setToSourceEnabled(
2193 UsesSystemPrincipal());
2194 mJSSettings.content.realmOptions.creationOptions().setToSourceEnabled(
2195 UsesSystemPrincipal());
2197 if (mIsSecureContext) {
2198 mJSSettings.chrome.realmOptions.creationOptions().setSecureContext(true);
2199 mJSSettings.content.realmOptions.creationOptions().setSecureContext(true);
2202 mIsInAutomation = xpc::IsInAutomation();
2204 // Our parent can get suspended after it initiates the async creation
2205 // of a new worker thread. In this case suspend the new worker as well.
2206 if (mLoadInfo.mWindow && mLoadInfo.mWindow->IsSuspended()) {
2207 ParentWindowPaused();
2210 if (mLoadInfo.mWindow && mLoadInfo.mWindow->IsFrozen()) {
2211 Freeze(mLoadInfo.mWindow);
2215 nsCOMPtr<nsISerialEventTarget> target;
2217 // A child worker just inherits the parent workers ThrottledEventQueue
2218 // and main thread target for now. This is mainly due to the restriction
2219 // that ThrottledEventQueue can only be created on the main thread at the
2220 // moment.
2221 if (aParent) {
2222 mMainThreadEventTargetForMessaging =
2223 aParent->mMainThreadEventTargetForMessaging;
2224 mMainThreadEventTarget = aParent->mMainThreadEventTarget;
2225 mMainThreadDebuggeeEventTarget = aParent->mMainThreadDebuggeeEventTarget;
2226 return;
2229 MOZ_ASSERT(NS_IsMainThread());
2230 target =
2231 GetWindow() ? GetWindow()->EventTargetFor(TaskCategory::Worker) : nullptr;
2233 if (!target) {
2234 target = GetMainThreadSerialEventTarget();
2235 MOZ_DIAGNOSTIC_ASSERT(target);
2238 // Throttle events to the main thread using a ThrottledEventQueue specific to
2239 // this tree of worker threads.
2240 mMainThreadEventTargetForMessaging =
2241 ThrottledEventQueue::Create(target, "Worker queue for messaging");
2242 if (StaticPrefs::dom_worker_use_medium_high_event_queue()) {
2243 mMainThreadEventTarget = ThrottledEventQueue::Create(
2244 GetMainThreadSerialEventTarget(), "Worker queue",
2245 nsIRunnablePriority::PRIORITY_MEDIUMHIGH);
2246 } else {
2247 mMainThreadEventTarget = mMainThreadEventTargetForMessaging;
2249 mMainThreadDebuggeeEventTarget =
2250 ThrottledEventQueue::Create(target, "Worker debuggee queue");
2251 if (IsParentWindowPaused() || IsFrozen()) {
2252 MOZ_ALWAYS_SUCCEEDS(mMainThreadDebuggeeEventTarget->SetIsPaused(true));
2256 WorkerPrivate::~WorkerPrivate() {
2257 DropJSObjects(this);
2259 mWorkerControlEventTarget->ForgetWorkerPrivate(this);
2261 // We force the hybrid event target to forget the thread when we
2262 // enter the Killing state, but we do it again here to be safe.
2263 // Its possible that we may be created and destroyed without progressing
2264 // to Killing via some obscure code path.
2265 mWorkerHybridEventTarget->ForgetWorkerPrivate(this);
2268 // static
2269 already_AddRefed<WorkerPrivate> WorkerPrivate::Constructor(
2270 JSContext* aCx, const nsAString& aScriptURL, bool aIsChromeWorker,
2271 WorkerType aWorkerType, const nsAString& aWorkerName,
2272 const nsACString& aServiceWorkerScope, WorkerLoadInfo* aLoadInfo,
2273 ErrorResult& aRv, nsString aId) {
2274 WorkerPrivate* parent =
2275 NS_IsMainThread() ? nullptr : GetCurrentThreadWorkerPrivate();
2277 // If this is a sub-worker, we need to keep the parent worker alive until this
2278 // one is registered.
2279 RefPtr<StrongWorkerRef> workerRef;
2280 if (parent) {
2281 parent->AssertIsOnWorkerThread();
2283 workerRef = StrongWorkerRef::Create(parent, "WorkerPrivate::Constructor");
2284 if (NS_WARN_IF(!workerRef)) {
2285 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
2286 return nullptr;
2288 } else {
2289 AssertIsOnMainThread();
2292 Maybe<WorkerLoadInfo> stackLoadInfo;
2293 if (!aLoadInfo) {
2294 stackLoadInfo.emplace();
2296 nsresult rv =
2297 GetLoadInfo(aCx, nullptr, parent, aScriptURL, aIsChromeWorker,
2298 InheritLoadGroup, aWorkerType, stackLoadInfo.ptr());
2299 aRv.MightThrowJSException();
2300 if (NS_FAILED(rv)) {
2301 workerinternals::ReportLoadError(aRv, rv, aScriptURL);
2302 return nullptr;
2305 aLoadInfo = stackLoadInfo.ptr();
2308 // NB: This has to be done before creating the WorkerPrivate, because it will
2309 // attempt to use static variables that are initialized in the RuntimeService
2310 // constructor.
2311 RuntimeService* runtimeService;
2313 if (!parent) {
2314 runtimeService = RuntimeService::GetOrCreateService();
2315 if (!runtimeService) {
2316 aRv.Throw(NS_ERROR_FAILURE);
2317 return nullptr;
2319 } else {
2320 runtimeService = RuntimeService::GetService();
2323 MOZ_ASSERT(runtimeService);
2325 nsILoadInfo::CrossOriginOpenerPolicy agentClusterCoop =
2326 nsILoadInfo::OPENER_POLICY_NULL;
2327 nsID agentClusterId;
2328 if (parent) {
2329 MOZ_ASSERT(aWorkerType == WorkerType::WorkerTypeDedicated);
2331 agentClusterId = parent->AgentClusterId();
2332 agentClusterCoop = parent->mAgentClusterOpenerPolicy;
2333 } else {
2334 AssertIsOnMainThread();
2336 if (aWorkerType == WorkerType::WorkerTypeService ||
2337 aWorkerType == WorkerType::WorkerTypeShared) {
2338 agentClusterId = aLoadInfo->mAgentClusterId;
2339 } else if (aLoadInfo->mWindow) {
2340 Document* doc = aLoadInfo->mWindow->GetExtantDoc();
2341 MOZ_DIAGNOSTIC_ASSERT(doc);
2342 RefPtr<DocGroup> docGroup = doc->GetDocGroup();
2344 agentClusterId = docGroup ? docGroup->AgentClusterId()
2345 : nsContentUtils::GenerateUUID();
2347 BrowsingContext* bc = aLoadInfo->mWindow->GetBrowsingContext();
2348 MOZ_DIAGNOSTIC_ASSERT(bc);
2349 agentClusterCoop = bc->Top()->GetOpenerPolicy();
2350 } else {
2351 // If the window object was failed to be set into the WorkerLoadInfo, we
2352 // make the worker into another agent cluster group instead of failures.
2353 agentClusterId = nsContentUtils::GenerateUUID();
2357 RefPtr<WorkerPrivate> worker =
2358 new WorkerPrivate(parent, aScriptURL, aIsChromeWorker, aWorkerType,
2359 aWorkerName, aServiceWorkerScope, *aLoadInfo,
2360 std::move(aId), agentClusterId, agentClusterCoop);
2362 // Gecko contexts always have an explicitly-set default locale (set by
2363 // XPJSRuntime::Initialize for the main thread, set by
2364 // WorkerThreadPrimaryRunnable::Run for workers just before running worker
2365 // code), so this is never SpiderMonkey's builtin default locale.
2366 JS::UniqueChars defaultLocale = JS_GetDefaultLocale(aCx);
2367 if (NS_WARN_IF(!defaultLocale)) {
2368 aRv.Throw(NS_ERROR_UNEXPECTED);
2369 return nullptr;
2372 worker->mDefaultLocale = std::move(defaultLocale);
2374 if (!runtimeService->RegisterWorker(worker)) {
2375 aRv.Throw(NS_ERROR_UNEXPECTED);
2376 return nullptr;
2379 worker->EnableDebugger();
2381 MOZ_DIAGNOSTIC_ASSERT(worker->PrincipalIsValid());
2383 UniquePtr<SerializedStackHolder> stack;
2384 if (worker->IsWatchedByDevtools()) {
2385 stack = GetCurrentStackForNetMonitor(aCx);
2388 RefPtr<CompileScriptRunnable> compiler =
2389 new CompileScriptRunnable(worker, std::move(stack), aScriptURL);
2390 if (!compiler->Dispatch()) {
2391 aRv.Throw(NS_ERROR_UNEXPECTED);
2392 return nullptr;
2395 worker->mSelfRef = worker;
2397 return worker.forget();
2400 nsresult WorkerPrivate::SetIsDebuggerReady(bool aReady) {
2401 AssertIsOnMainThread();
2402 MutexAutoLock lock(mMutex);
2404 if (mDebuggerReady == aReady) {
2405 return NS_OK;
2408 if (!aReady && mDebuggerRegistered) {
2409 // The debugger can only be marked as not ready during registration.
2410 return NS_ERROR_FAILURE;
2413 mDebuggerReady = aReady;
2415 if (aReady && mDebuggerRegistered) {
2416 // Dispatch all the delayed runnables without releasing the lock, to ensure
2417 // that the order in which debuggee runnables execute is the same as the
2418 // order in which they were originally dispatched.
2419 auto pending = std::move(mDelayedDebuggeeRunnables);
2420 for (uint32_t i = 0; i < pending.Length(); i++) {
2421 RefPtr<WorkerRunnable> runnable = pending[i].forget();
2422 nsresult rv = DispatchLockHeld(runnable.forget(), nullptr, lock);
2423 NS_ENSURE_SUCCESS(rv, rv);
2425 MOZ_RELEASE_ASSERT(mDelayedDebuggeeRunnables.IsEmpty());
2428 return NS_OK;
2431 // static
2432 nsresult WorkerPrivate::GetLoadInfo(JSContext* aCx, nsPIDOMWindowInner* aWindow,
2433 WorkerPrivate* aParent,
2434 const nsAString& aScriptURL,
2435 bool aIsChromeWorker,
2436 LoadGroupBehavior aLoadGroupBehavior,
2437 WorkerType aWorkerType,
2438 WorkerLoadInfo* aLoadInfo) {
2439 using namespace mozilla::dom::workerinternals;
2441 MOZ_ASSERT(aCx);
2442 MOZ_ASSERT_IF(NS_IsMainThread(),
2443 aCx == nsContentUtils::GetCurrentJSContext());
2445 if (aWindow) {
2446 AssertIsOnMainThread();
2449 WorkerLoadInfo loadInfo;
2450 nsresult rv;
2452 if (aParent) {
2453 aParent->AssertIsOnWorkerThread();
2455 // If the parent is going away give up now.
2456 WorkerStatus parentStatus;
2458 MutexAutoLock lock(aParent->mMutex);
2459 parentStatus = aParent->mStatus;
2462 if (parentStatus > Running) {
2463 return NS_ERROR_FAILURE;
2466 // Passing a pointer to our stack loadInfo is safe here because this
2467 // method uses a sync runnable to get the channel from the main thread.
2468 rv = ChannelFromScriptURLWorkerThread(aCx, aParent, aScriptURL, loadInfo);
2469 if (NS_FAILED(rv)) {
2470 MOZ_ALWAYS_TRUE(loadInfo.ProxyReleaseMainThreadObjects(aParent));
2471 return rv;
2474 // Now that we've spun the loop there's no guarantee that our parent is
2475 // still alive. We may have received control messages initiating shutdown.
2477 MutexAutoLock lock(aParent->mMutex);
2478 parentStatus = aParent->mStatus;
2481 if (parentStatus > Running) {
2482 MOZ_ALWAYS_TRUE(loadInfo.ProxyReleaseMainThreadObjects(aParent));
2483 return NS_ERROR_FAILURE;
2486 loadInfo.mDomain = aParent->Domain();
2487 loadInfo.mFromWindow = aParent->IsFromWindow();
2488 loadInfo.mWindowID = aParent->WindowID();
2489 loadInfo.mStorageAccess = aParent->StorageAccess();
2490 loadInfo.mOriginAttributes = aParent->GetOriginAttributes();
2491 loadInfo.mServiceWorkersTestingInWindow =
2492 aParent->ServiceWorkersTestingInWindow();
2493 loadInfo.mParentController = aParent->GetController();
2494 loadInfo.mWatchedByDevtools = aParent->IsWatchedByDevtools();
2495 } else {
2496 AssertIsOnMainThread();
2498 // Make sure that the IndexedDatabaseManager is set up
2499 Unused << NS_WARN_IF(!IndexedDatabaseManager::GetOrCreate());
2501 nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
2502 MOZ_ASSERT(ssm);
2504 bool isChrome = nsContentUtils::IsSystemCaller(aCx);
2506 // First check to make sure the caller has permission to make a privileged
2507 // worker if they called the ChromeWorker/ChromeSharedWorker constructor.
2508 if (aIsChromeWorker && !isChrome) {
2509 return NS_ERROR_DOM_SECURITY_ERR;
2512 // Chrome callers (whether creating a ChromeWorker or Worker) always get the
2513 // system principal here as they're allowed to load anything. The script
2514 // loader will refuse to run any script that does not also have the system
2515 // principal.
2516 if (isChrome) {
2517 rv = ssm->GetSystemPrincipal(getter_AddRefs(loadInfo.mLoadingPrincipal));
2518 NS_ENSURE_SUCCESS(rv, rv);
2520 loadInfo.mPrincipalIsSystem = true;
2523 // See if we're being called from a window.
2524 nsCOMPtr<nsPIDOMWindowInner> globalWindow = aWindow;
2525 if (!globalWindow) {
2526 globalWindow = xpc::CurrentWindowOrNull(aCx);
2529 nsCOMPtr<Document> document;
2530 Maybe<ClientInfo> clientInfo;
2532 if (globalWindow) {
2533 // Only use the current inner window, and only use it if the caller can
2534 // access it.
2535 if (nsPIDOMWindowOuter* outerWindow = globalWindow->GetOuterWindow()) {
2536 loadInfo.mWindow = outerWindow->GetCurrentInnerWindow();
2537 // TODO: fix this for SharedWorkers with multiple documents (bug
2538 // 1177935)
2539 loadInfo.mServiceWorkersTestingInWindow =
2540 outerWindow->GetServiceWorkersTestingEnabled();
2543 if (!loadInfo.mWindow ||
2544 (globalWindow != loadInfo.mWindow &&
2545 !nsContentUtils::CanCallerAccess(loadInfo.mWindow))) {
2546 return NS_ERROR_DOM_SECURITY_ERR;
2549 nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(loadInfo.mWindow);
2550 MOZ_ASSERT(sgo);
2552 loadInfo.mScriptContext = sgo->GetContext();
2553 NS_ENSURE_TRUE(loadInfo.mScriptContext, NS_ERROR_FAILURE);
2555 // If we're called from a window then we can dig out the principal and URI
2556 // from the document.
2557 document = loadInfo.mWindow->GetExtantDoc();
2558 NS_ENSURE_TRUE(document, NS_ERROR_FAILURE);
2560 loadInfo.mBaseURI = document->GetDocBaseURI();
2561 loadInfo.mLoadGroup = document->GetDocumentLoadGroup();
2562 NS_ENSURE_TRUE(loadInfo.mLoadGroup, NS_ERROR_FAILURE);
2564 clientInfo = globalWindow->GetClientInfo();
2566 // Use the document's NodePrincipal as loading principal if we're not
2567 // being called from chrome.
2568 if (!loadInfo.mLoadingPrincipal) {
2569 loadInfo.mLoadingPrincipal = document->NodePrincipal();
2570 NS_ENSURE_TRUE(loadInfo.mLoadingPrincipal, NS_ERROR_FAILURE);
2572 // We use the document's base domain to limit the number of workers
2573 // each domain can create. For sandboxed documents, we use the domain
2574 // of their first non-sandboxed document, walking up until we find
2575 // one. If we can't find one, we fall back to using the GUID of the
2576 // null principal as the base domain.
2577 if (document->GetSandboxFlags() & SANDBOXED_ORIGIN) {
2578 nsCOMPtr<Document> tmpDoc = document;
2579 do {
2580 tmpDoc = tmpDoc->GetInProcessParentDocument();
2581 } while (tmpDoc && tmpDoc->GetSandboxFlags() & SANDBOXED_ORIGIN);
2583 if (tmpDoc) {
2584 // There was an unsandboxed ancestor, yay!
2585 nsCOMPtr<nsIPrincipal> tmpPrincipal = tmpDoc->NodePrincipal();
2586 rv = tmpPrincipal->GetBaseDomain(loadInfo.mDomain);
2587 NS_ENSURE_SUCCESS(rv, rv);
2588 } else {
2589 // No unsandboxed ancestor, use our GUID.
2590 rv = loadInfo.mLoadingPrincipal->GetBaseDomain(loadInfo.mDomain);
2591 NS_ENSURE_SUCCESS(rv, rv);
2593 } else {
2594 // Document creating the worker is not sandboxed.
2595 rv = loadInfo.mLoadingPrincipal->GetBaseDomain(loadInfo.mDomain);
2596 NS_ENSURE_SUCCESS(rv, rv);
2600 NS_ENSURE_TRUE(NS_LoadGroupMatchesPrincipal(loadInfo.mLoadGroup,
2601 loadInfo.mLoadingPrincipal),
2602 NS_ERROR_FAILURE);
2604 nsCOMPtr<nsIPermissionManager> permMgr =
2605 do_GetService(NS_PERMISSIONMANAGER_CONTRACTID, &rv);
2606 NS_ENSURE_SUCCESS(rv, rv);
2608 uint32_t perm;
2609 rv = permMgr->TestPermissionFromPrincipal(
2610 loadInfo.mLoadingPrincipal, NS_LITERAL_CSTRING("systemXHR"), &perm);
2611 NS_ENSURE_SUCCESS(rv, rv);
2613 loadInfo.mXHRParamsAllowed = perm == nsIPermissionManager::ALLOW_ACTION;
2615 nsIDocShell* docShell = globalWindow->GetDocShell();
2616 if (docShell) {
2617 loadInfo.mWatchedByDevtools = docShell->GetWatchedByDevtools();
2620 loadInfo.mReferrerInfo =
2621 ReferrerInfo::CreateForFetch(loadInfo.mLoadingPrincipal, document);
2622 loadInfo.mFromWindow = true;
2623 loadInfo.mWindowID = globalWindow->WindowID();
2624 loadInfo.mStorageAccess = StorageAllowedForWindow(globalWindow);
2625 loadInfo.mCookieSettings = document->CookieSettings();
2626 loadInfo.mOriginAttributes =
2627 nsContentUtils::GetOriginAttributes(document);
2628 loadInfo.mParentController = globalWindow->GetController();
2629 loadInfo.mSecureContext = loadInfo.mWindow->IsSecureContext()
2630 ? WorkerLoadInfo::eSecureContext
2631 : WorkerLoadInfo::eInsecureContext;
2632 } else {
2633 // Not a window
2634 MOZ_ASSERT(isChrome);
2636 // We're being created outside of a window. Need to figure out the script
2637 // that is creating us in order for us to use relative URIs later on.
2638 JS::AutoFilename fileName;
2639 if (JS::DescribeScriptedCaller(aCx, &fileName)) {
2640 // In most cases, fileName is URI. In a few other cases
2641 // (e.g. xpcshell), fileName is a file path. Ideally, we would
2642 // prefer testing whether fileName parses as an URI and fallback
2643 // to file path in case of error, but Windows file paths have
2644 // the interesting property that they can be parsed as bogus
2645 // URIs (e.g. C:/Windows/Tmp is interpreted as scheme "C",
2646 // hostname "Windows", path "Tmp"), which defeats this algorithm.
2647 // Therefore, we adopt the opposite convention.
2648 nsCOMPtr<nsIFile> scriptFile =
2649 do_CreateInstance("@mozilla.org/file/local;1", &rv);
2650 if (NS_FAILED(rv)) {
2651 return rv;
2654 rv = scriptFile->InitWithPath(NS_ConvertUTF8toUTF16(fileName.get()));
2655 if (NS_SUCCEEDED(rv)) {
2656 rv = NS_NewFileURI(getter_AddRefs(loadInfo.mBaseURI), scriptFile);
2658 if (NS_FAILED(rv)) {
2659 // As expected, fileName is not a path, so proceed with
2660 // a uri.
2661 rv = NS_NewURI(getter_AddRefs(loadInfo.mBaseURI), fileName.get());
2663 if (NS_FAILED(rv)) {
2664 return rv;
2667 loadInfo.mXHRParamsAllowed = true;
2668 loadInfo.mFromWindow = false;
2669 loadInfo.mWindowID = UINT64_MAX;
2670 loadInfo.mStorageAccess = StorageAccess::eAllow;
2671 loadInfo.mCookieSettings = mozilla::net::CookieSettings::Create();
2672 MOZ_ASSERT(loadInfo.mCookieSettings);
2674 loadInfo.mOriginAttributes = OriginAttributes();
2677 MOZ_ASSERT(loadInfo.mLoadingPrincipal);
2678 MOZ_ASSERT(isChrome || !loadInfo.mDomain.IsEmpty());
2680 if (!loadInfo.mLoadGroup || aLoadGroupBehavior == OverrideLoadGroup) {
2681 OverrideLoadInfoLoadGroup(loadInfo, loadInfo.mLoadingPrincipal);
2683 MOZ_ASSERT(NS_LoadGroupMatchesPrincipal(loadInfo.mLoadGroup,
2684 loadInfo.mLoadingPrincipal));
2686 // Top level workers' main script use the document charset for the script
2687 // uri encoding.
2688 nsCOMPtr<nsIURI> url;
2689 rv = nsContentUtils::NewURIWithDocumentCharset(
2690 getter_AddRefs(url), aScriptURL, document, loadInfo.mBaseURI);
2691 NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SYNTAX_ERR);
2693 rv = ChannelFromScriptURLMainThread(
2694 loadInfo.mLoadingPrincipal, document, loadInfo.mLoadGroup, url,
2695 clientInfo, ContentPolicyType(aWorkerType), loadInfo.mCookieSettings,
2696 loadInfo.mReferrerInfo, getter_AddRefs(loadInfo.mChannel));
2697 NS_ENSURE_SUCCESS(rv, rv);
2699 rv = NS_GetFinalChannelURI(loadInfo.mChannel,
2700 getter_AddRefs(loadInfo.mResolvedScriptURI));
2701 NS_ENSURE_SUCCESS(rv, rv);
2703 rv = loadInfo.SetPrincipalsAndCSPFromChannel(loadInfo.mChannel);
2704 NS_ENSURE_SUCCESS(rv, rv);
2707 MOZ_DIAGNOSTIC_ASSERT(loadInfo.mLoadingPrincipal);
2708 MOZ_DIAGNOSTIC_ASSERT(loadInfo.PrincipalIsValid());
2710 *aLoadInfo = std::move(loadInfo);
2711 return NS_OK;
2714 // static
2715 void WorkerPrivate::OverrideLoadInfoLoadGroup(WorkerLoadInfo& aLoadInfo,
2716 nsIPrincipal* aPrincipal) {
2717 MOZ_ASSERT(!aLoadInfo.mInterfaceRequestor);
2718 MOZ_ASSERT(aLoadInfo.mLoadingPrincipal == aPrincipal);
2720 aLoadInfo.mInterfaceRequestor =
2721 new WorkerLoadInfo::InterfaceRequestor(aPrincipal, aLoadInfo.mLoadGroup);
2722 aLoadInfo.mInterfaceRequestor->MaybeAddBrowserChild(aLoadInfo.mLoadGroup);
2724 // NOTE: this defaults the load context to:
2725 // - private browsing = false
2726 // - content = true
2727 // - use remote tabs = false
2728 nsCOMPtr<nsILoadGroup> loadGroup = do_CreateInstance(NS_LOADGROUP_CONTRACTID);
2730 nsresult rv =
2731 loadGroup->SetNotificationCallbacks(aLoadInfo.mInterfaceRequestor);
2732 MOZ_ALWAYS_SUCCEEDS(rv);
2734 aLoadInfo.mLoadGroup = loadGroup.forget();
2736 MOZ_ASSERT(NS_LoadGroupMatchesPrincipal(aLoadInfo.mLoadGroup, aPrincipal));
2739 void WorkerPrivate::DoRunLoop(JSContext* aCx) {
2740 MOZ_ACCESS_THREAD_BOUND(mWorkerThreadAccessible, data);
2741 MOZ_ASSERT(mThread);
2744 MutexAutoLock lock(mMutex);
2745 mJSContext = aCx;
2747 MOZ_ASSERT(mStatus == Pending);
2748 mStatus = Running;
2751 // Now that we've done that, we can go ahead and set up our AutoJSAPI. We
2752 // can't before this point, because it can't find the right JSContext before
2753 // then, since it gets it from our mJSContext.
2754 AutoJSAPI jsapi;
2755 jsapi.Init();
2756 MOZ_ASSERT(jsapi.cx() == aCx);
2758 EnableMemoryReporter();
2760 InitializeGCTimers();
2762 for (;;) {
2763 WorkerStatus currentStatus;
2764 bool debuggerRunnablesPending = false;
2765 bool normalRunnablesPending = false;
2768 MutexAutoLock lock(mMutex);
2770 // Wait for a runnable to arrive that we can execute, or for it to be okay
2771 // to shutdown this worker once all holders have been removed.
2772 // Holders may be removed from inside normal runnables, but we don't check
2773 // for that after processing normal runnables, so we need to let control
2774 // flow to the shutdown logic without blocking.
2775 while (mControlQueue.IsEmpty() &&
2776 !(debuggerRunnablesPending = !mDebuggerQueue.IsEmpty()) &&
2777 !(normalRunnablesPending = NS_HasPendingEvents(mThread)) &&
2778 !(mStatus != Running && !HasActiveWorkerRefs())) {
2779 // We pop out to this loop when there are no pending events.
2780 // If we don't reset these, we may not re-enter ProcessNextEvent()
2781 // until we have events to process, and it may seem like we have
2782 // an event running for a very long time.
2783 mThread->SetRunningEventDelay(TimeDuration(), TimeStamp());
2785 WaitForWorkerEvents();
2788 auto result = ProcessAllControlRunnablesLocked();
2789 if (result != ProcessAllControlRunnablesResult::Nothing) {
2790 // NB: There's no JS on the stack here, so Abort vs MayContinue is
2791 // irrelevant
2793 // The state of the world may have changed, recheck it.
2794 normalRunnablesPending = NS_HasPendingEvents(mThread);
2795 // The debugger queue doesn't get cleared, so we can ignore that.
2798 currentStatus = mStatus;
2801 // if all holders are done then we can kill this thread.
2802 if (currentStatus != Running && !HasActiveWorkerRefs()) {
2803 // Now we are ready to kill the worker thread.
2804 if (currentStatus == Canceling) {
2805 NotifyInternal(Killing);
2807 #ifdef DEBUG
2809 MutexAutoLock lock(mMutex);
2810 currentStatus = mStatus;
2812 MOZ_ASSERT(currentStatus == Killing);
2813 #else
2814 currentStatus = Killing;
2815 #endif
2818 // If we're supposed to die then we should exit the loop.
2819 if (currentStatus == Killing) {
2820 // The ClientSource should be cleared in NotifyInternal() when we reach
2821 // or pass Canceling.
2822 MOZ_DIAGNOSTIC_ASSERT(!data->mClientSource);
2824 // Flush uncaught rejections immediately, without
2825 // waiting for a next tick.
2826 PromiseDebugging::FlushUncaughtRejections();
2828 ShutdownGCTimers();
2830 DisableMemoryReporter();
2833 MutexAutoLock lock(mMutex);
2835 mStatus = Dead;
2836 mJSContext = nullptr;
2839 // After mStatus is set to Dead there can be no more
2840 // WorkerControlRunnables so no need to lock here.
2841 if (!mControlQueue.IsEmpty()) {
2842 WorkerControlRunnable* runnable = nullptr;
2843 while (mControlQueue.Pop(runnable)) {
2844 runnable->Cancel();
2845 runnable->Release();
2849 // Unroot the globals
2850 data->mScope = nullptr;
2851 data->mDebuggerScope = nullptr;
2853 return;
2857 if (debuggerRunnablesPending || normalRunnablesPending) {
2858 // Start the periodic GC timer if it is not already running.
2859 SetGCTimerMode(PeriodicTimer);
2862 if (debuggerRunnablesPending) {
2863 WorkerRunnable* runnable = nullptr;
2866 MutexAutoLock lock(mMutex);
2868 mDebuggerQueue.Pop(runnable);
2869 debuggerRunnablesPending = !mDebuggerQueue.IsEmpty();
2872 MOZ_ASSERT(runnable);
2873 static_cast<nsIRunnable*>(runnable)->Run();
2874 runnable->Release();
2876 CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get();
2877 ccjs->PerformDebuggerMicroTaskCheckpoint();
2879 if (debuggerRunnablesPending) {
2880 WorkerDebuggerGlobalScope* globalScope = DebuggerGlobalScope();
2881 MOZ_ASSERT(globalScope);
2883 // Now *might* be a good time to GC. Let the JS engine make the
2884 // decision.
2885 JSAutoRealm ar(aCx, globalScope->GetGlobalJSObject());
2886 JS_MaybeGC(aCx);
2888 } else if (normalRunnablesPending) {
2889 // Process a single runnable from the main queue.
2890 NS_ProcessNextEvent(mThread, false);
2892 normalRunnablesPending = NS_HasPendingEvents(mThread);
2893 if (normalRunnablesPending && GlobalScope()) {
2894 // Now *might* be a good time to GC. Let the JS engine make the
2895 // decision.
2896 JSAutoRealm ar(aCx, GlobalScope()->GetGlobalJSObject());
2897 JS_MaybeGC(aCx);
2901 if (!debuggerRunnablesPending && !normalRunnablesPending) {
2902 // Both the debugger event queue and the normal event queue has been
2903 // exhausted, cancel the periodic GC timer and schedule the idle GC timer.
2904 SetGCTimerMode(IdleTimer);
2907 // If the worker thread is spamming the main thread faster than it can
2908 // process the work, then pause the worker thread until the main thread
2909 // catches up.
2910 size_t queuedEvents = mMainThreadEventTargetForMessaging->Length() +
2911 mMainThreadDebuggeeEventTarget->Length();
2912 if (queuedEvents > 5000) {
2913 // Note, postMessage uses mMainThreadDebuggeeEventTarget!
2914 mMainThreadDebuggeeEventTarget->AwaitIdle();
2918 MOZ_CRASH("Shouldn't get here!");
2921 void WorkerPrivate::OnProcessNextEvent() {
2922 AssertIsOnWorkerThread();
2924 uint32_t recursionDepth = CycleCollectedJSContext::Get()->RecursionDepth();
2925 MOZ_ASSERT(recursionDepth);
2927 // Normally we process control runnables in DoRunLoop or RunCurrentSyncLoop.
2928 // However, it's possible that non-worker C++ could spin its own nested event
2929 // loop, and in that case we must ensure that we continue to process control
2930 // runnables here.
2931 if (recursionDepth > 1 && mSyncLoopStack.Length() < recursionDepth - 1) {
2932 Unused << ProcessAllControlRunnables();
2933 // There's no running JS, and no state to revalidate, so we can ignore the
2934 // return value.
2938 void WorkerPrivate::AfterProcessNextEvent() {
2939 AssertIsOnWorkerThread();
2940 MOZ_ASSERT(CycleCollectedJSContext::Get()->RecursionDepth());
2943 nsIEventTarget* WorkerPrivate::MainThreadEventTargetForMessaging() {
2944 return mMainThreadEventTargetForMessaging;
2947 nsresult WorkerPrivate::DispatchToMainThreadForMessaging(nsIRunnable* aRunnable,
2948 uint32_t aFlags) {
2949 nsCOMPtr<nsIRunnable> r = aRunnable;
2950 return DispatchToMainThreadForMessaging(r.forget(), aFlags);
2953 nsresult WorkerPrivate::DispatchToMainThreadForMessaging(
2954 already_AddRefed<nsIRunnable> aRunnable, uint32_t aFlags) {
2955 return mMainThreadEventTargetForMessaging->Dispatch(std::move(aRunnable),
2956 aFlags);
2959 nsIEventTarget* WorkerPrivate::MainThreadEventTarget() {
2960 return mMainThreadEventTarget;
2963 nsresult WorkerPrivate::DispatchToMainThread(nsIRunnable* aRunnable,
2964 uint32_t aFlags) {
2965 nsCOMPtr<nsIRunnable> r = aRunnable;
2966 return DispatchToMainThread(r.forget(), aFlags);
2969 nsresult WorkerPrivate::DispatchToMainThread(
2970 already_AddRefed<nsIRunnable> aRunnable, uint32_t aFlags) {
2971 return mMainThreadEventTarget->Dispatch(std::move(aRunnable), aFlags);
2974 nsresult WorkerPrivate::DispatchDebuggeeToMainThread(
2975 already_AddRefed<WorkerDebuggeeRunnable> aRunnable, uint32_t aFlags) {
2976 return mMainThreadDebuggeeEventTarget->Dispatch(std::move(aRunnable), aFlags);
2979 nsISerialEventTarget* WorkerPrivate::ControlEventTarget() {
2980 return mWorkerControlEventTarget;
2983 nsISerialEventTarget* WorkerPrivate::HybridEventTarget() {
2984 return mWorkerHybridEventTarget;
2987 bool WorkerPrivate::EnsureClientSource() {
2988 MOZ_ACCESS_THREAD_BOUND(mWorkerThreadAccessible, data);
2990 if (data->mClientSource) {
2991 return true;
2994 ClientType type;
2995 switch (Type()) {
2996 case WorkerTypeDedicated:
2997 type = ClientType::Worker;
2998 break;
2999 case WorkerTypeShared:
3000 type = ClientType::Sharedworker;
3001 break;
3002 case WorkerTypeService:
3003 type = ClientType::Serviceworker;
3004 break;
3005 default:
3006 MOZ_CRASH("unknown worker type!");
3009 data->mClientSource = ClientManager::CreateSource(
3010 type, mWorkerHybridEventTarget, GetPrincipalInfo());
3011 MOZ_DIAGNOSTIC_ASSERT(data->mClientSource);
3013 data->mClientSource->SetAgentClusterId(mAgentClusterId);
3015 if (data->mFrozen) {
3016 data->mClientSource->Freeze();
3019 // Shortly after the client is reserved we will try loading the main script
3020 // for the worker. This may get intercepted by the ServiceWorkerManager
3021 // which will then try to create a ClientHandle. Its actually possible for
3022 // the main thread to create this ClientHandle before our IPC message creating
3023 // the ClientSource completes. To avoid this race we synchronously ping our
3024 // parent Client actor here. This ensure the worker ClientSource is created
3025 // in the parent before the main thread might try reaching it with a
3026 // ClientHandle.
3028 // An alternative solution would have been to handle the out-of-order
3029 // operations on the parent side. We could have created a small window where
3030 // we allow ClientHandle objects to exist without a ClientSource. We would
3031 // then time out these handles if they stayed orphaned for too long. This
3032 // approach would be much more complex, but also avoid this extra bit of
3033 // latency when starting workers.
3035 // Note, we only have to do this for workers that can be controlled by a
3036 // service worker. So avoid the sync overhead here if we are starting a
3037 // service worker or a chrome worker.
3038 if (Type() != WorkerTypeService && !IsChromeWorker()) {
3039 data->mClientSource->WorkerSyncPing(this);
3042 return true;
3045 bool WorkerPrivate::EnsureCSPEventListener() {
3046 if (!mCSPEventListener) {
3047 mCSPEventListener = WorkerCSPEventListener::Create(this);
3048 if (NS_WARN_IF(!mCSPEventListener)) {
3049 return false;
3052 return true;
3055 nsICSPEventListener* WorkerPrivate::CSPEventListener() const {
3056 MOZ_ASSERT(mCSPEventListener);
3057 return mCSPEventListener;
3060 void WorkerPrivate::EnsurePerformanceStorage() {
3061 AssertIsOnWorkerThread();
3063 if (!mPerformanceStorage) {
3064 mPerformanceStorage = PerformanceStorageWorker::Create(this);
3068 Maybe<ClientInfo> WorkerPrivate::GetClientInfo() const {
3069 MOZ_ACCESS_THREAD_BOUND(mWorkerThreadAccessible, data);
3070 Maybe<ClientInfo> clientInfo;
3071 if (!data->mClientSource) {
3072 MOZ_DIAGNOSTIC_ASSERT(mStatus >= Canceling);
3073 return clientInfo;
3075 clientInfo.emplace(data->mClientSource->Info());
3076 return clientInfo;
3079 const ClientState WorkerPrivate::GetClientState() const {
3080 MOZ_ACCESS_THREAD_BOUND(mWorkerThreadAccessible, data);
3081 MOZ_DIAGNOSTIC_ASSERT(data->mClientSource);
3082 ClientState state;
3083 data->mClientSource->SnapshotState(&state);
3084 return state;
3087 const Maybe<ServiceWorkerDescriptor> WorkerPrivate::GetController() {
3088 MOZ_ACCESS_THREAD_BOUND(mWorkerThreadAccessible, data);
3090 MutexAutoLock lock(mMutex);
3091 if (mStatus >= Canceling) {
3092 return Maybe<ServiceWorkerDescriptor>();
3095 MOZ_DIAGNOSTIC_ASSERT(data->mClientSource);
3096 return data->mClientSource->GetController();
3099 void WorkerPrivate::Control(const ServiceWorkerDescriptor& aServiceWorker) {
3100 MOZ_ACCESS_THREAD_BOUND(mWorkerThreadAccessible, data);
3101 MOZ_DIAGNOSTIC_ASSERT(!IsChromeWorker());
3102 MOZ_DIAGNOSTIC_ASSERT(Type() != WorkerTypeService);
3104 MutexAutoLock lock(mMutex);
3105 if (mStatus >= Canceling) {
3106 return;
3109 MOZ_DIAGNOSTIC_ASSERT(data->mClientSource);
3111 if (IsBlobURI(mLoadInfo.mBaseURI)) {
3112 // Blob URL workers can only become controlled by inheriting from
3113 // their parent. Make sure to note this properly.
3114 data->mClientSource->InheritController(aServiceWorker);
3115 } else {
3116 // Otherwise this is a normal interception and we simply record the
3117 // controller locally.
3118 data->mClientSource->SetController(aServiceWorker);
3122 void WorkerPrivate::ExecutionReady() {
3123 MOZ_ACCESS_THREAD_BOUND(mWorkerThreadAccessible, data);
3125 MutexAutoLock lock(mMutex);
3126 if (mStatus >= Canceling) {
3127 return;
3130 MOZ_DIAGNOSTIC_ASSERT(data->mClientSource);
3131 data->mClientSource->WorkerExecutionReady(this);
3134 void WorkerPrivate::InitializeGCTimers() {
3135 MOZ_ACCESS_THREAD_BOUND(mWorkerThreadAccessible, data);
3137 // We need a timer for GC. The basic plan is to run a non-shrinking GC
3138 // periodically (PERIODIC_GC_TIMER_DELAY_SEC) while the worker is running.
3139 // Once the worker goes idle we set a short (IDLE_GC_TIMER_DELAY_SEC) timer to
3140 // run a shrinking GC. If the worker receives more messages then the short
3141 // timer is canceled and the periodic timer resumes.
3142 data->mGCTimer = NS_NewTimer();
3143 MOZ_ASSERT(data->mGCTimer);
3145 data->mPeriodicGCTimerRunning = false;
3146 data->mIdleGCTimerRunning = false;
3149 void WorkerPrivate::SetGCTimerMode(GCTimerMode aMode) {
3150 MOZ_ACCESS_THREAD_BOUND(mWorkerThreadAccessible, data);
3151 MOZ_ASSERT(data->mGCTimer);
3153 if ((aMode == PeriodicTimer && data->mPeriodicGCTimerRunning) ||
3154 (aMode == IdleTimer && data->mIdleGCTimerRunning)) {
3155 return;
3158 MOZ_ALWAYS_SUCCEEDS(data->mGCTimer->Cancel());
3160 data->mPeriodicGCTimerRunning = false;
3161 data->mIdleGCTimerRunning = false;
3162 LOG(WorkerLog(),
3163 ("Worker %p canceled GC timer because %s\n", this,
3164 aMode == PeriodicTimer ? "periodic"
3165 : aMode == IdleTimer ? "idle" : "none"));
3167 if (aMode == NoTimer) {
3168 return;
3171 MOZ_ASSERT(aMode == PeriodicTimer || aMode == IdleTimer);
3173 uint32_t delay = 0;
3174 int16_t type = nsITimer::TYPE_ONE_SHOT;
3175 nsTimerCallbackFunc callback = nullptr;
3176 const char* name = nullptr;
3178 if (aMode == PeriodicTimer) {
3179 delay = PERIODIC_GC_TIMER_DELAY_SEC * 1000;
3180 type = nsITimer::TYPE_REPEATING_SLACK;
3181 callback = PeriodicGCTimerCallback;
3182 name = "dom::PeriodicGCTimerCallback";
3183 } else {
3184 delay = IDLE_GC_TIMER_DELAY_SEC * 1000;
3185 type = nsITimer::TYPE_ONE_SHOT;
3186 callback = IdleGCTimerCallback;
3187 name = "dom::IdleGCTimerCallback";
3190 MOZ_ALWAYS_SUCCEEDS(data->mGCTimer->SetTarget(mWorkerControlEventTarget));
3191 MOZ_ALWAYS_SUCCEEDS(data->mGCTimer->InitWithNamedFuncCallback(
3192 callback, this, delay, type, name));
3194 if (aMode == PeriodicTimer) {
3195 LOG(WorkerLog(), ("Worker %p scheduled periodic GC timer\n", this));
3196 data->mPeriodicGCTimerRunning = true;
3197 } else {
3198 LOG(WorkerLog(), ("Worker %p scheduled idle GC timer\n", this));
3199 data->mIdleGCTimerRunning = true;
3203 void WorkerPrivate::ShutdownGCTimers() {
3204 MOZ_ACCESS_THREAD_BOUND(mWorkerThreadAccessible, data);
3206 MOZ_ASSERT(data->mGCTimer);
3208 // Always make sure the timer is canceled.
3209 MOZ_ALWAYS_SUCCEEDS(data->mGCTimer->Cancel());
3211 LOG(WorkerLog(), ("Worker %p killed the GC timer\n", this));
3213 data->mGCTimer = nullptr;
3214 data->mPeriodicGCTimerRunning = false;
3215 data->mIdleGCTimerRunning = false;
3218 bool WorkerPrivate::InterruptCallback(JSContext* aCx) {
3219 MOZ_ACCESS_THREAD_BOUND(mWorkerThreadAccessible, data);
3221 // If we are here it's because a WorkerControlRunnable has been dispatched.
3222 // The runnable could be processed here or it could have already been
3223 // processed by a sync event loop.
3224 // The most important thing this method must do, is to decide if the JS
3225 // execution should continue or not. If the runnable returns an error or if
3226 // the worker status is >= Canceling, we should stop the JS execution.
3228 MOZ_ASSERT(!JS_IsExceptionPending(aCx));
3230 bool mayContinue = true;
3231 bool scheduledIdleGC = false;
3233 for (;;) {
3234 // Run all control events now.
3235 auto result = ProcessAllControlRunnables();
3236 if (result == ProcessAllControlRunnablesResult::Abort) {
3237 mayContinue = false;
3240 bool mayFreeze = data->mFrozen;
3243 MutexAutoLock lock(mMutex);
3245 if (mayFreeze) {
3246 mayFreeze = mStatus <= Running;
3249 if (mStatus >= Canceling) {
3250 mayContinue = false;
3254 if (!mayContinue || !mayFreeze) {
3255 break;
3258 // Cancel the periodic GC timer here before freezing. The idle GC timer
3259 // will clean everything up once it runs.
3260 if (!scheduledIdleGC) {
3261 SetGCTimerMode(IdleTimer);
3262 scheduledIdleGC = true;
3265 while ((mayContinue = MayContinueRunning())) {
3266 MutexAutoLock lock(mMutex);
3267 if (!mControlQueue.IsEmpty()) {
3268 break;
3271 WaitForWorkerEvents();
3275 if (!mayContinue) {
3276 // We want only uncatchable exceptions here.
3277 NS_ASSERTION(!JS_IsExceptionPending(aCx),
3278 "Should not have an exception set here!");
3279 return false;
3282 // Make sure the periodic timer gets turned back on here.
3283 SetGCTimerMode(PeriodicTimer);
3285 return true;
3288 void WorkerPrivate::CloseInternal() {
3289 AssertIsOnWorkerThread();
3290 NotifyInternal(Closing);
3293 bool WorkerPrivate::IsOnCurrentThread() {
3294 // May be called on any thread!
3296 MOZ_ASSERT(mPRThread);
3297 return PR_GetCurrentThread() == mPRThread;
3300 void WorkerPrivate::ScheduleDeletion(WorkerRanOrNot aRanOrNot) {
3302 // mWorkerThreadAccessible's accessor must be destructed before
3303 // the scheduled Runnable gets to run.
3304 MOZ_ACCESS_THREAD_BOUND(mWorkerThreadAccessible, data);
3305 MOZ_ASSERT(data->mChildWorkers.IsEmpty());
3307 MOZ_ASSERT(mSyncLoopStack.IsEmpty());
3308 MOZ_ASSERT(mPostSyncLoopOperations == 0);
3310 ClearMainEventQueue(aRanOrNot);
3311 #ifdef DEBUG
3312 if (WorkerRan == aRanOrNot) {
3313 nsIThread* currentThread = NS_GetCurrentThread();
3314 MOZ_ASSERT(currentThread);
3315 MOZ_ASSERT(!NS_HasPendingEvents(currentThread));
3317 #endif
3319 if (WorkerPrivate* parent = GetParent()) {
3320 RefPtr<WorkerFinishedRunnable> runnable =
3321 new WorkerFinishedRunnable(parent, this);
3322 if (!runnable->Dispatch()) {
3323 NS_WARNING("Failed to dispatch runnable!");
3325 } else {
3326 // Note, this uses the lower priority DispatchToMainThreadForMessaging for
3327 // dispatching TopLevelWorkerFinishedRunnable to the main thread so that
3328 // other relevant runnables are guaranteed to run before it.
3329 RefPtr<TopLevelWorkerFinishedRunnable> runnable =
3330 new TopLevelWorkerFinishedRunnable(this);
3331 if (NS_FAILED(DispatchToMainThreadForMessaging(runnable.forget()))) {
3332 NS_WARNING("Failed to dispatch runnable!");
3337 bool WorkerPrivate::CollectRuntimeStats(JS::RuntimeStats* aRtStats,
3338 bool aAnonymize) {
3339 AssertIsOnWorkerThread();
3340 NS_ASSERTION(aRtStats, "Null RuntimeStats!");
3341 NS_ASSERTION(mJSContext, "This must never be null!");
3343 return JS::CollectRuntimeStats(mJSContext, aRtStats, nullptr, aAnonymize);
3346 void WorkerPrivate::EnableMemoryReporter() {
3347 MOZ_ACCESS_THREAD_BOUND(mWorkerThreadAccessible, data);
3348 MOZ_ASSERT(!data->mMemoryReporter);
3350 // No need to lock here since the main thread can't race until we've
3351 // successfully registered the reporter.
3352 data->mMemoryReporter = new MemoryReporter(this);
3354 if (NS_FAILED(RegisterWeakAsyncMemoryReporter(data->mMemoryReporter))) {
3355 NS_WARNING("Failed to register memory reporter!");
3356 // No need to lock here since a failed registration means our memory
3357 // reporter can't start running. Just clean up.
3358 data->mMemoryReporter = nullptr;
3362 void WorkerPrivate::DisableMemoryReporter() {
3363 MOZ_ACCESS_THREAD_BOUND(mWorkerThreadAccessible, data);
3365 RefPtr<MemoryReporter> memoryReporter;
3367 // Mutex protectes MemoryReporter::mWorkerPrivate which is cleared by
3368 // MemoryReporter::Disable() below.
3369 MutexAutoLock lock(mMutex);
3371 // There is nothing to do here if the memory reporter was never successfully
3372 // registered.
3373 if (!data->mMemoryReporter) {
3374 return;
3377 // We don't need this set any longer. Swap it out so that we can unregister
3378 // below.
3379 data->mMemoryReporter.swap(memoryReporter);
3381 // Next disable the memory reporter so that the main thread stops trying to
3382 // signal us.
3383 memoryReporter->Disable();
3386 // Finally unregister the memory reporter.
3387 if (NS_FAILED(UnregisterWeakMemoryReporter(memoryReporter))) {
3388 NS_WARNING("Failed to unregister memory reporter!");
3392 void WorkerPrivate::WaitForWorkerEvents() {
3393 AUTO_PROFILER_LABEL("WorkerPrivate::WaitForWorkerEvents", IDLE);
3395 AssertIsOnWorkerThread();
3396 mMutex.AssertCurrentThreadOwns();
3398 AUTO_PROFILER_THREAD_SLEEP;
3400 // Wait for a worker event.
3401 mCondVar.Wait();
3404 WorkerPrivate::ProcessAllControlRunnablesResult
3405 WorkerPrivate::ProcessAllControlRunnablesLocked() {
3406 AssertIsOnWorkerThread();
3407 mMutex.AssertCurrentThreadOwns();
3409 auto result = ProcessAllControlRunnablesResult::Nothing;
3411 for (;;) {
3412 WorkerControlRunnable* event;
3413 if (!mControlQueue.Pop(event)) {
3414 break;
3417 MutexAutoUnlock unlock(mMutex);
3419 MOZ_ASSERT(event);
3420 if (NS_FAILED(static_cast<nsIRunnable*>(event)->Run())) {
3421 result = ProcessAllControlRunnablesResult::Abort;
3424 if (result == ProcessAllControlRunnablesResult::Nothing) {
3425 // We ran at least one thing.
3426 result = ProcessAllControlRunnablesResult::MayContinue;
3428 event->Release();
3431 return result;
3434 void WorkerPrivate::ClearMainEventQueue(WorkerRanOrNot aRanOrNot) {
3435 AssertIsOnWorkerThread();
3437 MOZ_ASSERT(mSyncLoopStack.IsEmpty());
3438 MOZ_ASSERT(!mCancelAllPendingRunnables);
3439 mCancelAllPendingRunnables = true;
3441 if (WorkerNeverRan == aRanOrNot) {
3442 for (uint32_t count = mPreStartRunnables.Length(), index = 0; index < count;
3443 index++) {
3444 RefPtr<WorkerRunnable> runnable = mPreStartRunnables[index].forget();
3445 static_cast<nsIRunnable*>(runnable.get())->Run();
3447 } else {
3448 nsIThread* currentThread = NS_GetCurrentThread();
3449 MOZ_ASSERT(currentThread);
3451 NS_ProcessPendingEvents(currentThread);
3453 // We are about to destroy worker, report all use counters.
3454 ReportUseCounters();
3457 MOZ_ASSERT(mCancelAllPendingRunnables);
3458 mCancelAllPendingRunnables = false;
3461 void WorkerPrivate::ClearDebuggerEventQueue() {
3462 while (!mDebuggerQueue.IsEmpty()) {
3463 WorkerRunnable* runnable = nullptr;
3464 mDebuggerQueue.Pop(runnable);
3465 // It should be ok to simply release the runnable, without running it.
3466 runnable->Release();
3470 bool WorkerPrivate::FreezeInternal() {
3471 MOZ_ACCESS_THREAD_BOUND(mWorkerThreadAccessible, data);
3472 NS_ASSERTION(!data->mFrozen, "Already frozen!");
3474 if (data->mClientSource) {
3475 data->mClientSource->Freeze();
3478 data->mFrozen = true;
3480 for (uint32_t index = 0; index < data->mChildWorkers.Length(); index++) {
3481 data->mChildWorkers[index]->Freeze(nullptr);
3484 return true;
3487 bool WorkerPrivate::ThawInternal() {
3488 MOZ_ACCESS_THREAD_BOUND(mWorkerThreadAccessible, data);
3490 NS_ASSERTION(data->mFrozen, "Not yet frozen!");
3492 for (uint32_t index = 0; index < data->mChildWorkers.Length(); index++) {
3493 data->mChildWorkers[index]->Thaw(nullptr);
3496 data->mFrozen = false;
3498 if (data->mClientSource) {
3499 data->mClientSource->Thaw();
3502 return true;
3505 void WorkerPrivate::PropagateFirstPartyStorageAccessGrantedInternal() {
3506 MOZ_ACCESS_THREAD_BOUND(mWorkerThreadAccessible, data);
3508 mLoadInfo.mFirstPartyStorageAccessGranted = true;
3510 WorkerGlobalScope* globalScope = GlobalScope();
3511 if (globalScope) {
3512 globalScope->FirstPartyStorageAccessGranted();
3515 for (uint32_t index = 0; index < data->mChildWorkers.Length(); index++) {
3516 data->mChildWorkers[index]->PropagateFirstPartyStorageAccessGranted();
3520 void WorkerPrivate::TraverseTimeouts(nsCycleCollectionTraversalCallback& cb) {
3521 MOZ_ACCESS_THREAD_BOUND(mWorkerThreadAccessible, data);
3522 for (uint32_t i = 0; i < data->mTimeouts.Length(); ++i) {
3523 TimeoutInfo* tmp = data->mTimeouts[i];
3524 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mHandler)
3528 void WorkerPrivate::UnlinkTimeouts() {
3529 MOZ_ACCESS_THREAD_BOUND(mWorkerThreadAccessible, data);
3530 data->mTimeouts.Clear();
3533 bool WorkerPrivate::ModifyBusyCountFromWorker(bool aIncrease) {
3534 AssertIsOnWorkerThread();
3537 MutexAutoLock lock(mMutex);
3539 // If we're in shutdown then the busy count is no longer being considered so
3540 // just return now.
3541 if (mStatus >= Killing) {
3542 return true;
3546 RefPtr<ModifyBusyCountRunnable> runnable =
3547 new ModifyBusyCountRunnable(this, aIncrease);
3548 return runnable->Dispatch();
3551 bool WorkerPrivate::AddChildWorker(WorkerPrivate* aChildWorker) {
3552 MOZ_ACCESS_THREAD_BOUND(mWorkerThreadAccessible, data);
3554 #ifdef DEBUG
3556 WorkerStatus currentStatus;
3558 MutexAutoLock lock(mMutex);
3559 currentStatus = mStatus;
3562 MOZ_ASSERT(currentStatus == Running);
3564 #endif
3566 NS_ASSERTION(!data->mChildWorkers.Contains(aChildWorker),
3567 "Already know about this one!");
3568 data->mChildWorkers.AppendElement(aChildWorker);
3570 return data->mChildWorkers.Length() == 1 ? ModifyBusyCountFromWorker(true)
3571 : true;
3574 void WorkerPrivate::RemoveChildWorker(WorkerPrivate* aChildWorker) {
3575 MOZ_ACCESS_THREAD_BOUND(mWorkerThreadAccessible, data);
3577 NS_ASSERTION(data->mChildWorkers.Contains(aChildWorker),
3578 "Didn't know about this one!");
3579 data->mChildWorkers.RemoveElement(aChildWorker);
3581 if (data->mChildWorkers.IsEmpty() && !ModifyBusyCountFromWorker(false)) {
3582 NS_WARNING("Failed to modify busy count!");
3586 bool WorkerPrivate::AddWorkerRef(WorkerRef* aWorkerRef,
3587 WorkerStatus aFailStatus) {
3588 MOZ_ASSERT(aWorkerRef);
3589 MOZ_ACCESS_THREAD_BOUND(mWorkerThreadAccessible, data);
3592 MutexAutoLock lock(mMutex);
3594 if (mStatus >= aFailStatus) {
3595 return false;
3599 MOZ_ASSERT(!data->mWorkerRefs.Contains(aWorkerRef),
3600 "Already know about this one!");
3602 if (aWorkerRef->IsPreventingShutdown()) {
3603 if (!data->mNumWorkerRefsPreventingShutdownStart &&
3604 !ModifyBusyCountFromWorker(true)) {
3605 return false;
3607 data->mNumWorkerRefsPreventingShutdownStart += 1;
3610 data->mWorkerRefs.AppendElement(aWorkerRef);
3611 return true;
3614 void WorkerPrivate::RemoveWorkerRef(WorkerRef* aWorkerRef) {
3615 MOZ_ASSERT(aWorkerRef);
3616 MOZ_ACCESS_THREAD_BOUND(mWorkerThreadAccessible, data);
3618 MOZ_ASSERT(data->mWorkerRefs.Contains(aWorkerRef),
3619 "Didn't know about this one!");
3620 data->mWorkerRefs.RemoveElement(aWorkerRef);
3622 if (aWorkerRef->IsPreventingShutdown()) {
3623 data->mNumWorkerRefsPreventingShutdownStart -= 1;
3624 if (!data->mNumWorkerRefsPreventingShutdownStart &&
3625 !ModifyBusyCountFromWorker(false)) {
3626 NS_WARNING("Failed to modify busy count!");
3631 void WorkerPrivate::NotifyWorkerRefs(WorkerStatus aStatus) {
3632 MOZ_ACCESS_THREAD_BOUND(mWorkerThreadAccessible, data);
3634 NS_ASSERTION(aStatus > Closing, "Bad status!");
3636 nsTObserverArray<WorkerRef*>::ForwardIterator iter(data->mWorkerRefs);
3637 while (iter.HasMore()) {
3638 iter.GetNext()->Notify();
3641 AutoTArray<WorkerPrivate*, 10> children;
3642 children.AppendElements(data->mChildWorkers);
3644 for (uint32_t index = 0; index < children.Length(); index++) {
3645 if (!children[index]->Notify(aStatus)) {
3646 NS_WARNING("Failed to notify child worker!");
3651 void WorkerPrivate::CancelAllTimeouts() {
3652 MOZ_ACCESS_THREAD_BOUND(mWorkerThreadAccessible, data);
3654 LOG(TimeoutsLog(), ("Worker %p CancelAllTimeouts.\n", this));
3656 if (data->mTimerRunning) {
3657 NS_ASSERTION(data->mTimer && data->mTimerRunnable, "Huh?!");
3658 NS_ASSERTION(!data->mTimeouts.IsEmpty(), "Huh?!");
3660 if (NS_FAILED(data->mTimer->Cancel())) {
3661 NS_WARNING("Failed to cancel timer!");
3664 for (uint32_t index = 0; index < data->mTimeouts.Length(); index++) {
3665 data->mTimeouts[index]->mCanceled = true;
3668 // If mRunningExpiredTimeouts, then the fact that they are all canceled now
3669 // means that the currently executing RunExpiredTimeouts will deal with
3670 // them. Otherwise, we need to clean them up ourselves.
3671 if (!data->mRunningExpiredTimeouts) {
3672 data->mTimeouts.Clear();
3673 ModifyBusyCountFromWorker(false);
3676 // Set mTimerRunning false even if mRunningExpiredTimeouts is true, so that
3677 // if we get reentered under this same RunExpiredTimeouts call we don't
3678 // assert above that !mTimeouts().IsEmpty(), because that's clearly false
3679 // now.
3680 data->mTimerRunning = false;
3682 #ifdef DEBUG
3683 else if (!data->mRunningExpiredTimeouts) {
3684 NS_ASSERTION(data->mTimeouts.IsEmpty(), "Huh?!");
3686 #endif
3688 data->mTimer = nullptr;
3689 data->mTimerRunnable = nullptr;
3692 already_AddRefed<nsIEventTarget> WorkerPrivate::CreateNewSyncLoop(
3693 WorkerStatus aFailStatus) {
3694 AssertIsOnWorkerThread();
3695 MOZ_ASSERT(
3696 aFailStatus >= Canceling,
3697 "Sync loops can be created when the worker is in Running/Closing state!");
3700 MutexAutoLock lock(mMutex);
3702 if (mStatus >= aFailStatus) {
3703 return nullptr;
3707 auto queue =
3708 static_cast<ThreadEventQueue<EventQueue>*>(mThread->EventQueue());
3709 nsCOMPtr<nsISerialEventTarget> realEventTarget = queue->PushEventQueue();
3710 MOZ_ASSERT(realEventTarget);
3712 RefPtr<EventTarget> workerEventTarget =
3713 new EventTarget(this, realEventTarget);
3716 // Modifications must be protected by mMutex in DEBUG builds, see comment
3717 // about mSyncLoopStack in WorkerPrivate.h.
3718 #ifdef DEBUG
3719 MutexAutoLock lock(mMutex);
3720 #endif
3722 mSyncLoopStack.AppendElement(new SyncLoopInfo(workerEventTarget));
3725 return workerEventTarget.forget();
3728 bool WorkerPrivate::RunCurrentSyncLoop() {
3729 AssertIsOnWorkerThread();
3731 JSContext* cx = GetJSContext();
3732 MOZ_ASSERT(cx);
3734 AutoPushEventLoopGlobal eventLoopGlobal(this, cx);
3736 // This should not change between now and the time we finish running this sync
3737 // loop.
3738 uint32_t currentLoopIndex = mSyncLoopStack.Length() - 1;
3740 SyncLoopInfo* loopInfo = mSyncLoopStack[currentLoopIndex];
3742 MOZ_ASSERT(loopInfo);
3743 MOZ_ASSERT(!loopInfo->mHasRun);
3744 MOZ_ASSERT(!loopInfo->mCompleted);
3746 #ifdef DEBUG
3747 loopInfo->mHasRun = true;
3748 #endif
3750 while (!loopInfo->mCompleted) {
3751 bool normalRunnablesPending = false;
3753 // Don't block with the periodic GC timer running.
3754 if (!NS_HasPendingEvents(mThread)) {
3755 SetGCTimerMode(IdleTimer);
3758 // Wait for something to do.
3760 MutexAutoLock lock(mMutex);
3762 for (;;) {
3763 while (mControlQueue.IsEmpty() && !normalRunnablesPending &&
3764 !(normalRunnablesPending = NS_HasPendingEvents(mThread))) {
3765 WaitForWorkerEvents();
3768 auto result = ProcessAllControlRunnablesLocked();
3769 if (result != ProcessAllControlRunnablesResult::Nothing) {
3770 // The state of the world may have changed. Recheck it if we need to
3771 // continue.
3772 normalRunnablesPending =
3773 result == ProcessAllControlRunnablesResult::MayContinue &&
3774 NS_HasPendingEvents(mThread);
3776 // NB: If we processed a NotifyRunnable, we might have run
3777 // non-control runnables, one of which may have shut down the
3778 // sync loop.
3779 if (loopInfo->mCompleted) {
3780 break;
3784 // If we *didn't* run any control runnables, this should be unchanged.
3785 MOZ_ASSERT(!loopInfo->mCompleted);
3787 if (normalRunnablesPending) {
3788 break;
3793 if (normalRunnablesPending) {
3794 // Make sure the periodic timer is running before we continue.
3795 SetGCTimerMode(PeriodicTimer);
3797 MOZ_ALWAYS_TRUE(NS_ProcessNextEvent(mThread, false));
3799 // Now *might* be a good time to GC. Let the JS engine make the decision.
3800 if (GetCurrentEventLoopGlobal()) {
3801 // If GetCurrentEventLoopGlobal() is non-null, our JSContext is in a
3802 // Realm, so it's safe to try to GC.
3803 MOZ_ASSERT(JS::CurrentGlobalOrNull(cx));
3804 JS_MaybeGC(cx);
3809 // Make sure that the stack didn't change underneath us.
3810 MOZ_ASSERT(mSyncLoopStack[currentLoopIndex] == loopInfo);
3812 return DestroySyncLoop(currentLoopIndex);
3815 bool WorkerPrivate::DestroySyncLoop(uint32_t aLoopIndex) {
3816 MOZ_ASSERT(!mSyncLoopStack.IsEmpty());
3817 MOZ_ASSERT(mSyncLoopStack.Length() - 1 == aLoopIndex);
3819 // We're about to delete the loop, stash its event target and result.
3820 SyncLoopInfo* loopInfo = mSyncLoopStack[aLoopIndex];
3821 nsIEventTarget* nestedEventTarget =
3822 loopInfo->mEventTarget->GetWeakNestedEventTarget();
3823 MOZ_ASSERT(nestedEventTarget);
3825 bool result = loopInfo->mResult;
3828 // Modifications must be protected by mMutex in DEBUG builds, see comment
3829 // about mSyncLoopStack in WorkerPrivate.h.
3830 #ifdef DEBUG
3831 MutexAutoLock lock(mMutex);
3832 #endif
3834 // This will delete |loopInfo|!
3835 mSyncLoopStack.RemoveElementAt(aLoopIndex);
3838 auto queue =
3839 static_cast<ThreadEventQueue<EventQueue>*>(mThread->EventQueue());
3840 queue->PopEventQueue(nestedEventTarget);
3842 if (mSyncLoopStack.IsEmpty()) {
3843 if ((mPostSyncLoopOperations & ePendingEventQueueClearing)) {
3844 ClearMainEventQueue(WorkerRan);
3847 if ((mPostSyncLoopOperations & eDispatchCancelingRunnable)) {
3848 DispatchCancelingRunnable();
3851 mPostSyncLoopOperations = 0;
3854 return result;
3857 void WorkerPrivate::DispatchCancelingRunnable() {
3858 // Here we use a normal runnable to know when the current JS chunk of code
3859 // is finished. We cannot use a WorkerRunnable because they are not
3860 // accepted any more by the worker, and we do not want to use a
3861 // WorkerControlRunnable because they are immediately executed.
3862 RefPtr<CancelingRunnable> r = new CancelingRunnable();
3863 mThread->nsThread::Dispatch(r.forget(), NS_DISPATCH_NORMAL);
3865 // At the same time, we want to be sure that we interrupt infinite loops.
3866 // The following runnable starts a timer that cancel the worker, from the
3867 // parent thread, after CANCELING_TIMEOUT millseconds.
3868 RefPtr<CancelingWithTimeoutOnParentRunnable> rr =
3869 new CancelingWithTimeoutOnParentRunnable(this);
3870 rr->Dispatch();
3873 void WorkerPrivate::ReportUseCounters() {
3874 AssertIsOnWorkerThread();
3876 static const bool kDebugUseCounters = false;
3878 if (mReportedUseCounters) {
3879 return;
3881 mReportedUseCounters = true;
3883 if (Telemetry::HistogramUseCounterWorkerCount <= 0 || IsChromeWorker()) {
3884 return;
3887 const size_t type = Type();
3888 switch (type) {
3889 case WorkerTypeDedicated:
3890 Telemetry::Accumulate(Telemetry::DEDICATED_WORKER_DESTROYED, 1);
3891 break;
3892 case WorkerTypeShared:
3893 Telemetry::Accumulate(Telemetry::SHARED_WORKER_DESTROYED, 1);
3894 break;
3895 case WorkerTypeService:
3896 Telemetry::Accumulate(Telemetry::SERVICE_WORKER_DESTROYED, 1);
3897 break;
3898 default:
3899 MOZ_ASSERT(false, "Unknown worker type");
3900 return;
3903 if (kDebugUseCounters) {
3904 nsAutoCString path(Domain());
3905 path.AppendLiteral("(");
3906 NS_ConvertUTF16toUTF8 script(ScriptURL());
3907 path.Append(script);
3908 path.AppendPrintf(", 0x%p)", static_cast<void*>(this));
3909 printf("-- Worker use counters for %s --\n", path.get());
3912 static_assert(
3913 static_cast<size_t>(UseCounterWorker::Count) * 3 ==
3914 static_cast<size_t>(Telemetry::HistogramUseCounterWorkerCount),
3915 "There should be three histograms (dedicated and shared and "
3916 "servie) for each worker use counter");
3917 const size_t count = static_cast<size_t>(UseCounterWorker::Count);
3918 const size_t factor =
3919 static_cast<size_t>(Telemetry::HistogramUseCounterWorkerCount) / count;
3920 MOZ_ASSERT(factor > type);
3922 for (size_t c = 0; c < count; ++c) {
3923 // Histograms for worker use counters use the same order as the worker types
3924 // , so we can use the worker type to index to corresponding histogram.
3925 Telemetry::HistogramID id = static_cast<Telemetry::HistogramID>(
3926 Telemetry::HistogramFirstUseCounterWorker + c * factor + type);
3927 MOZ_ASSERT(id <= Telemetry::HistogramLastUseCounterWorker);
3929 if (bool value = GetUseCounter(static_cast<UseCounterWorker>(c))) {
3930 Telemetry::Accumulate(id, 1);
3932 if (kDebugUseCounters) {
3933 const char* name = Telemetry::GetHistogramName(id);
3934 printf(" %s #%d: %d\n", name, id, value);
3940 void WorkerPrivate::StopSyncLoop(nsIEventTarget* aSyncLoopTarget,
3941 bool aResult) {
3942 AssertIsOnWorkerThread();
3943 AssertValidSyncLoop(aSyncLoopTarget);
3945 MOZ_ASSERT(!mSyncLoopStack.IsEmpty());
3947 for (uint32_t index = mSyncLoopStack.Length(); index > 0; index--) {
3948 nsAutoPtr<SyncLoopInfo>& loopInfo = mSyncLoopStack[index - 1];
3949 MOZ_ASSERT(loopInfo);
3950 MOZ_ASSERT(loopInfo->mEventTarget);
3952 if (loopInfo->mEventTarget == aSyncLoopTarget) {
3953 // Can't assert |loop->mHasRun| here because dispatch failures can cause
3954 // us to bail out early.
3955 MOZ_ASSERT(!loopInfo->mCompleted);
3957 loopInfo->mResult = aResult;
3958 loopInfo->mCompleted = true;
3960 loopInfo->mEventTarget->Disable();
3962 return;
3965 MOZ_ASSERT(!SameCOMIdentity(loopInfo->mEventTarget, aSyncLoopTarget));
3968 MOZ_CRASH("Unknown sync loop!");
3971 #ifdef DEBUG
3972 void WorkerPrivate::AssertValidSyncLoop(nsIEventTarget* aSyncLoopTarget) {
3973 MOZ_ASSERT(aSyncLoopTarget);
3975 EventTarget* workerTarget;
3976 nsresult rv = aSyncLoopTarget->QueryInterface(
3977 kDEBUGWorkerEventTargetIID, reinterpret_cast<void**>(&workerTarget));
3978 MOZ_ASSERT(NS_SUCCEEDED(rv));
3979 MOZ_ASSERT(workerTarget);
3981 bool valid = false;
3984 MutexAutoLock lock(mMutex);
3986 for (uint32_t index = 0; index < mSyncLoopStack.Length(); index++) {
3987 nsAutoPtr<SyncLoopInfo>& loopInfo = mSyncLoopStack[index];
3988 MOZ_ASSERT(loopInfo);
3989 MOZ_ASSERT(loopInfo->mEventTarget);
3991 if (loopInfo->mEventTarget == aSyncLoopTarget) {
3992 valid = true;
3993 break;
3996 MOZ_ASSERT(!SameCOMIdentity(loopInfo->mEventTarget, aSyncLoopTarget));
4000 MOZ_ASSERT(valid);
4002 #endif
4004 void WorkerPrivate::PostMessageToParent(
4005 JSContext* aCx, JS::Handle<JS::Value> aMessage,
4006 const Sequence<JSObject*>& aTransferable, ErrorResult& aRv) {
4007 AssertIsOnWorkerThread();
4008 MOZ_DIAGNOSTIC_ASSERT(IsDedicatedWorker());
4010 JS::Rooted<JS::Value> transferable(aCx, JS::UndefinedValue());
4012 aRv = nsContentUtils::CreateJSValueFromSequenceOfObject(aCx, aTransferable,
4013 &transferable);
4014 if (NS_WARN_IF(aRv.Failed())) {
4015 return;
4018 RefPtr<MessageEventRunnable> runnable = new MessageEventRunnable(
4019 this, WorkerRunnable::ParentThreadUnchangedBusyCount);
4021 UniquePtr<AbstractTimelineMarker> start;
4022 UniquePtr<AbstractTimelineMarker> end;
4023 RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
4024 bool isTimelineRecording = timelines && !timelines->IsEmpty();
4026 if (isTimelineRecording) {
4027 start = MakeUnique<WorkerTimelineMarker>(
4028 NS_IsMainThread()
4029 ? ProfileTimelineWorkerOperationType::SerializeDataOnMainThread
4030 : ProfileTimelineWorkerOperationType::SerializeDataOffMainThread,
4031 MarkerTracingType::START);
4034 JS::CloneDataPolicy clonePolicy;
4035 if (IsSharedMemoryAllowed()) {
4036 clonePolicy.allowIntraClusterClonableSharedObjects();
4038 runnable->Write(aCx, aMessage, transferable, clonePolicy, aRv);
4040 if (isTimelineRecording) {
4041 end = MakeUnique<WorkerTimelineMarker>(
4042 NS_IsMainThread()
4043 ? ProfileTimelineWorkerOperationType::SerializeDataOnMainThread
4044 : ProfileTimelineWorkerOperationType::SerializeDataOffMainThread,
4045 MarkerTracingType::END);
4046 timelines->AddMarkerForAllObservedDocShells(start);
4047 timelines->AddMarkerForAllObservedDocShells(end);
4050 if (NS_WARN_IF(aRv.Failed())) {
4051 return;
4054 if (!runnable->Dispatch()) {
4055 aRv = NS_ERROR_FAILURE;
4059 void WorkerPrivate::EnterDebuggerEventLoop() {
4060 MOZ_ACCESS_THREAD_BOUND(mWorkerThreadAccessible, data);
4062 JSContext* cx = GetJSContext();
4063 MOZ_ASSERT(cx);
4065 AutoPushEventLoopGlobal eventLoopGlobal(this, cx);
4067 CycleCollectedJSContext* ccjscx = CycleCollectedJSContext::Get();
4069 uint32_t currentEventLoopLevel = ++data->mDebuggerEventLoopLevel;
4071 while (currentEventLoopLevel <= data->mDebuggerEventLoopLevel) {
4072 bool debuggerRunnablesPending = false;
4075 MutexAutoLock lock(mMutex);
4077 debuggerRunnablesPending = !mDebuggerQueue.IsEmpty();
4080 // Don't block with the periodic GC timer running.
4081 if (!debuggerRunnablesPending) {
4082 SetGCTimerMode(IdleTimer);
4085 // Wait for something to do
4087 MutexAutoLock lock(mMutex);
4089 std::queue<RefPtr<MicroTaskRunnable>>& debuggerMtQueue =
4090 ccjscx->GetDebuggerMicroTaskQueue();
4091 while (mControlQueue.IsEmpty() &&
4092 !(debuggerRunnablesPending = !mDebuggerQueue.IsEmpty()) &&
4093 debuggerMtQueue.empty()) {
4094 WaitForWorkerEvents();
4097 ProcessAllControlRunnablesLocked();
4099 // XXXkhuey should we abort JS on the stack here if we got Abort above?
4101 ccjscx->PerformDebuggerMicroTaskCheckpoint();
4102 if (debuggerRunnablesPending) {
4103 // Start the periodic GC timer if it is not already running.
4104 SetGCTimerMode(PeriodicTimer);
4106 WorkerRunnable* runnable = nullptr;
4109 MutexAutoLock lock(mMutex);
4111 mDebuggerQueue.Pop(runnable);
4114 MOZ_ASSERT(runnable);
4115 static_cast<nsIRunnable*>(runnable)->Run();
4116 runnable->Release();
4118 ccjscx->PerformDebuggerMicroTaskCheckpoint();
4120 // Now *might* be a good time to GC. Let the JS engine make the decision.
4121 if (GetCurrentEventLoopGlobal()) {
4122 // If GetCurrentEventLoopGlobal() is non-null, our JSContext is in a
4123 // Realm, so it's safe to try to GC.
4124 MOZ_ASSERT(JS::CurrentGlobalOrNull(cx));
4125 JS_MaybeGC(cx);
4131 void WorkerPrivate::LeaveDebuggerEventLoop() {
4132 MOZ_ACCESS_THREAD_BOUND(mWorkerThreadAccessible, data);
4134 // TODO: Why lock the mutex if we're accessing data accessible to one thread
4135 // only?
4136 MutexAutoLock lock(mMutex);
4138 if (data->mDebuggerEventLoopLevel > 0) {
4139 --data->mDebuggerEventLoopLevel;
4143 void WorkerPrivate::PostMessageToDebugger(const nsAString& aMessage) {
4144 mDebugger->PostMessageToDebugger(aMessage);
4147 void WorkerPrivate::SetDebuggerImmediate(dom::Function& aHandler,
4148 ErrorResult& aRv) {
4149 AssertIsOnWorkerThread();
4151 RefPtr<DebuggerImmediateRunnable> runnable =
4152 new DebuggerImmediateRunnable(this, aHandler);
4153 if (!runnable->Dispatch()) {
4154 aRv.Throw(NS_ERROR_FAILURE);
4158 void WorkerPrivate::ReportErrorToDebugger(const nsAString& aFilename,
4159 uint32_t aLineno,
4160 const nsAString& aMessage) {
4161 mDebugger->ReportErrorToDebugger(aFilename, aLineno, aMessage);
4164 bool WorkerPrivate::NotifyInternal(WorkerStatus aStatus) {
4165 MOZ_ACCESS_THREAD_BOUND(mWorkerThreadAccessible, data);
4167 NS_ASSERTION(aStatus > Running && aStatus < Dead, "Bad status!");
4169 RefPtr<EventTarget> eventTarget;
4171 // Save the old status and set the new status.
4172 WorkerStatus previousStatus;
4174 MutexAutoLock lock(mMutex);
4176 if (mStatus >= aStatus) {
4177 return true;
4180 MOZ_ASSERT_IF(aStatus == Killing, mStatus == Canceling);
4182 if (aStatus >= Canceling) {
4183 MutexAutoUnlock unlock(mMutex);
4184 data->mClientSource.reset();
4185 if (data->mScope) {
4186 data->mScope->NoteTerminating();
4190 // Make sure the hybrid event target stops dispatching runnables
4191 // once we reaching the killing state.
4192 if (aStatus == Killing) {
4193 // To avoid deadlock we always acquire the event target mutex before the
4194 // worker private mutex. (We do it in this order because this is what
4195 // workers best for event dispatching.) To enforce that order here we
4196 // need to unlock the worker private mutex before we lock the event target
4197 // mutex in ForgetWorkerPrivate.
4199 MutexAutoUnlock unlock(mMutex);
4200 mWorkerHybridEventTarget->ForgetWorkerPrivate(this);
4203 // Check the status code again in case another NotifyInternal came in
4204 // while we were unlocked above.
4205 if (mStatus >= aStatus) {
4206 return true;
4210 previousStatus = mStatus;
4211 mStatus = aStatus;
4213 // Mark parent status as closing immediately to avoid new events being
4214 // dispatched after we clear the queue below.
4215 if (aStatus == Closing) {
4216 Close();
4220 MOZ_ASSERT(previousStatus != Pending);
4222 if (aStatus >= Closing) {
4223 CancelAllTimeouts();
4226 // Let all our holders know the new status.
4227 if (aStatus > Closing) {
4228 NotifyWorkerRefs(aStatus);
4231 // If this is the first time our status has changed then we need to clear the
4232 // main event queue.
4233 if (previousStatus == Running) {
4234 // NB: If we're in a sync loop, we can't clear the queue immediately,
4235 // because this is the wrong queue. So we have to defer it until later.
4236 if (!mSyncLoopStack.IsEmpty()) {
4237 mPostSyncLoopOperations |= ePendingEventQueueClearing;
4238 } else {
4239 ClearMainEventQueue(WorkerRan);
4243 // If the worker script never ran, or failed to compile, we don't need to do
4244 // anything else.
4245 if (!GlobalScope()) {
4246 return true;
4249 // Don't abort the script now, but we dispatch a runnable to do it when the
4250 // current JS frame is executed.
4251 if (aStatus == Closing) {
4252 if (!mSyncLoopStack.IsEmpty()) {
4253 mPostSyncLoopOperations |= eDispatchCancelingRunnable;
4254 } else {
4255 DispatchCancelingRunnable();
4257 return true;
4260 MOZ_ASSERT(aStatus == Canceling || aStatus == Killing);
4262 // Always abort the script.
4263 return false;
4266 void WorkerPrivate::ReportError(JSContext* aCx,
4267 JS::ConstUTF8CharsZ aToStringResult,
4268 JSErrorReport* aReport) {
4269 MOZ_ACCESS_THREAD_BOUND(mWorkerThreadAccessible, data);
4271 if (!MayContinueRunning() || data->mErrorHandlerRecursionCount == 2) {
4272 return;
4275 NS_ASSERTION(data->mErrorHandlerRecursionCount == 0 ||
4276 data->mErrorHandlerRecursionCount == 1,
4277 "Bad recursion logic!");
4279 JS::Rooted<JS::Value> exn(aCx);
4280 if (!JS_GetPendingException(aCx, &exn)) {
4281 // Probably shouldn't actually happen? But let's go ahead and just use null
4282 // for lack of anything better.
4283 exn.setNull();
4285 JS::RootedObject exnStack(aCx, JS::GetPendingExceptionStack(aCx));
4286 JS_ClearPendingException(aCx);
4288 UniquePtr<WorkerErrorReport> report = MakeUnique<WorkerErrorReport>();
4289 if (aReport) {
4290 report->AssignErrorReport(aReport);
4291 } else {
4292 report->mFlags = nsIScriptError::errorFlag | nsIScriptError::exceptionFlag;
4295 JS::RootedObject stack(aCx), stackGlobal(aCx);
4296 xpc::FindExceptionStackForConsoleReport(nullptr, exn, exnStack, &stack,
4297 &stackGlobal);
4299 if (stack) {
4300 JSAutoRealm ar(aCx, stackGlobal);
4301 report->SerializeWorkerStack(aCx, this, stack);
4304 if (report->mMessage.IsEmpty() && aToStringResult) {
4305 nsDependentCString toStringResult(aToStringResult.c_str());
4306 if (!AppendUTF8toUTF16(toStringResult, report->mMessage,
4307 mozilla::fallible)) {
4308 // Try again, with only a 1 KB string. Do this infallibly this time.
4309 // If the user doesn't have 1 KB to spare we're done anyways.
4310 uint32_t index = std::min(uint32_t(1024), toStringResult.Length());
4312 // Drop the last code point that may be cropped.
4313 index = RewindToPriorUTF8Codepoint(toStringResult.BeginReading(), index);
4315 nsDependentCString truncatedToStringResult(aToStringResult.c_str(),
4316 index);
4317 AppendUTF8toUTF16(truncatedToStringResult, report->mMessage);
4321 data->mErrorHandlerRecursionCount++;
4323 // Don't want to run the scope's error handler if this is a recursive error or
4324 // if we ran out of memory.
4325 bool fireAtScope = data->mErrorHandlerRecursionCount == 1 &&
4326 report->mErrorNumber != JSMSG_OUT_OF_MEMORY &&
4327 JS::CurrentGlobalOrNull(aCx);
4329 WorkerErrorReport::ReportError(aCx, this, fireAtScope, nullptr,
4330 std::move(report), 0, exn);
4332 data->mErrorHandlerRecursionCount--;
4335 // static
4336 void WorkerPrivate::ReportErrorToConsole(const char* aMessage) {
4337 nsTArray<nsString> emptyParams;
4338 WorkerPrivate::ReportErrorToConsole(aMessage, emptyParams);
4341 // static
4342 void WorkerPrivate::ReportErrorToConsole(const char* aMessage,
4343 const nsTArray<nsString>& aParams) {
4344 WorkerPrivate* wp = nullptr;
4345 if (!NS_IsMainThread()) {
4346 wp = GetCurrentThreadWorkerPrivate();
4349 ReportErrorToConsoleRunnable::Report(wp, aMessage, aParams);
4352 int32_t WorkerPrivate::SetTimeout(JSContext* aCx, TimeoutHandler* aHandler,
4353 int32_t aTimeout, bool aIsInterval,
4354 ErrorResult& aRv) {
4355 MOZ_ACCESS_THREAD_BOUND(mWorkerThreadAccessible, data);
4356 MOZ_ASSERT(aHandler);
4358 const int32_t timerId = data->mNextTimeoutId++;
4360 WorkerStatus currentStatus;
4362 MutexAutoLock lock(mMutex);
4363 currentStatus = mStatus;
4366 // If the worker is trying to call setTimeout/setInterval and the parent
4367 // thread has initiated the close process then just silently fail.
4368 if (currentStatus >= Closing) {
4369 return timerId;
4372 nsAutoPtr<TimeoutInfo> newInfo(new TimeoutInfo());
4373 newInfo->mIsInterval = aIsInterval;
4374 newInfo->mId = timerId;
4376 if (MOZ_UNLIKELY(timerId == INT32_MAX)) {
4377 NS_WARNING("Timeout ids overflowed!");
4378 data->mNextTimeoutId = 1;
4381 newInfo->mHandler = aHandler;
4383 // See if any of the optional arguments were passed.
4384 aTimeout = std::max(0, aTimeout);
4385 newInfo->mInterval = TimeDuration::FromMilliseconds(aTimeout);
4387 newInfo->mTargetTime = TimeStamp::Now() + newInfo->mInterval;
4389 nsAutoPtr<TimeoutInfo>* insertedInfo = data->mTimeouts.InsertElementSorted(
4390 newInfo.forget(), GetAutoPtrComparator(data->mTimeouts));
4392 LOG(TimeoutsLog(), ("Worker %p has new timeout: delay=%d interval=%s\n", this,
4393 aTimeout, aIsInterval ? "yes" : "no"));
4395 // If the timeout we just made is set to fire next then we need to update the
4396 // timer, unless we're currently running timeouts.
4397 if (insertedInfo == data->mTimeouts.Elements() &&
4398 !data->mRunningExpiredTimeouts) {
4399 if (!data->mTimer) {
4400 data->mTimer = NS_NewTimer();
4401 if (!data->mTimer) {
4402 aRv.Throw(NS_ERROR_UNEXPECTED);
4403 return 0;
4406 data->mTimerRunnable = new TimerRunnable(this);
4409 if (!data->mTimerRunning) {
4410 if (!ModifyBusyCountFromWorker(true)) {
4411 aRv.Throw(NS_ERROR_FAILURE);
4412 return 0;
4414 data->mTimerRunning = true;
4417 if (!RescheduleTimeoutTimer(aCx)) {
4418 aRv.Throw(NS_ERROR_FAILURE);
4419 return 0;
4423 return timerId;
4426 void WorkerPrivate::ClearTimeout(int32_t aId) {
4427 MOZ_ACCESS_THREAD_BOUND(mWorkerThreadAccessible, data);
4429 if (!data->mTimeouts.IsEmpty()) {
4430 NS_ASSERTION(data->mTimerRunning, "Huh?!");
4432 for (uint32_t index = 0; index < data->mTimeouts.Length(); index++) {
4433 nsAutoPtr<TimeoutInfo>& info = data->mTimeouts[index];
4434 if (info->mId == aId) {
4435 info->mCanceled = true;
4436 break;
4442 bool WorkerPrivate::RunExpiredTimeouts(JSContext* aCx) {
4443 MOZ_ACCESS_THREAD_BOUND(mWorkerThreadAccessible, data);
4445 // We may be called recursively (e.g. close() inside a timeout) or we could
4446 // have been canceled while this event was pending, bail out if there is
4447 // nothing to do.
4448 if (data->mRunningExpiredTimeouts || !data->mTimerRunning) {
4449 return true;
4452 NS_ASSERTION(data->mTimer && data->mTimerRunnable, "Must have a timer!");
4453 NS_ASSERTION(!data->mTimeouts.IsEmpty(), "Should have some work to do!");
4455 bool retval = true;
4457 AutoPtrComparator<TimeoutInfo> comparator =
4458 GetAutoPtrComparator(data->mTimeouts);
4459 JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx));
4461 // We want to make sure to run *something*, even if the timer fired a little
4462 // early. Fudge the value of now to at least include the first timeout.
4463 const TimeStamp actual_now = TimeStamp::Now();
4464 const TimeStamp now = std::max(actual_now, data->mTimeouts[0]->mTargetTime);
4466 if (now != actual_now) {
4467 LOG(TimeoutsLog(), ("Worker %p fudged timeout by %f ms.\n", this,
4468 (now - actual_now).ToMilliseconds()));
4471 AutoTArray<TimeoutInfo*, 10> expiredTimeouts;
4472 for (uint32_t index = 0; index < data->mTimeouts.Length(); index++) {
4473 nsAutoPtr<TimeoutInfo>& info = data->mTimeouts[index];
4474 if (info->mTargetTime > now) {
4475 break;
4477 expiredTimeouts.AppendElement(info);
4480 // Guard against recursion.
4481 data->mRunningExpiredTimeouts = true;
4483 // Run expired timeouts.
4484 for (uint32_t index = 0; index < expiredTimeouts.Length(); index++) {
4485 TimeoutInfo*& info = expiredTimeouts[index];
4487 if (info->mCanceled) {
4488 continue;
4491 LOG(TimeoutsLog(),
4492 ("Worker %p executing timeout with original delay %f ms.\n", this,
4493 info->mInterval.ToMilliseconds()));
4495 // Always check JS_IsExceptionPending if something fails, and if
4496 // JS_IsExceptionPending returns false (i.e. uncatchable exception) then
4497 // break out of the loop.
4498 const char* reason;
4499 if (info->mIsInterval) {
4500 reason = "setInterval handler";
4501 } else {
4502 reason = "setTimeout handler";
4505 RefPtr<TimeoutHandler> handler(info->mHandler);
4507 RefPtr<WorkerGlobalScope> scope(this->GlobalScope());
4508 CallbackDebuggerNotificationGuard guard(
4509 scope, info->mIsInterval
4510 ? DebuggerNotificationType::SetIntervalCallback
4511 : DebuggerNotificationType::SetTimeoutCallback);
4512 if (!handler->Call(reason)) {
4513 retval = false;
4514 break;
4517 NS_ASSERTION(data->mRunningExpiredTimeouts, "Someone changed this!");
4520 // No longer possible to be called recursively.
4521 data->mRunningExpiredTimeouts = false;
4523 // Now remove canceled and expired timeouts from the main list.
4524 // NB: The timeouts present in expiredTimeouts must have the same order
4525 // with respect to each other in mTimeouts. That is, mTimeouts is just
4526 // expiredTimeouts with extra elements inserted. There may be unexpired
4527 // timeouts that have been inserted between the expired timeouts if the
4528 // timeout event handler called setTimeout/setInterval.
4529 for (uint32_t index = 0, expiredTimeoutIndex = 0,
4530 expiredTimeoutLength = expiredTimeouts.Length();
4531 index < data->mTimeouts.Length();) {
4532 nsAutoPtr<TimeoutInfo>& info = data->mTimeouts[index];
4533 if ((expiredTimeoutIndex < expiredTimeoutLength &&
4534 info == expiredTimeouts[expiredTimeoutIndex] &&
4535 ++expiredTimeoutIndex) ||
4536 info->mCanceled) {
4537 if (info->mIsInterval && !info->mCanceled) {
4538 // Reschedule intervals.
4539 info->mTargetTime = info->mTargetTime + info->mInterval;
4540 // Don't resort the list here, we'll do that at the end.
4541 ++index;
4542 } else {
4543 data->mTimeouts.RemoveElement(info);
4545 } else {
4546 // If info did not match the current entry in expiredTimeouts, it
4547 // shouldn't be there at all.
4548 NS_ASSERTION(!expiredTimeouts.Contains(info),
4549 "Our timeouts are out of order!");
4550 ++index;
4554 data->mTimeouts.Sort(comparator);
4556 // Either signal the parent that we're no longer using timeouts or reschedule
4557 // the timer.
4558 if (data->mTimeouts.IsEmpty()) {
4559 if (!ModifyBusyCountFromWorker(false)) {
4560 retval = false;
4562 data->mTimerRunning = false;
4563 } else if (retval && !RescheduleTimeoutTimer(aCx)) {
4564 retval = false;
4567 return retval;
4570 bool WorkerPrivate::RescheduleTimeoutTimer(JSContext* aCx) {
4571 MOZ_ACCESS_THREAD_BOUND(mWorkerThreadAccessible, data);
4572 MOZ_ASSERT(!data->mRunningExpiredTimeouts);
4573 NS_ASSERTION(!data->mTimeouts.IsEmpty(), "Should have some timeouts!");
4574 NS_ASSERTION(data->mTimer && data->mTimerRunnable, "Should have a timer!");
4576 // NB: This is important! The timer may have already fired, e.g. if a timeout
4577 // callback itself calls setTimeout for a short duration and then takes longer
4578 // than that to finish executing. If that has happened, it's very important
4579 // that we don't execute the event that is now pending in our event queue, or
4580 // our code in RunExpiredTimeouts to "fudge" the timeout value will unleash an
4581 // early timeout when we execute the event we're about to queue.
4582 data->mTimer->Cancel();
4584 double delta =
4585 (data->mTimeouts[0]->mTargetTime - TimeStamp::Now()).ToMilliseconds();
4586 uint32_t delay = delta > 0 ? std::min(delta, double(UINT32_MAX)) : 0;
4588 LOG(TimeoutsLog(),
4589 ("Worker %p scheduled timer for %d ms, %zu pending timeouts\n", this,
4590 delay, data->mTimeouts.Length()));
4592 nsresult rv = data->mTimer->InitWithCallback(data->mTimerRunnable, delay,
4593 nsITimer::TYPE_ONE_SHOT);
4594 if (NS_FAILED(rv)) {
4595 JS_ReportErrorASCII(aCx, "Failed to start timer!");
4596 return false;
4599 return true;
4602 void WorkerPrivate::StartCancelingTimer() {
4603 AssertIsOnParentThread();
4605 auto errorCleanup = MakeScopeExit([&] { mCancelingTimer = nullptr; });
4607 MOZ_ASSERT(!mCancelingTimer);
4609 if (WorkerPrivate* parent = GetParent()) {
4610 mCancelingTimer = NS_NewTimer(parent->ControlEventTarget());
4611 } else {
4612 mCancelingTimer = NS_NewTimer();
4615 if (NS_WARN_IF(!mCancelingTimer)) {
4616 return;
4619 // This is not needed if we are already in an advanced shutdown state.
4621 MutexAutoLock lock(mMutex);
4622 if (ParentStatus() >= Canceling) {
4623 return;
4627 uint32_t cancelingTimeoutMillis =
4628 StaticPrefs::dom_worker_canceling_timeoutMilliseconds();
4630 RefPtr<CancelingTimerCallback> callback = new CancelingTimerCallback(this);
4631 nsresult rv = mCancelingTimer->InitWithCallback(
4632 callback, cancelingTimeoutMillis, nsITimer::TYPE_ONE_SHOT);
4633 if (NS_WARN_IF(NS_FAILED(rv))) {
4634 return;
4637 errorCleanup.release();
4640 void WorkerPrivate::UpdateContextOptionsInternal(
4641 JSContext* aCx, const JS::ContextOptions& aContextOptions) {
4642 MOZ_ACCESS_THREAD_BOUND(mWorkerThreadAccessible, data);
4644 JS::ContextOptionsRef(aCx) = aContextOptions;
4646 for (uint32_t index = 0; index < data->mChildWorkers.Length(); index++) {
4647 data->mChildWorkers[index]->UpdateContextOptions(aContextOptions);
4651 void WorkerPrivate::UpdateLanguagesInternal(
4652 const nsTArray<nsString>& aLanguages) {
4653 WorkerGlobalScope* globalScope = GlobalScope();
4654 RefPtr<WorkerNavigator> nav = globalScope->GetExistingNavigator();
4655 if (nav) {
4656 nav->SetLanguages(aLanguages);
4659 MOZ_ACCESS_THREAD_BOUND(mWorkerThreadAccessible, data);
4660 for (uint32_t index = 0; index < data->mChildWorkers.Length(); index++) {
4661 data->mChildWorkers[index]->UpdateLanguages(aLanguages);
4664 RefPtr<Event> event = NS_NewDOMEvent(globalScope, nullptr, nullptr);
4666 event->InitEvent(NS_LITERAL_STRING("languagechange"), false, false);
4667 event->SetTrusted(true);
4669 globalScope->DispatchEvent(*event);
4672 void WorkerPrivate::UpdateJSWorkerMemoryParameterInternal(JSContext* aCx,
4673 JSGCParamKey aKey,
4674 uint32_t aValue) {
4675 MOZ_ACCESS_THREAD_BOUND(mWorkerThreadAccessible, data);
4677 // XXX aValue might be 0 here (telling us to unset a previous value for child
4678 // workers). Calling JS_SetGCParameter with a value of 0 isn't actually
4679 // supported though. We really need some way to revert to a default value
4680 // here.
4681 if (aValue) {
4682 JS_SetGCParameter(aCx, aKey, aValue);
4685 for (uint32_t index = 0; index < data->mChildWorkers.Length(); index++) {
4686 data->mChildWorkers[index]->UpdateJSWorkerMemoryParameter(aKey, aValue);
4690 #ifdef JS_GC_ZEAL
4691 void WorkerPrivate::UpdateGCZealInternal(JSContext* aCx, uint8_t aGCZeal,
4692 uint32_t aFrequency) {
4693 MOZ_ACCESS_THREAD_BOUND(mWorkerThreadAccessible, data);
4695 JS_SetGCZeal(aCx, aGCZeal, aFrequency);
4697 for (uint32_t index = 0; index < data->mChildWorkers.Length(); index++) {
4698 data->mChildWorkers[index]->UpdateGCZeal(aGCZeal, aFrequency);
4701 #endif
4703 void WorkerPrivate::SetLowMemoryStateInternal(JSContext* aCx, bool aState) {
4704 MOZ_ACCESS_THREAD_BOUND(mWorkerThreadAccessible, data);
4706 JS::SetLowMemoryState(aCx, aState);
4708 for (uint32_t index = 0; index < data->mChildWorkers.Length(); index++) {
4709 data->mChildWorkers[index]->SetLowMemoryState(aState);
4713 void WorkerPrivate::GarbageCollectInternal(JSContext* aCx, bool aShrinking,
4714 bool aCollectChildren) {
4715 MOZ_ACCESS_THREAD_BOUND(mWorkerThreadAccessible, data);
4717 if (!GlobalScope()) {
4718 // We haven't compiled anything yet. Just bail out.
4719 return;
4722 if (aShrinking || aCollectChildren) {
4723 JS::PrepareForFullGC(aCx);
4725 if (aShrinking) {
4726 JS::NonIncrementalGC(aCx, GC_SHRINK, JS::GCReason::DOM_WORKER);
4728 if (!aCollectChildren) {
4729 LOG(WorkerLog(), ("Worker %p collected idle garbage\n", this));
4731 } else {
4732 JS::NonIncrementalGC(aCx, GC_NORMAL, JS::GCReason::DOM_WORKER);
4733 LOG(WorkerLog(), ("Worker %p collected garbage\n", this));
4735 } else {
4736 JS_MaybeGC(aCx);
4737 LOG(WorkerLog(), ("Worker %p collected periodic garbage\n", this));
4740 if (aCollectChildren) {
4741 for (uint32_t index = 0; index < data->mChildWorkers.Length(); index++) {
4742 data->mChildWorkers[index]->GarbageCollect(aShrinking);
4747 void WorkerPrivate::CycleCollectInternal(bool aCollectChildren) {
4748 MOZ_ACCESS_THREAD_BOUND(mWorkerThreadAccessible, data);
4750 nsCycleCollector_collect(nullptr);
4752 if (aCollectChildren) {
4753 for (uint32_t index = 0; index < data->mChildWorkers.Length(); index++) {
4754 data->mChildWorkers[index]->CycleCollect(/* aDummy = */ false);
4759 void WorkerPrivate::MemoryPressureInternal() {
4760 MOZ_ACCESS_THREAD_BOUND(mWorkerThreadAccessible, data);
4762 if (data->mScope) {
4763 RefPtr<Console> console = data->mScope->GetConsoleIfExists();
4764 if (console) {
4765 console->ClearStorage();
4768 RefPtr<Performance> performance = data->mScope->GetPerformanceIfExists();
4769 if (performance) {
4770 performance->MemoryPressure();
4774 if (data->mDebuggerScope) {
4775 RefPtr<Console> console = data->mDebuggerScope->GetConsoleIfExists();
4776 if (console) {
4777 console->ClearStorage();
4781 for (uint32_t index = 0; index < data->mChildWorkers.Length(); index++) {
4782 data->mChildWorkers[index]->MemoryPressure(false);
4786 void WorkerPrivate::SetThread(WorkerThread* aThread) {
4787 if (aThread) {
4788 #ifdef DEBUG
4790 bool isOnCurrentThread;
4791 MOZ_ASSERT(NS_SUCCEEDED(aThread->IsOnCurrentThread(&isOnCurrentThread)));
4792 MOZ_ASSERT(!isOnCurrentThread);
4794 #endif
4796 MOZ_ASSERT(!mPRThread);
4797 mPRThread = PRThreadFromThread(aThread);
4798 MOZ_ASSERT(mPRThread);
4800 mWorkerThreadAccessible.Transfer(mPRThread);
4801 } else {
4802 MOZ_ASSERT(mPRThread);
4806 void WorkerPrivate::SetWorkerPrivateInWorkerThread(
4807 WorkerThread* const aThread) {
4808 MutexAutoLock lock(mMutex);
4810 MOZ_ASSERT(!mThread);
4811 MOZ_ASSERT(mStatus == Pending);
4813 mThread = aThread;
4814 mThread->SetWorker(WorkerThreadFriendKey{}, this);
4816 if (!mPreStartRunnables.IsEmpty()) {
4817 for (uint32_t index = 0; index < mPreStartRunnables.Length(); index++) {
4818 MOZ_ALWAYS_SUCCEEDS(mThread->DispatchAnyThread(
4819 WorkerThreadFriendKey{}, mPreStartRunnables[index].forget()));
4821 mPreStartRunnables.Clear();
4825 void WorkerPrivate::ResetWorkerPrivateInWorkerThread() {
4826 RefPtr<WorkerThread> doomedThread;
4828 // Release the mutex before doomedThread.
4829 MutexAutoLock lock(mMutex);
4831 MOZ_ASSERT(mThread);
4833 mThread->SetWorker(WorkerThreadFriendKey{}, nullptr);
4834 mThread.swap(doomedThread);
4837 void WorkerPrivate::BeginCTypesCall() {
4838 AssertIsOnWorkerThread();
4840 // Don't try to GC while we're blocked in a ctypes call.
4841 SetGCTimerMode(NoTimer);
4844 void WorkerPrivate::EndCTypesCall() {
4845 AssertIsOnWorkerThread();
4847 // Make sure the periodic timer is running before we start running JS again.
4848 SetGCTimerMode(PeriodicTimer);
4851 bool WorkerPrivate::ConnectMessagePort(JSContext* aCx,
4852 UniqueMessagePortId& aIdentifier) {
4853 AssertIsOnWorkerThread();
4855 WorkerGlobalScope* globalScope = GlobalScope();
4857 JS::Rooted<JSObject*> jsGlobal(aCx, globalScope->GetWrapper());
4858 MOZ_ASSERT(jsGlobal);
4860 // This UniqueMessagePortId is used to create a new port, still connected
4861 // with the other one, but in the worker thread.
4862 ErrorResult rv;
4863 RefPtr<MessagePort> port = MessagePort::Create(globalScope, aIdentifier, rv);
4864 if (NS_WARN_IF(rv.Failed())) {
4865 rv.SuppressException();
4866 return false;
4869 GlobalObject globalObject(aCx, jsGlobal);
4870 if (globalObject.Failed()) {
4871 return false;
4874 RootedDictionary<MessageEventInit> init(aCx);
4875 init.mData = JS_GetEmptyStringValue(aCx);
4876 init.mBubbles = false;
4877 init.mCancelable = false;
4878 init.mSource.SetValue().SetAsMessagePort() = port;
4879 if (!init.mPorts.AppendElement(port.forget(), fallible)) {
4880 return false;
4883 RefPtr<MessageEvent> event = MessageEvent::Constructor(
4884 globalObject, NS_LITERAL_STRING("connect"), init);
4886 event->SetTrusted(true);
4888 globalScope->DispatchEvent(*event);
4890 return true;
4893 WorkerGlobalScope* WorkerPrivate::GetOrCreateGlobalScope(JSContext* aCx) {
4894 MOZ_ACCESS_THREAD_BOUND(mWorkerThreadAccessible, data);
4896 if (!data->mScope) {
4897 RefPtr<WorkerGlobalScope> globalScope;
4898 if (IsSharedWorker()) {
4899 globalScope = new SharedWorkerGlobalScope(this, WorkerName());
4900 } else if (IsServiceWorker()) {
4901 globalScope = new ServiceWorkerGlobalScope(
4902 this, GetServiceWorkerRegistrationDescriptor());
4903 } else {
4904 globalScope = new DedicatedWorkerGlobalScope(this, WorkerName());
4907 JS::Rooted<JSObject*> global(aCx);
4908 NS_ENSURE_TRUE(globalScope->WrapGlobalObject(aCx, &global), nullptr);
4910 JSAutoRealm ar(aCx, global);
4912 // RegisterBindings() can spin a nested event loop so we have to set mScope
4913 // before calling it, and we have to make sure to unset mScope if it fails.
4914 data->mScope = std::move(globalScope);
4916 if (!RegisterBindings(aCx, global)) {
4917 data->mScope = nullptr;
4918 return nullptr;
4921 JS_FireOnNewGlobalObject(aCx, global);
4924 return data->mScope;
4927 WorkerDebuggerGlobalScope* WorkerPrivate::CreateDebuggerGlobalScope(
4928 JSContext* aCx) {
4929 MOZ_ACCESS_THREAD_BOUND(mWorkerThreadAccessible, data);
4931 MOZ_ASSERT(!data->mDebuggerScope);
4933 RefPtr<WorkerDebuggerGlobalScope> globalScope =
4934 new WorkerDebuggerGlobalScope(this);
4936 JS::Rooted<JSObject*> global(aCx);
4937 NS_ENSURE_TRUE(globalScope->WrapGlobalObject(aCx, &global), nullptr);
4939 JSAutoRealm ar(aCx, global);
4941 // RegisterDebuggerBindings() can spin a nested event loop so we have to set
4942 // mDebuggerScope before calling it, and we have to make sure to unset
4943 // mDebuggerScope if it fails.
4944 data->mDebuggerScope = std::move(globalScope);
4946 if (!RegisterDebuggerBindings(aCx, global)) {
4947 data->mDebuggerScope = nullptr;
4948 return nullptr;
4951 JS_FireOnNewGlobalObject(aCx, global);
4953 return data->mDebuggerScope;
4956 bool WorkerPrivate::IsOnWorkerThread() const {
4957 // We can't use mThread because it must be protected by mMutex and sometimes
4958 // this method is called when mMutex is already locked. This method should
4959 // always work.
4960 MOZ_ASSERT(mPRThread,
4961 "AssertIsOnWorkerThread() called before a thread was assigned!");
4963 return mPRThread == PR_GetCurrentThread();
4966 #ifdef DEBUG
4967 void WorkerPrivate::AssertIsOnWorkerThread() const {
4968 MOZ_ASSERT(IsOnWorkerThread());
4970 #endif // DEBUG
4972 void WorkerPrivate::DumpCrashInformation(nsACString& aString) {
4973 MOZ_ACCESS_THREAD_BOUND(mWorkerThreadAccessible, data);
4975 nsTObserverArray<WorkerRef*>::ForwardIterator iter(data->mWorkerRefs);
4976 while (iter.HasMore()) {
4977 WorkerRef* workerRef = iter.GetNext();
4978 if (workerRef->IsPreventingShutdown()) {
4979 aString.Append("|");
4980 aString.Append(workerRef->Name());
4985 void WorkerPrivate::EnsurePerformanceCounter() {
4986 AssertIsOnWorkerThread();
4987 if (!mPerformanceCounter) {
4988 nsPrintfCString workerName("Worker:%s",
4989 NS_ConvertUTF16toUTF8(mWorkerName).get());
4990 mPerformanceCounter = new PerformanceCounter(workerName);
4994 PerformanceCounter* WorkerPrivate::GetPerformanceCounter() {
4995 return mPerformanceCounter;
4998 PerformanceStorage* WorkerPrivate::GetPerformanceStorage() {
4999 AssertIsOnMainThread();
5000 MOZ_ASSERT(mPerformanceStorage);
5001 return mPerformanceStorage;
5004 void WorkerPrivate::SetRemoteWorkerController(RemoteWorkerChild* aController) {
5005 AssertIsOnMainThread();
5006 MOZ_ASSERT(aController);
5007 MOZ_ASSERT(!mRemoteWorkerController);
5009 mRemoteWorkerController = aController;
5012 RemoteWorkerChild* WorkerPrivate::GetRemoteWorkerController() {
5013 AssertIsOnMainThread();
5014 MOZ_ASSERT(mRemoteWorkerController);
5015 return mRemoteWorkerController;
5018 void WorkerPrivate::SetRemoteWorkerControllerWeakRef(
5019 ThreadSafeWeakPtr<RemoteWorkerChild> aWeakRef) {
5020 MOZ_ASSERT(aWeakRef);
5021 MOZ_ASSERT(!mRemoteWorkerControllerWeakRef);
5022 MOZ_ASSERT(IsServiceWorker());
5024 mRemoteWorkerControllerWeakRef = std::move(aWeakRef);
5027 ThreadSafeWeakPtr<RemoteWorkerChild>
5028 WorkerPrivate::GetRemoteWorkerControllerWeakRef() {
5029 MOZ_ASSERT(IsServiceWorker());
5030 return mRemoteWorkerControllerWeakRef;
5033 RefPtr<GenericPromise> WorkerPrivate::SetServiceWorkerSkipWaitingFlag() {
5034 AssertIsOnWorkerThread();
5035 MOZ_ASSERT(IsServiceWorker());
5037 RefPtr<RemoteWorkerChild> rwc(mRemoteWorkerControllerWeakRef);
5039 if (!rwc) {
5040 return GenericPromise::CreateAndReject(NS_ERROR_DOM_ABORT_ERR, __func__);
5043 RefPtr<GenericPromise> promise =
5044 rwc->MaybeSendSetServiceWorkerSkipWaitingFlag();
5046 NS_ProxyRelease("WorkerPrivate::mRemoteWorkerControllerWeakRef",
5047 RemoteWorkerService::Thread(), rwc.forget());
5049 return promise;
5052 const nsAString& WorkerPrivate::Id() {
5053 AssertIsOnMainThread();
5055 if (mId.IsEmpty()) {
5056 mId = ComputeWorkerPrivateId();
5059 MOZ_ASSERT(!mId.IsEmpty());
5061 return mId;
5064 bool WorkerPrivate::IsSharedMemoryAllowed() const {
5065 AssertIsOnWorkerThread();
5067 if (StaticPrefs::
5068 dom_postMessage_sharedArrayBuffer_bypassCOOP_COEP_insecure_enabled()) {
5069 return true;
5072 return CrossOriginIsolated();
5075 bool WorkerPrivate::CrossOriginIsolated() const {
5076 AssertIsOnWorkerThread();
5078 if (!StaticPrefs::dom_postMessage_sharedArrayBuffer_withCOOP_COEP()) {
5079 return false;
5082 return mAgentClusterOpenerPolicy ==
5083 nsILoadInfo::OPENER_POLICY_SAME_ORIGIN_EMBEDDER_POLICY_REQUIRE_CORP;
5086 NS_IMPL_ADDREF(WorkerPrivate::EventTarget)
5087 NS_IMPL_RELEASE(WorkerPrivate::EventTarget)
5089 NS_INTERFACE_MAP_BEGIN(WorkerPrivate::EventTarget)
5090 NS_INTERFACE_MAP_ENTRY(nsISerialEventTarget)
5091 NS_INTERFACE_MAP_ENTRY(nsIEventTarget)
5092 NS_INTERFACE_MAP_ENTRY(nsISupports)
5093 #ifdef DEBUG
5094 // kDEBUGWorkerEventTargetIID is special in that it does not AddRef its
5095 // result.
5096 if (aIID.Equals(kDEBUGWorkerEventTargetIID)) {
5097 *aInstancePtr = this;
5098 return NS_OK;
5099 } else
5100 #endif
5101 NS_INTERFACE_MAP_END
5103 NS_IMETHODIMP
5104 WorkerPrivate::EventTarget::DispatchFromScript(nsIRunnable* aRunnable,
5105 uint32_t aFlags) {
5106 nsCOMPtr<nsIRunnable> event(aRunnable);
5107 return Dispatch(event.forget(), aFlags);
5110 NS_IMETHODIMP
5111 WorkerPrivate::EventTarget::Dispatch(already_AddRefed<nsIRunnable> aRunnable,
5112 uint32_t aFlags) {
5113 // May be called on any thread!
5114 nsCOMPtr<nsIRunnable> event(aRunnable);
5116 // Workers only support asynchronous dispatch for now.
5117 if (NS_WARN_IF(aFlags != NS_DISPATCH_NORMAL)) {
5118 return NS_ERROR_UNEXPECTED;
5121 RefPtr<WorkerRunnable> workerRunnable;
5123 MutexAutoLock lock(mMutex);
5125 if (!mWorkerPrivate) {
5126 NS_WARNING(
5127 "A runnable was posted to a worker that is already shutting "
5128 "down!");
5129 return NS_ERROR_UNEXPECTED;
5132 if (event) {
5133 workerRunnable = mWorkerPrivate->MaybeWrapAsWorkerRunnable(event.forget());
5136 nsresult rv =
5137 mWorkerPrivate->Dispatch(workerRunnable.forget(), mNestedEventTarget);
5138 if (NS_WARN_IF(NS_FAILED(rv))) {
5139 return rv;
5142 return NS_OK;
5145 NS_IMETHODIMP
5146 WorkerPrivate::EventTarget::DelayedDispatch(already_AddRefed<nsIRunnable>,
5147 uint32_t)
5150 return NS_ERROR_NOT_IMPLEMENTED;
5153 NS_IMETHODIMP
5154 WorkerPrivate::EventTarget::IsOnCurrentThread(bool* aIsOnCurrentThread) {
5155 // May be called on any thread!
5157 MOZ_ASSERT(aIsOnCurrentThread);
5159 MutexAutoLock lock(mMutex);
5161 if (!mWorkerPrivate) {
5162 NS_WARNING("A worker's event target was used after the worker has !");
5163 return NS_ERROR_UNEXPECTED;
5166 *aIsOnCurrentThread = mWorkerPrivate->IsOnCurrentThread();
5167 return NS_OK;
5170 NS_IMETHODIMP_(bool)
5171 WorkerPrivate::EventTarget::IsOnCurrentThreadInfallible() {
5172 // May be called on any thread!
5174 MutexAutoLock lock(mMutex);
5176 if (!mWorkerPrivate) {
5177 NS_WARNING("A worker's event target was used after the worker has !");
5178 return false;
5181 return mWorkerPrivate->IsOnCurrentThread();
5184 WorkerPrivate::AutoPushEventLoopGlobal::AutoPushEventLoopGlobal(
5185 WorkerPrivate* aWorkerPrivate, JSContext* aCx)
5186 : mWorkerPrivate(aWorkerPrivate) {
5187 MOZ_ACCESS_THREAD_BOUND(mWorkerPrivate->mWorkerThreadAccessible, data);
5188 mOldEventLoopGlobal = data->mCurrentEventLoopGlobal.forget();
5189 if (JSObject* global = JS::CurrentGlobalOrNull(aCx)) {
5190 data->mCurrentEventLoopGlobal = xpc::NativeGlobal(global);
5194 WorkerPrivate::AutoPushEventLoopGlobal::~AutoPushEventLoopGlobal() {
5195 MOZ_ACCESS_THREAD_BOUND(mWorkerPrivate->mWorkerThreadAccessible, data);
5196 data->mCurrentEventLoopGlobal = mOldEventLoopGlobal.forget();
5199 } // namespace dom
5200 } // namespace mozilla