1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "WorkerPrivate.h"
8 
9 #include <utility>
10 
11 #include "js/CompilationAndEvaluation.h"
12 #include "js/ContextOptions.h"
13 #include "js/Exception.h"
14 #include "js/friend/ErrorMessages.h"  // JSMSG_OUT_OF_MEMORY
15 #include "js/LocaleSensitive.h"
16 #include "js/MemoryMetrics.h"
17 #include "js/SourceText.h"
18 #include "MessageEventRunnable.h"
19 #include "mozilla/BasePrincipal.h"
20 #include "mozilla/CycleCollectedJSContext.h"
21 #include "mozilla/ExtensionPolicyService.h"
22 #include "mozilla/ProfilerLabels.h"
23 #include "mozilla/Result.h"
24 #include "mozilla/ScopeExit.h"
25 #include "mozilla/StaticPrefs_browser.h"
26 #include "mozilla/StaticPrefs_dom.h"
27 #include "mozilla/dom/BrowsingContextGroup.h"
28 #include "mozilla/dom/CallbackDebuggerNotification.h"
29 #include "mozilla/dom/ClientManager.h"
30 #include "mozilla/dom/ClientState.h"
31 #include "mozilla/dom/Console.h"
32 #include "mozilla/dom/DocGroup.h"
33 #include "mozilla/dom/Document.h"
34 #include "mozilla/dom/DOMTypes.h"
35 #include "mozilla/dom/Event.h"
36 #include "mozilla/dom/Exceptions.h"
37 #include "mozilla/dom/FunctionBinding.h"
38 #include "mozilla/dom/IndexedDatabaseManager.h"
39 #include "mozilla/dom/MessageEvent.h"
40 #include "mozilla/dom/MessageEventBinding.h"
41 #include "mozilla/dom/MessagePort.h"
42 #include "mozilla/dom/MessagePortBinding.h"
43 #include "mozilla/dom/nsCSPContext.h"
44 #include "mozilla/dom/nsCSPUtils.h"
45 #include "mozilla/dom/Performance.h"
46 #include "mozilla/dom/PerformanceStorageWorker.h"
47 #include "mozilla/dom/PromiseDebugging.h"
48 #include "mozilla/dom/RemoteWorkerChild.h"
49 #include "mozilla/dom/RemoteWorkerService.h"
50 #include "mozilla/dom/TimeoutHandler.h"
51 #include "mozilla/dom/WorkerBinding.h"
52 #include "mozilla/dom/JSExecutionManager.h"
53 #include "mozilla/dom/WindowContext.h"
54 #include "mozilla/extensions/WebExtensionPolicy.h"
55 #include "mozilla/StorageAccess.h"
56 #include "mozilla/StoragePrincipalHelper.h"
57 #include "mozilla/Telemetry.h"
58 #include "mozilla/ThreadEventQueue.h"
59 #include "mozilla/ThrottledEventQueue.h"
60 #include "mozilla/TimelineConsumers.h"
61 #include "mozilla/WorkerTimelineMarker.h"
62 #include "nsCycleCollector.h"
63 #include "nsGlobalWindowInner.h"
64 #include "nsNetUtil.h"
65 #include "nsIFile.h"
66 #include "nsIMemoryReporter.h"
67 #include "nsIPermissionManager.h"
68 #include "nsIProtocolHandler.h"
69 #include "nsIScriptError.h"
70 #include "nsIURI.h"
71 #include "nsIURL.h"
72 #include "nsIUUIDGenerator.h"
73 #include "nsPrintfCString.h"
74 #include "nsProxyRelease.h"
75 #include "nsQueryObject.h"
76 #include "nsRFPService.h"
77 #include "nsSandboxFlags.h"
78 #include "nsUTF8Utils.h"
79 
80 #include "RuntimeService.h"
81 #include "ScriptLoader.h"
82 #include "mozilla/dom/ServiceWorkerEvents.h"
83 #include "mozilla/dom/ServiceWorkerManager.h"
84 #include "mozilla/net/CookieJarSettings.h"
85 #include "WorkerCSPEventListener.h"
86 #include "WorkerDebugger.h"
87 #include "WorkerDebuggerManager.h"
88 #include "WorkerError.h"
89 #include "WorkerEventTarget.h"
90 #include "WorkerNavigator.h"
91 #include "WorkerRef.h"
92 #include "WorkerRunnable.h"
93 #include "WorkerThread.h"
94 
95 #include "nsThreadManager.h"
96 
97 #ifdef XP_WIN
98 #  undef PostMessage
99 #endif
100 
101 // JS_MaybeGC will run once every second during normal execution.
102 #define PERIODIC_GC_TIMER_DELAY_SEC 1
103 
104 // A shrinking GC will run five seconds after the last event is processed.
105 #define IDLE_GC_TIMER_DELAY_SEC 5
106 
107 static mozilla::LazyLogModule sWorkerPrivateLog("WorkerPrivate");
108 static mozilla::LazyLogModule sWorkerTimeoutsLog("WorkerTimeouts");
109 
WorkerLog()110 mozilla::LogModule* WorkerLog() { return sWorkerPrivateLog; }
111 
TimeoutsLog()112 mozilla::LogModule* TimeoutsLog() { return sWorkerTimeoutsLog; }
113 
114 #ifdef LOG
115 #  undef LOG
116 #endif
117 #define LOG(log, _args) MOZ_LOG(log, LogLevel::Debug, _args);
118 
119 namespace mozilla {
120 
121 using namespace ipc;
122 
123 namespace dom {
124 
125 using namespace workerinternals;
126 
127 MOZ_DEFINE_MALLOC_SIZE_OF(JsWorkerMallocSizeOf)
128 
129 namespace {
130 
131 #ifdef DEBUG
132 
133 const nsIID kDEBUGWorkerEventTargetIID = {
134     0xccaba3fa,
135     0x5be2,
136     0x4de2,
137     {0xba, 0x87, 0x3b, 0x3b, 0x5b, 0x1d, 0x5, 0xfb}};
138 
139 #endif
140 
141 // The number of nested timeouts before we start clamping. HTML says 5.
142 const uint32_t kClampTimeoutNestingLevel = 5u;
143 
144 template <class T>
145 class UniquePtrComparator {
146   typedef UniquePtr<T> A;
147   typedef T* B;
148 
149  public:
Equals(const A & a,const A & b) const150   bool Equals(const A& a, const A& b) const {
151     return a && b ? *a == *b : !a && !b ? true : false;
152   }
LessThan(const A & a,const A & b) const153   bool LessThan(const A& a, const A& b) const {
154     return a && b ? *a < *b : b ? true : false;
155   }
156 };
157 
158 template <class T>
GetUniquePtrComparator(const nsTArray<UniquePtr<T>> &)159 inline UniquePtrComparator<T> GetUniquePtrComparator(
160     const nsTArray<UniquePtr<T>>&) {
161   return UniquePtrComparator<T>();
162 }
163 
164 // This class is used to wrap any runnables that the worker receives via the
165 // nsIEventTarget::Dispatch() method (either from NS_DispatchToCurrentThread or
166 // from the worker's EventTarget).
167 class ExternalRunnableWrapper final : public WorkerRunnable {
168   nsCOMPtr<nsIRunnable> mWrappedRunnable;
169 
170  public:
ExternalRunnableWrapper(WorkerPrivate * aWorkerPrivate,nsIRunnable * aWrappedRunnable)171   ExternalRunnableWrapper(WorkerPrivate* aWorkerPrivate,
172                           nsIRunnable* aWrappedRunnable)
173       : WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
174         mWrappedRunnable(aWrappedRunnable) {
175     MOZ_ASSERT(aWorkerPrivate);
176     MOZ_ASSERT(aWrappedRunnable);
177   }
178 
179   NS_INLINE_DECL_REFCOUNTING_INHERITED(ExternalRunnableWrapper, WorkerRunnable)
180 
181  private:
182   ~ExternalRunnableWrapper() = default;
183 
PreDispatch(WorkerPrivate * aWorkerPrivate)184   virtual bool PreDispatch(WorkerPrivate* aWorkerPrivate) override {
185     // Silence bad assertions.
186     return true;
187   }
188 
PostDispatch(WorkerPrivate * aWorkerPrivate,bool aDispatchResult)189   virtual void PostDispatch(WorkerPrivate* aWorkerPrivate,
190                             bool aDispatchResult) override {
191     // Silence bad assertions.
192   }
193 
WorkerRun(JSContext * aCx,WorkerPrivate * aWorkerPrivate)194   virtual bool WorkerRun(JSContext* aCx,
195                          WorkerPrivate* aWorkerPrivate) override {
196     nsresult rv = mWrappedRunnable->Run();
197     if (NS_FAILED(rv)) {
198       if (!JS_IsExceptionPending(aCx)) {
199         Throw(aCx, rv);
200       }
201       return false;
202     }
203     return true;
204   }
205 
Cancel()206   nsresult Cancel() override {
207     nsCOMPtr<nsIDiscardableRunnable> doomed =
208         do_QueryInterface(mWrappedRunnable);
209     MOZ_ASSERT(doomed);  // We checked this earlier!
210     doomed->OnDiscard();
211     return WorkerRunnable::Cancel();
212   }
213 };
214 
215 struct WindowAction {
216   nsPIDOMWindowInner* mWindow;
217   bool mDefaultAction;
218 
WindowActionmozilla::dom::__anon2df0b1fc0111::WindowAction219   MOZ_IMPLICIT WindowAction(nsPIDOMWindowInner* aWindow)
220       : mWindow(aWindow), mDefaultAction(true) {}
221 
operator ==mozilla::dom::__anon2df0b1fc0111::WindowAction222   bool operator==(const WindowAction& aOther) const {
223     return mWindow == aOther.mWindow;
224   }
225 };
226 
227 class WorkerFinishedRunnable final : public WorkerControlRunnable {
228   WorkerPrivate* mFinishedWorker;
229 
230  public:
WorkerFinishedRunnable(WorkerPrivate * aWorkerPrivate,WorkerPrivate * aFinishedWorker)231   WorkerFinishedRunnable(WorkerPrivate* aWorkerPrivate,
232                          WorkerPrivate* aFinishedWorker)
233       : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
234         mFinishedWorker(aFinishedWorker) {}
235 
236  private:
PreDispatch(WorkerPrivate * aWorkerPrivate)237   virtual bool PreDispatch(WorkerPrivate* aWorkerPrivate) override {
238     // Silence bad assertions.
239     return true;
240   }
241 
PostDispatch(WorkerPrivate * aWorkerPrivate,bool aDispatchResult)242   virtual void PostDispatch(WorkerPrivate* aWorkerPrivate,
243                             bool aDispatchResult) override {
244     // Silence bad assertions.
245   }
246 
WorkerRun(JSContext * aCx,WorkerPrivate * aWorkerPrivate)247   virtual bool WorkerRun(JSContext* aCx,
248                          WorkerPrivate* aWorkerPrivate) override {
249     // This may block on the main thread.
250     AutoYieldJSThreadExecution yield;
251 
252     if (!mFinishedWorker->ProxyReleaseMainThreadObjects()) {
253       NS_WARNING("Failed to dispatch, going to leak!");
254     }
255 
256     RuntimeService* runtime = RuntimeService::GetService();
257     NS_ASSERTION(runtime, "This should never be null!");
258 
259     mFinishedWorker->DisableDebugger();
260 
261     runtime->UnregisterWorker(*mFinishedWorker);
262 
263     mFinishedWorker->ClearSelfAndParentEventTargetRef();
264     return true;
265   }
266 };
267 
268 class TopLevelWorkerFinishedRunnable final : public Runnable {
269   WorkerPrivate* mFinishedWorker;
270 
271  public:
TopLevelWorkerFinishedRunnable(WorkerPrivate * aFinishedWorker)272   explicit TopLevelWorkerFinishedRunnable(WorkerPrivate* aFinishedWorker)
273       : mozilla::Runnable("TopLevelWorkerFinishedRunnable"),
274         mFinishedWorker(aFinishedWorker) {
275     aFinishedWorker->AssertIsOnWorkerThread();
276   }
277 
278   NS_INLINE_DECL_REFCOUNTING_INHERITED(TopLevelWorkerFinishedRunnable, Runnable)
279 
280  private:
281   ~TopLevelWorkerFinishedRunnable() = default;
282 
283   NS_IMETHOD
Run()284   Run() override {
285     AssertIsOnMainThread();
286 
287     RuntimeService* runtime = RuntimeService::GetService();
288     MOZ_ASSERT(runtime);
289 
290     mFinishedWorker->DisableDebugger();
291 
292     runtime->UnregisterWorker(*mFinishedWorker);
293 
294     if (!mFinishedWorker->ProxyReleaseMainThreadObjects()) {
295       NS_WARNING("Failed to dispatch, going to leak!");
296     }
297 
298     mFinishedWorker->ClearSelfAndParentEventTargetRef();
299     return NS_OK;
300   }
301 };
302 
303 class ModifyBusyCountRunnable final : public WorkerControlRunnable {
304   bool mIncrease;
305 
306  public:
ModifyBusyCountRunnable(WorkerPrivate * aWorkerPrivate,bool aIncrease)307   ModifyBusyCountRunnable(WorkerPrivate* aWorkerPrivate, bool aIncrease)
308       : WorkerControlRunnable(aWorkerPrivate, ParentThreadUnchangedBusyCount),
309         mIncrease(aIncrease) {}
310 
311  private:
WorkerRun(JSContext * aCx,WorkerPrivate * aWorkerPrivate)312   virtual bool WorkerRun(JSContext* aCx,
313                          WorkerPrivate* aWorkerPrivate) override {
314     return aWorkerPrivate->ModifyBusyCount(mIncrease);
315   }
316 
PostRun(JSContext * aCx,WorkerPrivate * aWorkerPrivate,bool aRunResult)317   virtual void PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
318                        bool aRunResult) override {
319     if (mIncrease) {
320       WorkerControlRunnable::PostRun(aCx, aWorkerPrivate, aRunResult);
321       return;
322     }
323     // Don't do anything here as it's possible that aWorkerPrivate has been
324     // deleted.
325   }
326 };
327 
328 class CompileScriptRunnable final : public WorkerDebuggeeRunnable {
329   nsString mScriptURL;
330   UniquePtr<SerializedStackHolder> mOriginStack;
331 
332  public:
CompileScriptRunnable(WorkerPrivate * aWorkerPrivate,UniquePtr<SerializedStackHolder> aOriginStack,const nsAString & aScriptURL)333   explicit CompileScriptRunnable(WorkerPrivate* aWorkerPrivate,
334                                  UniquePtr<SerializedStackHolder> aOriginStack,
335                                  const nsAString& aScriptURL)
336       : WorkerDebuggeeRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount),
337         mScriptURL(aScriptURL),
338         mOriginStack(aOriginStack.release()) {}
339 
340  private:
341   // We can't implement PreRun effectively, because at the point when that would
342   // run we have not yet done our load so don't know things like our final
343   // principal and whatnot.
344 
WorkerRun(JSContext * aCx,WorkerPrivate * aWorkerPrivate)345   virtual bool WorkerRun(JSContext* aCx,
346                          WorkerPrivate* aWorkerPrivate) override {
347     aWorkerPrivate->AssertIsOnWorkerThread();
348 
349     WorkerGlobalScope* globalScope =
350         aWorkerPrivate->GetOrCreateGlobalScope(aCx);
351     if (NS_WARN_IF(!globalScope)) {
352       return false;
353     }
354 
355     if (NS_WARN_IF(!aWorkerPrivate->EnsureCSPEventListener())) {
356       return false;
357     }
358 
359     ErrorResult rv;
360     workerinternals::LoadMainScript(aWorkerPrivate, std::move(mOriginStack),
361                                     mScriptURL, WorkerScript, rv);
362     rv.WouldReportJSException();
363     // Explicitly ignore NS_BINDING_ABORTED on rv.  Or more precisely, still
364     // return false and don't SetWorkerScriptExecutedSuccessfully() in that
365     // case, but don't throw anything on aCx.  The idea is to not dispatch error
366     // events if our load is canceled with that error code.
367     if (rv.ErrorCodeIs(NS_BINDING_ABORTED)) {
368       rv.SuppressException();
369       return false;
370     }
371 
372     // Make sure to propagate exceptions from rv onto aCx, so that they will get
373     // reported after we return.  We want to propagate just JS exceptions,
374     // because all the other errors are handled when the script is loaded.
375     // See: https://dom.spec.whatwg.org/#concept-event-fire
376     if (rv.Failed() && !rv.IsJSException()) {
377       WorkerErrorReport::CreateAndDispatchGenericErrorRunnableToParent(
378           aWorkerPrivate);
379       rv.SuppressException();
380       return false;
381     }
382 
383     // This is a little dumb, but aCx is in the null realm here because we
384     // set it up that way in our Run(), since we had not created the global at
385     // that point yet.  So we need to enter the realm of our global,
386     // because setting a pending exception on aCx involves wrapping into its
387     // current compartment.  Luckily we have a global now.
388     JSAutoRealm ar(aCx, globalScope->GetGlobalJSObject());
389     if (rv.MaybeSetPendingException(aCx)) {
390       // In the event of an uncaught exception, the worker should still keep
391       // running (return true) but should not be marked as having executed
392       // successfully (which will cause ServiceWorker installation to fail).
393       // In previous error handling cases in this method, we return false (to
394       // trigger CloseInternal) because the global is not in an operable
395       // state at all.
396       //
397       // For ServiceWorkers, this would correspond to the "Run Service Worker"
398       // algorithm returning an "abrupt completion" and _not_ failure.
399       //
400       // For DedicatedWorkers and SharedWorkers, this would correspond to the
401       // "run a worker" algorithm disregarding the return value of "run the
402       // classic script"/"run the module script" in step 24:
403       //
404       // "If script is a classic script, then run the classic script script.
405       // Otherwise, it is a module script; run the module script script."
406       return true;
407     }
408 
409     aWorkerPrivate->SetWorkerScriptExecutedSuccessfully();
410     return true;
411   }
412 
PostRun(JSContext * aCx,WorkerPrivate * aWorkerPrivate,bool aRunResult)413   void PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
414                bool aRunResult) override {
415     if (!aRunResult) {
416       aWorkerPrivate->CloseInternal();
417     }
418     WorkerRunnable::PostRun(aCx, aWorkerPrivate, aRunResult);
419   }
420 };
421 
422 class NotifyRunnable final : public WorkerControlRunnable {
423   WorkerStatus mStatus;
424 
425  public:
NotifyRunnable(WorkerPrivate * aWorkerPrivate,WorkerStatus aStatus)426   NotifyRunnable(WorkerPrivate* aWorkerPrivate, WorkerStatus aStatus)
427       : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
428         mStatus(aStatus) {
429     MOZ_ASSERT(aStatus == Closing || aStatus == Canceling ||
430                aStatus == Killing);
431   }
432 
433  private:
PreDispatch(WorkerPrivate * aWorkerPrivate)434   virtual bool PreDispatch(WorkerPrivate* aWorkerPrivate) override {
435     aWorkerPrivate->AssertIsOnParentThread();
436     return aWorkerPrivate->ModifyBusyCount(true);
437   }
438 
PostDispatch(WorkerPrivate * aWorkerPrivate,bool aDispatchResult)439   virtual void PostDispatch(WorkerPrivate* aWorkerPrivate,
440                             bool aDispatchResult) override {
441     aWorkerPrivate->AssertIsOnParentThread();
442     if (!aDispatchResult) {
443       // We couldn't dispatch to the worker, which means it's already dead.
444       // Undo the busy count modification.
445       aWorkerPrivate->ModifyBusyCount(false);
446     }
447   }
448 
PostRun(JSContext * aCx,WorkerPrivate * aWorkerPrivate,bool aRunResult)449   virtual void PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
450                        bool aRunResult) override {
451     aWorkerPrivate->ModifyBusyCountFromWorker(false);
452   }
453 
WorkerRun(JSContext * aCx,WorkerPrivate * aWorkerPrivate)454   virtual bool WorkerRun(JSContext* aCx,
455                          WorkerPrivate* aWorkerPrivate) override {
456     return aWorkerPrivate->NotifyInternal(mStatus);
457   }
458 };
459 
460 class FreezeRunnable final : public WorkerControlRunnable {
461  public:
FreezeRunnable(WorkerPrivate * aWorkerPrivate)462   explicit FreezeRunnable(WorkerPrivate* aWorkerPrivate)
463       : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount) {}
464 
465  private:
WorkerRun(JSContext * aCx,WorkerPrivate * aWorkerPrivate)466   virtual bool WorkerRun(JSContext* aCx,
467                          WorkerPrivate* aWorkerPrivate) override {
468     return aWorkerPrivate->FreezeInternal();
469   }
470 };
471 
472 class ThawRunnable final : public WorkerControlRunnable {
473  public:
ThawRunnable(WorkerPrivate * aWorkerPrivate)474   explicit ThawRunnable(WorkerPrivate* aWorkerPrivate)
475       : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount) {}
476 
477  private:
WorkerRun(JSContext * aCx,WorkerPrivate * aWorkerPrivate)478   virtual bool WorkerRun(JSContext* aCx,
479                          WorkerPrivate* aWorkerPrivate) override {
480     return aWorkerPrivate->ThawInternal();
481   }
482 };
483 
484 class PropagateStorageAccessPermissionGrantedRunnable final
485     : public WorkerControlRunnable {
486  public:
PropagateStorageAccessPermissionGrantedRunnable(WorkerPrivate * aWorkerPrivate)487   explicit PropagateStorageAccessPermissionGrantedRunnable(
488       WorkerPrivate* aWorkerPrivate)
489       : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount) {}
490 
491  private:
WorkerRun(JSContext * aCx,WorkerPrivate * aWorkerPrivate)492   bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
493     aWorkerPrivate->PropagateStorageAccessPermissionGrantedInternal();
494     return true;
495   }
496 };
497 
498 class ReportErrorToConsoleRunnable final : public WorkerRunnable {
499   const char* mMessage;
500   const nsTArray<nsString> mParams;
501 
502  public:
503   // aWorkerPrivate is the worker thread we're on (or the main thread, if null)
Report(WorkerPrivate * aWorkerPrivate,const char * aMessage,const nsTArray<nsString> & aParams)504   static void Report(WorkerPrivate* aWorkerPrivate, const char* aMessage,
505                      const nsTArray<nsString>& aParams) {
506     if (aWorkerPrivate) {
507       aWorkerPrivate->AssertIsOnWorkerThread();
508     } else {
509       AssertIsOnMainThread();
510     }
511 
512     // Now fire a runnable to do the same on the parent's thread if we can.
513     if (aWorkerPrivate) {
514       RefPtr<ReportErrorToConsoleRunnable> runnable =
515           new ReportErrorToConsoleRunnable(aWorkerPrivate, aMessage, aParams);
516       runnable->Dispatch();
517       return;
518     }
519 
520     // Log a warning to the console.
521     nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, "DOM"_ns,
522                                     nullptr, nsContentUtils::eDOM_PROPERTIES,
523                                     aMessage, aParams);
524   }
525 
526  private:
ReportErrorToConsoleRunnable(WorkerPrivate * aWorkerPrivate,const char * aMessage,const nsTArray<nsString> & aParams)527   ReportErrorToConsoleRunnable(WorkerPrivate* aWorkerPrivate,
528                                const char* aMessage,
529                                const nsTArray<nsString>& aParams)
530       : WorkerRunnable(aWorkerPrivate, ParentThreadUnchangedBusyCount),
531         mMessage(aMessage),
532         mParams(aParams.Clone()) {}
533 
PostDispatch(WorkerPrivate * aWorkerPrivate,bool aDispatchResult)534   virtual void PostDispatch(WorkerPrivate* aWorkerPrivate,
535                             bool aDispatchResult) override {
536     aWorkerPrivate->AssertIsOnWorkerThread();
537 
538     // Dispatch may fail if the worker was canceled, no need to report that as
539     // an error, so don't call base class PostDispatch.
540   }
541 
WorkerRun(JSContext * aCx,WorkerPrivate * aWorkerPrivate)542   virtual bool WorkerRun(JSContext* aCx,
543                          WorkerPrivate* aWorkerPrivate) override {
544     WorkerPrivate* parent = aWorkerPrivate->GetParent();
545     MOZ_ASSERT_IF(!parent, NS_IsMainThread());
546     Report(parent, mMessage, mParams);
547     return true;
548   }
549 };
550 
551 class TimerRunnable final : public WorkerRunnable,
552                             public nsITimerCallback,
553                             public nsINamed {
554  public:
555   NS_DECL_ISUPPORTS_INHERITED
556 
TimerRunnable(WorkerPrivate * aWorkerPrivate)557   explicit TimerRunnable(WorkerPrivate* aWorkerPrivate)
558       : WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount) {}
559 
560  private:
561   ~TimerRunnable() = default;
562 
PreDispatch(WorkerPrivate * aWorkerPrivate)563   virtual bool PreDispatch(WorkerPrivate* aWorkerPrivate) override {
564     // Silence bad assertions.
565     return true;
566   }
567 
PostDispatch(WorkerPrivate * aWorkerPrivate,bool aDispatchResult)568   virtual void PostDispatch(WorkerPrivate* aWorkerPrivate,
569                             bool aDispatchResult) override {
570     // Silence bad assertions.
571   }
572 
573   // MOZ_CAN_RUN_SCRIPT_BOUNDARY until worker runnables are generally
574   // MOZ_CAN_RUN_SCRIPT.
575   MOZ_CAN_RUN_SCRIPT_BOUNDARY
WorkerRun(JSContext * aCx,WorkerPrivate * aWorkerPrivate)576   virtual bool WorkerRun(JSContext* aCx,
577                          WorkerPrivate* aWorkerPrivate) override {
578     return aWorkerPrivate->RunExpiredTimeouts(aCx);
579   }
580 
581   NS_IMETHOD
Notify(nsITimer * aTimer)582   Notify(nsITimer* aTimer) override { return Run(); }
583 
584   NS_IMETHOD
GetName(nsACString & aName)585   GetName(nsACString& aName) override {
586     aName.AssignLiteral("TimerRunnable");
587     return NS_OK;
588   }
589 };
590 
591 NS_IMPL_ISUPPORTS_INHERITED(TimerRunnable, WorkerRunnable, nsITimerCallback,
592                             nsINamed)
593 
594 class DebuggerImmediateRunnable : public WorkerRunnable {
595   RefPtr<dom::Function> mHandler;
596 
597  public:
DebuggerImmediateRunnable(WorkerPrivate * aWorkerPrivate,dom::Function & aHandler)598   explicit DebuggerImmediateRunnable(WorkerPrivate* aWorkerPrivate,
599                                      dom::Function& aHandler)
600       : WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
601         mHandler(&aHandler) {}
602 
603  private:
IsDebuggerRunnable() const604   virtual bool IsDebuggerRunnable() const override { return true; }
605 
PreDispatch(WorkerPrivate * aWorkerPrivate)606   virtual bool PreDispatch(WorkerPrivate* aWorkerPrivate) override {
607     // Silence bad assertions.
608     return true;
609   }
610 
PostDispatch(WorkerPrivate * aWorkerPrivate,bool aDispatchResult)611   virtual void PostDispatch(WorkerPrivate* aWorkerPrivate,
612                             bool aDispatchResult) override {
613     // Silence bad assertions.
614   }
615 
WorkerRun(JSContext * aCx,WorkerPrivate * aWorkerPrivate)616   virtual bool WorkerRun(JSContext* aCx,
617                          WorkerPrivate* aWorkerPrivate) override {
618     JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx));
619     JS::Rooted<JS::Value> callable(
620         aCx, JS::ObjectOrNullValue(mHandler->CallableOrNull()));
621     JS::HandleValueArray args = JS::HandleValueArray::empty();
622     JS::Rooted<JS::Value> rval(aCx);
623     if (!JS_CallFunctionValue(aCx, global, callable, args, &rval)) {
624       // Just return false; WorkerRunnable::Run will report the exception.
625       return false;
626     }
627 
628     return true;
629   }
630 };
631 
PeriodicGCTimerCallback(nsITimer * aTimer,void * aClosure)632 void PeriodicGCTimerCallback(nsITimer* aTimer, void* aClosure) {
633   auto workerPrivate = static_cast<WorkerPrivate*>(aClosure);
634   MOZ_DIAGNOSTIC_ASSERT(workerPrivate);
635   workerPrivate->AssertIsOnWorkerThread();
636   workerPrivate->GarbageCollectInternal(workerPrivate->GetJSContext(),
637                                         false /* shrinking */,
638                                         false /* collect children */);
639 }
640 
IdleGCTimerCallback(nsITimer * aTimer,void * aClosure)641 void IdleGCTimerCallback(nsITimer* aTimer, void* aClosure) {
642   auto workerPrivate = static_cast<WorkerPrivate*>(aClosure);
643   MOZ_DIAGNOSTIC_ASSERT(workerPrivate);
644   workerPrivate->AssertIsOnWorkerThread();
645   workerPrivate->GarbageCollectInternal(workerPrivate->GetJSContext(),
646                                         true /* shrinking */,
647                                         false /* collect children */);
648 }
649 
650 class UpdateContextOptionsRunnable final : public WorkerControlRunnable {
651   JS::ContextOptions mContextOptions;
652 
653  public:
UpdateContextOptionsRunnable(WorkerPrivate * aWorkerPrivate,const JS::ContextOptions & aContextOptions)654   UpdateContextOptionsRunnable(WorkerPrivate* aWorkerPrivate,
655                                const JS::ContextOptions& aContextOptions)
656       : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
657         mContextOptions(aContextOptions) {}
658 
659  private:
WorkerRun(JSContext * aCx,WorkerPrivate * aWorkerPrivate)660   virtual bool WorkerRun(JSContext* aCx,
661                          WorkerPrivate* aWorkerPrivate) override {
662     aWorkerPrivate->UpdateContextOptionsInternal(aCx, mContextOptions);
663     return true;
664   }
665 };
666 
667 class UpdateLanguagesRunnable final : public WorkerRunnable {
668   nsTArray<nsString> mLanguages;
669 
670  public:
UpdateLanguagesRunnable(WorkerPrivate * aWorkerPrivate,const nsTArray<nsString> & aLanguages)671   UpdateLanguagesRunnable(WorkerPrivate* aWorkerPrivate,
672                           const nsTArray<nsString>& aLanguages)
673       : WorkerRunnable(aWorkerPrivate), mLanguages(aLanguages.Clone()) {}
674 
WorkerRun(JSContext * aCx,WorkerPrivate * aWorkerPrivate)675   virtual bool WorkerRun(JSContext* aCx,
676                          WorkerPrivate* aWorkerPrivate) override {
677     aWorkerPrivate->UpdateLanguagesInternal(mLanguages);
678     return true;
679   }
680 };
681 
682 class UpdateJSWorkerMemoryParameterRunnable final
683     : public WorkerControlRunnable {
684   Maybe<uint32_t> mValue;
685   JSGCParamKey mKey;
686 
687  public:
UpdateJSWorkerMemoryParameterRunnable(WorkerPrivate * aWorkerPrivate,JSGCParamKey aKey,Maybe<uint32_t> aValue)688   UpdateJSWorkerMemoryParameterRunnable(WorkerPrivate* aWorkerPrivate,
689                                         JSGCParamKey aKey,
690                                         Maybe<uint32_t> aValue)
691       : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
692         mValue(aValue),
693         mKey(aKey) {}
694 
695  private:
WorkerRun(JSContext * aCx,WorkerPrivate * aWorkerPrivate)696   virtual bool WorkerRun(JSContext* aCx,
697                          WorkerPrivate* aWorkerPrivate) override {
698     aWorkerPrivate->UpdateJSWorkerMemoryParameterInternal(aCx, mKey, mValue);
699     return true;
700   }
701 };
702 
703 #ifdef JS_GC_ZEAL
704 class UpdateGCZealRunnable final : public WorkerControlRunnable {
705   uint8_t mGCZeal;
706   uint32_t mFrequency;
707 
708  public:
UpdateGCZealRunnable(WorkerPrivate * aWorkerPrivate,uint8_t aGCZeal,uint32_t aFrequency)709   UpdateGCZealRunnable(WorkerPrivate* aWorkerPrivate, uint8_t aGCZeal,
710                        uint32_t aFrequency)
711       : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
712         mGCZeal(aGCZeal),
713         mFrequency(aFrequency) {}
714 
715  private:
WorkerRun(JSContext * aCx,WorkerPrivate * aWorkerPrivate)716   virtual bool WorkerRun(JSContext* aCx,
717                          WorkerPrivate* aWorkerPrivate) override {
718     aWorkerPrivate->UpdateGCZealInternal(aCx, mGCZeal, mFrequency);
719     return true;
720   }
721 };
722 #endif
723 
724 class SetLowMemoryStateRunnable final : public WorkerControlRunnable {
725   bool mState;
726 
727  public:
SetLowMemoryStateRunnable(WorkerPrivate * aWorkerPrivate,bool aState)728   SetLowMemoryStateRunnable(WorkerPrivate* aWorkerPrivate, bool aState)
729       : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
730         mState(aState) {}
731 
732  private:
WorkerRun(JSContext * aCx,WorkerPrivate * aWorkerPrivate)733   virtual bool WorkerRun(JSContext* aCx,
734                          WorkerPrivate* aWorkerPrivate) override {
735     aWorkerPrivate->SetLowMemoryStateInternal(aCx, mState);
736     return true;
737   }
738 };
739 
740 class GarbageCollectRunnable final : public WorkerControlRunnable {
741   bool mShrinking;
742   bool mCollectChildren;
743 
744  public:
GarbageCollectRunnable(WorkerPrivate * aWorkerPrivate,bool aShrinking,bool aCollectChildren)745   GarbageCollectRunnable(WorkerPrivate* aWorkerPrivate, bool aShrinking,
746                          bool aCollectChildren)
747       : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
748         mShrinking(aShrinking),
749         mCollectChildren(aCollectChildren) {}
750 
751  private:
PreDispatch(WorkerPrivate * aWorkerPrivate)752   virtual bool PreDispatch(WorkerPrivate* aWorkerPrivate) override {
753     // Silence bad assertions, this can be dispatched from either the main
754     // thread or the timer thread..
755     return true;
756   }
757 
PostDispatch(WorkerPrivate * aWorkerPrivate,bool aDispatchResult)758   virtual void PostDispatch(WorkerPrivate* aWorkerPrivate,
759                             bool aDispatchResult) override {
760     // Silence bad assertions, this can be dispatched from either the main
761     // thread or the timer thread..
762   }
763 
WorkerRun(JSContext * aCx,WorkerPrivate * aWorkerPrivate)764   virtual bool WorkerRun(JSContext* aCx,
765                          WorkerPrivate* aWorkerPrivate) override {
766     aWorkerPrivate->GarbageCollectInternal(aCx, mShrinking, mCollectChildren);
767     return true;
768   }
769 };
770 
771 class CycleCollectRunnable : public WorkerControlRunnable {
772   bool mCollectChildren;
773 
774  public:
CycleCollectRunnable(WorkerPrivate * aWorkerPrivate,bool aCollectChildren)775   CycleCollectRunnable(WorkerPrivate* aWorkerPrivate, bool aCollectChildren)
776       : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
777         mCollectChildren(aCollectChildren) {}
778 
WorkerRun(JSContext * aCx,WorkerPrivate * aWorkerPrivate)779   bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
780     aWorkerPrivate->CycleCollectInternal(mCollectChildren);
781     return true;
782   }
783 };
784 
785 class OfflineStatusChangeRunnable : public WorkerRunnable {
786  public:
OfflineStatusChangeRunnable(WorkerPrivate * aWorkerPrivate,bool aIsOffline)787   OfflineStatusChangeRunnable(WorkerPrivate* aWorkerPrivate, bool aIsOffline)
788       : WorkerRunnable(aWorkerPrivate), mIsOffline(aIsOffline) {}
789 
WorkerRun(JSContext * aCx,WorkerPrivate * aWorkerPrivate)790   bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
791     aWorkerPrivate->OfflineStatusChangeEventInternal(mIsOffline);
792     return true;
793   }
794 
795  private:
796   bool mIsOffline;
797 };
798 
799 class MemoryPressureRunnable : public WorkerControlRunnable {
800  public:
MemoryPressureRunnable(WorkerPrivate * aWorkerPrivate)801   explicit MemoryPressureRunnable(WorkerPrivate* aWorkerPrivate)
802       : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount) {}
803 
WorkerRun(JSContext * aCx,WorkerPrivate * aWorkerPrivate)804   bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
805     aWorkerPrivate->MemoryPressureInternal();
806     return true;
807   }
808 };
809 
810 #ifdef DEBUG
StartsWithExplicit(nsACString & s)811 static bool StartsWithExplicit(nsACString& s) {
812   return StringBeginsWith(s, "explicit/"_ns);
813 }
814 #endif
815 
PRThreadFromThread(nsIThread * aThread)816 PRThread* PRThreadFromThread(nsIThread* aThread) {
817   MOZ_ASSERT(aThread);
818 
819   PRThread* result;
820   MOZ_ALWAYS_SUCCEEDS(aThread->GetPRThread(&result));
821   MOZ_ASSERT(result);
822 
823   return result;
824 }
825 
826 // A runnable to cancel the worker from the parent thread when self.close() is
827 // called. This runnable is executed on the parent process in order to cancel
828 // the current runnable. It uses a normal WorkerDebuggeeRunnable in order to be
829 // sure that all the pending WorkerDebuggeeRunnables are executed before this.
830 class CancelingOnParentRunnable final : public WorkerDebuggeeRunnable {
831  public:
CancelingOnParentRunnable(WorkerPrivate * aWorkerPrivate)832   explicit CancelingOnParentRunnable(WorkerPrivate* aWorkerPrivate)
833       : WorkerDebuggeeRunnable(aWorkerPrivate, ParentThreadUnchangedBusyCount) {
834   }
835 
WorkerRun(JSContext * aCx,WorkerPrivate * aWorkerPrivate)836   bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
837     aWorkerPrivate->Cancel();
838     return true;
839   }
840 };
841 
842 // A runnable to cancel the worker from the parent process.
843 class CancelingWithTimeoutOnParentRunnable final
844     : public WorkerControlRunnable {
845  public:
CancelingWithTimeoutOnParentRunnable(WorkerPrivate * aWorkerPrivate)846   explicit CancelingWithTimeoutOnParentRunnable(WorkerPrivate* aWorkerPrivate)
847       : WorkerControlRunnable(aWorkerPrivate, ParentThreadUnchangedBusyCount) {}
848 
WorkerRun(JSContext * aCx,WorkerPrivate * aWorkerPrivate)849   bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
850     aWorkerPrivate->AssertIsOnParentThread();
851     aWorkerPrivate->StartCancelingTimer();
852     return true;
853   }
854 };
855 
856 class CancelingTimerCallback final : public nsITimerCallback {
857  public:
858   NS_DECL_ISUPPORTS
859 
CancelingTimerCallback(WorkerPrivate * aWorkerPrivate)860   explicit CancelingTimerCallback(WorkerPrivate* aWorkerPrivate)
861       : mWorkerPrivate(aWorkerPrivate) {}
862 
863   NS_IMETHOD
Notify(nsITimer * aTimer)864   Notify(nsITimer* aTimer) override {
865     mWorkerPrivate->AssertIsOnParentThread();
866     mWorkerPrivate->Cancel();
867     return NS_OK;
868   }
869 
870  private:
871   ~CancelingTimerCallback() = default;
872 
873   // Raw pointer here is OK because the timer is canceled during the shutdown
874   // steps.
875   WorkerPrivate* mWorkerPrivate;
876 };
877 
878 NS_IMPL_ISUPPORTS(CancelingTimerCallback, nsITimerCallback)
879 
880 // This runnable starts the canceling of a worker after a self.close().
881 class CancelingRunnable final : public Runnable {
882  public:
CancelingRunnable()883   CancelingRunnable() : Runnable("CancelingRunnable") {}
884 
885   NS_IMETHOD
Run()886   Run() override {
887     WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
888     MOZ_ASSERT(workerPrivate);
889     workerPrivate->AssertIsOnWorkerThread();
890 
891     // Now we can cancel the this worker from the parent process.
892     RefPtr<CancelingOnParentRunnable> r =
893         new CancelingOnParentRunnable(workerPrivate);
894     r->Dispatch();
895 
896     return NS_OK;
897   }
898 };
899 
900 } /* anonymous namespace */
901 
ComputeWorkerPrivateId()902 nsString ComputeWorkerPrivateId() {
903   nsresult rv;
904   nsCOMPtr<nsIUUIDGenerator> uuidGenerator =
905       do_GetService("@mozilla.org/uuid-generator;1", &rv);
906   MOZ_ASSERT(NS_SUCCEEDED(rv));
907 
908   nsID uuid;
909   rv = uuidGenerator->GenerateUUIDInPlace(&uuid);
910   MOZ_ASSERT(NS_SUCCEEDED(rv));
911   char buffer[NSID_LENGTH];
912   uuid.ToProvidedString(buffer);
913 
914   nsString id;
915   // Remove {} and the null terminator
916   id.AssignASCII(&buffer[1], NSID_LENGTH - 3);
917 
918   return id;
919 }
920 
921 class WorkerPrivate::EventTarget final : public nsISerialEventTarget {
922   // This mutex protects mWorkerPrivate and must be acquired *before* the
923   // WorkerPrivate's mutex whenever they must both be held.
924   mozilla::Mutex mMutex;
925   WorkerPrivate* mWorkerPrivate;
926   nsIEventTarget* mWeakNestedEventTarget;
927   nsCOMPtr<nsIEventTarget> mNestedEventTarget;
928 
929  public:
EventTarget(WorkerPrivate * aWorkerPrivate)930   explicit EventTarget(WorkerPrivate* aWorkerPrivate)
931       : mMutex("WorkerPrivate::EventTarget::mMutex"),
932         mWorkerPrivate(aWorkerPrivate),
933         mWeakNestedEventTarget(nullptr) {
934     MOZ_ASSERT(aWorkerPrivate);
935   }
936 
EventTarget(WorkerPrivate * aWorkerPrivate,nsIEventTarget * aNestedEventTarget)937   EventTarget(WorkerPrivate* aWorkerPrivate, nsIEventTarget* aNestedEventTarget)
938       : mMutex("WorkerPrivate::EventTarget::mMutex"),
939         mWorkerPrivate(aWorkerPrivate),
940         mWeakNestedEventTarget(aNestedEventTarget),
941         mNestedEventTarget(aNestedEventTarget) {
942     MOZ_ASSERT(aWorkerPrivate);
943     MOZ_ASSERT(aNestedEventTarget);
944   }
945 
Disable()946   void Disable() {
947     nsCOMPtr<nsIEventTarget> nestedEventTarget;
948     {
949       MutexAutoLock lock(mMutex);
950 
951       // Note, Disable() can be called more than once safely.
952       mWorkerPrivate = nullptr;
953       mNestedEventTarget.swap(nestedEventTarget);
954     }
955   }
956 
GetWeakNestedEventTarget() const957   nsIEventTarget* GetWeakNestedEventTarget() const {
958     MOZ_ASSERT(mWeakNestedEventTarget);
959     return mWeakNestedEventTarget;
960   }
961 
962   NS_DECL_THREADSAFE_ISUPPORTS
963   NS_DECL_NSIEVENTTARGET_FULL
964 
965  private:
966   ~EventTarget() = default;
967 };
968 
969 struct WorkerPrivate::TimeoutInfo {
TimeoutInfomozilla::dom::WorkerPrivate::TimeoutInfo970   TimeoutInfo()
971       : mId(0),
972         mNestingLevel(0),
973         mIsInterval(false),
974         mCanceled(false),
975         mOnChromeWorker(false) {
976     MOZ_COUNT_CTOR(mozilla::dom::WorkerPrivate::TimeoutInfo);
977   }
978 
~TimeoutInfomozilla::dom::WorkerPrivate::TimeoutInfo979   ~TimeoutInfo() { MOZ_COUNT_DTOR(mozilla::dom::WorkerPrivate::TimeoutInfo); }
980 
operator ==mozilla::dom::WorkerPrivate::TimeoutInfo981   bool operator==(const TimeoutInfo& aOther) {
982     return mTargetTime == aOther.mTargetTime;
983   }
984 
operator <mozilla::dom::WorkerPrivate::TimeoutInfo985   bool operator<(const TimeoutInfo& aOther) {
986     return mTargetTime < aOther.mTargetTime;
987   }
988 
AccumulateNestingLevelmozilla::dom::WorkerPrivate::TimeoutInfo989   void AccumulateNestingLevel(const uint32_t& aBaseLevel) {
990     if (aBaseLevel < kClampTimeoutNestingLevel) {
991       mNestingLevel = aBaseLevel + 1;
992       return;
993     }
994     mNestingLevel = kClampTimeoutNestingLevel;
995   }
996 
CalculateTargetTimemozilla::dom::WorkerPrivate::TimeoutInfo997   void CalculateTargetTime() {
998     auto target = mInterval;
999     // Don't clamp timeout for chrome workers
1000     if (mNestingLevel >= kClampTimeoutNestingLevel && !mOnChromeWorker) {
1001       target = TimeDuration::Max(
1002           mInterval,
1003           TimeDuration::FromMilliseconds(StaticPrefs::dom_min_timeout_value()));
1004     }
1005     mTargetTime = TimeStamp::Now() + target;
1006   }
1007 
1008   RefPtr<TimeoutHandler> mHandler;
1009   mozilla::TimeStamp mTargetTime;
1010   mozilla::TimeDuration mInterval;
1011   int32_t mId;
1012   uint32_t mNestingLevel;
1013   bool mIsInterval;
1014   bool mCanceled;
1015   bool mOnChromeWorker;
1016 };
1017 
1018 class WorkerJSContextStats final : public JS::RuntimeStats {
1019   const nsCString mRtPath;
1020 
1021  public:
WorkerJSContextStats(const nsACString & aRtPath)1022   explicit WorkerJSContextStats(const nsACString& aRtPath)
1023       : JS::RuntimeStats(JsWorkerMallocSizeOf), mRtPath(aRtPath) {}
1024 
~WorkerJSContextStats()1025   ~WorkerJSContextStats() {
1026     for (size_t i = 0; i != zoneStatsVector.length(); i++) {
1027       delete static_cast<xpc::ZoneStatsExtras*>(zoneStatsVector[i].extra);
1028     }
1029 
1030     for (size_t i = 0; i != realmStatsVector.length(); i++) {
1031       delete static_cast<xpc::RealmStatsExtras*>(realmStatsVector[i].extra);
1032     }
1033   }
1034 
Path() const1035   const nsCString& Path() const { return mRtPath; }
1036 
initExtraZoneStats(JS::Zone * aZone,JS::ZoneStats * aZoneStats,const JS::AutoRequireNoGC & nogc)1037   virtual void initExtraZoneStats(JS::Zone* aZone, JS::ZoneStats* aZoneStats,
1038                                   const JS::AutoRequireNoGC& nogc) override {
1039     MOZ_ASSERT(!aZoneStats->extra);
1040 
1041     // ReportJSRuntimeExplicitTreeStats expects that
1042     // aZoneStats->extra is a xpc::ZoneStatsExtras pointer.
1043     xpc::ZoneStatsExtras* extras = new xpc::ZoneStatsExtras;
1044     extras->pathPrefix = mRtPath;
1045     extras->pathPrefix += nsPrintfCString("zone(0x%p)/", (void*)aZone);
1046 
1047     MOZ_ASSERT(StartsWithExplicit(extras->pathPrefix));
1048 
1049     aZoneStats->extra = extras;
1050   }
1051 
initExtraRealmStats(JS::Realm * aRealm,JS::RealmStats * aRealmStats,const JS::AutoRequireNoGC & nogc)1052   virtual void initExtraRealmStats(JS::Realm* aRealm,
1053                                    JS::RealmStats* aRealmStats,
1054                                    const JS::AutoRequireNoGC& nogc) override {
1055     MOZ_ASSERT(!aRealmStats->extra);
1056 
1057     // ReportJSRuntimeExplicitTreeStats expects that
1058     // aRealmStats->extra is a xpc::RealmStatsExtras pointer.
1059     xpc::RealmStatsExtras* extras = new xpc::RealmStatsExtras;
1060 
1061     // This is the |jsPathPrefix|.  Each worker has exactly one realm.
1062     extras->jsPathPrefix.Assign(mRtPath);
1063     extras->jsPathPrefix +=
1064         nsPrintfCString("zone(0x%p)/", (void*)js::GetRealmZone(aRealm));
1065     extras->jsPathPrefix += "realm(web-worker)/"_ns;
1066 
1067     // This should never be used when reporting with workers (hence the "?!").
1068     extras->domPathPrefix.AssignLiteral("explicit/workers/?!/");
1069 
1070     MOZ_ASSERT(StartsWithExplicit(extras->jsPathPrefix));
1071     MOZ_ASSERT(StartsWithExplicit(extras->domPathPrefix));
1072 
1073     extras->location = nullptr;
1074 
1075     aRealmStats->extra = extras;
1076   }
1077 };
1078 
1079 class WorkerPrivate::MemoryReporter final : public nsIMemoryReporter {
1080   NS_DECL_THREADSAFE_ISUPPORTS
1081 
1082   friend class WorkerPrivate;
1083 
1084   SharedMutex mMutex;
1085   WorkerPrivate* mWorkerPrivate;
1086 
1087  public:
MemoryReporter(WorkerPrivate * aWorkerPrivate)1088   explicit MemoryReporter(WorkerPrivate* aWorkerPrivate)
1089       : mMutex(aWorkerPrivate->mMutex), mWorkerPrivate(aWorkerPrivate) {
1090     aWorkerPrivate->AssertIsOnWorkerThread();
1091   }
1092 
1093   NS_IMETHOD
1094   CollectReports(nsIHandleReportCallback* aHandleReport, nsISupports* aData,
1095                  bool aAnonymize) override;
1096 
1097  private:
1098   class FinishCollectRunnable;
1099 
1100   class CollectReportsRunnable final : public MainThreadWorkerControlRunnable {
1101     RefPtr<FinishCollectRunnable> mFinishCollectRunnable;
1102     const bool mAnonymize;
1103 
1104    public:
1105     CollectReportsRunnable(WorkerPrivate* aWorkerPrivate,
1106                            nsIHandleReportCallback* aHandleReport,
1107                            nsISupports* aHandlerData, bool aAnonymize,
1108                            const nsACString& aPath);
1109 
1110    private:
1111     bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override;
1112 
~CollectReportsRunnable()1113     ~CollectReportsRunnable() {
1114       if (NS_IsMainThread()) {
1115         mFinishCollectRunnable->Run();
1116         return;
1117       }
1118 
1119       WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
1120       MOZ_ASSERT(workerPrivate);
1121       MOZ_ALWAYS_SUCCEEDS(workerPrivate->DispatchToMainThreadForMessaging(
1122           mFinishCollectRunnable.forget()));
1123     }
1124   };
1125 
1126   class FinishCollectRunnable final : public Runnable {
1127     nsCOMPtr<nsIHandleReportCallback> mHandleReport;
1128     nsCOMPtr<nsISupports> mHandlerData;
1129     size_t mPerformanceUserEntries;
1130     size_t mPerformanceResourceEntries;
1131     const bool mAnonymize;
1132     bool mSuccess;
1133 
1134    public:
1135     WorkerJSContextStats mCxStats;
1136 
1137     explicit FinishCollectRunnable(nsIHandleReportCallback* aHandleReport,
1138                                    nsISupports* aHandlerData, bool aAnonymize,
1139                                    const nsACString& aPath);
1140 
1141     NS_IMETHOD Run() override;
1142 
SetPerformanceSizes(size_t userEntries,size_t resourceEntries)1143     void SetPerformanceSizes(size_t userEntries, size_t resourceEntries) {
1144       mPerformanceUserEntries = userEntries;
1145       mPerformanceResourceEntries = resourceEntries;
1146     }
1147 
SetSuccess(bool success)1148     void SetSuccess(bool success) { mSuccess = success; }
1149 
1150    private:
~FinishCollectRunnable()1151     ~FinishCollectRunnable() {
1152       // mHandleReport and mHandlerData are released on the main thread.
1153       AssertIsOnMainThread();
1154     }
1155 
1156     FinishCollectRunnable(const FinishCollectRunnable&) = delete;
1157     FinishCollectRunnable& operator=(const FinishCollectRunnable&) = delete;
1158     FinishCollectRunnable& operator=(const FinishCollectRunnable&&) = delete;
1159   };
1160 
1161   ~MemoryReporter() = default;
1162 
Disable()1163   void Disable() {
1164     // Called from WorkerPrivate::DisableMemoryReporter.
1165     mMutex.AssertCurrentThreadOwns();
1166 
1167     NS_ASSERTION(mWorkerPrivate, "Disabled more than once!");
1168     mWorkerPrivate = nullptr;
1169   }
1170 };
1171 
NS_IMPL_ISUPPORTS(WorkerPrivate::MemoryReporter,nsIMemoryReporter)1172 NS_IMPL_ISUPPORTS(WorkerPrivate::MemoryReporter, nsIMemoryReporter)
1173 
1174 NS_IMETHODIMP
1175 WorkerPrivate::MemoryReporter::CollectReports(
1176     nsIHandleReportCallback* aHandleReport, nsISupports* aData,
1177     bool aAnonymize) {
1178   AssertIsOnMainThread();
1179 
1180   RefPtr<CollectReportsRunnable> runnable;
1181 
1182   {
1183     MutexAutoLock lock(mMutex);
1184 
1185     if (!mWorkerPrivate) {
1186       // This will effectively report 0 memory.
1187       nsCOMPtr<nsIMemoryReporterManager> manager =
1188           do_GetService("@mozilla.org/memory-reporter-manager;1");
1189       if (manager) {
1190         manager->EndReport();
1191       }
1192       return NS_OK;
1193     }
1194 
1195     nsAutoCString path;
1196     path.AppendLiteral("explicit/workers/workers(");
1197     if (aAnonymize && !mWorkerPrivate->Domain().IsEmpty()) {
1198       path.AppendLiteral("<anonymized-domain>)/worker(<anonymized-url>");
1199     } else {
1200       nsAutoCString escapedDomain(mWorkerPrivate->Domain());
1201       if (escapedDomain.IsEmpty()) {
1202         escapedDomain += "chrome";
1203       } else {
1204         escapedDomain.ReplaceChar('/', '\\');
1205       }
1206       path.Append(escapedDomain);
1207       path.AppendLiteral(")/worker(");
1208       NS_ConvertUTF16toUTF8 escapedURL(mWorkerPrivate->ScriptURL());
1209       escapedURL.ReplaceChar('/', '\\');
1210       path.Append(escapedURL);
1211     }
1212     path.AppendPrintf(", 0x%p)/", static_cast<void*>(mWorkerPrivate));
1213 
1214     runnable = new CollectReportsRunnable(mWorkerPrivate, aHandleReport, aData,
1215                                           aAnonymize, path);
1216   }
1217 
1218   if (!runnable->Dispatch()) {
1219     return NS_ERROR_UNEXPECTED;
1220   }
1221 
1222   return NS_OK;
1223 }
1224 
CollectReportsRunnable(WorkerPrivate * aWorkerPrivate,nsIHandleReportCallback * aHandleReport,nsISupports * aHandlerData,bool aAnonymize,const nsACString & aPath)1225 WorkerPrivate::MemoryReporter::CollectReportsRunnable::CollectReportsRunnable(
1226     WorkerPrivate* aWorkerPrivate, nsIHandleReportCallback* aHandleReport,
1227     nsISupports* aHandlerData, bool aAnonymize, const nsACString& aPath)
1228     : MainThreadWorkerControlRunnable(aWorkerPrivate),
1229       mFinishCollectRunnable(new FinishCollectRunnable(
1230           aHandleReport, aHandlerData, aAnonymize, aPath)),
1231       mAnonymize(aAnonymize) {}
1232 
WorkerRun(JSContext * aCx,WorkerPrivate * aWorkerPrivate)1233 bool WorkerPrivate::MemoryReporter::CollectReportsRunnable::WorkerRun(
1234     JSContext* aCx, WorkerPrivate* aWorkerPrivate) {
1235   aWorkerPrivate->AssertIsOnWorkerThread();
1236 
1237   RefPtr<WorkerGlobalScope> scope = aWorkerPrivate->GlobalScope();
1238   RefPtr<Performance> performance =
1239       scope ? scope->GetPerformanceIfExists() : nullptr;
1240   if (performance) {
1241     size_t userEntries = performance->SizeOfUserEntries(JsWorkerMallocSizeOf);
1242     size_t resourceEntries =
1243         performance->SizeOfResourceEntries(JsWorkerMallocSizeOf);
1244     mFinishCollectRunnable->SetPerformanceSizes(userEntries, resourceEntries);
1245   }
1246 
1247   mFinishCollectRunnable->SetSuccess(aWorkerPrivate->CollectRuntimeStats(
1248       &mFinishCollectRunnable->mCxStats, mAnonymize));
1249 
1250   return true;
1251 }
1252 
FinishCollectRunnable(nsIHandleReportCallback * aHandleReport,nsISupports * aHandlerData,bool aAnonymize,const nsACString & aPath)1253 WorkerPrivate::MemoryReporter::FinishCollectRunnable::FinishCollectRunnable(
1254     nsIHandleReportCallback* aHandleReport, nsISupports* aHandlerData,
1255     bool aAnonymize, const nsACString& aPath)
1256     : mozilla::Runnable(
1257           "dom::WorkerPrivate::MemoryReporter::FinishCollectRunnable"),
1258       mHandleReport(aHandleReport),
1259       mHandlerData(aHandlerData),
1260       mPerformanceUserEntries(0),
1261       mPerformanceResourceEntries(0),
1262       mAnonymize(aAnonymize),
1263       mSuccess(false),
1264       mCxStats(aPath) {}
1265 
1266 NS_IMETHODIMP
Run()1267 WorkerPrivate::MemoryReporter::FinishCollectRunnable::Run() {
1268   AssertIsOnMainThread();
1269 
1270   nsCOMPtr<nsIMemoryReporterManager> manager =
1271       do_GetService("@mozilla.org/memory-reporter-manager;1");
1272 
1273   if (!manager) return NS_OK;
1274 
1275   if (mSuccess) {
1276     xpc::ReportJSRuntimeExplicitTreeStats(
1277         mCxStats, mCxStats.Path(), mHandleReport, mHandlerData, mAnonymize);
1278 
1279     if (mPerformanceUserEntries) {
1280       nsCString path = mCxStats.Path();
1281       path.AppendLiteral("dom/performance/user-entries");
1282       mHandleReport->Callback(
1283           ""_ns, path, nsIMemoryReporter::KIND_HEAP,
1284           nsIMemoryReporter::UNITS_BYTES, mPerformanceUserEntries,
1285           "Memory used for performance user entries."_ns, mHandlerData);
1286     }
1287 
1288     if (mPerformanceResourceEntries) {
1289       nsCString path = mCxStats.Path();
1290       path.AppendLiteral("dom/performance/resource-entries");
1291       mHandleReport->Callback(
1292           ""_ns, path, nsIMemoryReporter::KIND_HEAP,
1293           nsIMemoryReporter::UNITS_BYTES, mPerformanceResourceEntries,
1294           "Memory used for performance resource entries."_ns, mHandlerData);
1295     }
1296   }
1297 
1298   manager->EndReport();
1299 
1300   return NS_OK;
1301 }
1302 
SyncLoopInfo(EventTarget * aEventTarget)1303 WorkerPrivate::SyncLoopInfo::SyncLoopInfo(EventTarget* aEventTarget)
1304     : mEventTarget(aEventTarget),
1305       mCompleted(false),
1306       mResult(false)
1307 #ifdef DEBUG
1308       ,
1309       mHasRun(false)
1310 #endif
1311 {
1312 }
1313 
GetDocument() const1314 Document* WorkerPrivate::GetDocument() const {
1315   AssertIsOnMainThread();
1316   if (mLoadInfo.mWindow) {
1317     return mLoadInfo.mWindow->GetExtantDoc();
1318   }
1319   // if we don't have a document, we should query the document
1320   // from the parent in case of a nested worker
1321   WorkerPrivate* parent = mParent;
1322   while (parent) {
1323     if (parent->mLoadInfo.mWindow) {
1324       return parent->mLoadInfo.mWindow->GetExtantDoc();
1325     }
1326     parent = parent->GetParent();
1327   }
1328   // couldn't query a document, give up and return nullptr
1329   return nullptr;
1330 }
1331 
SetCSP(nsIContentSecurityPolicy * aCSP)1332 void WorkerPrivate::SetCSP(nsIContentSecurityPolicy* aCSP) {
1333   AssertIsOnMainThread();
1334   if (!aCSP) {
1335     return;
1336   }
1337   aCSP->EnsureEventTarget(mMainThreadEventTarget);
1338 
1339   mLoadInfo.mCSP = aCSP;
1340   mLoadInfo.mCSPInfo = MakeUnique<CSPInfo>();
1341   nsresult rv = CSPToCSPInfo(mLoadInfo.mCSP, mLoadInfo.mCSPInfo.get());
1342   if (NS_WARN_IF(NS_FAILED(rv))) {
1343     return;
1344   }
1345 }
1346 
SetCSPFromHeaderValues(const nsACString & aCSPHeaderValue,const nsACString & aCSPReportOnlyHeaderValue)1347 nsresult WorkerPrivate::SetCSPFromHeaderValues(
1348     const nsACString& aCSPHeaderValue,
1349     const nsACString& aCSPReportOnlyHeaderValue) {
1350   AssertIsOnMainThread();
1351   MOZ_DIAGNOSTIC_ASSERT(!mLoadInfo.mCSP);
1352 
1353   NS_ConvertASCIItoUTF16 cspHeaderValue(aCSPHeaderValue);
1354   NS_ConvertASCIItoUTF16 cspROHeaderValue(aCSPReportOnlyHeaderValue);
1355 
1356   nsresult rv;
1357   nsCOMPtr<nsIContentSecurityPolicy> csp = new nsCSPContext();
1358 
1359   // First, we try to query the URI from the Principal, but
1360   // in case selfURI remains empty (e.g in case the Principal
1361   // is a SystemPrincipal) then we fall back and use the
1362   // base URI as selfURI for CSP.
1363   nsCOMPtr<nsIURI> selfURI;
1364   // Its not recommended to use the BasePrincipal to get the URI
1365   // but in this case we need to make an exception
1366   auto* basePrin = BasePrincipal::Cast(mLoadInfo.mPrincipal);
1367   if (basePrin) {
1368     basePrin->GetURI(getter_AddRefs(selfURI));
1369   }
1370   if (!selfURI) {
1371     selfURI = mLoadInfo.mBaseURI;
1372   }
1373   MOZ_ASSERT(selfURI, "need a self URI for CSP");
1374 
1375   rv = csp->SetRequestContextWithPrincipal(mLoadInfo.mPrincipal, selfURI,
1376                                            u""_ns, 0);
1377   NS_ENSURE_SUCCESS(rv, rv);
1378 
1379   csp->EnsureEventTarget(mMainThreadEventTarget);
1380 
1381   // If there's a CSP header, apply it.
1382   if (!cspHeaderValue.IsEmpty()) {
1383     rv = CSP_AppendCSPFromHeader(csp, cspHeaderValue, false);
1384     NS_ENSURE_SUCCESS(rv, rv);
1385   }
1386   // If there's a report-only CSP header, apply it.
1387   if (!cspROHeaderValue.IsEmpty()) {
1388     rv = CSP_AppendCSPFromHeader(csp, cspROHeaderValue, true);
1389     NS_ENSURE_SUCCESS(rv, rv);
1390   }
1391 
1392   // Set evalAllowed, default value is set in GetAllowsEval
1393   bool evalAllowed = false;
1394   bool reportEvalViolations = false;
1395   rv = csp->GetAllowsEval(&reportEvalViolations, &evalAllowed);
1396   NS_ENSURE_SUCCESS(rv, rv);
1397 
1398   mLoadInfo.mCSP = csp;
1399   mLoadInfo.mEvalAllowed = evalAllowed;
1400   mLoadInfo.mReportCSPViolations = reportEvalViolations;
1401 
1402   mLoadInfo.mCSPInfo = MakeUnique<CSPInfo>();
1403   rv = CSPToCSPInfo(csp, mLoadInfo.mCSPInfo.get());
1404   if (NS_WARN_IF(NS_FAILED(rv))) {
1405     return rv;
1406   }
1407   return NS_OK;
1408 }
1409 
StoreCSPOnClient()1410 void WorkerPrivate::StoreCSPOnClient() {
1411   auto data = mWorkerThreadAccessible.Access();
1412   MOZ_ASSERT(data->mScope);
1413   if (mLoadInfo.mCSPInfo) {
1414     data->mScope->MutableClientSourceRef().SetCspInfo(*mLoadInfo.mCSPInfo);
1415   }
1416 }
1417 
UpdateReferrerInfoFromHeader(const nsACString & aReferrerPolicyHeaderValue)1418 void WorkerPrivate::UpdateReferrerInfoFromHeader(
1419     const nsACString& aReferrerPolicyHeaderValue) {
1420   NS_ConvertUTF8toUTF16 headerValue(aReferrerPolicyHeaderValue);
1421 
1422   if (headerValue.IsEmpty()) {
1423     return;
1424   }
1425 
1426   ReferrerPolicy policy =
1427       ReferrerInfo::ReferrerPolicyFromHeaderString(headerValue);
1428   if (policy == ReferrerPolicy::_empty) {
1429     return;
1430   }
1431 
1432   nsCOMPtr<nsIReferrerInfo> referrerInfo =
1433       static_cast<ReferrerInfo*>(GetReferrerInfo())->CloneWithNewPolicy(policy);
1434   SetReferrerInfo(referrerInfo);
1435 }
1436 
Traverse(nsCycleCollectionTraversalCallback & aCb)1437 void WorkerPrivate::Traverse(nsCycleCollectionTraversalCallback& aCb) {
1438   AssertIsOnParentThread();
1439 
1440   // The WorkerPrivate::mParentEventTargetRef has a reference to the exposed
1441   // Worker object, which is really held by the worker thread.  We traverse this
1442   // reference if and only if our busy count is zero and we have not released
1443   // the main thread reference.  We do not unlink it.  This allows the CC to
1444   // break cycles involving the Worker and begin shutting it down (which does
1445   // happen in unlink) but ensures that the WorkerPrivate won't be deleted
1446   // before we're done shutting down the thread.
1447   if (!mBusyCount && !mMainThreadObjectsForgotten) {
1448     nsCycleCollectionTraversalCallback& cb = aCb;
1449     WorkerPrivate* tmp = this;
1450     NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParentEventTargetRef);
1451   }
1452 }
1453 
Dispatch(already_AddRefed<WorkerRunnable> aRunnable,nsIEventTarget * aSyncLoopTarget)1454 nsresult WorkerPrivate::Dispatch(already_AddRefed<WorkerRunnable> aRunnable,
1455                                  nsIEventTarget* aSyncLoopTarget) {
1456   // May be called on any thread!
1457   MutexAutoLock lock(mMutex);
1458   return DispatchLockHeld(std::move(aRunnable), aSyncLoopTarget, lock);
1459 }
1460 
DispatchLockHeld(already_AddRefed<WorkerRunnable> aRunnable,nsIEventTarget * aSyncLoopTarget,const MutexAutoLock & aProofOfLock)1461 nsresult WorkerPrivate::DispatchLockHeld(
1462     already_AddRefed<WorkerRunnable> aRunnable, nsIEventTarget* aSyncLoopTarget,
1463     const MutexAutoLock& aProofOfLock) {
1464   // May be called on any thread!
1465   RefPtr<WorkerRunnable> runnable(aRunnable);
1466 
1467   MOZ_ASSERT_IF(aSyncLoopTarget, mThread);
1468 
1469   if (mStatus == Dead || (!aSyncLoopTarget && ParentStatus() > Running)) {
1470     NS_WARNING(
1471         "A runnable was posted to a worker that is already shutting "
1472         "down!");
1473     return NS_ERROR_UNEXPECTED;
1474   }
1475 
1476   if (runnable->IsDebuggeeRunnable() && !mDebuggerReady) {
1477     MOZ_RELEASE_ASSERT(!aSyncLoopTarget);
1478     mDelayedDebuggeeRunnables.AppendElement(runnable);
1479     return NS_OK;
1480   }
1481 
1482   if (!mThread) {
1483     if (ParentStatus() == Pending || mStatus == Pending) {
1484       mPreStartRunnables.AppendElement(runnable);
1485       return NS_OK;
1486     }
1487 
1488     NS_WARNING(
1489         "Using a worker event target after the thread has already"
1490         "been released!");
1491     return NS_ERROR_UNEXPECTED;
1492   }
1493 
1494   nsresult rv;
1495   if (aSyncLoopTarget) {
1496     rv = aSyncLoopTarget->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
1497   } else {
1498     // WorkerDebuggeeRunnables don't need any special treatment here. True,
1499     // they should not be delivered to a frozen worker. But frozen workers
1500     // aren't drawing from the thread's main event queue anyway, only from
1501     // mControlQueue.
1502     rv = mThread->DispatchAnyThread(WorkerThreadFriendKey(), runnable.forget());
1503   }
1504 
1505   if (NS_WARN_IF(NS_FAILED(rv))) {
1506     return rv;
1507   }
1508 
1509   mCondVar.Notify();
1510   return NS_OK;
1511 }
1512 
EnableDebugger()1513 void WorkerPrivate::EnableDebugger() {
1514   AssertIsOnParentThread();
1515 
1516   if (NS_FAILED(RegisterWorkerDebugger(this))) {
1517     NS_WARNING("Failed to register worker debugger!");
1518     return;
1519   }
1520 }
1521 
DisableDebugger()1522 void WorkerPrivate::DisableDebugger() {
1523   AssertIsOnParentThread();
1524 
1525   // RegisterDebuggerMainThreadRunnable might be dispatched but not executed.
1526   // Wait for its execution before unregistraion.
1527   if (!NS_IsMainThread()) {
1528     WaitForIsDebuggerRegistered(true);
1529   }
1530 
1531   if (NS_FAILED(UnregisterWorkerDebugger(this))) {
1532     NS_WARNING("Failed to unregister worker debugger!");
1533   }
1534 }
1535 
DispatchControlRunnable(already_AddRefed<WorkerControlRunnable> aWorkerControlRunnable)1536 nsresult WorkerPrivate::DispatchControlRunnable(
1537     already_AddRefed<WorkerControlRunnable> aWorkerControlRunnable) {
1538   // May be called on any thread!
1539   RefPtr<WorkerControlRunnable> runnable(aWorkerControlRunnable);
1540   MOZ_ASSERT(runnable);
1541 
1542   {
1543     MutexAutoLock lock(mMutex);
1544 
1545     if (mStatus == Dead) {
1546       return NS_ERROR_UNEXPECTED;
1547     }
1548 
1549     // Transfer ownership to the control queue.
1550     mControlQueue.Push(runnable.forget().take());
1551 
1552     if (JSContext* cx = mJSContext) {
1553       MOZ_ASSERT(mThread);
1554       JS_RequestInterruptCallback(cx);
1555     }
1556 
1557     mCondVar.Notify();
1558   }
1559 
1560   return NS_OK;
1561 }
1562 
DispatchDebuggerRunnable(already_AddRefed<WorkerRunnable> aDebuggerRunnable)1563 nsresult WorkerPrivate::DispatchDebuggerRunnable(
1564     already_AddRefed<WorkerRunnable> aDebuggerRunnable) {
1565   // May be called on any thread!
1566 
1567   RefPtr<WorkerRunnable> runnable(aDebuggerRunnable);
1568 
1569   MOZ_ASSERT(runnable);
1570 
1571   {
1572     MutexAutoLock lock(mMutex);
1573 
1574     if (mStatus == Dead) {
1575       NS_WARNING(
1576           "A debugger runnable was posted to a worker that is already "
1577           "shutting down!");
1578       return NS_ERROR_UNEXPECTED;
1579     }
1580 
1581     // Transfer ownership to the debugger queue.
1582     mDebuggerQueue.Push(runnable.forget().take());
1583 
1584     mCondVar.Notify();
1585   }
1586 
1587   return NS_OK;
1588 }
1589 
MaybeWrapAsWorkerRunnable(already_AddRefed<nsIRunnable> aRunnable)1590 already_AddRefed<WorkerRunnable> WorkerPrivate::MaybeWrapAsWorkerRunnable(
1591     already_AddRefed<nsIRunnable> aRunnable) {
1592   // May be called on any thread!
1593 
1594   nsCOMPtr<nsIRunnable> runnable(aRunnable);
1595   MOZ_ASSERT(runnable);
1596 
1597   RefPtr<WorkerRunnable> workerRunnable =
1598       WorkerRunnable::FromRunnable(runnable);
1599   if (workerRunnable) {
1600     return workerRunnable.forget();
1601   }
1602 
1603   nsCOMPtr<nsIDiscardableRunnable> maybe = do_QueryInterface(runnable);
1604   if (!maybe) {
1605     MOZ_CRASH(
1606         "All runnables destined for a worker thread must be "
1607         "nsIDiscardableRunnable!");
1608   }
1609 
1610   workerRunnable = new ExternalRunnableWrapper(this, runnable);
1611   return workerRunnable.forget();
1612 }
1613 
Start()1614 bool WorkerPrivate::Start() {
1615   // May be called on any thread!
1616   {
1617     MutexAutoLock lock(mMutex);
1618     NS_ASSERTION(mParentStatus != Running, "How can this be?!");
1619 
1620     if (mParentStatus == Pending) {
1621       mParentStatus = Running;
1622       return true;
1623     }
1624   }
1625 
1626   return false;
1627 }
1628 
1629 // aCx is null when called from the finalizer
Notify(WorkerStatus aStatus)1630 bool WorkerPrivate::Notify(WorkerStatus aStatus) {
1631   AssertIsOnParentThread();
1632 
1633   bool pending;
1634   {
1635     MutexAutoLock lock(mMutex);
1636 
1637     if (mParentStatus >= aStatus) {
1638       return true;
1639     }
1640 
1641     pending = mParentStatus == Pending;
1642     mParentStatus = aStatus;
1643   }
1644 
1645   if (pending) {
1646 #ifdef DEBUG
1647     {
1648       // Fake a thread here just so that our assertions don't go off for no
1649       // reason.
1650       nsIThread* currentThread = NS_GetCurrentThread();
1651       MOZ_ASSERT(currentThread);
1652 
1653       MOZ_ASSERT(!mPRThread);
1654       mPRThread = PRThreadFromThread(currentThread);
1655       MOZ_ASSERT(mPRThread);
1656     }
1657 #endif
1658 
1659     // Worker never got a chance to run, go ahead and delete it.
1660     ScheduleDeletion(WorkerPrivate::WorkerNeverRan);
1661     return true;
1662   }
1663 
1664   // No Canceling timeout is needed.
1665   if (mCancelingTimer) {
1666     mCancelingTimer->Cancel();
1667     mCancelingTimer = nullptr;
1668   }
1669 
1670   RefPtr<NotifyRunnable> runnable = new NotifyRunnable(this, aStatus);
1671   return runnable->Dispatch();
1672 }
1673 
Freeze(const nsPIDOMWindowInner * aWindow)1674 bool WorkerPrivate::Freeze(const nsPIDOMWindowInner* aWindow) {
1675   AssertIsOnParentThread();
1676 
1677   mParentFrozen = true;
1678 
1679   // WorkerDebuggeeRunnables sent from a worker to content must not be delivered
1680   // while the worker is frozen.
1681   //
1682   // Since a top-level worker and all its children share the same
1683   // mMainThreadDebuggeeEventTarget, it's sufficient to do this only in the
1684   // top-level worker.
1685   if (aWindow) {
1686     // This is called from WorkerPrivate construction, and We may not have
1687     // allocated mMainThreadDebuggeeEventTarget yet.
1688     if (mMainThreadDebuggeeEventTarget) {
1689       // Pausing a ThrottledEventQueue is infallible.
1690       MOZ_ALWAYS_SUCCEEDS(mMainThreadDebuggeeEventTarget->SetIsPaused(true));
1691     }
1692   }
1693 
1694   {
1695     MutexAutoLock lock(mMutex);
1696 
1697     if (mParentStatus >= Canceling) {
1698       return true;
1699     }
1700   }
1701 
1702   DisableDebugger();
1703 
1704   RefPtr<FreezeRunnable> runnable = new FreezeRunnable(this);
1705   if (!runnable->Dispatch()) {
1706     return false;
1707   }
1708 
1709   return true;
1710 }
1711 
Thaw(const nsPIDOMWindowInner * aWindow)1712 bool WorkerPrivate::Thaw(const nsPIDOMWindowInner* aWindow) {
1713   AssertIsOnParentThread();
1714   MOZ_ASSERT(mParentFrozen);
1715 
1716   mParentFrozen = false;
1717 
1718   // Delivery of WorkerDebuggeeRunnables to the window may resume.
1719   //
1720   // Since a top-level worker and all its children share the same
1721   // mMainThreadDebuggeeEventTarget, it's sufficient to do this only in the
1722   // top-level worker.
1723   if (aWindow) {
1724     // Since the worker is no longer frozen, only a paused parent window should
1725     // require the queue to remain paused.
1726     //
1727     // This can only fail if the ThrottledEventQueue cannot dispatch its
1728     // executor to the main thread, in which case the main thread was never
1729     // going to draw runnables from it anyway, so the failure doesn't matter.
1730     Unused << mMainThreadDebuggeeEventTarget->SetIsPaused(
1731         IsParentWindowPaused());
1732   }
1733 
1734   {
1735     MutexAutoLock lock(mMutex);
1736 
1737     if (mParentStatus >= Canceling) {
1738       return true;
1739     }
1740   }
1741 
1742   EnableDebugger();
1743 
1744   RefPtr<ThawRunnable> runnable = new ThawRunnable(this);
1745   if (!runnable->Dispatch()) {
1746     return false;
1747   }
1748 
1749   return true;
1750 }
1751 
ParentWindowPaused()1752 void WorkerPrivate::ParentWindowPaused() {
1753   AssertIsOnMainThread();
1754   MOZ_ASSERT(!mParentWindowPaused);
1755   mParentWindowPaused = true;
1756 
1757   // This is called from WorkerPrivate construction, and we may not have
1758   // allocated mMainThreadDebuggeeEventTarget yet.
1759   if (mMainThreadDebuggeeEventTarget) {
1760     // Pausing a ThrottledEventQueue is infallible.
1761     MOZ_ALWAYS_SUCCEEDS(mMainThreadDebuggeeEventTarget->SetIsPaused(true));
1762   }
1763 }
1764 
ParentWindowResumed()1765 void WorkerPrivate::ParentWindowResumed() {
1766   AssertIsOnMainThread();
1767 
1768   MOZ_ASSERT(mParentWindowPaused);
1769   mParentWindowPaused = false;
1770 
1771   {
1772     MutexAutoLock lock(mMutex);
1773 
1774     if (mParentStatus >= Canceling) {
1775       return;
1776     }
1777   }
1778 
1779   // Since the window is no longer paused, the queue should only remain paused
1780   // if the worker is frozen.
1781   //
1782   // This can only fail if the ThrottledEventQueue cannot dispatch its executor
1783   // to the main thread, in which case the main thread was never going to draw
1784   // runnables from it anyway, so the failure doesn't matter.
1785   Unused << mMainThreadDebuggeeEventTarget->SetIsPaused(IsFrozen());
1786 }
1787 
PropagateStorageAccessPermissionGranted()1788 void WorkerPrivate::PropagateStorageAccessPermissionGranted() {
1789   AssertIsOnParentThread();
1790 
1791   {
1792     MutexAutoLock lock(mMutex);
1793 
1794     if (mParentStatus >= Canceling) {
1795       return;
1796     }
1797   }
1798 
1799   RefPtr<PropagateStorageAccessPermissionGrantedRunnable> runnable =
1800       new PropagateStorageAccessPermissionGrantedRunnable(this);
1801   Unused << NS_WARN_IF(!runnable->Dispatch());
1802 }
1803 
Close()1804 bool WorkerPrivate::Close() {
1805   mMutex.AssertCurrentThreadOwns();
1806   if (mParentStatus < Closing) {
1807     mParentStatus = Closing;
1808   }
1809 
1810   return true;
1811 }
1812 
ModifyBusyCount(bool aIncrease)1813 bool WorkerPrivate::ModifyBusyCount(bool aIncrease) {
1814   AssertIsOnParentThread();
1815 
1816   MOZ_ASSERT(aIncrease || mBusyCount, "Mismatched busy count mods!");
1817 
1818   if (aIncrease) {
1819     mBusyCount++;
1820     return true;
1821   }
1822 
1823   if (--mBusyCount == 0) {
1824     bool shouldCancel;
1825     {
1826       MutexAutoLock lock(mMutex);
1827       shouldCancel = mParentStatus == Canceling;
1828     }
1829 
1830     if (shouldCancel && !Cancel()) {
1831       return false;
1832     }
1833   }
1834 
1835   return true;
1836 }
1837 
ProxyReleaseMainThreadObjects()1838 bool WorkerPrivate::ProxyReleaseMainThreadObjects() {
1839   AssertIsOnParentThread();
1840   MOZ_ASSERT(!mMainThreadObjectsForgotten);
1841 
1842   nsCOMPtr<nsILoadGroup> loadGroupToCancel;
1843   // If we're not overriden, then do nothing here.  Let the load group get
1844   // handled in ForgetMainThreadObjects().
1845   if (mLoadInfo.mInterfaceRequestor) {
1846     mLoadInfo.mLoadGroup.swap(loadGroupToCancel);
1847   }
1848 
1849   bool result = mLoadInfo.ProxyReleaseMainThreadObjects(
1850       this, std::move(loadGroupToCancel));
1851 
1852   mMainThreadObjectsForgotten = true;
1853 
1854   return result;
1855 }
1856 
UpdateContextOptions(const JS::ContextOptions & aContextOptions)1857 void WorkerPrivate::UpdateContextOptions(
1858     const JS::ContextOptions& aContextOptions) {
1859   AssertIsOnParentThread();
1860 
1861   {
1862     MutexAutoLock lock(mMutex);
1863     mJSSettings.contextOptions = aContextOptions;
1864   }
1865 
1866   RefPtr<UpdateContextOptionsRunnable> runnable =
1867       new UpdateContextOptionsRunnable(this, aContextOptions);
1868   if (!runnable->Dispatch()) {
1869     NS_WARNING("Failed to update worker context options!");
1870   }
1871 }
1872 
UpdateLanguages(const nsTArray<nsString> & aLanguages)1873 void WorkerPrivate::UpdateLanguages(const nsTArray<nsString>& aLanguages) {
1874   AssertIsOnParentThread();
1875 
1876   RefPtr<UpdateLanguagesRunnable> runnable =
1877       new UpdateLanguagesRunnable(this, aLanguages);
1878   if (!runnable->Dispatch()) {
1879     NS_WARNING("Failed to update worker languages!");
1880   }
1881 }
1882 
UpdateJSWorkerMemoryParameter(JSGCParamKey aKey,Maybe<uint32_t> aValue)1883 void WorkerPrivate::UpdateJSWorkerMemoryParameter(JSGCParamKey aKey,
1884                                                   Maybe<uint32_t> aValue) {
1885   AssertIsOnParentThread();
1886 
1887   bool changed = false;
1888 
1889   {
1890     MutexAutoLock lock(mMutex);
1891     changed = mJSSettings.ApplyGCSetting(aKey, aValue);
1892   }
1893 
1894   if (changed) {
1895     RefPtr<UpdateJSWorkerMemoryParameterRunnable> runnable =
1896         new UpdateJSWorkerMemoryParameterRunnable(this, aKey, aValue);
1897     if (!runnable->Dispatch()) {
1898       NS_WARNING("Failed to update memory parameter!");
1899     }
1900   }
1901 }
1902 
1903 #ifdef JS_GC_ZEAL
UpdateGCZeal(uint8_t aGCZeal,uint32_t aFrequency)1904 void WorkerPrivate::UpdateGCZeal(uint8_t aGCZeal, uint32_t aFrequency) {
1905   AssertIsOnParentThread();
1906 
1907   {
1908     MutexAutoLock lock(mMutex);
1909     mJSSettings.gcZeal = aGCZeal;
1910     mJSSettings.gcZealFrequency = aFrequency;
1911   }
1912 
1913   RefPtr<UpdateGCZealRunnable> runnable =
1914       new UpdateGCZealRunnable(this, aGCZeal, aFrequency);
1915   if (!runnable->Dispatch()) {
1916     NS_WARNING("Failed to update worker gczeal!");
1917   }
1918 }
1919 #endif
1920 
SetLowMemoryState(bool aState)1921 void WorkerPrivate::SetLowMemoryState(bool aState) {
1922   AssertIsOnParentThread();
1923 
1924   RefPtr<SetLowMemoryStateRunnable> runnable =
1925       new SetLowMemoryStateRunnable(this, aState);
1926   if (!runnable->Dispatch()) {
1927     NS_WARNING("Failed to set low memory state!");
1928   }
1929 }
1930 
GarbageCollect(bool aShrinking)1931 void WorkerPrivate::GarbageCollect(bool aShrinking) {
1932   AssertIsOnParentThread();
1933 
1934   RefPtr<GarbageCollectRunnable> runnable = new GarbageCollectRunnable(
1935       this, aShrinking, /* collectChildren = */ true);
1936   if (!runnable->Dispatch()) {
1937     NS_WARNING("Failed to GC worker!");
1938   }
1939 }
1940 
CycleCollect()1941 void WorkerPrivate::CycleCollect() {
1942   AssertIsOnParentThread();
1943 
1944   RefPtr<CycleCollectRunnable> runnable =
1945       new CycleCollectRunnable(this, /* collectChildren = */ true);
1946   if (!runnable->Dispatch()) {
1947     NS_WARNING("Failed to CC worker!");
1948   }
1949 }
1950 
OfflineStatusChangeEvent(bool aIsOffline)1951 void WorkerPrivate::OfflineStatusChangeEvent(bool aIsOffline) {
1952   AssertIsOnParentThread();
1953 
1954   RefPtr<OfflineStatusChangeRunnable> runnable =
1955       new OfflineStatusChangeRunnable(this, aIsOffline);
1956   if (!runnable->Dispatch()) {
1957     NS_WARNING("Failed to dispatch offline status change event!");
1958   }
1959 }
1960 
OfflineStatusChangeEventInternal(bool aIsOffline)1961 void WorkerPrivate::OfflineStatusChangeEventInternal(bool aIsOffline) {
1962   auto data = mWorkerThreadAccessible.Access();
1963 
1964   // The worker is already in this state. No need to dispatch an event.
1965   if (data->mOnLine == !aIsOffline) {
1966     return;
1967   }
1968 
1969   for (uint32_t index = 0; index < data->mChildWorkers.Length(); ++index) {
1970     data->mChildWorkers[index]->OfflineStatusChangeEvent(aIsOffline);
1971   }
1972 
1973   data->mOnLine = !aIsOffline;
1974   WorkerGlobalScope* globalScope = GlobalScope();
1975   RefPtr<WorkerNavigator> nav = globalScope->GetExistingNavigator();
1976   if (nav) {
1977     nav->SetOnLine(data->mOnLine);
1978   }
1979 
1980   nsString eventType;
1981   if (aIsOffline) {
1982     eventType.AssignLiteral("offline");
1983   } else {
1984     eventType.AssignLiteral("online");
1985   }
1986 
1987   RefPtr<Event> event = NS_NewDOMEvent(globalScope, nullptr, nullptr);
1988 
1989   event->InitEvent(eventType, false, false);
1990   event->SetTrusted(true);
1991 
1992   globalScope->DispatchEvent(*event);
1993 }
1994 
MemoryPressure()1995 void WorkerPrivate::MemoryPressure() {
1996   AssertIsOnParentThread();
1997 
1998   RefPtr<MemoryPressureRunnable> runnable = new MemoryPressureRunnable(this);
1999   Unused << NS_WARN_IF(!runnable->Dispatch());
2000 }
2001 
WorkerScriptLoaded()2002 void WorkerPrivate::WorkerScriptLoaded() {
2003   AssertIsOnMainThread();
2004 
2005   if (IsSharedWorker() || IsServiceWorker()) {
2006     // No longer need to hold references to the window or document we came from.
2007     mLoadInfo.mWindow = nullptr;
2008     mLoadInfo.mScriptContext = nullptr;
2009   }
2010 }
2011 
SetBaseURI(nsIURI * aBaseURI)2012 void WorkerPrivate::SetBaseURI(nsIURI* aBaseURI) {
2013   AssertIsOnMainThread();
2014 
2015   if (!mLoadInfo.mBaseURI) {
2016     NS_ASSERTION(GetParent(), "Shouldn't happen without a parent!");
2017     mLoadInfo.mResolvedScriptURI = aBaseURI;
2018   }
2019 
2020   mLoadInfo.mBaseURI = aBaseURI;
2021 
2022   if (NS_FAILED(aBaseURI->GetSpec(mLocationInfo.mHref))) {
2023     mLocationInfo.mHref.Truncate();
2024   }
2025 
2026   mLocationInfo.mHostname.Truncate();
2027   nsContentUtils::GetHostOrIPv6WithBrackets(aBaseURI, mLocationInfo.mHostname);
2028 
2029   nsCOMPtr<nsIURL> url(do_QueryInterface(aBaseURI));
2030   if (!url || NS_FAILED(url->GetFilePath(mLocationInfo.mPathname))) {
2031     mLocationInfo.mPathname.Truncate();
2032   }
2033 
2034   nsCString temp;
2035 
2036   if (url && NS_SUCCEEDED(url->GetQuery(temp)) && !temp.IsEmpty()) {
2037     mLocationInfo.mSearch.Assign('?');
2038     mLocationInfo.mSearch.Append(temp);
2039   }
2040 
2041   if (NS_SUCCEEDED(aBaseURI->GetRef(temp)) && !temp.IsEmpty()) {
2042     if (mLocationInfo.mHash.IsEmpty()) {
2043       mLocationInfo.mHash.Assign('#');
2044       mLocationInfo.mHash.Append(temp);
2045     }
2046   }
2047 
2048   if (NS_SUCCEEDED(aBaseURI->GetScheme(mLocationInfo.mProtocol))) {
2049     mLocationInfo.mProtocol.Append(':');
2050   } else {
2051     mLocationInfo.mProtocol.Truncate();
2052   }
2053 
2054   int32_t port;
2055   if (NS_SUCCEEDED(aBaseURI->GetPort(&port)) && port != -1) {
2056     mLocationInfo.mPort.AppendInt(port);
2057 
2058     nsAutoCString host(mLocationInfo.mHostname);
2059     host.Append(':');
2060     host.Append(mLocationInfo.mPort);
2061 
2062     mLocationInfo.mHost.Assign(host);
2063   } else {
2064     mLocationInfo.mHost.Assign(mLocationInfo.mHostname);
2065   }
2066 
2067   nsContentUtils::GetUTFOrigin(aBaseURI, mLocationInfo.mOrigin);
2068 }
2069 
SetPrincipalsAndCSPOnMainThread(nsIPrincipal * aPrincipal,nsIPrincipal * aPartitionedPrincipal,nsILoadGroup * aLoadGroup,nsIContentSecurityPolicy * aCsp)2070 nsresult WorkerPrivate::SetPrincipalsAndCSPOnMainThread(
2071     nsIPrincipal* aPrincipal, nsIPrincipal* aPartitionedPrincipal,
2072     nsILoadGroup* aLoadGroup, nsIContentSecurityPolicy* aCsp) {
2073   return mLoadInfo.SetPrincipalsAndCSPOnMainThread(
2074       aPrincipal, aPartitionedPrincipal, aLoadGroup, aCsp);
2075 }
2076 
SetPrincipalsAndCSPFromChannel(nsIChannel * aChannel)2077 nsresult WorkerPrivate::SetPrincipalsAndCSPFromChannel(nsIChannel* aChannel) {
2078   return mLoadInfo.SetPrincipalsAndCSPFromChannel(aChannel);
2079 }
2080 
FinalChannelPrincipalIsValid(nsIChannel * aChannel)2081 bool WorkerPrivate::FinalChannelPrincipalIsValid(nsIChannel* aChannel) {
2082   return mLoadInfo.FinalChannelPrincipalIsValid(aChannel);
2083 }
2084 
2085 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
PrincipalURIMatchesScriptURL()2086 bool WorkerPrivate::PrincipalURIMatchesScriptURL() {
2087   return mLoadInfo.PrincipalURIMatchesScriptURL();
2088 }
2089 #endif
2090 
UpdateOverridenLoadGroup(nsILoadGroup * aBaseLoadGroup)2091 void WorkerPrivate::UpdateOverridenLoadGroup(nsILoadGroup* aBaseLoadGroup) {
2092   AssertIsOnMainThread();
2093 
2094   // The load group should have been overriden at init time.
2095   mLoadInfo.mInterfaceRequestor->MaybeAddBrowserChild(aBaseLoadGroup);
2096 }
2097 
2098 #ifdef DEBUG
2099 
AssertIsOnParentThread() const2100 void WorkerPrivate::AssertIsOnParentThread() const {
2101   if (GetParent()) {
2102     GetParent()->AssertIsOnWorkerThread();
2103   } else {
2104     AssertIsOnMainThread();
2105   }
2106 }
2107 
AssertInnerWindowIsCorrect() const2108 void WorkerPrivate::AssertInnerWindowIsCorrect() const {
2109   AssertIsOnParentThread();
2110 
2111   // Only care about top level workers from windows.
2112   if (mParent || !mLoadInfo.mWindow) {
2113     return;
2114   }
2115 
2116   AssertIsOnMainThread();
2117 
2118   nsPIDOMWindowOuter* outer = mLoadInfo.mWindow->GetOuterWindow();
2119   NS_ASSERTION(outer && outer->GetCurrentInnerWindow() == mLoadInfo.mWindow,
2120                "Inner window no longer correct!");
2121 }
2122 
2123 #endif
2124 
2125 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
PrincipalIsValid() const2126 bool WorkerPrivate::PrincipalIsValid() const {
2127   return mLoadInfo.PrincipalIsValid();
2128 }
2129 #endif
2130 
WorkerThreadAccessible(WorkerPrivate * const aParent)2131 WorkerPrivate::WorkerThreadAccessible::WorkerThreadAccessible(
2132     WorkerPrivate* const aParent)
2133     : mNumWorkerRefsPreventingShutdownStart(0),
2134       mDebuggerEventLoopLevel(0),
2135       mErrorHandlerRecursionCount(0),
2136       mNextTimeoutId(1),
2137       mCurrentTimerNestingLevel(0),
2138       mFrozen(false),
2139       mTimerRunning(false),
2140       mRunningExpiredTimeouts(false),
2141       mPeriodicGCTimerRunning(false),
2142       mIdleGCTimerRunning(false),
2143       mOnLine(aParent ? aParent->OnLine() : !NS_IsOffline()),
2144       mJSThreadExecutionGranted(false),
2145       mCCCollectedAnything(false) {}
2146 
2147 namespace {
2148 
IsNewWorkerSecureContext(const WorkerPrivate * const aParent,const WorkerKind aWorkerKind,const WorkerLoadInfo & aLoadInfo)2149 bool IsNewWorkerSecureContext(const WorkerPrivate* const aParent,
2150                               const WorkerKind aWorkerKind,
2151                               const WorkerLoadInfo& aLoadInfo) {
2152   if (aParent) {
2153     return aParent->IsSecureContext();
2154   }
2155 
2156   // Our secure context state depends on the kind of worker we have.
2157 
2158   if (aLoadInfo.mPrincipalIsSystem) {
2159     return true;
2160   }
2161 
2162   if (aWorkerKind == WorkerKindService) {
2163     return true;
2164   }
2165 
2166   if (aLoadInfo.mSecureContext != WorkerLoadInfo::eNotSet) {
2167     return aLoadInfo.mSecureContext == WorkerLoadInfo::eSecureContext;
2168   }
2169 
2170   MOZ_ASSERT_UNREACHABLE(
2171       "non-chrome worker that is not a service worker "
2172       "that has no parent and no associated window");
2173 
2174   return false;
2175 }
2176 
2177 }  // namespace
2178 
WorkerPrivate(WorkerPrivate * aParent,const nsAString & aScriptURL,bool aIsChromeWorker,WorkerKind aWorkerKind,const nsAString & aWorkerName,const nsACString & aServiceWorkerScope,WorkerLoadInfo & aLoadInfo,nsString && aId,const nsID & aAgentClusterId,const nsILoadInfo::CrossOriginOpenerPolicy aAgentClusterOpenerPolicy)2179 WorkerPrivate::WorkerPrivate(
2180     WorkerPrivate* aParent, const nsAString& aScriptURL, bool aIsChromeWorker,
2181     WorkerKind aWorkerKind, const nsAString& aWorkerName,
2182     const nsACString& aServiceWorkerScope, WorkerLoadInfo& aLoadInfo,
2183     nsString&& aId, const nsID& aAgentClusterId,
2184     const nsILoadInfo::CrossOriginOpenerPolicy aAgentClusterOpenerPolicy)
2185     : mMutex("WorkerPrivate Mutex"),
2186       mCondVar(mMutex, "WorkerPrivate CondVar"),
2187       mParent(aParent),
2188       mScriptURL(aScriptURL),
2189       mWorkerName(aWorkerName),
2190       mWorkerKind(aWorkerKind),
2191       mLoadInfo(std::move(aLoadInfo)),
2192       mDebugger(nullptr),
2193       mJSContext(nullptr),
2194       mPRThread(nullptr),
2195       mWorkerControlEventTarget(new WorkerEventTarget(
2196           this, WorkerEventTarget::Behavior::ControlOnly)),
2197       mWorkerHybridEventTarget(
2198           new WorkerEventTarget(this, WorkerEventTarget::Behavior::Hybrid)),
2199       mParentStatus(Pending),
2200       mStatus(Pending),
2201       mBusyCount(0),
2202       mLoadingWorkerScript(false),
2203       mCreationTimeStamp(TimeStamp::Now()),
2204       mCreationTimeHighRes((double)PR_Now() / PR_USEC_PER_MSEC),
2205       mReportedUseCounters(false),
2206       mAgentClusterId(aAgentClusterId),
2207       mWorkerThreadAccessible(aParent),
2208       mPostSyncLoopOperations(0),
2209       mParentWindowPaused(false),
2210       mCancelAllPendingRunnables(false),
2211       mWorkerScriptExecutedSuccessfully(false),
2212       mFetchHandlerWasAdded(false),
2213       mMainThreadObjectsForgotten(false),
2214       mIsChromeWorker(aIsChromeWorker),
2215       mParentFrozen(false),
2216       mIsSecureContext(
2217           IsNewWorkerSecureContext(mParent, mWorkerKind, mLoadInfo)),
2218       mDebuggerRegistered(false),
2219       mDebuggerReady(true),
2220       mExtensionAPIAllowed(false),
2221       mIsInAutomation(false),
2222       mId(std::move(aId)),
2223       mAgentClusterOpenerPolicy(aAgentClusterOpenerPolicy),
2224       mIsPrivilegedAddonGlobal(false) {
2225   MOZ_ASSERT_IF(!IsDedicatedWorker(), NS_IsMainThread());
2226 
2227   if (aParent) {
2228     aParent->AssertIsOnWorkerThread();
2229 
2230     // Note that this copies our parent's secure context state into mJSSettings.
2231     aParent->CopyJSSettings(mJSSettings);
2232 
2233     MOZ_ASSERT_IF(mIsChromeWorker, mIsSecureContext);
2234 
2235     mIsInAutomation = aParent->IsInAutomation();
2236 
2237     MOZ_ASSERT(IsDedicatedWorker());
2238 
2239     if (aParent->mParentFrozen) {
2240       Freeze(nullptr);
2241     }
2242 
2243     mIsPrivilegedAddonGlobal = aParent->mIsPrivilegedAddonGlobal;
2244   } else {
2245     AssertIsOnMainThread();
2246 
2247     RuntimeService::GetDefaultJSSettings(mJSSettings);
2248 
2249     {
2250       JS::RealmOptions& chromeRealmOptions = mJSSettings.chromeRealmOptions;
2251       JS::RealmOptions& contentRealmOptions = mJSSettings.contentRealmOptions;
2252 
2253       JS::RealmBehaviors& chromeRealmBehaviors = chromeRealmOptions.behaviors();
2254       JS::RealmBehaviors& contentRealmBehaviors =
2255           contentRealmOptions.behaviors();
2256 
2257       bool usesSystemPrincipal = UsesSystemPrincipal();
2258 
2259       // Make timing imprecise in unprivileged code to blunt Spectre timing
2260       // attacks.
2261       bool clampAndJitterTime = !usesSystemPrincipal;
2262       chromeRealmBehaviors.setClampAndJitterTime(clampAndJitterTime);
2263       contentRealmBehaviors.setClampAndJitterTime(clampAndJitterTime);
2264 
2265       JS::RealmCreationOptions& chromeCreationOptions =
2266           chromeRealmOptions.creationOptions();
2267       JS::RealmCreationOptions& contentCreationOptions =
2268           contentRealmOptions.creationOptions();
2269 
2270       // Expose uneval and toSource functions only if this is privileged code.
2271       bool toSourceEnabled = usesSystemPrincipal;
2272       chromeCreationOptions.setToSourceEnabled(toSourceEnabled);
2273       contentCreationOptions.setToSourceEnabled(toSourceEnabled);
2274 
2275       if (mIsSecureContext) {
2276         chromeCreationOptions.setSecureContext(true);
2277         contentCreationOptions.setSecureContext(true);
2278       }
2279 
2280       // Check if it's a privileged addon executing in order to allow access
2281       // to SharedArrayBuffer
2282       if (mLoadInfo.mPrincipal) {
2283         if (auto* policy =
2284                 BasePrincipal::Cast(mLoadInfo.mPrincipal)->AddonPolicy()) {
2285           if (policy->IsPrivileged() &&
2286               ExtensionPolicyService::GetSingleton().IsExtensionProcess()) {
2287             // Privileged extensions are allowed to use SharedArrayBuffer in
2288             // their extension process, but never in content scripts in
2289             // content processes.
2290             mIsPrivilegedAddonGlobal = true;
2291           }
2292 
2293           if (StaticPrefs::
2294                   extensions_backgroundServiceWorker_enabled_AtStartup() &&
2295               mWorkerKind == WorkerKindService &&
2296               policy->IsManifestBackgroundWorker(mScriptURL)) {
2297             // Only allows ExtensionAPI for extension service workers
2298             // that are declared in the extension manifest json as
2299             // the background service worker.
2300             mExtensionAPIAllowed = true;
2301           }
2302         }
2303       }
2304 
2305       // The SharedArrayBuffer global constructor property should not be present
2306       // in a fresh global object when shared memory objects aren't allowed
2307       // (because COOP/COEP support isn't enabled, or because COOP/COEP don't
2308       // act to isolate this worker to a separate process).
2309       const bool defineSharedArrayBufferConstructor = IsSharedMemoryAllowed();
2310       chromeCreationOptions.setDefineSharedArrayBufferConstructor(
2311           defineSharedArrayBufferConstructor);
2312       contentCreationOptions.setDefineSharedArrayBufferConstructor(
2313           defineSharedArrayBufferConstructor);
2314     }
2315 
2316     mIsInAutomation = xpc::IsInAutomation();
2317 
2318     // Our parent can get suspended after it initiates the async creation
2319     // of a new worker thread.  In this case suspend the new worker as well.
2320     if (mLoadInfo.mWindow && mLoadInfo.mWindow->IsSuspended()) {
2321       ParentWindowPaused();
2322     }
2323 
2324     if (mLoadInfo.mWindow && mLoadInfo.mWindow->IsFrozen()) {
2325       Freeze(mLoadInfo.mWindow);
2326     }
2327   }
2328 
2329   nsCOMPtr<nsISerialEventTarget> target;
2330 
2331   // A child worker just inherits the parent workers ThrottledEventQueue
2332   // and main thread target for now.  This is mainly due to the restriction
2333   // that ThrottledEventQueue can only be created on the main thread at the
2334   // moment.
2335   if (aParent) {
2336     mMainThreadEventTargetForMessaging =
2337         aParent->mMainThreadEventTargetForMessaging;
2338     mMainThreadEventTarget = aParent->mMainThreadEventTarget;
2339     mMainThreadDebuggeeEventTarget = aParent->mMainThreadDebuggeeEventTarget;
2340     return;
2341   }
2342 
2343   MOZ_ASSERT(NS_IsMainThread());
2344   target = GetWindow()
2345                ? GetWindow()->GetBrowsingContextGroup()->GetWorkerEventQueue()
2346                : nullptr;
2347 
2348   if (!target) {
2349     target = GetMainThreadSerialEventTarget();
2350     MOZ_DIAGNOSTIC_ASSERT(target);
2351   }
2352 
2353   // Throttle events to the main thread using a ThrottledEventQueue specific to
2354   // this tree of worker threads.
2355   mMainThreadEventTargetForMessaging =
2356       ThrottledEventQueue::Create(target, "Worker queue for messaging");
2357   if (StaticPrefs::dom_worker_use_medium_high_event_queue()) {
2358     mMainThreadEventTarget = ThrottledEventQueue::Create(
2359         GetMainThreadSerialEventTarget(), "Worker queue",
2360         nsIRunnablePriority::PRIORITY_MEDIUMHIGH);
2361   } else {
2362     mMainThreadEventTarget = mMainThreadEventTargetForMessaging;
2363   }
2364   mMainThreadDebuggeeEventTarget =
2365       ThrottledEventQueue::Create(target, "Worker debuggee queue");
2366   if (IsParentWindowPaused() || IsFrozen()) {
2367     MOZ_ALWAYS_SUCCEEDS(mMainThreadDebuggeeEventTarget->SetIsPaused(true));
2368   }
2369 }
2370 
~WorkerPrivate()2371 WorkerPrivate::~WorkerPrivate() {
2372   mWorkerControlEventTarget->ForgetWorkerPrivate(this);
2373 
2374   // We force the hybrid event target to forget the thread when we
2375   // enter the Killing state, but we do it again here to be safe.
2376   // Its possible that we may be created and destroyed without progressing
2377   // to Killing via some obscure code path.
2378   mWorkerHybridEventTarget->ForgetWorkerPrivate(this);
2379 }
2380 
2381 // static
Constructor(JSContext * aCx,const nsAString & aScriptURL,bool aIsChromeWorker,WorkerKind aWorkerKind,const nsAString & aWorkerName,const nsACString & aServiceWorkerScope,WorkerLoadInfo * aLoadInfo,ErrorResult & aRv,nsString aId)2382 already_AddRefed<WorkerPrivate> WorkerPrivate::Constructor(
2383     JSContext* aCx, const nsAString& aScriptURL, bool aIsChromeWorker,
2384     WorkerKind aWorkerKind, const nsAString& aWorkerName,
2385     const nsACString& aServiceWorkerScope, WorkerLoadInfo* aLoadInfo,
2386     ErrorResult& aRv, nsString aId) {
2387   WorkerPrivate* parent =
2388       NS_IsMainThread() ? nullptr : GetCurrentThreadWorkerPrivate();
2389 
2390   // If this is a sub-worker, we need to keep the parent worker alive until this
2391   // one is registered.
2392   RefPtr<StrongWorkerRef> workerRef;
2393   if (parent) {
2394     parent->AssertIsOnWorkerThread();
2395 
2396     workerRef = StrongWorkerRef::Create(parent, "WorkerPrivate::Constructor");
2397     if (NS_WARN_IF(!workerRef)) {
2398       aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
2399       return nullptr;
2400     }
2401   } else {
2402     AssertIsOnMainThread();
2403   }
2404 
2405   Maybe<WorkerLoadInfo> stackLoadInfo;
2406   if (!aLoadInfo) {
2407     stackLoadInfo.emplace();
2408 
2409     nsresult rv =
2410         GetLoadInfo(aCx, nullptr, parent, aScriptURL, aIsChromeWorker,
2411                     InheritLoadGroup, aWorkerKind, stackLoadInfo.ptr());
2412     aRv.MightThrowJSException();
2413     if (NS_FAILED(rv)) {
2414       workerinternals::ReportLoadError(aRv, rv, aScriptURL);
2415       return nullptr;
2416     }
2417 
2418     aLoadInfo = stackLoadInfo.ptr();
2419   }
2420 
2421   // NB: This has to be done before creating the WorkerPrivate, because it will
2422   // attempt to use static variables that are initialized in the RuntimeService
2423   // constructor.
2424   RuntimeService* runtimeService;
2425 
2426   if (!parent) {
2427     runtimeService = RuntimeService::GetOrCreateService();
2428     if (!runtimeService) {
2429       aRv.Throw(NS_ERROR_FAILURE);
2430       return nullptr;
2431     }
2432   } else {
2433     runtimeService = RuntimeService::GetService();
2434   }
2435 
2436   MOZ_ASSERT(runtimeService);
2437 
2438   // Don't create a worker with the shutting down RuntimeService.
2439   if (runtimeService->IsShuttingDown()) {
2440     aRv.Throw(NS_ERROR_UNEXPECTED);
2441     return nullptr;
2442   }
2443 
2444   nsILoadInfo::CrossOriginOpenerPolicy agentClusterCoop =
2445       nsILoadInfo::OPENER_POLICY_UNSAFE_NONE;
2446   nsID agentClusterId;
2447   if (parent) {
2448     MOZ_ASSERT(aWorkerKind == WorkerKind::WorkerKindDedicated);
2449 
2450     agentClusterId = parent->AgentClusterId();
2451     agentClusterCoop = parent->mAgentClusterOpenerPolicy;
2452   } else {
2453     AssertIsOnMainThread();
2454 
2455     if (aWorkerKind == WorkerKind::WorkerKindService ||
2456         aWorkerKind == WorkerKind::WorkerKindShared) {
2457       agentClusterId = aLoadInfo->mAgentClusterId;
2458     } else if (aLoadInfo->mWindow) {
2459       Document* doc = aLoadInfo->mWindow->GetExtantDoc();
2460       MOZ_DIAGNOSTIC_ASSERT(doc);
2461       RefPtr<DocGroup> docGroup = doc->GetDocGroup();
2462 
2463       agentClusterId = docGroup ? docGroup->AgentClusterId()
2464                                 : nsContentUtils::GenerateUUID();
2465 
2466       BrowsingContext* bc = aLoadInfo->mWindow->GetBrowsingContext();
2467       MOZ_DIAGNOSTIC_ASSERT(bc);
2468       agentClusterCoop = bc->Top()->GetOpenerPolicy();
2469     } else {
2470       // If the window object was failed to be set into the WorkerLoadInfo, we
2471       // make the worker into another agent cluster group instead of failures.
2472       agentClusterId = nsContentUtils::GenerateUUID();
2473     }
2474   }
2475 
2476   RefPtr<WorkerPrivate> worker =
2477       new WorkerPrivate(parent, aScriptURL, aIsChromeWorker, aWorkerKind,
2478                         aWorkerName, aServiceWorkerScope, *aLoadInfo,
2479                         std::move(aId), agentClusterId, agentClusterCoop);
2480 
2481   // Gecko contexts always have an explicitly-set default locale (set by
2482   // XPJSRuntime::Initialize for the main thread, set by
2483   // WorkerThreadPrimaryRunnable::Run for workers just before running worker
2484   // code), so this is never SpiderMonkey's builtin default locale.
2485   JS::UniqueChars defaultLocale = JS_GetDefaultLocale(aCx);
2486   if (NS_WARN_IF(!defaultLocale)) {
2487     aRv.Throw(NS_ERROR_UNEXPECTED);
2488     return nullptr;
2489   }
2490 
2491   worker->mDefaultLocale = std::move(defaultLocale);
2492 
2493   if (!runtimeService->RegisterWorker(*worker)) {
2494     aRv.Throw(NS_ERROR_UNEXPECTED);
2495     return nullptr;
2496   }
2497 
2498   // From this point on (worker thread has been started) we
2499   // must keep ourself alive. We can now only be cleared by
2500   // ClearSelfAndParentEventTargetRef().
2501   worker->mSelfRef = worker;
2502 
2503   worker->EnableDebugger();
2504 
2505   MOZ_DIAGNOSTIC_ASSERT(worker->PrincipalIsValid());
2506 
2507   UniquePtr<SerializedStackHolder> stack;
2508   if (worker->IsWatchedByDevTools()) {
2509     stack = GetCurrentStackForNetMonitor(aCx);
2510   }
2511 
2512   RefPtr<CompileScriptRunnable> compiler =
2513       new CompileScriptRunnable(worker, std::move(stack), aScriptURL);
2514   if (!compiler->Dispatch()) {
2515     aRv.Throw(NS_ERROR_UNEXPECTED);
2516     return nullptr;
2517   }
2518 
2519   return worker.forget();
2520 }
2521 
SetIsDebuggerReady(bool aReady)2522 nsresult WorkerPrivate::SetIsDebuggerReady(bool aReady) {
2523   AssertIsOnMainThread();
2524   MutexAutoLock lock(mMutex);
2525 
2526   if (mDebuggerReady == aReady) {
2527     return NS_OK;
2528   }
2529 
2530   if (!aReady && mDebuggerRegistered) {
2531     // The debugger can only be marked as not ready during registration.
2532     return NS_ERROR_FAILURE;
2533   }
2534 
2535   mDebuggerReady = aReady;
2536 
2537   if (aReady && mDebuggerRegistered) {
2538     // Dispatch all the delayed runnables without releasing the lock, to ensure
2539     // that the order in which debuggee runnables execute is the same as the
2540     // order in which they were originally dispatched.
2541     auto pending = std::move(mDelayedDebuggeeRunnables);
2542     for (uint32_t i = 0; i < pending.Length(); i++) {
2543       RefPtr<WorkerRunnable> runnable = std::move(pending[i]);
2544       nsresult rv = DispatchLockHeld(runnable.forget(), nullptr, lock);
2545       NS_ENSURE_SUCCESS(rv, rv);
2546     }
2547     MOZ_RELEASE_ASSERT(mDelayedDebuggeeRunnables.IsEmpty());
2548   }
2549 
2550   return NS_OK;
2551 }
2552 
2553 // static
GetLoadInfo(JSContext * aCx,nsPIDOMWindowInner * aWindow,WorkerPrivate * aParent,const nsAString & aScriptURL,bool aIsChromeWorker,LoadGroupBehavior aLoadGroupBehavior,WorkerKind aWorkerKind,WorkerLoadInfo * aLoadInfo)2554 nsresult WorkerPrivate::GetLoadInfo(JSContext* aCx, nsPIDOMWindowInner* aWindow,
2555                                     WorkerPrivate* aParent,
2556                                     const nsAString& aScriptURL,
2557                                     bool aIsChromeWorker,
2558                                     LoadGroupBehavior aLoadGroupBehavior,
2559                                     WorkerKind aWorkerKind,
2560                                     WorkerLoadInfo* aLoadInfo) {
2561   using namespace mozilla::dom::workerinternals;
2562 
2563   MOZ_ASSERT(aCx);
2564   MOZ_ASSERT_IF(NS_IsMainThread(),
2565                 aCx == nsContentUtils::GetCurrentJSContext());
2566 
2567   if (aWindow) {
2568     AssertIsOnMainThread();
2569   }
2570 
2571   WorkerLoadInfo loadInfo;
2572   nsresult rv;
2573 
2574   if (aParent) {
2575     aParent->AssertIsOnWorkerThread();
2576 
2577     // If the parent is going away give up now.
2578     WorkerStatus parentStatus;
2579     {
2580       MutexAutoLock lock(aParent->mMutex);
2581       parentStatus = aParent->mStatus;
2582     }
2583 
2584     if (parentStatus > Running) {
2585       return NS_ERROR_FAILURE;
2586     }
2587 
2588     // Passing a pointer to our stack loadInfo is safe here because this
2589     // method uses a sync runnable to get the channel from the main thread.
2590     rv = ChannelFromScriptURLWorkerThread(aCx, aParent, aScriptURL, loadInfo);
2591     if (NS_FAILED(rv)) {
2592       MOZ_ALWAYS_TRUE(loadInfo.ProxyReleaseMainThreadObjects(aParent));
2593       return rv;
2594     }
2595 
2596     // Now that we've spun the loop there's no guarantee that our parent is
2597     // still alive.  We may have received control messages initiating shutdown.
2598     {
2599       MutexAutoLock lock(aParent->mMutex);
2600       parentStatus = aParent->mStatus;
2601     }
2602 
2603     if (parentStatus > Running) {
2604       MOZ_ALWAYS_TRUE(loadInfo.ProxyReleaseMainThreadObjects(aParent));
2605       return NS_ERROR_FAILURE;
2606     }
2607 
2608     loadInfo.mDomain = aParent->Domain();
2609     loadInfo.mFromWindow = aParent->IsFromWindow();
2610     loadInfo.mWindowID = aParent->WindowID();
2611     loadInfo.mStorageAccess = aParent->StorageAccess();
2612     loadInfo.mUseRegularPrincipal = aParent->UseRegularPrincipal();
2613     loadInfo.mHasStorageAccessPermissionGranted =
2614         aParent->HasStorageAccessPermissionGranted();
2615     loadInfo.mCookieJarSettings = aParent->CookieJarSettings();
2616     loadInfo.mOriginAttributes = aParent->GetOriginAttributes();
2617     loadInfo.mServiceWorkersTestingInWindow =
2618         aParent->ServiceWorkersTestingInWindow();
2619     loadInfo.mParentController = aParent->GlobalScope()->GetController();
2620     loadInfo.mWatchedByDevTools = aParent->IsWatchedByDevTools();
2621   } else {
2622     AssertIsOnMainThread();
2623 
2624     // Make sure that the IndexedDatabaseManager is set up
2625     Unused << NS_WARN_IF(!IndexedDatabaseManager::GetOrCreate());
2626 
2627     nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
2628     MOZ_ASSERT(ssm);
2629 
2630     bool isChrome = nsContentUtils::IsSystemCaller(aCx);
2631 
2632     // First check to make sure the caller has permission to make a privileged
2633     // worker if they called the ChromeWorker/ChromeSharedWorker constructor.
2634     if (aIsChromeWorker && !isChrome) {
2635       return NS_ERROR_DOM_SECURITY_ERR;
2636     }
2637 
2638     // Chrome callers (whether creating a ChromeWorker or Worker) always get the
2639     // system principal here as they're allowed to load anything. The script
2640     // loader will refuse to run any script that does not also have the system
2641     // principal.
2642     if (isChrome) {
2643       rv = ssm->GetSystemPrincipal(getter_AddRefs(loadInfo.mLoadingPrincipal));
2644       NS_ENSURE_SUCCESS(rv, rv);
2645 
2646       loadInfo.mPrincipalIsSystem = true;
2647     }
2648 
2649     // See if we're being called from a window.
2650     nsCOMPtr<nsPIDOMWindowInner> globalWindow = aWindow;
2651     if (!globalWindow) {
2652       globalWindow = xpc::CurrentWindowOrNull(aCx);
2653     }
2654 
2655     nsCOMPtr<Document> document;
2656     Maybe<ClientInfo> clientInfo;
2657 
2658     if (globalWindow) {
2659       // Only use the current inner window, and only use it if the caller can
2660       // access it.
2661       if (nsPIDOMWindowOuter* outerWindow = globalWindow->GetOuterWindow()) {
2662         loadInfo.mWindow = outerWindow->GetCurrentInnerWindow();
2663       }
2664 
2665       BrowsingContext* browsingContext = globalWindow->GetBrowsingContext();
2666 
2667       // TODO: fix this for SharedWorkers with multiple documents (bug
2668       // 1177935)
2669       loadInfo.mServiceWorkersTestingInWindow =
2670           browsingContext &&
2671           browsingContext->Top()->ServiceWorkersTestingEnabled();
2672 
2673       if (!loadInfo.mWindow ||
2674           (globalWindow != loadInfo.mWindow &&
2675            !nsContentUtils::CanCallerAccess(loadInfo.mWindow))) {
2676         return NS_ERROR_DOM_SECURITY_ERR;
2677       }
2678 
2679       nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(loadInfo.mWindow);
2680       MOZ_ASSERT(sgo);
2681 
2682       loadInfo.mScriptContext = sgo->GetContext();
2683       NS_ENSURE_TRUE(loadInfo.mScriptContext, NS_ERROR_FAILURE);
2684 
2685       // If we're called from a window then we can dig out the principal and URI
2686       // from the document.
2687       document = loadInfo.mWindow->GetExtantDoc();
2688       NS_ENSURE_TRUE(document, NS_ERROR_FAILURE);
2689 
2690       loadInfo.mBaseURI = document->GetDocBaseURI();
2691       loadInfo.mLoadGroup = document->GetDocumentLoadGroup();
2692       NS_ENSURE_TRUE(loadInfo.mLoadGroup, NS_ERROR_FAILURE);
2693 
2694       clientInfo = globalWindow->GetClientInfo();
2695 
2696       // Use the document's NodePrincipal as loading principal if we're not
2697       // being called from chrome.
2698       if (!loadInfo.mLoadingPrincipal) {
2699         loadInfo.mLoadingPrincipal = document->NodePrincipal();
2700         NS_ENSURE_TRUE(loadInfo.mLoadingPrincipal, NS_ERROR_FAILURE);
2701 
2702         // We use the document's base domain to limit the number of workers
2703         // each domain can create. For sandboxed documents, we use the domain
2704         // of their first non-sandboxed document, walking up until we find
2705         // one. If we can't find one, we fall back to using the GUID of the
2706         // null principal as the base domain.
2707         if (document->GetSandboxFlags() & SANDBOXED_ORIGIN) {
2708           nsCOMPtr<Document> tmpDoc = document;
2709           do {
2710             tmpDoc = tmpDoc->GetInProcessParentDocument();
2711           } while (tmpDoc && tmpDoc->GetSandboxFlags() & SANDBOXED_ORIGIN);
2712 
2713           if (tmpDoc) {
2714             // There was an unsandboxed ancestor, yay!
2715             nsCOMPtr<nsIPrincipal> tmpPrincipal = tmpDoc->NodePrincipal();
2716             rv = tmpPrincipal->GetBaseDomain(loadInfo.mDomain);
2717             NS_ENSURE_SUCCESS(rv, rv);
2718           } else {
2719             // No unsandboxed ancestor, use our GUID.
2720             rv = loadInfo.mLoadingPrincipal->GetBaseDomain(loadInfo.mDomain);
2721             NS_ENSURE_SUCCESS(rv, rv);
2722           }
2723         } else {
2724           // Document creating the worker is not sandboxed.
2725           rv = loadInfo.mLoadingPrincipal->GetBaseDomain(loadInfo.mDomain);
2726           NS_ENSURE_SUCCESS(rv, rv);
2727         }
2728       }
2729 
2730       NS_ENSURE_TRUE(NS_LoadGroupMatchesPrincipal(loadInfo.mLoadGroup,
2731                                                   loadInfo.mLoadingPrincipal),
2732                      NS_ERROR_FAILURE);
2733 
2734       nsCOMPtr<nsIPermissionManager> permMgr =
2735           do_GetService(NS_PERMISSIONMANAGER_CONTRACTID, &rv);
2736       NS_ENSURE_SUCCESS(rv, rv);
2737 
2738       uint32_t perm;
2739       rv = permMgr->TestPermissionFromPrincipal(loadInfo.mLoadingPrincipal,
2740                                                 "systemXHR"_ns, &perm);
2741       NS_ENSURE_SUCCESS(rv, rv);
2742 
2743       loadInfo.mXHRParamsAllowed = perm == nsIPermissionManager::ALLOW_ACTION;
2744 
2745       loadInfo.mWatchedByDevTools =
2746           browsingContext && browsingContext->WatchedByDevTools();
2747 
2748       loadInfo.mReferrerInfo =
2749           ReferrerInfo::CreateForFetch(loadInfo.mLoadingPrincipal, document);
2750       loadInfo.mFromWindow = true;
2751       loadInfo.mWindowID = globalWindow->WindowID();
2752       loadInfo.mStorageAccess = StorageAllowedForWindow(globalWindow);
2753       loadInfo.mUseRegularPrincipal = document->UseRegularPrincipal();
2754       loadInfo.mHasStorageAccessPermissionGranted =
2755           document->HasStorageAccessPermissionGranted();
2756 
2757       // This is an hack to deny the storage-access-permission for workers of
2758       // sub-iframes.
2759       if (loadInfo.mHasStorageAccessPermissionGranted &&
2760           StorageAllowedForDocument(document) != StorageAccess::eAllow) {
2761         loadInfo.mHasStorageAccessPermissionGranted = false;
2762       }
2763       loadInfo.mCookieJarSettings = document->CookieJarSettings();
2764       StoragePrincipalHelper::GetRegularPrincipalOriginAttributes(
2765           document, loadInfo.mOriginAttributes);
2766       loadInfo.mParentController = globalWindow->GetController();
2767       loadInfo.mSecureContext = loadInfo.mWindow->IsSecureContext()
2768                                     ? WorkerLoadInfo::eSecureContext
2769                                     : WorkerLoadInfo::eInsecureContext;
2770     } else {
2771       // Not a window
2772       MOZ_ASSERT(isChrome);
2773 
2774       // We're being created outside of a window. Need to figure out the script
2775       // that is creating us in order for us to use relative URIs later on.
2776       JS::AutoFilename fileName;
2777       if (JS::DescribeScriptedCaller(aCx, &fileName)) {
2778         // In most cases, fileName is URI. In a few other cases
2779         // (e.g. xpcshell), fileName is a file path. Ideally, we would
2780         // prefer testing whether fileName parses as an URI and fallback
2781         // to file path in case of error, but Windows file paths have
2782         // the interesting property that they can be parsed as bogus
2783         // URIs (e.g. C:/Windows/Tmp is interpreted as scheme "C",
2784         // hostname "Windows", path "Tmp"), which defeats this algorithm.
2785         // Therefore, we adopt the opposite convention.
2786         nsCOMPtr<nsIFile> scriptFile =
2787             do_CreateInstance("@mozilla.org/file/local;1", &rv);
2788         if (NS_FAILED(rv)) {
2789           return rv;
2790         }
2791 
2792         rv = scriptFile->InitWithPath(NS_ConvertUTF8toUTF16(fileName.get()));
2793         if (NS_SUCCEEDED(rv)) {
2794           rv = NS_NewFileURI(getter_AddRefs(loadInfo.mBaseURI), scriptFile);
2795         }
2796         if (NS_FAILED(rv)) {
2797           // As expected, fileName is not a path, so proceed with
2798           // a uri.
2799           rv = NS_NewURI(getter_AddRefs(loadInfo.mBaseURI), fileName.get());
2800         }
2801         if (NS_FAILED(rv)) {
2802           return rv;
2803         }
2804       }
2805       loadInfo.mXHRParamsAllowed = true;
2806       loadInfo.mFromWindow = false;
2807       loadInfo.mWindowID = UINT64_MAX;
2808       loadInfo.mStorageAccess = StorageAccess::eAllow;
2809       loadInfo.mUseRegularPrincipal = true;
2810       loadInfo.mHasStorageAccessPermissionGranted = false;
2811       loadInfo.mCookieJarSettings =
2812           mozilla::net::CookieJarSettings::Create(loadInfo.mLoadingPrincipal);
2813       MOZ_ASSERT(loadInfo.mCookieJarSettings);
2814 
2815       loadInfo.mOriginAttributes = OriginAttributes();
2816     }
2817 
2818     MOZ_ASSERT(loadInfo.mLoadingPrincipal);
2819     MOZ_ASSERT(isChrome || !loadInfo.mDomain.IsEmpty());
2820 
2821     if (!loadInfo.mLoadGroup || aLoadGroupBehavior == OverrideLoadGroup) {
2822       OverrideLoadInfoLoadGroup(loadInfo, loadInfo.mLoadingPrincipal);
2823     }
2824     MOZ_ASSERT(NS_LoadGroupMatchesPrincipal(loadInfo.mLoadGroup,
2825                                             loadInfo.mLoadingPrincipal));
2826 
2827     // Top level workers' main script use the document charset for the script
2828     // uri encoding.
2829     nsCOMPtr<nsIURI> url;
2830     rv = nsContentUtils::NewURIWithDocumentCharset(
2831         getter_AddRefs(url), aScriptURL, document, loadInfo.mBaseURI);
2832     NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SYNTAX_ERR);
2833 
2834     rv = ChannelFromScriptURLMainThread(
2835         loadInfo.mLoadingPrincipal, document, loadInfo.mLoadGroup, url,
2836         clientInfo, ContentPolicyType(aWorkerKind), loadInfo.mCookieJarSettings,
2837         loadInfo.mReferrerInfo, getter_AddRefs(loadInfo.mChannel));
2838     NS_ENSURE_SUCCESS(rv, rv);
2839 
2840     rv = NS_GetFinalChannelURI(loadInfo.mChannel,
2841                                getter_AddRefs(loadInfo.mResolvedScriptURI));
2842     NS_ENSURE_SUCCESS(rv, rv);
2843 
2844     // We need the correct hasStoragePermission flag for the channel here since
2845     // we will do a content blocking check later when we set the storage
2846     // principal for the worker. The channel here won't be opened when we do the
2847     // check later, so the hasStoragePermission flag is incorrect. To address
2848     // this, We copy the hasStoragePermission flag from the document if there is
2849     // a window. The worker is created as the same origin of the window. So, the
2850     // worker is supposed to have the same storage permission as the window as
2851     // well as the hasStoragePermission flag.
2852     nsCOMPtr<nsILoadInfo> channelLoadInfo = loadInfo.mChannel->LoadInfo();
2853     rv = channelLoadInfo->SetHasStoragePermission(
2854         loadInfo.mHasStorageAccessPermissionGranted);
2855     NS_ENSURE_SUCCESS(rv, rv);
2856 
2857     rv = loadInfo.SetPrincipalsAndCSPFromChannel(loadInfo.mChannel);
2858     NS_ENSURE_SUCCESS(rv, rv);
2859   }
2860 
2861   MOZ_DIAGNOSTIC_ASSERT(loadInfo.mLoadingPrincipal);
2862   MOZ_DIAGNOSTIC_ASSERT(loadInfo.PrincipalIsValid());
2863 
2864   *aLoadInfo = std::move(loadInfo);
2865   return NS_OK;
2866 }
2867 
2868 // static
OverrideLoadInfoLoadGroup(WorkerLoadInfo & aLoadInfo,nsIPrincipal * aPrincipal)2869 void WorkerPrivate::OverrideLoadInfoLoadGroup(WorkerLoadInfo& aLoadInfo,
2870                                               nsIPrincipal* aPrincipal) {
2871   MOZ_ASSERT(!aLoadInfo.mInterfaceRequestor);
2872   MOZ_ASSERT(aLoadInfo.mLoadingPrincipal == aPrincipal);
2873 
2874   aLoadInfo.mInterfaceRequestor =
2875       new WorkerLoadInfo::InterfaceRequestor(aPrincipal, aLoadInfo.mLoadGroup);
2876   aLoadInfo.mInterfaceRequestor->MaybeAddBrowserChild(aLoadInfo.mLoadGroup);
2877 
2878   // NOTE: this defaults the load context to:
2879   //  - private browsing = false
2880   //  - content = true
2881   //  - use remote tabs = false
2882   nsCOMPtr<nsILoadGroup> loadGroup = do_CreateInstance(NS_LOADGROUP_CONTRACTID);
2883 
2884   nsresult rv =
2885       loadGroup->SetNotificationCallbacks(aLoadInfo.mInterfaceRequestor);
2886   MOZ_ALWAYS_SUCCEEDS(rv);
2887 
2888   aLoadInfo.mLoadGroup = std::move(loadGroup);
2889 
2890   MOZ_ASSERT(NS_LoadGroupMatchesPrincipal(aLoadInfo.mLoadGroup, aPrincipal));
2891 }
2892 
RunLoopNeverRan()2893 void WorkerPrivate::RunLoopNeverRan() {
2894   {
2895     MutexAutoLock lock(mMutex);
2896 
2897     mStatus = Dead;
2898   }
2899 
2900   // After mStatus is set to Dead there can be no more
2901   // WorkerControlRunnables so no need to lock here.
2902   if (!mControlQueue.IsEmpty()) {
2903     WorkerControlRunnable* runnable = nullptr;
2904     while (mControlQueue.Pop(runnable)) {
2905       runnable->Cancel();
2906       runnable->Release();
2907     }
2908   }
2909 
2910   ScheduleDeletion(WorkerPrivate::WorkerRan);
2911 }
2912 
DoRunLoop(JSContext * aCx)2913 void WorkerPrivate::DoRunLoop(JSContext* aCx) {
2914   auto data = mWorkerThreadAccessible.Access();
2915   MOZ_ASSERT(mThread);
2916 
2917   MOZ_RELEASE_ASSERT(!GetExecutionManager());
2918 
2919   {
2920     MutexAutoLock lock(mMutex);
2921     mJSContext = aCx;
2922 
2923     MOZ_ASSERT(mStatus == Pending);
2924     mStatus = Running;
2925   }
2926 
2927   // Now that we've done that, we can go ahead and set up our AutoJSAPI.  We
2928   // can't before this point, because it can't find the right JSContext before
2929   // then, since it gets it from our mJSContext.
2930   AutoJSAPI jsapi;
2931   jsapi.Init();
2932   MOZ_ASSERT(jsapi.cx() == aCx);
2933 
2934   EnableMemoryReporter();
2935 
2936   InitializeGCTimers();
2937 
2938   for (;;) {
2939     WorkerStatus currentStatus;
2940     bool debuggerRunnablesPending = false;
2941     bool normalRunnablesPending = false;
2942 
2943     {
2944       MutexAutoLock lock(mMutex);
2945 
2946       // Wait for a runnable to arrive that we can execute, or for it to be okay
2947       // to shutdown this worker once all holders have been removed.
2948       // Holders may be removed from inside normal runnables, but we don't check
2949       // for that after processing normal runnables, so we need to let control
2950       // flow to the shutdown logic without blocking.
2951       while (mControlQueue.IsEmpty() &&
2952              !(debuggerRunnablesPending = !mDebuggerQueue.IsEmpty()) &&
2953              !(normalRunnablesPending = NS_HasPendingEvents(mThread)) &&
2954              !(mStatus != Running && !HasActiveWorkerRefs())) {
2955         // We pop out to this loop when there are no pending events.
2956         // If we don't reset these, we may not re-enter ProcessNextEvent()
2957         // until we have events to process, and it may seem like we have
2958         // an event running for a very long time.
2959         mThread->SetRunningEventDelay(TimeDuration(), TimeStamp());
2960 
2961         WaitForWorkerEvents();
2962       }
2963 
2964       auto result = ProcessAllControlRunnablesLocked();
2965       if (result != ProcessAllControlRunnablesResult::Nothing) {
2966         // NB: There's no JS on the stack here, so Abort vs MayContinue is
2967         // irrelevant
2968 
2969         // The state of the world may have changed, recheck it.
2970         normalRunnablesPending = NS_HasPendingEvents(mThread);
2971         // The debugger queue doesn't get cleared, so we can ignore that.
2972       }
2973 
2974       currentStatus = mStatus;
2975     }
2976 
2977     // if all holders are done then we can kill this thread.
2978     if (currentStatus != Running && !HasActiveWorkerRefs()) {
2979       // Now we are ready to kill the worker thread.
2980       if (currentStatus == Canceling) {
2981         NotifyInternal(Killing);
2982 
2983 #ifdef DEBUG
2984         {
2985           MutexAutoLock lock(mMutex);
2986           currentStatus = mStatus;
2987         }
2988         MOZ_ASSERT(currentStatus == Killing);
2989 #else
2990         currentStatus = Killing;
2991 #endif
2992       }
2993 
2994       // If we're supposed to die then we should exit the loop.
2995       if (currentStatus == Killing) {
2996         // We are about to destroy worker, report all use counters.
2997         ReportUseCounters();
2998 
2999         // Flush uncaught rejections immediately, without
3000         // waiting for a next tick.
3001         PromiseDebugging::FlushUncaughtRejections();
3002 
3003         ShutdownGCTimers();
3004 
3005         DisableMemoryReporter();
3006 
3007         {
3008           MutexAutoLock lock(mMutex);
3009 
3010           mStatus = Dead;
3011           mJSContext = nullptr;
3012         }
3013 
3014         // After mStatus is set to Dead there can be no more
3015         // WorkerControlRunnables so no need to lock here.
3016         if (!mControlQueue.IsEmpty()) {
3017           WorkerControlRunnable* runnable = nullptr;
3018           while (mControlQueue.Pop(runnable)) {
3019             runnable->Cancel();
3020             runnable->Release();
3021           }
3022         }
3023 
3024         // Unroot the globals
3025         data->mScope = nullptr;
3026         data->mDebuggerScope = nullptr;
3027 
3028         return;
3029       }
3030     }
3031 
3032     if (debuggerRunnablesPending || normalRunnablesPending) {
3033       // Start the periodic GC timer if it is not already running.
3034       SetGCTimerMode(PeriodicTimer);
3035     }
3036 
3037     if (debuggerRunnablesPending) {
3038       WorkerRunnable* runnable = nullptr;
3039 
3040       {
3041         MutexAutoLock lock(mMutex);
3042 
3043         mDebuggerQueue.Pop(runnable);
3044         debuggerRunnablesPending = !mDebuggerQueue.IsEmpty();
3045       }
3046 
3047       MOZ_ASSERT(runnable);
3048       static_cast<nsIRunnable*>(runnable)->Run();
3049       runnable->Release();
3050 
3051       CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get();
3052       ccjs->PerformDebuggerMicroTaskCheckpoint();
3053 
3054       if (debuggerRunnablesPending) {
3055         WorkerDebuggerGlobalScope* globalScope = DebuggerGlobalScope();
3056         MOZ_ASSERT(globalScope);
3057 
3058         // Now *might* be a good time to GC. Let the JS engine make the
3059         // decision.
3060         JSAutoRealm ar(aCx, globalScope->GetGlobalJSObject());
3061         JS_MaybeGC(aCx);
3062       }
3063     } else if (normalRunnablesPending) {
3064       // Process a single runnable from the main queue.
3065       NS_ProcessNextEvent(mThread, false);
3066 
3067       normalRunnablesPending = NS_HasPendingEvents(mThread);
3068       if (normalRunnablesPending && GlobalScope()) {
3069         // Now *might* be a good time to GC. Let the JS engine make the
3070         // decision.
3071         JSAutoRealm ar(aCx, GlobalScope()->GetGlobalJSObject());
3072         JS_MaybeGC(aCx);
3073       }
3074     }
3075 
3076     if (!debuggerRunnablesPending && !normalRunnablesPending) {
3077       // Both the debugger event queue and the normal event queue has been
3078       // exhausted, cancel the periodic GC timer and schedule the idle GC timer.
3079       SetGCTimerMode(IdleTimer);
3080     }
3081 
3082     // If the worker thread is spamming the main thread faster than it can
3083     // process the work, then pause the worker thread until the main thread
3084     // catches up.
3085     size_t queuedEvents = mMainThreadEventTargetForMessaging->Length() +
3086                           mMainThreadDebuggeeEventTarget->Length();
3087     if (queuedEvents > 5000) {
3088       // Note, postMessage uses mMainThreadDebuggeeEventTarget!
3089       mMainThreadDebuggeeEventTarget->AwaitIdle();
3090     }
3091   }
3092 
3093   MOZ_CRASH("Shouldn't get here!");
3094 }
3095 
3096 namespace {
3097 /**
3098  * If there is a current CycleCollectedJSContext, return its recursion depth,
3099  * otherwise return 1.
3100  *
3101  * In the edge case where a worker is starting up so late that PBackground is
3102  * already shutting down, the cycle collected context will never be created,
3103  * but we will need to drain the event loop in ClearMainEventQueue.  This will
3104  * result in a normal NS_ProcessPendingEvents invocation which will call
3105  * WorkerPrivate::OnProcessNextEvent and WorkerPrivate::AfterProcessNextEvent
3106  * which want to handle the need to process control runnables and perform a
3107  * sanity check assertion, respectively.
3108  *
3109  * We claim a depth of 1 when there's no CCJS because this most corresponds to
3110  * reality, but this doesn't meant that other code might want to drain various
3111  * runnable queues as part of this cleanup.
3112  */
GetEffectiveEventLoopRecursionDepth()3113 uint32_t GetEffectiveEventLoopRecursionDepth() {
3114   auto ccjs = CycleCollectedJSContext::Get();
3115   if (ccjs) {
3116     return ccjs->RecursionDepth();
3117   }
3118 
3119   return 1;
3120 }
3121 
3122 }  // namespace
3123 
OnProcessNextEvent()3124 void WorkerPrivate::OnProcessNextEvent() {
3125   AssertIsOnWorkerThread();
3126 
3127   uint32_t recursionDepth = GetEffectiveEventLoopRecursionDepth();
3128   MOZ_ASSERT(recursionDepth);
3129 
3130   // Normally we process control runnables in DoRunLoop or RunCurrentSyncLoop.
3131   // However, it's possible that non-worker C++ could spin its own nested event
3132   // loop, and in that case we must ensure that we continue to process control
3133   // runnables here.
3134   if (recursionDepth > 1 && mSyncLoopStack.Length() < recursionDepth - 1) {
3135     Unused << ProcessAllControlRunnables();
3136     // There's no running JS, and no state to revalidate, so we can ignore the
3137     // return value.
3138   }
3139 }
3140 
AfterProcessNextEvent()3141 void WorkerPrivate::AfterProcessNextEvent() {
3142   AssertIsOnWorkerThread();
3143   MOZ_ASSERT(GetEffectiveEventLoopRecursionDepth());
3144 }
3145 
MainThreadEventTargetForMessaging()3146 nsIEventTarget* WorkerPrivate::MainThreadEventTargetForMessaging() {
3147   return mMainThreadEventTargetForMessaging;
3148 }
3149 
DispatchToMainThreadForMessaging(nsIRunnable * aRunnable,uint32_t aFlags)3150 nsresult WorkerPrivate::DispatchToMainThreadForMessaging(nsIRunnable* aRunnable,
3151                                                          uint32_t aFlags) {
3152   nsCOMPtr<nsIRunnable> r = aRunnable;
3153   return DispatchToMainThreadForMessaging(r.forget(), aFlags);
3154 }
3155 
DispatchToMainThreadForMessaging(already_AddRefed<nsIRunnable> aRunnable,uint32_t aFlags)3156 nsresult WorkerPrivate::DispatchToMainThreadForMessaging(
3157     already_AddRefed<nsIRunnable> aRunnable, uint32_t aFlags) {
3158   return mMainThreadEventTargetForMessaging->Dispatch(std::move(aRunnable),
3159                                                       aFlags);
3160 }
3161 
MainThreadEventTarget()3162 nsIEventTarget* WorkerPrivate::MainThreadEventTarget() {
3163   return mMainThreadEventTarget;
3164 }
3165 
DispatchToMainThread(nsIRunnable * aRunnable,uint32_t aFlags)3166 nsresult WorkerPrivate::DispatchToMainThread(nsIRunnable* aRunnable,
3167                                              uint32_t aFlags) {
3168   nsCOMPtr<nsIRunnable> r = aRunnable;
3169   return DispatchToMainThread(r.forget(), aFlags);
3170 }
3171 
DispatchToMainThread(already_AddRefed<nsIRunnable> aRunnable,uint32_t aFlags)3172 nsresult WorkerPrivate::DispatchToMainThread(
3173     already_AddRefed<nsIRunnable> aRunnable, uint32_t aFlags) {
3174   return mMainThreadEventTarget->Dispatch(std::move(aRunnable), aFlags);
3175 }
3176 
DispatchDebuggeeToMainThread(already_AddRefed<WorkerDebuggeeRunnable> aRunnable,uint32_t aFlags)3177 nsresult WorkerPrivate::DispatchDebuggeeToMainThread(
3178     already_AddRefed<WorkerDebuggeeRunnable> aRunnable, uint32_t aFlags) {
3179   return mMainThreadDebuggeeEventTarget->Dispatch(std::move(aRunnable), aFlags);
3180 }
3181 
ControlEventTarget()3182 nsISerialEventTarget* WorkerPrivate::ControlEventTarget() {
3183   return mWorkerControlEventTarget;
3184 }
3185 
HybridEventTarget()3186 nsISerialEventTarget* WorkerPrivate::HybridEventTarget() {
3187   return mWorkerHybridEventTarget;
3188 }
3189 
GetClientType() const3190 ClientType WorkerPrivate::GetClientType() const {
3191   switch (Kind()) {
3192     case WorkerKindDedicated:
3193       return ClientType::Worker;
3194     case WorkerKindShared:
3195       return ClientType::Sharedworker;
3196     case WorkerKindService:
3197       return ClientType::Serviceworker;
3198     default:
3199       MOZ_CRASH("unknown worker type!");
3200   }
3201 }
3202 
CreateClientSource()3203 UniquePtr<ClientSource> WorkerPrivate::CreateClientSource() {
3204   auto data = mWorkerThreadAccessible.Access();
3205   MOZ_ASSERT(!data->mScope, "Client should be created before the global");
3206 
3207   auto clientSource = ClientManager::CreateSource(
3208       GetClientType(), mWorkerHybridEventTarget, GetPrincipalInfo());
3209   MOZ_DIAGNOSTIC_ASSERT(clientSource);
3210 
3211   clientSource->SetAgentClusterId(mAgentClusterId);
3212 
3213   if (data->mFrozen) {
3214     clientSource->Freeze();
3215   }
3216 
3217   // Shortly after the client is reserved we will try loading the main script
3218   // for the worker.  This may get intercepted by the ServiceWorkerManager
3219   // which will then try to create a ClientHandle.  Its actually possible for
3220   // the main thread to create this ClientHandle before our IPC message creating
3221   // the ClientSource completes.  To avoid this race we synchronously ping our
3222   // parent Client actor here.  This ensure the worker ClientSource is created
3223   // in the parent before the main thread might try reaching it with a
3224   // ClientHandle.
3225   //
3226   // An alternative solution would have been to handle the out-of-order
3227   // operations on the parent side.  We could have created a small window where
3228   // we allow ClientHandle objects to exist without a ClientSource.  We would
3229   // then time out these handles if they stayed orphaned for too long.  This
3230   // approach would be much more complex, but also avoid this extra bit of
3231   // latency when starting workers.
3232   //
3233   // Note, we only have to do this for workers that can be controlled by a
3234   // service worker.  So avoid the sync overhead here if we are starting a
3235   // service worker or a chrome worker.
3236   if (Kind() != WorkerKindService && !IsChromeWorker()) {
3237     clientSource->WorkerSyncPing(this);
3238   }
3239 
3240   return clientSource;
3241 }
3242 
EnsureCSPEventListener()3243 bool WorkerPrivate::EnsureCSPEventListener() {
3244   if (!mCSPEventListener) {
3245     mCSPEventListener = WorkerCSPEventListener::Create(this);
3246     if (NS_WARN_IF(!mCSPEventListener)) {
3247       return false;
3248     }
3249   }
3250   return true;
3251 }
3252 
CSPEventListener() const3253 nsICSPEventListener* WorkerPrivate::CSPEventListener() const {
3254   MOZ_ASSERT(mCSPEventListener);
3255   return mCSPEventListener;
3256 }
3257 
EnsurePerformanceStorage()3258 void WorkerPrivate::EnsurePerformanceStorage() {
3259   AssertIsOnWorkerThread();
3260 
3261   if (!mPerformanceStorage) {
3262     mPerformanceStorage = PerformanceStorageWorker::Create(this);
3263   }
3264 }
3265 
GetExecutionGranted() const3266 bool WorkerPrivate::GetExecutionGranted() const {
3267   auto data = mWorkerThreadAccessible.Access();
3268   return data->mJSThreadExecutionGranted;
3269 }
3270 
SetExecutionGranted(bool aGranted)3271 void WorkerPrivate::SetExecutionGranted(bool aGranted) {
3272   auto data = mWorkerThreadAccessible.Access();
3273   data->mJSThreadExecutionGranted = aGranted;
3274 }
3275 
ScheduleTimeSliceExpiration(uint32_t aDelay)3276 void WorkerPrivate::ScheduleTimeSliceExpiration(uint32_t aDelay) {
3277   auto data = mWorkerThreadAccessible.Access();
3278 
3279   if (!data->mTSTimer) {
3280     data->mTSTimer = NS_NewTimer();
3281     MOZ_ALWAYS_SUCCEEDS(data->mTSTimer->SetTarget(mWorkerControlEventTarget));
3282   }
3283 
3284   // Whenever an event is scheduled on the WorkerControlEventTarget an
3285   // interrupt is automatically requested which causes us to yield JS execution
3286   // and the next JS execution in the queue to execute.
3287   // This allows for simple code reuse of the existing interrupt callback code
3288   // used for control events.
3289   MOZ_ALWAYS_SUCCEEDS(data->mTSTimer->InitWithNamedFuncCallback(
3290       [](nsITimer* Timer, void* aClosure) { return; }, nullptr, aDelay,
3291       nsITimer::TYPE_ONE_SHOT, "TimeSliceExpirationTimer"));
3292 }
3293 
CancelTimeSliceExpiration()3294 void WorkerPrivate::CancelTimeSliceExpiration() {
3295   auto data = mWorkerThreadAccessible.Access();
3296   MOZ_ALWAYS_SUCCEEDS(data->mTSTimer->Cancel());
3297 }
3298 
GetExecutionManager() const3299 JSExecutionManager* WorkerPrivate::GetExecutionManager() const {
3300   auto data = mWorkerThreadAccessible.Access();
3301   return data->mExecutionManager.get();
3302 }
3303 
SetExecutionManager(JSExecutionManager * aManager)3304 void WorkerPrivate::SetExecutionManager(JSExecutionManager* aManager) {
3305   auto data = mWorkerThreadAccessible.Access();
3306   data->mExecutionManager = aManager;
3307 }
3308 
ExecutionReady()3309 void WorkerPrivate::ExecutionReady() {
3310   auto data = mWorkerThreadAccessible.Access();
3311   {
3312     MutexAutoLock lock(mMutex);
3313     if (mStatus >= Canceling) {
3314       return;
3315     }
3316   }
3317 
3318   data->mScope->MutableClientSourceRef().WorkerExecutionReady(this);
3319 }
3320 
InitializeGCTimers()3321 void WorkerPrivate::InitializeGCTimers() {
3322   auto data = mWorkerThreadAccessible.Access();
3323 
3324   // We need a timer for GC. The basic plan is to run a non-shrinking GC
3325   // periodically (PERIODIC_GC_TIMER_DELAY_SEC) while the worker is running.
3326   // Once the worker goes idle we set a short (IDLE_GC_TIMER_DELAY_SEC) timer to
3327   // run a shrinking GC. If the worker receives more messages then the short
3328   // timer is canceled and the periodic timer resumes.
3329   data->mGCTimer = NS_NewTimer();
3330   MOZ_ASSERT(data->mGCTimer);
3331 
3332   data->mPeriodicGCTimerRunning = false;
3333   data->mIdleGCTimerRunning = false;
3334 }
3335 
SetGCTimerMode(GCTimerMode aMode)3336 void WorkerPrivate::SetGCTimerMode(GCTimerMode aMode) {
3337   auto data = mWorkerThreadAccessible.Access();
3338   MOZ_ASSERT(data->mGCTimer);
3339 
3340   if ((aMode == PeriodicTimer && data->mPeriodicGCTimerRunning) ||
3341       (aMode == IdleTimer && data->mIdleGCTimerRunning)) {
3342     return;
3343   }
3344 
3345   MOZ_ALWAYS_SUCCEEDS(data->mGCTimer->Cancel());
3346 
3347   data->mPeriodicGCTimerRunning = false;
3348   data->mIdleGCTimerRunning = false;
3349   LOG(WorkerLog(), ("Worker %p canceled GC timer because %s\n", this,
3350                     aMode == PeriodicTimer ? "periodic"
3351                     : aMode == IdleTimer   ? "idle"
3352                                            : "none"));
3353 
3354   if (aMode == NoTimer) {
3355     return;
3356   }
3357 
3358   MOZ_ASSERT(aMode == PeriodicTimer || aMode == IdleTimer);
3359 
3360   uint32_t delay = 0;
3361   int16_t type = nsITimer::TYPE_ONE_SHOT;
3362   nsTimerCallbackFunc callback = nullptr;
3363   const char* name = nullptr;
3364 
3365   if (aMode == PeriodicTimer) {
3366     delay = PERIODIC_GC_TIMER_DELAY_SEC * 1000;
3367     type = nsITimer::TYPE_REPEATING_SLACK;
3368     callback = PeriodicGCTimerCallback;
3369     name = "dom::PeriodicGCTimerCallback";
3370   } else {
3371     delay = IDLE_GC_TIMER_DELAY_SEC * 1000;
3372     type = nsITimer::TYPE_ONE_SHOT;
3373     callback = IdleGCTimerCallback;
3374     name = "dom::IdleGCTimerCallback";
3375   }
3376 
3377   MOZ_ALWAYS_SUCCEEDS(data->mGCTimer->SetTarget(mWorkerControlEventTarget));
3378   MOZ_ALWAYS_SUCCEEDS(data->mGCTimer->InitWithNamedFuncCallback(
3379       callback, this, delay, type, name));
3380 
3381   if (aMode == PeriodicTimer) {
3382     LOG(WorkerLog(), ("Worker %p scheduled periodic GC timer\n", this));
3383     data->mPeriodicGCTimerRunning = true;
3384   } else {
3385     LOG(WorkerLog(), ("Worker %p scheduled idle GC timer\n", this));
3386     data->mIdleGCTimerRunning = true;
3387   }
3388 }
3389 
ShutdownGCTimers()3390 void WorkerPrivate::ShutdownGCTimers() {
3391   auto data = mWorkerThreadAccessible.Access();
3392 
3393   MOZ_ASSERT(data->mGCTimer);
3394 
3395   // Always make sure the timer is canceled.
3396   MOZ_ALWAYS_SUCCEEDS(data->mGCTimer->Cancel());
3397 
3398   LOG(WorkerLog(), ("Worker %p killed the GC timer\n", this));
3399 
3400   data->mGCTimer = nullptr;
3401   data->mPeriodicGCTimerRunning = false;
3402   data->mIdleGCTimerRunning = false;
3403 }
3404 
InterruptCallback(JSContext * aCx)3405 bool WorkerPrivate::InterruptCallback(JSContext* aCx) {
3406   auto data = mWorkerThreadAccessible.Access();
3407 
3408   AutoYieldJSThreadExecution yield;
3409 
3410   // If we are here it's because a WorkerControlRunnable has been dispatched.
3411   // The runnable could be processed here or it could have already been
3412   // processed by a sync event loop.
3413   // The most important thing this method must do, is to decide if the JS
3414   // execution should continue or not. If the runnable returns an error or if
3415   // the worker status is >= Canceling, we should stop the JS execution.
3416 
3417   MOZ_ASSERT(!JS_IsExceptionPending(aCx));
3418 
3419   bool mayContinue = true;
3420   bool scheduledIdleGC = false;
3421 
3422   for (;;) {
3423     // Run all control events now.
3424     auto result = ProcessAllControlRunnables();
3425     if (result == ProcessAllControlRunnablesResult::Abort) {
3426       mayContinue = false;
3427     }
3428 
3429     bool mayFreeze = data->mFrozen;
3430 
3431     {
3432       MutexAutoLock lock(mMutex);
3433 
3434       if (mayFreeze) {
3435         mayFreeze = mStatus <= Running;
3436       }
3437 
3438       if (mStatus >= Canceling) {
3439         mayContinue = false;
3440       }
3441     }
3442 
3443     if (!mayContinue || !mayFreeze) {
3444       break;
3445     }
3446 
3447     // Cancel the periodic GC timer here before freezing. The idle GC timer
3448     // will clean everything up once it runs.
3449     if (!scheduledIdleGC) {
3450       SetGCTimerMode(IdleTimer);
3451       scheduledIdleGC = true;
3452     }
3453 
3454     while ((mayContinue = MayContinueRunning())) {
3455       MutexAutoLock lock(mMutex);
3456       if (!mControlQueue.IsEmpty()) {
3457         break;
3458       }
3459 
3460       WaitForWorkerEvents();
3461     }
3462   }
3463 
3464   if (!mayContinue) {
3465     // We want only uncatchable exceptions here.
3466     NS_ASSERTION(!JS_IsExceptionPending(aCx),
3467                  "Should not have an exception set here!");
3468     return false;
3469   }
3470 
3471   // Make sure the periodic timer gets turned back on here.
3472   SetGCTimerMode(PeriodicTimer);
3473 
3474   return true;
3475 }
3476 
CloseInternal()3477 void WorkerPrivate::CloseInternal() {
3478   AssertIsOnWorkerThread();
3479   NotifyInternal(Closing);
3480 }
3481 
IsOnCurrentThread()3482 bool WorkerPrivate::IsOnCurrentThread() {
3483   // May be called on any thread!
3484 
3485   MOZ_ASSERT(mPRThread);
3486   return PR_GetCurrentThread() == mPRThread;
3487 }
3488 
ScheduleDeletion(WorkerRanOrNot aRanOrNot)3489 void WorkerPrivate::ScheduleDeletion(WorkerRanOrNot aRanOrNot) {
3490   AssertIsOnWorkerThread();
3491   {
3492     // mWorkerThreadAccessible's accessor must be destructed before
3493     // the scheduled Runnable gets to run.
3494     auto data = mWorkerThreadAccessible.Access();
3495     MOZ_ASSERT(data->mChildWorkers.IsEmpty());
3496 
3497     MOZ_RELEASE_ASSERT(!data->mDeletionScheduled);
3498     data->mDeletionScheduled.Flip();
3499   }
3500   MOZ_ASSERT(mSyncLoopStack.IsEmpty());
3501   MOZ_ASSERT(mPostSyncLoopOperations == 0);
3502 
3503   ClearMainEventQueue(aRanOrNot);
3504 #ifdef DEBUG
3505   if (WorkerRan == aRanOrNot) {
3506     nsIThread* currentThread = NS_GetCurrentThread();
3507     MOZ_ASSERT(currentThread);
3508     MOZ_ASSERT(!NS_HasPendingEvents(currentThread));
3509   }
3510 #endif
3511 
3512   if (WorkerPrivate* parent = GetParent()) {
3513     RefPtr<WorkerFinishedRunnable> runnable =
3514         new WorkerFinishedRunnable(parent, this);
3515     if (!runnable->Dispatch()) {
3516       NS_WARNING("Failed to dispatch runnable!");
3517     }
3518   } else {
3519     // Note, this uses the lower priority DispatchToMainThreadForMessaging for
3520     // dispatching TopLevelWorkerFinishedRunnable to the main thread so that
3521     // other relevant runnables are guaranteed to run before it.
3522     RefPtr<TopLevelWorkerFinishedRunnable> runnable =
3523         new TopLevelWorkerFinishedRunnable(this);
3524     if (NS_FAILED(DispatchToMainThreadForMessaging(runnable.forget()))) {
3525       NS_WARNING("Failed to dispatch runnable!");
3526     }
3527   }
3528 }
3529 
CollectRuntimeStats(JS::RuntimeStats * aRtStats,bool aAnonymize)3530 bool WorkerPrivate::CollectRuntimeStats(JS::RuntimeStats* aRtStats,
3531                                         bool aAnonymize) {
3532   AssertIsOnWorkerThread();
3533   NS_ASSERTION(aRtStats, "Null RuntimeStats!");
3534   NS_ASSERTION(mJSContext, "This must never be null!");
3535 
3536   return JS::CollectRuntimeStats(mJSContext, aRtStats, nullptr, aAnonymize);
3537 }
3538 
EnableMemoryReporter()3539 void WorkerPrivate::EnableMemoryReporter() {
3540   auto data = mWorkerThreadAccessible.Access();
3541   MOZ_ASSERT(!data->mMemoryReporter);
3542 
3543   // No need to lock here since the main thread can't race until we've
3544   // successfully registered the reporter.
3545   data->mMemoryReporter = new MemoryReporter(this);
3546 
3547   if (NS_FAILED(RegisterWeakAsyncMemoryReporter(data->mMemoryReporter))) {
3548     NS_WARNING("Failed to register memory reporter!");
3549     // No need to lock here since a failed registration means our memory
3550     // reporter can't start running. Just clean up.
3551     data->mMemoryReporter = nullptr;
3552   }
3553 }
3554 
DisableMemoryReporter()3555 void WorkerPrivate::DisableMemoryReporter() {
3556   auto data = mWorkerThreadAccessible.Access();
3557 
3558   RefPtr<MemoryReporter> memoryReporter;
3559   {
3560     // Mutex protectes MemoryReporter::mWorkerPrivate which is cleared by
3561     // MemoryReporter::Disable() below.
3562     MutexAutoLock lock(mMutex);
3563 
3564     // There is nothing to do here if the memory reporter was never successfully
3565     // registered.
3566     if (!data->mMemoryReporter) {
3567       return;
3568     }
3569 
3570     // We don't need this set any longer. Swap it out so that we can unregister
3571     // below.
3572     data->mMemoryReporter.swap(memoryReporter);
3573 
3574     // Next disable the memory reporter so that the main thread stops trying to
3575     // signal us.
3576     memoryReporter->Disable();
3577   }
3578 
3579   // Finally unregister the memory reporter.
3580   if (NS_FAILED(UnregisterWeakMemoryReporter(memoryReporter))) {
3581     NS_WARNING("Failed to unregister memory reporter!");
3582   }
3583 }
3584 
WaitForWorkerEvents()3585 void WorkerPrivate::WaitForWorkerEvents() {
3586   AUTO_PROFILER_LABEL("WorkerPrivate::WaitForWorkerEvents", IDLE);
3587 
3588   AssertIsOnWorkerThread();
3589   mMutex.AssertCurrentThreadOwns();
3590 
3591   // Wait for a worker event.
3592   mCondVar.Wait();
3593 }
3594 
3595 WorkerPrivate::ProcessAllControlRunnablesResult
ProcessAllControlRunnablesLocked()3596 WorkerPrivate::ProcessAllControlRunnablesLocked() {
3597   AssertIsOnWorkerThread();
3598   mMutex.AssertCurrentThreadOwns();
3599 
3600   AutoYieldJSThreadExecution yield;
3601 
3602   auto result = ProcessAllControlRunnablesResult::Nothing;
3603 
3604   for (;;) {
3605     WorkerControlRunnable* event;
3606     if (!mControlQueue.Pop(event)) {
3607       break;
3608     }
3609 
3610     MutexAutoUnlock unlock(mMutex);
3611 
3612     MOZ_ASSERT(event);
3613     if (NS_FAILED(static_cast<nsIRunnable*>(event)->Run())) {
3614       result = ProcessAllControlRunnablesResult::Abort;
3615     }
3616 
3617     if (result == ProcessAllControlRunnablesResult::Nothing) {
3618       // We ran at least one thing.
3619       result = ProcessAllControlRunnablesResult::MayContinue;
3620     }
3621     event->Release();
3622   }
3623 
3624   return result;
3625 }
3626 
ClearMainEventQueue(WorkerRanOrNot aRanOrNot)3627 void WorkerPrivate::ClearMainEventQueue(WorkerRanOrNot aRanOrNot) {
3628   AssertIsOnWorkerThread();
3629 
3630   MOZ_ASSERT((mPostSyncLoopOperations & ePendingEventQueueClearing)
3631                  ? (mSyncLoopStack.Length() == 1)
3632                  : mSyncLoopStack.IsEmpty());
3633   MOZ_ASSERT(!mCancelAllPendingRunnables);
3634 
3635   mCancelAllPendingRunnables = true;
3636   WorkerGlobalScope* globalScope = GlobalScope();
3637   if (globalScope) {
3638     // It's appropriate to disconnect event targets at the point that it's no
3639     // longer possible for new tasks to be dispatched at the global, and this is
3640     // that point.
3641     globalScope->DisconnectEventTargetObjects();
3642 
3643     globalScope->WorkerPrivateSaysForbidScript();
3644   }
3645 
3646   if (WorkerNeverRan == aRanOrNot) {
3647     for (uint32_t count = mPreStartRunnables.Length(), index = 0; index < count;
3648          index++) {
3649       RefPtr<WorkerRunnable> runnable = std::move(mPreStartRunnables[index]);
3650       static_cast<nsIRunnable*>(runnable.get())->Run();
3651     }
3652   } else {
3653     nsIThread* currentThread = NS_GetCurrentThread();
3654     MOZ_ASSERT(currentThread);
3655 
3656     NS_ProcessPendingEvents(currentThread);
3657   }
3658 
3659   if (globalScope) {
3660     globalScope->WorkerPrivateSaysAllowScript();
3661   }
3662   MOZ_ASSERT(mCancelAllPendingRunnables);
3663   mCancelAllPendingRunnables = false;
3664 }
3665 
ClearDebuggerEventQueue()3666 void WorkerPrivate::ClearDebuggerEventQueue() {
3667   while (!mDebuggerQueue.IsEmpty()) {
3668     WorkerRunnable* runnable = nullptr;
3669     mDebuggerQueue.Pop(runnable);
3670     // It should be ok to simply release the runnable, without running it.
3671     runnable->Release();
3672   }
3673 }
3674 
FreezeInternal()3675 bool WorkerPrivate::FreezeInternal() {
3676   auto data = mWorkerThreadAccessible.Access();
3677   NS_ASSERTION(!data->mFrozen, "Already frozen!");
3678 
3679   AutoYieldJSThreadExecution yield;
3680 
3681   // The worker can freeze even if it failed to run (and doesn't have a global).
3682   if (data->mScope) {
3683     data->mScope->MutableClientSourceRef().Freeze();
3684   }
3685 
3686   data->mFrozen = true;
3687 
3688   for (uint32_t index = 0; index < data->mChildWorkers.Length(); index++) {
3689     data->mChildWorkers[index]->Freeze(nullptr);
3690   }
3691 
3692   return true;
3693 }
3694 
ThawInternal()3695 bool WorkerPrivate::ThawInternal() {
3696   auto data = mWorkerThreadAccessible.Access();
3697   NS_ASSERTION(data->mFrozen, "Not yet frozen!");
3698 
3699   for (uint32_t index = 0; index < data->mChildWorkers.Length(); index++) {
3700     data->mChildWorkers[index]->Thaw(nullptr);
3701   }
3702 
3703   data->mFrozen = false;
3704 
3705   // The worker can thaw even if it failed to run (and doesn't have a global).
3706   if (data->mScope) {
3707     data->mScope->MutableClientSourceRef().Thaw();
3708   }
3709 
3710   return true;
3711 }
3712 
PropagateStorageAccessPermissionGrantedInternal()3713 void WorkerPrivate::PropagateStorageAccessPermissionGrantedInternal() {
3714   auto data = mWorkerThreadAccessible.Access();
3715 
3716   mLoadInfo.mUseRegularPrincipal = true;
3717   mLoadInfo.mHasStorageAccessPermissionGranted = true;
3718 
3719   WorkerGlobalScope* globalScope = GlobalScope();
3720   if (globalScope) {
3721     globalScope->StorageAccessPermissionGranted();
3722   }
3723 
3724   for (uint32_t index = 0; index < data->mChildWorkers.Length(); index++) {
3725     data->mChildWorkers[index]->PropagateStorageAccessPermissionGranted();
3726   }
3727 }
3728 
TraverseTimeouts(nsCycleCollectionTraversalCallback & cb)3729 void WorkerPrivate::TraverseTimeouts(nsCycleCollectionTraversalCallback& cb) {
3730   auto data = mWorkerThreadAccessible.Access();
3731   for (uint32_t i = 0; i < data->mTimeouts.Length(); ++i) {
3732     // TODO(erahm): No idea what's going on here.
3733     TimeoutInfo* tmp = data->mTimeouts[i].get();
3734     NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mHandler)
3735   }
3736 }
3737 
UnlinkTimeouts()3738 void WorkerPrivate::UnlinkTimeouts() {
3739   auto data = mWorkerThreadAccessible.Access();
3740   data->mTimeouts.Clear();
3741 }
3742 
ModifyBusyCountFromWorker(bool aIncrease)3743 bool WorkerPrivate::ModifyBusyCountFromWorker(bool aIncrease) {
3744   AssertIsOnWorkerThread();
3745 
3746   {
3747     MutexAutoLock lock(mMutex);
3748 
3749     // If we're in shutdown then the busy count is no longer being considered so
3750     // just return now.
3751     if (mStatus >= Killing) {
3752       return true;
3753     }
3754   }
3755 
3756   RefPtr<ModifyBusyCountRunnable> runnable =
3757       new ModifyBusyCountRunnable(this, aIncrease);
3758   return runnable->Dispatch();
3759 }
3760 
AddChildWorker(WorkerPrivate * aChildWorker)3761 bool WorkerPrivate::AddChildWorker(WorkerPrivate* aChildWorker) {
3762   auto data = mWorkerThreadAccessible.Access();
3763 
3764 #ifdef DEBUG
3765   {
3766     WorkerStatus currentStatus;
3767     {
3768       MutexAutoLock lock(mMutex);
3769       currentStatus = mStatus;
3770     }
3771 
3772     MOZ_ASSERT(currentStatus == Running);
3773   }
3774 #endif
3775 
3776   NS_ASSERTION(!data->mChildWorkers.Contains(aChildWorker),
3777                "Already know about this one!");
3778   data->mChildWorkers.AppendElement(aChildWorker);
3779 
3780   return data->mChildWorkers.Length() == 1 ? ModifyBusyCountFromWorker(true)
3781                                            : true;
3782 }
3783 
RemoveChildWorker(WorkerPrivate * aChildWorker)3784 void WorkerPrivate::RemoveChildWorker(WorkerPrivate* aChildWorker) {
3785   auto data = mWorkerThreadAccessible.Access();
3786 
3787   NS_ASSERTION(data->mChildWorkers.Contains(aChildWorker),
3788                "Didn't know about this one!");
3789   data->mChildWorkers.RemoveElement(aChildWorker);
3790 
3791   if (data->mChildWorkers.IsEmpty() && !ModifyBusyCountFromWorker(false)) {
3792     NS_WARNING("Failed to modify busy count!");
3793   }
3794 }
3795 
AddWorkerRef(WorkerRef * aWorkerRef,WorkerStatus aFailStatus)3796 bool WorkerPrivate::AddWorkerRef(WorkerRef* aWorkerRef,
3797                                  WorkerStatus aFailStatus) {
3798   MOZ_ASSERT(aWorkerRef);
3799   auto data = mWorkerThreadAccessible.Access();
3800 
3801   {
3802     MutexAutoLock lock(mMutex);
3803 
3804     if (mStatus >= aFailStatus) {
3805       return false;
3806     }
3807 
3808     // We shouldn't create strong references to workers before their main loop
3809     // begins running. Strong references must be disposed of on the worker
3810     // thread, so strong references from other threads use a control runnable
3811     // for that purpose. If the worker fails to reach the main loop stage then
3812     // no control runnables get run and it would be impossible to get rid of the
3813     // reference properly.
3814     MOZ_DIAGNOSTIC_ASSERT_IF(aWorkerRef->IsPreventingShutdown(),
3815                              mStatus >= WorkerStatus::Running);
3816   }
3817 
3818   MOZ_ASSERT(!data->mWorkerRefs.Contains(aWorkerRef),
3819              "Already know about this one!");
3820 
3821   if (aWorkerRef->IsPreventingShutdown()) {
3822     if (!data->mNumWorkerRefsPreventingShutdownStart &&
3823         !ModifyBusyCountFromWorker(true)) {
3824       return false;
3825     }
3826     data->mNumWorkerRefsPreventingShutdownStart += 1;
3827   }
3828 
3829   data->mWorkerRefs.AppendElement(aWorkerRef);
3830   return true;
3831 }
3832 
RemoveWorkerRef(WorkerRef * aWorkerRef)3833 void WorkerPrivate::RemoveWorkerRef(WorkerRef* aWorkerRef) {
3834   MOZ_ASSERT(aWorkerRef);
3835   auto data = mWorkerThreadAccessible.Access();
3836 
3837   MOZ_ASSERT(data->mWorkerRefs.Contains(aWorkerRef),
3838              "Didn't know about this one!");
3839   data->mWorkerRefs.RemoveElement(aWorkerRef);
3840 
3841   if (aWorkerRef->IsPreventingShutdown()) {
3842     data->mNumWorkerRefsPreventingShutdownStart -= 1;
3843     if (!data->mNumWorkerRefsPreventingShutdownStart &&
3844         !ModifyBusyCountFromWorker(false)) {
3845       NS_WARNING("Failed to modify busy count!");
3846     }
3847   }
3848 }
3849 
NotifyWorkerRefs(WorkerStatus aStatus)3850 void WorkerPrivate::NotifyWorkerRefs(WorkerStatus aStatus) {
3851   auto data = mWorkerThreadAccessible.Access();
3852 
3853   NS_ASSERTION(aStatus > Closing, "Bad status!");
3854 
3855   for (auto* workerRef : data->mWorkerRefs.ForwardRange()) {
3856     workerRef->Notify();
3857   }
3858 
3859   AutoTArray<WorkerPrivate*, 10> children;
3860   children.AppendElements(data->mChildWorkers);
3861 
3862   for (uint32_t index = 0; index < children.Length(); index++) {
3863     if (!children[index]->Notify(aStatus)) {
3864       NS_WARNING("Failed to notify child worker!");
3865     }
3866   }
3867 }
3868 
CancelAllTimeouts()3869 void WorkerPrivate::CancelAllTimeouts() {
3870   auto data = mWorkerThreadAccessible.Access();
3871 
3872   LOG(TimeoutsLog(), ("Worker %p CancelAllTimeouts.\n", this));
3873 
3874   if (data->mTimerRunning) {
3875     NS_ASSERTION(data->mTimer && data->mTimerRunnable, "Huh?!");
3876     NS_ASSERTION(!data->mTimeouts.IsEmpty(), "Huh?!");
3877 
3878     if (NS_FAILED(data->mTimer->Cancel())) {
3879       NS_WARNING("Failed to cancel timer!");
3880     }
3881 
3882     for (uint32_t index = 0; index < data->mTimeouts.Length(); index++) {
3883       data->mTimeouts[index]->mCanceled = true;
3884     }
3885 
3886     // If mRunningExpiredTimeouts, then the fact that they are all canceled now
3887     // means that the currently executing RunExpiredTimeouts will deal with
3888     // them.  Otherwise, we need to clean them up ourselves.
3889     if (!data->mRunningExpiredTimeouts) {
3890       data->mTimeouts.Clear();
3891       ModifyBusyCountFromWorker(false);
3892     }
3893 
3894     // Set mTimerRunning false even if mRunningExpiredTimeouts is true, so that
3895     // if we get reentered under this same RunExpiredTimeouts call we don't
3896     // assert above that !mTimeouts().IsEmpty(), because that's clearly false
3897     // now.
3898     data->mTimerRunning = false;
3899   }
3900 #ifdef DEBUG
3901   else if (!data->mRunningExpiredTimeouts) {
3902     NS_ASSERTION(data->mTimeouts.IsEmpty(), "Huh?!");
3903   }
3904 #endif
3905 
3906   data->mTimer = nullptr;
3907   data->mTimerRunnable = nullptr;
3908 }
3909 
CreateNewSyncLoop(WorkerStatus aFailStatus)3910 already_AddRefed<nsIEventTarget> WorkerPrivate::CreateNewSyncLoop(
3911     WorkerStatus aFailStatus) {
3912   AssertIsOnWorkerThread();
3913   MOZ_ASSERT(
3914       aFailStatus >= Canceling,
3915       "Sync loops can be created when the worker is in Running/Closing state!");
3916 
3917   {
3918     MutexAutoLock lock(mMutex);
3919 
3920     if (mStatus >= aFailStatus) {
3921       return nullptr;
3922     }
3923   }
3924 
3925   auto queue = static_cast<ThreadEventQueue*>(mThread->EventQueue());
3926   nsCOMPtr<nsISerialEventTarget> realEventTarget = queue->PushEventQueue();
3927   MOZ_ASSERT(realEventTarget);
3928 
3929   RefPtr<EventTarget> workerEventTarget =
3930       new EventTarget(this, realEventTarget);
3931 
3932   {
3933     // Modifications must be protected by mMutex in DEBUG builds, see comment
3934     // about mSyncLoopStack in WorkerPrivate.h.
3935 #ifdef DEBUG
3936     MutexAutoLock lock(mMutex);
3937 #endif
3938 
3939     mSyncLoopStack.AppendElement(new SyncLoopInfo(workerEventTarget));
3940   }
3941 
3942   return workerEventTarget.forget();
3943 }
3944 
RunCurrentSyncLoop()3945 bool WorkerPrivate::RunCurrentSyncLoop() {
3946   AssertIsOnWorkerThread();
3947 
3948   JSContext* cx = GetJSContext();
3949   MOZ_ASSERT(cx);
3950 
3951   AutoPushEventLoopGlobal eventLoopGlobal(this, cx);
3952 
3953   // This should not change between now and the time we finish running this sync
3954   // loop.
3955   uint32_t currentLoopIndex = mSyncLoopStack.Length() - 1;
3956 
3957   SyncLoopInfo* loopInfo = mSyncLoopStack[currentLoopIndex].get();
3958 
3959   AutoYieldJSThreadExecution yield;
3960 
3961   MOZ_ASSERT(loopInfo);
3962   MOZ_ASSERT(!loopInfo->mHasRun);
3963   MOZ_ASSERT(!loopInfo->mCompleted);
3964 
3965 #ifdef DEBUG
3966   loopInfo->mHasRun = true;
3967 #endif
3968 
3969   while (!loopInfo->mCompleted) {
3970     bool normalRunnablesPending = false;
3971 
3972     // Don't block with the periodic GC timer running.
3973     if (!NS_HasPendingEvents(mThread)) {
3974       SetGCTimerMode(IdleTimer);
3975     }
3976 
3977     // Wait for something to do.
3978     {
3979       MutexAutoLock lock(mMutex);
3980 
3981       for (;;) {
3982         while (mControlQueue.IsEmpty() && !normalRunnablesPending &&
3983                !(normalRunnablesPending = NS_HasPendingEvents(mThread))) {
3984           WaitForWorkerEvents();
3985         }
3986 
3987         auto result = ProcessAllControlRunnablesLocked();
3988         if (result != ProcessAllControlRunnablesResult::Nothing) {
3989           // The state of the world may have changed. Recheck it if we need to
3990           // continue.
3991           normalRunnablesPending =
3992               result == ProcessAllControlRunnablesResult::MayContinue &&
3993               NS_HasPendingEvents(mThread);
3994 
3995           // NB: If we processed a NotifyRunnable, we might have run
3996           // non-control runnables, one of which may have shut down the
3997           // sync loop.
3998           if (loopInfo->mCompleted) {
3999             break;
4000           }
4001         }
4002 
4003         // If we *didn't* run any control runnables, this should be unchanged.
4004         MOZ_ASSERT(!loopInfo->mCompleted);
4005 
4006         if (normalRunnablesPending) {
4007           break;
4008         }
4009       }
4010     }
4011 
4012     if (normalRunnablesPending) {
4013       // Make sure the periodic timer is running before we continue.
4014       SetGCTimerMode(PeriodicTimer);
4015 
4016       MOZ_ALWAYS_TRUE(NS_ProcessNextEvent(mThread, false));
4017 
4018       // Now *might* be a good time to GC. Let the JS engine make the decision.
4019       if (GetCurrentEventLoopGlobal()) {
4020         // If GetCurrentEventLoopGlobal() is non-null, our JSContext is in a
4021         // Realm, so it's safe to try to GC.
4022         MOZ_ASSERT(JS::CurrentGlobalOrNull(cx));
4023         JS_MaybeGC(cx);
4024       }
4025     }
4026   }
4027 
4028   // Make sure that the stack didn't change underneath us.
4029   MOZ_ASSERT(mSyncLoopStack[currentLoopIndex].get() == loopInfo);
4030 
4031   return DestroySyncLoop(currentLoopIndex);
4032 }
4033 
DestroySyncLoop(uint32_t aLoopIndex)4034 bool WorkerPrivate::DestroySyncLoop(uint32_t aLoopIndex) {
4035   MOZ_ASSERT(!mSyncLoopStack.IsEmpty());
4036   MOZ_ASSERT(mSyncLoopStack.Length() - 1 == aLoopIndex);
4037 
4038   AutoYieldJSThreadExecution yield;
4039 
4040   // We're about to delete the loop, stash its event target and result.
4041   const auto& loopInfo = mSyncLoopStack[aLoopIndex];
4042   nsIEventTarget* nestedEventTarget =
4043       loopInfo->mEventTarget->GetWeakNestedEventTarget();
4044   MOZ_ASSERT(nestedEventTarget);
4045 
4046   bool result = loopInfo->mResult;
4047 
4048   auto queue = static_cast<ThreadEventQueue*>(mThread->EventQueue());
4049   queue->PopEventQueue(nestedEventTarget);
4050 
4051   // Are we making a 1 -> 0 transition here?
4052   if (mSyncLoopStack.Length() == 1) {
4053     if ((mPostSyncLoopOperations & ePendingEventQueueClearing)) {
4054       ClearMainEventQueue(WorkerRan);
4055     }
4056 
4057     if ((mPostSyncLoopOperations & eDispatchCancelingRunnable)) {
4058       DispatchCancelingRunnable();
4059     }
4060 
4061     mPostSyncLoopOperations = 0;
4062   }
4063 
4064   {
4065     // Modifications must be protected by mMutex in DEBUG builds, see comment
4066     // about mSyncLoopStack in WorkerPrivate.h.
4067 #ifdef DEBUG
4068     MutexAutoLock lock(mMutex);
4069 #endif
4070 
4071     // This will delete |loopInfo|!
4072     mSyncLoopStack.RemoveElementAt(aLoopIndex);
4073   }
4074 
4075   return result;
4076 }
4077 
DispatchCancelingRunnable()4078 void WorkerPrivate::DispatchCancelingRunnable() {
4079   // Here we use a normal runnable to know when the current JS chunk of code
4080   // is finished. We cannot use a WorkerRunnable because they are not
4081   // accepted any more by the worker, and we do not want to use a
4082   // WorkerControlRunnable because they are immediately executed.
4083   RefPtr<CancelingRunnable> r = new CancelingRunnable();
4084   mThread->nsThread::Dispatch(r.forget(), NS_DISPATCH_NORMAL);
4085 
4086   // At the same time, we want to be sure that we interrupt infinite loops.
4087   // The following runnable starts a timer that cancel the worker, from the
4088   // parent thread, after CANCELING_TIMEOUT millseconds.
4089   RefPtr<CancelingWithTimeoutOnParentRunnable> rr =
4090       new CancelingWithTimeoutOnParentRunnable(this);
4091   rr->Dispatch();
4092 }
4093 
ReportUseCounters()4094 void WorkerPrivate::ReportUseCounters() {
4095   AssertIsOnWorkerThread();
4096 
4097   static const bool kDebugUseCounters = false;
4098 
4099   if (mReportedUseCounters) {
4100     return;
4101   }
4102   mReportedUseCounters = true;
4103 
4104   if (Telemetry::HistogramUseCounterWorkerCount <= 0 || IsChromeWorker()) {
4105     return;
4106   }
4107 
4108   const size_t kind = Kind();
4109   switch (kind) {
4110     case WorkerKindDedicated:
4111       Telemetry::Accumulate(Telemetry::DEDICATED_WORKER_DESTROYED, 1);
4112       break;
4113     case WorkerKindShared:
4114       Telemetry::Accumulate(Telemetry::SHARED_WORKER_DESTROYED, 1);
4115       break;
4116     case WorkerKindService:
4117       Telemetry::Accumulate(Telemetry::SERVICE_WORKER_DESTROYED, 1);
4118       break;
4119     default:
4120       MOZ_ASSERT(false, "Unknown worker kind");
4121       return;
4122   }
4123 
4124   if (kDebugUseCounters) {
4125     nsAutoCString path(Domain());
4126     path.AppendLiteral("(");
4127     NS_ConvertUTF16toUTF8 script(ScriptURL());
4128     path.Append(script);
4129     path.AppendPrintf(", 0x%p)", static_cast<void*>(this));
4130     printf("-- Worker use counters for %s --\n", path.get());
4131   }
4132 
4133   static_assert(
4134       static_cast<size_t>(UseCounterWorker::Count) * 3 ==
4135           static_cast<size_t>(Telemetry::HistogramUseCounterWorkerCount),
4136       "There should be three histograms (dedicated and shared and "
4137       "servie) for each worker use counter");
4138   const size_t count = static_cast<size_t>(UseCounterWorker::Count);
4139   const size_t factor =
4140       static_cast<size_t>(Telemetry::HistogramUseCounterWorkerCount) / count;
4141   MOZ_ASSERT(factor > kind);
4142 
4143   for (size_t c = 0; c < count; ++c) {
4144     // Histograms for worker use counters use the same order as the worker kinds
4145     // , so we can use the worker kind to index to corresponding histogram.
4146     Telemetry::HistogramID id = static_cast<Telemetry::HistogramID>(
4147         Telemetry::HistogramFirstUseCounterWorker + c * factor + kind);
4148     MOZ_ASSERT(id <= Telemetry::HistogramLastUseCounterWorker);
4149 
4150     if (bool value = GetUseCounter(static_cast<UseCounterWorker>(c))) {
4151       Telemetry::Accumulate(id, 1);
4152 
4153       if (kDebugUseCounters) {
4154         const char* name = Telemetry::GetHistogramName(id);
4155         printf("  %s  #%d: %d\n", name, id, value);
4156       }
4157     }
4158   }
4159 }
4160 
StopSyncLoop(nsIEventTarget * aSyncLoopTarget,bool aResult)4161 void WorkerPrivate::StopSyncLoop(nsIEventTarget* aSyncLoopTarget,
4162                                  bool aResult) {
4163   AssertIsOnWorkerThread();
4164   AssertValidSyncLoop(aSyncLoopTarget);
4165 
4166   MOZ_ASSERT(!mSyncLoopStack.IsEmpty());
4167 
4168   for (uint32_t index = mSyncLoopStack.Length(); index > 0; index--) {
4169     const auto& loopInfo = mSyncLoopStack[index - 1];
4170     MOZ_ASSERT(loopInfo);
4171     MOZ_ASSERT(loopInfo->mEventTarget);
4172 
4173     if (loopInfo->mEventTarget == aSyncLoopTarget) {
4174       // Can't assert |loop->mHasRun| here because dispatch failures can cause
4175       // us to bail out early.
4176       MOZ_ASSERT(!loopInfo->mCompleted);
4177 
4178       loopInfo->mResult = aResult;
4179       loopInfo->mCompleted = true;
4180 
4181       loopInfo->mEventTarget->Disable();
4182 
4183       return;
4184     }
4185 
4186     MOZ_ASSERT(!SameCOMIdentity(loopInfo->mEventTarget, aSyncLoopTarget));
4187   }
4188 
4189   MOZ_CRASH("Unknown sync loop!");
4190 }
4191 
4192 #ifdef DEBUG
AssertValidSyncLoop(nsIEventTarget * aSyncLoopTarget)4193 void WorkerPrivate::AssertValidSyncLoop(nsIEventTarget* aSyncLoopTarget) {
4194   MOZ_ASSERT(aSyncLoopTarget);
4195 
4196   EventTarget* workerTarget;
4197   nsresult rv = aSyncLoopTarget->QueryInterface(
4198       kDEBUGWorkerEventTargetIID, reinterpret_cast<void**>(&workerTarget));
4199   MOZ_ASSERT(NS_SUCCEEDED(rv));
4200   MOZ_ASSERT(workerTarget);
4201 
4202   bool valid = false;
4203 
4204   {
4205     MutexAutoLock lock(mMutex);
4206 
4207     for (uint32_t index = 0; index < mSyncLoopStack.Length(); index++) {
4208       const auto& loopInfo = mSyncLoopStack[index];
4209       MOZ_ASSERT(loopInfo);
4210       MOZ_ASSERT(loopInfo->mEventTarget);
4211 
4212       if (loopInfo->mEventTarget == aSyncLoopTarget) {
4213         valid = true;
4214         break;
4215       }
4216 
4217       MOZ_ASSERT(!SameCOMIdentity(loopInfo->mEventTarget, aSyncLoopTarget));
4218     }
4219   }
4220 
4221   MOZ_ASSERT(valid);
4222 }
4223 #endif
4224 
PostMessageToParent(JSContext * aCx,JS::Handle<JS::Value> aMessage,const Sequence<JSObject * > & aTransferable,ErrorResult & aRv)4225 void WorkerPrivate::PostMessageToParent(
4226     JSContext* aCx, JS::Handle<JS::Value> aMessage,
4227     const Sequence<JSObject*>& aTransferable, ErrorResult& aRv) {
4228   AssertIsOnWorkerThread();
4229   MOZ_DIAGNOSTIC_ASSERT(IsDedicatedWorker());
4230 
4231   JS::Rooted<JS::Value> transferable(aCx, JS::UndefinedValue());
4232 
4233   aRv = nsContentUtils::CreateJSValueFromSequenceOfObject(aCx, aTransferable,
4234                                                           &transferable);
4235   if (NS_WARN_IF(aRv.Failed())) {
4236     return;
4237   }
4238 
4239   RefPtr<MessageEventRunnable> runnable = new MessageEventRunnable(
4240       this, WorkerRunnable::ParentThreadUnchangedBusyCount);
4241 
4242   UniquePtr<AbstractTimelineMarker> start;
4243   UniquePtr<AbstractTimelineMarker> end;
4244   RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
4245   bool isTimelineRecording = timelines && !timelines->IsEmpty();
4246 
4247   if (isTimelineRecording) {
4248     start = MakeUnique<WorkerTimelineMarker>(
4249         NS_IsMainThread()
4250             ? ProfileTimelineWorkerOperationType::SerializeDataOnMainThread
4251             : ProfileTimelineWorkerOperationType::SerializeDataOffMainThread,
4252         MarkerTracingType::START);
4253   }
4254 
4255   JS::CloneDataPolicy clonePolicy;
4256 
4257   // Parent and dedicated workers are always part of the same cluster.
4258   clonePolicy.allowIntraClusterClonableSharedObjects();
4259 
4260   if (IsSharedMemoryAllowed()) {
4261     clonePolicy.allowSharedMemoryObjects();
4262   }
4263 
4264   runnable->Write(aCx, aMessage, transferable, clonePolicy, aRv);
4265 
4266   if (isTimelineRecording) {
4267     end = MakeUnique<WorkerTimelineMarker>(
4268         NS_IsMainThread()
4269             ? ProfileTimelineWorkerOperationType::SerializeDataOnMainThread
4270             : ProfileTimelineWorkerOperationType::SerializeDataOffMainThread,
4271         MarkerTracingType::END);
4272     timelines->AddMarkerForAllObservedDocShells(start);
4273     timelines->AddMarkerForAllObservedDocShells(end);
4274   }
4275 
4276   if (NS_WARN_IF(aRv.Failed())) {
4277     return;
4278   }
4279 
4280   if (!runnable->Dispatch()) {
4281     aRv = NS_ERROR_FAILURE;
4282   }
4283 }
4284 
EnterDebuggerEventLoop()4285 void WorkerPrivate::EnterDebuggerEventLoop() {
4286   auto data = mWorkerThreadAccessible.Access();
4287 
4288   JSContext* cx = GetJSContext();
4289   MOZ_ASSERT(cx);
4290 
4291   AutoPushEventLoopGlobal eventLoopGlobal(this, cx);
4292   AutoYieldJSThreadExecution yield;
4293 
4294   CycleCollectedJSContext* ccjscx = CycleCollectedJSContext::Get();
4295 
4296   uint32_t currentEventLoopLevel = ++data->mDebuggerEventLoopLevel;
4297 
4298   while (currentEventLoopLevel <= data->mDebuggerEventLoopLevel) {
4299     bool debuggerRunnablesPending = false;
4300 
4301     {
4302       MutexAutoLock lock(mMutex);
4303 
4304       debuggerRunnablesPending = !mDebuggerQueue.IsEmpty();
4305     }
4306 
4307     // Don't block with the periodic GC timer running.
4308     if (!debuggerRunnablesPending) {
4309       SetGCTimerMode(IdleTimer);
4310     }
4311 
4312     // Wait for something to do
4313     {
4314       MutexAutoLock lock(mMutex);
4315 
4316       std::deque<RefPtr<MicroTaskRunnable>>& debuggerMtQueue =
4317           ccjscx->GetDebuggerMicroTaskQueue();
4318       while (mControlQueue.IsEmpty() &&
4319              !(debuggerRunnablesPending = !mDebuggerQueue.IsEmpty()) &&
4320              debuggerMtQueue.empty()) {
4321         WaitForWorkerEvents();
4322       }
4323 
4324       ProcessAllControlRunnablesLocked();
4325 
4326       // XXXkhuey should we abort JS on the stack here if we got Abort above?
4327     }
4328     ccjscx->PerformDebuggerMicroTaskCheckpoint();
4329     if (debuggerRunnablesPending) {
4330       // Start the periodic GC timer if it is not already running.
4331       SetGCTimerMode(PeriodicTimer);
4332 
4333       WorkerRunnable* runnable = nullptr;
4334 
4335       {
4336         MutexAutoLock lock(mMutex);
4337 
4338         mDebuggerQueue.Pop(runnable);
4339       }
4340 
4341       MOZ_ASSERT(runnable);
4342       static_cast<nsIRunnable*>(runnable)->Run();
4343       runnable->Release();
4344 
4345       ccjscx->PerformDebuggerMicroTaskCheckpoint();
4346 
4347       // Now *might* be a good time to GC. Let the JS engine make the decision.
4348       if (GetCurrentEventLoopGlobal()) {
4349         // If GetCurrentEventLoopGlobal() is non-null, our JSContext is in a
4350         // Realm, so it's safe to try to GC.
4351         MOZ_ASSERT(JS::CurrentGlobalOrNull(cx));
4352         JS_MaybeGC(cx);
4353       }
4354     }
4355   }
4356 }
4357 
LeaveDebuggerEventLoop()4358 void WorkerPrivate::LeaveDebuggerEventLoop() {
4359   auto data = mWorkerThreadAccessible.Access();
4360 
4361   // TODO: Why lock the mutex if we're accessing data accessible to one thread
4362   // only?
4363   MutexAutoLock lock(mMutex);
4364 
4365   if (data->mDebuggerEventLoopLevel > 0) {
4366     --data->mDebuggerEventLoopLevel;
4367   }
4368 }
4369 
PostMessageToDebugger(const nsAString & aMessage)4370 void WorkerPrivate::PostMessageToDebugger(const nsAString& aMessage) {
4371   mDebugger->PostMessageToDebugger(aMessage);
4372 }
4373 
SetDebuggerImmediate(dom::Function & aHandler,ErrorResult & aRv)4374 void WorkerPrivate::SetDebuggerImmediate(dom::Function& aHandler,
4375                                          ErrorResult& aRv) {
4376   AssertIsOnWorkerThread();
4377 
4378   RefPtr<DebuggerImmediateRunnable> runnable =
4379       new DebuggerImmediateRunnable(this, aHandler);
4380   if (!runnable->Dispatch()) {
4381     aRv.Throw(NS_ERROR_FAILURE);
4382   }
4383 }
4384 
ReportErrorToDebugger(const nsAString & aFilename,uint32_t aLineno,const nsAString & aMessage)4385 void WorkerPrivate::ReportErrorToDebugger(const nsAString& aFilename,
4386                                           uint32_t aLineno,
4387                                           const nsAString& aMessage) {
4388   mDebugger->ReportErrorToDebugger(aFilename, aLineno, aMessage);
4389 }
4390 
NotifyInternal(WorkerStatus aStatus)4391 bool WorkerPrivate::NotifyInternal(WorkerStatus aStatus) {
4392   auto data = mWorkerThreadAccessible.Access();
4393 
4394   // Yield execution while notifying out-of-module WorkerRefs and cancelling
4395   // runnables.
4396   AutoYieldJSThreadExecution yield;
4397 
4398   NS_ASSERTION(aStatus > Running && aStatus < Dead, "Bad status!");
4399 
4400   RefPtr<EventTarget> eventTarget;
4401 
4402   // Save the old status and set the new status.
4403   WorkerStatus previousStatus;
4404   {
4405     MutexAutoLock lock(mMutex);
4406 
4407     if (mStatus >= aStatus) {
4408       return true;
4409     }
4410 
4411     MOZ_ASSERT_IF(aStatus == Killing, mStatus == Canceling);
4412 
4413     if (aStatus >= Canceling) {
4414       MutexAutoUnlock unlock(mMutex);
4415       if (data->mScope) {
4416         data->mScope->NoteTerminating();
4417       }
4418     }
4419 
4420     // Make sure the hybrid event target stops dispatching runnables
4421     // once we reaching the killing state.
4422     if (aStatus == Killing) {
4423       // To avoid deadlock we always acquire the event target mutex before the
4424       // worker private mutex.  (We do it in this order because this is what
4425       // workers best for event dispatching.)  To enforce that order here we
4426       // need to unlock the worker private mutex before we lock the event target
4427       // mutex in ForgetWorkerPrivate.
4428       {
4429         MutexAutoUnlock unlock(mMutex);
4430         mWorkerHybridEventTarget->ForgetWorkerPrivate(this);
4431       }
4432 
4433       // Check the status code again in case another NotifyInternal came in
4434       // while we were unlocked above.
4435       if (mStatus >= aStatus) {
4436         return true;
4437       }
4438     }
4439 
4440     previousStatus = mStatus;
4441     mStatus = aStatus;
4442 
4443     // Mark parent status as closing immediately to avoid new events being
4444     // dispatched after we clear the queue below.
4445     if (aStatus == Closing) {
4446       Close();
4447     }
4448   }
4449 
4450   MOZ_ASSERT(previousStatus != Pending);
4451 
4452   if (aStatus >= Closing) {
4453     CancelAllTimeouts();
4454   }
4455 
4456   // Let all our holders know the new status.
4457   if (aStatus > Closing) {
4458     NotifyWorkerRefs(aStatus);
4459   }
4460 
4461   // If this is the first time our status has changed then we need to clear the
4462   // main event queue.
4463   if (previousStatus == Running) {
4464     // NB: If we're in a sync loop, we can't clear the queue immediately,
4465     // because this is the wrong queue. So we have to defer it until later.
4466     if (!mSyncLoopStack.IsEmpty()) {
4467       mPostSyncLoopOperations |= ePendingEventQueueClearing;
4468     } else {
4469       ClearMainEventQueue(WorkerRan);
4470     }
4471   }
4472 
4473   // If the worker script never ran, or failed to compile, we don't need to do
4474   // anything else.
4475   if (!GlobalScope()) {
4476     return true;
4477   }
4478 
4479   // Don't abort the script now, but we dispatch a runnable to do it when the
4480   // current JS frame is executed.
4481   if (aStatus == Closing) {
4482     if (!mSyncLoopStack.IsEmpty()) {
4483       mPostSyncLoopOperations |= eDispatchCancelingRunnable;
4484     } else {
4485       DispatchCancelingRunnable();
4486     }
4487     return true;
4488   }
4489 
4490   MOZ_ASSERT(aStatus == Canceling || aStatus == Killing);
4491 
4492   // Always abort the script.
4493   return false;
4494 }
4495 
ReportError(JSContext * aCx,JS::ConstUTF8CharsZ aToStringResult,JSErrorReport * aReport)4496 void WorkerPrivate::ReportError(JSContext* aCx,
4497                                 JS::ConstUTF8CharsZ aToStringResult,
4498                                 JSErrorReport* aReport) {
4499   auto data = mWorkerThreadAccessible.Access();
4500 
4501   if (!MayContinueRunning() || data->mErrorHandlerRecursionCount == 2) {
4502     return;
4503   }
4504 
4505   NS_ASSERTION(data->mErrorHandlerRecursionCount == 0 ||
4506                    data->mErrorHandlerRecursionCount == 1,
4507                "Bad recursion logic!");
4508 
4509   UniquePtr<WorkerErrorReport> report = MakeUnique<WorkerErrorReport>();
4510   if (aReport) {
4511     report->AssignErrorReport(aReport);
4512   }
4513 
4514   JS::ExceptionStack exnStack(aCx);
4515   if (JS_IsExceptionPending(aCx)) {
4516     if (!JS::StealPendingExceptionStack(aCx, &exnStack)) {
4517       JS_ClearPendingException(aCx);
4518       return;
4519     }
4520 
4521     JS::RootedObject stack(aCx), stackGlobal(aCx);
4522     xpc::FindExceptionStackForConsoleReport(
4523         nullptr, exnStack.exception(), exnStack.stack(), &stack, &stackGlobal);
4524 
4525     if (stack) {
4526       JSAutoRealm ar(aCx, stackGlobal);
4527       report->SerializeWorkerStack(aCx, this, stack);
4528     }
4529   } else {
4530     // ReportError is also used for reporting warnings,
4531     // so there won't be a pending exception.
4532     MOZ_ASSERT(aReport->isWarning());
4533   }
4534 
4535   if (report->mMessage.IsEmpty() && aToStringResult) {
4536     nsDependentCString toStringResult(aToStringResult.c_str());
4537     if (!AppendUTF8toUTF16(toStringResult, report->mMessage,
4538                            mozilla::fallible)) {
4539       // Try again, with only a 1 KB string. Do this infallibly this time.
4540       // If the user doesn't have 1 KB to spare we're done anyways.
4541       size_t index = std::min<size_t>(1024, toStringResult.Length());
4542 
4543       // Drop the last code point that may be cropped.
4544       index = RewindToPriorUTF8Codepoint(toStringResult.BeginReading(), index);
4545 
4546       nsDependentCString truncatedToStringResult(aToStringResult.c_str(),
4547                                                  index);
4548       AppendUTF8toUTF16(truncatedToStringResult, report->mMessage);
4549     }
4550   }
4551 
4552   data->mErrorHandlerRecursionCount++;
4553 
4554   // Don't want to run the scope's error handler if this is a recursive error or
4555   // if we ran out of memory.
4556   bool fireAtScope = data->mErrorHandlerRecursionCount == 1 &&
4557                      report->mErrorNumber != JSMSG_OUT_OF_MEMORY &&
4558                      JS::CurrentGlobalOrNull(aCx);
4559 
4560   WorkerErrorReport::ReportError(aCx, this, fireAtScope, nullptr,
4561                                  std::move(report), 0, exnStack.exception());
4562 
4563   data->mErrorHandlerRecursionCount--;
4564 }
4565 
4566 // static
ReportErrorToConsole(const char * aMessage)4567 void WorkerPrivate::ReportErrorToConsole(const char* aMessage) {
4568   nsTArray<nsString> emptyParams;
4569   WorkerPrivate::ReportErrorToConsole(aMessage, emptyParams);
4570 }
4571 
4572 // static
ReportErrorToConsole(const char * aMessage,const nsTArray<nsString> & aParams)4573 void WorkerPrivate::ReportErrorToConsole(const char* aMessage,
4574                                          const nsTArray<nsString>& aParams) {
4575   WorkerPrivate* wp = nullptr;
4576   if (!NS_IsMainThread()) {
4577     wp = GetCurrentThreadWorkerPrivate();
4578   }
4579 
4580   ReportErrorToConsoleRunnable::Report(wp, aMessage, aParams);
4581 }
4582 
SetTimeout(JSContext * aCx,TimeoutHandler * aHandler,int32_t aTimeout,bool aIsInterval,ErrorResult & aRv)4583 int32_t WorkerPrivate::SetTimeout(JSContext* aCx, TimeoutHandler* aHandler,
4584                                   int32_t aTimeout, bool aIsInterval,
4585                                   ErrorResult& aRv) {
4586   auto data = mWorkerThreadAccessible.Access();
4587   MOZ_ASSERT(aHandler);
4588 
4589   const int32_t timerId = data->mNextTimeoutId++;
4590 
4591   WorkerStatus currentStatus;
4592   {
4593     MutexAutoLock lock(mMutex);
4594     currentStatus = mStatus;
4595   }
4596 
4597   // If the worker is trying to call setTimeout/setInterval and the parent
4598   // thread has initiated the close process then just silently fail.
4599   if (currentStatus >= Closing) {
4600     return timerId;
4601   }
4602 
4603   auto newInfo = MakeUnique<TimeoutInfo>();
4604   newInfo->mOnChromeWorker = mIsChromeWorker;
4605   newInfo->mIsInterval = aIsInterval;
4606   newInfo->mId = timerId;
4607   newInfo->AccumulateNestingLevel(data->mCurrentTimerNestingLevel);
4608 
4609   if (MOZ_UNLIKELY(timerId == INT32_MAX)) {
4610     NS_WARNING("Timeout ids overflowed!");
4611     data->mNextTimeoutId = 1;
4612   }
4613 
4614   newInfo->mHandler = aHandler;
4615 
4616   // See if any of the optional arguments were passed.
4617   aTimeout = std::max(0, aTimeout);
4618   newInfo->mInterval = TimeDuration::FromMilliseconds(aTimeout);
4619   newInfo->CalculateTargetTime();
4620 
4621   const auto& insertedInfo = data->mTimeouts.InsertElementSorted(
4622       std::move(newInfo), GetUniquePtrComparator(data->mTimeouts));
4623 
4624   LOG(TimeoutsLog(), ("Worker %p has new timeout: delay=%d interval=%s\n", this,
4625                       aTimeout, aIsInterval ? "yes" : "no"));
4626 
4627   // If the timeout we just made is set to fire next then we need to update the
4628   // timer, unless we're currently running timeouts.
4629   if (insertedInfo == data->mTimeouts.Elements() &&
4630       !data->mRunningExpiredTimeouts) {
4631     if (!data->mTimer) {
4632       data->mTimer = NS_NewTimer();
4633       if (!data->mTimer) {
4634         aRv.Throw(NS_ERROR_UNEXPECTED);
4635         return 0;
4636       }
4637 
4638       data->mTimerRunnable = new TimerRunnable(this);
4639     }
4640 
4641     if (!data->mTimerRunning) {
4642       if (!ModifyBusyCountFromWorker(true)) {
4643         aRv.Throw(NS_ERROR_FAILURE);
4644         return 0;
4645       }
4646       data->mTimerRunning = true;
4647     }
4648 
4649     if (!RescheduleTimeoutTimer(aCx)) {
4650       aRv.Throw(NS_ERROR_FAILURE);
4651       return 0;
4652     }
4653   }
4654 
4655   return timerId;
4656 }
4657 
ClearTimeout(int32_t aId)4658 void WorkerPrivate::ClearTimeout(int32_t aId) {
4659   auto data = mWorkerThreadAccessible.Access();
4660 
4661   if (!data->mTimeouts.IsEmpty()) {
4662     NS_ASSERTION(data->mTimerRunning, "Huh?!");
4663 
4664     for (uint32_t index = 0; index < data->mTimeouts.Length(); index++) {
4665       const auto& info = data->mTimeouts[index];
4666       if (info->mId == aId) {
4667         info->mCanceled = true;
4668         break;
4669       }
4670     }
4671   }
4672 }
4673 
RunExpiredTimeouts(JSContext * aCx)4674 bool WorkerPrivate::RunExpiredTimeouts(JSContext* aCx) {
4675   auto data = mWorkerThreadAccessible.Access();
4676 
4677   // We may be called recursively (e.g. close() inside a timeout) or we could
4678   // have been canceled while this event was pending, bail out if there is
4679   // nothing to do.
4680   if (data->mRunningExpiredTimeouts || !data->mTimerRunning) {
4681     return true;
4682   }
4683 
4684   NS_ASSERTION(data->mTimer && data->mTimerRunnable, "Must have a timer!");
4685   NS_ASSERTION(!data->mTimeouts.IsEmpty(), "Should have some work to do!");
4686 
4687   bool retval = true;
4688 
4689   auto comparator = GetUniquePtrComparator(data->mTimeouts);
4690   JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx));
4691 
4692   // We want to make sure to run *something*, even if the timer fired a little
4693   // early. Fudge the value of now to at least include the first timeout.
4694   const TimeStamp actual_now = TimeStamp::Now();
4695   const TimeStamp now = std::max(actual_now, data->mTimeouts[0]->mTargetTime);
4696 
4697   if (now != actual_now) {
4698     LOG(TimeoutsLog(), ("Worker %p fudged timeout by %f ms.\n", this,
4699                         (now - actual_now).ToMilliseconds()));
4700   }
4701 
4702   AutoTArray<TimeoutInfo*, 10> expiredTimeouts;
4703   for (uint32_t index = 0; index < data->mTimeouts.Length(); index++) {
4704     TimeoutInfo* info = data->mTimeouts[index].get();
4705     if (info->mTargetTime > now) {
4706       break;
4707     }
4708     expiredTimeouts.AppendElement(info);
4709   }
4710 
4711   // Guard against recursion.
4712   data->mRunningExpiredTimeouts = true;
4713 
4714   MOZ_DIAGNOSTIC_ASSERT(data->mCurrentTimerNestingLevel == 0);
4715 
4716   // Run expired timeouts.
4717   for (uint32_t index = 0; index < expiredTimeouts.Length(); index++) {
4718     TimeoutInfo*& info = expiredTimeouts[index];
4719     AutoRestore<uint32_t> nestingLevel(data->mCurrentTimerNestingLevel);
4720 
4721     if (info->mCanceled) {
4722       continue;
4723     }
4724 
4725     // Set current timer nesting level to current running timer handler's
4726     // nesting level
4727     data->mCurrentTimerNestingLevel = info->mNestingLevel;
4728 
4729     LOG(TimeoutsLog(),
4730         ("Worker %p executing timeout with original delay %f ms.\n", this,
4731          info->mInterval.ToMilliseconds()));
4732 
4733     // Always check JS_IsExceptionPending if something fails, and if
4734     // JS_IsExceptionPending returns false (i.e. uncatchable exception) then
4735     // break out of the loop.
4736     const char* reason;
4737     if (info->mIsInterval) {
4738       reason = "setInterval handler";
4739     } else {
4740       reason = "setTimeout handler";
4741     }
4742 
4743     RefPtr<TimeoutHandler> handler(info->mHandler);
4744 
4745     RefPtr<WorkerGlobalScope> scope(this->GlobalScope());
4746     CallbackDebuggerNotificationGuard guard(
4747         scope, info->mIsInterval
4748                    ? DebuggerNotificationType::SetIntervalCallback
4749                    : DebuggerNotificationType::SetTimeoutCallback);
4750     if (!handler->Call(reason)) {
4751       retval = false;
4752       break;
4753     }
4754 
4755     NS_ASSERTION(data->mRunningExpiredTimeouts, "Someone changed this!");
4756   }
4757 
4758   // No longer possible to be called recursively.
4759   data->mRunningExpiredTimeouts = false;
4760 
4761   // Now remove canceled and expired timeouts from the main list.
4762   // NB: The timeouts present in expiredTimeouts must have the same order
4763   // with respect to each other in mTimeouts.  That is, mTimeouts is just
4764   // expiredTimeouts with extra elements inserted.  There may be unexpired
4765   // timeouts that have been inserted between the expired timeouts if the
4766   // timeout event handler called setTimeout/setInterval.
4767   for (uint32_t index = 0, expiredTimeoutIndex = 0,
4768                 expiredTimeoutLength = expiredTimeouts.Length();
4769        index < data->mTimeouts.Length();) {
4770     const auto& info = data->mTimeouts[index];
4771     if ((expiredTimeoutIndex < expiredTimeoutLength &&
4772          info == expiredTimeouts[expiredTimeoutIndex] &&
4773          ++expiredTimeoutIndex) ||
4774         info->mCanceled) {
4775       if (info->mIsInterval && !info->mCanceled) {
4776         // Reschedule intervals.
4777         // Reschedule a timeout, if needed, increase the nesting level.
4778         info->AccumulateNestingLevel(info->mNestingLevel);
4779         info->CalculateTargetTime();
4780         // Don't resort the list here, we'll do that at the end.
4781         ++index;
4782       } else {
4783         data->mTimeouts.RemoveElement(info);
4784       }
4785     } else {
4786       // If info did not match the current entry in expiredTimeouts, it
4787       // shouldn't be there at all.
4788       NS_ASSERTION(!expiredTimeouts.Contains(info),
4789                    "Our timeouts are out of order!");
4790       ++index;
4791     }
4792   }
4793 
4794   data->mTimeouts.Sort(comparator);
4795 
4796   // Either signal the parent that we're no longer using timeouts or reschedule
4797   // the timer.
4798   if (data->mTimeouts.IsEmpty()) {
4799     if (!ModifyBusyCountFromWorker(false)) {
4800       retval = false;
4801     }
4802     data->mTimerRunning = false;
4803   } else if (retval && !RescheduleTimeoutTimer(aCx)) {
4804     retval = false;
4805   }
4806 
4807   return retval;
4808 }
4809 
RescheduleTimeoutTimer(JSContext * aCx)4810 bool WorkerPrivate::RescheduleTimeoutTimer(JSContext* aCx) {
4811   auto data = mWorkerThreadAccessible.Access();
4812   MOZ_ASSERT(!data->mRunningExpiredTimeouts);
4813   NS_ASSERTION(!data->mTimeouts.IsEmpty(), "Should have some timeouts!");
4814   NS_ASSERTION(data->mTimer && data->mTimerRunnable, "Should have a timer!");
4815 
4816   // NB: This is important! The timer may have already fired, e.g. if a timeout
4817   // callback itself calls setTimeout for a short duration and then takes longer
4818   // than that to finish executing. If that has happened, it's very important
4819   // that we don't execute the event that is now pending in our event queue, or
4820   // our code in RunExpiredTimeouts to "fudge" the timeout value will unleash an
4821   // early timeout when we execute the event we're about to queue.
4822   data->mTimer->Cancel();
4823 
4824   double delta =
4825       (data->mTimeouts[0]->mTargetTime - TimeStamp::Now()).ToMilliseconds();
4826   uint32_t delay = delta > 0 ? std::min(delta, double(UINT32_MAX)) : 0;
4827 
4828   LOG(TimeoutsLog(),
4829       ("Worker %p scheduled timer for %d ms, %zu pending timeouts\n", this,
4830        delay, data->mTimeouts.Length()));
4831 
4832   nsresult rv = data->mTimer->InitWithCallback(data->mTimerRunnable, delay,
4833                                                nsITimer::TYPE_ONE_SHOT);
4834   if (NS_FAILED(rv)) {
4835     JS_ReportErrorASCII(aCx, "Failed to start timer!");
4836     return false;
4837   }
4838 
4839   return true;
4840 }
4841 
StartCancelingTimer()4842 void WorkerPrivate::StartCancelingTimer() {
4843   AssertIsOnParentThread();
4844 
4845   auto errorCleanup = MakeScopeExit([&] { mCancelingTimer = nullptr; });
4846 
4847   MOZ_ASSERT(!mCancelingTimer);
4848 
4849   if (WorkerPrivate* parent = GetParent()) {
4850     mCancelingTimer = NS_NewTimer(parent->ControlEventTarget());
4851   } else {
4852     mCancelingTimer = NS_NewTimer();
4853   }
4854 
4855   if (NS_WARN_IF(!mCancelingTimer)) {
4856     return;
4857   }
4858 
4859   // This is not needed if we are already in an advanced shutdown state.
4860   {
4861     MutexAutoLock lock(mMutex);
4862     if (ParentStatus() >= Canceling) {
4863       return;
4864     }
4865   }
4866 
4867   uint32_t cancelingTimeoutMillis =
4868       StaticPrefs::dom_worker_canceling_timeoutMilliseconds();
4869 
4870   RefPtr<CancelingTimerCallback> callback = new CancelingTimerCallback(this);
4871   nsresult rv = mCancelingTimer->InitWithCallback(
4872       callback, cancelingTimeoutMillis, nsITimer::TYPE_ONE_SHOT);
4873   if (NS_WARN_IF(NS_FAILED(rv))) {
4874     return;
4875   }
4876 
4877   errorCleanup.release();
4878 }
4879 
UpdateContextOptionsInternal(JSContext * aCx,const JS::ContextOptions & aContextOptions)4880 void WorkerPrivate::UpdateContextOptionsInternal(
4881     JSContext* aCx, const JS::ContextOptions& aContextOptions) {
4882   auto data = mWorkerThreadAccessible.Access();
4883 
4884   JS::ContextOptionsRef(aCx) = aContextOptions;
4885 
4886   for (uint32_t index = 0; index < data->mChildWorkers.Length(); index++) {
4887     data->mChildWorkers[index]->UpdateContextOptions(aContextOptions);
4888   }
4889 }
4890 
UpdateLanguagesInternal(const nsTArray<nsString> & aLanguages)4891 void WorkerPrivate::UpdateLanguagesInternal(
4892     const nsTArray<nsString>& aLanguages) {
4893   WorkerGlobalScope* globalScope = GlobalScope();
4894   RefPtr<WorkerNavigator> nav = globalScope->GetExistingNavigator();
4895   if (nav) {
4896     nav->SetLanguages(aLanguages);
4897   }
4898 
4899   auto data = mWorkerThreadAccessible.Access();
4900   for (uint32_t index = 0; index < data->mChildWorkers.Length(); index++) {
4901     data->mChildWorkers[index]->UpdateLanguages(aLanguages);
4902   }
4903 
4904   RefPtr<Event> event = NS_NewDOMEvent(globalScope, nullptr, nullptr);
4905 
4906   event->InitEvent(u"languagechange"_ns, false, false);
4907   event->SetTrusted(true);
4908 
4909   globalScope->DispatchEvent(*event);
4910 }
4911 
UpdateJSWorkerMemoryParameterInternal(JSContext * aCx,JSGCParamKey aKey,Maybe<uint32_t> aValue)4912 void WorkerPrivate::UpdateJSWorkerMemoryParameterInternal(
4913     JSContext* aCx, JSGCParamKey aKey, Maybe<uint32_t> aValue) {
4914   auto data = mWorkerThreadAccessible.Access();
4915 
4916   if (aValue) {
4917     JS_SetGCParameter(aCx, aKey, *aValue);
4918   } else {
4919     JS_ResetGCParameter(aCx, aKey);
4920   }
4921 
4922   for (uint32_t index = 0; index < data->mChildWorkers.Length(); index++) {
4923     data->mChildWorkers[index]->UpdateJSWorkerMemoryParameter(aKey, aValue);
4924   }
4925 }
4926 
4927 #ifdef JS_GC_ZEAL
UpdateGCZealInternal(JSContext * aCx,uint8_t aGCZeal,uint32_t aFrequency)4928 void WorkerPrivate::UpdateGCZealInternal(JSContext* aCx, uint8_t aGCZeal,
4929                                          uint32_t aFrequency) {
4930   auto data = mWorkerThreadAccessible.Access();
4931 
4932   JS_SetGCZeal(aCx, aGCZeal, aFrequency);
4933 
4934   for (uint32_t index = 0; index < data->mChildWorkers.Length(); index++) {
4935     data->mChildWorkers[index]->UpdateGCZeal(aGCZeal, aFrequency);
4936   }
4937 }
4938 #endif
4939 
SetLowMemoryStateInternal(JSContext * aCx,bool aState)4940 void WorkerPrivate::SetLowMemoryStateInternal(JSContext* aCx, bool aState) {
4941   auto data = mWorkerThreadAccessible.Access();
4942 
4943   JS::SetLowMemoryState(aCx, aState);
4944 
4945   for (uint32_t index = 0; index < data->mChildWorkers.Length(); index++) {
4946     data->mChildWorkers[index]->SetLowMemoryState(aState);
4947   }
4948 }
4949 
SetCCCollectedAnything(bool collectedAnything)4950 void WorkerPrivate::SetCCCollectedAnything(bool collectedAnything) {
4951   mWorkerThreadAccessible.Access()->mCCCollectedAnything = collectedAnything;
4952 }
4953 
GarbageCollectInternal(JSContext * aCx,bool aShrinking,bool aCollectChildren)4954 void WorkerPrivate::GarbageCollectInternal(JSContext* aCx, bool aShrinking,
4955                                            bool aCollectChildren) {
4956   // Perform GC followed by CC (the CC is triggered by
4957   // WorkerJSRuntime::CustomGCCallback at the end of the collection).
4958 
4959   auto data = mWorkerThreadAccessible.Access();
4960 
4961   if (!GlobalScope()) {
4962     // We haven't compiled anything yet. Just bail out.
4963     return;
4964   }
4965 
4966   if (aShrinking || aCollectChildren) {
4967     JS::PrepareForFullGC(aCx);
4968 
4969     if (aShrinking && mSyncLoopStack.IsEmpty()) {
4970       JS::NonIncrementalGC(aCx, JS::GCOptions::Shrink,
4971                            JS::GCReason::DOM_WORKER);
4972 
4973       // Check whether the CC collected anything and if so GC again. This is
4974       // necessary to collect all garbage.
4975       if (data->mCCCollectedAnything) {
4976         JS::NonIncrementalGC(aCx, JS::GCOptions::Normal,
4977                              JS::GCReason::DOM_WORKER);
4978       }
4979 
4980       if (!aCollectChildren) {
4981         LOG(WorkerLog(), ("Worker %p collected idle garbage\n", this));
4982       }
4983     } else {
4984       JS::NonIncrementalGC(aCx, JS::GCOptions::Normal,
4985                            JS::GCReason::DOM_WORKER);
4986       LOG(WorkerLog(), ("Worker %p collected garbage\n", this));
4987     }
4988   } else {
4989     JS_MaybeGC(aCx);
4990     LOG(WorkerLog(), ("Worker %p collected periodic garbage\n", this));
4991   }
4992 
4993   if (aCollectChildren) {
4994     for (uint32_t index = 0; index < data->mChildWorkers.Length(); index++) {
4995       data->mChildWorkers[index]->GarbageCollect(aShrinking);
4996     }
4997   }
4998 }
4999 
CycleCollectInternal(bool aCollectChildren)5000 void WorkerPrivate::CycleCollectInternal(bool aCollectChildren) {
5001   auto data = mWorkerThreadAccessible.Access();
5002 
5003   nsCycleCollector_collect(nullptr);
5004 
5005   if (aCollectChildren) {
5006     for (uint32_t index = 0; index < data->mChildWorkers.Length(); index++) {
5007       data->mChildWorkers[index]->CycleCollect();
5008     }
5009   }
5010 }
5011 
MemoryPressureInternal()5012 void WorkerPrivate::MemoryPressureInternal() {
5013   auto data = mWorkerThreadAccessible.Access();
5014 
5015   if (data->mScope) {
5016     RefPtr<Console> console = data->mScope->GetConsoleIfExists();
5017     if (console) {
5018       console->ClearStorage();
5019     }
5020 
5021     RefPtr<Performance> performance = data->mScope->GetPerformanceIfExists();
5022     if (performance) {
5023       performance->MemoryPressure();
5024     }
5025 
5026     data->mScope->RemoveReportRecords();
5027   }
5028 
5029   if (data->mDebuggerScope) {
5030     RefPtr<Console> console = data->mDebuggerScope->GetConsoleIfExists();
5031     if (console) {
5032       console->ClearStorage();
5033     }
5034   }
5035 
5036   for (uint32_t index = 0; index < data->mChildWorkers.Length(); index++) {
5037     data->mChildWorkers[index]->MemoryPressure();
5038   }
5039 }
5040 
SetThread(WorkerThread * aThread)5041 void WorkerPrivate::SetThread(WorkerThread* aThread) {
5042   if (aThread) {
5043 #ifdef DEBUG
5044     {
5045       bool isOnCurrentThread;
5046       MOZ_ASSERT(NS_SUCCEEDED(aThread->IsOnCurrentThread(&isOnCurrentThread)));
5047       MOZ_ASSERT(!isOnCurrentThread);
5048     }
5049 #endif
5050 
5051     MOZ_ASSERT(!mPRThread);
5052     mPRThread = PRThreadFromThread(aThread);
5053     MOZ_ASSERT(mPRThread);
5054 
5055     mWorkerThreadAccessible.Transfer(mPRThread);
5056   } else {
5057     MOZ_ASSERT(mPRThread);
5058   }
5059 }
5060 
SetWorkerPrivateInWorkerThread(WorkerThread * const aThread)5061 void WorkerPrivate::SetWorkerPrivateInWorkerThread(
5062     WorkerThread* const aThread) {
5063   MutexAutoLock lock(mMutex);
5064 
5065   MOZ_ASSERT(!mThread);
5066   MOZ_ASSERT(mStatus == Pending);
5067 
5068   mThread = aThread;
5069   mThread->SetWorker(WorkerThreadFriendKey{}, this);
5070 
5071   if (!mPreStartRunnables.IsEmpty()) {
5072     for (uint32_t index = 0; index < mPreStartRunnables.Length(); index++) {
5073       MOZ_ALWAYS_SUCCEEDS(mThread->DispatchAnyThread(
5074           WorkerThreadFriendKey{}, mPreStartRunnables[index].forget()));
5075     }
5076     mPreStartRunnables.Clear();
5077   }
5078 }
5079 
ResetWorkerPrivateInWorkerThread()5080 void WorkerPrivate::ResetWorkerPrivateInWorkerThread() {
5081   RefPtr<WorkerThread> doomedThread;
5082 
5083   // Release the mutex before doomedThread.
5084   MutexAutoLock lock(mMutex);
5085 
5086   MOZ_ASSERT(mThread);
5087 
5088   mThread->SetWorker(WorkerThreadFriendKey{}, nullptr);
5089   mThread.swap(doomedThread);
5090 }
5091 
BeginCTypesCall()5092 void WorkerPrivate::BeginCTypesCall() {
5093   AssertIsOnWorkerThread();
5094   auto data = mWorkerThreadAccessible.Access();
5095 
5096   // Don't try to GC while we're blocked in a ctypes call.
5097   SetGCTimerMode(NoTimer);
5098 
5099   data->mYieldJSThreadExecution.EmplaceBack();
5100 }
5101 
EndCTypesCall()5102 void WorkerPrivate::EndCTypesCall() {
5103   AssertIsOnWorkerThread();
5104   auto data = mWorkerThreadAccessible.Access();
5105 
5106   data->mYieldJSThreadExecution.RemoveLastElement();
5107 
5108   // Make sure the periodic timer is running before we start running JS again.
5109   SetGCTimerMode(PeriodicTimer);
5110 }
5111 
BeginCTypesCallback()5112 void WorkerPrivate::BeginCTypesCallback() {
5113   AssertIsOnWorkerThread();
5114 
5115   // Make sure the periodic timer is running before we start running JS again.
5116   SetGCTimerMode(PeriodicTimer);
5117 
5118   // Re-requesting execution is not needed since the JSRuntime code calling
5119   // this will do an AutoEntryScript.
5120 }
5121 
EndCTypesCallback()5122 void WorkerPrivate::EndCTypesCallback() {
5123   AssertIsOnWorkerThread();
5124 
5125   // Don't try to GC while we're blocked in a ctypes call.
5126   SetGCTimerMode(NoTimer);
5127 }
5128 
ConnectMessagePort(JSContext * aCx,UniqueMessagePortId & aIdentifier)5129 bool WorkerPrivate::ConnectMessagePort(JSContext* aCx,
5130                                        UniqueMessagePortId& aIdentifier) {
5131   AssertIsOnWorkerThread();
5132 
5133   WorkerGlobalScope* globalScope = GlobalScope();
5134 
5135   JS::Rooted<JSObject*> jsGlobal(aCx, globalScope->GetWrapper());
5136   MOZ_ASSERT(jsGlobal);
5137 
5138   // This UniqueMessagePortId is used to create a new port, still connected
5139   // with the other one, but in the worker thread.
5140   ErrorResult rv;
5141   RefPtr<MessagePort> port = MessagePort::Create(globalScope, aIdentifier, rv);
5142   if (NS_WARN_IF(rv.Failed())) {
5143     rv.SuppressException();
5144     return false;
5145   }
5146 
5147   GlobalObject globalObject(aCx, jsGlobal);
5148   if (globalObject.Failed()) {
5149     return false;
5150   }
5151 
5152   RootedDictionary<MessageEventInit> init(aCx);
5153   init.mData = JS_GetEmptyStringValue(aCx);
5154   init.mBubbles = false;
5155   init.mCancelable = false;
5156   init.mSource.SetValue().SetAsMessagePort() = port;
5157   if (!init.mPorts.AppendElement(port.forget(), fallible)) {
5158     return false;
5159   }
5160 
5161   RefPtr<MessageEvent> event =
5162       MessageEvent::Constructor(globalObject, u"connect"_ns, init);
5163 
5164   event->SetTrusted(true);
5165 
5166   globalScope->DispatchEvent(*event);
5167 
5168   return true;
5169 }
5170 
GetOrCreateGlobalScope(JSContext * aCx)5171 WorkerGlobalScope* WorkerPrivate::GetOrCreateGlobalScope(JSContext* aCx) {
5172   auto data = mWorkerThreadAccessible.Access();
5173 
5174   if (data->mScope) {
5175     return data->mScope;
5176   }
5177 
5178   if (IsSharedWorker()) {
5179     data->mScope = new SharedWorkerGlobalScope(
5180         WrapNotNull(this), CreateClientSource(), WorkerName());
5181   } else if (IsServiceWorker()) {
5182     data->mScope =
5183         new ServiceWorkerGlobalScope(WrapNotNull(this), CreateClientSource(),
5184                                      GetServiceWorkerRegistrationDescriptor());
5185   } else {
5186     data->mScope = new DedicatedWorkerGlobalScope(
5187         WrapNotNull(this), CreateClientSource(), WorkerName());
5188   }
5189 
5190   JS::Rooted<JSObject*> global(aCx);
5191   NS_ENSURE_TRUE(data->mScope->WrapGlobalObject(aCx, &global), nullptr);
5192 
5193   JSAutoRealm ar(aCx, global);
5194 
5195   if (!RegisterBindings(aCx, global)) {
5196     data->mScope = nullptr;
5197     return nullptr;
5198   }
5199 
5200   JS_FireOnNewGlobalObject(aCx, global);
5201 
5202   return data->mScope;
5203 }
5204 
CreateDebuggerGlobalScope(JSContext * aCx)5205 WorkerDebuggerGlobalScope* WorkerPrivate::CreateDebuggerGlobalScope(
5206     JSContext* aCx) {
5207   auto data = mWorkerThreadAccessible.Access();
5208   MOZ_ASSERT(!data->mDebuggerScope);
5209 
5210   // The debugger global gets a dummy client, not the "real" client used by the
5211   // debugee worker.
5212   auto clientSource = ClientManager::CreateSource(
5213       GetClientType(), HybridEventTarget(), NullPrincipalInfo());
5214 
5215   data->mDebuggerScope =
5216       new WorkerDebuggerGlobalScope(WrapNotNull(this), std::move(clientSource));
5217 
5218   JS::Rooted<JSObject*> global(aCx);
5219   NS_ENSURE_TRUE(data->mDebuggerScope->WrapGlobalObject(aCx, &global), nullptr);
5220 
5221   JSAutoRealm ar(aCx, global);
5222 
5223   if (!RegisterDebuggerBindings(aCx, global)) {
5224     data->mDebuggerScope = nullptr;
5225     return nullptr;
5226   }
5227 
5228   JS_FireOnNewGlobalObject(aCx, global);
5229 
5230   return data->mDebuggerScope;
5231 }
5232 
IsOnWorkerThread() const5233 bool WorkerPrivate::IsOnWorkerThread() const {
5234   // We can't use mThread because it must be protected by mMutex and sometimes
5235   // this method is called when mMutex is already locked. This method should
5236   // always work.
5237   MOZ_ASSERT(mPRThread,
5238              "AssertIsOnWorkerThread() called before a thread was assigned!");
5239 
5240   return mPRThread == PR_GetCurrentThread();
5241 }
5242 
5243 #ifdef DEBUG
AssertIsOnWorkerThread() const5244 void WorkerPrivate::AssertIsOnWorkerThread() const {
5245   MOZ_ASSERT(IsOnWorkerThread());
5246 }
5247 #endif  // DEBUG
5248 
DumpCrashInformation(nsACString & aString)5249 void WorkerPrivate::DumpCrashInformation(nsACString& aString) {
5250   auto data = mWorkerThreadAccessible.Access();
5251 
5252   aString.Append("IsChromeWorker(");
5253   if (IsChromeWorker()) {
5254     aString.Append(NS_ConvertUTF16toUTF8(ScriptURL()));
5255   } else {
5256     aString.Append("false");
5257   }
5258   aString.Append(")");
5259   for (const auto* workerRef : data->mWorkerRefs.NonObservingRange()) {
5260     if (workerRef->IsPreventingShutdown()) {
5261       aString.Append("|");
5262       aString.Append(workerRef->Name());
5263     }
5264   }
5265 }
5266 
GetPerformanceStorage()5267 PerformanceStorage* WorkerPrivate::GetPerformanceStorage() {
5268   AssertIsOnMainThread();
5269   MOZ_ASSERT(mPerformanceStorage);
5270   return mPerformanceStorage;
5271 }
5272 
SetRemoteWorkerController(RemoteWorkerChild * aController)5273 void WorkerPrivate::SetRemoteWorkerController(RemoteWorkerChild* aController) {
5274   AssertIsOnMainThread();
5275   MOZ_ASSERT(aController);
5276   MOZ_ASSERT(!mRemoteWorkerController);
5277 
5278   mRemoteWorkerController = aController;
5279 }
5280 
GetRemoteWorkerController()5281 RemoteWorkerChild* WorkerPrivate::GetRemoteWorkerController() {
5282   AssertIsOnMainThread();
5283   MOZ_ASSERT(mRemoteWorkerController);
5284   return mRemoteWorkerController;
5285 }
5286 
SetRemoteWorkerControllerWeakRef(ThreadSafeWeakPtr<RemoteWorkerChild> aWeakRef)5287 void WorkerPrivate::SetRemoteWorkerControllerWeakRef(
5288     ThreadSafeWeakPtr<RemoteWorkerChild> aWeakRef) {
5289   MOZ_ASSERT(!aWeakRef.IsNull());
5290   MOZ_ASSERT(mRemoteWorkerControllerWeakRef.IsNull());
5291   MOZ_ASSERT(IsServiceWorker());
5292 
5293   mRemoteWorkerControllerWeakRef = std::move(aWeakRef);
5294 }
5295 
5296 ThreadSafeWeakPtr<RemoteWorkerChild>
GetRemoteWorkerControllerWeakRef()5297 WorkerPrivate::GetRemoteWorkerControllerWeakRef() {
5298   MOZ_ASSERT(IsServiceWorker());
5299   return mRemoteWorkerControllerWeakRef;
5300 }
5301 
SetServiceWorkerSkipWaitingFlag()5302 RefPtr<GenericPromise> WorkerPrivate::SetServiceWorkerSkipWaitingFlag() {
5303   AssertIsOnWorkerThread();
5304   MOZ_ASSERT(IsServiceWorker());
5305 
5306   RefPtr<RemoteWorkerChild> rwc(mRemoteWorkerControllerWeakRef);
5307 
5308   if (!rwc) {
5309     return GenericPromise::CreateAndReject(NS_ERROR_DOM_ABORT_ERR, __func__);
5310   }
5311 
5312   RefPtr<GenericPromise> promise =
5313       rwc->MaybeSendSetServiceWorkerSkipWaitingFlag();
5314 
5315   NS_ProxyRelease("WorkerPrivate::mRemoteWorkerControllerWeakRef",
5316                   RemoteWorkerService::Thread(), rwc.forget());
5317 
5318   return promise;
5319 }
5320 
Id()5321 const nsAString& WorkerPrivate::Id() {
5322   AssertIsOnMainThread();
5323 
5324   if (mId.IsEmpty()) {
5325     mId = ComputeWorkerPrivateId();
5326   }
5327 
5328   MOZ_ASSERT(!mId.IsEmpty());
5329 
5330   return mId;
5331 }
5332 
IsSharedMemoryAllowed() const5333 bool WorkerPrivate::IsSharedMemoryAllowed() const {
5334   if (StaticPrefs::
5335           dom_postMessage_sharedArrayBuffer_bypassCOOP_COEP_insecure_enabled()) {
5336     return true;
5337   }
5338 
5339   if (mIsPrivilegedAddonGlobal) {
5340     return true;
5341   }
5342 
5343   return CrossOriginIsolated();
5344 }
5345 
CrossOriginIsolated() const5346 bool WorkerPrivate::CrossOriginIsolated() const {
5347   if (!StaticPrefs::
5348           dom_postMessage_sharedArrayBuffer_withCOOP_COEP_AtStartup()) {
5349     return false;
5350   }
5351 
5352   return mAgentClusterOpenerPolicy ==
5353          nsILoadInfo::OPENER_POLICY_SAME_ORIGIN_EMBEDDER_POLICY_REQUIRE_CORP;
5354 }
5355 
GetEmbedderPolicy() const5356 nsILoadInfo::CrossOriginEmbedderPolicy WorkerPrivate::GetEmbedderPolicy()
5357     const {
5358   if (!StaticPrefs::browser_tabs_remote_useCrossOriginEmbedderPolicy()) {
5359     return nsILoadInfo::EMBEDDER_POLICY_NULL;
5360   }
5361 
5362   return mEmbedderPolicy.valueOr(nsILoadInfo::EMBEDDER_POLICY_NULL);
5363 }
5364 
SetEmbedderPolicy(nsILoadInfo::CrossOriginEmbedderPolicy aPolicy)5365 Result<Ok, nsresult> WorkerPrivate::SetEmbedderPolicy(
5366     nsILoadInfo::CrossOriginEmbedderPolicy aPolicy) {
5367   MOZ_ASSERT(NS_IsMainThread());
5368   MOZ_ASSERT(mEmbedderPolicy.isNothing());
5369 
5370   if (!StaticPrefs::browser_tabs_remote_useCrossOriginEmbedderPolicy()) {
5371     return Ok();
5372   }
5373 
5374   // If owner's emebedder policy is corp_reqired, aPolicy must also be
5375   // corp_reqired. But if owner's embedder policy is null, aPolicy needs not
5376   // match owner's value.
5377   // https://wicg.github.io/cross-origin-embedder-policy/#cascade-vs-require
5378   EnsureOwnerEmbedderPolicy();
5379   if (mOwnerEmbedderPolicy.valueOr(nsILoadInfo::EMBEDDER_POLICY_NULL) !=
5380       nsILoadInfo::EMBEDDER_POLICY_NULL) {
5381     if (mOwnerEmbedderPolicy.valueOr(aPolicy) != aPolicy) {
5382       return Err(NS_ERROR_BLOCKED_BY_POLICY);
5383     }
5384   }
5385 
5386   mEmbedderPolicy.emplace(aPolicy);
5387 
5388   return Ok();
5389 }
5390 
InheritOwnerEmbedderPolicyOrNull(nsIRequest * aRequest)5391 void WorkerPrivate::InheritOwnerEmbedderPolicyOrNull(nsIRequest* aRequest) {
5392   MOZ_ASSERT(NS_IsMainThread());
5393   MOZ_ASSERT(aRequest);
5394 
5395   EnsureOwnerEmbedderPolicy();
5396 
5397   if (mOwnerEmbedderPolicy.isSome()) {
5398     nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
5399     MOZ_ASSERT(channel);
5400 
5401     nsCOMPtr<nsIURI> scriptURI;
5402     MOZ_ALWAYS_SUCCEEDS(channel->GetURI(getter_AddRefs(scriptURI)));
5403 
5404     bool isLocalScriptURI = false;
5405     MOZ_ALWAYS_SUCCEEDS(NS_URIChainHasFlags(
5406         scriptURI, nsIProtocolHandler::URI_IS_LOCAL_RESOURCE,
5407         &isLocalScriptURI));
5408 
5409     MOZ_RELEASE_ASSERT(isLocalScriptURI);
5410   }
5411 
5412   mEmbedderPolicy.emplace(
5413       mOwnerEmbedderPolicy.valueOr(nsILoadInfo::EMBEDDER_POLICY_NULL));
5414 }
5415 
MatchEmbedderPolicy(nsILoadInfo::CrossOriginEmbedderPolicy aPolicy) const5416 bool WorkerPrivate::MatchEmbedderPolicy(
5417     nsILoadInfo::CrossOriginEmbedderPolicy aPolicy) const {
5418   MOZ_ASSERT(NS_IsMainThread());
5419 
5420   if (!StaticPrefs::browser_tabs_remote_useCrossOriginEmbedderPolicy()) {
5421     return true;
5422   }
5423 
5424   return mEmbedderPolicy.value() == aPolicy;
5425 }
5426 
GetOwnerEmbedderPolicy() const5427 nsILoadInfo::CrossOriginEmbedderPolicy WorkerPrivate::GetOwnerEmbedderPolicy()
5428     const {
5429   if (!StaticPrefs::browser_tabs_remote_useCrossOriginEmbedderPolicy()) {
5430     return nsILoadInfo::EMBEDDER_POLICY_NULL;
5431   }
5432 
5433   return mOwnerEmbedderPolicy.valueOr(nsILoadInfo::EMBEDDER_POLICY_NULL);
5434 }
5435 
EnsureOwnerEmbedderPolicy()5436 void WorkerPrivate::EnsureOwnerEmbedderPolicy() {
5437   MOZ_ASSERT(NS_IsMainThread());
5438   MOZ_ASSERT(mOwnerEmbedderPolicy.isNothing());
5439 
5440   if (GetParent()) {
5441     mOwnerEmbedderPolicy.emplace(GetParent()->GetEmbedderPolicy());
5442   } else if (GetWindow() && GetWindow()->GetWindowContext()) {
5443     mOwnerEmbedderPolicy.emplace(
5444         GetWindow()->GetWindowContext()->GetEmbedderPolicy());
5445   }
5446 }
5447 
5448 const mozilla::ipc::PrincipalInfo&
GetEffectiveStoragePrincipalInfo() const5449 WorkerPrivate::GetEffectiveStoragePrincipalInfo() const {
5450   AssertIsOnWorkerThread();
5451 
5452   if (mLoadInfo.mUseRegularPrincipal) {
5453     return *mLoadInfo.mPrincipalInfo;
5454   }
5455 
5456   return *mLoadInfo.mPartitionedPrincipalInfo;
5457 }
5458 
EffectiveStoragePrincipalOrigin() const5459 const nsACString& WorkerPrivate::EffectiveStoragePrincipalOrigin() const {
5460   AssertIsOnWorkerThread();
5461 
5462   if (mLoadInfo.mUseRegularPrincipal) {
5463     return mLoadInfo.mOrigin;
5464   }
5465 
5466   return mLoadInfo.mPartitionedOrigin;
5467 }
5468 
5469 NS_IMPL_ADDREF(WorkerPrivate::EventTarget)
5470 NS_IMPL_RELEASE(WorkerPrivate::EventTarget)
5471 
5472 NS_INTERFACE_MAP_BEGIN(WorkerPrivate::EventTarget)
5473   NS_INTERFACE_MAP_ENTRY(nsISerialEventTarget)
5474   NS_INTERFACE_MAP_ENTRY(nsIEventTarget)
5475   NS_INTERFACE_MAP_ENTRY(nsISupports)
5476 #ifdef DEBUG
5477   // kDEBUGWorkerEventTargetIID is special in that it does not AddRef its
5478   // result.
5479   if (aIID.Equals(kDEBUGWorkerEventTargetIID)) {
5480     *aInstancePtr = this;
5481     return NS_OK;
5482   } else
5483 #endif
5484 NS_INTERFACE_MAP_END
5485 
5486 NS_IMETHODIMP
DispatchFromScript(nsIRunnable * aRunnable,uint32_t aFlags)5487 WorkerPrivate::EventTarget::DispatchFromScript(nsIRunnable* aRunnable,
5488                                                uint32_t aFlags) {
5489   nsCOMPtr<nsIRunnable> event(aRunnable);
5490   return Dispatch(event.forget(), aFlags);
5491 }
5492 
5493 NS_IMETHODIMP
Dispatch(already_AddRefed<nsIRunnable> aRunnable,uint32_t aFlags)5494 WorkerPrivate::EventTarget::Dispatch(already_AddRefed<nsIRunnable> aRunnable,
5495                                      uint32_t aFlags) {
5496   // May be called on any thread!
5497   nsCOMPtr<nsIRunnable> event(aRunnable);
5498 
5499   // Workers only support asynchronous dispatch for now.
5500   if (NS_WARN_IF(aFlags != NS_DISPATCH_NORMAL)) {
5501     return NS_ERROR_UNEXPECTED;
5502   }
5503 
5504   RefPtr<WorkerRunnable> workerRunnable;
5505 
5506   MutexAutoLock lock(mMutex);
5507 
5508   if (!mWorkerPrivate) {
5509     NS_WARNING(
5510         "A runnable was posted to a worker that is already shutting "
5511         "down!");
5512     return NS_ERROR_UNEXPECTED;
5513   }
5514 
5515   if (event) {
5516     workerRunnable = mWorkerPrivate->MaybeWrapAsWorkerRunnable(event.forget());
5517   }
5518 
5519   nsresult rv =
5520       mWorkerPrivate->Dispatch(workerRunnable.forget(), mNestedEventTarget);
5521   if (NS_WARN_IF(NS_FAILED(rv))) {
5522     return rv;
5523   }
5524 
5525   return NS_OK;
5526 }
5527 
5528 NS_IMETHODIMP
DelayedDispatch(already_AddRefed<nsIRunnable>,uint32_t)5529 WorkerPrivate::EventTarget::DelayedDispatch(already_AddRefed<nsIRunnable>,
5530                                             uint32_t)
5531 
5532 {
5533   return NS_ERROR_NOT_IMPLEMENTED;
5534 }
5535 
5536 NS_IMETHODIMP
IsOnCurrentThread(bool * aIsOnCurrentThread)5537 WorkerPrivate::EventTarget::IsOnCurrentThread(bool* aIsOnCurrentThread) {
5538   // May be called on any thread!
5539 
5540   MOZ_ASSERT(aIsOnCurrentThread);
5541 
5542   MutexAutoLock lock(mMutex);
5543 
5544   if (!mWorkerPrivate) {
5545     NS_WARNING("A worker's event target was used after the worker has !");
5546     return NS_ERROR_UNEXPECTED;
5547   }
5548 
5549   *aIsOnCurrentThread = mWorkerPrivate->IsOnCurrentThread();
5550   return NS_OK;
5551 }
5552 
NS_IMETHODIMP_(bool)5553 NS_IMETHODIMP_(bool)
5554 WorkerPrivate::EventTarget::IsOnCurrentThreadInfallible() {
5555   // May be called on any thread!
5556 
5557   MutexAutoLock lock(mMutex);
5558 
5559   if (!mWorkerPrivate) {
5560     NS_WARNING("A worker's event target was used after the worker has !");
5561     return false;
5562   }
5563 
5564   return mWorkerPrivate->IsOnCurrentThread();
5565 }
5566 
AutoPushEventLoopGlobal(WorkerPrivate * aWorkerPrivate,JSContext * aCx)5567 WorkerPrivate::AutoPushEventLoopGlobal::AutoPushEventLoopGlobal(
5568     WorkerPrivate* aWorkerPrivate, JSContext* aCx)
5569     : mWorkerPrivate(aWorkerPrivate) {
5570   auto data = mWorkerPrivate->mWorkerThreadAccessible.Access();
5571   mOldEventLoopGlobal = std::move(data->mCurrentEventLoopGlobal);
5572   if (JSObject* global = JS::CurrentGlobalOrNull(aCx)) {
5573     data->mCurrentEventLoopGlobal = xpc::NativeGlobal(global);
5574   }
5575 }
5576 
~AutoPushEventLoopGlobal()5577 WorkerPrivate::AutoPushEventLoopGlobal::~AutoPushEventLoopGlobal() {
5578   auto data = mWorkerPrivate->mWorkerThreadAccessible.Access();
5579   data->mCurrentEventLoopGlobal = std::move(mOldEventLoopGlobal);
5580 }
5581 
5582 }  // namespace dom
5583 }  // namespace mozilla
5584