dom/workers/WorkerPrivate.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

     1 /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
     2 /* vim: set ts=2 et sw=2 tw=80: */
     3 /* This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 #include "WorkerPrivate.h"
     9 #include "amIAddonManager.h"
    10 #include "nsIClassInfo.h"
    11 #include "nsIContentSecurityPolicy.h"
    12 #include "nsIConsoleService.h"
    13 #include "nsIDOMDOMException.h"
    14 #include "nsIDOMEvent.h"
    15 #include "nsIDOMFile.h"
    16 #include "nsIDOMMessageEvent.h"
    17 #include "nsIDocument.h"
    18 #include "nsIDocShell.h"
    19 #include "nsIMemoryReporter.h"
    20 #include "nsIPermissionManager.h"
    21 #include "nsIScriptError.h"
    22 #include "nsIScriptGlobalObject.h"
    23 #include "nsIScriptSecurityManager.h"
    24 #include "nsPIDOMWindow.h"
    25 #include "nsITextToSubURI.h"
    26 #include "nsIThreadInternal.h"
    27 #include "nsITimer.h"
    28 #include "nsIURI.h"
    29 #include "nsIURL.h"
    30 #include "nsIXPConnect.h"
    32 #include <algorithm>
    33 #include "jsfriendapi.h"
    34 #include "js/OldDebugAPI.h"
    35 #include "js/MemoryMetrics.h"
    36 #include "mozilla/Assertions.h"
    37 #include "mozilla/ContentEvents.h"
    38 #include "mozilla/EventDispatcher.h"
    39 #include "mozilla/Likely.h"
    40 #include "mozilla/dom/BindingUtils.h"
    41 #include "mozilla/dom/ErrorEvent.h"
    42 #include "mozilla/dom/ErrorEventBinding.h"
    43 #include "mozilla/dom/Exceptions.h"
    44 #include "mozilla/dom/FunctionBinding.h"
    45 #include "mozilla/dom/ImageData.h"
    46 #include "mozilla/dom/ImageDataBinding.h"
    47 #include "mozilla/dom/MessageEvent.h"
    48 #include "mozilla/dom/MessageEventBinding.h"
    49 #include "mozilla/dom/MessagePortList.h"
    50 #include "mozilla/dom/WorkerBinding.h"
    51 #include "mozilla/Preferences.h"
    52 #include "nsAlgorithm.h"
    53 #include "nsContentUtils.h"
    54 #include "nsCxPusher.h"
    55 #include "nsError.h"
    56 #include "nsDOMJSUtils.h"
    57 #include "nsHostObjectProtocolHandler.h"
    58 #include "nsJSEnvironment.h"
    59 #include "nsJSUtils.h"
    60 #include "nsNetUtil.h"
    61 #include "nsPrintfCString.h"
    62 #include "nsProxyRelease.h"
    63 #include "nsSandboxFlags.h"
    64 #include "xpcpublic.h"
    66 #ifdef ANDROID
    67 #include <android/log.h>
    68 #endif
    70 #ifdef DEBUG
    71 #include "nsThreadManager.h"
    72 #endif
    74 #include "File.h"
    75 #include "MessagePort.h"
    76 #include "Navigator.h"
    77 #include "Principal.h"
    78 #include "RuntimeService.h"
    79 #include "ScriptLoader.h"
    80 #include "SharedWorker.h"
    81 #include "WorkerFeature.h"
    82 #include "WorkerRunnable.h"
    83 #include "WorkerScope.h"
    85 // JS_MaybeGC will run once every second during normal execution.
    86 #define PERIODIC_GC_TIMER_DELAY_SEC 1
    88 // A shrinking GC will run five seconds after the last event is processed.
    89 #define IDLE_GC_TIMER_DELAY_SEC 5
    91 #define PREF_WORKERS_ENABLED "dom.workers.enabled"
    93 #ifdef WORKER_LOGGING
    94 #define LOG(_args) do { printf _args ; fflush(stdout); } while (0)
    95 #else
    96 #define LOG(_args) do { } while (0)
    97 #endif
    99 using namespace mozilla;
   100 using namespace mozilla::dom;
   101 USING_WORKERS_NAMESPACE
   103 MOZ_DEFINE_MALLOC_SIZE_OF(JsWorkerMallocSizeOf)
   105 #ifdef DEBUG
   107 BEGIN_WORKERS_NAMESPACE
   109 void
   110 AssertIsOnMainThread()
   111 {
   112   MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
   113 }
   115 END_WORKERS_NAMESPACE
   117 #endif
   119 namespace {
   121 #ifdef DEBUG
   123 const nsIID kDEBUGWorkerEventTargetIID = {
   124   0xccaba3fa, 0x5be2, 0x4de2, { 0xba, 0x87, 0x3b, 0x3b, 0x5b, 0x1d, 0x5, 0xfb }
   125 };
   127 #endif
   129 template <class T>
   130 class AutoPtrComparator
   131 {
   132   typedef nsAutoPtr<T> A;
   133   typedef T* B;
   135 public:
   136   bool Equals(const A& a, const B& b) const {
   137     return a && b ? *a == *b : !a && !b ? true : false;
   138   }
   139   bool LessThan(const A& a, const B& b) const {
   140     return a && b ? *a < *b : b ? true : false;
   141   }
   142 };
   144 template <class T>
   145 inline AutoPtrComparator<T>
   146 GetAutoPtrComparator(const nsTArray<nsAutoPtr<T> >&)
   147 {
   148   return AutoPtrComparator<T>();
   149 }
   151 // Specialize this if there's some class that has multiple nsISupports bases.
   152 template <class T>
   153 struct ISupportsBaseInfo
   154 {
   155   typedef T ISupportsBase;
   156 };
   158 template <template <class> class SmartPtr, class T>
   159 inline void
   160 SwapToISupportsArray(SmartPtr<T>& aSrc,
   161                      nsTArray<nsCOMPtr<nsISupports> >& aDest)
   162 {
   163   nsCOMPtr<nsISupports>* dest = aDest.AppendElement();
   165   T* raw = nullptr;
   166   aSrc.swap(raw);
   168   nsISupports* rawSupports =
   169     static_cast<typename ISupportsBaseInfo<T>::ISupportsBase*>(raw);
   170   dest->swap(rawSupports);
   171 }
   173 // This class is used to wrap any runnables that the worker receives via the
   174 // nsIEventTarget::Dispatch() method (either from NS_DispatchToCurrentThread or
   175 // from the worker's EventTarget).
   176 class ExternalRunnableWrapper MOZ_FINAL : public WorkerRunnable
   177 {
   178   nsCOMPtr<nsICancelableRunnable> mWrappedRunnable;
   180 public:
   181   ExternalRunnableWrapper(WorkerPrivate* aWorkerPrivate,
   182                           nsICancelableRunnable* aWrappedRunnable)
   183   : WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
   184     mWrappedRunnable(aWrappedRunnable)
   185   {
   186     MOZ_ASSERT(aWorkerPrivate);
   187     MOZ_ASSERT(aWrappedRunnable);
   188   }
   190   NS_DECL_ISUPPORTS_INHERITED
   192 private:
   193   ~ExternalRunnableWrapper()
   194   { }
   196   virtual bool
   197   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
   198   {
   199     nsresult rv = mWrappedRunnable->Run();
   200     if (NS_FAILED(rv)) {
   201       if (!JS_IsExceptionPending(aCx)) {
   202         Throw(aCx, rv);
   203       }
   204       return false;
   205     }
   206     return true;
   207   }
   209   NS_IMETHOD
   210   Cancel() MOZ_OVERRIDE
   211   {
   212     nsresult rv = mWrappedRunnable->Cancel();
   213     nsresult rv2 = WorkerRunnable::Cancel();
   214     return NS_FAILED(rv) ? rv : rv2;
   215   }
   216 };
   218 struct WindowAction
   219 {
   220   nsPIDOMWindow* mWindow;
   221   JSContext* mJSContext;
   222   bool mDefaultAction;
   224   WindowAction(nsPIDOMWindow* aWindow, JSContext* aJSContext)
   225   : mWindow(aWindow), mJSContext(aJSContext), mDefaultAction(true)
   226   {
   227     MOZ_ASSERT(aJSContext);
   228   }
   230   WindowAction(nsPIDOMWindow* aWindow)
   231   : mWindow(aWindow), mJSContext(nullptr), mDefaultAction(true)
   232   { }
   234   bool
   235   operator==(const WindowAction& aOther) const
   236   {
   237     return mWindow == aOther.mWindow;
   238   }
   239 };
   241 void
   242 LogErrorToConsole(const nsAString& aMessage,
   243                   const nsAString& aFilename,
   244                   const nsAString& aLine,
   245                   uint32_t aLineNumber,
   246                   uint32_t aColumnNumber,
   247                   uint32_t aFlags,
   248                   uint64_t aInnerWindowId)
   249 {
   250   AssertIsOnMainThread();
   252   nsCOMPtr<nsIScriptError> scriptError =
   253     do_CreateInstance(NS_SCRIPTERROR_CONTRACTID);
   254   NS_WARN_IF_FALSE(scriptError, "Failed to create script error!");
   256   if (scriptError) {
   257     if (NS_FAILED(scriptError->InitWithWindowID(aMessage, aFilename, aLine,
   258                                                 aLineNumber, aColumnNumber,
   259                                                 aFlags, "Web Worker",
   260                                                 aInnerWindowId))) {
   261       NS_WARNING("Failed to init script error!");
   262       scriptError = nullptr;
   263     }
   264   }
   266   nsCOMPtr<nsIConsoleService> consoleService =
   267     do_GetService(NS_CONSOLESERVICE_CONTRACTID);
   268   NS_WARN_IF_FALSE(consoleService, "Failed to get console service!");
   270   if (consoleService) {
   271     if (scriptError) {
   272       if (NS_SUCCEEDED(consoleService->LogMessage(scriptError))) {
   273         return;
   274       }
   275       NS_WARNING("LogMessage failed!");
   276     } else if (NS_SUCCEEDED(consoleService->LogStringMessage(
   277                                                     aMessage.BeginReading()))) {
   278       return;
   279     }
   280     NS_WARNING("LogStringMessage failed!");
   281   }
   283   NS_ConvertUTF16toUTF8 msg(aMessage);
   284   NS_ConvertUTF16toUTF8 filename(aFilename);
   286   static const char kErrorString[] = "JS error in Web Worker: %s [%s:%u]";
   288 #ifdef ANDROID
   289   __android_log_print(ANDROID_LOG_INFO, "Gecko", kErrorString, msg.get(),
   290                       filename.get(), aLineNumber);
   291 #endif
   293   fprintf(stderr, kErrorString, msg.get(), filename.get(), aLineNumber);
   294   fflush(stderr);
   295 }
   297 struct WorkerStructuredCloneCallbacks
   298 {
   299   static JSObject*
   300   Read(JSContext* aCx, JSStructuredCloneReader* aReader, uint32_t aTag,
   301        uint32_t aData, void* aClosure)
   302   {
   303     // See if object is a nsIDOMFile pointer.
   304     if (aTag == DOMWORKER_SCTAG_FILE) {
   305       MOZ_ASSERT(!aData);
   307       nsIDOMFile* file;
   308       if (JS_ReadBytes(aReader, &file, sizeof(file))) {
   309         MOZ_ASSERT(file);
   311 #ifdef DEBUG
   312         {
   313           // File should not be mutable.
   314           nsCOMPtr<nsIMutable> mutableFile = do_QueryInterface(file);
   315           bool isMutable;
   316           NS_ASSERTION(NS_SUCCEEDED(mutableFile->GetMutable(&isMutable)) &&
   317                        !isMutable,
   318                        "Only immutable file should be passed to worker");
   319         }
   320 #endif
   322         // nsIDOMFiles should be threadsafe, thus we will use the same instance
   323         // in the worker.
   324         JSObject* jsFile = file::CreateFile(aCx, file);
   325         return jsFile;
   326       }
   327     }
   328     // See if object is a nsIDOMBlob pointer.
   329     else if (aTag == DOMWORKER_SCTAG_BLOB) {
   330       MOZ_ASSERT(!aData);
   332       nsIDOMBlob* blob;
   333       if (JS_ReadBytes(aReader, &blob, sizeof(blob))) {
   334         MOZ_ASSERT(blob);
   336 #ifdef DEBUG
   337         {
   338           // Blob should not be mutable.
   339           nsCOMPtr<nsIMutable> mutableBlob = do_QueryInterface(blob);
   340           bool isMutable;
   341           NS_ASSERTION(NS_SUCCEEDED(mutableBlob->GetMutable(&isMutable)) &&
   342                        !isMutable,
   343                        "Only immutable blob should be passed to worker");
   344         }
   345 #endif
   347         // nsIDOMBlob should be threadsafe, thus we will use the same instance
   348         // in the worker.
   349         JSObject* jsBlob = file::CreateBlob(aCx, blob);
   350         return jsBlob;
   351       }
   352     }
   353     // See if the object is an ImageData.
   354     else if (aTag == SCTAG_DOM_IMAGEDATA) {
   355       MOZ_ASSERT(!aData);
   357       // Read the information out of the stream.
   358       uint32_t width, height;
   359       JS::Rooted<JS::Value> dataArray(aCx);
   360       if (!JS_ReadUint32Pair(aReader, &width, &height) ||
   361           !JS_ReadTypedArray(aReader, &dataArray))
   362       {
   363         return nullptr;
   364       }
   365       MOZ_ASSERT(dataArray.isObject());
   367       JS::Rooted<JSObject*> result(aCx);
   368       {
   369         // Construct the ImageData.
   370         nsRefPtr<ImageData> imageData = new ImageData(width, height,
   371                                                       dataArray.toObject());
   372         // Wrap it in a JS::Value, protected from a moving GC during ~nsRefPtr.
   373         result = imageData->WrapObject(aCx);
   374       }
   375       return result;
   376     }
   378     Error(aCx, 0);
   379     return nullptr;
   380   }
   382   static bool
   383   Write(JSContext* aCx, JSStructuredCloneWriter* aWriter,
   384         JS::Handle<JSObject*> aObj, void* aClosure)
   385   {
   386     NS_ASSERTION(aClosure, "Null pointer!");
   388     // We'll stash any nsISupports pointers that need to be AddRef'd here.
   389     nsTArray<nsCOMPtr<nsISupports> >* clonedObjects =
   390       static_cast<nsTArray<nsCOMPtr<nsISupports> >*>(aClosure);
   392     // See if this is a File object.
   393     {
   394       nsIDOMFile* file = file::GetDOMFileFromJSObject(aObj);
   395       if (file) {
   396         if (JS_WriteUint32Pair(aWriter, DOMWORKER_SCTAG_FILE, 0) &&
   397             JS_WriteBytes(aWriter, &file, sizeof(file))) {
   398           clonedObjects->AppendElement(file);
   399           return true;
   400         }
   401       }
   402     }
   404     // See if this is a Blob object.
   405     {
   406       nsIDOMBlob* blob = file::GetDOMBlobFromJSObject(aObj);
   407       if (blob) {
   408         nsCOMPtr<nsIMutable> mutableBlob = do_QueryInterface(blob);
   409         if (mutableBlob && NS_SUCCEEDED(mutableBlob->SetMutable(false)) &&
   410             JS_WriteUint32Pair(aWriter, DOMWORKER_SCTAG_BLOB, 0) &&
   411             JS_WriteBytes(aWriter, &blob, sizeof(blob))) {
   412           clonedObjects->AppendElement(blob);
   413           return true;
   414         }
   415       }
   416     }
   418     // See if this is an ImageData object.
   419     {
   420       ImageData* imageData = nullptr;
   421       if (NS_SUCCEEDED(UNWRAP_OBJECT(ImageData, aObj, imageData))) {
   422         // Prepare the ImageData internals.
   423         uint32_t width = imageData->Width();
   424         uint32_t height = imageData->Height();
   425         JS::Rooted<JSObject*> dataArray(aCx, imageData->GetDataObject());
   427         // Write the internals to the stream.
   428         JSAutoCompartment ac(aCx, dataArray);
   429         JS::Rooted<JS::Value> arrayValue(aCx, JS::ObjectValue(*dataArray));
   430         return JS_WriteUint32Pair(aWriter, SCTAG_DOM_IMAGEDATA, 0) &&
   431                JS_WriteUint32Pair(aWriter, width, height) &&
   432                JS_WriteTypedArray(aWriter, arrayValue);
   433       }
   434     }
   436     Error(aCx, 0);
   437     return false;
   438   }
   440   static void
   441   Error(JSContext* aCx, uint32_t /* aErrorId */)
   442   {
   443     Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
   444   }
   445 };
   447 JSStructuredCloneCallbacks gWorkerStructuredCloneCallbacks = {
   448   WorkerStructuredCloneCallbacks::Read,
   449   WorkerStructuredCloneCallbacks::Write,
   450   WorkerStructuredCloneCallbacks::Error,
   451   nullptr,
   452   nullptr,
   453   nullptr
   454 };
   456 struct MainThreadWorkerStructuredCloneCallbacks
   457 {
   458   static JSObject*
   459   Read(JSContext* aCx, JSStructuredCloneReader* aReader, uint32_t aTag,
   460        uint32_t aData, void* aClosure)
   461   {
   462     AssertIsOnMainThread();
   464     // See if object is a nsIDOMFile pointer.
   465     if (aTag == DOMWORKER_SCTAG_FILE) {
   466       MOZ_ASSERT(!aData);
   468       nsIDOMFile* file;
   469       if (JS_ReadBytes(aReader, &file, sizeof(file))) {
   470         MOZ_ASSERT(file);
   472 #ifdef DEBUG
   473         {
   474           // File should not be mutable.
   475           nsCOMPtr<nsIMutable> mutableFile = do_QueryInterface(file);
   476           bool isMutable;
   477           NS_ASSERTION(NS_SUCCEEDED(mutableFile->GetMutable(&isMutable)) &&
   478                        !isMutable,
   479                        "Only immutable file should be passed to worker");
   480         }
   481 #endif
   483         // nsIDOMFiles should be threadsafe, thus we will use the same instance
   484         // on the main thread.
   485         JS::Rooted<JS::Value> wrappedFile(aCx);
   486         nsresult rv = nsContentUtils::WrapNative(aCx, file,
   487                                                  &NS_GET_IID(nsIDOMFile),
   488                                                  &wrappedFile);
   489         if (NS_FAILED(rv)) {
   490           Error(aCx, nsIDOMDOMException::DATA_CLONE_ERR);
   491           return nullptr;
   492         }
   494         return &wrappedFile.toObject();
   495       }
   496     }
   497     // See if object is a nsIDOMBlob pointer.
   498     else if (aTag == DOMWORKER_SCTAG_BLOB) {
   499       MOZ_ASSERT(!aData);
   501       nsIDOMBlob* blob;
   502       if (JS_ReadBytes(aReader, &blob, sizeof(blob))) {
   503         MOZ_ASSERT(blob);
   505 #ifdef DEBUG
   506         {
   507           // Blob should not be mutable.
   508           nsCOMPtr<nsIMutable> mutableBlob = do_QueryInterface(blob);
   509           bool isMutable;
   510           NS_ASSERTION(NS_SUCCEEDED(mutableBlob->GetMutable(&isMutable)) &&
   511                        !isMutable,
   512                        "Only immutable blob should be passed to worker");
   513         }
   514 #endif
   516         // nsIDOMBlobs should be threadsafe, thus we will use the same instance
   517         // on the main thread.
   518         JS::Rooted<JS::Value> wrappedBlob(aCx);
   519         nsresult rv = nsContentUtils::WrapNative(aCx, blob,
   520                                                  &NS_GET_IID(nsIDOMBlob),
   521                                                  &wrappedBlob);
   522         if (NS_FAILED(rv)) {
   523           Error(aCx, nsIDOMDOMException::DATA_CLONE_ERR);
   524           return nullptr;
   525         }
   527         return &wrappedBlob.toObject();
   528       }
   529     }
   531     JS_ClearPendingException(aCx);
   532     return NS_DOMReadStructuredClone(aCx, aReader, aTag, aData, nullptr);
   533   }
   535   static bool
   536   Write(JSContext* aCx, JSStructuredCloneWriter* aWriter,
   537         JS::Handle<JSObject*> aObj, void* aClosure)
   538   {
   539     AssertIsOnMainThread();
   541     NS_ASSERTION(aClosure, "Null pointer!");
   543     // We'll stash any nsISupports pointers that need to be AddRef'd here.
   544     nsTArray<nsCOMPtr<nsISupports> >* clonedObjects =
   545       static_cast<nsTArray<nsCOMPtr<nsISupports> >*>(aClosure);
   547     // See if this is a wrapped native.
   548     nsCOMPtr<nsIXPConnectWrappedNative> wrappedNative;
   549     nsContentUtils::XPConnect()->
   550       GetWrappedNativeOfJSObject(aCx, aObj, getter_AddRefs(wrappedNative));
   552     if (wrappedNative) {
   553       // Get the raw nsISupports out of it.
   554       nsISupports* wrappedObject = wrappedNative->Native();
   555       NS_ASSERTION(wrappedObject, "Null pointer?!");
   557       nsISupports* ccISupports = nullptr;
   558       wrappedObject->QueryInterface(NS_GET_IID(nsCycleCollectionISupports),
   559                                     reinterpret_cast<void**>(&ccISupports));
   560       if (ccISupports) {
   561         NS_WARNING("Cycle collected objects are not supported!");
   562       }
   563       else {
   564         // See if the wrapped native is a nsIDOMFile.
   565         nsCOMPtr<nsIDOMFile> file = do_QueryInterface(wrappedObject);
   566         if (file) {
   567           nsCOMPtr<nsIMutable> mutableFile = do_QueryInterface(file);
   568           if (mutableFile && NS_SUCCEEDED(mutableFile->SetMutable(false))) {
   569             nsIDOMFile* filePtr = file;
   570             if (JS_WriteUint32Pair(aWriter, DOMWORKER_SCTAG_FILE, 0) &&
   571                 JS_WriteBytes(aWriter, &filePtr, sizeof(filePtr))) {
   572               clonedObjects->AppendElement(file);
   573               return true;
   574             }
   575           }
   576         }
   578         // See if the wrapped native is a nsIDOMBlob.
   579         nsCOMPtr<nsIDOMBlob> blob = do_QueryInterface(wrappedObject);
   580         if (blob) {
   581           nsCOMPtr<nsIMutable> mutableBlob = do_QueryInterface(blob);
   582           if (mutableBlob && NS_SUCCEEDED(mutableBlob->SetMutable(false))) {
   583             nsIDOMBlob* blobPtr = blob;
   584             if (JS_WriteUint32Pair(aWriter, DOMWORKER_SCTAG_BLOB, 0) &&
   585                 JS_WriteBytes(aWriter, &blobPtr, sizeof(blobPtr))) {
   586               clonedObjects->AppendElement(blob);
   587               return true;
   588             }
   589           }
   590         }
   591       }
   592     }
   594     JS_ClearPendingException(aCx);
   595     return NS_DOMWriteStructuredClone(aCx, aWriter, aObj, nullptr);
   596   }
   598   static void
   599   Error(JSContext* aCx, uint32_t aErrorId)
   600   {
   601     AssertIsOnMainThread();
   603     NS_DOMStructuredCloneError(aCx, aErrorId);
   604   }
   605 };
   607 JSStructuredCloneCallbacks gMainThreadWorkerStructuredCloneCallbacks = {
   608   MainThreadWorkerStructuredCloneCallbacks::Read,
   609   MainThreadWorkerStructuredCloneCallbacks::Write,
   610   MainThreadWorkerStructuredCloneCallbacks::Error,
   611   nullptr,
   612   nullptr,
   613   nullptr
   614 };
   616 struct ChromeWorkerStructuredCloneCallbacks
   617 {
   618   static JSObject*
   619   Read(JSContext* aCx, JSStructuredCloneReader* aReader, uint32_t aTag,
   620        uint32_t aData, void* aClosure)
   621   {
   622     return WorkerStructuredCloneCallbacks::Read(aCx, aReader, aTag, aData,
   623                                                 aClosure);
   624   }
   626   static bool
   627   Write(JSContext* aCx, JSStructuredCloneWriter* aWriter,
   628         JS::Handle<JSObject*> aObj, void* aClosure)
   629   {
   630     return WorkerStructuredCloneCallbacks::Write(aCx, aWriter, aObj, aClosure);
   631   }
   633   static void
   634   Error(JSContext* aCx, uint32_t aErrorId)
   635   {
   636     return WorkerStructuredCloneCallbacks::Error(aCx, aErrorId);
   637   }
   638 };
   640 JSStructuredCloneCallbacks gChromeWorkerStructuredCloneCallbacks = {
   641   ChromeWorkerStructuredCloneCallbacks::Read,
   642   ChromeWorkerStructuredCloneCallbacks::Write,
   643   ChromeWorkerStructuredCloneCallbacks::Error,
   644   nullptr,
   645   nullptr,
   646   nullptr
   647 };
   649 struct MainThreadChromeWorkerStructuredCloneCallbacks
   650 {
   651   static JSObject*
   652   Read(JSContext* aCx, JSStructuredCloneReader* aReader, uint32_t aTag,
   653        uint32_t aData, void* aClosure)
   654   {
   655     AssertIsOnMainThread();
   657     JSObject* clone =
   658       MainThreadWorkerStructuredCloneCallbacks::Read(aCx, aReader, aTag, aData,
   659                                                      aClosure);
   660     if (clone) {
   661       return clone;
   662     }
   664     clone =
   665       ChromeWorkerStructuredCloneCallbacks::Read(aCx, aReader, aTag, aData,
   666                                                  aClosure);
   667     if (clone) {
   668       return clone;
   669     }
   671     JS_ClearPendingException(aCx);
   672     return NS_DOMReadStructuredClone(aCx, aReader, aTag, aData, nullptr);
   673   }
   675   static bool
   676   Write(JSContext* aCx, JSStructuredCloneWriter* aWriter,
   677         JS::Handle<JSObject*> aObj, void* aClosure)
   678   {
   679     AssertIsOnMainThread();
   681     if (MainThreadWorkerStructuredCloneCallbacks::Write(aCx, aWriter, aObj,
   682                                                         aClosure) ||
   683         ChromeWorkerStructuredCloneCallbacks::Write(aCx, aWriter, aObj,
   684                                                     aClosure) ||
   685         NS_DOMWriteStructuredClone(aCx, aWriter, aObj, nullptr)) {
   686       return true;
   687     }
   689     return false;
   690   }
   692   static void
   693   Error(JSContext* aCx, uint32_t aErrorId)
   694   {
   695     AssertIsOnMainThread();
   697     NS_DOMStructuredCloneError(aCx, aErrorId);
   698   }
   699 };
   701 JSStructuredCloneCallbacks gMainThreadChromeWorkerStructuredCloneCallbacks = {
   702   MainThreadChromeWorkerStructuredCloneCallbacks::Read,
   703   MainThreadChromeWorkerStructuredCloneCallbacks::Write,
   704   MainThreadChromeWorkerStructuredCloneCallbacks::Error,
   705   nullptr,
   706   nullptr,
   707   nullptr
   708 };
   710 class MainThreadReleaseRunnable MOZ_FINAL : public nsRunnable
   711 {
   712   nsTArray<nsCOMPtr<nsISupports>> mDoomed;
   713   nsTArray<nsCString> mHostObjectURIs;
   715 public:
   716   MainThreadReleaseRunnable(nsTArray<nsCOMPtr<nsISupports>>& aDoomed,
   717                             nsTArray<nsCString>& aHostObjectURIs)
   718   {
   719     mDoomed.SwapElements(aDoomed);
   720     mHostObjectURIs.SwapElements(aHostObjectURIs);
   721   }
   723   NS_DECL_ISUPPORTS_INHERITED
   725   NS_IMETHOD
   726   Run() MOZ_OVERRIDE
   727   {
   728     mDoomed.Clear();
   730     for (uint32_t index = 0; index < mHostObjectURIs.Length(); index++) {
   731       nsHostObjectProtocolHandler::RemoveDataEntry(mHostObjectURIs[index]);
   732     }
   734     return NS_OK;
   735   }
   737 private:
   738   ~MainThreadReleaseRunnable()
   739   { }
   740 };
   742 class WorkerFinishedRunnable MOZ_FINAL : public WorkerControlRunnable
   743 {
   744   WorkerPrivate* mFinishedWorker;
   746 public:
   747   WorkerFinishedRunnable(WorkerPrivate* aWorkerPrivate,
   748                          WorkerPrivate* aFinishedWorker)
   749   : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
   750     mFinishedWorker(aFinishedWorker)
   751   { }
   753 private:
   754   virtual bool
   755   PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
   756   {
   757     // Silence bad assertions.
   758     return true;
   759   }
   761   virtual void
   762   PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
   763                bool aDispatchResult) MOZ_OVERRIDE
   764   {
   765     // Silence bad assertions.
   766   }
   768   virtual bool
   769   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
   770   {
   771     nsTArray<nsCOMPtr<nsISupports>> doomed;
   772     mFinishedWorker->ForgetMainThreadObjects(doomed);
   774     nsTArray<nsCString> hostObjectURIs;
   775     mFinishedWorker->StealHostObjectURIs(hostObjectURIs);
   777     nsRefPtr<MainThreadReleaseRunnable> runnable =
   778       new MainThreadReleaseRunnable(doomed, hostObjectURIs);
   779     if (NS_FAILED(NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL))) {
   780       NS_WARNING("Failed to dispatch, going to leak!");
   781     }
   783     RuntimeService* runtime = RuntimeService::GetService();
   784     NS_ASSERTION(runtime, "This should never be null!");
   786     runtime->UnregisterWorker(aCx, mFinishedWorker);
   788     mFinishedWorker->ClearSelfRef();
   789     return true;
   790   }
   791 };
   793 class TopLevelWorkerFinishedRunnable MOZ_FINAL : public nsRunnable
   794 {
   795   WorkerPrivate* mFinishedWorker;
   797 public:
   798   TopLevelWorkerFinishedRunnable(WorkerPrivate* aFinishedWorker)
   799   : mFinishedWorker(aFinishedWorker)
   800   {
   801     aFinishedWorker->AssertIsOnWorkerThread();
   802   }
   804   NS_DECL_ISUPPORTS_INHERITED
   806 private:
   807   NS_IMETHOD
   808   Run() MOZ_OVERRIDE
   809   {
   810     AssertIsOnMainThread();
   812     RuntimeService* runtime = RuntimeService::GetService();
   813     MOZ_ASSERT(runtime);
   815     AutoSafeJSContext cx;
   816     JSAutoRequest ar(cx);
   818     runtime->UnregisterWorker(cx, mFinishedWorker);
   820     nsTArray<nsCOMPtr<nsISupports> > doomed;
   821     mFinishedWorker->ForgetMainThreadObjects(doomed);
   823     nsTArray<nsCString> hostObjectURIs;
   824     mFinishedWorker->StealHostObjectURIs(hostObjectURIs);
   826     nsRefPtr<MainThreadReleaseRunnable> runnable =
   827       new MainThreadReleaseRunnable(doomed, hostObjectURIs);
   828     if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) {
   829       NS_WARNING("Failed to dispatch, going to leak!");
   830     }
   832     mFinishedWorker->ClearSelfRef();
   833     return NS_OK;
   834   }
   835 };
   837 class ModifyBusyCountRunnable MOZ_FINAL : public WorkerControlRunnable
   838 {
   839   bool mIncrease;
   841 public:
   842   ModifyBusyCountRunnable(WorkerPrivate* aWorkerPrivate, bool aIncrease)
   843   : WorkerControlRunnable(aWorkerPrivate, ParentThreadUnchangedBusyCount),
   844     mIncrease(aIncrease)
   845   { }
   847 private:
   848   virtual bool
   849   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
   850   {
   851     return aWorkerPrivate->ModifyBusyCount(aCx, mIncrease);
   852   }
   854   virtual void
   855   PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate, bool aRunResult)
   856           MOZ_OVERRIDE
   857   {
   858     if (mIncrease) {
   859       WorkerControlRunnable::PostRun(aCx, aWorkerPrivate, aRunResult);
   860       return;
   861     }
   862     // Don't do anything here as it's possible that aWorkerPrivate has been
   863     // deleted.
   864   }
   865 };
   867 class CompileScriptRunnable MOZ_FINAL : public WorkerRunnable
   868 {
   869 public:
   870   CompileScriptRunnable(WorkerPrivate* aWorkerPrivate)
   871   : WorkerRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount)
   872   { }
   874 private:
   875   virtual bool
   876   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
   877   {
   878     JS::Rooted<JSObject*> global(aCx,
   879       aWorkerPrivate->CreateGlobalScope(aCx));
   880     if (!global) {
   881       NS_WARNING("Failed to make global!");
   882       return false;
   883     }
   885     JSAutoCompartment ac(aCx, global);
   886     return scriptloader::LoadWorkerScript(aCx);
   887   }
   888 };
   890 class CloseEventRunnable MOZ_FINAL : public WorkerRunnable
   891 {
   892 public:
   893   CloseEventRunnable(WorkerPrivate* aWorkerPrivate)
   894   : WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
   895   { }
   897 private:
   898   virtual bool
   899   PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
   900   {
   901     MOZ_ASSUME_UNREACHABLE("Don't call Dispatch() on CloseEventRunnable!");
   902   }
   904   virtual void
   905   PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
   906                bool aDispatchResult) MOZ_OVERRIDE
   907   {
   908     MOZ_ASSUME_UNREACHABLE("Don't call Dispatch() on CloseEventRunnable!");
   909   }
   911   virtual bool
   912   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
   913   {
   914     JS::Rooted<JSObject*> target(aCx, JS::CurrentGlobalOrNull(aCx));
   915     NS_ASSERTION(target, "This must never be null!");
   917     aWorkerPrivate->CloseHandlerStarted();
   919     WorkerGlobalScope* globalScope = aWorkerPrivate->GlobalScope();
   921     nsCOMPtr<nsIDOMEvent> event;
   922     nsresult rv =
   923       NS_NewDOMEvent(getter_AddRefs(event), globalScope, nullptr, nullptr);
   924     if (NS_FAILED(rv)) {
   925       Throw(aCx, rv);
   926       return false;
   927     }
   929     rv = event->InitEvent(NS_LITERAL_STRING("close"), false, false);
   930     if (NS_FAILED(rv)) {
   931       Throw(aCx, rv);
   932       return false;
   933     }
   935     event->SetTrusted(true);
   937     globalScope->DispatchDOMEvent(nullptr, event, nullptr, nullptr);
   939     return true;
   940   }
   942   virtual void
   943   PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate, bool aRunResult)
   944           MOZ_OVERRIDE
   945   {
   946     // Report errors.
   947     WorkerRunnable::PostRun(aCx, aWorkerPrivate, aRunResult);
   949     // Match the busy count increase from NotifyRunnable.
   950     if (!aWorkerPrivate->ModifyBusyCountFromWorker(aCx, false)) {
   951       JS_ReportPendingException(aCx);
   952     }
   954     aWorkerPrivate->CloseHandlerFinished();
   955   }
   956 };
   958 class MessageEventRunnable MOZ_FINAL : public WorkerRunnable
   959 {
   960   JSAutoStructuredCloneBuffer mBuffer;
   961   nsTArray<nsCOMPtr<nsISupports> > mClonedObjects;
   962   uint64_t mMessagePortSerial;
   963   bool mToMessagePort;
   965 public:
   966   MessageEventRunnable(WorkerPrivate* aWorkerPrivate,
   967                        TargetAndBusyBehavior aBehavior,
   968                        JSAutoStructuredCloneBuffer&& aData,
   969                        nsTArray<nsCOMPtr<nsISupports> >& aClonedObjects,
   970                        bool aToMessagePort, uint64_t aMessagePortSerial)
   971   : WorkerRunnable(aWorkerPrivate, aBehavior)
   972   , mBuffer(Move(aData))
   973   , mMessagePortSerial(aMessagePortSerial)
   974   , mToMessagePort(aToMessagePort)
   975   {
   976     mClonedObjects.SwapElements(aClonedObjects);
   977   }
   979   bool
   980   DispatchDOMEvent(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
   981                    DOMEventTargetHelper* aTarget, bool aIsMainThread)
   982   {
   983     // Release reference to objects that were AddRef'd for
   984     // cloning into worker when array goes out of scope.
   985     nsTArray<nsCOMPtr<nsISupports>> clonedObjects;
   986     clonedObjects.SwapElements(mClonedObjects);
   988     JS::Rooted<JS::Value> messageData(aCx);
   989     if (!mBuffer.read(aCx, &messageData,
   990                       workers::WorkerStructuredCloneCallbacks(aIsMainThread))) {
   991       xpc::Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
   992       return false;
   993     }
   995     nsRefPtr<MessageEvent> event = new MessageEvent(aTarget, nullptr, nullptr);
   996     nsresult rv =
   997       event->InitMessageEvent(NS_LITERAL_STRING("message"),
   998                               false /* non-bubbling */,
   999                               true /* cancelable */,
  1000                               messageData,
  1001                               EmptyString(),
  1002                               EmptyString(),
  1003                               nullptr);
  1004     if (NS_FAILED(rv)) {
  1005       xpc::Throw(aCx, rv);
  1006       return false;
  1009     event->SetTrusted(true);
  1011     nsCOMPtr<nsIDOMEvent> domEvent = do_QueryObject(event);
  1013     nsEventStatus dummy = nsEventStatus_eIgnore;
  1014     aTarget->DispatchDOMEvent(nullptr, domEvent, nullptr, &dummy);
  1015     return true;
  1018 private:
  1019   virtual bool
  1020   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
  1022     MOZ_ASSERT_IF(mToMessagePort, aWorkerPrivate->IsSharedWorker());
  1024     if (mBehavior == ParentThreadUnchangedBusyCount) {
  1025       // Don't fire this event if the JS object has been disconnected from the
  1026       // private object.
  1027       if (!aWorkerPrivate->IsAcceptingEvents()) {
  1028         return true;
  1031       if (mToMessagePort) {
  1032         return
  1033           aWorkerPrivate->DispatchMessageEventToMessagePort(aCx,
  1034                                                             mMessagePortSerial,
  1035                                                             Move(mBuffer),
  1036                                                             mClonedObjects);
  1039       if (aWorkerPrivate->IsSuspended()) {
  1040         aWorkerPrivate->QueueRunnable(this);
  1041         return true;
  1044       aWorkerPrivate->AssertInnerWindowIsCorrect();
  1046       return DispatchDOMEvent(aCx, aWorkerPrivate, aWorkerPrivate,
  1047                               !aWorkerPrivate->GetParent());
  1050     MOZ_ASSERT(aWorkerPrivate == GetWorkerPrivateFromContext(aCx));
  1052     if (mToMessagePort) {
  1053       nsRefPtr<workers::MessagePort> port =
  1054         aWorkerPrivate->GetMessagePort(mMessagePortSerial);
  1055       if (!port) {
  1056         // Must have been closed already.
  1057         return true;
  1059       return DispatchDOMEvent(aCx, aWorkerPrivate, port, false);
  1062     return DispatchDOMEvent(aCx, aWorkerPrivate, aWorkerPrivate->GlobalScope(),
  1063                             false);
  1065 };
  1067 class NotifyRunnable MOZ_FINAL : public WorkerControlRunnable
  1069   Status mStatus;
  1071 public:
  1072   NotifyRunnable(WorkerPrivate* aWorkerPrivate, Status aStatus)
  1073   : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
  1074     mStatus(aStatus)
  1076     MOZ_ASSERT(aStatus == Closing || aStatus == Terminating ||
  1077                aStatus == Canceling || aStatus == Killing);
  1080 private:
  1081   virtual bool
  1082   PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
  1084     // Modify here, but not in PostRun! This busy count addition will be matched
  1085     // by the CloseEventRunnable.
  1086     return aWorkerPrivate->ModifyBusyCount(aCx, true);
  1089   virtual void
  1090   PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
  1091                bool aDispatchResult) MOZ_OVERRIDE
  1093     if (!aDispatchResult) {
  1094       // We couldn't dispatch to the worker, which means it's already dead.
  1095       // Undo the busy count modification.
  1096       aWorkerPrivate->ModifyBusyCount(aCx, false);
  1100   virtual bool
  1101   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
  1103     return aWorkerPrivate->NotifyInternal(aCx, mStatus);
  1105 };
  1107 class CloseRunnable MOZ_FINAL : public WorkerControlRunnable
  1109 public:
  1110   CloseRunnable(WorkerPrivate* aWorkerPrivate)
  1111   : WorkerControlRunnable(aWorkerPrivate, ParentThreadUnchangedBusyCount)
  1112   { }
  1114 private:
  1115   virtual bool
  1116   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
  1118     // This busy count will be matched by the CloseEventRunnable.
  1119     return aWorkerPrivate->ModifyBusyCount(aCx, true) &&
  1120            aWorkerPrivate->Close(aCx);
  1122 };
  1124 class SuspendRunnable MOZ_FINAL : public WorkerControlRunnable
  1126 public:
  1127   SuspendRunnable(WorkerPrivate* aWorkerPrivate)
  1128   : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
  1129   { }
  1131 private:
  1132   virtual bool
  1133   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
  1135     return aWorkerPrivate->SuspendInternal(aCx);
  1137 };
  1139 class ResumeRunnable MOZ_FINAL : public WorkerControlRunnable
  1141 public:
  1142   ResumeRunnable(WorkerPrivate* aWorkerPrivate)
  1143   : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
  1144   { }
  1146 private:
  1147   virtual bool
  1148   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
  1150     return aWorkerPrivate->ResumeInternal(aCx);
  1152 };
  1154 class ReportErrorRunnable MOZ_FINAL : public WorkerRunnable
  1156   nsString mMessage;
  1157   nsString mFilename;
  1158   nsString mLine;
  1159   uint32_t mLineNumber;
  1160   uint32_t mColumnNumber;
  1161   uint32_t mFlags;
  1162   uint32_t mErrorNumber;
  1164 public:
  1165   // aWorkerPrivate is the worker thread we're on (or the main thread, if null)
  1166   // aTarget is the worker object that we are going to fire an error at
  1167   // (if any).
  1168   static bool
  1169   ReportError(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
  1170               bool aFireAtScope, WorkerPrivate* aTarget,
  1171               const nsString& aMessage, const nsString& aFilename,
  1172               const nsString& aLine, uint32_t aLineNumber,
  1173               uint32_t aColumnNumber, uint32_t aFlags,
  1174               uint32_t aErrorNumber, uint64_t aInnerWindowId)
  1176     if (aWorkerPrivate) {
  1177       aWorkerPrivate->AssertIsOnWorkerThread();
  1179     else {
  1180       AssertIsOnMainThread();
  1183     JS::Rooted<JSString*> message(aCx, JS_NewUCStringCopyN(aCx, aMessage.get(),
  1184                                                            aMessage.Length()));
  1185     if (!message) {
  1186       return false;
  1189     JS::Rooted<JSString*> filename(aCx, JS_NewUCStringCopyN(aCx, aFilename.get(),
  1190                                                             aFilename.Length()));
  1191     if (!filename) {
  1192       return false;
  1195     // We should not fire error events for warnings but instead make sure that
  1196     // they show up in the error console.
  1197     if (!JSREPORT_IS_WARNING(aFlags)) {
  1198       // First fire an ErrorEvent at the worker.
  1199       RootedDictionary<ErrorEventInit> init(aCx);
  1200       init.mMessage = aMessage;
  1201       init.mFilename = aFilename;
  1202       init.mLineno = aLineNumber;
  1203       init.mCancelable = true;
  1204       init.mBubbles = true;
  1206       if (aTarget) {
  1207         nsRefPtr<ErrorEvent> event =
  1208           ErrorEvent::Constructor(aTarget, NS_LITERAL_STRING("error"), init);
  1209         event->SetTrusted(true);
  1211         nsEventStatus status = nsEventStatus_eIgnore;
  1212         aTarget->DispatchDOMEvent(nullptr, event, nullptr, &status);
  1214         if (status == nsEventStatus_eConsumeNoDefault) {
  1215           return true;
  1219       // Now fire an event at the global object, but don't do that if the error
  1220       // code is too much recursion and this is the same script threw the error.
  1221       if (aFireAtScope && (aTarget || aErrorNumber != JSMSG_OVER_RECURSED)) {
  1222         JS::Rooted<JSObject*> target(aCx, JS::CurrentGlobalOrNull(aCx));
  1223         NS_ASSERTION(target, "This should never be null!");
  1225         nsEventStatus status = nsEventStatus_eIgnore;
  1226         nsIScriptGlobalObject* sgo;
  1228         if (aWorkerPrivate) {
  1229           WorkerGlobalScope* globalTarget = aWorkerPrivate->GlobalScope();
  1230           MOZ_ASSERT(target == globalTarget->GetWrapperPreserveColor());
  1232           nsRefPtr<ErrorEvent> event =
  1233             ErrorEvent::Constructor(aTarget, NS_LITERAL_STRING("error"), init);
  1234           event->SetTrusted(true);
  1236           nsIDOMEventTarget* target = static_cast<nsIDOMEventTarget*>(globalTarget);
  1237           if (NS_FAILED(EventDispatcher::DispatchDOMEvent(target, nullptr,
  1238                                                           event, nullptr,
  1239                                                           &status))) {
  1240             NS_WARNING("Failed to dispatch worker thread error event!");
  1241             status = nsEventStatus_eIgnore;
  1244         else if ((sgo = nsJSUtils::GetStaticScriptGlobal(target))) {
  1245           MOZ_ASSERT(NS_IsMainThread());
  1247           if (NS_FAILED(sgo->HandleScriptError(init, &status))) {
  1248             NS_WARNING("Failed to dispatch main thread error event!");
  1249             status = nsEventStatus_eIgnore;
  1253         // Was preventDefault() called?
  1254         if (status == nsEventStatus_eConsumeNoDefault) {
  1255           return true;
  1260     // Now fire a runnable to do the same on the parent's thread if we can.
  1261     if (aWorkerPrivate) {
  1262       nsRefPtr<ReportErrorRunnable> runnable =
  1263         new ReportErrorRunnable(aWorkerPrivate, aMessage, aFilename, aLine,
  1264                                 aLineNumber, aColumnNumber, aFlags,
  1265                                 aErrorNumber);
  1266       return runnable->Dispatch(aCx);
  1269     // Otherwise log an error to the error console.
  1270     LogErrorToConsole(aMessage, aFilename, aLine, aLineNumber, aColumnNumber,
  1271                       aFlags, aInnerWindowId);
  1272     return true;
  1275 private:
  1276   ReportErrorRunnable(WorkerPrivate* aWorkerPrivate, const nsString& aMessage,
  1277                       const nsString& aFilename, const nsString& aLine,
  1278                       uint32_t aLineNumber, uint32_t aColumnNumber,
  1279                       uint32_t aFlags, uint32_t aErrorNumber)
  1280   : WorkerRunnable(aWorkerPrivate, ParentThreadUnchangedBusyCount),
  1281     mMessage(aMessage), mFilename(aFilename), mLine(aLine),
  1282     mLineNumber(aLineNumber), mColumnNumber(aColumnNumber), mFlags(aFlags),
  1283     mErrorNumber(aErrorNumber)
  1284   { }
  1286   virtual void
  1287   PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
  1288                bool aDispatchResult) MOZ_OVERRIDE
  1290     aWorkerPrivate->AssertIsOnWorkerThread();
  1292     // Dispatch may fail if the worker was canceled, no need to report that as
  1293     // an error, so don't call base class PostDispatch.
  1296   virtual bool
  1297   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
  1299     // Don't fire this event if the JS object has been disconnected from the
  1300     // private object.
  1301     if (!aWorkerPrivate->IsAcceptingEvents()) {
  1302       return true;
  1305     JS::Rooted<JSObject*> target(aCx, aWorkerPrivate->GetWrapper());
  1307     uint64_t innerWindowId;
  1308     bool fireAtScope = true;
  1310     WorkerPrivate* parent = aWorkerPrivate->GetParent();
  1311     if (parent) {
  1312       innerWindowId = 0;
  1314     else {
  1315       AssertIsOnMainThread();
  1317       if (aWorkerPrivate->IsSuspended()) {
  1318         aWorkerPrivate->QueueRunnable(this);
  1319         return true;
  1322       if (aWorkerPrivate->IsSharedWorker()) {
  1323         aWorkerPrivate->BroadcastErrorToSharedWorkers(aCx, mMessage, mFilename,
  1324                                                       mLine, mLineNumber,
  1325                                                       mColumnNumber, mFlags);
  1326         return true;
  1329       aWorkerPrivate->AssertInnerWindowIsCorrect();
  1331       innerWindowId = aWorkerPrivate->GetInnerWindowId();
  1334     return ReportError(aCx, parent, fireAtScope, aWorkerPrivate, mMessage,
  1335                        mFilename, mLine, mLineNumber, mColumnNumber, mFlags,
  1336                        mErrorNumber, innerWindowId);
  1338 };
  1340 class TimerRunnable MOZ_FINAL : public WorkerRunnable
  1342 public:
  1343   TimerRunnable(WorkerPrivate* aWorkerPrivate)
  1344   : WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
  1345   { }
  1347 private:
  1348   virtual bool
  1349   PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
  1351     // Silence bad assertions.
  1352     return true;
  1355   virtual void
  1356   PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
  1357                bool aDispatchResult) MOZ_OVERRIDE
  1359     // Silence bad assertions.
  1362   virtual bool
  1363   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
  1365     return aWorkerPrivate->RunExpiredTimeouts(aCx);
  1367 };
  1369 void
  1370 DummyCallback(nsITimer* aTimer, void* aClosure)
  1372   // Nothing!
  1375 class TimerThreadEventTarget MOZ_FINAL : public nsIEventTarget
  1377   WorkerPrivate* mWorkerPrivate;
  1378   nsRefPtr<WorkerRunnable> mWorkerRunnable;
  1380 public:
  1381   TimerThreadEventTarget(WorkerPrivate* aWorkerPrivate,
  1382                          WorkerRunnable* aWorkerRunnable)
  1383   : mWorkerPrivate(aWorkerPrivate), mWorkerRunnable(aWorkerRunnable)
  1385     MOZ_ASSERT(aWorkerPrivate);
  1386     MOZ_ASSERT(aWorkerRunnable);
  1389   NS_DECL_THREADSAFE_ISUPPORTS
  1391 protected:
  1392   NS_IMETHOD
  1393   Dispatch(nsIRunnable* aRunnable, uint32_t aFlags) MOZ_OVERRIDE
  1395     // This should only happen on the timer thread.
  1396     MOZ_ASSERT(!NS_IsMainThread());
  1397     MOZ_ASSERT(aFlags == nsIEventTarget::DISPATCH_NORMAL);
  1399     nsRefPtr<TimerThreadEventTarget> kungFuDeathGrip = this;
  1401     // Run the runnable we're given now (should just call DummyCallback()),
  1402     // otherwise the timer thread will leak it...  If we run this after
  1403     // dispatch running the event can race against resetting the timer.
  1404     aRunnable->Run();
  1406     // This can fail if we're racing to terminate or cancel, should be handled
  1407     // by the terminate or cancel code.
  1408     mWorkerRunnable->Dispatch(nullptr);
  1410     return NS_OK;
  1413   NS_IMETHOD
  1414   IsOnCurrentThread(bool* aIsOnCurrentThread) MOZ_OVERRIDE
  1416     MOZ_ASSERT(aIsOnCurrentThread);
  1418     nsresult rv = mWorkerPrivate->IsOnCurrentThread(aIsOnCurrentThread);
  1419     if (NS_WARN_IF(NS_FAILED(rv))) {
  1420       return rv;
  1423     return NS_OK;
  1425 };
  1427 class KillCloseEventRunnable MOZ_FINAL : public WorkerRunnable
  1429   nsCOMPtr<nsITimer> mTimer;
  1431   class KillScriptRunnable MOZ_FINAL : public WorkerControlRunnable
  1433   public:
  1434     KillScriptRunnable(WorkerPrivate* aWorkerPrivate)
  1435     : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
  1436     { }
  1438   private:
  1439     virtual bool
  1440     PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
  1442       // Silence bad assertions, this is dispatched from the timer thread.
  1443       return true;
  1446     virtual void
  1447     PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
  1448                  bool aDispatchResult) MOZ_OVERRIDE
  1450       // Silence bad assertions, this is dispatched from the timer thread.
  1453     virtual bool
  1454     WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
  1456       // Kill running script.
  1457       return false;
  1459   };
  1461 public:
  1462   KillCloseEventRunnable(WorkerPrivate* aWorkerPrivate)
  1463   : WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
  1464   { }
  1466   bool
  1467   SetTimeout(JSContext* aCx, uint32_t aDelayMS)
  1469     nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID);
  1470     if (!timer) {
  1471       JS_ReportError(aCx, "Failed to create timer!");
  1472       return false;
  1475     nsRefPtr<KillScriptRunnable> runnable =
  1476       new KillScriptRunnable(mWorkerPrivate);
  1478     nsRefPtr<TimerThreadEventTarget> target =
  1479       new TimerThreadEventTarget(mWorkerPrivate, runnable);
  1481     if (NS_FAILED(timer->SetTarget(target))) {
  1482       JS_ReportError(aCx, "Failed to set timer's target!");
  1483       return false;
  1486     if (NS_FAILED(timer->InitWithFuncCallback(DummyCallback, nullptr, aDelayMS,
  1487                                               nsITimer::TYPE_ONE_SHOT))) {
  1488       JS_ReportError(aCx, "Failed to start timer!");
  1489       return false;
  1492     mTimer.swap(timer);
  1493     return true;
  1496 private:
  1497   ~KillCloseEventRunnable()
  1499     if (mTimer) {
  1500       mTimer->Cancel();
  1504   virtual bool
  1505   PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
  1507     MOZ_ASSUME_UNREACHABLE("Don't call Dispatch() on KillCloseEventRunnable!");
  1510   virtual void
  1511   PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
  1512                bool aDispatchResult) MOZ_OVERRIDE
  1514     MOZ_ASSUME_UNREACHABLE("Don't call Dispatch() on KillCloseEventRunnable!");
  1517   virtual bool
  1518   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
  1520     if (mTimer) {
  1521       mTimer->Cancel();
  1522       mTimer = nullptr;
  1525     return true;
  1527 };
  1529 class UpdateRuntimeAndContextOptionsRunnable MOZ_FINAL : public WorkerControlRunnable
  1531   JS::RuntimeOptions mRuntimeOptions;
  1532   JS::ContextOptions mContentCxOptions;
  1533   JS::ContextOptions mChromeCxOptions;
  1535 public:
  1536   UpdateRuntimeAndContextOptionsRunnable(
  1537                                     WorkerPrivate* aWorkerPrivate,
  1538                                     const JS::RuntimeOptions& aRuntimeOptions,
  1539                                     const JS::ContextOptions& aContentCxOptions,
  1540                                     const JS::ContextOptions& aChromeCxOptions)
  1541   : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
  1542     mRuntimeOptions(aRuntimeOptions),
  1543     mContentCxOptions(aContentCxOptions),
  1544     mChromeCxOptions(aChromeCxOptions)
  1545   { }
  1547 private:
  1548   virtual bool
  1549   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
  1551     aWorkerPrivate->UpdateRuntimeAndContextOptionsInternal(aCx,
  1552                                                            mRuntimeOptions,
  1553                                                            mContentCxOptions,
  1554                                                            mChromeCxOptions);
  1555     return true;
  1557 };
  1559 class UpdatePreferenceRunnable MOZ_FINAL : public WorkerControlRunnable
  1561   WorkerPreference mPref;
  1562   bool mValue;
  1564 public:
  1565   UpdatePreferenceRunnable(WorkerPrivate* aWorkerPrivate,
  1566                            WorkerPreference aPref,
  1567                            bool aValue)
  1568     : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
  1569       mPref(aPref),
  1570       mValue(aValue)
  1571   { }
  1573   virtual bool
  1574   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
  1576     aWorkerPrivate->UpdatePreferenceInternal(aCx, mPref, mValue);
  1577     return true;
  1579 };
  1581 class UpdateJSWorkerMemoryParameterRunnable MOZ_FINAL :
  1582   public WorkerControlRunnable
  1584   uint32_t mValue;
  1585   JSGCParamKey mKey;
  1587 public:
  1588   UpdateJSWorkerMemoryParameterRunnable(WorkerPrivate* aWorkerPrivate,
  1589                                         JSGCParamKey aKey,
  1590                                         uint32_t aValue)
  1591   : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
  1592     mValue(aValue), mKey(aKey)
  1593   { }
  1595 private:
  1596   virtual bool
  1597   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
  1599     aWorkerPrivate->UpdateJSWorkerMemoryParameterInternal(aCx, mKey, mValue);
  1600     return true;
  1602 };
  1604 #ifdef JS_GC_ZEAL
  1605 class UpdateGCZealRunnable MOZ_FINAL : public WorkerControlRunnable
  1607   uint8_t mGCZeal;
  1608   uint32_t mFrequency;
  1610 public:
  1611   UpdateGCZealRunnable(WorkerPrivate* aWorkerPrivate,
  1612                        uint8_t aGCZeal,
  1613                        uint32_t aFrequency)
  1614   : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
  1615     mGCZeal(aGCZeal), mFrequency(aFrequency)
  1616   { }
  1618 private:
  1619   virtual bool
  1620   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
  1622     aWorkerPrivate->UpdateGCZealInternal(aCx, mGCZeal, mFrequency);
  1623     return true;
  1625 };
  1626 #endif
  1628 class GarbageCollectRunnable MOZ_FINAL : public WorkerControlRunnable
  1630   bool mShrinking;
  1631   bool mCollectChildren;
  1633 public:
  1634   GarbageCollectRunnable(WorkerPrivate* aWorkerPrivate, bool aShrinking,
  1635                          bool aCollectChildren)
  1636   : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
  1637     mShrinking(aShrinking), mCollectChildren(aCollectChildren)
  1638   { }
  1640 private:
  1641   virtual bool
  1642   PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
  1644     // Silence bad assertions, this can be dispatched from either the main
  1645     // thread or the timer thread..
  1646     return true;
  1649   virtual void
  1650   PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
  1651                 bool aDispatchResult) MOZ_OVERRIDE
  1653     // Silence bad assertions, this can be dispatched from either the main
  1654     // thread or the timer thread..
  1657   virtual bool
  1658   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
  1660     aWorkerPrivate->GarbageCollectInternal(aCx, mShrinking, mCollectChildren);
  1661     return true;
  1663 };
  1665 class CycleCollectRunnable : public WorkerControlRunnable
  1667   bool mCollectChildren;
  1669 public:
  1670   CycleCollectRunnable(WorkerPrivate* aWorkerPrivate, bool aCollectChildren)
  1671   : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
  1672     mCollectChildren(aCollectChildren)
  1673   { }
  1675   bool
  1676   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
  1678     aWorkerPrivate->CycleCollectInternal(aCx, mCollectChildren);
  1679     return true;
  1681 };
  1683 class OfflineStatusChangeRunnable : public WorkerRunnable
  1685 public:
  1686   OfflineStatusChangeRunnable(WorkerPrivate* aWorkerPrivate, bool aIsOffline)
  1687     : WorkerRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount),
  1688       mIsOffline(aIsOffline)
  1692   bool
  1693   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
  1695     aWorkerPrivate->OfflineStatusChangeEventInternal(aCx, mIsOffline);
  1696     return true;
  1699 private:
  1700   bool mIsOffline;
  1701 };
  1703 class WorkerJSRuntimeStats : public JS::RuntimeStats
  1705   const nsACString& mRtPath;
  1707 public:
  1708   WorkerJSRuntimeStats(const nsACString& aRtPath)
  1709   : JS::RuntimeStats(JsWorkerMallocSizeOf), mRtPath(aRtPath)
  1710   { }
  1712   ~WorkerJSRuntimeStats()
  1714     for (size_t i = 0; i != zoneStatsVector.length(); i++) {
  1715       delete static_cast<xpc::ZoneStatsExtras*>(zoneStatsVector[i].extra);
  1718     for (size_t i = 0; i != compartmentStatsVector.length(); i++) {
  1719       delete static_cast<xpc::CompartmentStatsExtras*>(compartmentStatsVector[i].extra);
  1723   virtual void
  1724   initExtraZoneStats(JS::Zone* aZone,
  1725                      JS::ZoneStats* aZoneStats)
  1726                      MOZ_OVERRIDE
  1728     MOZ_ASSERT(!aZoneStats->extra);
  1730     // ReportJSRuntimeExplicitTreeStats expects that
  1731     // aZoneStats->extra is a xpc::ZoneStatsExtras pointer.
  1732     xpc::ZoneStatsExtras* extras = new xpc::ZoneStatsExtras;
  1733     extras->pathPrefix = mRtPath;
  1734     extras->pathPrefix += nsPrintfCString("zone(0x%p)/", (void *)aZone);
  1735     aZoneStats->extra = extras;
  1738   virtual void
  1739   initExtraCompartmentStats(JSCompartment* aCompartment,
  1740                             JS::CompartmentStats* aCompartmentStats)
  1741                             MOZ_OVERRIDE
  1743     MOZ_ASSERT(!aCompartmentStats->extra);
  1745     // ReportJSRuntimeExplicitTreeStats expects that
  1746     // aCompartmentStats->extra is a xpc::CompartmentStatsExtras pointer.
  1747     xpc::CompartmentStatsExtras* extras = new xpc::CompartmentStatsExtras;
  1749     // This is the |jsPathPrefix|.  Each worker has exactly two compartments:
  1750     // one for atoms, and one for everything else.
  1751     extras->jsPathPrefix.Assign(mRtPath);
  1752     extras->jsPathPrefix += nsPrintfCString("zone(0x%p)/",
  1753                                             (void *)js::GetCompartmentZone(aCompartment));
  1754     extras->jsPathPrefix += js::IsAtomsCompartment(aCompartment)
  1755                             ? NS_LITERAL_CSTRING("compartment(web-worker-atoms)/")
  1756                             : NS_LITERAL_CSTRING("compartment(web-worker)/");
  1758     // This should never be used when reporting with workers (hence the "?!").
  1759     extras->domPathPrefix.AssignLiteral("explicit/workers/?!/");
  1761     aCompartmentStats->extra = extras;
  1763 };
  1765 class MessagePortRunnable MOZ_FINAL : public WorkerRunnable
  1767   uint64_t mMessagePortSerial;
  1768   bool mConnect;
  1770 public:
  1771   MessagePortRunnable(WorkerPrivate* aWorkerPrivate,
  1772                       uint64_t aMessagePortSerial,
  1773                       bool aConnect)
  1774   : WorkerRunnable(aWorkerPrivate, aConnect ?
  1775                                    WorkerThreadModifyBusyCount :
  1776                                    WorkerThreadUnchangedBusyCount),
  1777     mMessagePortSerial(aMessagePortSerial), mConnect(aConnect)
  1778   { }
  1780 private:
  1781   ~MessagePortRunnable()
  1782   { }
  1784   virtual bool
  1785   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
  1787     if (mConnect) {
  1788       return aWorkerPrivate->ConnectMessagePort(aCx, mMessagePortSerial);
  1791     aWorkerPrivate->DisconnectMessagePort(mMessagePortSerial);
  1792     return true;
  1794 };
  1796 #ifdef DEBUG
  1798 PRThread*
  1799 PRThreadFromThread(nsIThread* aThread)
  1801   MOZ_ASSERT(aThread);
  1803   PRThread* result;
  1804   MOZ_ASSERT(NS_SUCCEEDED(aThread->GetPRThread(&result)));
  1805   MOZ_ASSERT(result);
  1807   return result;
  1810 #endif // DEBUG
  1812 } /* anonymous namespace */
  1814 NS_IMPL_ISUPPORTS_INHERITED0(MainThreadReleaseRunnable, nsRunnable)
  1816 NS_IMPL_ISUPPORTS_INHERITED0(TopLevelWorkerFinishedRunnable, nsRunnable)
  1818 NS_IMPL_ISUPPORTS(TimerThreadEventTarget, nsIEventTarget)
  1820 template <class Derived>
  1821 class WorkerPrivateParent<Derived>::SynchronizeAndResumeRunnable MOZ_FINAL
  1822   : public nsRunnable
  1824   friend class nsRevocableEventPtr<SynchronizeAndResumeRunnable>;
  1826   WorkerPrivate* mWorkerPrivate;
  1827   nsCOMPtr<nsPIDOMWindow> mWindow;
  1828   nsCOMPtr<nsIScriptContext> mScriptContext;
  1830 public:
  1831   SynchronizeAndResumeRunnable(WorkerPrivate* aWorkerPrivate,
  1832                                nsPIDOMWindow* aWindow,
  1833                                nsIScriptContext* aScriptContext)
  1834   : mWorkerPrivate(aWorkerPrivate), mWindow(aWindow),
  1835     mScriptContext(aScriptContext)
  1837     AssertIsOnMainThread();
  1838     MOZ_ASSERT(aWorkerPrivate);
  1839     MOZ_ASSERT(aWindow);
  1840     MOZ_ASSERT(!aWorkerPrivate->GetParent());
  1843 private:
  1844   ~SynchronizeAndResumeRunnable()
  1845   { }
  1847   NS_IMETHOD
  1848   Run() MOZ_OVERRIDE
  1850     AssertIsOnMainThread();
  1852     if (mWorkerPrivate) {
  1853       AutoPushJSContext cx(mScriptContext ?
  1854                            mScriptContext->GetNativeContext() :
  1855                            nsContentUtils::GetSafeJSContext());
  1857       if (!mWorkerPrivate->Resume(cx, mWindow)) {
  1858         JS_ReportPendingException(cx);
  1862     return NS_OK;
  1865   void
  1866   Revoke()
  1868     AssertIsOnMainThread();
  1869     MOZ_ASSERT(mWorkerPrivate);
  1870     MOZ_ASSERT(mWindow);
  1872     mWorkerPrivate = nullptr;
  1873     mWindow = nullptr;
  1874     mScriptContext = nullptr;
  1876 };
  1878 template <class Derived>
  1879 class WorkerPrivateParent<Derived>::EventTarget MOZ_FINAL
  1880   : public nsIEventTarget
  1882   // This mutex protects mWorkerPrivate and must be acquired *before* the
  1883   // WorkerPrivate's mutex whenever they must both be held.
  1884   mozilla::Mutex mMutex;
  1885   WorkerPrivate* mWorkerPrivate;
  1886   nsIEventTarget* mWeakNestedEventTarget;
  1887   nsCOMPtr<nsIEventTarget> mNestedEventTarget;
  1889 public:
  1890   EventTarget(WorkerPrivate* aWorkerPrivate)
  1891   : mMutex("WorkerPrivateParent::EventTarget::mMutex"),
  1892     mWorkerPrivate(aWorkerPrivate), mWeakNestedEventTarget(nullptr)
  1894     MOZ_ASSERT(aWorkerPrivate);
  1897   EventTarget(WorkerPrivate* aWorkerPrivate, nsIEventTarget* aNestedEventTarget)
  1898   : mMutex("WorkerPrivateParent::EventTarget::mMutex"),
  1899     mWorkerPrivate(aWorkerPrivate), mWeakNestedEventTarget(aNestedEventTarget),
  1900     mNestedEventTarget(aNestedEventTarget)
  1902     MOZ_ASSERT(aWorkerPrivate);
  1903     MOZ_ASSERT(aNestedEventTarget);
  1906   void
  1907   Disable()
  1909     nsCOMPtr<nsIEventTarget> nestedEventTarget;
  1911       MutexAutoLock lock(mMutex);
  1913       MOZ_ASSERT(mWorkerPrivate);
  1914       mWorkerPrivate = nullptr;
  1915       mNestedEventTarget.swap(nestedEventTarget);
  1919   nsIEventTarget*
  1920   GetWeakNestedEventTarget() const
  1922     MOZ_ASSERT(mWeakNestedEventTarget);
  1923     return mWeakNestedEventTarget;
  1926   NS_DECL_THREADSAFE_ISUPPORTS
  1927   NS_DECL_NSIEVENTTARGET
  1929 private:
  1930   ~EventTarget()
  1931   { }
  1932 };
  1934 struct WorkerPrivate::TimeoutInfo
  1936   TimeoutInfo()
  1937   : mTimeoutCallable(JS::UndefinedValue()), mLineNumber(0), mId(0),
  1938     mIsInterval(false), mCanceled(false)
  1940     MOZ_COUNT_CTOR(mozilla::dom::workers::WorkerPrivate::TimeoutInfo);
  1943   ~TimeoutInfo()
  1945     MOZ_COUNT_DTOR(mozilla::dom::workers::WorkerPrivate::TimeoutInfo);
  1948   bool operator==(const TimeoutInfo& aOther)
  1950     return mTargetTime == aOther.mTargetTime;
  1953   bool operator<(const TimeoutInfo& aOther)
  1955     return mTargetTime < aOther.mTargetTime;
  1958   JS::Heap<JS::Value> mTimeoutCallable;
  1959   nsString mTimeoutString;
  1960   nsTArray<JS::Heap<JS::Value> > mExtraArgVals;
  1961   mozilla::TimeStamp mTargetTime;
  1962   mozilla::TimeDuration mInterval;
  1963   nsCString mFilename;
  1964   uint32_t mLineNumber;
  1965   int32_t mId;
  1966   bool mIsInterval;
  1967   bool mCanceled;
  1968 };
  1970 class WorkerPrivate::MemoryReporter MOZ_FINAL : public nsIMemoryReporter
  1972   NS_DECL_THREADSAFE_ISUPPORTS
  1974   friend class WorkerPrivate;
  1976   SharedMutex mMutex;
  1977   WorkerPrivate* mWorkerPrivate;
  1978   nsCString mRtPath;
  1979   bool mAlreadyMappedToAddon;
  1981 public:
  1982   MemoryReporter(WorkerPrivate* aWorkerPrivate)
  1983   : mMutex(aWorkerPrivate->mMutex), mWorkerPrivate(aWorkerPrivate),
  1984     mAlreadyMappedToAddon(false)
  1986     aWorkerPrivate->AssertIsOnWorkerThread();
  1988     nsCString escapedDomain(aWorkerPrivate->Domain());
  1989     escapedDomain.ReplaceChar('/', '\\');
  1991     NS_ConvertUTF16toUTF8 escapedURL(aWorkerPrivate->ScriptURL());
  1992     escapedURL.ReplaceChar('/', '\\');
  1994     nsAutoCString addressString;
  1995     addressString.AppendPrintf("0x%p", static_cast<void*>(aWorkerPrivate));
  1997     mRtPath = NS_LITERAL_CSTRING("explicit/workers/workers(") +
  1998               escapedDomain + NS_LITERAL_CSTRING(")/worker(") +
  1999               escapedURL + NS_LITERAL_CSTRING(", ") + addressString +
  2000               NS_LITERAL_CSTRING(")/");
  2003   NS_IMETHOD
  2004   CollectReports(nsIMemoryReporterCallback* aCallback,
  2005                  nsISupports* aClosure)
  2007     AssertIsOnMainThread();
  2009     // Assumes that WorkerJSRuntimeStats will hold a reference to mRtPath,
  2010     // and not a copy, as TryToMapAddon() may later modify the string again.
  2011     WorkerJSRuntimeStats rtStats(mRtPath);
  2014       MutexAutoLock lock(mMutex);
  2016       TryToMapAddon();
  2018       if (!mWorkerPrivate ||
  2019           !mWorkerPrivate->BlockAndCollectRuntimeStats(&rtStats)) {
  2020         // Returning NS_OK here will effectively report 0 memory.
  2021         return NS_OK;
  2025     return xpc::ReportJSRuntimeExplicitTreeStats(rtStats, mRtPath,
  2026                                                  aCallback, aClosure);
  2029 private:
  2030   ~MemoryReporter()
  2031   { }
  2033   void
  2034   Disable()
  2036     // Called from WorkerPrivate::DisableMemoryReporter.
  2037     mMutex.AssertCurrentThreadOwns();
  2039     NS_ASSERTION(mWorkerPrivate, "Disabled more than once!");
  2040     mWorkerPrivate = nullptr;
  2043   // Only call this from the main thread and under mMutex lock.
  2044   void
  2045   TryToMapAddon()
  2047     AssertIsOnMainThread();
  2048     mMutex.AssertCurrentThreadOwns();
  2050     if (mAlreadyMappedToAddon || !mWorkerPrivate) {
  2051       return;
  2054     nsCOMPtr<nsIURI> scriptURI;
  2055     if (NS_FAILED(NS_NewURI(getter_AddRefs(scriptURI),
  2056                             mWorkerPrivate->ScriptURL()))) {
  2057       return;
  2060     mAlreadyMappedToAddon = true;
  2062     if (XRE_GetProcessType() != GeckoProcessType_Default) {
  2063       // Only try to access the service from the main process.
  2064       return;
  2067     nsAutoCString addonId;
  2068     bool ok;
  2069     nsCOMPtr<amIAddonManager> addonManager =
  2070       do_GetService("@mozilla.org/addons/integration;1");
  2072     if (!addonManager ||
  2073         NS_FAILED(addonManager->MapURIToAddonID(scriptURI, addonId, &ok)) ||
  2074         !ok) {
  2075       return;
  2078     static const size_t explicitLength = strlen("explicit/");
  2079     addonId.Insert(NS_LITERAL_CSTRING("add-ons/"), 0);
  2080     addonId += "/";
  2081     mRtPath.Insert(addonId, explicitLength);
  2083 };
  2085 NS_IMPL_ISUPPORTS(WorkerPrivate::MemoryReporter, nsIMemoryReporter)
  2087 WorkerPrivate::SyncLoopInfo::SyncLoopInfo(EventTarget* aEventTarget)
  2088 : mEventTarget(aEventTarget), mCompleted(false), mResult(false)
  2089 #ifdef DEBUG
  2090   , mHasRun(false)
  2091 #endif
  2095 // Can't use NS_IMPL_CYCLE_COLLECTION_CLASS(WorkerPrivateParent) because of the
  2096 // templates.
  2097 template <class Derived>
  2098 typename WorkerPrivateParent<Derived>::cycleCollection
  2099   WorkerPrivateParent<Derived>::_cycleCollectorGlobal =
  2100     WorkerPrivateParent<Derived>::cycleCollection();
  2102 template <class Derived>
  2103 WorkerPrivateParent<Derived>::WorkerPrivateParent(
  2104                                            JSContext* aCx,
  2105                                            WorkerPrivate* aParent,
  2106                                            const nsAString& aScriptURL,
  2107                                            bool aIsChromeWorker,
  2108                                            WorkerType aWorkerType,
  2109                                            const nsACString& aSharedWorkerName,
  2110                                            LoadInfo& aLoadInfo)
  2111 : mMutex("WorkerPrivateParent Mutex"),
  2112   mCondVar(mMutex, "WorkerPrivateParent CondVar"),
  2113   mMemoryReportCondVar(mMutex, "WorkerPrivateParent Memory Report CondVar"),
  2114   mParent(aParent), mScriptURL(aScriptURL),
  2115   mSharedWorkerName(aSharedWorkerName), mBusyCount(0), mMessagePortSerial(0),
  2116   mParentStatus(Pending), mParentSuspended(false),
  2117   mIsChromeWorker(aIsChromeWorker), mMainThreadObjectsForgotten(false),
  2118   mWorkerType(aWorkerType),
  2119   mCreationTimeStamp(TimeStamp::Now())
  2121   SetIsDOMBinding();
  2123   MOZ_ASSERT_IF(IsSharedWorker(), !aSharedWorkerName.IsVoid() &&
  2124                                   NS_IsMainThread());
  2125   MOZ_ASSERT_IF(!IsSharedWorker(), aSharedWorkerName.IsEmpty());
  2127   if (aLoadInfo.mWindow) {
  2128     AssertIsOnMainThread();
  2129     MOZ_ASSERT(aLoadInfo.mWindow->IsInnerWindow(),
  2130                "Should have inner window here!");
  2131     BindToOwner(aLoadInfo.mWindow);
  2134   mLoadInfo.StealFrom(aLoadInfo);
  2136   if (aParent) {
  2137     aParent->AssertIsOnWorkerThread();
  2139     aParent->CopyJSSettings(mJSSettings);
  2141   else {
  2142     AssertIsOnMainThread();
  2144     RuntimeService::GetDefaultJSSettings(mJSSettings);
  2148 template <class Derived>
  2149 WorkerPrivateParent<Derived>::~WorkerPrivateParent()
  2151   DropJSObjects(this);
  2154 template <class Derived>
  2155 JSObject*
  2156 WorkerPrivateParent<Derived>::WrapObject(JSContext* aCx)
  2158   MOZ_ASSERT(!IsSharedWorker(),
  2159              "We should never wrap a WorkerPrivate for a SharedWorker");
  2161   AssertIsOnParentThread();
  2163   // XXXkhuey this should not need to be rooted, the analysis is dumb.
  2164   // See bug 980181.
  2165   JS::Rooted<JSObject*> wrapper(aCx,
  2166     WorkerBinding::Wrap(aCx, ParentAsWorkerPrivate()));
  2167   if (wrapper) {
  2168     MOZ_ALWAYS_TRUE(TryPreserveWrapper(wrapper));
  2171   return wrapper;
  2174 template <class Derived>
  2175 nsresult
  2176 WorkerPrivateParent<Derived>::DispatchPrivate(WorkerRunnable* aRunnable,
  2177                                               nsIEventTarget* aSyncLoopTarget)
  2179   // May be called on any thread!
  2181   WorkerPrivate* self = ParentAsWorkerPrivate();
  2184     MutexAutoLock lock(mMutex);
  2186     MOZ_ASSERT_IF(aSyncLoopTarget, self->mThread);
  2188     if (!self->mThread) {
  2189       if (ParentStatus() == Pending || self->mStatus == Pending) {
  2190         mPreStartRunnables.AppendElement(aRunnable);
  2191         return NS_OK;
  2194       NS_WARNING("Using a worker event target after the thread has already"
  2195                  "been released!");
  2196       return NS_ERROR_UNEXPECTED;
  2199     if (self->mStatus == Dead ||
  2200         (!aSyncLoopTarget && ParentStatus() > Running)) {
  2201       NS_WARNING("A runnable was posted to a worker that is already shutting "
  2202                  "down!");
  2203       return NS_ERROR_UNEXPECTED;
  2206     nsCOMPtr<nsIEventTarget> target;
  2207     if (aSyncLoopTarget) {
  2208       target = aSyncLoopTarget;
  2210     else {
  2211       target = self->mThread;
  2214     nsresult rv = target->Dispatch(aRunnable, NS_DISPATCH_NORMAL);
  2215     if (NS_WARN_IF(NS_FAILED(rv))) {
  2216       return rv;
  2219     mCondVar.Notify();
  2222   return NS_OK;
  2225 template <class Derived>
  2226 nsresult
  2227 WorkerPrivateParent<Derived>::DispatchControlRunnable(
  2228                                   WorkerControlRunnable* aWorkerControlRunnable)
  2230   // May be called on any thread!
  2232   MOZ_ASSERT(aWorkerControlRunnable);
  2234   nsRefPtr<WorkerControlRunnable> runnable = aWorkerControlRunnable;
  2236   WorkerPrivate* self = ParentAsWorkerPrivate();
  2239     MutexAutoLock lock(mMutex);
  2241     if (self->mStatus == Dead) {
  2242       NS_WARNING("A control runnable was posted to a worker that is already "
  2243                  "shutting down!");
  2244       return NS_ERROR_UNEXPECTED;
  2247     // Transfer ownership to the control queue.
  2248     self->mControlQueue.Push(runnable.forget().take());
  2250     if (JSContext* cx = self->mJSContext) {
  2251       MOZ_ASSERT(self->mThread);
  2253       JSRuntime* rt = JS_GetRuntime(cx);
  2254       MOZ_ASSERT(rt);
  2256       JS_RequestInterruptCallback(rt);
  2259     mCondVar.Notify();
  2262   return NS_OK;
  2265 template <class Derived>
  2266 already_AddRefed<WorkerRunnable>
  2267 WorkerPrivateParent<Derived>::MaybeWrapAsWorkerRunnable(nsIRunnable* aRunnable)
  2269   // May be called on any thread!
  2271   MOZ_ASSERT(aRunnable);
  2273   nsRefPtr<WorkerRunnable> workerRunnable =
  2274     WorkerRunnable::FromRunnable(aRunnable);
  2275   if (workerRunnable) {
  2276     return workerRunnable.forget();
  2279   nsCOMPtr<nsICancelableRunnable> cancelable = do_QueryInterface(aRunnable);
  2280   if (!cancelable) {
  2281     MOZ_CRASH("All runnables destined for a worker thread must be cancelable!");
  2284   workerRunnable =
  2285     new ExternalRunnableWrapper(ParentAsWorkerPrivate(), cancelable);
  2286   return workerRunnable.forget();
  2289 template <class Derived>
  2290 already_AddRefed<nsIEventTarget>
  2291 WorkerPrivateParent<Derived>::GetEventTarget()
  2293   WorkerPrivate* self = ParentAsWorkerPrivate();
  2295   nsCOMPtr<nsIEventTarget> target;
  2298     MutexAutoLock lock(mMutex);
  2300     if (!mEventTarget &&
  2301         ParentStatus() <= Running &&
  2302         self->mStatus <= Running) {
  2303       mEventTarget = new EventTarget(self);
  2306     target = mEventTarget;
  2309   NS_WARN_IF_FALSE(target,
  2310                    "Requested event target for a worker that is already "
  2311                    "shutting down!");
  2313   return target.forget();
  2316 template <class Derived>
  2317 bool
  2318 WorkerPrivateParent<Derived>::Start()
  2320   // May be called on any thread!
  2322     MutexAutoLock lock(mMutex);
  2324     NS_ASSERTION(mParentStatus != Running, "How can this be?!");
  2326     if (mParentStatus == Pending) {
  2327       mParentStatus = Running;
  2328       return true;
  2332   return false;
  2335 // aCx is null when called from the finalizer
  2336 template <class Derived>
  2337 bool
  2338 WorkerPrivateParent<Derived>::NotifyPrivate(JSContext* aCx, Status aStatus)
  2340   AssertIsOnParentThread();
  2342   bool pending;
  2344     MutexAutoLock lock(mMutex);
  2346     if (mParentStatus >= aStatus) {
  2347       return true;
  2350     pending = mParentStatus == Pending;
  2351     mParentStatus = aStatus;
  2354   if (IsSharedWorker()) {
  2355     RuntimeService* runtime = RuntimeService::GetService();
  2356     MOZ_ASSERT(runtime);
  2358     runtime->ForgetSharedWorker(ParentAsWorkerPrivate());
  2361   if (pending) {
  2362     WorkerPrivate* self = ParentAsWorkerPrivate();
  2364 #ifdef DEBUG
  2366       // Fake a thread here just so that our assertions don't go off for no
  2367       // reason.
  2368       nsIThread* currentThread = NS_GetCurrentThread();
  2369       MOZ_ASSERT(currentThread);
  2371       MOZ_ASSERT(!self->mPRThread);
  2372       self->mPRThread = PRThreadFromThread(currentThread);
  2373       MOZ_ASSERT(self->mPRThread);
  2375 #endif
  2377     // Worker never got a chance to run, go ahead and delete it.
  2378     self->ScheduleDeletion(WorkerPrivate::WorkerNeverRan);
  2379     return true;
  2382   // Only top-level workers should have a synchronize runnable.
  2383   MOZ_ASSERT_IF(mSynchronizeRunnable.get(), !GetParent());
  2384   mSynchronizeRunnable.Revoke();
  2386   NS_ASSERTION(aStatus != Terminating || mQueuedRunnables.IsEmpty(),
  2387                "Shouldn't have anything queued!");
  2389   // Anything queued will be discarded.
  2390   mQueuedRunnables.Clear();
  2392   nsRefPtr<NotifyRunnable> runnable =
  2393     new NotifyRunnable(ParentAsWorkerPrivate(), aStatus);
  2394   return runnable->Dispatch(aCx);
  2397 template <class Derived>
  2398 bool
  2399 WorkerPrivateParent<Derived>::Suspend(JSContext* aCx, nsPIDOMWindow* aWindow)
  2401   AssertIsOnParentThread();
  2402   MOZ_ASSERT(aCx);
  2404   // Shared workers are only suspended if all of their owning documents are
  2405   // suspended.
  2406   if (IsSharedWorker()) {
  2407     AssertIsOnMainThread();
  2408     MOZ_ASSERT(mSharedWorkers.Count());
  2410     struct Closure
  2412       nsPIDOMWindow* mWindow;
  2413       bool mAllSuspended;
  2415       Closure(nsPIDOMWindow* aWindow)
  2416       : mWindow(aWindow), mAllSuspended(true)
  2418         AssertIsOnMainThread();
  2419         // aWindow may be null here.
  2422       static PLDHashOperator
  2423       Suspend(const uint64_t& aKey,
  2424               SharedWorker* aSharedWorker,
  2425               void* aClosure)
  2427         AssertIsOnMainThread();
  2428         MOZ_ASSERT(aSharedWorker);
  2429         MOZ_ASSERT(aClosure);
  2431         auto closure = static_cast<Closure*>(aClosure);
  2433         if (closure->mWindow && aSharedWorker->GetOwner() == closure->mWindow) {
  2434           // Calling Suspend() may change the refcount, ensure that the worker
  2435           // outlives this call.
  2436           nsRefPtr<SharedWorker> kungFuDeathGrip = aSharedWorker;
  2438           aSharedWorker->Suspend();
  2439         } else {
  2440           MOZ_ASSERT_IF(aSharedWorker->GetOwner() && closure->mWindow,
  2441                         !SameCOMIdentity(aSharedWorker->GetOwner(),
  2442                                          closure->mWindow));
  2443           if (!aSharedWorker->IsSuspended()) {
  2444             closure->mAllSuspended = false;
  2447         return PL_DHASH_NEXT;
  2449     };
  2451     Closure closure(aWindow);
  2453     mSharedWorkers.EnumerateRead(Closure::Suspend, &closure);
  2455     if (!closure.mAllSuspended || mParentSuspended) {
  2456       return true;
  2460 //  MOZ_ASSERT(!mParentSuspended, "Suspended more than once!");
  2462   mParentSuspended = true;
  2465     MutexAutoLock lock(mMutex);
  2467     if (mParentStatus >= Terminating) {
  2468       return true;
  2472   nsRefPtr<SuspendRunnable> runnable =
  2473     new SuspendRunnable(ParentAsWorkerPrivate());
  2474   if (!runnable->Dispatch(aCx)) {
  2475     return false;
  2478   return true;
  2481 template <class Derived>
  2482 bool
  2483 WorkerPrivateParent<Derived>::Resume(JSContext* aCx, nsPIDOMWindow* aWindow)
  2485   AssertIsOnParentThread();
  2486   MOZ_ASSERT(aCx);
  2487   MOZ_ASSERT_IF(!IsSharedWorker(), mParentSuspended);
  2489   // Shared workers are resumed if any of their owning documents are resumed.
  2490   if (IsSharedWorker()) {
  2491     AssertIsOnMainThread();
  2492     MOZ_ASSERT(mSharedWorkers.Count());
  2494     struct Closure
  2496       nsPIDOMWindow* mWindow;
  2497       bool mAnyRunning;
  2499       Closure(nsPIDOMWindow* aWindow)
  2500       : mWindow(aWindow), mAnyRunning(false)
  2502         AssertIsOnMainThread();
  2503         // aWindow may be null here.
  2506       static PLDHashOperator
  2507       Resume(const uint64_t& aKey,
  2508               SharedWorker* aSharedWorker,
  2509               void* aClosure)
  2511         AssertIsOnMainThread();
  2512         MOZ_ASSERT(aSharedWorker);
  2513         MOZ_ASSERT(aClosure);
  2515         auto closure = static_cast<Closure*>(aClosure);
  2517         if (closure->mWindow && aSharedWorker->GetOwner() == closure->mWindow) {
  2518           // Calling Resume() may change the refcount, ensure that the worker
  2519           // outlives this call.
  2520           nsRefPtr<SharedWorker> kungFuDeathGrip = aSharedWorker;
  2522           aSharedWorker->Resume();
  2523           closure->mAnyRunning = true;
  2524         } else {
  2525           MOZ_ASSERT_IF(aSharedWorker->GetOwner() && closure->mWindow,
  2526                         !SameCOMIdentity(aSharedWorker->GetOwner(),
  2527                                          closure->mWindow));
  2528           if (!aSharedWorker->IsSuspended()) {
  2529             closure->mAnyRunning = true;
  2532         return PL_DHASH_NEXT;
  2534     };
  2536     Closure closure(aWindow);
  2538     mSharedWorkers.EnumerateRead(Closure::Resume, &closure);
  2540     if (!closure.mAnyRunning || !mParentSuspended) {
  2541       return true;
  2545   MOZ_ASSERT(mParentSuspended);
  2547   mParentSuspended = false;
  2550     MutexAutoLock lock(mMutex);
  2552     if (mParentStatus >= Terminating) {
  2553       return true;
  2557   // Only top-level workers should have a synchronize runnable.
  2558   MOZ_ASSERT_IF(mSynchronizeRunnable.get(), !GetParent());
  2559   mSynchronizeRunnable.Revoke();
  2561   // Execute queued runnables before waking up the worker, otherwise the worker
  2562   // could post new messages before we run those that have been queued.
  2563   if (!mQueuedRunnables.IsEmpty()) {
  2564     AssertIsOnMainThread();
  2565     MOZ_ASSERT(IsDedicatedWorker());
  2567     nsTArray<nsCOMPtr<nsIRunnable>> runnables;
  2568     mQueuedRunnables.SwapElements(runnables);
  2570     for (uint32_t index = 0; index < runnables.Length(); index++) {
  2571       runnables[index]->Run();
  2575   nsRefPtr<ResumeRunnable> runnable =
  2576     new ResumeRunnable(ParentAsWorkerPrivate());
  2577   if (!runnable->Dispatch(aCx)) {
  2578     return false;
  2581   return true;
  2584 template <class Derived>
  2585 bool
  2586 WorkerPrivateParent<Derived>::SynchronizeAndResume(
  2587                                                JSContext* aCx,
  2588                                                nsPIDOMWindow* aWindow,
  2589                                                nsIScriptContext* aScriptContext)
  2591   AssertIsOnMainThread();
  2592   MOZ_ASSERT(!GetParent());
  2593   MOZ_ASSERT_IF(IsDedicatedWorker(), mParentSuspended);
  2595   // NB: There may be pending unqueued messages.  If we resume here we will
  2596   // execute those messages out of order.  Instead we post an event to the
  2597   // end of the event queue, allowing all of the outstanding messages to be
  2598   // queued up in order on the worker.  Then and only then we execute all of
  2599   // the messages.
  2601   nsRefPtr<SynchronizeAndResumeRunnable> runnable =
  2602     new SynchronizeAndResumeRunnable(ParentAsWorkerPrivate(), aWindow,
  2603                                      aScriptContext);
  2604   if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) {
  2605     JS_ReportError(aCx, "Failed to dispatch to current thread!");
  2606     return false;
  2609   mSynchronizeRunnable = runnable;
  2610   return true;
  2613 template <class Derived>
  2614 bool
  2615 WorkerPrivateParent<Derived>::Close(JSContext* aCx)
  2617   AssertIsOnParentThread();
  2620     MutexAutoLock lock(mMutex);
  2622     if (mParentStatus < Closing) {
  2623       mParentStatus = Closing;
  2627   return true;
  2630 template <class Derived>
  2631 bool
  2632 WorkerPrivateParent<Derived>::ModifyBusyCount(JSContext* aCx, bool aIncrease)
  2634   AssertIsOnParentThread();
  2636   NS_ASSERTION(aIncrease || mBusyCount, "Mismatched busy count mods!");
  2638   if (aIncrease) {
  2639     mBusyCount++;
  2640     return true;
  2643   if (--mBusyCount == 0) {
  2645     bool shouldCancel;
  2647       MutexAutoLock lock(mMutex);
  2648       shouldCancel = mParentStatus == Terminating;
  2651     if (shouldCancel && !Cancel(aCx)) {
  2652       return false;
  2656   return true;
  2659 template <class Derived>
  2660 void
  2661 WorkerPrivateParent<Derived>::ForgetMainThreadObjects(
  2662                                       nsTArray<nsCOMPtr<nsISupports> >& aDoomed)
  2664   AssertIsOnParentThread();
  2665   MOZ_ASSERT(!mMainThreadObjectsForgotten);
  2667   static const uint32_t kDoomedCount = 7;
  2669   aDoomed.SetCapacity(kDoomedCount);
  2671   SwapToISupportsArray(mLoadInfo.mWindow, aDoomed);
  2672   SwapToISupportsArray(mLoadInfo.mScriptContext, aDoomed);
  2673   SwapToISupportsArray(mLoadInfo.mBaseURI, aDoomed);
  2674   SwapToISupportsArray(mLoadInfo.mResolvedScriptURI, aDoomed);
  2675   SwapToISupportsArray(mLoadInfo.mPrincipal, aDoomed);
  2676   SwapToISupportsArray(mLoadInfo.mChannel, aDoomed);
  2677   SwapToISupportsArray(mLoadInfo.mCSP, aDoomed);
  2678   // Before adding anything here update kDoomedCount above!
  2680   MOZ_ASSERT(aDoomed.Length() == kDoomedCount);
  2682   mMainThreadObjectsForgotten = true;
  2685 template <class Derived>
  2686 void
  2687 WorkerPrivateParent<Derived>::PostMessageInternal(
  2688                                             JSContext* aCx,
  2689                                             JS::Handle<JS::Value> aMessage,
  2690                                             const Optional<Sequence<JS::Value> >& aTransferable,
  2691                                             bool aToMessagePort,
  2692                                             uint64_t aMessagePortSerial,
  2693                                             ErrorResult& aRv)
  2695   AssertIsOnParentThread();
  2698     MutexAutoLock lock(mMutex);
  2699     if (mParentStatus > Running) {
  2700       return;
  2704   JSStructuredCloneCallbacks* callbacks;
  2705   if (GetParent()) {
  2706     if (IsChromeWorker()) {
  2707       callbacks = &gChromeWorkerStructuredCloneCallbacks;
  2709     else {
  2710       callbacks = &gWorkerStructuredCloneCallbacks;
  2713   else {
  2714     AssertIsOnMainThread();
  2716     if (IsChromeWorker()) {
  2717       callbacks = &gMainThreadChromeWorkerStructuredCloneCallbacks;
  2719     else {
  2720       callbacks = &gMainThreadWorkerStructuredCloneCallbacks;
  2724   JS::Rooted<JS::Value> transferable(aCx, JS::UndefinedValue());
  2725   if (aTransferable.WasPassed()) {
  2726     const Sequence<JS::Value>& realTransferable = aTransferable.Value();
  2728     // The input sequence only comes from the generated bindings code, which
  2729     // ensures it is rooted.
  2730     JS::HandleValueArray elements =
  2731       JS::HandleValueArray::fromMarkedLocation(realTransferable.Length(),
  2732                                                realTransferable.Elements());
  2734     JSObject* array =
  2735       JS_NewArrayObject(aCx, elements);
  2736     if (!array) {
  2737       aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
  2738       return;
  2740     transferable.setObject(*array);
  2743   nsTArray<nsCOMPtr<nsISupports> > clonedObjects;
  2745   JSAutoStructuredCloneBuffer buffer;
  2746   if (!buffer.write(aCx, aMessage, transferable, callbacks, &clonedObjects)) {
  2747     aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
  2748     return;
  2751   nsRefPtr<MessageEventRunnable> runnable =
  2752     new MessageEventRunnable(ParentAsWorkerPrivate(),
  2753                              WorkerRunnable::WorkerThreadModifyBusyCount,
  2754                              Move(buffer), clonedObjects, aToMessagePort,
  2755                              aMessagePortSerial);
  2756   if (!runnable->Dispatch(aCx)) {
  2757     aRv.Throw(NS_ERROR_FAILURE);
  2761 template <class Derived>
  2762 void
  2763 WorkerPrivateParent<Derived>::PostMessageToMessagePort(
  2764                              JSContext* aCx,
  2765                              uint64_t aMessagePortSerial,
  2766                              JS::Handle<JS::Value> aMessage,
  2767                              const Optional<Sequence<JS::Value>>& aTransferable,
  2768                              ErrorResult& aRv)
  2770   AssertIsOnMainThread();
  2772   PostMessageInternal(aCx, aMessage, aTransferable, true, aMessagePortSerial,
  2773                       aRv);
  2776 template <class Derived>
  2777 bool
  2778 WorkerPrivateParent<Derived>::DispatchMessageEventToMessagePort(
  2779                                 JSContext* aCx, uint64_t aMessagePortSerial,
  2780                                 JSAutoStructuredCloneBuffer&& aBuffer,
  2781                                 nsTArray<nsCOMPtr<nsISupports>>& aClonedObjects)
  2783   AssertIsOnMainThread();
  2785   JSAutoStructuredCloneBuffer buffer(Move(aBuffer));
  2787   nsTArray<nsCOMPtr<nsISupports>> clonedObjects;
  2788   clonedObjects.SwapElements(aClonedObjects);
  2790   SharedWorker* sharedWorker;
  2791   if (!mSharedWorkers.Get(aMessagePortSerial, &sharedWorker)) {
  2792     // SharedWorker has already been unregistered?
  2793     return true;
  2796   nsRefPtr<MessagePort> port = sharedWorker->Port();
  2797   NS_ASSERTION(port, "SharedWorkers always have a port!");
  2799   if (port->IsClosed()) {
  2800     return true;
  2803   nsCOMPtr<nsIScriptGlobalObject> sgo;
  2804   port->GetParentObject(getter_AddRefs(sgo));
  2805   MOZ_ASSERT(sgo, "Should never happen if IsClosed() returned false!");
  2806   MOZ_ASSERT(sgo->GetGlobalJSObject());
  2808   nsCOMPtr<nsIScriptContext> scx = sgo->GetContext();
  2809   MOZ_ASSERT_IF(scx, scx->GetNativeContext());
  2811   AutoPushJSContext cx(scx ? scx->GetNativeContext() : aCx);
  2812   JSAutoCompartment(cx, sgo->GetGlobalJSObject());
  2814   JS::Rooted<JS::Value> data(cx);
  2815   if (!buffer.read(cx, &data, WorkerStructuredCloneCallbacks(true))) {
  2816     return false;
  2819   buffer.clear();
  2821   nsRefPtr<MessageEvent> event = new MessageEvent(port, nullptr, nullptr);
  2822   nsresult rv =
  2823     event->InitMessageEvent(NS_LITERAL_STRING("message"), false, false, data,
  2824                             EmptyString(), EmptyString(), nullptr);
  2825   if (NS_FAILED(rv)) {
  2826     xpc::Throw(cx, rv);
  2827     return false;
  2830   event->SetTrusted(true);
  2832   nsTArray<nsRefPtr<MessagePortBase>> ports;
  2833   ports.AppendElement(port);
  2835   nsRefPtr<MessagePortList> portList = new MessagePortList(port, ports);
  2836   event->SetPorts(portList);
  2838   nsCOMPtr<nsIDOMEvent> domEvent;
  2839   CallQueryInterface(event.get(), getter_AddRefs(domEvent));
  2840   NS_ASSERTION(domEvent, "This should never fail!");
  2842   bool ignored;
  2843   rv = port->DispatchEvent(domEvent, &ignored);
  2844   if (NS_FAILED(rv)) {
  2845     xpc::Throw(cx, rv);
  2846     return false;
  2849   return true;
  2852 template <class Derived>
  2853 uint64_t
  2854 WorkerPrivateParent<Derived>::GetInnerWindowId()
  2856   AssertIsOnMainThread();
  2857   NS_ASSERTION(!mLoadInfo.mWindow || mLoadInfo.mWindow->IsInnerWindow(),
  2858                "Outer window?");
  2859   return mLoadInfo.mWindow ? mLoadInfo.mWindow->WindowID() : 0;
  2862 template <class Derived>
  2863 void
  2864 WorkerPrivateParent<Derived>::UpdateRuntimeAndContextOptions(
  2865                                     JSContext* aCx,
  2866                                     const JS::RuntimeOptions& aRuntimeOptions,
  2867                                     const JS::ContextOptions& aContentCxOptions,
  2868                                     const JS::ContextOptions& aChromeCxOptions)
  2870   AssertIsOnParentThread();
  2873     MutexAutoLock lock(mMutex);
  2874     mJSSettings.runtimeOptions = aRuntimeOptions;
  2875     mJSSettings.content.contextOptions = aContentCxOptions;
  2876     mJSSettings.chrome.contextOptions = aChromeCxOptions;
  2879   nsRefPtr<UpdateRuntimeAndContextOptionsRunnable> runnable =
  2880     new UpdateRuntimeAndContextOptionsRunnable(ParentAsWorkerPrivate(),
  2881                                                aRuntimeOptions,
  2882                                                aContentCxOptions,
  2883                                                aChromeCxOptions);
  2884   if (!runnable->Dispatch(aCx)) {
  2885     NS_WARNING("Failed to update worker context options!");
  2886     JS_ClearPendingException(aCx);
  2890 template <class Derived>
  2891 void
  2892 WorkerPrivateParent<Derived>::UpdatePreference(JSContext* aCx, WorkerPreference aPref, bool aValue)
  2894   AssertIsOnParentThread();
  2895   MOZ_ASSERT(aPref >= 0 && aPref < WORKERPREF_COUNT);
  2897   nsRefPtr<UpdatePreferenceRunnable> runnable =
  2898     new UpdatePreferenceRunnable(ParentAsWorkerPrivate(), aPref, aValue);
  2899   if (!runnable->Dispatch(aCx)) {
  2900     NS_WARNING("Failed to update worker preferences!");
  2901     JS_ClearPendingException(aCx);
  2905 template <class Derived>
  2906 void
  2907 WorkerPrivateParent<Derived>::UpdateJSWorkerMemoryParameter(JSContext* aCx,
  2908                                                             JSGCParamKey aKey,
  2909                                                             uint32_t aValue)
  2911   AssertIsOnParentThread();
  2913   bool found = false;
  2916     MutexAutoLock lock(mMutex);
  2917     found = mJSSettings.ApplyGCSetting(aKey, aValue);
  2920   if (found) {
  2921     nsRefPtr<UpdateJSWorkerMemoryParameterRunnable> runnable =
  2922       new UpdateJSWorkerMemoryParameterRunnable(ParentAsWorkerPrivate(), aKey,
  2923                                                 aValue);
  2924     if (!runnable->Dispatch(aCx)) {
  2925       NS_WARNING("Failed to update memory parameter!");
  2926       JS_ClearPendingException(aCx);
  2931 #ifdef JS_GC_ZEAL
  2932 template <class Derived>
  2933 void
  2934 WorkerPrivateParent<Derived>::UpdateGCZeal(JSContext* aCx, uint8_t aGCZeal,
  2935                                            uint32_t aFrequency)
  2937   AssertIsOnParentThread();
  2940     MutexAutoLock lock(mMutex);
  2941     mJSSettings.gcZeal = aGCZeal;
  2942     mJSSettings.gcZealFrequency = aFrequency;
  2945   nsRefPtr<UpdateGCZealRunnable> runnable =
  2946     new UpdateGCZealRunnable(ParentAsWorkerPrivate(), aGCZeal, aFrequency);
  2947   if (!runnable->Dispatch(aCx)) {
  2948     NS_WARNING("Failed to update worker gczeal!");
  2949     JS_ClearPendingException(aCx);
  2952 #endif
  2954 template <class Derived>
  2955 void
  2956 WorkerPrivateParent<Derived>::GarbageCollect(JSContext* aCx, bool aShrinking)
  2958   AssertIsOnParentThread();
  2960   nsRefPtr<GarbageCollectRunnable> runnable =
  2961     new GarbageCollectRunnable(ParentAsWorkerPrivate(), aShrinking,
  2962                                /* collectChildren = */ true);
  2963   if (!runnable->Dispatch(aCx)) {
  2964     NS_WARNING("Failed to GC worker!");
  2965     JS_ClearPendingException(aCx);
  2969 template <class Derived>
  2970 void
  2971 WorkerPrivateParent<Derived>::CycleCollect(JSContext* aCx, bool aDummy)
  2973   AssertIsOnParentThread();
  2975   nsRefPtr<CycleCollectRunnable> runnable =
  2976     new CycleCollectRunnable(ParentAsWorkerPrivate(),
  2977                              /* collectChildren = */ true);
  2978   if (!runnable->Dispatch(aCx)) {
  2979     NS_WARNING("Failed to CC worker!");
  2980     JS_ClearPendingException(aCx);
  2984 template <class Derived>
  2985 void
  2986 WorkerPrivateParent<Derived>::OfflineStatusChangeEvent(JSContext* aCx, bool aIsOffline)
  2988   AssertIsOnParentThread();
  2990   nsRefPtr<OfflineStatusChangeRunnable> runnable =
  2991     new OfflineStatusChangeRunnable(ParentAsWorkerPrivate(), aIsOffline);
  2992   if (!runnable->Dispatch(aCx)) {
  2993     NS_WARNING("Failed to dispatch offline status change event!");
  2994     JS_ClearPendingException(aCx);
  2998 void
  2999 WorkerPrivate::OfflineStatusChangeEventInternal(JSContext* aCx, bool aIsOffline)
  3001   AssertIsOnWorkerThread();
  3003   for (uint32_t index = 0; index < mChildWorkers.Length(); ++index) {
  3004     mChildWorkers[index]->OfflineStatusChangeEvent(aCx, aIsOffline);
  3007   mOnLine = !aIsOffline;
  3008   WorkerGlobalScope* globalScope = GlobalScope();
  3009   nsRefPtr<WorkerNavigator> nav = globalScope->GetExistingNavigator();
  3010   if (nav) {
  3011     nav->SetOnLine(mOnLine);
  3014   nsString eventType;
  3015   if (aIsOffline) {
  3016     eventType.AssignLiteral("offline");
  3017   } else {
  3018     eventType.AssignLiteral("online");
  3021   nsCOMPtr<nsIDOMEvent> event;
  3022   nsresult rv =
  3023     NS_NewDOMEvent(getter_AddRefs(event), globalScope, nullptr, nullptr);
  3024   NS_ENSURE_SUCCESS_VOID(rv);
  3026   rv = event->InitEvent(eventType, false, false);
  3027   NS_ENSURE_SUCCESS_VOID(rv);
  3029   event->SetTrusted(true);
  3031   globalScope->DispatchDOMEvent(nullptr, event, nullptr, nullptr);
  3034 template <class Derived>
  3035 bool
  3036 WorkerPrivateParent<Derived>::RegisterSharedWorker(JSContext* aCx,
  3037                                                    SharedWorker* aSharedWorker)
  3039   AssertIsOnMainThread();
  3040   MOZ_ASSERT(aSharedWorker);
  3041   MOZ_ASSERT(IsSharedWorker());
  3042   MOZ_ASSERT(!mSharedWorkers.Get(aSharedWorker->Serial()));
  3044   nsRefPtr<MessagePortRunnable> runnable =
  3045     new MessagePortRunnable(ParentAsWorkerPrivate(), aSharedWorker->Serial(),
  3046                             true);
  3047   if (!runnable->Dispatch(aCx)) {
  3048     return false;
  3051   mSharedWorkers.Put(aSharedWorker->Serial(), aSharedWorker);
  3053   // If there were other SharedWorker objects attached to this worker then they
  3054   // may all have been suspended and this worker would need to be resumed.
  3055   if (mSharedWorkers.Count() > 1 && !Resume(aCx, nullptr)) {
  3056     return false;
  3059   return true;
  3062 template <class Derived>
  3063 void
  3064 WorkerPrivateParent<Derived>::UnregisterSharedWorker(
  3065                                                     JSContext* aCx,
  3066                                                     SharedWorker* aSharedWorker)
  3068   AssertIsOnMainThread();
  3069   MOZ_ASSERT(aSharedWorker);
  3070   MOZ_ASSERT(IsSharedWorker());
  3071   MOZ_ASSERT(mSharedWorkers.Get(aSharedWorker->Serial()));
  3073   nsRefPtr<MessagePortRunnable> runnable =
  3074     new MessagePortRunnable(ParentAsWorkerPrivate(), aSharedWorker->Serial(),
  3075                             false);
  3076   if (!runnable->Dispatch(aCx)) {
  3077     JS_ReportPendingException(aCx);
  3080   mSharedWorkers.Remove(aSharedWorker->Serial());
  3082   // If there are still SharedWorker objects attached to this worker then they
  3083   // may all be suspended and this worker would need to be suspended. Otherwise,
  3084   // if that was the last SharedWorker then it's time to cancel this worker.
  3085   if (mSharedWorkers.Count()) {
  3086     if (!Suspend(aCx, nullptr)) {
  3087       JS_ReportPendingException(aCx);
  3089   } else if (!Cancel(aCx)) {
  3090     JS_ReportPendingException(aCx);
  3094 template <class Derived>
  3095 void
  3096 WorkerPrivateParent<Derived>::BroadcastErrorToSharedWorkers(
  3097                                                     JSContext* aCx,
  3098                                                     const nsAString& aMessage,
  3099                                                     const nsAString& aFilename,
  3100                                                     const nsAString& aLine,
  3101                                                     uint32_t aLineNumber,
  3102                                                     uint32_t aColumnNumber,
  3103                                                     uint32_t aFlags)
  3105   AssertIsOnMainThread();
  3107   nsAutoTArray<nsRefPtr<SharedWorker>, 10> sharedWorkers;
  3108   GetAllSharedWorkers(sharedWorkers);
  3110   if (sharedWorkers.IsEmpty()) {
  3111     return;
  3114   nsAutoTArray<WindowAction, 10> windowActions;
  3115   nsresult rv;
  3117   // First fire the error event at all SharedWorker objects. This may include
  3118   // multiple objects in a single window as well as objects in different
  3119   // windows.
  3120   for (uint32_t index = 0; index < sharedWorkers.Length(); index++) {
  3121     nsRefPtr<SharedWorker>& sharedWorker = sharedWorkers[index];
  3123     // May be null.
  3124     nsPIDOMWindow* window = sharedWorker->GetOwner();
  3126     uint32_t actionsIndex = windowActions.LastIndexOf(WindowAction(window));
  3128     nsIGlobalObject* global = sharedWorker->GetParentObject();
  3129     AutoJSAPIWithErrorsReportedToWindow jsapi(global);
  3130     JSContext* cx = jsapi.cx();
  3131     JSAutoCompartment ac(cx, global->GetGlobalJSObject());
  3133     RootedDictionary<ErrorEventInit> errorInit(aCx);
  3134     errorInit.mBubbles = false;
  3135     errorInit.mCancelable = true;
  3136     errorInit.mMessage = aMessage;
  3137     errorInit.mFilename = aFilename;
  3138     errorInit.mLineno = aLineNumber;
  3139     errorInit.mColno = aColumnNumber;
  3141     nsRefPtr<ErrorEvent> errorEvent =
  3142       ErrorEvent::Constructor(sharedWorker, NS_LITERAL_STRING("error"),
  3143                               errorInit);
  3144     if (!errorEvent) {
  3145       Throw(cx, NS_ERROR_UNEXPECTED);
  3146       JS_ReportPendingException(cx);
  3147       continue;
  3150     errorEvent->SetTrusted(true);
  3152     bool defaultActionEnabled;
  3153     nsresult rv = sharedWorker->DispatchEvent(errorEvent, &defaultActionEnabled);
  3154     if (NS_FAILED(rv)) {
  3155       Throw(cx, rv);
  3156       JS_ReportPendingException(cx);
  3157       continue;
  3160     if (defaultActionEnabled) {
  3161       // Add the owning window to our list so that we will fire an error event
  3162       // at it later.
  3163       if (!windowActions.Contains(window)) {
  3164         windowActions.AppendElement(WindowAction(window, cx));
  3166     } else if (actionsIndex != windowActions.NoIndex) {
  3167       // Any listener that calls preventDefault() will prevent the window from
  3168       // receiving the error event.
  3169       windowActions[actionsIndex].mDefaultAction = false;
  3173   // If there are no windows to consider further then we're done.
  3174   if (windowActions.IsEmpty()) {
  3175     return;
  3178   bool shouldLogErrorToConsole = true;
  3180   // Now fire error events at all the windows remaining.
  3181   for (uint32_t index = 0; index < windowActions.Length(); index++) {
  3182     WindowAction& windowAction = windowActions[index];
  3184     // If there is no window or the script already called preventDefault then
  3185     // skip this window.
  3186     if (!windowAction.mWindow || !windowAction.mDefaultAction) {
  3187       continue;
  3190     JSContext* cx = windowAction.mJSContext;
  3192     AutoCxPusher autoPush(cx);
  3194     nsCOMPtr<nsIScriptGlobalObject> sgo =
  3195       do_QueryInterface(windowAction.mWindow);
  3196     MOZ_ASSERT(sgo);
  3198     MOZ_ASSERT(NS_IsMainThread());
  3199     RootedDictionary<ErrorEventInit> init(aCx);
  3200     init.mLineno = aLineNumber;
  3201     init.mFilename = aFilename;
  3202     init.mMessage = aMessage;
  3203     init.mCancelable = true;
  3204     init.mBubbles = true;
  3206     nsEventStatus status = nsEventStatus_eIgnore;
  3207     rv = sgo->HandleScriptError(init, &status);
  3208     if (NS_FAILED(rv)) {
  3209       Throw(cx, rv);
  3210       JS_ReportPendingException(cx);
  3211       continue;
  3214     if (status == nsEventStatus_eConsumeNoDefault) {
  3215       shouldLogErrorToConsole = false;
  3219   // Finally log a warning in the console if no window tried to prevent it.
  3220   if (shouldLogErrorToConsole) {
  3221     LogErrorToConsole(aMessage, aFilename, aLine, aLineNumber, aColumnNumber,
  3222                       aFlags, 0);
  3226 template <class Derived>
  3227 void
  3228 WorkerPrivateParent<Derived>::GetAllSharedWorkers(
  3229                                nsTArray<nsRefPtr<SharedWorker>>& aSharedWorkers)
  3231   AssertIsOnMainThread();
  3232   MOZ_ASSERT(IsSharedWorker());
  3234   struct Helper
  3236     static PLDHashOperator
  3237     Collect(const uint64_t& aKey,
  3238             SharedWorker* aSharedWorker,
  3239             void* aClosure)
  3241       AssertIsOnMainThread();
  3242       MOZ_ASSERT(aSharedWorker);
  3243       MOZ_ASSERT(aClosure);
  3245       auto array = static_cast<nsTArray<nsRefPtr<SharedWorker>>*>(aClosure);
  3246       array->AppendElement(aSharedWorker);
  3248       return PL_DHASH_NEXT;
  3250   };
  3252   if (!aSharedWorkers.IsEmpty()) {
  3253     aSharedWorkers.Clear();
  3256   mSharedWorkers.EnumerateRead(Helper::Collect, &aSharedWorkers);
  3259 template <class Derived>
  3260 void
  3261 WorkerPrivateParent<Derived>::CloseSharedWorkersForWindow(
  3262                                                          nsPIDOMWindow* aWindow)
  3264   AssertIsOnMainThread();
  3265   MOZ_ASSERT(IsSharedWorker());
  3266   MOZ_ASSERT(aWindow);
  3268   struct Closure
  3270     nsPIDOMWindow* mWindow;
  3271     nsAutoTArray<nsRefPtr<SharedWorker>, 10> mSharedWorkers;
  3273     Closure(nsPIDOMWindow* aWindow)
  3274     : mWindow(aWindow)
  3276       AssertIsOnMainThread();
  3277       MOZ_ASSERT(aWindow);
  3280     static PLDHashOperator
  3281     Collect(const uint64_t& aKey,
  3282             SharedWorker* aSharedWorker,
  3283             void* aClosure)
  3285       AssertIsOnMainThread();
  3286       MOZ_ASSERT(aSharedWorker);
  3287       MOZ_ASSERT(aClosure);
  3289       auto closure = static_cast<Closure*>(aClosure);
  3290       MOZ_ASSERT(closure->mWindow);
  3292       if (aSharedWorker->GetOwner() == closure->mWindow) {
  3293         closure->mSharedWorkers.AppendElement(aSharedWorker);
  3294       } else {
  3295         MOZ_ASSERT(!SameCOMIdentity(aSharedWorker->GetOwner(),
  3296                                     closure->mWindow));
  3299       return PL_DHASH_NEXT;
  3301   };
  3303   Closure closure(aWindow);
  3305   mSharedWorkers.EnumerateRead(Closure::Collect, &closure);
  3307   for (uint32_t index = 0; index < closure.mSharedWorkers.Length(); index++) {
  3308     closure.mSharedWorkers[index]->Close();
  3312 template <class Derived>
  3313 void
  3314 WorkerPrivateParent<Derived>::WorkerScriptLoaded()
  3316   AssertIsOnMainThread();
  3318   if (IsSharedWorker()) {
  3319     // No longer need to hold references to the window or document we came from.
  3320     mLoadInfo.mWindow = nullptr;
  3321     mLoadInfo.mScriptContext = nullptr;
  3325 template <class Derived>
  3326 void
  3327 WorkerPrivateParent<Derived>::SetBaseURI(nsIURI* aBaseURI)
  3329   AssertIsOnMainThread();
  3331   if (!mLoadInfo.mBaseURI) {
  3332     NS_ASSERTION(GetParent(), "Shouldn't happen without a parent!");
  3333     mLoadInfo.mResolvedScriptURI = aBaseURI;
  3336   mLoadInfo.mBaseURI = aBaseURI;
  3338   if (NS_FAILED(aBaseURI->GetSpec(mLocationInfo.mHref))) {
  3339     mLocationInfo.mHref.Truncate();
  3342   if (NS_FAILED(aBaseURI->GetHost(mLocationInfo.mHostname))) {
  3343     mLocationInfo.mHostname.Truncate();
  3346   if (NS_FAILED(aBaseURI->GetPath(mLocationInfo.mPathname))) {
  3347     mLocationInfo.mPathname.Truncate();
  3350   nsCString temp;
  3352   nsCOMPtr<nsIURL> url(do_QueryInterface(aBaseURI));
  3353   if (url && NS_SUCCEEDED(url->GetQuery(temp)) && !temp.IsEmpty()) {
  3354     mLocationInfo.mSearch.AssignLiteral("?");
  3355     mLocationInfo.mSearch.Append(temp);
  3358   if (NS_SUCCEEDED(aBaseURI->GetRef(temp)) && !temp.IsEmpty()) {
  3359     nsCOMPtr<nsITextToSubURI> converter =
  3360       do_GetService(NS_ITEXTTOSUBURI_CONTRACTID);
  3361     if (converter) {
  3362       nsCString charset;
  3363       nsAutoString unicodeRef;
  3364       if (NS_SUCCEEDED(aBaseURI->GetOriginCharset(charset)) &&
  3365           NS_SUCCEEDED(converter->UnEscapeURIForUI(charset, temp,
  3366                                                    unicodeRef))) {
  3367         mLocationInfo.mHash.AssignLiteral("#");
  3368         mLocationInfo.mHash.Append(NS_ConvertUTF16toUTF8(unicodeRef));
  3372     if (mLocationInfo.mHash.IsEmpty()) {
  3373       mLocationInfo.mHash.AssignLiteral("#");
  3374       mLocationInfo.mHash.Append(temp);
  3378   if (NS_SUCCEEDED(aBaseURI->GetScheme(mLocationInfo.mProtocol))) {
  3379     mLocationInfo.mProtocol.AppendLiteral(":");
  3381   else {
  3382     mLocationInfo.mProtocol.Truncate();
  3385   int32_t port;
  3386   if (NS_SUCCEEDED(aBaseURI->GetPort(&port)) && port != -1) {
  3387     mLocationInfo.mPort.AppendInt(port);
  3389     nsAutoCString host(mLocationInfo.mHostname);
  3390     host.AppendLiteral(":");
  3391     host.Append(mLocationInfo.mPort);
  3393     mLocationInfo.mHost.Assign(host);
  3395   else {
  3396     mLocationInfo.mHost.Assign(mLocationInfo.mHostname);
  3399   nsContentUtils::GetUTFNonNullOrigin(aBaseURI, mLocationInfo.mOrigin);
  3402 template <class Derived>
  3403 void
  3404 WorkerPrivateParent<Derived>::SetPrincipal(nsIPrincipal* aPrincipal)
  3406   AssertIsOnMainThread();
  3408   mLoadInfo.mPrincipal = aPrincipal;
  3409   mLoadInfo.mPrincipalIsSystem = nsContentUtils::IsSystemPrincipal(aPrincipal);
  3410   uint16_t appStatus = aPrincipal->GetAppStatus();
  3411   mLoadInfo.mIsInPrivilegedApp =
  3412     (appStatus == nsIPrincipal::APP_STATUS_CERTIFIED ||
  3413      appStatus == nsIPrincipal::APP_STATUS_PRIVILEGED);
  3414   mLoadInfo.mIsInCertifiedApp = (appStatus == nsIPrincipal::APP_STATUS_CERTIFIED);
  3417 template <class Derived>
  3418 JSContext*
  3419 WorkerPrivateParent<Derived>::ParentJSContext() const
  3421   AssertIsOnParentThread();
  3423   if (mParent) {
  3424     return mParent->GetJSContext();
  3427   AssertIsOnMainThread();
  3429   return mLoadInfo.mScriptContext ?
  3430          mLoadInfo.mScriptContext->GetNativeContext() :
  3431          nsContentUtils::GetSafeJSContext();
  3434 template <class Derived>
  3435 void
  3436 WorkerPrivateParent<Derived>::RegisterHostObjectURI(const nsACString& aURI)
  3438   AssertIsOnMainThread();
  3439   mHostObjectURIs.AppendElement(aURI);
  3442 template <class Derived>
  3443 void
  3444 WorkerPrivateParent<Derived>::UnregisterHostObjectURI(const nsACString& aURI)
  3446   AssertIsOnMainThread();
  3447   mHostObjectURIs.RemoveElement(aURI);
  3450 template <class Derived>
  3451 void
  3452 WorkerPrivateParent<Derived>::StealHostObjectURIs(nsTArray<nsCString>& aArray)
  3454   aArray.SwapElements(mHostObjectURIs);
  3457 template <class Derived>
  3458 NS_IMPL_ADDREF_INHERITED(WorkerPrivateParent<Derived>, DOMEventTargetHelper)
  3460 template <class Derived>
  3461 NS_IMPL_RELEASE_INHERITED(WorkerPrivateParent<Derived>, DOMEventTargetHelper)
  3463 template <class Derived>
  3464 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(WorkerPrivateParent<Derived>)
  3465 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
  3467 template <class Derived>
  3468 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(WorkerPrivateParent<Derived>,
  3469                                                   DOMEventTargetHelper)
  3470   tmp->AssertIsOnParentThread();
  3472   // The WorkerPrivate::mSelfRef has a reference to itself, which is really
  3473   // held by the worker thread.  We traverse this reference if and only if our
  3474   // busy count is zero and we have not released the main thread reference.
  3475   // We do not unlink it.  This allows the CC to break cycles involving the
  3476   // WorkerPrivate and begin shutting it down (which does happen in unlink) but
  3477   // ensures that the WorkerPrivate won't be deleted before we're done shutting
  3478   // down the thread.
  3480   if (!tmp->mBusyCount && !tmp->mMainThreadObjectsForgotten) {
  3481     NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSelfRef)
  3484   // The various strong references in LoadInfo are managed manually and cannot
  3485   // be cycle collected.
  3486 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
  3488 template <class Derived>
  3489 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(WorkerPrivateParent<Derived>,
  3490                                                 DOMEventTargetHelper)
  3491   tmp->Terminate(nullptr);
  3492 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
  3494 template <class Derived>
  3495 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(WorkerPrivateParent<Derived>,
  3496                                                DOMEventTargetHelper)
  3497   tmp->AssertIsOnParentThread();
  3498 NS_IMPL_CYCLE_COLLECTION_TRACE_END
  3500 #ifdef DEBUG
  3502 template <class Derived>
  3503 void
  3504 WorkerPrivateParent<Derived>::AssertIsOnParentThread() const
  3506   if (GetParent()) {
  3507     GetParent()->AssertIsOnWorkerThread();
  3509   else {
  3510     AssertIsOnMainThread();
  3514 template <class Derived>
  3515 void
  3516 WorkerPrivateParent<Derived>::AssertInnerWindowIsCorrect() const
  3518   AssertIsOnParentThread();
  3520   // Only care about top level workers from windows.
  3521   if (mParent || !mLoadInfo.mWindow) {
  3522     return;
  3525   AssertIsOnMainThread();
  3527   nsPIDOMWindow* outer = mLoadInfo.mWindow->GetOuterWindow();
  3528   NS_ASSERTION(outer && outer->GetCurrentInnerWindow() == mLoadInfo.mWindow,
  3529                "Inner window no longer correct!");
  3532 #endif
  3533 WorkerPrivate::WorkerPrivate(JSContext* aCx,
  3534                              WorkerPrivate* aParent,
  3535                              const nsAString& aScriptURL,
  3536                              bool aIsChromeWorker, WorkerType aWorkerType,
  3537                              const nsACString& aSharedWorkerName,
  3538                              LoadInfo& aLoadInfo)
  3539 : WorkerPrivateParent<WorkerPrivate>(aCx, aParent, aScriptURL,
  3540                                      aIsChromeWorker, aWorkerType,
  3541                                      aSharedWorkerName, aLoadInfo),
  3542   mJSContext(nullptr), mErrorHandlerRecursionCount(0), mNextTimeoutId(1),
  3543   mStatus(Pending), mSuspended(false), mTimerRunning(false),
  3544   mRunningExpiredTimeouts(false), mCloseHandlerStarted(false),
  3545   mCloseHandlerFinished(false), mMemoryReporterRunning(false),
  3546   mBlockedForMemoryReporter(false), mCancelAllPendingRunnables(false),
  3547   mPeriodicGCTimerRunning(false), mIdleGCTimerRunning(false)
  3548 #ifdef DEBUG
  3549   , mPRThread(nullptr)
  3550 #endif
  3552   MOZ_ASSERT_IF(IsSharedWorker(), !aSharedWorkerName.IsVoid());
  3553   MOZ_ASSERT_IF(!IsSharedWorker(), aSharedWorkerName.IsEmpty());
  3555   if (aParent) {
  3556     aParent->AssertIsOnWorkerThread();
  3557     aParent->GetAllPreferences(mPreferences);
  3558     mOnLine = aParent->OnLine();
  3560   else {
  3561     AssertIsOnMainThread();
  3562     RuntimeService::GetDefaultPreferences(mPreferences);
  3563     mOnLine = !NS_IsOffline();
  3567 WorkerPrivate::~WorkerPrivate()
  3571 // static
  3572 already_AddRefed<WorkerPrivate>
  3573 WorkerPrivate::Constructor(const GlobalObject& aGlobal,
  3574                            const nsAString& aScriptURL,
  3575                            ErrorResult& aRv)
  3577   return WorkerPrivate::Constructor(aGlobal, aScriptURL, false,
  3578                                     WorkerTypeDedicated, EmptyCString(),
  3579                                     nullptr, aRv);
  3582 // static
  3583 bool
  3584 WorkerPrivate::WorkerAvailable(JSContext* /* unused */, JSObject* /* unused */)
  3586   // If we're already on a worker workers are clearly enabled.
  3587   if (!NS_IsMainThread()) {
  3588     return true;
  3591   // If our caller is chrome, workers are always available.
  3592   if (nsContentUtils::IsCallerChrome()) {
  3593     return true;
  3596   // Else check the pref.
  3597   return Preferences::GetBool(PREF_WORKERS_ENABLED);
  3600 // static
  3601 already_AddRefed<ChromeWorkerPrivate>
  3602 ChromeWorkerPrivate::Constructor(const GlobalObject& aGlobal,
  3603                                  const nsAString& aScriptURL,
  3604                                  ErrorResult& aRv)
  3606   return WorkerPrivate::Constructor(aGlobal, aScriptURL, true,
  3607                                     WorkerTypeDedicated, EmptyCString(),
  3608                                     nullptr, aRv)
  3609                                     .downcast<ChromeWorkerPrivate>();
  3612 // static
  3613 bool
  3614 ChromeWorkerPrivate::WorkerAvailable(JSContext* /* unused */, JSObject* /* unused */)
  3616   // Chrome is always allowed to use workers, and content is never allowed to
  3617   // use ChromeWorker, so all we have to check is the caller.
  3618   return nsContentUtils::ThreadsafeIsCallerChrome();
  3621 // static
  3622 already_AddRefed<WorkerPrivate>
  3623 WorkerPrivate::Constructor(const GlobalObject& aGlobal,
  3624                            const nsAString& aScriptURL,
  3625                            bool aIsChromeWorker, WorkerType aWorkerType,
  3626                            const nsACString& aSharedWorkerName,
  3627                            LoadInfo* aLoadInfo, ErrorResult& aRv)
  3629   WorkerPrivate* parent = NS_IsMainThread() ?
  3630                           nullptr :
  3631                           GetCurrentThreadWorkerPrivate();
  3632   if (parent) {
  3633     parent->AssertIsOnWorkerThread();
  3634   } else {
  3635     AssertIsOnMainThread();
  3638   JSContext* cx = aGlobal.GetContext();
  3640   MOZ_ASSERT_IF(aWorkerType == WorkerTypeShared,
  3641                 !aSharedWorkerName.IsVoid());
  3642   MOZ_ASSERT_IF(aWorkerType != WorkerTypeShared,
  3643                 aSharedWorkerName.IsEmpty());
  3645   Maybe<LoadInfo> stackLoadInfo;
  3646   if (!aLoadInfo) {
  3647     stackLoadInfo.construct();
  3649     nsresult rv = GetLoadInfo(cx, nullptr, parent, aScriptURL,
  3650                               aIsChromeWorker, stackLoadInfo.addr());
  3651     if (NS_FAILED(rv)) {
  3652       scriptloader::ReportLoadError(cx, aScriptURL, rv, !parent);
  3653       aRv.Throw(rv);
  3654       return nullptr;
  3657     aLoadInfo = stackLoadInfo.addr();
  3660   // NB: This has to be done before creating the WorkerPrivate, because it will
  3661   // attempt to use static variables that are initialized in the RuntimeService
  3662   // constructor.
  3663   RuntimeService* runtimeService;
  3665   if (!parent) {
  3666     runtimeService = RuntimeService::GetOrCreateService();
  3667     if (!runtimeService) {
  3668       JS_ReportError(cx, "Failed to create runtime service!");
  3669       aRv.Throw(NS_ERROR_FAILURE);
  3670       return nullptr;
  3673   else {
  3674     runtimeService = RuntimeService::GetService();
  3677   MOZ_ASSERT(runtimeService);
  3679   nsRefPtr<WorkerPrivate> worker =
  3680     new WorkerPrivate(cx, parent, aScriptURL, aIsChromeWorker,
  3681                       aWorkerType, aSharedWorkerName, *aLoadInfo);
  3683   if (!runtimeService->RegisterWorker(cx, worker)) {
  3684     aRv.Throw(NS_ERROR_UNEXPECTED);
  3685     return nullptr;
  3688   nsRefPtr<CompileScriptRunnable> compiler = new CompileScriptRunnable(worker);
  3689   if (!compiler->Dispatch(cx)) {
  3690     aRv.Throw(NS_ERROR_UNEXPECTED);
  3691     return nullptr;
  3694   worker->mSelfRef = worker;
  3696   return worker.forget();
  3699 // static
  3700 nsresult
  3701 WorkerPrivate::GetLoadInfo(JSContext* aCx, nsPIDOMWindow* aWindow,
  3702                            WorkerPrivate* aParent, const nsAString& aScriptURL,
  3703                            bool aIsChromeWorker, LoadInfo* aLoadInfo)
  3705   using namespace mozilla::dom::workers::scriptloader;
  3707   MOZ_ASSERT(aCx);
  3709   if (aWindow) {
  3710     AssertIsOnMainThread();
  3713   LoadInfo loadInfo;
  3714   nsresult rv;
  3716   if (aParent) {
  3717     aParent->AssertIsOnWorkerThread();
  3719     // If the parent is going away give up now.
  3720     Status parentStatus;
  3722       MutexAutoLock lock(aParent->mMutex);
  3723       parentStatus = aParent->mStatus;
  3726     if (parentStatus > Running) {
  3727       NS_WARNING("Cannot create child workers from the close handler!");
  3728       return NS_ERROR_FAILURE;
  3731     // StartAssignment() is used instead getter_AddRefs because, getter_AddRefs
  3732     // does QI in debug build and, if this worker runs in a child process,
  3733     // HttpChannelChild will crash because it's not thread-safe.
  3734     rv = ChannelFromScriptURLWorkerThread(aCx, aParent, aScriptURL,
  3735                                           loadInfo.mChannel.StartAssignment());
  3736     NS_ENSURE_SUCCESS(rv, rv);
  3738     // Now that we've spun the loop there's no guarantee that our parent is
  3739     // still alive.  We may have received control messages initiating shutdown.
  3741       MutexAutoLock lock(aParent->mMutex);
  3742       parentStatus = aParent->mStatus;
  3745     if (parentStatus > Running) {
  3746       nsCOMPtr<nsIThread> mainThread;
  3747       if (NS_FAILED(NS_GetMainThread(getter_AddRefs(mainThread))) ||
  3748           NS_FAILED(NS_ProxyRelease(mainThread, loadInfo.mChannel))) {
  3749         NS_WARNING("Failed to proxy release of channel, leaking instead!");
  3751       return NS_ERROR_FAILURE;
  3754     loadInfo.mDomain = aParent->Domain();
  3755   } else {
  3756     AssertIsOnMainThread();
  3758     nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
  3759     MOZ_ASSERT(ssm);
  3761     bool isChrome = nsContentUtils::IsCallerChrome();
  3763     // First check to make sure the caller has permission to make a privileged
  3764     // worker if they called the ChromeWorker/ChromeSharedWorker constructor.
  3765     if (aIsChromeWorker && !isChrome) {
  3766       return NS_ERROR_DOM_SECURITY_ERR;
  3769     // Chrome callers (whether ChromeWorker of Worker) always get the system
  3770     // principal here as they're allowed to load anything. The script loader may
  3771     // change the principal later depending on the script uri.
  3772     if (isChrome) {
  3773       rv = ssm->GetSystemPrincipal(getter_AddRefs(loadInfo.mPrincipal));
  3774       NS_ENSURE_SUCCESS(rv, rv);
  3777     // See if we're being called from a window.
  3778     nsCOMPtr<nsPIDOMWindow> globalWindow = aWindow;
  3779     if (!globalWindow) {
  3780       nsCOMPtr<nsIScriptGlobalObject> scriptGlobal =
  3781         nsJSUtils::GetStaticScriptGlobal(JS::CurrentGlobalOrNull(aCx));
  3782       if (scriptGlobal) {
  3783         globalWindow = do_QueryInterface(scriptGlobal);
  3784         MOZ_ASSERT(globalWindow);
  3788     nsCOMPtr<nsIDocument> document;
  3790     if (globalWindow) {
  3791       // Only use the current inner window, and only use it if the caller can
  3792       // access it.
  3793       nsPIDOMWindow* outerWindow = globalWindow->GetOuterWindow();
  3794       if (outerWindow) {
  3795         loadInfo.mWindow = outerWindow->GetCurrentInnerWindow();
  3798       if (!loadInfo.mWindow ||
  3799           (globalWindow != loadInfo.mWindow &&
  3800             !nsContentUtils::CanCallerAccess(loadInfo.mWindow))) {
  3801         return NS_ERROR_DOM_SECURITY_ERR;
  3804       nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(loadInfo.mWindow);
  3805       MOZ_ASSERT(sgo);
  3807       loadInfo.mScriptContext = sgo->GetContext();
  3808       NS_ENSURE_TRUE(loadInfo.mScriptContext, NS_ERROR_FAILURE);
  3810       // If we're called from a window then we can dig out the principal and URI
  3811       // from the document.
  3812       document = loadInfo.mWindow->GetExtantDoc();
  3813       NS_ENSURE_TRUE(document, NS_ERROR_FAILURE);
  3815       loadInfo.mBaseURI = document->GetDocBaseURI();
  3817       // Use the document's NodePrincipal as our principal if we're not being
  3818       // called from chrome.
  3819       if (!loadInfo.mPrincipal) {
  3820         loadInfo.mPrincipal = document->NodePrincipal();
  3821         NS_ENSURE_TRUE(loadInfo.mPrincipal, NS_ERROR_FAILURE);
  3823         // We use the document's base domain to limit the number of workers
  3824         // each domain can create. For sandboxed documents, we use the domain
  3825         // of their first non-sandboxed document, walking up until we find
  3826         // one. If we can't find one, we fall back to using the GUID of the
  3827         // null principal as the base domain.
  3828         if (document->GetSandboxFlags() & SANDBOXED_ORIGIN) {
  3829           nsCOMPtr<nsIDocument> tmpDoc = document;
  3830           do {
  3831             tmpDoc = tmpDoc->GetParentDocument();
  3832           } while (tmpDoc && tmpDoc->GetSandboxFlags() & SANDBOXED_ORIGIN);
  3834           if (tmpDoc) {
  3835             // There was an unsandboxed ancestor, yay!
  3836             nsCOMPtr<nsIPrincipal> tmpPrincipal = tmpDoc->NodePrincipal();
  3837             rv = tmpPrincipal->GetBaseDomain(loadInfo.mDomain);
  3838             NS_ENSURE_SUCCESS(rv, rv);
  3839           } else {
  3840             // No unsandboxed ancestor, use our GUID.
  3841             rv = loadInfo.mPrincipal->GetBaseDomain(loadInfo.mDomain);
  3842             NS_ENSURE_SUCCESS(rv, rv);
  3844         } else {
  3845           // Document creating the worker is not sandboxed.
  3846           rv = loadInfo.mPrincipal->GetBaseDomain(loadInfo.mDomain);
  3847           NS_ENSURE_SUCCESS(rv, rv);
  3851       nsCOMPtr<nsIPermissionManager> permMgr =
  3852         do_GetService(NS_PERMISSIONMANAGER_CONTRACTID, &rv);
  3853       NS_ENSURE_SUCCESS(rv, rv);
  3855       uint32_t perm;
  3856       rv = permMgr->TestPermissionFromPrincipal(loadInfo.mPrincipal, "systemXHR",
  3857                                                 &perm);
  3858       NS_ENSURE_SUCCESS(rv, rv);
  3860       loadInfo.mXHRParamsAllowed = perm == nsIPermissionManager::ALLOW_ACTION;
  3861     } else {
  3862       // Not a window
  3863       MOZ_ASSERT(isChrome);
  3865       // We're being created outside of a window. Need to figure out the script
  3866       // that is creating us in order for us to use relative URIs later on.
  3867       JS::AutoFilename fileName;
  3868       if (JS::DescribeScriptedCaller(aCx, &fileName)) {
  3869         // In most cases, fileName is URI. In a few other cases
  3870         // (e.g. xpcshell), fileName is a file path. Ideally, we would
  3871         // prefer testing whether fileName parses as an URI and fallback
  3872         // to file path in case of error, but Windows file paths have
  3873         // the interesting property that they can be parsed as bogus
  3874         // URIs (e.g. C:/Windows/Tmp is interpreted as scheme "C",
  3875         // hostname "Windows", path "Tmp"), which defeats this algorithm.
  3876         // Therefore, we adopt the opposite convention.
  3877         nsCOMPtr<nsIFile> scriptFile =
  3878           do_CreateInstance("@mozilla.org/file/local;1", &rv);
  3879         if (NS_FAILED(rv)) {
  3880           return rv;
  3883         rv = scriptFile->InitWithPath(NS_ConvertUTF8toUTF16(fileName.get()));
  3884         if (NS_SUCCEEDED(rv)) {
  3885           rv = NS_NewFileURI(getter_AddRefs(loadInfo.mBaseURI),
  3886                              scriptFile);
  3888         if (NS_FAILED(rv)) {
  3889           // As expected, fileName is not a path, so proceed with
  3890           // a uri.
  3891           rv = NS_NewURI(getter_AddRefs(loadInfo.mBaseURI),
  3892                          fileName.get());
  3894         if (NS_FAILED(rv)) {
  3895           return rv;
  3898       loadInfo.mXHRParamsAllowed = true;
  3901     MOZ_ASSERT(loadInfo.mPrincipal);
  3902     MOZ_ASSERT(isChrome || !loadInfo.mDomain.IsEmpty());
  3904     // XXXbent Use subject principal here instead of the one we already have?
  3905     nsCOMPtr<nsIPrincipal> subjectPrincipal = ssm->GetCxSubjectPrincipal(aCx);
  3906     MOZ_ASSERT(subjectPrincipal);
  3908     if (!nsContentUtils::GetContentSecurityPolicy(aCx,
  3909                                                getter_AddRefs(loadInfo.mCSP))) {
  3910       NS_WARNING("Failed to get CSP!");
  3911       return NS_ERROR_FAILURE;
  3914     if (loadInfo.mCSP) {
  3915       rv = loadInfo.mCSP->GetAllowsEval(&loadInfo.mReportCSPViolations,
  3916                                         &loadInfo.mEvalAllowed);
  3917       NS_ENSURE_SUCCESS(rv, rv);
  3918     } else {
  3919       loadInfo.mEvalAllowed = true;
  3920       loadInfo.mReportCSPViolations = false;
  3923     rv = ChannelFromScriptURLMainThread(loadInfo.mPrincipal, loadInfo.mBaseURI,
  3924                                         document, aScriptURL,
  3925                                         getter_AddRefs(loadInfo.mChannel));
  3926     NS_ENSURE_SUCCESS(rv, rv);
  3928     rv = NS_GetFinalChannelURI(loadInfo.mChannel,
  3929                                getter_AddRefs(loadInfo.mResolvedScriptURI));
  3930     NS_ENSURE_SUCCESS(rv, rv);
  3933   aLoadInfo->StealFrom(loadInfo);
  3934   return NS_OK;
  3937 void
  3938 WorkerPrivate::DoRunLoop(JSContext* aCx)
  3940   AssertIsOnWorkerThread();
  3941   MOZ_ASSERT(mThread);
  3944     MutexAutoLock lock(mMutex);
  3945     mJSContext = aCx;
  3947     MOZ_ASSERT(mStatus == Pending);
  3948     mStatus = Running;
  3951   EnableMemoryReporter();
  3953   InitializeGCTimers();
  3955   Maybe<JSAutoCompartment> workerCompartment;
  3957   for (;;) {
  3958     // Workers lazily create a global object in CompileScriptRunnable. We need
  3959     // to enter the global's compartment as soon as it has been created.
  3960     if (workerCompartment.empty()) {
  3961       if (JSObject* global = js::DefaultObjectForContextOrNull(aCx)) {
  3962         workerCompartment.construct(aCx, global);
  3966     Status currentStatus;
  3967     bool normalRunnablesPending = false;
  3970       MutexAutoLock lock(mMutex);
  3972       while (mControlQueue.IsEmpty() &&
  3973              !(normalRunnablesPending = NS_HasPendingEvents(mThread))) {
  3974         WaitForWorkerEvents();
  3977       ProcessAllControlRunnablesLocked();
  3979       currentStatus = mStatus;
  3982     // If the close handler has finished and all features are done then we can
  3983     // kill this thread.
  3984     if (currentStatus != Running && !HasActiveFeatures()) {
  3985       if (mCloseHandlerFinished && currentStatus != Killing) {
  3986         if (!NotifyInternal(aCx, Killing)) {
  3987           JS_ReportPendingException(aCx);
  3989 #ifdef DEBUG
  3991           MutexAutoLock lock(mMutex);
  3992           currentStatus = mStatus;
  3994         MOZ_ASSERT(currentStatus == Killing);
  3995 #else
  3996         currentStatus = Killing;
  3997 #endif
  4000       // If we're supposed to die then we should exit the loop.
  4001       if (currentStatus == Killing) {
  4002         ShutdownGCTimers();
  4004         DisableMemoryReporter();
  4007           MutexAutoLock lock(mMutex);
  4009           mStatus = Dead;
  4010           mJSContext = nullptr;
  4013         // After mStatus is set to Dead there can be no more
  4014         // WorkerControlRunnables so no need to lock here.
  4015         if (!mControlQueue.IsEmpty()) {
  4016           WorkerControlRunnable* runnable;
  4017           while (mControlQueue.Pop(runnable)) {
  4018             runnable->Cancel();
  4019             runnable->Release();
  4023         // Clear away our MessagePorts.
  4024         mWorkerPorts.Clear();
  4026         // Unroot the global
  4027         mScope = nullptr;
  4029         return;
  4033     // Nothing to do here if we don't have any runnables in the main queue.
  4034     if (!normalRunnablesPending) {
  4035       SetGCTimerMode(IdleTimer);
  4036       continue;
  4039     MOZ_ASSERT(NS_HasPendingEvents(mThread));
  4041     // Start the periodic GC timer if it is not already running.
  4042     SetGCTimerMode(PeriodicTimer);
  4044     // Process a single runnable from the main queue.
  4045     MOZ_ALWAYS_TRUE(NS_ProcessNextEvent(mThread, false));
  4047     if (NS_HasPendingEvents(mThread)) {
  4048       // Now *might* be a good time to GC. Let the JS engine make the decision.
  4049       if (!workerCompartment.empty()) {
  4050         JS_MaybeGC(aCx);
  4053     else {
  4054       // The normal event queue has been exhausted, cancel the periodic GC timer
  4055       // and schedule the idle GC timer.
  4056       SetGCTimerMode(IdleTimer);
  4060   MOZ_ASSUME_UNREACHABLE("Shouldn't get here!");
  4063 void
  4064 WorkerPrivate::OnProcessNextEvent(uint32_t aRecursionDepth)
  4066   AssertIsOnWorkerThread();
  4067   MOZ_ASSERT(aRecursionDepth);
  4069   // Normally we process control runnables in DoRunLoop or RunCurrentSyncLoop.
  4070   // However, it's possible that non-worker C++ could spin its own nested event
  4071   // loop, and in that case we must ensure that we continue to process control
  4072   // runnables here.
  4073   if (aRecursionDepth > 1 &&
  4074       mSyncLoopStack.Length() < aRecursionDepth - 1) {
  4075     ProcessAllControlRunnables();
  4079 void
  4080 WorkerPrivate::AfterProcessNextEvent(uint32_t aRecursionDepth)
  4082   AssertIsOnWorkerThread();
  4083   MOZ_ASSERT(aRecursionDepth);
  4086 void
  4087 WorkerPrivate::InitializeGCTimers()
  4089   AssertIsOnWorkerThread();
  4091   // We need a timer for GC. The basic plan is to run a non-shrinking GC
  4092   // periodically (PERIODIC_GC_TIMER_DELAY_SEC) while the worker is running.
  4093   // Once the worker goes idle we set a short (IDLE_GC_TIMER_DELAY_SEC) timer to
  4094   // run a shrinking GC. If the worker receives more messages then the short
  4095   // timer is canceled and the periodic timer resumes.
  4096   mGCTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
  4097   MOZ_ASSERT(mGCTimer);
  4099   nsRefPtr<GarbageCollectRunnable> runnable =
  4100     new GarbageCollectRunnable(this, false, false);
  4101   mPeriodicGCTimerTarget = new TimerThreadEventTarget(this, runnable);
  4103   runnable = new GarbageCollectRunnable(this, true, false);
  4104   mIdleGCTimerTarget = new TimerThreadEventTarget(this, runnable);
  4106   mPeriodicGCTimerRunning = false;
  4107   mIdleGCTimerRunning = false;
  4110 void
  4111 WorkerPrivate::SetGCTimerMode(GCTimerMode aMode)
  4113   AssertIsOnWorkerThread();
  4114   MOZ_ASSERT(mGCTimer);
  4115   MOZ_ASSERT(mPeriodicGCTimerTarget);
  4116   MOZ_ASSERT(mIdleGCTimerTarget);
  4118   if ((aMode == PeriodicTimer && mPeriodicGCTimerRunning) ||
  4119       (aMode == IdleTimer && mIdleGCTimerRunning)) {
  4120     return;
  4123   MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mGCTimer->Cancel()));
  4125   mPeriodicGCTimerRunning = false;
  4126   mIdleGCTimerRunning = false;
  4128   LOG(("Worker %p canceled GC timer because %s\n", this,
  4129        aMode == PeriodicTimer ?
  4130        "periodic" :
  4131        aMode == IdleTimer ? "idle" : "none"));
  4133   if (aMode == NoTimer) {
  4134     return;
  4137   MOZ_ASSERT(aMode == PeriodicTimer || aMode == IdleTimer);
  4139   nsIEventTarget* target;
  4140   uint32_t delay;
  4141   int16_t type;
  4143   if (aMode == PeriodicTimer) {
  4144     target = mPeriodicGCTimerTarget;
  4145     delay = PERIODIC_GC_TIMER_DELAY_SEC * 1000;
  4146     type = nsITimer::TYPE_REPEATING_SLACK;
  4148   else {
  4149     target = mIdleGCTimerTarget;
  4150     delay = IDLE_GC_TIMER_DELAY_SEC * 1000;
  4151     type = nsITimer::TYPE_ONE_SHOT;
  4154   MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mGCTimer->SetTarget(target)));
  4155   MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mGCTimer->InitWithFuncCallback(DummyCallback,
  4156                                                               nullptr, delay,
  4157                                                               type)));
  4159   if (aMode == PeriodicTimer) {
  4160     LOG(("Worker %p scheduled periodic GC timer\n", this));
  4161     mPeriodicGCTimerRunning = true;
  4163   else {
  4164     LOG(("Worker %p scheduled idle GC timer\n", this));
  4165     mIdleGCTimerRunning = true;
  4169 void
  4170 WorkerPrivate::ShutdownGCTimers()
  4172   AssertIsOnWorkerThread();
  4174   MOZ_ASSERT(mGCTimer);
  4176   // Always make sure the timer is canceled.
  4177   MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mGCTimer->Cancel()));
  4179   LOG(("Worker %p killed the GC timer\n", this));
  4181   mGCTimer = nullptr;
  4182   mPeriodicGCTimerTarget = nullptr;
  4183   mIdleGCTimerTarget = nullptr;
  4184   mPeriodicGCTimerRunning = false;
  4185   mIdleGCTimerRunning = false;
  4188 bool
  4189 WorkerPrivate::InterruptCallback(JSContext* aCx)
  4191   AssertIsOnWorkerThread();
  4193   bool mayContinue = true;
  4194   bool scheduledIdleGC = false;
  4196   for (;;) {
  4197     // Run all control events now.
  4198     mayContinue = ProcessAllControlRunnables();
  4200     bool maySuspend = mSuspended;
  4201     if (maySuspend) {
  4202       MutexAutoLock lock(mMutex);
  4203       maySuspend = mStatus <= Running;
  4206     if (!mayContinue || !maySuspend) {
  4207       break;
  4210     // Cancel the periodic GC timer here before suspending. The idle GC timer
  4211     // will clean everything up once it runs.
  4212     if (!scheduledIdleGC) {
  4213       SetGCTimerMode(IdleTimer);
  4214       scheduledIdleGC = true;
  4217     while ((mayContinue = MayContinueRunning())) {
  4218       MutexAutoLock lock(mMutex);
  4219       if (!mControlQueue.IsEmpty()) {
  4220         break;
  4223       WaitForWorkerEvents(PR_MillisecondsToInterval(RemainingRunTimeMS()));
  4227   if (!mayContinue) {
  4228     // We want only uncatchable exceptions here.
  4229     NS_ASSERTION(!JS_IsExceptionPending(aCx),
  4230                  "Should not have an exception set here!");
  4231     return false;
  4234   // Make sure the periodic timer gets turned back on here.
  4235   SetGCTimerMode(PeriodicTimer);
  4237   return true;
  4240 nsresult
  4241 WorkerPrivate::IsOnCurrentThread(bool* aIsOnCurrentThread)
  4243   // May be called on any thread!
  4245   MOZ_ASSERT(aIsOnCurrentThread);
  4247   nsCOMPtr<nsIThread> thread;
  4249     MutexAutoLock lock(mMutex);
  4250     thread = mThread;
  4253   if (!thread) {
  4254     NS_WARNING("Trying to test thread correctness after the worker has "
  4255                "released its thread!");
  4256     return NS_ERROR_FAILURE;
  4259   nsresult rv = thread->IsOnCurrentThread(aIsOnCurrentThread);
  4260   if (NS_WARN_IF(NS_FAILED(rv))) {
  4261     return rv;
  4264   return NS_OK;
  4267 void
  4268 WorkerPrivate::ScheduleDeletion(WorkerRanOrNot aRanOrNot)
  4270   AssertIsOnWorkerThread();
  4271   MOZ_ASSERT(mChildWorkers.IsEmpty());
  4272   MOZ_ASSERT(mSyncLoopStack.IsEmpty());
  4274   ClearMainEventQueue(aRanOrNot);
  4276   if (WorkerPrivate* parent = GetParent()) {
  4277     nsRefPtr<WorkerFinishedRunnable> runnable =
  4278       new WorkerFinishedRunnable(parent, this);
  4279     if (!runnable->Dispatch(nullptr)) {
  4280       NS_WARNING("Failed to dispatch runnable!");
  4283   else {
  4284     nsRefPtr<TopLevelWorkerFinishedRunnable> runnable =
  4285       new TopLevelWorkerFinishedRunnable(this);
  4286     if (NS_FAILED(NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL))) {
  4287       NS_WARNING("Failed to dispatch runnable!");
  4292 bool
  4293 WorkerPrivate::BlockAndCollectRuntimeStats(JS::RuntimeStats* aRtStats)
  4295   AssertIsOnMainThread();
  4296   mMutex.AssertCurrentThreadOwns();
  4297   NS_ASSERTION(aRtStats, "Null RuntimeStats!");
  4299   NS_ASSERTION(!mMemoryReporterRunning, "How can we get reentered here?!");
  4301   // This signals the worker that it should block itself as soon as possible.
  4302   mMemoryReporterRunning = true;
  4304   NS_ASSERTION(mJSContext, "This must never be null!");
  4305   JSRuntime* rt = JS_GetRuntime(mJSContext);
  4307   // If the worker is not already blocked (e.g. waiting for a worker event or
  4308   // currently in a ctypes call) then we need to trigger the interrupt
  4309   // callback to trap the worker.
  4310   if (!mBlockedForMemoryReporter) {
  4311     JS_RequestInterruptCallback(rt);
  4313     // Wait until the worker actually blocks.
  4314     while (!mBlockedForMemoryReporter) {
  4315       mMemoryReportCondVar.Wait();
  4319   bool succeeded = false;
  4321   // If mMemoryReporter is still set then we can do the actual report. Otherwise
  4322   // we're trying to shut down and we don't want to do anything but clean up.
  4323   if (mMemoryReporter) {
  4324     // Don't hold the lock while doing the actual report.
  4325     MutexAutoUnlock unlock(mMutex);
  4326     succeeded = JS::CollectRuntimeStats(rt, aRtStats, nullptr);
  4329   NS_ASSERTION(mMemoryReporterRunning, "This isn't possible!");
  4330   NS_ASSERTION(mBlockedForMemoryReporter, "Somehow we got unblocked!");
  4332   // Tell the worker that it can now continue its execution.
  4333   mMemoryReporterRunning = false;
  4335   // The worker may be waiting so we must notify.
  4336   mMemoryReportCondVar.Notify();
  4338   return succeeded;
  4341 void
  4342 WorkerPrivate::EnableMemoryReporter()
  4344   AssertIsOnWorkerThread();
  4345   MOZ_ASSERT(!mMemoryReporter);
  4347   // No need to lock here since the main thread can't race until we've
  4348   // successfully registered the reporter.
  4349   mMemoryReporter = new MemoryReporter(this);
  4351   if (NS_FAILED(RegisterWeakMemoryReporter(mMemoryReporter))) {
  4352     NS_WARNING("Failed to register memory reporter!");
  4353     // No need to lock here since a failed registration means our memory
  4354     // reporter can't start running. Just clean up.
  4355     mMemoryReporter = nullptr;
  4359 void
  4360 WorkerPrivate::DisableMemoryReporter()
  4362   AssertIsOnWorkerThread();
  4364   nsRefPtr<MemoryReporter> memoryReporter;
  4366     MutexAutoLock lock(mMutex);
  4368     // There is nothing to do here if the memory reporter was never successfully
  4369     // registered.
  4370     if (!mMemoryReporter) {
  4371       return;
  4374     // We don't need this set any longer. Swap it out so that we can unregister
  4375     // below.
  4376     mMemoryReporter.swap(memoryReporter);
  4378     // Next disable the memory reporter so that the main thread stops trying to
  4379     // signal us.
  4380     memoryReporter->Disable();
  4382     // If the memory reporter is waiting to start then we need to wait for it to
  4383     // finish.
  4384     if (mMemoryReporterRunning) {
  4385       NS_ASSERTION(!mBlockedForMemoryReporter,
  4386                    "Can't be blocked in more than one place at the same time!");
  4387       mBlockedForMemoryReporter = true;
  4389       // Tell the main thread that we're blocked.
  4390       mMemoryReportCondVar.Notify();
  4392       // Wait for it the main thread to finish. Since we swapped out
  4393       // mMemoryReporter above the main thread should respond quickly.
  4394       while (mMemoryReporterRunning) {
  4395         mMemoryReportCondVar.Wait();
  4398       NS_ASSERTION(mBlockedForMemoryReporter, "Somehow we got unblocked!");
  4399       mBlockedForMemoryReporter = false;
  4403   // Finally unregister the memory reporter.
  4404   if (NS_FAILED(UnregisterWeakMemoryReporter(memoryReporter))) {
  4405     NS_WARNING("Failed to unregister memory reporter!");
  4409 void
  4410 WorkerPrivate::WaitForWorkerEvents(PRIntervalTime aInterval)
  4412   AssertIsOnWorkerThread();
  4413   mMutex.AssertCurrentThreadOwns();
  4415   NS_ASSERTION(!mBlockedForMemoryReporter,
  4416                 "Can't be blocked in more than one place at the same time!");
  4418   // Let the main thread know that the worker is blocked and that memory
  4419   // reporting may proceed.
  4420   mBlockedForMemoryReporter = true;
  4422   // The main thread may be waiting so we must notify.
  4423   mMemoryReportCondVar.Notify();
  4425   // Now wait for an actual worker event.
  4426   mCondVar.Wait(aInterval);
  4428   // We've gotten some kind of signal but we can't continue until the memory
  4429   // reporter has finished. Wait again.
  4430   while (mMemoryReporterRunning) {
  4431     mMemoryReportCondVar.Wait();
  4434   NS_ASSERTION(mBlockedForMemoryReporter, "Somehow we got unblocked!");
  4436   // No need to notify here as the main thread isn't watching for this state.
  4437   mBlockedForMemoryReporter = false;
  4440 bool
  4441 WorkerPrivate::ProcessAllControlRunnablesLocked()
  4443   AssertIsOnWorkerThread();
  4444   mMutex.AssertCurrentThreadOwns();
  4446   bool result = true;
  4448   for (;;) {
  4449     // Block here if the memory reporter is trying to run.
  4450     if (mMemoryReporterRunning) {
  4451       MOZ_ASSERT(!mBlockedForMemoryReporter);
  4453       // Let the main thread know that we've received the block request and
  4454       // that memory reporting may proceed.
  4455       mBlockedForMemoryReporter = true;
  4457       // The main thread is almost certainly waiting so we must notify here.
  4458       mMemoryReportCondVar.Notify();
  4460       // Wait for the memory report to finish.
  4461       while (mMemoryReporterRunning) {
  4462         mMemoryReportCondVar.Wait();
  4465       MOZ_ASSERT(mBlockedForMemoryReporter);
  4467       // No need to notify here as the main thread isn't watching for this
  4468       // state.
  4469       mBlockedForMemoryReporter = false;
  4472     WorkerControlRunnable* event;
  4473     if (!mControlQueue.Pop(event)) {
  4474       break;
  4477     MutexAutoUnlock unlock(mMutex);
  4479     MOZ_ASSERT(event);
  4480     if (NS_FAILED(static_cast<nsIRunnable*>(event)->Run())) {
  4481       result = false;
  4484     event->Release();
  4487   return result;
  4490 void
  4491 WorkerPrivate::ClearMainEventQueue(WorkerRanOrNot aRanOrNot)
  4493   AssertIsOnWorkerThread();
  4495   MOZ_ASSERT(!mCancelAllPendingRunnables);
  4496   mCancelAllPendingRunnables = true;
  4498   if (WorkerNeverRan == aRanOrNot) {
  4499     for (uint32_t count = mPreStartRunnables.Length(), index = 0;
  4500          index < count;
  4501          index++) {
  4502       nsRefPtr<WorkerRunnable> runnable = mPreStartRunnables[index].forget();
  4503       static_cast<nsIRunnable*>(runnable.get())->Run();
  4505   } else {
  4506     nsIThread* currentThread = NS_GetCurrentThread();
  4507     MOZ_ASSERT(currentThread);
  4509     NS_ProcessPendingEvents(currentThread);
  4510     MOZ_ASSERT(!NS_HasPendingEvents(currentThread));
  4513   MOZ_ASSERT(mCancelAllPendingRunnables);
  4514   mCancelAllPendingRunnables = false;
  4517 uint32_t
  4518 WorkerPrivate::RemainingRunTimeMS() const
  4520   if (mKillTime.IsNull()) {
  4521     return UINT32_MAX;
  4523   TimeDuration runtime = mKillTime - TimeStamp::Now();
  4524   double ms = runtime > TimeDuration(0) ? runtime.ToMilliseconds() : 0;
  4525   return ms > double(UINT32_MAX) ? UINT32_MAX : uint32_t(ms);
  4528 bool
  4529 WorkerPrivate::SuspendInternal(JSContext* aCx)
  4531   AssertIsOnWorkerThread();
  4533   NS_ASSERTION(!mSuspended, "Already suspended!");
  4535   mSuspended = true;
  4536   return true;
  4539 bool
  4540 WorkerPrivate::ResumeInternal(JSContext* aCx)
  4542   AssertIsOnWorkerThread();
  4544   NS_ASSERTION(mSuspended, "Not yet suspended!");
  4546   mSuspended = false;
  4547   return true;
  4550 void
  4551 WorkerPrivate::TraceTimeouts(const TraceCallbacks& aCallbacks,
  4552                              void* aClosure) const
  4554   AssertIsOnWorkerThread();
  4556   for (uint32_t index = 0; index < mTimeouts.Length(); index++) {
  4557     TimeoutInfo* info = mTimeouts[index];
  4559     if (info->mTimeoutCallable.isUndefined()) {
  4560       continue;
  4563     aCallbacks.Trace(&info->mTimeoutCallable, "mTimeoutCallable", aClosure);
  4564     for (uint32_t index2 = 0; index2 < info->mExtraArgVals.Length(); index2++) {
  4565       aCallbacks.Trace(&info->mExtraArgVals[index2], "mExtraArgVals[i]", aClosure);
  4570 bool
  4571 WorkerPrivate::ModifyBusyCountFromWorker(JSContext* aCx, bool aIncrease)
  4573   AssertIsOnWorkerThread();
  4576     MutexAutoLock lock(mMutex);
  4578     // If we're in shutdown then the busy count is no longer being considered so
  4579     // just return now.
  4580     if (mStatus >= Killing) {
  4581       return true;
  4585   nsRefPtr<ModifyBusyCountRunnable> runnable =
  4586     new ModifyBusyCountRunnable(this, aIncrease);
  4587   return runnable->Dispatch(aCx);
  4590 bool
  4591 WorkerPrivate::AddChildWorker(JSContext* aCx, ParentType* aChildWorker)
  4593   AssertIsOnWorkerThread();
  4595 #ifdef DEBUG
  4597     Status currentStatus;
  4599       MutexAutoLock lock(mMutex);
  4600       currentStatus = mStatus;
  4603     MOZ_ASSERT(currentStatus == Running);
  4605 #endif
  4607   NS_ASSERTION(!mChildWorkers.Contains(aChildWorker),
  4608                "Already know about this one!");
  4609   mChildWorkers.AppendElement(aChildWorker);
  4611   return mChildWorkers.Length() == 1 ?
  4612          ModifyBusyCountFromWorker(aCx, true) :
  4613          true;
  4616 void
  4617 WorkerPrivate::RemoveChildWorker(JSContext* aCx, ParentType* aChildWorker)
  4619   AssertIsOnWorkerThread();
  4621   NS_ASSERTION(mChildWorkers.Contains(aChildWorker),
  4622                "Didn't know about this one!");
  4623   mChildWorkers.RemoveElement(aChildWorker);
  4625   if (mChildWorkers.IsEmpty() && !ModifyBusyCountFromWorker(aCx, false)) {
  4626     NS_WARNING("Failed to modify busy count!");
  4630 bool
  4631 WorkerPrivate::AddFeature(JSContext* aCx, WorkerFeature* aFeature)
  4633   AssertIsOnWorkerThread();
  4636     MutexAutoLock lock(mMutex);
  4638     if (mStatus >= Canceling) {
  4639       return false;
  4643   NS_ASSERTION(!mFeatures.Contains(aFeature), "Already know about this one!");
  4644   mFeatures.AppendElement(aFeature);
  4646   return mFeatures.Length() == 1 ?
  4647          ModifyBusyCountFromWorker(aCx, true) :
  4648          true;
  4651 void
  4652 WorkerPrivate::RemoveFeature(JSContext* aCx, WorkerFeature* aFeature)
  4654   AssertIsOnWorkerThread();
  4656   NS_ASSERTION(mFeatures.Contains(aFeature), "Didn't know about this one!");
  4657   mFeatures.RemoveElement(aFeature);
  4659   if (mFeatures.IsEmpty() && !ModifyBusyCountFromWorker(aCx, false)) {
  4660     NS_WARNING("Failed to modify busy count!");
  4664 void
  4665 WorkerPrivate::NotifyFeatures(JSContext* aCx, Status aStatus)
  4667   AssertIsOnWorkerThread();
  4669   NS_ASSERTION(aStatus > Running, "Bad status!");
  4671   if (aStatus >= Closing) {
  4672     CancelAllTimeouts(aCx);
  4675   nsAutoTArray<WorkerFeature*, 30> features;
  4676   features.AppendElements(mFeatures);
  4678   for (uint32_t index = 0; index < features.Length(); index++) {
  4679     if (!features[index]->Notify(aCx, aStatus)) {
  4680       NS_WARNING("Failed to notify feature!");
  4684   nsAutoTArray<ParentType*, 10> children;
  4685   children.AppendElements(mChildWorkers);
  4687   for (uint32_t index = 0; index < children.Length(); index++) {
  4688     if (!children[index]->Notify(aCx, aStatus)) {
  4689       NS_WARNING("Failed to notify child worker!");
  4694 void
  4695 WorkerPrivate::CancelAllTimeouts(JSContext* aCx)
  4697   AssertIsOnWorkerThread();
  4699   if (mTimerRunning) {
  4700     NS_ASSERTION(mTimer, "Huh?!");
  4701     NS_ASSERTION(!mTimeouts.IsEmpty(), "Huh?!");
  4703     if (NS_FAILED(mTimer->Cancel())) {
  4704       NS_WARNING("Failed to cancel timer!");
  4707     for (uint32_t index = 0; index < mTimeouts.Length(); index++) {
  4708       mTimeouts[index]->mCanceled = true;
  4711     if (!RunExpiredTimeouts(aCx)) {
  4712       JS_ReportPendingException(aCx);
  4715     mTimerRunning = false;
  4717 #ifdef DEBUG
  4718   else if (!mRunningExpiredTimeouts) {
  4719     NS_ASSERTION(mTimeouts.IsEmpty(), "Huh?!");
  4721 #endif
  4723   mTimer = nullptr;
  4726 already_AddRefed<nsIEventTarget>
  4727 WorkerPrivate::CreateNewSyncLoop()
  4729   AssertIsOnWorkerThread();
  4731   nsCOMPtr<nsIThreadInternal> thread = do_QueryInterface(NS_GetCurrentThread());
  4732   MOZ_ASSERT(thread);
  4734   nsCOMPtr<nsIEventTarget> realEventTarget;
  4735   MOZ_ALWAYS_TRUE(NS_SUCCEEDED(thread->PushEventQueue(
  4736                                              getter_AddRefs(realEventTarget))));
  4738   nsRefPtr<EventTarget> workerEventTarget =
  4739     new EventTarget(this, realEventTarget);
  4742     // Modifications must be protected by mMutex in DEBUG builds, see comment
  4743     // about mSyncLoopStack in WorkerPrivate.h.
  4744 #ifdef DEBUG
  4745     MutexAutoLock lock(mMutex);
  4746 #endif
  4748     mSyncLoopStack.AppendElement(new SyncLoopInfo(workerEventTarget));
  4751   return workerEventTarget.forget();
  4754 bool
  4755 WorkerPrivate::RunCurrentSyncLoop()
  4757   AssertIsOnWorkerThread();
  4759   JSContext* cx = GetJSContext();
  4760   MOZ_ASSERT(cx);
  4762   // This should not change between now and the time we finish running this sync
  4763   // loop.
  4764   uint32_t currentLoopIndex = mSyncLoopStack.Length() - 1;
  4766   SyncLoopInfo* loopInfo = mSyncLoopStack[currentLoopIndex];
  4768   MOZ_ASSERT(loopInfo);
  4769   MOZ_ASSERT(!loopInfo->mHasRun);
  4770   MOZ_ASSERT(!loopInfo->mCompleted);
  4772 #ifdef DEBUG
  4773   loopInfo->mHasRun = true;
  4774 #endif
  4776   nsCOMPtr<nsIThreadInternal> thread = do_QueryInterface(mThread);
  4777   MOZ_ASSERT(thread);
  4779   while (!loopInfo->mCompleted) {
  4780     bool normalRunnablesPending = false;
  4782     // Don't block with the periodic GC timer running.
  4783     if (!NS_HasPendingEvents(thread)) {
  4784       SetGCTimerMode(IdleTimer);
  4787     // Wait for something to do.
  4789       MutexAutoLock lock(mMutex);
  4791       for (;;) {
  4792         while (mControlQueue.IsEmpty() &&
  4793                !normalRunnablesPending &&
  4794                !(normalRunnablesPending = NS_HasPendingEvents(thread))) {
  4795           WaitForWorkerEvents();
  4798         ProcessAllControlRunnablesLocked();
  4800         // NB: If we processed a NotifyRunnable, we might have run non-control
  4801         // runnables, one of which may have shut down the sync loop.
  4802         if (normalRunnablesPending || loopInfo->mCompleted) {
  4803           break;
  4808     if (normalRunnablesPending) {
  4809       // Make sure the periodic timer is running before we continue.
  4810       SetGCTimerMode(PeriodicTimer);
  4812       MOZ_ALWAYS_TRUE(NS_ProcessNextEvent(thread, false));
  4814       // Now *might* be a good time to GC. Let the JS engine make the decision.
  4815       JS_MaybeGC(cx);
  4819   // Make sure that the stack didn't change underneath us.
  4820   MOZ_ASSERT(mSyncLoopStack[currentLoopIndex] == loopInfo);
  4822   return DestroySyncLoop(currentLoopIndex);
  4825 bool
  4826 WorkerPrivate::DestroySyncLoop(uint32_t aLoopIndex, nsIThreadInternal* aThread)
  4828   MOZ_ASSERT(!mSyncLoopStack.IsEmpty());
  4829   MOZ_ASSERT(mSyncLoopStack.Length() - 1 == aLoopIndex);
  4831   if (!aThread) {
  4832     nsCOMPtr<nsIThreadInternal> thread = do_QueryInterface(mThread);
  4833     MOZ_ASSERT(thread);
  4835     aThread = thread.get();
  4838   // We're about to delete the loop, stash its event target and result.
  4839   SyncLoopInfo* loopInfo = mSyncLoopStack[aLoopIndex];
  4840   nsIEventTarget* nestedEventTarget =
  4841     loopInfo->mEventTarget->GetWeakNestedEventTarget();
  4842   MOZ_ASSERT(nestedEventTarget);
  4844   bool result = loopInfo->mResult;
  4847     // Modifications must be protected by mMutex in DEBUG builds, see comment
  4848     // about mSyncLoopStack in WorkerPrivate.h.
  4849 #ifdef DEBUG
  4850     MutexAutoLock lock(mMutex);
  4851 #endif
  4853     // This will delete |loopInfo|!
  4854     mSyncLoopStack.RemoveElementAt(aLoopIndex);
  4857   MOZ_ALWAYS_TRUE(NS_SUCCEEDED(aThread->PopEventQueue(nestedEventTarget)));
  4859   return result;
  4862 void
  4863 WorkerPrivate::StopSyncLoop(nsIEventTarget* aSyncLoopTarget, bool aResult)
  4865   AssertIsOnWorkerThread();
  4866   AssertValidSyncLoop(aSyncLoopTarget);
  4868   MOZ_ASSERT(!mSyncLoopStack.IsEmpty());
  4870   for (uint32_t index = mSyncLoopStack.Length(); index > 0; index--) {
  4871     nsAutoPtr<SyncLoopInfo>& loopInfo = mSyncLoopStack[index - 1];
  4872     MOZ_ASSERT(loopInfo);
  4873     MOZ_ASSERT(loopInfo->mEventTarget);
  4875     if (loopInfo->mEventTarget == aSyncLoopTarget) {
  4876       // Can't assert |loop->mHasRun| here because dispatch failures can cause
  4877       // us to bail out early.
  4878       MOZ_ASSERT(!loopInfo->mCompleted);
  4880       loopInfo->mResult = aResult;
  4881       loopInfo->mCompleted = true;
  4883       loopInfo->mEventTarget->Disable();
  4885       return;
  4888     MOZ_ASSERT(!SameCOMIdentity(loopInfo->mEventTarget, aSyncLoopTarget));
  4891   MOZ_CRASH("Unknown sync loop!");
  4894 #ifdef DEBUG
  4895 void
  4896 WorkerPrivate::AssertValidSyncLoop(nsIEventTarget* aSyncLoopTarget)
  4898   MOZ_ASSERT(aSyncLoopTarget);
  4900   EventTarget* workerTarget;
  4901   nsresult rv =
  4902     aSyncLoopTarget->QueryInterface(kDEBUGWorkerEventTargetIID,
  4903                                     reinterpret_cast<void**>(&workerTarget));
  4904   MOZ_ASSERT(NS_SUCCEEDED(rv));
  4905   MOZ_ASSERT(workerTarget);
  4907   bool valid = false;
  4910     MutexAutoLock lock(mMutex);
  4912     for (uint32_t index = 0; index < mSyncLoopStack.Length(); index++) {
  4913       nsAutoPtr<SyncLoopInfo>& loopInfo = mSyncLoopStack[index];
  4914       MOZ_ASSERT(loopInfo);
  4915       MOZ_ASSERT(loopInfo->mEventTarget);
  4917       if (loopInfo->mEventTarget == aSyncLoopTarget) {
  4918         valid = true;
  4919         break;
  4922       MOZ_ASSERT(!SameCOMIdentity(loopInfo->mEventTarget, aSyncLoopTarget));
  4926   MOZ_ASSERT(valid);
  4928 #endif
  4930 void
  4931 WorkerPrivate::PostMessageToParentInternal(
  4932                             JSContext* aCx,
  4933                             JS::Handle<JS::Value> aMessage,
  4934                             const Optional<Sequence<JS::Value>>& aTransferable,
  4935                             bool aToMessagePort,
  4936                             uint64_t aMessagePortSerial,
  4937                             ErrorResult& aRv)
  4939   AssertIsOnWorkerThread();
  4941   JS::Rooted<JS::Value> transferable(aCx, JS::UndefinedValue());
  4942   if (aTransferable.WasPassed()) {
  4943     const Sequence<JS::Value>& realTransferable = aTransferable.Value();
  4945     // The input sequence only comes from the generated bindings code, which
  4946     // ensures it is rooted.
  4947     JS::HandleValueArray elements =
  4948       JS::HandleValueArray::fromMarkedLocation(realTransferable.Length(),
  4949                                                realTransferable.Elements());
  4951     JSObject* array = JS_NewArrayObject(aCx, elements);
  4952     if (!array) {
  4953       aRv = NS_ERROR_OUT_OF_MEMORY;
  4954       return;
  4956     transferable.setObject(*array);
  4959   JSStructuredCloneCallbacks* callbacks =
  4960     IsChromeWorker() ?
  4961     &gChromeWorkerStructuredCloneCallbacks :
  4962     &gWorkerStructuredCloneCallbacks;
  4964   nsTArray<nsCOMPtr<nsISupports>> clonedObjects;
  4966   JSAutoStructuredCloneBuffer buffer;
  4967   if (!buffer.write(aCx, aMessage, transferable, callbacks, &clonedObjects)) {
  4968     aRv = NS_ERROR_DOM_DATA_CLONE_ERR;
  4969     return;
  4972   nsRefPtr<MessageEventRunnable> runnable =
  4973     new MessageEventRunnable(this,
  4974                              WorkerRunnable::ParentThreadUnchangedBusyCount,
  4975                              Move(buffer), clonedObjects, aToMessagePort,
  4976                              aMessagePortSerial);
  4977   if (!runnable->Dispatch(aCx)) {
  4978     aRv = NS_ERROR_FAILURE;
  4982 void
  4983 WorkerPrivate::PostMessageToParentMessagePort(
  4984                              JSContext* aCx,
  4985                              uint64_t aMessagePortSerial,
  4986                              JS::Handle<JS::Value> aMessage,
  4987                              const Optional<Sequence<JS::Value>>& aTransferable,
  4988                              ErrorResult& aRv)
  4990   AssertIsOnWorkerThread();
  4992   if (!mWorkerPorts.GetWeak(aMessagePortSerial)) {
  4993     // This port has been closed from the main thread. There's no point in
  4994     // sending this message so just bail.
  4995     return;
  4998   PostMessageToParentInternal(aCx, aMessage, aTransferable, true,
  4999                               aMessagePortSerial, aRv);
  5002 bool
  5003 WorkerPrivate::NotifyInternal(JSContext* aCx, Status aStatus)
  5005   AssertIsOnWorkerThread();
  5007   NS_ASSERTION(aStatus > Running && aStatus < Dead, "Bad status!");
  5009   nsRefPtr<EventTarget> eventTarget;
  5011   // Save the old status and set the new status.
  5012   Status previousStatus;
  5014     MutexAutoLock lock(mMutex);
  5016     if (mStatus >= aStatus) {
  5017       MOZ_ASSERT(!mEventTarget);
  5018       return true;
  5021     previousStatus = mStatus;
  5022     mStatus = aStatus;
  5024     mEventTarget.swap(eventTarget);
  5027   // Now that mStatus > Running, no-one can create a new WorkerEventTarget or
  5028   // WorkerCrossThreadDispatcher if we don't already have one.
  5029   if (eventTarget) {
  5030     // Since we'll no longer process events, make sure we no longer allow anyone
  5031     // to post them. We have to do this without mMutex held, since our mutex
  5032     // must be acquired *after* the WorkerEventTarget's mutex when they're both
  5033     // held.
  5034     eventTarget->Disable();
  5035     eventTarget = nullptr;
  5038   if (mCrossThreadDispatcher) {
  5039     // Since we'll no longer process events, make sure we no longer allow
  5040     // anyone to post them. We have to do this without mMutex held, since our
  5041     // mutex must be acquired *after* mCrossThreadDispatcher's mutex when
  5042     // they're both held.
  5043     mCrossThreadDispatcher->Forget();
  5044     mCrossThreadDispatcher = nullptr;
  5047   MOZ_ASSERT(previousStatus != Pending);
  5049   MOZ_ASSERT(previousStatus >= Canceling || mKillTime.IsNull());
  5051   // Let all our features know the new status.
  5052   NotifyFeatures(aCx, aStatus);
  5054   // If this is the first time our status has changed then we need to clear the
  5055   // main event queue.
  5056   if (previousStatus == Running) {
  5057     ClearMainEventQueue(WorkerRan);
  5060   // If we've run the close handler, we don't need to do anything else.
  5061   if (mCloseHandlerFinished) {
  5062     return true;
  5065   // If the worker script never ran, or failed to compile, we don't need to do
  5066   // anything else, except pretend that we ran the close handler.
  5067   if (!JS::CurrentGlobalOrNull(aCx)) {
  5068     mCloseHandlerStarted = true;
  5069     mCloseHandlerFinished = true;
  5070     return true;
  5073   // If this is the first time our status has changed we also need to schedule
  5074   // the close handler unless we're being shut down.
  5075   if (previousStatus == Running && aStatus != Killing) {
  5076     MOZ_ASSERT(!mCloseHandlerStarted && !mCloseHandlerFinished);
  5078     nsRefPtr<CloseEventRunnable> closeRunnable = new CloseEventRunnable(this);
  5079     MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToCurrentThread(closeRunnable)));
  5082   if (aStatus == Closing) {
  5083     // Notify parent to stop sending us messages and balance our busy count.
  5084     nsRefPtr<CloseRunnable> runnable = new CloseRunnable(this);
  5085     if (!runnable->Dispatch(aCx)) {
  5086       return false;
  5089     // Don't abort the script.
  5090     return true;
  5093   if (aStatus == Terminating) {
  5094     // Only abort the script if we're not yet running the close handler.
  5095     return mCloseHandlerStarted;
  5098   if (aStatus == Canceling) {
  5099     // We need to enforce a timeout on the close handler.
  5100     MOZ_ASSERT(previousStatus >= Running && previousStatus <= Terminating);
  5102     uint32_t killSeconds = IsChromeWorker() ?
  5103       RuntimeService::GetChromeCloseHandlerTimeoutSeconds() :
  5104       RuntimeService::GetContentCloseHandlerTimeoutSeconds();
  5106     if (killSeconds) {
  5107       mKillTime = TimeStamp::Now() + TimeDuration::FromSeconds(killSeconds);
  5109       if (!mCloseHandlerFinished && !ScheduleKillCloseEventRunnable(aCx)) {
  5110         return false;
  5114     // Only abort the script if we're not yet running the close handler.
  5115     return mCloseHandlerStarted;
  5118   MOZ_ASSERT(aStatus == Killing);
  5120   mKillTime = TimeStamp::Now();
  5122   if (mCloseHandlerStarted && !mCloseHandlerFinished) {
  5123     ScheduleKillCloseEventRunnable(aCx);
  5126   // Always abort the script.
  5127   return false;
  5130 bool
  5131 WorkerPrivate::ScheduleKillCloseEventRunnable(JSContext* aCx)
  5133   AssertIsOnWorkerThread();
  5134   MOZ_ASSERT(!mKillTime.IsNull());
  5136   nsRefPtr<KillCloseEventRunnable> killCloseEventRunnable =
  5137     new KillCloseEventRunnable(this);
  5138   if (!killCloseEventRunnable->SetTimeout(aCx, RemainingRunTimeMS())) {
  5139     return false;
  5142   MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToCurrentThread(
  5143                                                       killCloseEventRunnable)));
  5145   return true;
  5148 void
  5149 WorkerPrivate::ReportError(JSContext* aCx, const char* aMessage,
  5150                            JSErrorReport* aReport)
  5152   AssertIsOnWorkerThread();
  5154   if (!MayContinueRunning() || mErrorHandlerRecursionCount == 2) {
  5155     return;
  5158   NS_ASSERTION(mErrorHandlerRecursionCount == 0 ||
  5159                mErrorHandlerRecursionCount == 1,
  5160                "Bad recursion logic!");
  5162   JS_ClearPendingException(aCx);
  5164   nsString message, filename, line;
  5165   uint32_t lineNumber, columnNumber, flags, errorNumber;
  5167   if (aReport) {
  5168     // ErrorEvent objects don't have a |name| field the way ES |Error| objects
  5169     // do. Traditionally (and mostly by accident), the |message| field of
  5170     // ErrorEvent has corresponded to |Name: Message| of the original Error
  5171     // object. Things have been cleaned up in the JS engine, so now we need to
  5172     // format this string explicitly.
  5173     JS::Rooted<JSString*> messageStr(aCx,
  5174                                      js::ErrorReportToString(aCx, aReport));
  5175     if (messageStr) {
  5176       nsDependentJSString depStr;
  5177       if (depStr.init(aCx, messageStr)) {
  5178         message = depStr;
  5181     filename = NS_ConvertUTF8toUTF16(aReport->filename);
  5182     line = aReport->uclinebuf;
  5183     lineNumber = aReport->lineno;
  5184     columnNumber = aReport->uctokenptr - aReport->uclinebuf;
  5185     flags = aReport->flags;
  5186     errorNumber = aReport->errorNumber;
  5188   else {
  5189     lineNumber = columnNumber = errorNumber = 0;
  5190     flags = nsIScriptError::errorFlag | nsIScriptError::exceptionFlag;
  5193   if (message.IsEmpty()) {
  5194     message = NS_ConvertUTF8toUTF16(aMessage);
  5197   mErrorHandlerRecursionCount++;
  5199   // Don't want to run the scope's error handler if this is a recursive error or
  5200   // if there was an error in the close handler or if we ran out of memory.
  5201   bool fireAtScope = mErrorHandlerRecursionCount == 1 &&
  5202                      !mCloseHandlerStarted &&
  5203                      errorNumber != JSMSG_OUT_OF_MEMORY;
  5205   if (!ReportErrorRunnable::ReportError(aCx, this, fireAtScope, nullptr, message,
  5206                                         filename, line, lineNumber,
  5207                                         columnNumber, flags, errorNumber, 0)) {
  5208     JS_ReportPendingException(aCx);
  5211   mErrorHandlerRecursionCount--;
  5214 int32_t
  5215 WorkerPrivate::SetTimeout(JSContext* aCx,
  5216                           Function* aHandler,
  5217                           const nsAString& aStringHandler,
  5218                           int32_t aTimeout,
  5219                           const Sequence<JS::Value>& aArguments,
  5220                           bool aIsInterval,
  5221                           ErrorResult& aRv)
  5223   AssertIsOnWorkerThread();
  5225   const int32_t timerId = mNextTimeoutId++;
  5227   Status currentStatus;
  5229     MutexAutoLock lock(mMutex);
  5230     currentStatus = mStatus;
  5233   // It's a script bug if setTimeout/setInterval are called from a close handler
  5234   // so throw an exception.
  5235   if (currentStatus == Closing) {
  5236     JS_ReportError(aCx, "Cannot schedule timeouts from the close handler!");
  5239   // If the worker is trying to call setTimeout/setInterval and the parent
  5240   // thread has initiated the close process then just silently fail.
  5241   if (currentStatus >= Closing) {
  5242     aRv.Throw(NS_ERROR_FAILURE);
  5243     return 0;
  5246   nsAutoPtr<TimeoutInfo> newInfo(new TimeoutInfo());
  5247   newInfo->mIsInterval = aIsInterval;
  5248   newInfo->mId = timerId;
  5250   if (MOZ_UNLIKELY(timerId == INT32_MAX)) {
  5251     NS_WARNING("Timeout ids overflowed!");
  5252     mNextTimeoutId = 1;
  5255   // Take care of the main argument.
  5256   if (aHandler) {
  5257     newInfo->mTimeoutCallable = JS::ObjectValue(*aHandler->Callable());
  5259   else if (!aStringHandler.IsEmpty()) {
  5260     newInfo->mTimeoutString = aStringHandler;
  5262   else {
  5263     JS_ReportError(aCx, "Useless %s call (missing quotes around argument?)",
  5264                    aIsInterval ? "setInterval" : "setTimeout");
  5265     return 0;
  5268   // See if any of the optional arguments were passed.
  5269   aTimeout = std::max(0, aTimeout);
  5270   newInfo->mInterval = TimeDuration::FromMilliseconds(aTimeout);
  5272   uint32_t argc = aArguments.Length();
  5273   if (argc && !newInfo->mTimeoutCallable.isUndefined()) {
  5274     nsTArray<JS::Heap<JS::Value>> extraArgVals(argc);
  5275     for (uint32_t index = 0; index < argc; index++) {
  5276       extraArgVals.AppendElement(aArguments[index]);
  5278     newInfo->mExtraArgVals.SwapElements(extraArgVals);
  5281   newInfo->mTargetTime = TimeStamp::Now() + newInfo->mInterval;
  5283   if (!newInfo->mTimeoutString.IsEmpty()) {
  5284     const char* filenameChars;
  5285     uint32_t lineNumber;
  5286     if (nsJSUtils::GetCallingLocation(aCx, &filenameChars, &lineNumber)) {
  5287       newInfo->mFilename = filenameChars;
  5288       newInfo->mLineNumber = lineNumber;
  5290     else {
  5291       NS_WARNING("Failed to get calling location!");
  5295   nsAutoPtr<TimeoutInfo>* insertedInfo =
  5296     mTimeouts.InsertElementSorted(newInfo.forget(), GetAutoPtrComparator(mTimeouts));
  5298   // If the timeout we just made is set to fire next then we need to update the
  5299   // timer.
  5300   if (insertedInfo == mTimeouts.Elements()) {
  5301     nsresult rv;
  5303     if (!mTimer) {
  5304       nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
  5305       if (NS_FAILED(rv)) {
  5306         aRv.Throw(rv);
  5307         return 0;
  5310       nsRefPtr<TimerRunnable> runnable = new TimerRunnable(this);
  5312       nsRefPtr<TimerThreadEventTarget> target =
  5313         new TimerThreadEventTarget(this, runnable);
  5315       rv = timer->SetTarget(target);
  5316       if (NS_FAILED(rv)) {
  5317         aRv.Throw(rv);
  5318         return 0;
  5321       timer.swap(mTimer);
  5324     if (!mTimerRunning) {
  5325       if (!ModifyBusyCountFromWorker(aCx, true)) {
  5326         aRv.Throw(NS_ERROR_FAILURE);
  5327         return 0;
  5329       mTimerRunning = true;
  5332     if (!RescheduleTimeoutTimer(aCx)) {
  5333       aRv.Throw(NS_ERROR_FAILURE);
  5334       return 0;
  5338   return timerId;
  5341 void
  5342 WorkerPrivate::ClearTimeout(int32_t aId)
  5344   AssertIsOnWorkerThread();
  5346   if (!mTimeouts.IsEmpty()) {
  5347     NS_ASSERTION(mTimerRunning, "Huh?!");
  5349     for (uint32_t index = 0; index < mTimeouts.Length(); index++) {
  5350       nsAutoPtr<TimeoutInfo>& info = mTimeouts[index];
  5351       if (info->mId == aId) {
  5352         info->mCanceled = true;
  5353         break;
  5359 bool
  5360 WorkerPrivate::RunExpiredTimeouts(JSContext* aCx)
  5362   AssertIsOnWorkerThread();
  5364   // We may be called recursively (e.g. close() inside a timeout) or we could
  5365   // have been canceled while this event was pending, bail out if there is
  5366   // nothing to do.
  5367   if (mRunningExpiredTimeouts || !mTimerRunning) {
  5368     return true;
  5371   NS_ASSERTION(mTimer, "Must have a timer!");
  5372   NS_ASSERTION(!mTimeouts.IsEmpty(), "Should have some work to do!");
  5374   bool retval = true;
  5376   AutoPtrComparator<TimeoutInfo> comparator = GetAutoPtrComparator(mTimeouts);
  5377   JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx));
  5379   // We want to make sure to run *something*, even if the timer fired a little
  5380   // early. Fudge the value of now to at least include the first timeout.
  5381   const TimeStamp now = std::max(TimeStamp::Now(), mTimeouts[0]->mTargetTime);
  5383   nsAutoTArray<TimeoutInfo*, 10> expiredTimeouts;
  5384   for (uint32_t index = 0; index < mTimeouts.Length(); index++) {
  5385     nsAutoPtr<TimeoutInfo>& info = mTimeouts[index];
  5386     if (info->mTargetTime > now) {
  5387       break;
  5389     expiredTimeouts.AppendElement(info);
  5392   // Guard against recursion.
  5393   mRunningExpiredTimeouts = true;
  5395   // Run expired timeouts.
  5396   for (uint32_t index = 0; index < expiredTimeouts.Length(); index++) {
  5397     TimeoutInfo*& info = expiredTimeouts[index];
  5399     if (info->mCanceled) {
  5400       continue;
  5403     // Always call JS_ReportPendingException if something fails, and if
  5404     // JS_ReportPendingException returns false (i.e. uncatchable exception) then
  5405     // break out of the loop.
  5407     if (!info->mTimeoutCallable.isUndefined()) {
  5408       JS::Rooted<JS::Value> rval(aCx);
  5409       JS::HandleValueArray args =
  5410         JS::HandleValueArray::fromMarkedLocation(info->mExtraArgVals.Length(),
  5411                                                  info->mExtraArgVals.Elements()->address());
  5412       JS::Rooted<JS::Value> callable(aCx, info->mTimeoutCallable);
  5413       if (!JS_CallFunctionValue(aCx, global, callable, args, &rval) &&
  5414           !JS_ReportPendingException(aCx)) {
  5415         retval = false;
  5416         break;
  5419     else {
  5420       nsString expression = info->mTimeoutString;
  5422       JS::CompileOptions options(aCx);
  5423       options.setFileAndLine(info->mFilename.get(), info->mLineNumber);
  5425       if ((expression.IsEmpty() ||
  5426            !JS::Evaluate(aCx, global, options, expression.get(), expression.Length())) &&
  5427           !JS_ReportPendingException(aCx)) {
  5428         retval = false;
  5429         break;
  5433     NS_ASSERTION(mRunningExpiredTimeouts, "Someone changed this!");
  5436   // No longer possible to be called recursively.
  5437   mRunningExpiredTimeouts = false;
  5439   // Now remove canceled and expired timeouts from the main list.
  5440   // NB: The timeouts present in expiredTimeouts must have the same order
  5441   // with respect to each other in mTimeouts.  That is, mTimeouts is just
  5442   // expiredTimeouts with extra elements inserted.  There may be unexpired
  5443   // timeouts that have been inserted between the expired timeouts if the
  5444   // timeout event handler called setTimeout/setInterval.
  5445   for (uint32_t index = 0, expiredTimeoutIndex = 0,
  5446        expiredTimeoutLength = expiredTimeouts.Length();
  5447        index < mTimeouts.Length(); ) {
  5448     nsAutoPtr<TimeoutInfo>& info = mTimeouts[index];
  5449     if ((expiredTimeoutIndex < expiredTimeoutLength &&
  5450          info == expiredTimeouts[expiredTimeoutIndex] &&
  5451          ++expiredTimeoutIndex) ||
  5452         info->mCanceled) {
  5453       if (info->mIsInterval && !info->mCanceled) {
  5454         // Reschedule intervals.
  5455         info->mTargetTime = info->mTargetTime + info->mInterval;
  5456         // Don't resort the list here, we'll do that at the end.
  5457         ++index;
  5459       else {
  5460         mTimeouts.RemoveElement(info);
  5463     else {
  5464       // If info did not match the current entry in expiredTimeouts, it
  5465       // shouldn't be there at all.
  5466       NS_ASSERTION(!expiredTimeouts.Contains(info),
  5467                    "Our timeouts are out of order!");
  5468       ++index;
  5472   mTimeouts.Sort(comparator);
  5474   // Either signal the parent that we're no longer using timeouts or reschedule
  5475   // the timer.
  5476   if (mTimeouts.IsEmpty()) {
  5477     if (!ModifyBusyCountFromWorker(aCx, false)) {
  5478       retval = false;
  5480     mTimerRunning = false;
  5482   else if (retval && !RescheduleTimeoutTimer(aCx)) {
  5483     retval = false;
  5486   return retval;
  5489 bool
  5490 WorkerPrivate::RescheduleTimeoutTimer(JSContext* aCx)
  5492   AssertIsOnWorkerThread();
  5493   NS_ASSERTION(!mTimeouts.IsEmpty(), "Should have some timeouts!");
  5494   NS_ASSERTION(mTimer, "Should have a timer!");
  5496   double delta =
  5497     (mTimeouts[0]->mTargetTime - TimeStamp::Now()).ToMilliseconds();
  5498   uint32_t delay = delta > 0 ? std::min(delta, double(UINT32_MAX)) : 0;
  5500   nsresult rv = mTimer->InitWithFuncCallback(DummyCallback, nullptr, delay,
  5501                                              nsITimer::TYPE_ONE_SHOT);
  5502   if (NS_FAILED(rv)) {
  5503     JS_ReportError(aCx, "Failed to start timer!");
  5504     return false;
  5507   return true;
  5510 void
  5511 WorkerPrivate::UpdateRuntimeAndContextOptionsInternal(
  5512                                     JSContext* aCx,
  5513                                     const JS::RuntimeOptions& aRuntimeOptions,
  5514                                     const JS::ContextOptions& aContentCxOptions,
  5515                                     const JS::ContextOptions& aChromeCxOptions)
  5517   AssertIsOnWorkerThread();
  5519   JS::RuntimeOptionsRef(aCx) = aRuntimeOptions;
  5520   JS::ContextOptionsRef(aCx) = IsChromeWorker() ? aChromeCxOptions : aContentCxOptions;
  5522   for (uint32_t index = 0; index < mChildWorkers.Length(); index++) {
  5523     mChildWorkers[index]->UpdateRuntimeAndContextOptions(aCx, aRuntimeOptions,
  5524                                                          aContentCxOptions,
  5525                                                          aChromeCxOptions);
  5529 void
  5530 WorkerPrivate::UpdatePreferenceInternal(JSContext* aCx, WorkerPreference aPref, bool aValue)
  5532   AssertIsOnWorkerThread();
  5533   MOZ_ASSERT(aPref >= 0 && aPref < WORKERPREF_COUNT);
  5535   mPreferences[aPref] = aValue;
  5537   for (uint32_t index = 0; index < mChildWorkers.Length(); index++) {
  5538     mChildWorkers[index]->UpdatePreference(aCx, aPref, aValue);
  5542 void
  5543 WorkerPrivate::UpdateJSWorkerMemoryParameterInternal(JSContext* aCx,
  5544                                                      JSGCParamKey aKey,
  5545                                                      uint32_t aValue)
  5547   AssertIsOnWorkerThread();
  5549   // XXX aValue might be 0 here (telling us to unset a previous value for child
  5550   // workers). Calling JS_SetGCParameter with a value of 0 isn't actually
  5551   // supported though. We really need some way to revert to a default value
  5552   // here.
  5553   if (aValue) {
  5554     JS_SetGCParameter(JS_GetRuntime(aCx), aKey, aValue);
  5557   for (uint32_t index = 0; index < mChildWorkers.Length(); index++) {
  5558     mChildWorkers[index]->UpdateJSWorkerMemoryParameter(aCx, aKey, aValue);
  5562 #ifdef JS_GC_ZEAL
  5563 void
  5564 WorkerPrivate::UpdateGCZealInternal(JSContext* aCx, uint8_t aGCZeal,
  5565                                     uint32_t aFrequency)
  5567   AssertIsOnWorkerThread();
  5569   JS_SetGCZeal(aCx, aGCZeal, aFrequency);
  5571   for (uint32_t index = 0; index < mChildWorkers.Length(); index++) {
  5572     mChildWorkers[index]->UpdateGCZeal(aCx, aGCZeal, aFrequency);
  5575 #endif
  5577 void
  5578 WorkerPrivate::GarbageCollectInternal(JSContext* aCx, bool aShrinking,
  5579                                       bool aCollectChildren)
  5581   AssertIsOnWorkerThread();
  5583   if (!JS::CurrentGlobalOrNull(aCx)) {
  5584     // We haven't compiled anything yet. Just bail out.
  5585     return;
  5588   if (aShrinking || aCollectChildren) {
  5589     JSRuntime* rt = JS_GetRuntime(aCx);
  5590     JS::PrepareForFullGC(rt);
  5592     if (aShrinking) {
  5593       JS::ShrinkingGC(rt, JS::gcreason::DOM_WORKER);
  5595       if (!aCollectChildren) {
  5596         LOG(("Worker %p collected idle garbage\n", this));
  5599     else {
  5600       JS::GCForReason(rt, JS::gcreason::DOM_WORKER);
  5601       LOG(("Worker %p collected garbage\n", this));
  5604   else {
  5605     JS_MaybeGC(aCx);
  5606     LOG(("Worker %p collected periodic garbage\n", this));
  5609   if (aCollectChildren) {
  5610     for (uint32_t index = 0; index < mChildWorkers.Length(); index++) {
  5611       mChildWorkers[index]->GarbageCollect(aCx, aShrinking);
  5616 void
  5617 WorkerPrivate::CycleCollectInternal(JSContext* aCx, bool aCollectChildren)
  5619   AssertIsOnWorkerThread();
  5621   nsCycleCollector_collect(nullptr);
  5623   if (aCollectChildren) {
  5624     for (uint32_t index = 0; index < mChildWorkers.Length(); index++) {
  5625       mChildWorkers[index]->CycleCollect(aCx, /* dummy = */ false);
  5630 void
  5631 WorkerPrivate::SetThread(nsIThread* aThread)
  5633 #ifdef DEBUG
  5634   if (aThread) {
  5635     bool isOnCurrentThread;
  5636     MOZ_ASSERT(NS_SUCCEEDED(aThread->IsOnCurrentThread(&isOnCurrentThread)));
  5637     MOZ_ASSERT(isOnCurrentThread);
  5639     MOZ_ASSERT(!mPRThread);
  5640     mPRThread = PRThreadFromThread(aThread);
  5641     MOZ_ASSERT(mPRThread);
  5643   else {
  5644     MOZ_ASSERT(mPRThread);
  5646 #endif
  5648   nsCOMPtr<nsIThread> doomedThread;
  5650   { // Scope so that |doomedThread| is released without holding the lock.
  5651     MutexAutoLock lock(mMutex);
  5653     if (aThread) {
  5654       MOZ_ASSERT(!mThread);
  5655       MOZ_ASSERT(mStatus == Pending);
  5657       mThread = aThread;
  5659       if (!mPreStartRunnables.IsEmpty()) {
  5660         for (uint32_t index = 0; index < mPreStartRunnables.Length(); index++) {
  5661           MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mThread->Dispatch(
  5662                                                       mPreStartRunnables[index],
  5663                                                       NS_DISPATCH_NORMAL)));
  5665         mPreStartRunnables.Clear();
  5668     else {
  5669       MOZ_ASSERT(mThread);
  5670       mThread.swap(doomedThread);
  5675 WorkerCrossThreadDispatcher*
  5676 WorkerPrivate::GetCrossThreadDispatcher()
  5678   MutexAutoLock lock(mMutex);
  5680   if (!mCrossThreadDispatcher && mStatus <= Running) {
  5681     mCrossThreadDispatcher = new WorkerCrossThreadDispatcher(this);
  5684   return mCrossThreadDispatcher;
  5687 void
  5688 WorkerPrivate::BeginCTypesCall()
  5690   AssertIsOnWorkerThread();
  5692   // Don't try to GC while we're blocked in a ctypes call.
  5693   SetGCTimerMode(NoTimer);
  5695   MutexAutoLock lock(mMutex);
  5697   NS_ASSERTION(!mBlockedForMemoryReporter,
  5698                "Can't be blocked in more than one place at the same time!");
  5700   // Let the main thread know that the worker is effectively blocked while in
  5701   // this ctypes call. It isn't technically true (obviously the call could do
  5702   // non-blocking things), but we're assuming that ctypes can't call back into
  5703   // JSAPI here and therefore any work the ctypes call does will not alter the
  5704   // data structures of this JS runtime.
  5705   mBlockedForMemoryReporter = true;
  5707   // The main thread may be waiting on us so it must be notified.
  5708   mMemoryReportCondVar.Notify();
  5711 void
  5712 WorkerPrivate::EndCTypesCall()
  5714   AssertIsOnWorkerThread();
  5717     MutexAutoLock lock(mMutex);
  5719     NS_ASSERTION(mBlockedForMemoryReporter, "Somehow we got unblocked!");
  5721     // Don't continue until the memory reporter has finished.
  5722     while (mMemoryReporterRunning) {
  5723       mMemoryReportCondVar.Wait();
  5726     // No need to notify the main thread here as it shouldn't be waiting to see
  5727     // this state.
  5728     mBlockedForMemoryReporter = false;
  5731   // Make sure the periodic timer is running before we start running JS again.
  5732   SetGCTimerMode(PeriodicTimer);
  5735 bool
  5736 WorkerPrivate::ConnectMessagePort(JSContext* aCx, uint64_t aMessagePortSerial)
  5738   AssertIsOnWorkerThread();
  5740   NS_ASSERTION(!mWorkerPorts.GetWeak(aMessagePortSerial),
  5741                "Already have this port registered!");
  5743   WorkerGlobalScope* globalScope = GlobalScope();
  5745   JS::Rooted<JSObject*> jsGlobal(aCx, globalScope->GetWrapper());
  5746   MOZ_ASSERT(jsGlobal);
  5748   nsRefPtr<MessagePort> port = new MessagePort(this, aMessagePortSerial);
  5750   GlobalObject globalObject(aCx, jsGlobal);
  5751   if (globalObject.Failed()) {
  5752     return false;
  5755   RootedDictionary<MessageEventInit> init(aCx);
  5756   init.mBubbles = false;
  5757   init.mCancelable = false;
  5758   init.mSource.SetValue().SetAsMessagePort() = port;
  5760   ErrorResult rv;
  5762   nsRefPtr<MessageEvent> event =
  5763     MessageEvent::Constructor(globalObject, aCx,
  5764                               NS_LITERAL_STRING("connect"), init, rv);
  5766   event->SetTrusted(true);
  5768   nsTArray<nsRefPtr<MessagePortBase>> ports;
  5769   ports.AppendElement(port);
  5771   nsRefPtr<MessagePortList> portList =
  5772     new MessagePortList(static_cast<nsIDOMEventTarget*>(globalScope), ports);
  5773   event->SetPorts(portList);
  5775   mWorkerPorts.Put(aMessagePortSerial, port);
  5777   nsCOMPtr<nsIDOMEvent> domEvent = do_QueryObject(event);
  5779   nsEventStatus dummy = nsEventStatus_eIgnore;
  5780   globalScope->DispatchDOMEvent(nullptr, domEvent, nullptr, &dummy);
  5781   return true;
  5784 void
  5785 WorkerPrivate::DisconnectMessagePort(uint64_t aMessagePortSerial)
  5787   AssertIsOnWorkerThread();
  5789   mWorkerPorts.Remove(aMessagePortSerial);
  5792 workers::MessagePort*
  5793 WorkerPrivate::GetMessagePort(uint64_t aMessagePortSerial)
  5795   AssertIsOnWorkerThread();
  5797   nsRefPtr<MessagePort> port;
  5798   if (mWorkerPorts.Get(aMessagePortSerial, getter_AddRefs(port))) {
  5799     return port;
  5802   return nullptr;
  5805 JSObject*
  5806 WorkerPrivate::CreateGlobalScope(JSContext* aCx)
  5808   AssertIsOnWorkerThread();
  5810   nsRefPtr<WorkerGlobalScope> globalScope;
  5811   if (IsSharedWorker()) {
  5812     globalScope = new SharedWorkerGlobalScope(this, SharedWorkerName());
  5814   else {
  5815     globalScope = new DedicatedWorkerGlobalScope(this);
  5818   JS::Rooted<JSObject*> global(aCx, globalScope->WrapGlobalObject(aCx));
  5819   NS_ENSURE_TRUE(global, nullptr);
  5821   JSAutoCompartment ac(aCx, global);
  5823   if (!RegisterBindings(aCx, global)) {
  5824     return nullptr;
  5827   mScope = globalScope.forget();
  5829   JS_FireOnNewGlobalObject(aCx, global);
  5831   return global;
  5834 #ifdef DEBUG
  5836 void
  5837 WorkerPrivate::AssertIsOnWorkerThread() const
  5839   // This is much more complicated than it needs to be but we can't use mThread
  5840   // because it must be protected by mMutex and sometimes this method is called
  5841   // when mMutex is already locked. This method should always work.
  5842   MOZ_ASSERT(mPRThread,
  5843              "AssertIsOnWorkerThread() called before a thread was assigned!");
  5845   MOZ_ASSERT(nsThreadManager::get());
  5847   nsCOMPtr<nsIThread> thread;
  5848   nsresult rv =
  5849     nsThreadManager::get()->GetThreadFromPRThread(mPRThread,
  5850                                                   getter_AddRefs(thread));
  5851   MOZ_ASSERT(NS_SUCCEEDED(rv));
  5852   MOZ_ASSERT(thread);
  5854   bool current;
  5855   rv = thread->IsOnCurrentThread(&current);
  5856   MOZ_ASSERT(NS_SUCCEEDED(rv));
  5857   MOZ_ASSERT(current, "Wrong thread!");
  5860 #endif // DEBUG
  5862 NS_IMPL_ISUPPORTS_INHERITED0(ExternalRunnableWrapper, WorkerRunnable)
  5864 template <class Derived>
  5865 NS_IMPL_ADDREF(WorkerPrivateParent<Derived>::EventTarget)
  5867 template <class Derived>
  5868 NS_IMPL_RELEASE(WorkerPrivateParent<Derived>::EventTarget)
  5870 template <class Derived>
  5871 NS_INTERFACE_MAP_BEGIN(WorkerPrivateParent<Derived>::EventTarget)
  5872   NS_INTERFACE_MAP_ENTRY(nsIEventTarget)
  5873   NS_INTERFACE_MAP_ENTRY(nsISupports)
  5874 #ifdef DEBUG
  5875   // kDEBUGWorkerEventTargetIID is special in that it does not AddRef its
  5876   // result.
  5877   if (aIID.Equals(kDEBUGWorkerEventTargetIID)) {
  5878     *aInstancePtr = this;
  5879     return NS_OK;
  5881   else
  5882 #endif
  5883 NS_INTERFACE_MAP_END
  5885 template <class Derived>
  5886 NS_IMETHODIMP
  5887 WorkerPrivateParent<Derived>::
  5888 EventTarget::Dispatch(nsIRunnable* aRunnable, uint32_t aFlags)
  5890   // May be called on any thread!
  5892   // Workers only support asynchronous dispatch for now.
  5893   if (NS_WARN_IF(aFlags != NS_DISPATCH_NORMAL)) {
  5894     return NS_ERROR_UNEXPECTED;
  5897   nsRefPtr<WorkerRunnable> workerRunnable;
  5899   MutexAutoLock lock(mMutex);
  5901   if (!mWorkerPrivate) {
  5902     NS_WARNING("A runnable was posted to a worker that is already shutting "
  5903                "down!");
  5904     return NS_ERROR_UNEXPECTED;
  5907   if (aRunnable) {
  5908     workerRunnable = mWorkerPrivate->MaybeWrapAsWorkerRunnable(aRunnable);
  5911   nsresult rv =
  5912     mWorkerPrivate->DispatchPrivate(workerRunnable, mNestedEventTarget);
  5913   if (NS_WARN_IF(NS_FAILED(rv))) {
  5914     return rv;
  5917   return NS_OK;
  5920 template <class Derived>
  5921 NS_IMETHODIMP
  5922 WorkerPrivateParent<Derived>::
  5923 EventTarget::IsOnCurrentThread(bool* aIsOnCurrentThread)
  5925   // May be called on any thread!
  5927   MOZ_ASSERT(aIsOnCurrentThread);
  5929   MutexAutoLock lock(mMutex);
  5931   if (!mWorkerPrivate) {
  5932     NS_WARNING("A worker's event target was used after the worker has !");
  5933     return NS_ERROR_UNEXPECTED;
  5936   nsresult rv = mWorkerPrivate->IsOnCurrentThread(aIsOnCurrentThread);
  5937   if (NS_WARN_IF(NS_FAILED(rv))) {
  5938     return rv;
  5941   return NS_OK;
  5944 BEGIN_WORKERS_NAMESPACE
  5946 WorkerCrossThreadDispatcher*
  5947 GetWorkerCrossThreadDispatcher(JSContext* aCx, JS::Value aWorker)
  5949   if (!aWorker.isObject()) {
  5950     return nullptr;
  5953   WorkerPrivate* w = nullptr;
  5954   UNWRAP_OBJECT(Worker, &aWorker.toObject(), w);
  5955   MOZ_ASSERT(w);
  5956   return w->GetCrossThreadDispatcher();
  5959 JSStructuredCloneCallbacks*
  5960 WorkerStructuredCloneCallbacks(bool aMainRuntime)
  5962   return aMainRuntime ?
  5963          &gMainThreadWorkerStructuredCloneCallbacks :
  5964          &gWorkerStructuredCloneCallbacks;
  5967 JSStructuredCloneCallbacks*
  5968 ChromeWorkerStructuredCloneCallbacks(bool aMainRuntime)
  5970   return aMainRuntime ?
  5971          &gMainThreadChromeWorkerStructuredCloneCallbacks :
  5972          &gChromeWorkerStructuredCloneCallbacks;
  5975 // Force instantiation.
  5976 template class WorkerPrivateParent<WorkerPrivate>;
  5978 END_WORKERS_NAMESPACE

mercurial