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"
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"
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"
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
; }
101 #define LOG(log, _args) MOZ_LOG(log, LogLevel::Debug, _args);
109 using namespace workerinternals
;
111 MOZ_DEFINE_MALLOC_SIZE_OF(JsWorkerMallocSizeOf
)
117 const nsIID kDEBUGWorkerEventTargetIID
= {
121 {0xba, 0x87, 0x3b, 0x3b, 0x5b, 0x1d, 0x5, 0xfb}};
126 class AutoPtrComparator
{
127 typedef nsAutoPtr
<T
> A
;
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;
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
;
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
)
163 ~ExternalRunnableWrapper() {}
165 virtual bool PreDispatch(WorkerPrivate
* aWorkerPrivate
) override
{
166 // Silence bad assertions.
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();
179 if (!JS_IsExceptionPending(aCx
)) {
187 nsresult
Cancel() override
{
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
;
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
;
214 WorkerFinishedRunnable(WorkerPrivate
* aWorkerPrivate
,
215 WorkerPrivate
* aFinishedWorker
)
216 : WorkerControlRunnable(aWorkerPrivate
, WorkerThreadUnchangedBusyCount
),
217 mFinishedWorker(aFinishedWorker
) {}
220 virtual bool PreDispatch(WorkerPrivate
* aWorkerPrivate
) override
{
221 // Silence bad assertions.
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();
248 class TopLevelWorkerFinishedRunnable final
: public Runnable
{
249 WorkerPrivate
* mFinishedWorker
;
252 explicit TopLevelWorkerFinishedRunnable(WorkerPrivate
* aFinishedWorker
)
253 : mozilla::Runnable("TopLevelWorkerFinishedRunnable"),
254 mFinishedWorker(aFinishedWorker
) {
255 aFinishedWorker
->AssertIsOnWorkerThread();
258 NS_INLINE_DECL_REFCOUNTING_INHERITED(TopLevelWorkerFinishedRunnable
, Runnable
)
261 ~TopLevelWorkerFinishedRunnable() {}
265 AssertIsOnMainThread();
267 RuntimeService
* runtime
= RuntimeService::GetService();
270 mFinishedWorker
->DisableDebugger();
272 runtime
->UnregisterWorker(mFinishedWorker
);
274 if (!mFinishedWorker
->ProxyReleaseMainThreadObjects()) {
275 NS_WARNING("Failed to dispatch, going to leak!");
278 mFinishedWorker
->ClearSelfAndParentEventTargetRef();
283 class ModifyBusyCountRunnable final
: public WorkerControlRunnable
{
287 ModifyBusyCountRunnable(WorkerPrivate
* aWorkerPrivate
, bool aIncrease
)
288 : WorkerControlRunnable(aWorkerPrivate
, ParentThreadUnchangedBusyCount
),
289 mIncrease(aIncrease
) {}
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
{
300 WorkerControlRunnable::PostRun(aCx
, aWorkerPrivate
, aRunResult
);
303 // Don't do anything here as it's possible that aWorkerPrivate has been
308 class CompileScriptRunnable final
: public WorkerDebuggeeRunnable
{
310 UniquePtr
<SerializedStackHolder
> mOriginStack
;
313 explicit CompileScriptRunnable(WorkerPrivate
* aWorkerPrivate
,
314 UniquePtr
<SerializedStackHolder
> aOriginStack
,
315 const nsAString
& aScriptURL
)
316 : WorkerDebuggeeRunnable(aWorkerPrivate
, WorkerThreadModifyBusyCount
),
317 mScriptURL(aScriptURL
),
318 mOriginStack(aOriginStack
.release()) {}
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())) {
333 if (NS_WARN_IF(!aWorkerPrivate
->EnsureCSPEventListener())) {
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
341 aWorkerPrivate
->EnsurePerformanceStorage();
342 aWorkerPrivate
->EnsurePerformanceCounter();
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();
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
362 rv
.SuppressException();
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(
373 rv
.SuppressException();
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
)) {
387 aWorkerPrivate
->SetWorkerScriptExecutedSuccessfully();
391 void PostRun(JSContext
* aCx
, WorkerPrivate
* aWorkerPrivate
,
392 bool aRunResult
) override
{
394 aWorkerPrivate
->CloseInternal();
396 WorkerRunnable::PostRun(aCx
, aWorkerPrivate
, aRunResult
);
400 class NotifyRunnable final
: public WorkerControlRunnable
{
401 WorkerStatus mStatus
;
404 NotifyRunnable(WorkerPrivate
* aWorkerPrivate
, WorkerStatus aStatus
)
405 : WorkerControlRunnable(aWorkerPrivate
, WorkerThreadUnchangedBusyCount
),
407 MOZ_ASSERT(aStatus
== Closing
|| aStatus
== Canceling
||
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
{
440 explicit FreezeRunnable(WorkerPrivate
* aWorkerPrivate
)
441 : WorkerControlRunnable(aWorkerPrivate
, WorkerThreadUnchangedBusyCount
) {}
444 virtual bool WorkerRun(JSContext
* aCx
,
445 WorkerPrivate
* aWorkerPrivate
) override
{
446 return aWorkerPrivate
->FreezeInternal();
450 class ThawRunnable final
: public WorkerControlRunnable
{
452 explicit ThawRunnable(WorkerPrivate
* aWorkerPrivate
)
453 : WorkerControlRunnable(aWorkerPrivate
, WorkerThreadUnchangedBusyCount
) {}
456 virtual bool WorkerRun(JSContext
* aCx
,
457 WorkerPrivate
* aWorkerPrivate
) override
{
458 return aWorkerPrivate
->ThawInternal();
462 class PropagateFirstPartyStorageAccessGrantedRunnable final
463 : public WorkerControlRunnable
{
465 explicit PropagateFirstPartyStorageAccessGrantedRunnable(
466 WorkerPrivate
* aWorkerPrivate
)
467 : WorkerControlRunnable(aWorkerPrivate
, WorkerThreadUnchangedBusyCount
) {}
470 bool WorkerRun(JSContext
* aCx
, WorkerPrivate
* aWorkerPrivate
) override
{
471 aWorkerPrivate
->PropagateFirstPartyStorageAccessGrantedInternal();
476 class ReportErrorToConsoleRunnable final
: public WorkerRunnable
{
477 const char* mMessage
;
478 const nsTArray
<nsString
> mParams
;
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();
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();
498 // Log a warning to the console.
499 nsContentUtils::ReportToConsole(
500 nsIScriptError::warningFlag
, NS_LITERAL_CSTRING("DOM"), nullptr,
501 nsContentUtils::eDOM_PROPERTIES
, aMessage
, aParams
);
505 ReportErrorToConsoleRunnable(WorkerPrivate
* aWorkerPrivate
,
506 const char* aMessage
,
507 const nsTArray
<nsString
>& aParams
)
508 : WorkerRunnable(aWorkerPrivate
, ParentThreadUnchangedBusyCount
),
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
);
529 class TimerRunnable final
: public WorkerRunnable
,
530 public nsITimerCallback
,
533 NS_DECL_ISUPPORTS_INHERITED
535 explicit TimerRunnable(WorkerPrivate
* aWorkerPrivate
)
536 : WorkerRunnable(aWorkerPrivate
, WorkerThreadUnchangedBusyCount
) {}
541 virtual bool PreDispatch(WorkerPrivate
* aWorkerPrivate
) override
{
542 // Silence bad assertions.
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
);
560 Notify(nsITimer
* aTimer
) override
{ return Run(); }
563 GetName(nsACString
& aName
) override
{
564 aName
.AssignLiteral("TimerRunnable");
569 NS_IMPL_ISUPPORTS_INHERITED(TimerRunnable
, WorkerRunnable
, nsITimerCallback
,
572 class DebuggerImmediateRunnable
: public WorkerRunnable
{
573 RefPtr
<dom::Function
> mHandler
;
576 explicit DebuggerImmediateRunnable(WorkerPrivate
* aWorkerPrivate
,
577 dom::Function
& aHandler
)
578 : WorkerRunnable(aWorkerPrivate
, WorkerThreadUnchangedBusyCount
),
579 mHandler(&aHandler
) {}
582 virtual bool IsDebuggerRunnable() const override
{ return true; }
584 virtual bool PreDispatch(WorkerPrivate
* aWorkerPrivate
) override
{
585 // Silence bad assertions.
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.
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
;
632 UpdateContextOptionsRunnable(WorkerPrivate
* aWorkerPrivate
,
633 const JS::ContextOptions
& aContextOptions
)
634 : WorkerControlRunnable(aWorkerPrivate
, WorkerThreadUnchangedBusyCount
),
635 mContextOptions(aContextOptions
) {}
638 virtual bool WorkerRun(JSContext
* aCx
,
639 WorkerPrivate
* aWorkerPrivate
) override
{
640 aWorkerPrivate
->UpdateContextOptionsInternal(aCx
, mContextOptions
);
645 class UpdateLanguagesRunnable final
: public WorkerRunnable
{
646 nsTArray
<nsString
> mLanguages
;
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
);
660 class UpdateJSWorkerMemoryParameterRunnable final
661 : public WorkerControlRunnable
{
666 UpdateJSWorkerMemoryParameterRunnable(WorkerPrivate
* aWorkerPrivate
,
667 JSGCParamKey aKey
, uint32_t aValue
)
668 : WorkerControlRunnable(aWorkerPrivate
, WorkerThreadUnchangedBusyCount
),
673 virtual bool WorkerRun(JSContext
* aCx
,
674 WorkerPrivate
* aWorkerPrivate
) override
{
675 aWorkerPrivate
->UpdateJSWorkerMemoryParameterInternal(aCx
, mKey
, mValue
);
681 class UpdateGCZealRunnable final
: public WorkerControlRunnable
{
686 UpdateGCZealRunnable(WorkerPrivate
* aWorkerPrivate
, uint8_t aGCZeal
,
688 : WorkerControlRunnable(aWorkerPrivate
, WorkerThreadUnchangedBusyCount
),
690 mFrequency(aFrequency
) {}
693 virtual bool WorkerRun(JSContext
* aCx
,
694 WorkerPrivate
* aWorkerPrivate
) override
{
695 aWorkerPrivate
->UpdateGCZealInternal(aCx
, mGCZeal
, mFrequency
);
701 class SetLowMemoryStateRunnable final
: public WorkerControlRunnable
{
705 SetLowMemoryStateRunnable(WorkerPrivate
* aWorkerPrivate
, bool aState
)
706 : WorkerControlRunnable(aWorkerPrivate
, WorkerThreadUnchangedBusyCount
),
710 virtual bool WorkerRun(JSContext
* aCx
,
711 WorkerPrivate
* aWorkerPrivate
) override
{
712 aWorkerPrivate
->SetLowMemoryStateInternal(aCx
, mState
);
717 class GarbageCollectRunnable final
: public WorkerControlRunnable
{
719 bool mCollectChildren
;
722 GarbageCollectRunnable(WorkerPrivate
* aWorkerPrivate
, bool aShrinking
,
723 bool aCollectChildren
)
724 : WorkerControlRunnable(aWorkerPrivate
, WorkerThreadUnchangedBusyCount
),
725 mShrinking(aShrinking
),
726 mCollectChildren(aCollectChildren
) {}
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..
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
);
748 class CycleCollectRunnable
: public WorkerControlRunnable
{
749 bool mCollectChildren
;
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
);
762 class OfflineStatusChangeRunnable
: public WorkerRunnable
{
764 OfflineStatusChangeRunnable(WorkerPrivate
* aWorkerPrivate
, bool aIsOffline
)
765 : WorkerRunnable(aWorkerPrivate
), mIsOffline(aIsOffline
) {}
767 bool WorkerRun(JSContext
* aCx
, WorkerPrivate
* aWorkerPrivate
) override
{
768 aWorkerPrivate
->OfflineStatusChangeEventInternal(mIsOffline
);
776 class MemoryPressureRunnable
: public WorkerControlRunnable
{
778 explicit MemoryPressureRunnable(WorkerPrivate
* aWorkerPrivate
)
779 : WorkerControlRunnable(aWorkerPrivate
, WorkerThreadUnchangedBusyCount
) {}
781 bool WorkerRun(JSContext
* aCx
, WorkerPrivate
* aWorkerPrivate
) override
{
782 aWorkerPrivate
->MemoryPressureInternal();
788 static bool StartsWithExplicit(nsACString
& s
) {
789 return StringBeginsWith(s
, NS_LITERAL_CSTRING("explicit/"));
793 PRThread
* PRThreadFromThread(nsIThread
* aThread
) {
797 MOZ_ALWAYS_SUCCEEDS(aThread
->GetPRThread(&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
{
809 explicit CancelingOnParentRunnable(WorkerPrivate
* aWorkerPrivate
)
810 : WorkerDebuggeeRunnable(aWorkerPrivate
, ParentThreadUnchangedBusyCount
) {
813 bool WorkerRun(JSContext
* aCx
, WorkerPrivate
* aWorkerPrivate
) override
{
814 aWorkerPrivate
->Cancel();
819 // A runnable to cancel the worker from the parent process.
820 class CancelingWithTimeoutOnParentRunnable final
821 : public WorkerControlRunnable
{
823 explicit CancelingWithTimeoutOnParentRunnable(WorkerPrivate
* aWorkerPrivate
)
824 : WorkerControlRunnable(aWorkerPrivate
, ParentThreadUnchangedBusyCount
) {}
826 bool WorkerRun(JSContext
* aCx
, WorkerPrivate
* aWorkerPrivate
) override
{
827 aWorkerPrivate
->AssertIsOnParentThread();
828 aWorkerPrivate
->StartCancelingTimer();
833 class CancelingTimerCallback final
: public nsITimerCallback
{
837 explicit CancelingTimerCallback(WorkerPrivate
* aWorkerPrivate
)
838 : mWorkerPrivate(aWorkerPrivate
) {}
841 Notify(nsITimer
* aTimer
) override
{
842 mWorkerPrivate
->AssertIsOnParentThread();
843 mWorkerPrivate
->Cancel();
848 ~CancelingTimerCallback() = default;
850 // Raw pointer here is OK because the timer is canceled during the shutdown
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
{
860 CancelingRunnable() : Runnable("CancelingRunnable") {}
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
);
877 } /* anonymous namespace */
879 nsString
ComputeWorkerPrivateId() {
881 nsCOMPtr
<nsIUUIDGenerator
> uuidGenerator
=
882 do_GetService("@mozilla.org/uuid-generator;1", &rv
);
883 MOZ_ASSERT(NS_SUCCEEDED(rv
));
886 rv
= uuidGenerator
->GenerateUUIDInPlace(&uuid
);
887 MOZ_ASSERT(NS_SUCCEEDED(rv
));
888 char buffer
[NSID_LENGTH
];
889 uuid
.ToProvidedString(buffer
);
892 // Remove {} and the null terminator
893 id
.AssignASCII(&buffer
[1], NSID_LENGTH
- 3);
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
;
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
);
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
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
;
969 class WorkerJSContextStats final
: public JS::RuntimeStats
{
970 const nsCString mRtPath
;
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
;
1035 WorkerPrivate
* mWorkerPrivate
;
1038 explicit MemoryReporter(WorkerPrivate
* aWorkerPrivate
)
1039 : mMutex(aWorkerPrivate
->mMutex
), mWorkerPrivate(aWorkerPrivate
) {
1040 aWorkerPrivate
->AssertIsOnWorkerThread();
1044 CollectReports(nsIHandleReportCallback
* aHandleReport
, nsISupports
* aData
,
1045 bool aAnonymize
) override
;
1048 class FinishCollectRunnable
;
1050 class CollectReportsRunnable final
: public MainThreadWorkerControlRunnable
{
1051 RefPtr
<FinishCollectRunnable
> mFinishCollectRunnable
;
1052 const bool mAnonymize
;
1055 CollectReportsRunnable(WorkerPrivate
* aWorkerPrivate
,
1056 nsIHandleReportCallback
* aHandleReport
,
1057 nsISupports
* aHandlerData
, bool aAnonymize
,
1058 const nsACString
& aPath
);
1061 bool WorkerRun(JSContext
* aCx
, WorkerPrivate
* aWorkerPrivate
) override
;
1063 ~CollectReportsRunnable() {
1064 if (NS_IsMainThread()) {
1065 mFinishCollectRunnable
->Run();
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
;
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
; }
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() {}
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
)
1125 WorkerPrivate::MemoryReporter::CollectReports(
1126 nsIHandleReportCallback
* aHandleReport
, nsISupports
* aData
,
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");
1140 manager
->EndReport();
1146 path
.AppendLiteral("explicit/workers/workers(");
1147 if (aAnonymize
&& !mWorkerPrivate
->Domain().IsEmpty()) {
1148 path
.AppendLiteral("<anonymized-domain>)/worker(<anonymized-url>");
1150 nsAutoCString
escapedDomain(mWorkerPrivate
->Domain());
1151 if (escapedDomain
.IsEmpty()) {
1152 escapedDomain
+= "chrome";
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
,
1168 if (!runnable
->Dispatch()) {
1169 return NS_ERROR_UNEXPECTED
;
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;
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
));
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
),
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
;
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."),
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."),
1250 manager
->EndReport();
1255 WorkerPrivate::SyncLoopInfo::SyncLoopInfo(EventTarget
* aEventTarget
)
1256 : mEventTarget(aEventTarget
),
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
;
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
1284 void WorkerPrivate::SetCSP(nsIContentSecurityPolicy
* aCSP
) {
1285 AssertIsOnMainThread();
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
))) {
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
);
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
));
1318 selfURI
= mLoadInfo
.mBaseURI
;
1320 MOZ_ASSERT(selfURI
, "need a self URI for CSP");
1322 rv
= csp
->SetRequestContextWithPrincipal(mLoadInfo
.mPrincipal
, selfURI
,
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
))) {
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()) {
1372 ReferrerPolicy policy
=
1373 ReferrerInfo::ReferrerPolicyFromHeaderString(headerValue
);
1374 if (policy
== ReferrerPolicy::_empty
) {
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
)) {
1417 "A runnable was posted to a worker that is already shutting "
1419 return NS_ERROR_UNEXPECTED
;
1422 if (runnable
->IsDebuggeeRunnable() && !mDebuggerReady
) {
1423 MOZ_RELEASE_ASSERT(!aSyncLoopTarget
);
1424 mDelayedDebuggeeRunnables
.AppendElement(runnable
);
1429 if (ParentStatus() == Pending
|| mStatus
== Pending
) {
1430 mPreStartRunnables
.AppendElement(runnable
);
1435 "Using a worker event target after the thread has already"
1437 return NS_ERROR_UNEXPECTED
;
1441 if (aSyncLoopTarget
) {
1442 rv
= aSyncLoopTarget
->Dispatch(runnable
.forget(), NS_DISPATCH_NORMAL
);
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
1448 rv
= mThread
->DispatchAnyThread(WorkerThreadFriendKey(), runnable
.forget());
1451 if (NS_WARN_IF(NS_FAILED(rv
))) {
1459 void WorkerPrivate::EnableDebugger() {
1460 AssertIsOnParentThread();
1462 if (NS_FAILED(RegisterWorkerDebugger(this))) {
1463 NS_WARNING("Failed to register worker debugger!");
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
);
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
) {
1522 "A debugger runnable was posted to a worker that is already "
1524 return NS_ERROR_UNEXPECTED
;
1527 // Transfer ownership to the debugger queue.
1528 mDebuggerQueue
.Push(runnable
.forget().take());
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
);
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
;
1573 // aCx is null when called from the finalizer
1574 bool WorkerPrivate::Notify(WorkerStatus aStatus
) {
1575 AssertIsOnParentThread();
1579 MutexAutoLock
lock(mMutex
);
1581 if (mParentStatus
>= aStatus
) {
1585 pending
= mParentStatus
== Pending
;
1586 mParentStatus
= aStatus
;
1592 // Fake a thread here just so that our assertions don't go off for no
1594 nsIThread
* currentThread
= NS_GetCurrentThread();
1595 MOZ_ASSERT(currentThread
);
1597 MOZ_ASSERT(!mPRThread
);
1598 mPRThread
= PRThreadFromThread(currentThread
);
1599 MOZ_ASSERT(mPRThread
);
1603 // Worker never got a chance to run, go ahead and delete it.
1604 ScheduleDeletion(WorkerPrivate::WorkerNeverRan
);
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.
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
) {
1648 RefPtr
<FreezeRunnable
> runnable
= new FreezeRunnable(this);
1649 if (!runnable
->Dispatch()) {
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.
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
) {
1688 RefPtr
<ThawRunnable
> runnable
= new ThawRunnable(this);
1689 if (!runnable
->Dispatch()) {
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
) {
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
) {
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
;
1757 bool WorkerPrivate::ModifyBusyCount(bool aIncrease
) {
1758 AssertIsOnParentThread();
1760 MOZ_ASSERT(aIncrease
|| mBusyCount
, "Mismatched busy count mods!");
1767 if (--mBusyCount
== 0) {
1770 MutexAutoLock
lock(mMutex
);
1771 shouldCancel
= mParentStatus
== Canceling
;
1774 if (shouldCancel
&& !Cancel()) {
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
);
1794 mLoadInfo
.ProxyReleaseMainThreadObjects(this, loadGroupToCancel
);
1796 mMainThreadObjectsForgotten
= true;
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
,
1829 AssertIsOnParentThread();
1834 MutexAutoLock
lock(mMutex
);
1835 found
= mJSSettings
.ApplyGCSetting(aKey
, aValue
);
1839 RefPtr
<UpdateJSWorkerMemoryParameterRunnable
> runnable
=
1840 new UpdateJSWorkerMemoryParameterRunnable(this, aKey
, aValue
);
1841 if (!runnable
->Dispatch()) {
1842 NS_WARNING("Failed to update memory parameter!");
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!");
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
) {
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();
1921 nav
->SetOnLine(data
->mOnLine
);
1926 eventType
.AssignLiteral("offline");
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();
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(':');
1995 mLocationInfo
.mProtocol
.Truncate();
1999 if (NS_SUCCEEDED(aBaseURI
->GetPort(&port
)) && port
!= -1) {
2000 mLocationInfo
.mPort
.AppendInt(port
);
2002 nsAutoCString
host(mLocationInfo
.mHostname
);
2004 host
.Append(mLocationInfo
.mPort
);
2006 mLocationInfo
.mHost
.Assign(host
);
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();
2035 void WorkerPrivate::UpdateOverridenLoadGroup(nsILoadGroup
* aBaseLoadGroup
) {
2036 AssertIsOnMainThread();
2038 // The load group should have been overriden at init time.
2039 mLoadInfo
.mInterfaceRequestor
->MaybeAddBrowserChild(aBaseLoadGroup
);
2044 void WorkerPrivate::AssertIsOnParentThread() const {
2046 GetParent()->AssertIsOnWorkerThread();
2048 AssertIsOnMainThread();
2052 void WorkerPrivate::AssertInnerWindowIsCorrect() const {
2053 AssertIsOnParentThread();
2055 // Only care about top level workers from windows.
2056 if (mParent
|| !mLoadInfo
.mWindow
) {
2060 AssertIsOnMainThread();
2062 nsPIDOMWindowOuter
* outer
= mLoadInfo
.mWindow
->GetOuterWindow();
2063 NS_ASSERTION(outer
&& outer
->GetCurrentInnerWindow() == mLoadInfo
.mWindow
,
2064 "Inner window no longer correct!");
2069 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
2070 bool WorkerPrivate::PrincipalIsValid() const {
2071 return mLoadInfo
.PrincipalIsValid();
2075 WorkerPrivate::WorkerThreadAccessible::WorkerThreadAccessible(
2076 WorkerPrivate
* const aParent
)
2077 : mNumWorkerRefsPreventingShutdownStart(0),
2078 mDebuggerEventLoopLevel(0),
2079 mErrorHandlerRecursionCount(0),
2082 mTimerRunning(false),
2083 mRunningExpiredTimeouts(false),
2084 mPeriodicGCTimerRunning(false),
2085 mIdleGCTimerRunning(false),
2086 mOnLine(aParent
? aParent
->OnLine() : !NS_IsOffline()) {}
2090 bool IsNewWorkerSecureContext(const WorkerPrivate
* const aParent
,
2091 const WorkerType aWorkerType
,
2092 const WorkerLoadInfo
& aLoadInfo
) {
2094 return aParent
->IsSecureContext();
2097 // Our secure context state depends on the kind of worker we have.
2099 if (aLoadInfo
.mPrincipalIsSystem
) {
2103 if (aWorkerType
== WorkerTypeService
) {
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");
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"),
2129 mScriptURL(aScriptURL
),
2130 mWorkerName(aWorkerName
),
2131 mWorkerType(aWorkerType
),
2132 mLoadInfo(std::move(aLoadInfo
)),
2134 mJSContext(nullptr),
2136 mWorkerControlEventTarget(new WorkerEventTarget(
2137 this, WorkerEventTarget::Behavior::ControlOnly
)),
2138 mWorkerHybridEventTarget(
2139 new WorkerEventTarget(this, WorkerEventTarget::Behavior::Hybrid
)),
2140 mParentStatus(Pending
),
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),
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());
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
) {
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
2222 mMainThreadEventTargetForMessaging
=
2223 aParent
->mMainThreadEventTargetForMessaging
;
2224 mMainThreadEventTarget
= aParent
->mMainThreadEventTarget
;
2225 mMainThreadDebuggeeEventTarget
= aParent
->mMainThreadDebuggeeEventTarget
;
2229 MOZ_ASSERT(NS_IsMainThread());
2231 GetWindow() ? GetWindow()->EventTargetFor(TaskCategory::Worker
) : nullptr;
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
);
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);
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
;
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
);
2289 AssertIsOnMainThread();
2292 Maybe
<WorkerLoadInfo
> stackLoadInfo
;
2294 stackLoadInfo
.emplace();
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
);
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
2311 RuntimeService
* runtimeService
;
2314 runtimeService
= RuntimeService::GetOrCreateService();
2315 if (!runtimeService
) {
2316 aRv
.Throw(NS_ERROR_FAILURE
);
2320 runtimeService
= RuntimeService::GetService();
2323 MOZ_ASSERT(runtimeService
);
2325 nsILoadInfo::CrossOriginOpenerPolicy agentClusterCoop
=
2326 nsILoadInfo::OPENER_POLICY_NULL
;
2327 nsID agentClusterId
;
2329 MOZ_ASSERT(aWorkerType
== WorkerType::WorkerTypeDedicated
);
2331 agentClusterId
= parent
->AgentClusterId();
2332 agentClusterCoop
= parent
->mAgentClusterOpenerPolicy
;
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();
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
);
2372 worker
->mDefaultLocale
= std::move(defaultLocale
);
2374 if (!runtimeService
->RegisterWorker(worker
)) {
2375 aRv
.Throw(NS_ERROR_UNEXPECTED
);
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
);
2395 worker
->mSelfRef
= worker
;
2397 return worker
.forget();
2400 nsresult
WorkerPrivate::SetIsDebuggerReady(bool aReady
) {
2401 AssertIsOnMainThread();
2402 MutexAutoLock
lock(mMutex
);
2404 if (mDebuggerReady
== aReady
) {
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());
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
;
2442 MOZ_ASSERT_IF(NS_IsMainThread(),
2443 aCx
== nsContentUtils::GetCurrentJSContext());
2446 AssertIsOnMainThread();
2449 WorkerLoadInfo loadInfo
;
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
));
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();
2496 AssertIsOnMainThread();
2498 // Make sure that the IndexedDatabaseManager is set up
2499 Unused
<< NS_WARN_IF(!IndexedDatabaseManager::GetOrCreate());
2501 nsIScriptSecurityManager
* ssm
= nsContentUtils::GetSecurityManager();
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
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
;
2533 // Only use the current inner window, and only use it if the caller can
2535 if (nsPIDOMWindowOuter
* outerWindow
= globalWindow
->GetOuterWindow()) {
2536 loadInfo
.mWindow
= outerWindow
->GetCurrentInnerWindow();
2537 // TODO: fix this for SharedWorkers with multiple documents (bug
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
);
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
;
2580 tmpDoc
= tmpDoc
->GetInProcessParentDocument();
2581 } while (tmpDoc
&& tmpDoc
->GetSandboxFlags() & SANDBOXED_ORIGIN
);
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
);
2589 // No unsandboxed ancestor, use our GUID.
2590 rv
= loadInfo
.mLoadingPrincipal
->GetBaseDomain(loadInfo
.mDomain
);
2591 NS_ENSURE_SUCCESS(rv
, rv
);
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
),
2604 nsCOMPtr
<nsIPermissionManager
> permMgr
=
2605 do_GetService(NS_PERMISSIONMANAGER_CONTRACTID
, &rv
);
2606 NS_ENSURE_SUCCESS(rv
, rv
);
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();
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
;
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
)) {
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
2661 rv
= NS_NewURI(getter_AddRefs(loadInfo
.mBaseURI
), fileName
.get());
2663 if (NS_FAILED(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
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
);
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
2727 // - use remote tabs = false
2728 nsCOMPtr
<nsILoadGroup
> loadGroup
= do_CreateInstance(NS_LOADGROUP_CONTRACTID
);
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
);
2747 MOZ_ASSERT(mStatus
== Pending
);
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.
2756 MOZ_ASSERT(jsapi
.cx() == aCx
);
2758 EnableMemoryReporter();
2760 InitializeGCTimers();
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
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
);
2809 MutexAutoLock
lock(mMutex
);
2810 currentStatus
= mStatus
;
2812 MOZ_ASSERT(currentStatus
== Killing
);
2814 currentStatus
= Killing
;
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();
2830 DisableMemoryReporter();
2833 MutexAutoLock
lock(mMutex
);
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
)) {
2845 runnable
->Release();
2849 // Unroot the globals
2850 data
->mScope
= nullptr;
2851 data
->mDebuggerScope
= nullptr;
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
2885 JSAutoRealm
ar(aCx
, globalScope
->GetGlobalJSObject());
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
2896 JSAutoRealm
ar(aCx
, GlobalScope()->GetGlobalJSObject());
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
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
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
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
,
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
),
2959 nsIEventTarget
* WorkerPrivate::MainThreadEventTarget() {
2960 return mMainThreadEventTarget
;
2963 nsresult
WorkerPrivate::DispatchToMainThread(nsIRunnable
* aRunnable
,
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
) {
2996 case WorkerTypeDedicated
:
2997 type
= ClientType::Worker
;
2999 case WorkerTypeShared
:
3000 type
= ClientType::Sharedworker
;
3002 case WorkerTypeService
:
3003 type
= ClientType::Serviceworker
;
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
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);
3045 bool WorkerPrivate::EnsureCSPEventListener() {
3046 if (!mCSPEventListener
) {
3047 mCSPEventListener
= WorkerCSPEventListener::Create(this);
3048 if (NS_WARN_IF(!mCSPEventListener
)) {
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
);
3075 clientInfo
.emplace(data
->mClientSource
->Info());
3079 const ClientState
WorkerPrivate::GetClientState() const {
3080 MOZ_ACCESS_THREAD_BOUND(mWorkerThreadAccessible
, data
);
3081 MOZ_DIAGNOSTIC_ASSERT(data
->mClientSource
);
3083 data
->mClientSource
->SnapshotState(&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
) {
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
);
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
) {
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
)) {
3158 MOZ_ALWAYS_SUCCEEDS(data
->mGCTimer
->Cancel());
3160 data
->mPeriodicGCTimerRunning
= false;
3161 data
->mIdleGCTimerRunning
= false;
3163 ("Worker %p canceled GC timer because %s\n", this,
3164 aMode
== PeriodicTimer
? "periodic"
3165 : aMode
== IdleTimer
? "idle" : "none"));
3167 if (aMode
== NoTimer
) {
3171 MOZ_ASSERT(aMode
== PeriodicTimer
|| aMode
== IdleTimer
);
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";
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;
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;
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
);
3246 mayFreeze
= mStatus
<= Running
;
3249 if (mStatus
>= Canceling
) {
3250 mayContinue
= false;
3254 if (!mayContinue
|| !mayFreeze
) {
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()) {
3271 WaitForWorkerEvents();
3276 // We want only uncatchable exceptions here.
3277 NS_ASSERTION(!JS_IsExceptionPending(aCx
),
3278 "Should not have an exception set here!");
3282 // Make sure the periodic timer gets turned back on here.
3283 SetGCTimerMode(PeriodicTimer
);
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
);
3312 if (WorkerRan
== aRanOrNot
) {
3313 nsIThread
* currentThread
= NS_GetCurrentThread();
3314 MOZ_ASSERT(currentThread
);
3315 MOZ_ASSERT(!NS_HasPendingEvents(currentThread
));
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!");
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
,
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
3373 if (!data
->mMemoryReporter
) {
3377 // We don't need this set any longer. Swap it out so that we can unregister
3379 data
->mMemoryReporter
.swap(memoryReporter
);
3381 // Next disable the memory reporter so that the main thread stops trying to
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.
3404 WorkerPrivate::ProcessAllControlRunnablesResult
3405 WorkerPrivate::ProcessAllControlRunnablesLocked() {
3406 AssertIsOnWorkerThread();
3407 mMutex
.AssertCurrentThreadOwns();
3409 auto result
= ProcessAllControlRunnablesResult::Nothing
;
3412 WorkerControlRunnable
* event
;
3413 if (!mControlQueue
.Pop(event
)) {
3417 MutexAutoUnlock
unlock(mMutex
);
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
;
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
;
3444 RefPtr
<WorkerRunnable
> runnable
= mPreStartRunnables
[index
].forget();
3445 static_cast<nsIRunnable
*>(runnable
.get())->Run();
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);
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();
3505 void WorkerPrivate::PropagateFirstPartyStorageAccessGrantedInternal() {
3506 MOZ_ACCESS_THREAD_BOUND(mWorkerThreadAccessible
, data
);
3508 mLoadInfo
.mFirstPartyStorageAccessGranted
= true;
3510 WorkerGlobalScope
* globalScope
= 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
3541 if (mStatus
>= Killing
) {
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
);
3556 WorkerStatus currentStatus
;
3558 MutexAutoLock
lock(mMutex
);
3559 currentStatus
= mStatus
;
3562 MOZ_ASSERT(currentStatus
== Running
);
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)
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
) {
3599 MOZ_ASSERT(!data
->mWorkerRefs
.Contains(aWorkerRef
),
3600 "Already know about this one!");
3602 if (aWorkerRef
->IsPreventingShutdown()) {
3603 if (!data
->mNumWorkerRefsPreventingShutdownStart
&&
3604 !ModifyBusyCountFromWorker(true)) {
3607 data
->mNumWorkerRefsPreventingShutdownStart
+= 1;
3610 data
->mWorkerRefs
.AppendElement(aWorkerRef
);
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
3680 data
->mTimerRunning
= false;
3683 else if (!data
->mRunningExpiredTimeouts
) {
3684 NS_ASSERTION(data
->mTimeouts
.IsEmpty(), "Huh?!");
3688 data
->mTimer
= nullptr;
3689 data
->mTimerRunnable
= nullptr;
3692 already_AddRefed
<nsIEventTarget
> WorkerPrivate::CreateNewSyncLoop(
3693 WorkerStatus aFailStatus
) {
3694 AssertIsOnWorkerThread();
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
) {
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.
3719 MutexAutoLock
lock(mMutex
);
3722 mSyncLoopStack
.AppendElement(new SyncLoopInfo(workerEventTarget
));
3725 return workerEventTarget
.forget();
3728 bool WorkerPrivate::RunCurrentSyncLoop() {
3729 AssertIsOnWorkerThread();
3731 JSContext
* cx
= GetJSContext();
3734 AutoPushEventLoopGlobal
eventLoopGlobal(this, cx
);
3736 // This should not change between now and the time we finish running this sync
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
);
3747 loopInfo
->mHasRun
= true;
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
);
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
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
3779 if (loopInfo
->mCompleted
) {
3784 // If we *didn't* run any control runnables, this should be unchanged.
3785 MOZ_ASSERT(!loopInfo
->mCompleted
);
3787 if (normalRunnablesPending
) {
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
));
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.
3831 MutexAutoLock
lock(mMutex
);
3834 // This will delete |loopInfo|!
3835 mSyncLoopStack
.RemoveElementAt(aLoopIndex
);
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;
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);
3873 void WorkerPrivate::ReportUseCounters() {
3874 AssertIsOnWorkerThread();
3876 static const bool kDebugUseCounters
= false;
3878 if (mReportedUseCounters
) {
3881 mReportedUseCounters
= true;
3883 if (Telemetry::HistogramUseCounterWorkerCount
<= 0 || IsChromeWorker()) {
3887 const size_t type
= Type();
3889 case WorkerTypeDedicated
:
3890 Telemetry::Accumulate(Telemetry::DEDICATED_WORKER_DESTROYED
, 1);
3892 case WorkerTypeShared
:
3893 Telemetry::Accumulate(Telemetry::SHARED_WORKER_DESTROYED
, 1);
3895 case WorkerTypeService
:
3896 Telemetry::Accumulate(Telemetry::SERVICE_WORKER_DESTROYED
, 1);
3899 MOZ_ASSERT(false, "Unknown worker type");
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());
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
,
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();
3965 MOZ_ASSERT(!SameCOMIdentity(loopInfo
->mEventTarget
, aSyncLoopTarget
));
3968 MOZ_CRASH("Unknown sync loop!");
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
);
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
) {
3996 MOZ_ASSERT(!SameCOMIdentity(loopInfo
->mEventTarget
, aSyncLoopTarget
));
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
,
4014 if (NS_WARN_IF(aRv
.Failed())) {
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
>(
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
>(
4043 ? ProfileTimelineWorkerOperationType::SerializeDataOnMainThread
4044 : ProfileTimelineWorkerOperationType::SerializeDataOffMainThread
,
4045 MarkerTracingType::END
);
4046 timelines
->AddMarkerForAllObservedDocShells(start
);
4047 timelines
->AddMarkerForAllObservedDocShells(end
);
4050 if (NS_WARN_IF(aRv
.Failed())) {
4054 if (!runnable
->Dispatch()) {
4055 aRv
= NS_ERROR_FAILURE
;
4059 void WorkerPrivate::EnterDebuggerEventLoop() {
4060 MOZ_ACCESS_THREAD_BOUND(mWorkerThreadAccessible
, data
);
4062 JSContext
* cx
= GetJSContext();
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
));
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
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
,
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
,
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
) {
4180 MOZ_ASSERT_IF(aStatus
== Killing
, mStatus
== Canceling
);
4182 if (aStatus
>= Canceling
) {
4183 MutexAutoUnlock
unlock(mMutex
);
4184 data
->mClientSource
.reset();
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
) {
4210 previousStatus
= mStatus
;
4213 // Mark parent status as closing immediately to avoid new events being
4214 // dispatched after we clear the queue below.
4215 if (aStatus
== Closing
) {
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
;
4239 ClearMainEventQueue(WorkerRan
);
4243 // If the worker script never ran, or failed to compile, we don't need to do
4245 if (!GlobalScope()) {
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
;
4255 DispatchCancelingRunnable();
4260 MOZ_ASSERT(aStatus
== Canceling
|| aStatus
== Killing
);
4262 // Always abort the script.
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) {
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.
4285 JS::RootedObject
exnStack(aCx
, JS::GetPendingExceptionStack(aCx
));
4286 JS_ClearPendingException(aCx
);
4288 UniquePtr
<WorkerErrorReport
> report
= MakeUnique
<WorkerErrorReport
>();
4290 report
->AssignErrorReport(aReport
);
4292 report
->mFlags
= nsIScriptError::errorFlag
| nsIScriptError::exceptionFlag
;
4295 JS::RootedObject
stack(aCx
), stackGlobal(aCx
);
4296 xpc::FindExceptionStackForConsoleReport(nullptr, exn
, exnStack
, &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(),
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
--;
4336 void WorkerPrivate::ReportErrorToConsole(const char* aMessage
) {
4337 nsTArray
<nsString
> emptyParams
;
4338 WorkerPrivate::ReportErrorToConsole(aMessage
, emptyParams
);
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
,
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
) {
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
);
4406 data
->mTimerRunnable
= new TimerRunnable(this);
4409 if (!data
->mTimerRunning
) {
4410 if (!ModifyBusyCountFromWorker(true)) {
4411 aRv
.Throw(NS_ERROR_FAILURE
);
4414 data
->mTimerRunning
= true;
4417 if (!RescheduleTimeoutTimer(aCx
)) {
4418 aRv
.Throw(NS_ERROR_FAILURE
);
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;
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
4448 if (data
->mRunningExpiredTimeouts
|| !data
->mTimerRunning
) {
4452 NS_ASSERTION(data
->mTimer
&& data
->mTimerRunnable
, "Must have a timer!");
4453 NS_ASSERTION(!data
->mTimeouts
.IsEmpty(), "Should have some work to do!");
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
) {
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
) {
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.
4499 if (info
->mIsInterval
) {
4500 reason
= "setInterval handler";
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
)) {
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
) ||
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.
4543 data
->mTimeouts
.RemoveElement(info
);
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!");
4554 data
->mTimeouts
.Sort(comparator
);
4556 // Either signal the parent that we're no longer using timeouts or reschedule
4558 if (data
->mTimeouts
.IsEmpty()) {
4559 if (!ModifyBusyCountFromWorker(false)) {
4562 data
->mTimerRunning
= false;
4563 } else if (retval
&& !RescheduleTimeoutTimer(aCx
)) {
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();
4585 (data
->mTimeouts
[0]->mTargetTime
- TimeStamp::Now()).ToMilliseconds();
4586 uint32_t delay
= delta
> 0 ? std::min(delta
, double(UINT32_MAX
)) : 0;
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!");
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());
4612 mCancelingTimer
= NS_NewTimer();
4615 if (NS_WARN_IF(!mCancelingTimer
)) {
4619 // This is not needed if we are already in an advanced shutdown state.
4621 MutexAutoLock
lock(mMutex
);
4622 if (ParentStatus() >= Canceling
) {
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
))) {
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();
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
,
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
4682 JS_SetGCParameter(aCx
, aKey
, aValue
);
4685 for (uint32_t index
= 0; index
< data
->mChildWorkers
.Length(); index
++) {
4686 data
->mChildWorkers
[index
]->UpdateJSWorkerMemoryParameter(aKey
, aValue
);
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
);
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.
4722 if (aShrinking
|| aCollectChildren
) {
4723 JS::PrepareForFullGC(aCx
);
4726 JS::NonIncrementalGC(aCx
, GC_SHRINK
, JS::GCReason::DOM_WORKER
);
4728 if (!aCollectChildren
) {
4729 LOG(WorkerLog(), ("Worker %p collected idle garbage\n", this));
4732 JS::NonIncrementalGC(aCx
, GC_NORMAL
, JS::GCReason::DOM_WORKER
);
4733 LOG(WorkerLog(), ("Worker %p collected garbage\n", this));
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
);
4763 RefPtr
<Console
> console
= data
->mScope
->GetConsoleIfExists();
4765 console
->ClearStorage();
4768 RefPtr
<Performance
> performance
= data
->mScope
->GetPerformanceIfExists();
4770 performance
->MemoryPressure();
4774 if (data
->mDebuggerScope
) {
4775 RefPtr
<Console
> console
= data
->mDebuggerScope
->GetConsoleIfExists();
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
) {
4790 bool isOnCurrentThread
;
4791 MOZ_ASSERT(NS_SUCCEEDED(aThread
->IsOnCurrentThread(&isOnCurrentThread
)));
4792 MOZ_ASSERT(!isOnCurrentThread
);
4796 MOZ_ASSERT(!mPRThread
);
4797 mPRThread
= PRThreadFromThread(aThread
);
4798 MOZ_ASSERT(mPRThread
);
4800 mWorkerThreadAccessible
.Transfer(mPRThread
);
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
);
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.
4863 RefPtr
<MessagePort
> port
= MessagePort::Create(globalScope
, aIdentifier
, rv
);
4864 if (NS_WARN_IF(rv
.Failed())) {
4865 rv
.SuppressException();
4869 GlobalObject
globalObject(aCx
, jsGlobal
);
4870 if (globalObject
.Failed()) {
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
)) {
4883 RefPtr
<MessageEvent
> event
= MessageEvent::Constructor(
4884 globalObject
, NS_LITERAL_STRING("connect"), init
);
4886 event
->SetTrusted(true);
4888 globalScope
->DispatchEvent(*event
);
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());
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;
4921 JS_FireOnNewGlobalObject(aCx
, global
);
4924 return data
->mScope
;
4927 WorkerDebuggerGlobalScope
* WorkerPrivate::CreateDebuggerGlobalScope(
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;
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
4960 MOZ_ASSERT(mPRThread
,
4961 "AssertIsOnWorkerThread() called before a thread was assigned!");
4963 return mPRThread
== PR_GetCurrentThread();
4967 void WorkerPrivate::AssertIsOnWorkerThread() const {
4968 MOZ_ASSERT(IsOnWorkerThread());
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
);
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());
5052 const nsAString
& WorkerPrivate::Id() {
5053 AssertIsOnMainThread();
5055 if (mId
.IsEmpty()) {
5056 mId
= ComputeWorkerPrivateId();
5059 MOZ_ASSERT(!mId
.IsEmpty());
5064 bool WorkerPrivate::IsSharedMemoryAllowed() const {
5065 AssertIsOnWorkerThread();
5068 dom_postMessage_sharedArrayBuffer_bypassCOOP_COEP_insecure_enabled()) {
5072 return CrossOriginIsolated();
5075 bool WorkerPrivate::CrossOriginIsolated() const {
5076 AssertIsOnWorkerThread();
5078 if (!StaticPrefs::dom_postMessage_sharedArrayBuffer_withCOOP_COEP()) {
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
)
5094 // kDEBUGWorkerEventTargetIID is special in that it does not AddRef its
5096 if (aIID
.Equals(kDEBUGWorkerEventTargetIID
)) {
5097 *aInstancePtr
= this;
5101 NS_INTERFACE_MAP_END
5104 WorkerPrivate::EventTarget::DispatchFromScript(nsIRunnable
* aRunnable
,
5106 nsCOMPtr
<nsIRunnable
> event(aRunnable
);
5107 return Dispatch(event
.forget(), aFlags
);
5111 WorkerPrivate::EventTarget::Dispatch(already_AddRefed
<nsIRunnable
> aRunnable
,
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
) {
5127 "A runnable was posted to a worker that is already shutting "
5129 return NS_ERROR_UNEXPECTED
;
5133 workerRunnable
= mWorkerPrivate
->MaybeWrapAsWorkerRunnable(event
.forget());
5137 mWorkerPrivate
->Dispatch(workerRunnable
.forget(), mNestedEventTarget
);
5138 if (NS_WARN_IF(NS_FAILED(rv
))) {
5146 WorkerPrivate::EventTarget::DelayedDispatch(already_AddRefed
<nsIRunnable
>,
5150 return NS_ERROR_NOT_IMPLEMENTED
;
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();
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 !");
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();
5200 } // namespace mozilla