LCOV - code coverage report
Current view: top level - dom/workers - WorkerPrivate.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 386 2303 16.8 %
Date: 2018-08-07 16:35:00 Functions: 0 0 -
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13-14-ga5dd952