1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/workers/WorkerPrivate.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,5978 @@ 1.4 +/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ 1.5 +/* vim: set ts=2 et sw=2 tw=80: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "WorkerPrivate.h" 1.11 + 1.12 +#include "amIAddonManager.h" 1.13 +#include "nsIClassInfo.h" 1.14 +#include "nsIContentSecurityPolicy.h" 1.15 +#include "nsIConsoleService.h" 1.16 +#include "nsIDOMDOMException.h" 1.17 +#include "nsIDOMEvent.h" 1.18 +#include "nsIDOMFile.h" 1.19 +#include "nsIDOMMessageEvent.h" 1.20 +#include "nsIDocument.h" 1.21 +#include "nsIDocShell.h" 1.22 +#include "nsIMemoryReporter.h" 1.23 +#include "nsIPermissionManager.h" 1.24 +#include "nsIScriptError.h" 1.25 +#include "nsIScriptGlobalObject.h" 1.26 +#include "nsIScriptSecurityManager.h" 1.27 +#include "nsPIDOMWindow.h" 1.28 +#include "nsITextToSubURI.h" 1.29 +#include "nsIThreadInternal.h" 1.30 +#include "nsITimer.h" 1.31 +#include "nsIURI.h" 1.32 +#include "nsIURL.h" 1.33 +#include "nsIXPConnect.h" 1.34 + 1.35 +#include <algorithm> 1.36 +#include "jsfriendapi.h" 1.37 +#include "js/OldDebugAPI.h" 1.38 +#include "js/MemoryMetrics.h" 1.39 +#include "mozilla/Assertions.h" 1.40 +#include "mozilla/ContentEvents.h" 1.41 +#include "mozilla/EventDispatcher.h" 1.42 +#include "mozilla/Likely.h" 1.43 +#include "mozilla/dom/BindingUtils.h" 1.44 +#include "mozilla/dom/ErrorEvent.h" 1.45 +#include "mozilla/dom/ErrorEventBinding.h" 1.46 +#include "mozilla/dom/Exceptions.h" 1.47 +#include "mozilla/dom/FunctionBinding.h" 1.48 +#include "mozilla/dom/ImageData.h" 1.49 +#include "mozilla/dom/ImageDataBinding.h" 1.50 +#include "mozilla/dom/MessageEvent.h" 1.51 +#include "mozilla/dom/MessageEventBinding.h" 1.52 +#include "mozilla/dom/MessagePortList.h" 1.53 +#include "mozilla/dom/WorkerBinding.h" 1.54 +#include "mozilla/Preferences.h" 1.55 +#include "nsAlgorithm.h" 1.56 +#include "nsContentUtils.h" 1.57 +#include "nsCxPusher.h" 1.58 +#include "nsError.h" 1.59 +#include "nsDOMJSUtils.h" 1.60 +#include "nsHostObjectProtocolHandler.h" 1.61 +#include "nsJSEnvironment.h" 1.62 +#include "nsJSUtils.h" 1.63 +#include "nsNetUtil.h" 1.64 +#include "nsPrintfCString.h" 1.65 +#include "nsProxyRelease.h" 1.66 +#include "nsSandboxFlags.h" 1.67 +#include "xpcpublic.h" 1.68 + 1.69 +#ifdef ANDROID 1.70 +#include <android/log.h> 1.71 +#endif 1.72 + 1.73 +#ifdef DEBUG 1.74 +#include "nsThreadManager.h" 1.75 +#endif 1.76 + 1.77 +#include "File.h" 1.78 +#include "MessagePort.h" 1.79 +#include "Navigator.h" 1.80 +#include "Principal.h" 1.81 +#include "RuntimeService.h" 1.82 +#include "ScriptLoader.h" 1.83 +#include "SharedWorker.h" 1.84 +#include "WorkerFeature.h" 1.85 +#include "WorkerRunnable.h" 1.86 +#include "WorkerScope.h" 1.87 + 1.88 +// JS_MaybeGC will run once every second during normal execution. 1.89 +#define PERIODIC_GC_TIMER_DELAY_SEC 1 1.90 + 1.91 +// A shrinking GC will run five seconds after the last event is processed. 1.92 +#define IDLE_GC_TIMER_DELAY_SEC 5 1.93 + 1.94 +#define PREF_WORKERS_ENABLED "dom.workers.enabled" 1.95 + 1.96 +#ifdef WORKER_LOGGING 1.97 +#define LOG(_args) do { printf _args ; fflush(stdout); } while (0) 1.98 +#else 1.99 +#define LOG(_args) do { } while (0) 1.100 +#endif 1.101 + 1.102 +using namespace mozilla; 1.103 +using namespace mozilla::dom; 1.104 +USING_WORKERS_NAMESPACE 1.105 + 1.106 +MOZ_DEFINE_MALLOC_SIZE_OF(JsWorkerMallocSizeOf) 1.107 + 1.108 +#ifdef DEBUG 1.109 + 1.110 +BEGIN_WORKERS_NAMESPACE 1.111 + 1.112 +void 1.113 +AssertIsOnMainThread() 1.114 +{ 1.115 + MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!"); 1.116 +} 1.117 + 1.118 +END_WORKERS_NAMESPACE 1.119 + 1.120 +#endif 1.121 + 1.122 +namespace { 1.123 + 1.124 +#ifdef DEBUG 1.125 + 1.126 +const nsIID kDEBUGWorkerEventTargetIID = { 1.127 + 0xccaba3fa, 0x5be2, 0x4de2, { 0xba, 0x87, 0x3b, 0x3b, 0x5b, 0x1d, 0x5, 0xfb } 1.128 +}; 1.129 + 1.130 +#endif 1.131 + 1.132 +template <class T> 1.133 +class AutoPtrComparator 1.134 +{ 1.135 + typedef nsAutoPtr<T> A; 1.136 + typedef T* B; 1.137 + 1.138 +public: 1.139 + bool Equals(const A& a, const B& b) const { 1.140 + return a && b ? *a == *b : !a && !b ? true : false; 1.141 + } 1.142 + bool LessThan(const A& a, const B& b) const { 1.143 + return a && b ? *a < *b : b ? true : false; 1.144 + } 1.145 +}; 1.146 + 1.147 +template <class T> 1.148 +inline AutoPtrComparator<T> 1.149 +GetAutoPtrComparator(const nsTArray<nsAutoPtr<T> >&) 1.150 +{ 1.151 + return AutoPtrComparator<T>(); 1.152 +} 1.153 + 1.154 +// Specialize this if there's some class that has multiple nsISupports bases. 1.155 +template <class T> 1.156 +struct ISupportsBaseInfo 1.157 +{ 1.158 + typedef T ISupportsBase; 1.159 +}; 1.160 + 1.161 +template <template <class> class SmartPtr, class T> 1.162 +inline void 1.163 +SwapToISupportsArray(SmartPtr<T>& aSrc, 1.164 + nsTArray<nsCOMPtr<nsISupports> >& aDest) 1.165 +{ 1.166 + nsCOMPtr<nsISupports>* dest = aDest.AppendElement(); 1.167 + 1.168 + T* raw = nullptr; 1.169 + aSrc.swap(raw); 1.170 + 1.171 + nsISupports* rawSupports = 1.172 + static_cast<typename ISupportsBaseInfo<T>::ISupportsBase*>(raw); 1.173 + dest->swap(rawSupports); 1.174 +} 1.175 + 1.176 +// This class is used to wrap any runnables that the worker receives via the 1.177 +// nsIEventTarget::Dispatch() method (either from NS_DispatchToCurrentThread or 1.178 +// from the worker's EventTarget). 1.179 +class ExternalRunnableWrapper MOZ_FINAL : public WorkerRunnable 1.180 +{ 1.181 + nsCOMPtr<nsICancelableRunnable> mWrappedRunnable; 1.182 + 1.183 +public: 1.184 + ExternalRunnableWrapper(WorkerPrivate* aWorkerPrivate, 1.185 + nsICancelableRunnable* aWrappedRunnable) 1.186 + : WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount), 1.187 + mWrappedRunnable(aWrappedRunnable) 1.188 + { 1.189 + MOZ_ASSERT(aWorkerPrivate); 1.190 + MOZ_ASSERT(aWrappedRunnable); 1.191 + } 1.192 + 1.193 + NS_DECL_ISUPPORTS_INHERITED 1.194 + 1.195 +private: 1.196 + ~ExternalRunnableWrapper() 1.197 + { } 1.198 + 1.199 + virtual bool 1.200 + WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE 1.201 + { 1.202 + nsresult rv = mWrappedRunnable->Run(); 1.203 + if (NS_FAILED(rv)) { 1.204 + if (!JS_IsExceptionPending(aCx)) { 1.205 + Throw(aCx, rv); 1.206 + } 1.207 + return false; 1.208 + } 1.209 + return true; 1.210 + } 1.211 + 1.212 + NS_IMETHOD 1.213 + Cancel() MOZ_OVERRIDE 1.214 + { 1.215 + nsresult rv = mWrappedRunnable->Cancel(); 1.216 + nsresult rv2 = WorkerRunnable::Cancel(); 1.217 + return NS_FAILED(rv) ? rv : rv2; 1.218 + } 1.219 +}; 1.220 + 1.221 +struct WindowAction 1.222 +{ 1.223 + nsPIDOMWindow* mWindow; 1.224 + JSContext* mJSContext; 1.225 + bool mDefaultAction; 1.226 + 1.227 + WindowAction(nsPIDOMWindow* aWindow, JSContext* aJSContext) 1.228 + : mWindow(aWindow), mJSContext(aJSContext), mDefaultAction(true) 1.229 + { 1.230 + MOZ_ASSERT(aJSContext); 1.231 + } 1.232 + 1.233 + WindowAction(nsPIDOMWindow* aWindow) 1.234 + : mWindow(aWindow), mJSContext(nullptr), mDefaultAction(true) 1.235 + { } 1.236 + 1.237 + bool 1.238 + operator==(const WindowAction& aOther) const 1.239 + { 1.240 + return mWindow == aOther.mWindow; 1.241 + } 1.242 +}; 1.243 + 1.244 +void 1.245 +LogErrorToConsole(const nsAString& aMessage, 1.246 + const nsAString& aFilename, 1.247 + const nsAString& aLine, 1.248 + uint32_t aLineNumber, 1.249 + uint32_t aColumnNumber, 1.250 + uint32_t aFlags, 1.251 + uint64_t aInnerWindowId) 1.252 +{ 1.253 + AssertIsOnMainThread(); 1.254 + 1.255 + nsCOMPtr<nsIScriptError> scriptError = 1.256 + do_CreateInstance(NS_SCRIPTERROR_CONTRACTID); 1.257 + NS_WARN_IF_FALSE(scriptError, "Failed to create script error!"); 1.258 + 1.259 + if (scriptError) { 1.260 + if (NS_FAILED(scriptError->InitWithWindowID(aMessage, aFilename, aLine, 1.261 + aLineNumber, aColumnNumber, 1.262 + aFlags, "Web Worker", 1.263 + aInnerWindowId))) { 1.264 + NS_WARNING("Failed to init script error!"); 1.265 + scriptError = nullptr; 1.266 + } 1.267 + } 1.268 + 1.269 + nsCOMPtr<nsIConsoleService> consoleService = 1.270 + do_GetService(NS_CONSOLESERVICE_CONTRACTID); 1.271 + NS_WARN_IF_FALSE(consoleService, "Failed to get console service!"); 1.272 + 1.273 + if (consoleService) { 1.274 + if (scriptError) { 1.275 + if (NS_SUCCEEDED(consoleService->LogMessage(scriptError))) { 1.276 + return; 1.277 + } 1.278 + NS_WARNING("LogMessage failed!"); 1.279 + } else if (NS_SUCCEEDED(consoleService->LogStringMessage( 1.280 + aMessage.BeginReading()))) { 1.281 + return; 1.282 + } 1.283 + NS_WARNING("LogStringMessage failed!"); 1.284 + } 1.285 + 1.286 + NS_ConvertUTF16toUTF8 msg(aMessage); 1.287 + NS_ConvertUTF16toUTF8 filename(aFilename); 1.288 + 1.289 + static const char kErrorString[] = "JS error in Web Worker: %s [%s:%u]"; 1.290 + 1.291 +#ifdef ANDROID 1.292 + __android_log_print(ANDROID_LOG_INFO, "Gecko", kErrorString, msg.get(), 1.293 + filename.get(), aLineNumber); 1.294 +#endif 1.295 + 1.296 + fprintf(stderr, kErrorString, msg.get(), filename.get(), aLineNumber); 1.297 + fflush(stderr); 1.298 +} 1.299 + 1.300 +struct WorkerStructuredCloneCallbacks 1.301 +{ 1.302 + static JSObject* 1.303 + Read(JSContext* aCx, JSStructuredCloneReader* aReader, uint32_t aTag, 1.304 + uint32_t aData, void* aClosure) 1.305 + { 1.306 + // See if object is a nsIDOMFile pointer. 1.307 + if (aTag == DOMWORKER_SCTAG_FILE) { 1.308 + MOZ_ASSERT(!aData); 1.309 + 1.310 + nsIDOMFile* file; 1.311 + if (JS_ReadBytes(aReader, &file, sizeof(file))) { 1.312 + MOZ_ASSERT(file); 1.313 + 1.314 +#ifdef DEBUG 1.315 + { 1.316 + // File should not be mutable. 1.317 + nsCOMPtr<nsIMutable> mutableFile = do_QueryInterface(file); 1.318 + bool isMutable; 1.319 + NS_ASSERTION(NS_SUCCEEDED(mutableFile->GetMutable(&isMutable)) && 1.320 + !isMutable, 1.321 + "Only immutable file should be passed to worker"); 1.322 + } 1.323 +#endif 1.324 + 1.325 + // nsIDOMFiles should be threadsafe, thus we will use the same instance 1.326 + // in the worker. 1.327 + JSObject* jsFile = file::CreateFile(aCx, file); 1.328 + return jsFile; 1.329 + } 1.330 + } 1.331 + // See if object is a nsIDOMBlob pointer. 1.332 + else if (aTag == DOMWORKER_SCTAG_BLOB) { 1.333 + MOZ_ASSERT(!aData); 1.334 + 1.335 + nsIDOMBlob* blob; 1.336 + if (JS_ReadBytes(aReader, &blob, sizeof(blob))) { 1.337 + MOZ_ASSERT(blob); 1.338 + 1.339 +#ifdef DEBUG 1.340 + { 1.341 + // Blob should not be mutable. 1.342 + nsCOMPtr<nsIMutable> mutableBlob = do_QueryInterface(blob); 1.343 + bool isMutable; 1.344 + NS_ASSERTION(NS_SUCCEEDED(mutableBlob->GetMutable(&isMutable)) && 1.345 + !isMutable, 1.346 + "Only immutable blob should be passed to worker"); 1.347 + } 1.348 +#endif 1.349 + 1.350 + // nsIDOMBlob should be threadsafe, thus we will use the same instance 1.351 + // in the worker. 1.352 + JSObject* jsBlob = file::CreateBlob(aCx, blob); 1.353 + return jsBlob; 1.354 + } 1.355 + } 1.356 + // See if the object is an ImageData. 1.357 + else if (aTag == SCTAG_DOM_IMAGEDATA) { 1.358 + MOZ_ASSERT(!aData); 1.359 + 1.360 + // Read the information out of the stream. 1.361 + uint32_t width, height; 1.362 + JS::Rooted<JS::Value> dataArray(aCx); 1.363 + if (!JS_ReadUint32Pair(aReader, &width, &height) || 1.364 + !JS_ReadTypedArray(aReader, &dataArray)) 1.365 + { 1.366 + return nullptr; 1.367 + } 1.368 + MOZ_ASSERT(dataArray.isObject()); 1.369 + 1.370 + JS::Rooted<JSObject*> result(aCx); 1.371 + { 1.372 + // Construct the ImageData. 1.373 + nsRefPtr<ImageData> imageData = new ImageData(width, height, 1.374 + dataArray.toObject()); 1.375 + // Wrap it in a JS::Value, protected from a moving GC during ~nsRefPtr. 1.376 + result = imageData->WrapObject(aCx); 1.377 + } 1.378 + return result; 1.379 + } 1.380 + 1.381 + Error(aCx, 0); 1.382 + return nullptr; 1.383 + } 1.384 + 1.385 + static bool 1.386 + Write(JSContext* aCx, JSStructuredCloneWriter* aWriter, 1.387 + JS::Handle<JSObject*> aObj, void* aClosure) 1.388 + { 1.389 + NS_ASSERTION(aClosure, "Null pointer!"); 1.390 + 1.391 + // We'll stash any nsISupports pointers that need to be AddRef'd here. 1.392 + nsTArray<nsCOMPtr<nsISupports> >* clonedObjects = 1.393 + static_cast<nsTArray<nsCOMPtr<nsISupports> >*>(aClosure); 1.394 + 1.395 + // See if this is a File object. 1.396 + { 1.397 + nsIDOMFile* file = file::GetDOMFileFromJSObject(aObj); 1.398 + if (file) { 1.399 + if (JS_WriteUint32Pair(aWriter, DOMWORKER_SCTAG_FILE, 0) && 1.400 + JS_WriteBytes(aWriter, &file, sizeof(file))) { 1.401 + clonedObjects->AppendElement(file); 1.402 + return true; 1.403 + } 1.404 + } 1.405 + } 1.406 + 1.407 + // See if this is a Blob object. 1.408 + { 1.409 + nsIDOMBlob* blob = file::GetDOMBlobFromJSObject(aObj); 1.410 + if (blob) { 1.411 + nsCOMPtr<nsIMutable> mutableBlob = do_QueryInterface(blob); 1.412 + if (mutableBlob && NS_SUCCEEDED(mutableBlob->SetMutable(false)) && 1.413 + JS_WriteUint32Pair(aWriter, DOMWORKER_SCTAG_BLOB, 0) && 1.414 + JS_WriteBytes(aWriter, &blob, sizeof(blob))) { 1.415 + clonedObjects->AppendElement(blob); 1.416 + return true; 1.417 + } 1.418 + } 1.419 + } 1.420 + 1.421 + // See if this is an ImageData object. 1.422 + { 1.423 + ImageData* imageData = nullptr; 1.424 + if (NS_SUCCEEDED(UNWRAP_OBJECT(ImageData, aObj, imageData))) { 1.425 + // Prepare the ImageData internals. 1.426 + uint32_t width = imageData->Width(); 1.427 + uint32_t height = imageData->Height(); 1.428 + JS::Rooted<JSObject*> dataArray(aCx, imageData->GetDataObject()); 1.429 + 1.430 + // Write the internals to the stream. 1.431 + JSAutoCompartment ac(aCx, dataArray); 1.432 + JS::Rooted<JS::Value> arrayValue(aCx, JS::ObjectValue(*dataArray)); 1.433 + return JS_WriteUint32Pair(aWriter, SCTAG_DOM_IMAGEDATA, 0) && 1.434 + JS_WriteUint32Pair(aWriter, width, height) && 1.435 + JS_WriteTypedArray(aWriter, arrayValue); 1.436 + } 1.437 + } 1.438 + 1.439 + Error(aCx, 0); 1.440 + return false; 1.441 + } 1.442 + 1.443 + static void 1.444 + Error(JSContext* aCx, uint32_t /* aErrorId */) 1.445 + { 1.446 + Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR); 1.447 + } 1.448 +}; 1.449 + 1.450 +JSStructuredCloneCallbacks gWorkerStructuredCloneCallbacks = { 1.451 + WorkerStructuredCloneCallbacks::Read, 1.452 + WorkerStructuredCloneCallbacks::Write, 1.453 + WorkerStructuredCloneCallbacks::Error, 1.454 + nullptr, 1.455 + nullptr, 1.456 + nullptr 1.457 +}; 1.458 + 1.459 +struct MainThreadWorkerStructuredCloneCallbacks 1.460 +{ 1.461 + static JSObject* 1.462 + Read(JSContext* aCx, JSStructuredCloneReader* aReader, uint32_t aTag, 1.463 + uint32_t aData, void* aClosure) 1.464 + { 1.465 + AssertIsOnMainThread(); 1.466 + 1.467 + // See if object is a nsIDOMFile pointer. 1.468 + if (aTag == DOMWORKER_SCTAG_FILE) { 1.469 + MOZ_ASSERT(!aData); 1.470 + 1.471 + nsIDOMFile* file; 1.472 + if (JS_ReadBytes(aReader, &file, sizeof(file))) { 1.473 + MOZ_ASSERT(file); 1.474 + 1.475 +#ifdef DEBUG 1.476 + { 1.477 + // File should not be mutable. 1.478 + nsCOMPtr<nsIMutable> mutableFile = do_QueryInterface(file); 1.479 + bool isMutable; 1.480 + NS_ASSERTION(NS_SUCCEEDED(mutableFile->GetMutable(&isMutable)) && 1.481 + !isMutable, 1.482 + "Only immutable file should be passed to worker"); 1.483 + } 1.484 +#endif 1.485 + 1.486 + // nsIDOMFiles should be threadsafe, thus we will use the same instance 1.487 + // on the main thread. 1.488 + JS::Rooted<JS::Value> wrappedFile(aCx); 1.489 + nsresult rv = nsContentUtils::WrapNative(aCx, file, 1.490 + &NS_GET_IID(nsIDOMFile), 1.491 + &wrappedFile); 1.492 + if (NS_FAILED(rv)) { 1.493 + Error(aCx, nsIDOMDOMException::DATA_CLONE_ERR); 1.494 + return nullptr; 1.495 + } 1.496 + 1.497 + return &wrappedFile.toObject(); 1.498 + } 1.499 + } 1.500 + // See if object is a nsIDOMBlob pointer. 1.501 + else if (aTag == DOMWORKER_SCTAG_BLOB) { 1.502 + MOZ_ASSERT(!aData); 1.503 + 1.504 + nsIDOMBlob* blob; 1.505 + if (JS_ReadBytes(aReader, &blob, sizeof(blob))) { 1.506 + MOZ_ASSERT(blob); 1.507 + 1.508 +#ifdef DEBUG 1.509 + { 1.510 + // Blob should not be mutable. 1.511 + nsCOMPtr<nsIMutable> mutableBlob = do_QueryInterface(blob); 1.512 + bool isMutable; 1.513 + NS_ASSERTION(NS_SUCCEEDED(mutableBlob->GetMutable(&isMutable)) && 1.514 + !isMutable, 1.515 + "Only immutable blob should be passed to worker"); 1.516 + } 1.517 +#endif 1.518 + 1.519 + // nsIDOMBlobs should be threadsafe, thus we will use the same instance 1.520 + // on the main thread. 1.521 + JS::Rooted<JS::Value> wrappedBlob(aCx); 1.522 + nsresult rv = nsContentUtils::WrapNative(aCx, blob, 1.523 + &NS_GET_IID(nsIDOMBlob), 1.524 + &wrappedBlob); 1.525 + if (NS_FAILED(rv)) { 1.526 + Error(aCx, nsIDOMDOMException::DATA_CLONE_ERR); 1.527 + return nullptr; 1.528 + } 1.529 + 1.530 + return &wrappedBlob.toObject(); 1.531 + } 1.532 + } 1.533 + 1.534 + JS_ClearPendingException(aCx); 1.535 + return NS_DOMReadStructuredClone(aCx, aReader, aTag, aData, nullptr); 1.536 + } 1.537 + 1.538 + static bool 1.539 + Write(JSContext* aCx, JSStructuredCloneWriter* aWriter, 1.540 + JS::Handle<JSObject*> aObj, void* aClosure) 1.541 + { 1.542 + AssertIsOnMainThread(); 1.543 + 1.544 + NS_ASSERTION(aClosure, "Null pointer!"); 1.545 + 1.546 + // We'll stash any nsISupports pointers that need to be AddRef'd here. 1.547 + nsTArray<nsCOMPtr<nsISupports> >* clonedObjects = 1.548 + static_cast<nsTArray<nsCOMPtr<nsISupports> >*>(aClosure); 1.549 + 1.550 + // See if this is a wrapped native. 1.551 + nsCOMPtr<nsIXPConnectWrappedNative> wrappedNative; 1.552 + nsContentUtils::XPConnect()-> 1.553 + GetWrappedNativeOfJSObject(aCx, aObj, getter_AddRefs(wrappedNative)); 1.554 + 1.555 + if (wrappedNative) { 1.556 + // Get the raw nsISupports out of it. 1.557 + nsISupports* wrappedObject = wrappedNative->Native(); 1.558 + NS_ASSERTION(wrappedObject, "Null pointer?!"); 1.559 + 1.560 + nsISupports* ccISupports = nullptr; 1.561 + wrappedObject->QueryInterface(NS_GET_IID(nsCycleCollectionISupports), 1.562 + reinterpret_cast<void**>(&ccISupports)); 1.563 + if (ccISupports) { 1.564 + NS_WARNING("Cycle collected objects are not supported!"); 1.565 + } 1.566 + else { 1.567 + // See if the wrapped native is a nsIDOMFile. 1.568 + nsCOMPtr<nsIDOMFile> file = do_QueryInterface(wrappedObject); 1.569 + if (file) { 1.570 + nsCOMPtr<nsIMutable> mutableFile = do_QueryInterface(file); 1.571 + if (mutableFile && NS_SUCCEEDED(mutableFile->SetMutable(false))) { 1.572 + nsIDOMFile* filePtr = file; 1.573 + if (JS_WriteUint32Pair(aWriter, DOMWORKER_SCTAG_FILE, 0) && 1.574 + JS_WriteBytes(aWriter, &filePtr, sizeof(filePtr))) { 1.575 + clonedObjects->AppendElement(file); 1.576 + return true; 1.577 + } 1.578 + } 1.579 + } 1.580 + 1.581 + // See if the wrapped native is a nsIDOMBlob. 1.582 + nsCOMPtr<nsIDOMBlob> blob = do_QueryInterface(wrappedObject); 1.583 + if (blob) { 1.584 + nsCOMPtr<nsIMutable> mutableBlob = do_QueryInterface(blob); 1.585 + if (mutableBlob && NS_SUCCEEDED(mutableBlob->SetMutable(false))) { 1.586 + nsIDOMBlob* blobPtr = blob; 1.587 + if (JS_WriteUint32Pair(aWriter, DOMWORKER_SCTAG_BLOB, 0) && 1.588 + JS_WriteBytes(aWriter, &blobPtr, sizeof(blobPtr))) { 1.589 + clonedObjects->AppendElement(blob); 1.590 + return true; 1.591 + } 1.592 + } 1.593 + } 1.594 + } 1.595 + } 1.596 + 1.597 + JS_ClearPendingException(aCx); 1.598 + return NS_DOMWriteStructuredClone(aCx, aWriter, aObj, nullptr); 1.599 + } 1.600 + 1.601 + static void 1.602 + Error(JSContext* aCx, uint32_t aErrorId) 1.603 + { 1.604 + AssertIsOnMainThread(); 1.605 + 1.606 + NS_DOMStructuredCloneError(aCx, aErrorId); 1.607 + } 1.608 +}; 1.609 + 1.610 +JSStructuredCloneCallbacks gMainThreadWorkerStructuredCloneCallbacks = { 1.611 + MainThreadWorkerStructuredCloneCallbacks::Read, 1.612 + MainThreadWorkerStructuredCloneCallbacks::Write, 1.613 + MainThreadWorkerStructuredCloneCallbacks::Error, 1.614 + nullptr, 1.615 + nullptr, 1.616 + nullptr 1.617 +}; 1.618 + 1.619 +struct ChromeWorkerStructuredCloneCallbacks 1.620 +{ 1.621 + static JSObject* 1.622 + Read(JSContext* aCx, JSStructuredCloneReader* aReader, uint32_t aTag, 1.623 + uint32_t aData, void* aClosure) 1.624 + { 1.625 + return WorkerStructuredCloneCallbacks::Read(aCx, aReader, aTag, aData, 1.626 + aClosure); 1.627 + } 1.628 + 1.629 + static bool 1.630 + Write(JSContext* aCx, JSStructuredCloneWriter* aWriter, 1.631 + JS::Handle<JSObject*> aObj, void* aClosure) 1.632 + { 1.633 + return WorkerStructuredCloneCallbacks::Write(aCx, aWriter, aObj, aClosure); 1.634 + } 1.635 + 1.636 + static void 1.637 + Error(JSContext* aCx, uint32_t aErrorId) 1.638 + { 1.639 + return WorkerStructuredCloneCallbacks::Error(aCx, aErrorId); 1.640 + } 1.641 +}; 1.642 + 1.643 +JSStructuredCloneCallbacks gChromeWorkerStructuredCloneCallbacks = { 1.644 + ChromeWorkerStructuredCloneCallbacks::Read, 1.645 + ChromeWorkerStructuredCloneCallbacks::Write, 1.646 + ChromeWorkerStructuredCloneCallbacks::Error, 1.647 + nullptr, 1.648 + nullptr, 1.649 + nullptr 1.650 +}; 1.651 + 1.652 +struct MainThreadChromeWorkerStructuredCloneCallbacks 1.653 +{ 1.654 + static JSObject* 1.655 + Read(JSContext* aCx, JSStructuredCloneReader* aReader, uint32_t aTag, 1.656 + uint32_t aData, void* aClosure) 1.657 + { 1.658 + AssertIsOnMainThread(); 1.659 + 1.660 + JSObject* clone = 1.661 + MainThreadWorkerStructuredCloneCallbacks::Read(aCx, aReader, aTag, aData, 1.662 + aClosure); 1.663 + if (clone) { 1.664 + return clone; 1.665 + } 1.666 + 1.667 + clone = 1.668 + ChromeWorkerStructuredCloneCallbacks::Read(aCx, aReader, aTag, aData, 1.669 + aClosure); 1.670 + if (clone) { 1.671 + return clone; 1.672 + } 1.673 + 1.674 + JS_ClearPendingException(aCx); 1.675 + return NS_DOMReadStructuredClone(aCx, aReader, aTag, aData, nullptr); 1.676 + } 1.677 + 1.678 + static bool 1.679 + Write(JSContext* aCx, JSStructuredCloneWriter* aWriter, 1.680 + JS::Handle<JSObject*> aObj, void* aClosure) 1.681 + { 1.682 + AssertIsOnMainThread(); 1.683 + 1.684 + if (MainThreadWorkerStructuredCloneCallbacks::Write(aCx, aWriter, aObj, 1.685 + aClosure) || 1.686 + ChromeWorkerStructuredCloneCallbacks::Write(aCx, aWriter, aObj, 1.687 + aClosure) || 1.688 + NS_DOMWriteStructuredClone(aCx, aWriter, aObj, nullptr)) { 1.689 + return true; 1.690 + } 1.691 + 1.692 + return false; 1.693 + } 1.694 + 1.695 + static void 1.696 + Error(JSContext* aCx, uint32_t aErrorId) 1.697 + { 1.698 + AssertIsOnMainThread(); 1.699 + 1.700 + NS_DOMStructuredCloneError(aCx, aErrorId); 1.701 + } 1.702 +}; 1.703 + 1.704 +JSStructuredCloneCallbacks gMainThreadChromeWorkerStructuredCloneCallbacks = { 1.705 + MainThreadChromeWorkerStructuredCloneCallbacks::Read, 1.706 + MainThreadChromeWorkerStructuredCloneCallbacks::Write, 1.707 + MainThreadChromeWorkerStructuredCloneCallbacks::Error, 1.708 + nullptr, 1.709 + nullptr, 1.710 + nullptr 1.711 +}; 1.712 + 1.713 +class MainThreadReleaseRunnable MOZ_FINAL : public nsRunnable 1.714 +{ 1.715 + nsTArray<nsCOMPtr<nsISupports>> mDoomed; 1.716 + nsTArray<nsCString> mHostObjectURIs; 1.717 + 1.718 +public: 1.719 + MainThreadReleaseRunnable(nsTArray<nsCOMPtr<nsISupports>>& aDoomed, 1.720 + nsTArray<nsCString>& aHostObjectURIs) 1.721 + { 1.722 + mDoomed.SwapElements(aDoomed); 1.723 + mHostObjectURIs.SwapElements(aHostObjectURIs); 1.724 + } 1.725 + 1.726 + NS_DECL_ISUPPORTS_INHERITED 1.727 + 1.728 + NS_IMETHOD 1.729 + Run() MOZ_OVERRIDE 1.730 + { 1.731 + mDoomed.Clear(); 1.732 + 1.733 + for (uint32_t index = 0; index < mHostObjectURIs.Length(); index++) { 1.734 + nsHostObjectProtocolHandler::RemoveDataEntry(mHostObjectURIs[index]); 1.735 + } 1.736 + 1.737 + return NS_OK; 1.738 + } 1.739 + 1.740 +private: 1.741 + ~MainThreadReleaseRunnable() 1.742 + { } 1.743 +}; 1.744 + 1.745 +class WorkerFinishedRunnable MOZ_FINAL : public WorkerControlRunnable 1.746 +{ 1.747 + WorkerPrivate* mFinishedWorker; 1.748 + 1.749 +public: 1.750 + WorkerFinishedRunnable(WorkerPrivate* aWorkerPrivate, 1.751 + WorkerPrivate* aFinishedWorker) 1.752 + : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount), 1.753 + mFinishedWorker(aFinishedWorker) 1.754 + { } 1.755 + 1.756 +private: 1.757 + virtual bool 1.758 + PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE 1.759 + { 1.760 + // Silence bad assertions. 1.761 + return true; 1.762 + } 1.763 + 1.764 + virtual void 1.765 + PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate, 1.766 + bool aDispatchResult) MOZ_OVERRIDE 1.767 + { 1.768 + // Silence bad assertions. 1.769 + } 1.770 + 1.771 + virtual bool 1.772 + WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE 1.773 + { 1.774 + nsTArray<nsCOMPtr<nsISupports>> doomed; 1.775 + mFinishedWorker->ForgetMainThreadObjects(doomed); 1.776 + 1.777 + nsTArray<nsCString> hostObjectURIs; 1.778 + mFinishedWorker->StealHostObjectURIs(hostObjectURIs); 1.779 + 1.780 + nsRefPtr<MainThreadReleaseRunnable> runnable = 1.781 + new MainThreadReleaseRunnable(doomed, hostObjectURIs); 1.782 + if (NS_FAILED(NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL))) { 1.783 + NS_WARNING("Failed to dispatch, going to leak!"); 1.784 + } 1.785 + 1.786 + RuntimeService* runtime = RuntimeService::GetService(); 1.787 + NS_ASSERTION(runtime, "This should never be null!"); 1.788 + 1.789 + runtime->UnregisterWorker(aCx, mFinishedWorker); 1.790 + 1.791 + mFinishedWorker->ClearSelfRef(); 1.792 + return true; 1.793 + } 1.794 +}; 1.795 + 1.796 +class TopLevelWorkerFinishedRunnable MOZ_FINAL : public nsRunnable 1.797 +{ 1.798 + WorkerPrivate* mFinishedWorker; 1.799 + 1.800 +public: 1.801 + TopLevelWorkerFinishedRunnable(WorkerPrivate* aFinishedWorker) 1.802 + : mFinishedWorker(aFinishedWorker) 1.803 + { 1.804 + aFinishedWorker->AssertIsOnWorkerThread(); 1.805 + } 1.806 + 1.807 + NS_DECL_ISUPPORTS_INHERITED 1.808 + 1.809 +private: 1.810 + NS_IMETHOD 1.811 + Run() MOZ_OVERRIDE 1.812 + { 1.813 + AssertIsOnMainThread(); 1.814 + 1.815 + RuntimeService* runtime = RuntimeService::GetService(); 1.816 + MOZ_ASSERT(runtime); 1.817 + 1.818 + AutoSafeJSContext cx; 1.819 + JSAutoRequest ar(cx); 1.820 + 1.821 + runtime->UnregisterWorker(cx, mFinishedWorker); 1.822 + 1.823 + nsTArray<nsCOMPtr<nsISupports> > doomed; 1.824 + mFinishedWorker->ForgetMainThreadObjects(doomed); 1.825 + 1.826 + nsTArray<nsCString> hostObjectURIs; 1.827 + mFinishedWorker->StealHostObjectURIs(hostObjectURIs); 1.828 + 1.829 + nsRefPtr<MainThreadReleaseRunnable> runnable = 1.830 + new MainThreadReleaseRunnable(doomed, hostObjectURIs); 1.831 + if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) { 1.832 + NS_WARNING("Failed to dispatch, going to leak!"); 1.833 + } 1.834 + 1.835 + mFinishedWorker->ClearSelfRef(); 1.836 + return NS_OK; 1.837 + } 1.838 +}; 1.839 + 1.840 +class ModifyBusyCountRunnable MOZ_FINAL : public WorkerControlRunnable 1.841 +{ 1.842 + bool mIncrease; 1.843 + 1.844 +public: 1.845 + ModifyBusyCountRunnable(WorkerPrivate* aWorkerPrivate, bool aIncrease) 1.846 + : WorkerControlRunnable(aWorkerPrivate, ParentThreadUnchangedBusyCount), 1.847 + mIncrease(aIncrease) 1.848 + { } 1.849 + 1.850 +private: 1.851 + virtual bool 1.852 + WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE 1.853 + { 1.854 + return aWorkerPrivate->ModifyBusyCount(aCx, mIncrease); 1.855 + } 1.856 + 1.857 + virtual void 1.858 + PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate, bool aRunResult) 1.859 + MOZ_OVERRIDE 1.860 + { 1.861 + if (mIncrease) { 1.862 + WorkerControlRunnable::PostRun(aCx, aWorkerPrivate, aRunResult); 1.863 + return; 1.864 + } 1.865 + // Don't do anything here as it's possible that aWorkerPrivate has been 1.866 + // deleted. 1.867 + } 1.868 +}; 1.869 + 1.870 +class CompileScriptRunnable MOZ_FINAL : public WorkerRunnable 1.871 +{ 1.872 +public: 1.873 + CompileScriptRunnable(WorkerPrivate* aWorkerPrivate) 1.874 + : WorkerRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount) 1.875 + { } 1.876 + 1.877 +private: 1.878 + virtual bool 1.879 + WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE 1.880 + { 1.881 + JS::Rooted<JSObject*> global(aCx, 1.882 + aWorkerPrivate->CreateGlobalScope(aCx)); 1.883 + if (!global) { 1.884 + NS_WARNING("Failed to make global!"); 1.885 + return false; 1.886 + } 1.887 + 1.888 + JSAutoCompartment ac(aCx, global); 1.889 + return scriptloader::LoadWorkerScript(aCx); 1.890 + } 1.891 +}; 1.892 + 1.893 +class CloseEventRunnable MOZ_FINAL : public WorkerRunnable 1.894 +{ 1.895 +public: 1.896 + CloseEventRunnable(WorkerPrivate* aWorkerPrivate) 1.897 + : WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount) 1.898 + { } 1.899 + 1.900 +private: 1.901 + virtual bool 1.902 + PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE 1.903 + { 1.904 + MOZ_ASSUME_UNREACHABLE("Don't call Dispatch() on CloseEventRunnable!"); 1.905 + } 1.906 + 1.907 + virtual void 1.908 + PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate, 1.909 + bool aDispatchResult) MOZ_OVERRIDE 1.910 + { 1.911 + MOZ_ASSUME_UNREACHABLE("Don't call Dispatch() on CloseEventRunnable!"); 1.912 + } 1.913 + 1.914 + virtual bool 1.915 + WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE 1.916 + { 1.917 + JS::Rooted<JSObject*> target(aCx, JS::CurrentGlobalOrNull(aCx)); 1.918 + NS_ASSERTION(target, "This must never be null!"); 1.919 + 1.920 + aWorkerPrivate->CloseHandlerStarted(); 1.921 + 1.922 + WorkerGlobalScope* globalScope = aWorkerPrivate->GlobalScope(); 1.923 + 1.924 + nsCOMPtr<nsIDOMEvent> event; 1.925 + nsresult rv = 1.926 + NS_NewDOMEvent(getter_AddRefs(event), globalScope, nullptr, nullptr); 1.927 + if (NS_FAILED(rv)) { 1.928 + Throw(aCx, rv); 1.929 + return false; 1.930 + } 1.931 + 1.932 + rv = event->InitEvent(NS_LITERAL_STRING("close"), false, false); 1.933 + if (NS_FAILED(rv)) { 1.934 + Throw(aCx, rv); 1.935 + return false; 1.936 + } 1.937 + 1.938 + event->SetTrusted(true); 1.939 + 1.940 + globalScope->DispatchDOMEvent(nullptr, event, nullptr, nullptr); 1.941 + 1.942 + return true; 1.943 + } 1.944 + 1.945 + virtual void 1.946 + PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate, bool aRunResult) 1.947 + MOZ_OVERRIDE 1.948 + { 1.949 + // Report errors. 1.950 + WorkerRunnable::PostRun(aCx, aWorkerPrivate, aRunResult); 1.951 + 1.952 + // Match the busy count increase from NotifyRunnable. 1.953 + if (!aWorkerPrivate->ModifyBusyCountFromWorker(aCx, false)) { 1.954 + JS_ReportPendingException(aCx); 1.955 + } 1.956 + 1.957 + aWorkerPrivate->CloseHandlerFinished(); 1.958 + } 1.959 +}; 1.960 + 1.961 +class MessageEventRunnable MOZ_FINAL : public WorkerRunnable 1.962 +{ 1.963 + JSAutoStructuredCloneBuffer mBuffer; 1.964 + nsTArray<nsCOMPtr<nsISupports> > mClonedObjects; 1.965 + uint64_t mMessagePortSerial; 1.966 + bool mToMessagePort; 1.967 + 1.968 +public: 1.969 + MessageEventRunnable(WorkerPrivate* aWorkerPrivate, 1.970 + TargetAndBusyBehavior aBehavior, 1.971 + JSAutoStructuredCloneBuffer&& aData, 1.972 + nsTArray<nsCOMPtr<nsISupports> >& aClonedObjects, 1.973 + bool aToMessagePort, uint64_t aMessagePortSerial) 1.974 + : WorkerRunnable(aWorkerPrivate, aBehavior) 1.975 + , mBuffer(Move(aData)) 1.976 + , mMessagePortSerial(aMessagePortSerial) 1.977 + , mToMessagePort(aToMessagePort) 1.978 + { 1.979 + mClonedObjects.SwapElements(aClonedObjects); 1.980 + } 1.981 + 1.982 + bool 1.983 + DispatchDOMEvent(JSContext* aCx, WorkerPrivate* aWorkerPrivate, 1.984 + DOMEventTargetHelper* aTarget, bool aIsMainThread) 1.985 + { 1.986 + // Release reference to objects that were AddRef'd for 1.987 + // cloning into worker when array goes out of scope. 1.988 + nsTArray<nsCOMPtr<nsISupports>> clonedObjects; 1.989 + clonedObjects.SwapElements(mClonedObjects); 1.990 + 1.991 + JS::Rooted<JS::Value> messageData(aCx); 1.992 + if (!mBuffer.read(aCx, &messageData, 1.993 + workers::WorkerStructuredCloneCallbacks(aIsMainThread))) { 1.994 + xpc::Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR); 1.995 + return false; 1.996 + } 1.997 + 1.998 + nsRefPtr<MessageEvent> event = new MessageEvent(aTarget, nullptr, nullptr); 1.999 + nsresult rv = 1.1000 + event->InitMessageEvent(NS_LITERAL_STRING("message"), 1.1001 + false /* non-bubbling */, 1.1002 + true /* cancelable */, 1.1003 + messageData, 1.1004 + EmptyString(), 1.1005 + EmptyString(), 1.1006 + nullptr); 1.1007 + if (NS_FAILED(rv)) { 1.1008 + xpc::Throw(aCx, rv); 1.1009 + return false; 1.1010 + } 1.1011 + 1.1012 + event->SetTrusted(true); 1.1013 + 1.1014 + nsCOMPtr<nsIDOMEvent> domEvent = do_QueryObject(event); 1.1015 + 1.1016 + nsEventStatus dummy = nsEventStatus_eIgnore; 1.1017 + aTarget->DispatchDOMEvent(nullptr, domEvent, nullptr, &dummy); 1.1018 + return true; 1.1019 + } 1.1020 + 1.1021 +private: 1.1022 + virtual bool 1.1023 + WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE 1.1024 + { 1.1025 + MOZ_ASSERT_IF(mToMessagePort, aWorkerPrivate->IsSharedWorker()); 1.1026 + 1.1027 + if (mBehavior == ParentThreadUnchangedBusyCount) { 1.1028 + // Don't fire this event if the JS object has been disconnected from the 1.1029 + // private object. 1.1030 + if (!aWorkerPrivate->IsAcceptingEvents()) { 1.1031 + return true; 1.1032 + } 1.1033 + 1.1034 + if (mToMessagePort) { 1.1035 + return 1.1036 + aWorkerPrivate->DispatchMessageEventToMessagePort(aCx, 1.1037 + mMessagePortSerial, 1.1038 + Move(mBuffer), 1.1039 + mClonedObjects); 1.1040 + } 1.1041 + 1.1042 + if (aWorkerPrivate->IsSuspended()) { 1.1043 + aWorkerPrivate->QueueRunnable(this); 1.1044 + return true; 1.1045 + } 1.1046 + 1.1047 + aWorkerPrivate->AssertInnerWindowIsCorrect(); 1.1048 + 1.1049 + return DispatchDOMEvent(aCx, aWorkerPrivate, aWorkerPrivate, 1.1050 + !aWorkerPrivate->GetParent()); 1.1051 + } 1.1052 + 1.1053 + MOZ_ASSERT(aWorkerPrivate == GetWorkerPrivateFromContext(aCx)); 1.1054 + 1.1055 + if (mToMessagePort) { 1.1056 + nsRefPtr<workers::MessagePort> port = 1.1057 + aWorkerPrivate->GetMessagePort(mMessagePortSerial); 1.1058 + if (!port) { 1.1059 + // Must have been closed already. 1.1060 + return true; 1.1061 + } 1.1062 + return DispatchDOMEvent(aCx, aWorkerPrivate, port, false); 1.1063 + } 1.1064 + 1.1065 + return DispatchDOMEvent(aCx, aWorkerPrivate, aWorkerPrivate->GlobalScope(), 1.1066 + false); 1.1067 + } 1.1068 +}; 1.1069 + 1.1070 +class NotifyRunnable MOZ_FINAL : public WorkerControlRunnable 1.1071 +{ 1.1072 + Status mStatus; 1.1073 + 1.1074 +public: 1.1075 + NotifyRunnable(WorkerPrivate* aWorkerPrivate, Status aStatus) 1.1076 + : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount), 1.1077 + mStatus(aStatus) 1.1078 + { 1.1079 + MOZ_ASSERT(aStatus == Closing || aStatus == Terminating || 1.1080 + aStatus == Canceling || aStatus == Killing); 1.1081 + } 1.1082 + 1.1083 +private: 1.1084 + virtual bool 1.1085 + PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE 1.1086 + { 1.1087 + // Modify here, but not in PostRun! This busy count addition will be matched 1.1088 + // by the CloseEventRunnable. 1.1089 + return aWorkerPrivate->ModifyBusyCount(aCx, true); 1.1090 + } 1.1091 + 1.1092 + virtual void 1.1093 + PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate, 1.1094 + bool aDispatchResult) MOZ_OVERRIDE 1.1095 + { 1.1096 + if (!aDispatchResult) { 1.1097 + // We couldn't dispatch to the worker, which means it's already dead. 1.1098 + // Undo the busy count modification. 1.1099 + aWorkerPrivate->ModifyBusyCount(aCx, false); 1.1100 + } 1.1101 + } 1.1102 + 1.1103 + virtual bool 1.1104 + WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE 1.1105 + { 1.1106 + return aWorkerPrivate->NotifyInternal(aCx, mStatus); 1.1107 + } 1.1108 +}; 1.1109 + 1.1110 +class CloseRunnable MOZ_FINAL : public WorkerControlRunnable 1.1111 +{ 1.1112 +public: 1.1113 + CloseRunnable(WorkerPrivate* aWorkerPrivate) 1.1114 + : WorkerControlRunnable(aWorkerPrivate, ParentThreadUnchangedBusyCount) 1.1115 + { } 1.1116 + 1.1117 +private: 1.1118 + virtual bool 1.1119 + WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE 1.1120 + { 1.1121 + // This busy count will be matched by the CloseEventRunnable. 1.1122 + return aWorkerPrivate->ModifyBusyCount(aCx, true) && 1.1123 + aWorkerPrivate->Close(aCx); 1.1124 + } 1.1125 +}; 1.1126 + 1.1127 +class SuspendRunnable MOZ_FINAL : public WorkerControlRunnable 1.1128 +{ 1.1129 +public: 1.1130 + SuspendRunnable(WorkerPrivate* aWorkerPrivate) 1.1131 + : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount) 1.1132 + { } 1.1133 + 1.1134 +private: 1.1135 + virtual bool 1.1136 + WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE 1.1137 + { 1.1138 + return aWorkerPrivate->SuspendInternal(aCx); 1.1139 + } 1.1140 +}; 1.1141 + 1.1142 +class ResumeRunnable MOZ_FINAL : public WorkerControlRunnable 1.1143 +{ 1.1144 +public: 1.1145 + ResumeRunnable(WorkerPrivate* aWorkerPrivate) 1.1146 + : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount) 1.1147 + { } 1.1148 + 1.1149 +private: 1.1150 + virtual bool 1.1151 + WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE 1.1152 + { 1.1153 + return aWorkerPrivate->ResumeInternal(aCx); 1.1154 + } 1.1155 +}; 1.1156 + 1.1157 +class ReportErrorRunnable MOZ_FINAL : public WorkerRunnable 1.1158 +{ 1.1159 + nsString mMessage; 1.1160 + nsString mFilename; 1.1161 + nsString mLine; 1.1162 + uint32_t mLineNumber; 1.1163 + uint32_t mColumnNumber; 1.1164 + uint32_t mFlags; 1.1165 + uint32_t mErrorNumber; 1.1166 + 1.1167 +public: 1.1168 + // aWorkerPrivate is the worker thread we're on (or the main thread, if null) 1.1169 + // aTarget is the worker object that we are going to fire an error at 1.1170 + // (if any). 1.1171 + static bool 1.1172 + ReportError(JSContext* aCx, WorkerPrivate* aWorkerPrivate, 1.1173 + bool aFireAtScope, WorkerPrivate* aTarget, 1.1174 + const nsString& aMessage, const nsString& aFilename, 1.1175 + const nsString& aLine, uint32_t aLineNumber, 1.1176 + uint32_t aColumnNumber, uint32_t aFlags, 1.1177 + uint32_t aErrorNumber, uint64_t aInnerWindowId) 1.1178 + { 1.1179 + if (aWorkerPrivate) { 1.1180 + aWorkerPrivate->AssertIsOnWorkerThread(); 1.1181 + } 1.1182 + else { 1.1183 + AssertIsOnMainThread(); 1.1184 + } 1.1185 + 1.1186 + JS::Rooted<JSString*> message(aCx, JS_NewUCStringCopyN(aCx, aMessage.get(), 1.1187 + aMessage.Length())); 1.1188 + if (!message) { 1.1189 + return false; 1.1190 + } 1.1191 + 1.1192 + JS::Rooted<JSString*> filename(aCx, JS_NewUCStringCopyN(aCx, aFilename.get(), 1.1193 + aFilename.Length())); 1.1194 + if (!filename) { 1.1195 + return false; 1.1196 + } 1.1197 + 1.1198 + // We should not fire error events for warnings but instead make sure that 1.1199 + // they show up in the error console. 1.1200 + if (!JSREPORT_IS_WARNING(aFlags)) { 1.1201 + // First fire an ErrorEvent at the worker. 1.1202 + RootedDictionary<ErrorEventInit> init(aCx); 1.1203 + init.mMessage = aMessage; 1.1204 + init.mFilename = aFilename; 1.1205 + init.mLineno = aLineNumber; 1.1206 + init.mCancelable = true; 1.1207 + init.mBubbles = true; 1.1208 + 1.1209 + if (aTarget) { 1.1210 + nsRefPtr<ErrorEvent> event = 1.1211 + ErrorEvent::Constructor(aTarget, NS_LITERAL_STRING("error"), init); 1.1212 + event->SetTrusted(true); 1.1213 + 1.1214 + nsEventStatus status = nsEventStatus_eIgnore; 1.1215 + aTarget->DispatchDOMEvent(nullptr, event, nullptr, &status); 1.1216 + 1.1217 + if (status == nsEventStatus_eConsumeNoDefault) { 1.1218 + return true; 1.1219 + } 1.1220 + } 1.1221 + 1.1222 + // Now fire an event at the global object, but don't do that if the error 1.1223 + // code is too much recursion and this is the same script threw the error. 1.1224 + if (aFireAtScope && (aTarget || aErrorNumber != JSMSG_OVER_RECURSED)) { 1.1225 + JS::Rooted<JSObject*> target(aCx, JS::CurrentGlobalOrNull(aCx)); 1.1226 + NS_ASSERTION(target, "This should never be null!"); 1.1227 + 1.1228 + nsEventStatus status = nsEventStatus_eIgnore; 1.1229 + nsIScriptGlobalObject* sgo; 1.1230 + 1.1231 + if (aWorkerPrivate) { 1.1232 + WorkerGlobalScope* globalTarget = aWorkerPrivate->GlobalScope(); 1.1233 + MOZ_ASSERT(target == globalTarget->GetWrapperPreserveColor()); 1.1234 + 1.1235 + nsRefPtr<ErrorEvent> event = 1.1236 + ErrorEvent::Constructor(aTarget, NS_LITERAL_STRING("error"), init); 1.1237 + event->SetTrusted(true); 1.1238 + 1.1239 + nsIDOMEventTarget* target = static_cast<nsIDOMEventTarget*>(globalTarget); 1.1240 + if (NS_FAILED(EventDispatcher::DispatchDOMEvent(target, nullptr, 1.1241 + event, nullptr, 1.1242 + &status))) { 1.1243 + NS_WARNING("Failed to dispatch worker thread error event!"); 1.1244 + status = nsEventStatus_eIgnore; 1.1245 + } 1.1246 + } 1.1247 + else if ((sgo = nsJSUtils::GetStaticScriptGlobal(target))) { 1.1248 + MOZ_ASSERT(NS_IsMainThread()); 1.1249 + 1.1250 + if (NS_FAILED(sgo->HandleScriptError(init, &status))) { 1.1251 + NS_WARNING("Failed to dispatch main thread error event!"); 1.1252 + status = nsEventStatus_eIgnore; 1.1253 + } 1.1254 + } 1.1255 + 1.1256 + // Was preventDefault() called? 1.1257 + if (status == nsEventStatus_eConsumeNoDefault) { 1.1258 + return true; 1.1259 + } 1.1260 + } 1.1261 + } 1.1262 + 1.1263 + // Now fire a runnable to do the same on the parent's thread if we can. 1.1264 + if (aWorkerPrivate) { 1.1265 + nsRefPtr<ReportErrorRunnable> runnable = 1.1266 + new ReportErrorRunnable(aWorkerPrivate, aMessage, aFilename, aLine, 1.1267 + aLineNumber, aColumnNumber, aFlags, 1.1268 + aErrorNumber); 1.1269 + return runnable->Dispatch(aCx); 1.1270 + } 1.1271 + 1.1272 + // Otherwise log an error to the error console. 1.1273 + LogErrorToConsole(aMessage, aFilename, aLine, aLineNumber, aColumnNumber, 1.1274 + aFlags, aInnerWindowId); 1.1275 + return true; 1.1276 + } 1.1277 + 1.1278 +private: 1.1279 + ReportErrorRunnable(WorkerPrivate* aWorkerPrivate, const nsString& aMessage, 1.1280 + const nsString& aFilename, const nsString& aLine, 1.1281 + uint32_t aLineNumber, uint32_t aColumnNumber, 1.1282 + uint32_t aFlags, uint32_t aErrorNumber) 1.1283 + : WorkerRunnable(aWorkerPrivate, ParentThreadUnchangedBusyCount), 1.1284 + mMessage(aMessage), mFilename(aFilename), mLine(aLine), 1.1285 + mLineNumber(aLineNumber), mColumnNumber(aColumnNumber), mFlags(aFlags), 1.1286 + mErrorNumber(aErrorNumber) 1.1287 + { } 1.1288 + 1.1289 + virtual void 1.1290 + PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate, 1.1291 + bool aDispatchResult) MOZ_OVERRIDE 1.1292 + { 1.1293 + aWorkerPrivate->AssertIsOnWorkerThread(); 1.1294 + 1.1295 + // Dispatch may fail if the worker was canceled, no need to report that as 1.1296 + // an error, so don't call base class PostDispatch. 1.1297 + } 1.1298 + 1.1299 + virtual bool 1.1300 + WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE 1.1301 + { 1.1302 + // Don't fire this event if the JS object has been disconnected from the 1.1303 + // private object. 1.1304 + if (!aWorkerPrivate->IsAcceptingEvents()) { 1.1305 + return true; 1.1306 + } 1.1307 + 1.1308 + JS::Rooted<JSObject*> target(aCx, aWorkerPrivate->GetWrapper()); 1.1309 + 1.1310 + uint64_t innerWindowId; 1.1311 + bool fireAtScope = true; 1.1312 + 1.1313 + WorkerPrivate* parent = aWorkerPrivate->GetParent(); 1.1314 + if (parent) { 1.1315 + innerWindowId = 0; 1.1316 + } 1.1317 + else { 1.1318 + AssertIsOnMainThread(); 1.1319 + 1.1320 + if (aWorkerPrivate->IsSuspended()) { 1.1321 + aWorkerPrivate->QueueRunnable(this); 1.1322 + return true; 1.1323 + } 1.1324 + 1.1325 + if (aWorkerPrivate->IsSharedWorker()) { 1.1326 + aWorkerPrivate->BroadcastErrorToSharedWorkers(aCx, mMessage, mFilename, 1.1327 + mLine, mLineNumber, 1.1328 + mColumnNumber, mFlags); 1.1329 + return true; 1.1330 + } 1.1331 + 1.1332 + aWorkerPrivate->AssertInnerWindowIsCorrect(); 1.1333 + 1.1334 + innerWindowId = aWorkerPrivate->GetInnerWindowId(); 1.1335 + } 1.1336 + 1.1337 + return ReportError(aCx, parent, fireAtScope, aWorkerPrivate, mMessage, 1.1338 + mFilename, mLine, mLineNumber, mColumnNumber, mFlags, 1.1339 + mErrorNumber, innerWindowId); 1.1340 + } 1.1341 +}; 1.1342 + 1.1343 +class TimerRunnable MOZ_FINAL : public WorkerRunnable 1.1344 +{ 1.1345 +public: 1.1346 + TimerRunnable(WorkerPrivate* aWorkerPrivate) 1.1347 + : WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount) 1.1348 + { } 1.1349 + 1.1350 +private: 1.1351 + virtual bool 1.1352 + PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE 1.1353 + { 1.1354 + // Silence bad assertions. 1.1355 + return true; 1.1356 + } 1.1357 + 1.1358 + virtual void 1.1359 + PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate, 1.1360 + bool aDispatchResult) MOZ_OVERRIDE 1.1361 + { 1.1362 + // Silence bad assertions. 1.1363 + } 1.1364 + 1.1365 + virtual bool 1.1366 + WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE 1.1367 + { 1.1368 + return aWorkerPrivate->RunExpiredTimeouts(aCx); 1.1369 + } 1.1370 +}; 1.1371 + 1.1372 +void 1.1373 +DummyCallback(nsITimer* aTimer, void* aClosure) 1.1374 +{ 1.1375 + // Nothing! 1.1376 +} 1.1377 + 1.1378 +class TimerThreadEventTarget MOZ_FINAL : public nsIEventTarget 1.1379 +{ 1.1380 + WorkerPrivate* mWorkerPrivate; 1.1381 + nsRefPtr<WorkerRunnable> mWorkerRunnable; 1.1382 + 1.1383 +public: 1.1384 + TimerThreadEventTarget(WorkerPrivate* aWorkerPrivate, 1.1385 + WorkerRunnable* aWorkerRunnable) 1.1386 + : mWorkerPrivate(aWorkerPrivate), mWorkerRunnable(aWorkerRunnable) 1.1387 + { 1.1388 + MOZ_ASSERT(aWorkerPrivate); 1.1389 + MOZ_ASSERT(aWorkerRunnable); 1.1390 + } 1.1391 + 1.1392 + NS_DECL_THREADSAFE_ISUPPORTS 1.1393 + 1.1394 +protected: 1.1395 + NS_IMETHOD 1.1396 + Dispatch(nsIRunnable* aRunnable, uint32_t aFlags) MOZ_OVERRIDE 1.1397 + { 1.1398 + // This should only happen on the timer thread. 1.1399 + MOZ_ASSERT(!NS_IsMainThread()); 1.1400 + MOZ_ASSERT(aFlags == nsIEventTarget::DISPATCH_NORMAL); 1.1401 + 1.1402 + nsRefPtr<TimerThreadEventTarget> kungFuDeathGrip = this; 1.1403 + 1.1404 + // Run the runnable we're given now (should just call DummyCallback()), 1.1405 + // otherwise the timer thread will leak it... If we run this after 1.1406 + // dispatch running the event can race against resetting the timer. 1.1407 + aRunnable->Run(); 1.1408 + 1.1409 + // This can fail if we're racing to terminate or cancel, should be handled 1.1410 + // by the terminate or cancel code. 1.1411 + mWorkerRunnable->Dispatch(nullptr); 1.1412 + 1.1413 + return NS_OK; 1.1414 + } 1.1415 + 1.1416 + NS_IMETHOD 1.1417 + IsOnCurrentThread(bool* aIsOnCurrentThread) MOZ_OVERRIDE 1.1418 + { 1.1419 + MOZ_ASSERT(aIsOnCurrentThread); 1.1420 + 1.1421 + nsresult rv = mWorkerPrivate->IsOnCurrentThread(aIsOnCurrentThread); 1.1422 + if (NS_WARN_IF(NS_FAILED(rv))) { 1.1423 + return rv; 1.1424 + } 1.1425 + 1.1426 + return NS_OK; 1.1427 + } 1.1428 +}; 1.1429 + 1.1430 +class KillCloseEventRunnable MOZ_FINAL : public WorkerRunnable 1.1431 +{ 1.1432 + nsCOMPtr<nsITimer> mTimer; 1.1433 + 1.1434 + class KillScriptRunnable MOZ_FINAL : public WorkerControlRunnable 1.1435 + { 1.1436 + public: 1.1437 + KillScriptRunnable(WorkerPrivate* aWorkerPrivate) 1.1438 + : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount) 1.1439 + { } 1.1440 + 1.1441 + private: 1.1442 + virtual bool 1.1443 + PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE 1.1444 + { 1.1445 + // Silence bad assertions, this is dispatched from the timer thread. 1.1446 + return true; 1.1447 + } 1.1448 + 1.1449 + virtual void 1.1450 + PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate, 1.1451 + bool aDispatchResult) MOZ_OVERRIDE 1.1452 + { 1.1453 + // Silence bad assertions, this is dispatched from the timer thread. 1.1454 + } 1.1455 + 1.1456 + virtual bool 1.1457 + WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE 1.1458 + { 1.1459 + // Kill running script. 1.1460 + return false; 1.1461 + } 1.1462 + }; 1.1463 + 1.1464 +public: 1.1465 + KillCloseEventRunnable(WorkerPrivate* aWorkerPrivate) 1.1466 + : WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount) 1.1467 + { } 1.1468 + 1.1469 + bool 1.1470 + SetTimeout(JSContext* aCx, uint32_t aDelayMS) 1.1471 + { 1.1472 + nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID); 1.1473 + if (!timer) { 1.1474 + JS_ReportError(aCx, "Failed to create timer!"); 1.1475 + return false; 1.1476 + } 1.1477 + 1.1478 + nsRefPtr<KillScriptRunnable> runnable = 1.1479 + new KillScriptRunnable(mWorkerPrivate); 1.1480 + 1.1481 + nsRefPtr<TimerThreadEventTarget> target = 1.1482 + new TimerThreadEventTarget(mWorkerPrivate, runnable); 1.1483 + 1.1484 + if (NS_FAILED(timer->SetTarget(target))) { 1.1485 + JS_ReportError(aCx, "Failed to set timer's target!"); 1.1486 + return false; 1.1487 + } 1.1488 + 1.1489 + if (NS_FAILED(timer->InitWithFuncCallback(DummyCallback, nullptr, aDelayMS, 1.1490 + nsITimer::TYPE_ONE_SHOT))) { 1.1491 + JS_ReportError(aCx, "Failed to start timer!"); 1.1492 + return false; 1.1493 + } 1.1494 + 1.1495 + mTimer.swap(timer); 1.1496 + return true; 1.1497 + } 1.1498 + 1.1499 +private: 1.1500 + ~KillCloseEventRunnable() 1.1501 + { 1.1502 + if (mTimer) { 1.1503 + mTimer->Cancel(); 1.1504 + } 1.1505 + } 1.1506 + 1.1507 + virtual bool 1.1508 + PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE 1.1509 + { 1.1510 + MOZ_ASSUME_UNREACHABLE("Don't call Dispatch() on KillCloseEventRunnable!"); 1.1511 + } 1.1512 + 1.1513 + virtual void 1.1514 + PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate, 1.1515 + bool aDispatchResult) MOZ_OVERRIDE 1.1516 + { 1.1517 + MOZ_ASSUME_UNREACHABLE("Don't call Dispatch() on KillCloseEventRunnable!"); 1.1518 + } 1.1519 + 1.1520 + virtual bool 1.1521 + WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE 1.1522 + { 1.1523 + if (mTimer) { 1.1524 + mTimer->Cancel(); 1.1525 + mTimer = nullptr; 1.1526 + } 1.1527 + 1.1528 + return true; 1.1529 + } 1.1530 +}; 1.1531 + 1.1532 +class UpdateRuntimeAndContextOptionsRunnable MOZ_FINAL : public WorkerControlRunnable 1.1533 +{ 1.1534 + JS::RuntimeOptions mRuntimeOptions; 1.1535 + JS::ContextOptions mContentCxOptions; 1.1536 + JS::ContextOptions mChromeCxOptions; 1.1537 + 1.1538 +public: 1.1539 + UpdateRuntimeAndContextOptionsRunnable( 1.1540 + WorkerPrivate* aWorkerPrivate, 1.1541 + const JS::RuntimeOptions& aRuntimeOptions, 1.1542 + const JS::ContextOptions& aContentCxOptions, 1.1543 + const JS::ContextOptions& aChromeCxOptions) 1.1544 + : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount), 1.1545 + mRuntimeOptions(aRuntimeOptions), 1.1546 + mContentCxOptions(aContentCxOptions), 1.1547 + mChromeCxOptions(aChromeCxOptions) 1.1548 + { } 1.1549 + 1.1550 +private: 1.1551 + virtual bool 1.1552 + WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE 1.1553 + { 1.1554 + aWorkerPrivate->UpdateRuntimeAndContextOptionsInternal(aCx, 1.1555 + mRuntimeOptions, 1.1556 + mContentCxOptions, 1.1557 + mChromeCxOptions); 1.1558 + return true; 1.1559 + } 1.1560 +}; 1.1561 + 1.1562 +class UpdatePreferenceRunnable MOZ_FINAL : public WorkerControlRunnable 1.1563 +{ 1.1564 + WorkerPreference mPref; 1.1565 + bool mValue; 1.1566 + 1.1567 +public: 1.1568 + UpdatePreferenceRunnable(WorkerPrivate* aWorkerPrivate, 1.1569 + WorkerPreference aPref, 1.1570 + bool aValue) 1.1571 + : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount), 1.1572 + mPref(aPref), 1.1573 + mValue(aValue) 1.1574 + { } 1.1575 + 1.1576 + virtual bool 1.1577 + WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE 1.1578 + { 1.1579 + aWorkerPrivate->UpdatePreferenceInternal(aCx, mPref, mValue); 1.1580 + return true; 1.1581 + } 1.1582 +}; 1.1583 + 1.1584 +class UpdateJSWorkerMemoryParameterRunnable MOZ_FINAL : 1.1585 + public WorkerControlRunnable 1.1586 +{ 1.1587 + uint32_t mValue; 1.1588 + JSGCParamKey mKey; 1.1589 + 1.1590 +public: 1.1591 + UpdateJSWorkerMemoryParameterRunnable(WorkerPrivate* aWorkerPrivate, 1.1592 + JSGCParamKey aKey, 1.1593 + uint32_t aValue) 1.1594 + : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount), 1.1595 + mValue(aValue), mKey(aKey) 1.1596 + { } 1.1597 + 1.1598 +private: 1.1599 + virtual bool 1.1600 + WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE 1.1601 + { 1.1602 + aWorkerPrivate->UpdateJSWorkerMemoryParameterInternal(aCx, mKey, mValue); 1.1603 + return true; 1.1604 + } 1.1605 +}; 1.1606 + 1.1607 +#ifdef JS_GC_ZEAL 1.1608 +class UpdateGCZealRunnable MOZ_FINAL : public WorkerControlRunnable 1.1609 +{ 1.1610 + uint8_t mGCZeal; 1.1611 + uint32_t mFrequency; 1.1612 + 1.1613 +public: 1.1614 + UpdateGCZealRunnable(WorkerPrivate* aWorkerPrivate, 1.1615 + uint8_t aGCZeal, 1.1616 + uint32_t aFrequency) 1.1617 + : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount), 1.1618 + mGCZeal(aGCZeal), mFrequency(aFrequency) 1.1619 + { } 1.1620 + 1.1621 +private: 1.1622 + virtual bool 1.1623 + WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE 1.1624 + { 1.1625 + aWorkerPrivate->UpdateGCZealInternal(aCx, mGCZeal, mFrequency); 1.1626 + return true; 1.1627 + } 1.1628 +}; 1.1629 +#endif 1.1630 + 1.1631 +class GarbageCollectRunnable MOZ_FINAL : public WorkerControlRunnable 1.1632 +{ 1.1633 + bool mShrinking; 1.1634 + bool mCollectChildren; 1.1635 + 1.1636 +public: 1.1637 + GarbageCollectRunnable(WorkerPrivate* aWorkerPrivate, bool aShrinking, 1.1638 + bool aCollectChildren) 1.1639 + : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount), 1.1640 + mShrinking(aShrinking), mCollectChildren(aCollectChildren) 1.1641 + { } 1.1642 + 1.1643 +private: 1.1644 + virtual bool 1.1645 + PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE 1.1646 + { 1.1647 + // Silence bad assertions, this can be dispatched from either the main 1.1648 + // thread or the timer thread.. 1.1649 + return true; 1.1650 + } 1.1651 + 1.1652 + virtual void 1.1653 + PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate, 1.1654 + bool aDispatchResult) MOZ_OVERRIDE 1.1655 + { 1.1656 + // Silence bad assertions, this can be dispatched from either the main 1.1657 + // thread or the timer thread.. 1.1658 + } 1.1659 + 1.1660 + virtual bool 1.1661 + WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE 1.1662 + { 1.1663 + aWorkerPrivate->GarbageCollectInternal(aCx, mShrinking, mCollectChildren); 1.1664 + return true; 1.1665 + } 1.1666 +}; 1.1667 + 1.1668 +class CycleCollectRunnable : public WorkerControlRunnable 1.1669 +{ 1.1670 + bool mCollectChildren; 1.1671 + 1.1672 +public: 1.1673 + CycleCollectRunnable(WorkerPrivate* aWorkerPrivate, bool aCollectChildren) 1.1674 + : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount), 1.1675 + mCollectChildren(aCollectChildren) 1.1676 + { } 1.1677 + 1.1678 + bool 1.1679 + WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) 1.1680 + { 1.1681 + aWorkerPrivate->CycleCollectInternal(aCx, mCollectChildren); 1.1682 + return true; 1.1683 + } 1.1684 +}; 1.1685 + 1.1686 +class OfflineStatusChangeRunnable : public WorkerRunnable 1.1687 +{ 1.1688 +public: 1.1689 + OfflineStatusChangeRunnable(WorkerPrivate* aWorkerPrivate, bool aIsOffline) 1.1690 + : WorkerRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount), 1.1691 + mIsOffline(aIsOffline) 1.1692 + { 1.1693 + } 1.1694 + 1.1695 + bool 1.1696 + WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) 1.1697 + { 1.1698 + aWorkerPrivate->OfflineStatusChangeEventInternal(aCx, mIsOffline); 1.1699 + return true; 1.1700 + } 1.1701 + 1.1702 +private: 1.1703 + bool mIsOffline; 1.1704 +}; 1.1705 + 1.1706 +class WorkerJSRuntimeStats : public JS::RuntimeStats 1.1707 +{ 1.1708 + const nsACString& mRtPath; 1.1709 + 1.1710 +public: 1.1711 + WorkerJSRuntimeStats(const nsACString& aRtPath) 1.1712 + : JS::RuntimeStats(JsWorkerMallocSizeOf), mRtPath(aRtPath) 1.1713 + { } 1.1714 + 1.1715 + ~WorkerJSRuntimeStats() 1.1716 + { 1.1717 + for (size_t i = 0; i != zoneStatsVector.length(); i++) { 1.1718 + delete static_cast<xpc::ZoneStatsExtras*>(zoneStatsVector[i].extra); 1.1719 + } 1.1720 + 1.1721 + for (size_t i = 0; i != compartmentStatsVector.length(); i++) { 1.1722 + delete static_cast<xpc::CompartmentStatsExtras*>(compartmentStatsVector[i].extra); 1.1723 + } 1.1724 + } 1.1725 + 1.1726 + virtual void 1.1727 + initExtraZoneStats(JS::Zone* aZone, 1.1728 + JS::ZoneStats* aZoneStats) 1.1729 + MOZ_OVERRIDE 1.1730 + { 1.1731 + MOZ_ASSERT(!aZoneStats->extra); 1.1732 + 1.1733 + // ReportJSRuntimeExplicitTreeStats expects that 1.1734 + // aZoneStats->extra is a xpc::ZoneStatsExtras pointer. 1.1735 + xpc::ZoneStatsExtras* extras = new xpc::ZoneStatsExtras; 1.1736 + extras->pathPrefix = mRtPath; 1.1737 + extras->pathPrefix += nsPrintfCString("zone(0x%p)/", (void *)aZone); 1.1738 + aZoneStats->extra = extras; 1.1739 + } 1.1740 + 1.1741 + virtual void 1.1742 + initExtraCompartmentStats(JSCompartment* aCompartment, 1.1743 + JS::CompartmentStats* aCompartmentStats) 1.1744 + MOZ_OVERRIDE 1.1745 + { 1.1746 + MOZ_ASSERT(!aCompartmentStats->extra); 1.1747 + 1.1748 + // ReportJSRuntimeExplicitTreeStats expects that 1.1749 + // aCompartmentStats->extra is a xpc::CompartmentStatsExtras pointer. 1.1750 + xpc::CompartmentStatsExtras* extras = new xpc::CompartmentStatsExtras; 1.1751 + 1.1752 + // This is the |jsPathPrefix|. Each worker has exactly two compartments: 1.1753 + // one for atoms, and one for everything else. 1.1754 + extras->jsPathPrefix.Assign(mRtPath); 1.1755 + extras->jsPathPrefix += nsPrintfCString("zone(0x%p)/", 1.1756 + (void *)js::GetCompartmentZone(aCompartment)); 1.1757 + extras->jsPathPrefix += js::IsAtomsCompartment(aCompartment) 1.1758 + ? NS_LITERAL_CSTRING("compartment(web-worker-atoms)/") 1.1759 + : NS_LITERAL_CSTRING("compartment(web-worker)/"); 1.1760 + 1.1761 + // This should never be used when reporting with workers (hence the "?!"). 1.1762 + extras->domPathPrefix.AssignLiteral("explicit/workers/?!/"); 1.1763 + 1.1764 + aCompartmentStats->extra = extras; 1.1765 + } 1.1766 +}; 1.1767 + 1.1768 +class MessagePortRunnable MOZ_FINAL : public WorkerRunnable 1.1769 +{ 1.1770 + uint64_t mMessagePortSerial; 1.1771 + bool mConnect; 1.1772 + 1.1773 +public: 1.1774 + MessagePortRunnable(WorkerPrivate* aWorkerPrivate, 1.1775 + uint64_t aMessagePortSerial, 1.1776 + bool aConnect) 1.1777 + : WorkerRunnable(aWorkerPrivate, aConnect ? 1.1778 + WorkerThreadModifyBusyCount : 1.1779 + WorkerThreadUnchangedBusyCount), 1.1780 + mMessagePortSerial(aMessagePortSerial), mConnect(aConnect) 1.1781 + { } 1.1782 + 1.1783 +private: 1.1784 + ~MessagePortRunnable() 1.1785 + { } 1.1786 + 1.1787 + virtual bool 1.1788 + WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE 1.1789 + { 1.1790 + if (mConnect) { 1.1791 + return aWorkerPrivate->ConnectMessagePort(aCx, mMessagePortSerial); 1.1792 + } 1.1793 + 1.1794 + aWorkerPrivate->DisconnectMessagePort(mMessagePortSerial); 1.1795 + return true; 1.1796 + } 1.1797 +}; 1.1798 + 1.1799 +#ifdef DEBUG 1.1800 + 1.1801 +PRThread* 1.1802 +PRThreadFromThread(nsIThread* aThread) 1.1803 +{ 1.1804 + MOZ_ASSERT(aThread); 1.1805 + 1.1806 + PRThread* result; 1.1807 + MOZ_ASSERT(NS_SUCCEEDED(aThread->GetPRThread(&result))); 1.1808 + MOZ_ASSERT(result); 1.1809 + 1.1810 + return result; 1.1811 +} 1.1812 + 1.1813 +#endif // DEBUG 1.1814 + 1.1815 +} /* anonymous namespace */ 1.1816 + 1.1817 +NS_IMPL_ISUPPORTS_INHERITED0(MainThreadReleaseRunnable, nsRunnable) 1.1818 + 1.1819 +NS_IMPL_ISUPPORTS_INHERITED0(TopLevelWorkerFinishedRunnable, nsRunnable) 1.1820 + 1.1821 +NS_IMPL_ISUPPORTS(TimerThreadEventTarget, nsIEventTarget) 1.1822 + 1.1823 +template <class Derived> 1.1824 +class WorkerPrivateParent<Derived>::SynchronizeAndResumeRunnable MOZ_FINAL 1.1825 + : public nsRunnable 1.1826 +{ 1.1827 + friend class nsRevocableEventPtr<SynchronizeAndResumeRunnable>; 1.1828 + 1.1829 + WorkerPrivate* mWorkerPrivate; 1.1830 + nsCOMPtr<nsPIDOMWindow> mWindow; 1.1831 + nsCOMPtr<nsIScriptContext> mScriptContext; 1.1832 + 1.1833 +public: 1.1834 + SynchronizeAndResumeRunnable(WorkerPrivate* aWorkerPrivate, 1.1835 + nsPIDOMWindow* aWindow, 1.1836 + nsIScriptContext* aScriptContext) 1.1837 + : mWorkerPrivate(aWorkerPrivate), mWindow(aWindow), 1.1838 + mScriptContext(aScriptContext) 1.1839 + { 1.1840 + AssertIsOnMainThread(); 1.1841 + MOZ_ASSERT(aWorkerPrivate); 1.1842 + MOZ_ASSERT(aWindow); 1.1843 + MOZ_ASSERT(!aWorkerPrivate->GetParent()); 1.1844 + } 1.1845 + 1.1846 +private: 1.1847 + ~SynchronizeAndResumeRunnable() 1.1848 + { } 1.1849 + 1.1850 + NS_IMETHOD 1.1851 + Run() MOZ_OVERRIDE 1.1852 + { 1.1853 + AssertIsOnMainThread(); 1.1854 + 1.1855 + if (mWorkerPrivate) { 1.1856 + AutoPushJSContext cx(mScriptContext ? 1.1857 + mScriptContext->GetNativeContext() : 1.1858 + nsContentUtils::GetSafeJSContext()); 1.1859 + 1.1860 + if (!mWorkerPrivate->Resume(cx, mWindow)) { 1.1861 + JS_ReportPendingException(cx); 1.1862 + } 1.1863 + } 1.1864 + 1.1865 + return NS_OK; 1.1866 + } 1.1867 + 1.1868 + void 1.1869 + Revoke() 1.1870 + { 1.1871 + AssertIsOnMainThread(); 1.1872 + MOZ_ASSERT(mWorkerPrivate); 1.1873 + MOZ_ASSERT(mWindow); 1.1874 + 1.1875 + mWorkerPrivate = nullptr; 1.1876 + mWindow = nullptr; 1.1877 + mScriptContext = nullptr; 1.1878 + } 1.1879 +}; 1.1880 + 1.1881 +template <class Derived> 1.1882 +class WorkerPrivateParent<Derived>::EventTarget MOZ_FINAL 1.1883 + : public nsIEventTarget 1.1884 +{ 1.1885 + // This mutex protects mWorkerPrivate and must be acquired *before* the 1.1886 + // WorkerPrivate's mutex whenever they must both be held. 1.1887 + mozilla::Mutex mMutex; 1.1888 + WorkerPrivate* mWorkerPrivate; 1.1889 + nsIEventTarget* mWeakNestedEventTarget; 1.1890 + nsCOMPtr<nsIEventTarget> mNestedEventTarget; 1.1891 + 1.1892 +public: 1.1893 + EventTarget(WorkerPrivate* aWorkerPrivate) 1.1894 + : mMutex("WorkerPrivateParent::EventTarget::mMutex"), 1.1895 + mWorkerPrivate(aWorkerPrivate), mWeakNestedEventTarget(nullptr) 1.1896 + { 1.1897 + MOZ_ASSERT(aWorkerPrivate); 1.1898 + } 1.1899 + 1.1900 + EventTarget(WorkerPrivate* aWorkerPrivate, nsIEventTarget* aNestedEventTarget) 1.1901 + : mMutex("WorkerPrivateParent::EventTarget::mMutex"), 1.1902 + mWorkerPrivate(aWorkerPrivate), mWeakNestedEventTarget(aNestedEventTarget), 1.1903 + mNestedEventTarget(aNestedEventTarget) 1.1904 + { 1.1905 + MOZ_ASSERT(aWorkerPrivate); 1.1906 + MOZ_ASSERT(aNestedEventTarget); 1.1907 + } 1.1908 + 1.1909 + void 1.1910 + Disable() 1.1911 + { 1.1912 + nsCOMPtr<nsIEventTarget> nestedEventTarget; 1.1913 + { 1.1914 + MutexAutoLock lock(mMutex); 1.1915 + 1.1916 + MOZ_ASSERT(mWorkerPrivate); 1.1917 + mWorkerPrivate = nullptr; 1.1918 + mNestedEventTarget.swap(nestedEventTarget); 1.1919 + } 1.1920 + } 1.1921 + 1.1922 + nsIEventTarget* 1.1923 + GetWeakNestedEventTarget() const 1.1924 + { 1.1925 + MOZ_ASSERT(mWeakNestedEventTarget); 1.1926 + return mWeakNestedEventTarget; 1.1927 + } 1.1928 + 1.1929 + NS_DECL_THREADSAFE_ISUPPORTS 1.1930 + NS_DECL_NSIEVENTTARGET 1.1931 + 1.1932 +private: 1.1933 + ~EventTarget() 1.1934 + { } 1.1935 +}; 1.1936 + 1.1937 +struct WorkerPrivate::TimeoutInfo 1.1938 +{ 1.1939 + TimeoutInfo() 1.1940 + : mTimeoutCallable(JS::UndefinedValue()), mLineNumber(0), mId(0), 1.1941 + mIsInterval(false), mCanceled(false) 1.1942 + { 1.1943 + MOZ_COUNT_CTOR(mozilla::dom::workers::WorkerPrivate::TimeoutInfo); 1.1944 + } 1.1945 + 1.1946 + ~TimeoutInfo() 1.1947 + { 1.1948 + MOZ_COUNT_DTOR(mozilla::dom::workers::WorkerPrivate::TimeoutInfo); 1.1949 + } 1.1950 + 1.1951 + bool operator==(const TimeoutInfo& aOther) 1.1952 + { 1.1953 + return mTargetTime == aOther.mTargetTime; 1.1954 + } 1.1955 + 1.1956 + bool operator<(const TimeoutInfo& aOther) 1.1957 + { 1.1958 + return mTargetTime < aOther.mTargetTime; 1.1959 + } 1.1960 + 1.1961 + JS::Heap<JS::Value> mTimeoutCallable; 1.1962 + nsString mTimeoutString; 1.1963 + nsTArray<JS::Heap<JS::Value> > mExtraArgVals; 1.1964 + mozilla::TimeStamp mTargetTime; 1.1965 + mozilla::TimeDuration mInterval; 1.1966 + nsCString mFilename; 1.1967 + uint32_t mLineNumber; 1.1968 + int32_t mId; 1.1969 + bool mIsInterval; 1.1970 + bool mCanceled; 1.1971 +}; 1.1972 + 1.1973 +class WorkerPrivate::MemoryReporter MOZ_FINAL : public nsIMemoryReporter 1.1974 +{ 1.1975 + NS_DECL_THREADSAFE_ISUPPORTS 1.1976 + 1.1977 + friend class WorkerPrivate; 1.1978 + 1.1979 + SharedMutex mMutex; 1.1980 + WorkerPrivate* mWorkerPrivate; 1.1981 + nsCString mRtPath; 1.1982 + bool mAlreadyMappedToAddon; 1.1983 + 1.1984 +public: 1.1985 + MemoryReporter(WorkerPrivate* aWorkerPrivate) 1.1986 + : mMutex(aWorkerPrivate->mMutex), mWorkerPrivate(aWorkerPrivate), 1.1987 + mAlreadyMappedToAddon(false) 1.1988 + { 1.1989 + aWorkerPrivate->AssertIsOnWorkerThread(); 1.1990 + 1.1991 + nsCString escapedDomain(aWorkerPrivate->Domain()); 1.1992 + escapedDomain.ReplaceChar('/', '\\'); 1.1993 + 1.1994 + NS_ConvertUTF16toUTF8 escapedURL(aWorkerPrivate->ScriptURL()); 1.1995 + escapedURL.ReplaceChar('/', '\\'); 1.1996 + 1.1997 + nsAutoCString addressString; 1.1998 + addressString.AppendPrintf("0x%p", static_cast<void*>(aWorkerPrivate)); 1.1999 + 1.2000 + mRtPath = NS_LITERAL_CSTRING("explicit/workers/workers(") + 1.2001 + escapedDomain + NS_LITERAL_CSTRING(")/worker(") + 1.2002 + escapedURL + NS_LITERAL_CSTRING(", ") + addressString + 1.2003 + NS_LITERAL_CSTRING(")/"); 1.2004 + } 1.2005 + 1.2006 + NS_IMETHOD 1.2007 + CollectReports(nsIMemoryReporterCallback* aCallback, 1.2008 + nsISupports* aClosure) 1.2009 + { 1.2010 + AssertIsOnMainThread(); 1.2011 + 1.2012 + // Assumes that WorkerJSRuntimeStats will hold a reference to mRtPath, 1.2013 + // and not a copy, as TryToMapAddon() may later modify the string again. 1.2014 + WorkerJSRuntimeStats rtStats(mRtPath); 1.2015 + 1.2016 + { 1.2017 + MutexAutoLock lock(mMutex); 1.2018 + 1.2019 + TryToMapAddon(); 1.2020 + 1.2021 + if (!mWorkerPrivate || 1.2022 + !mWorkerPrivate->BlockAndCollectRuntimeStats(&rtStats)) { 1.2023 + // Returning NS_OK here will effectively report 0 memory. 1.2024 + return NS_OK; 1.2025 + } 1.2026 + } 1.2027 + 1.2028 + return xpc::ReportJSRuntimeExplicitTreeStats(rtStats, mRtPath, 1.2029 + aCallback, aClosure); 1.2030 + } 1.2031 + 1.2032 +private: 1.2033 + ~MemoryReporter() 1.2034 + { } 1.2035 + 1.2036 + void 1.2037 + Disable() 1.2038 + { 1.2039 + // Called from WorkerPrivate::DisableMemoryReporter. 1.2040 + mMutex.AssertCurrentThreadOwns(); 1.2041 + 1.2042 + NS_ASSERTION(mWorkerPrivate, "Disabled more than once!"); 1.2043 + mWorkerPrivate = nullptr; 1.2044 + } 1.2045 + 1.2046 + // Only call this from the main thread and under mMutex lock. 1.2047 + void 1.2048 + TryToMapAddon() 1.2049 + { 1.2050 + AssertIsOnMainThread(); 1.2051 + mMutex.AssertCurrentThreadOwns(); 1.2052 + 1.2053 + if (mAlreadyMappedToAddon || !mWorkerPrivate) { 1.2054 + return; 1.2055 + } 1.2056 + 1.2057 + nsCOMPtr<nsIURI> scriptURI; 1.2058 + if (NS_FAILED(NS_NewURI(getter_AddRefs(scriptURI), 1.2059 + mWorkerPrivate->ScriptURL()))) { 1.2060 + return; 1.2061 + } 1.2062 + 1.2063 + mAlreadyMappedToAddon = true; 1.2064 + 1.2065 + if (XRE_GetProcessType() != GeckoProcessType_Default) { 1.2066 + // Only try to access the service from the main process. 1.2067 + return; 1.2068 + } 1.2069 + 1.2070 + nsAutoCString addonId; 1.2071 + bool ok; 1.2072 + nsCOMPtr<amIAddonManager> addonManager = 1.2073 + do_GetService("@mozilla.org/addons/integration;1"); 1.2074 + 1.2075 + if (!addonManager || 1.2076 + NS_FAILED(addonManager->MapURIToAddonID(scriptURI, addonId, &ok)) || 1.2077 + !ok) { 1.2078 + return; 1.2079 + } 1.2080 + 1.2081 + static const size_t explicitLength = strlen("explicit/"); 1.2082 + addonId.Insert(NS_LITERAL_CSTRING("add-ons/"), 0); 1.2083 + addonId += "/"; 1.2084 + mRtPath.Insert(addonId, explicitLength); 1.2085 + } 1.2086 +}; 1.2087 + 1.2088 +NS_IMPL_ISUPPORTS(WorkerPrivate::MemoryReporter, nsIMemoryReporter) 1.2089 + 1.2090 +WorkerPrivate::SyncLoopInfo::SyncLoopInfo(EventTarget* aEventTarget) 1.2091 +: mEventTarget(aEventTarget), mCompleted(false), mResult(false) 1.2092 +#ifdef DEBUG 1.2093 + , mHasRun(false) 1.2094 +#endif 1.2095 +{ 1.2096 +} 1.2097 + 1.2098 +// Can't use NS_IMPL_CYCLE_COLLECTION_CLASS(WorkerPrivateParent) because of the 1.2099 +// templates. 1.2100 +template <class Derived> 1.2101 +typename WorkerPrivateParent<Derived>::cycleCollection 1.2102 + WorkerPrivateParent<Derived>::_cycleCollectorGlobal = 1.2103 + WorkerPrivateParent<Derived>::cycleCollection(); 1.2104 + 1.2105 +template <class Derived> 1.2106 +WorkerPrivateParent<Derived>::WorkerPrivateParent( 1.2107 + JSContext* aCx, 1.2108 + WorkerPrivate* aParent, 1.2109 + const nsAString& aScriptURL, 1.2110 + bool aIsChromeWorker, 1.2111 + WorkerType aWorkerType, 1.2112 + const nsACString& aSharedWorkerName, 1.2113 + LoadInfo& aLoadInfo) 1.2114 +: mMutex("WorkerPrivateParent Mutex"), 1.2115 + mCondVar(mMutex, "WorkerPrivateParent CondVar"), 1.2116 + mMemoryReportCondVar(mMutex, "WorkerPrivateParent Memory Report CondVar"), 1.2117 + mParent(aParent), mScriptURL(aScriptURL), 1.2118 + mSharedWorkerName(aSharedWorkerName), mBusyCount(0), mMessagePortSerial(0), 1.2119 + mParentStatus(Pending), mParentSuspended(false), 1.2120 + mIsChromeWorker(aIsChromeWorker), mMainThreadObjectsForgotten(false), 1.2121 + mWorkerType(aWorkerType), 1.2122 + mCreationTimeStamp(TimeStamp::Now()) 1.2123 +{ 1.2124 + SetIsDOMBinding(); 1.2125 + 1.2126 + MOZ_ASSERT_IF(IsSharedWorker(), !aSharedWorkerName.IsVoid() && 1.2127 + NS_IsMainThread()); 1.2128 + MOZ_ASSERT_IF(!IsSharedWorker(), aSharedWorkerName.IsEmpty()); 1.2129 + 1.2130 + if (aLoadInfo.mWindow) { 1.2131 + AssertIsOnMainThread(); 1.2132 + MOZ_ASSERT(aLoadInfo.mWindow->IsInnerWindow(), 1.2133 + "Should have inner window here!"); 1.2134 + BindToOwner(aLoadInfo.mWindow); 1.2135 + } 1.2136 + 1.2137 + mLoadInfo.StealFrom(aLoadInfo); 1.2138 + 1.2139 + if (aParent) { 1.2140 + aParent->AssertIsOnWorkerThread(); 1.2141 + 1.2142 + aParent->CopyJSSettings(mJSSettings); 1.2143 + } 1.2144 + else { 1.2145 + AssertIsOnMainThread(); 1.2146 + 1.2147 + RuntimeService::GetDefaultJSSettings(mJSSettings); 1.2148 + } 1.2149 +} 1.2150 + 1.2151 +template <class Derived> 1.2152 +WorkerPrivateParent<Derived>::~WorkerPrivateParent() 1.2153 +{ 1.2154 + DropJSObjects(this); 1.2155 +} 1.2156 + 1.2157 +template <class Derived> 1.2158 +JSObject* 1.2159 +WorkerPrivateParent<Derived>::WrapObject(JSContext* aCx) 1.2160 +{ 1.2161 + MOZ_ASSERT(!IsSharedWorker(), 1.2162 + "We should never wrap a WorkerPrivate for a SharedWorker"); 1.2163 + 1.2164 + AssertIsOnParentThread(); 1.2165 + 1.2166 + // XXXkhuey this should not need to be rooted, the analysis is dumb. 1.2167 + // See bug 980181. 1.2168 + JS::Rooted<JSObject*> wrapper(aCx, 1.2169 + WorkerBinding::Wrap(aCx, ParentAsWorkerPrivate())); 1.2170 + if (wrapper) { 1.2171 + MOZ_ALWAYS_TRUE(TryPreserveWrapper(wrapper)); 1.2172 + } 1.2173 + 1.2174 + return wrapper; 1.2175 +} 1.2176 + 1.2177 +template <class Derived> 1.2178 +nsresult 1.2179 +WorkerPrivateParent<Derived>::DispatchPrivate(WorkerRunnable* aRunnable, 1.2180 + nsIEventTarget* aSyncLoopTarget) 1.2181 +{ 1.2182 + // May be called on any thread! 1.2183 + 1.2184 + WorkerPrivate* self = ParentAsWorkerPrivate(); 1.2185 + 1.2186 + { 1.2187 + MutexAutoLock lock(mMutex); 1.2188 + 1.2189 + MOZ_ASSERT_IF(aSyncLoopTarget, self->mThread); 1.2190 + 1.2191 + if (!self->mThread) { 1.2192 + if (ParentStatus() == Pending || self->mStatus == Pending) { 1.2193 + mPreStartRunnables.AppendElement(aRunnable); 1.2194 + return NS_OK; 1.2195 + } 1.2196 + 1.2197 + NS_WARNING("Using a worker event target after the thread has already" 1.2198 + "been released!"); 1.2199 + return NS_ERROR_UNEXPECTED; 1.2200 + } 1.2201 + 1.2202 + if (self->mStatus == Dead || 1.2203 + (!aSyncLoopTarget && ParentStatus() > Running)) { 1.2204 + NS_WARNING("A runnable was posted to a worker that is already shutting " 1.2205 + "down!"); 1.2206 + return NS_ERROR_UNEXPECTED; 1.2207 + } 1.2208 + 1.2209 + nsCOMPtr<nsIEventTarget> target; 1.2210 + if (aSyncLoopTarget) { 1.2211 + target = aSyncLoopTarget; 1.2212 + } 1.2213 + else { 1.2214 + target = self->mThread; 1.2215 + } 1.2216 + 1.2217 + nsresult rv = target->Dispatch(aRunnable, NS_DISPATCH_NORMAL); 1.2218 + if (NS_WARN_IF(NS_FAILED(rv))) { 1.2219 + return rv; 1.2220 + } 1.2221 + 1.2222 + mCondVar.Notify(); 1.2223 + } 1.2224 + 1.2225 + return NS_OK; 1.2226 +} 1.2227 + 1.2228 +template <class Derived> 1.2229 +nsresult 1.2230 +WorkerPrivateParent<Derived>::DispatchControlRunnable( 1.2231 + WorkerControlRunnable* aWorkerControlRunnable) 1.2232 +{ 1.2233 + // May be called on any thread! 1.2234 + 1.2235 + MOZ_ASSERT(aWorkerControlRunnable); 1.2236 + 1.2237 + nsRefPtr<WorkerControlRunnable> runnable = aWorkerControlRunnable; 1.2238 + 1.2239 + WorkerPrivate* self = ParentAsWorkerPrivate(); 1.2240 + 1.2241 + { 1.2242 + MutexAutoLock lock(mMutex); 1.2243 + 1.2244 + if (self->mStatus == Dead) { 1.2245 + NS_WARNING("A control runnable was posted to a worker that is already " 1.2246 + "shutting down!"); 1.2247 + return NS_ERROR_UNEXPECTED; 1.2248 + } 1.2249 + 1.2250 + // Transfer ownership to the control queue. 1.2251 + self->mControlQueue.Push(runnable.forget().take()); 1.2252 + 1.2253 + if (JSContext* cx = self->mJSContext) { 1.2254 + MOZ_ASSERT(self->mThread); 1.2255 + 1.2256 + JSRuntime* rt = JS_GetRuntime(cx); 1.2257 + MOZ_ASSERT(rt); 1.2258 + 1.2259 + JS_RequestInterruptCallback(rt); 1.2260 + } 1.2261 + 1.2262 + mCondVar.Notify(); 1.2263 + } 1.2264 + 1.2265 + return NS_OK; 1.2266 +} 1.2267 + 1.2268 +template <class Derived> 1.2269 +already_AddRefed<WorkerRunnable> 1.2270 +WorkerPrivateParent<Derived>::MaybeWrapAsWorkerRunnable(nsIRunnable* aRunnable) 1.2271 +{ 1.2272 + // May be called on any thread! 1.2273 + 1.2274 + MOZ_ASSERT(aRunnable); 1.2275 + 1.2276 + nsRefPtr<WorkerRunnable> workerRunnable = 1.2277 + WorkerRunnable::FromRunnable(aRunnable); 1.2278 + if (workerRunnable) { 1.2279 + return workerRunnable.forget(); 1.2280 + } 1.2281 + 1.2282 + nsCOMPtr<nsICancelableRunnable> cancelable = do_QueryInterface(aRunnable); 1.2283 + if (!cancelable) { 1.2284 + MOZ_CRASH("All runnables destined for a worker thread must be cancelable!"); 1.2285 + } 1.2286 + 1.2287 + workerRunnable = 1.2288 + new ExternalRunnableWrapper(ParentAsWorkerPrivate(), cancelable); 1.2289 + return workerRunnable.forget(); 1.2290 +} 1.2291 + 1.2292 +template <class Derived> 1.2293 +already_AddRefed<nsIEventTarget> 1.2294 +WorkerPrivateParent<Derived>::GetEventTarget() 1.2295 +{ 1.2296 + WorkerPrivate* self = ParentAsWorkerPrivate(); 1.2297 + 1.2298 + nsCOMPtr<nsIEventTarget> target; 1.2299 + 1.2300 + { 1.2301 + MutexAutoLock lock(mMutex); 1.2302 + 1.2303 + if (!mEventTarget && 1.2304 + ParentStatus() <= Running && 1.2305 + self->mStatus <= Running) { 1.2306 + mEventTarget = new EventTarget(self); 1.2307 + } 1.2308 + 1.2309 + target = mEventTarget; 1.2310 + } 1.2311 + 1.2312 + NS_WARN_IF_FALSE(target, 1.2313 + "Requested event target for a worker that is already " 1.2314 + "shutting down!"); 1.2315 + 1.2316 + return target.forget(); 1.2317 +} 1.2318 + 1.2319 +template <class Derived> 1.2320 +bool 1.2321 +WorkerPrivateParent<Derived>::Start() 1.2322 +{ 1.2323 + // May be called on any thread! 1.2324 + { 1.2325 + MutexAutoLock lock(mMutex); 1.2326 + 1.2327 + NS_ASSERTION(mParentStatus != Running, "How can this be?!"); 1.2328 + 1.2329 + if (mParentStatus == Pending) { 1.2330 + mParentStatus = Running; 1.2331 + return true; 1.2332 + } 1.2333 + } 1.2334 + 1.2335 + return false; 1.2336 +} 1.2337 + 1.2338 +// aCx is null when called from the finalizer 1.2339 +template <class Derived> 1.2340 +bool 1.2341 +WorkerPrivateParent<Derived>::NotifyPrivate(JSContext* aCx, Status aStatus) 1.2342 +{ 1.2343 + AssertIsOnParentThread(); 1.2344 + 1.2345 + bool pending; 1.2346 + { 1.2347 + MutexAutoLock lock(mMutex); 1.2348 + 1.2349 + if (mParentStatus >= aStatus) { 1.2350 + return true; 1.2351 + } 1.2352 + 1.2353 + pending = mParentStatus == Pending; 1.2354 + mParentStatus = aStatus; 1.2355 + } 1.2356 + 1.2357 + if (IsSharedWorker()) { 1.2358 + RuntimeService* runtime = RuntimeService::GetService(); 1.2359 + MOZ_ASSERT(runtime); 1.2360 + 1.2361 + runtime->ForgetSharedWorker(ParentAsWorkerPrivate()); 1.2362 + } 1.2363 + 1.2364 + if (pending) { 1.2365 + WorkerPrivate* self = ParentAsWorkerPrivate(); 1.2366 + 1.2367 +#ifdef DEBUG 1.2368 + { 1.2369 + // Fake a thread here just so that our assertions don't go off for no 1.2370 + // reason. 1.2371 + nsIThread* currentThread = NS_GetCurrentThread(); 1.2372 + MOZ_ASSERT(currentThread); 1.2373 + 1.2374 + MOZ_ASSERT(!self->mPRThread); 1.2375 + self->mPRThread = PRThreadFromThread(currentThread); 1.2376 + MOZ_ASSERT(self->mPRThread); 1.2377 + } 1.2378 +#endif 1.2379 + 1.2380 + // Worker never got a chance to run, go ahead and delete it. 1.2381 + self->ScheduleDeletion(WorkerPrivate::WorkerNeverRan); 1.2382 + return true; 1.2383 + } 1.2384 + 1.2385 + // Only top-level workers should have a synchronize runnable. 1.2386 + MOZ_ASSERT_IF(mSynchronizeRunnable.get(), !GetParent()); 1.2387 + mSynchronizeRunnable.Revoke(); 1.2388 + 1.2389 + NS_ASSERTION(aStatus != Terminating || mQueuedRunnables.IsEmpty(), 1.2390 + "Shouldn't have anything queued!"); 1.2391 + 1.2392 + // Anything queued will be discarded. 1.2393 + mQueuedRunnables.Clear(); 1.2394 + 1.2395 + nsRefPtr<NotifyRunnable> runnable = 1.2396 + new NotifyRunnable(ParentAsWorkerPrivate(), aStatus); 1.2397 + return runnable->Dispatch(aCx); 1.2398 +} 1.2399 + 1.2400 +template <class Derived> 1.2401 +bool 1.2402 +WorkerPrivateParent<Derived>::Suspend(JSContext* aCx, nsPIDOMWindow* aWindow) 1.2403 +{ 1.2404 + AssertIsOnParentThread(); 1.2405 + MOZ_ASSERT(aCx); 1.2406 + 1.2407 + // Shared workers are only suspended if all of their owning documents are 1.2408 + // suspended. 1.2409 + if (IsSharedWorker()) { 1.2410 + AssertIsOnMainThread(); 1.2411 + MOZ_ASSERT(mSharedWorkers.Count()); 1.2412 + 1.2413 + struct Closure 1.2414 + { 1.2415 + nsPIDOMWindow* mWindow; 1.2416 + bool mAllSuspended; 1.2417 + 1.2418 + Closure(nsPIDOMWindow* aWindow) 1.2419 + : mWindow(aWindow), mAllSuspended(true) 1.2420 + { 1.2421 + AssertIsOnMainThread(); 1.2422 + // aWindow may be null here. 1.2423 + } 1.2424 + 1.2425 + static PLDHashOperator 1.2426 + Suspend(const uint64_t& aKey, 1.2427 + SharedWorker* aSharedWorker, 1.2428 + void* aClosure) 1.2429 + { 1.2430 + AssertIsOnMainThread(); 1.2431 + MOZ_ASSERT(aSharedWorker); 1.2432 + MOZ_ASSERT(aClosure); 1.2433 + 1.2434 + auto closure = static_cast<Closure*>(aClosure); 1.2435 + 1.2436 + if (closure->mWindow && aSharedWorker->GetOwner() == closure->mWindow) { 1.2437 + // Calling Suspend() may change the refcount, ensure that the worker 1.2438 + // outlives this call. 1.2439 + nsRefPtr<SharedWorker> kungFuDeathGrip = aSharedWorker; 1.2440 + 1.2441 + aSharedWorker->Suspend(); 1.2442 + } else { 1.2443 + MOZ_ASSERT_IF(aSharedWorker->GetOwner() && closure->mWindow, 1.2444 + !SameCOMIdentity(aSharedWorker->GetOwner(), 1.2445 + closure->mWindow)); 1.2446 + if (!aSharedWorker->IsSuspended()) { 1.2447 + closure->mAllSuspended = false; 1.2448 + } 1.2449 + } 1.2450 + return PL_DHASH_NEXT; 1.2451 + } 1.2452 + }; 1.2453 + 1.2454 + Closure closure(aWindow); 1.2455 + 1.2456 + mSharedWorkers.EnumerateRead(Closure::Suspend, &closure); 1.2457 + 1.2458 + if (!closure.mAllSuspended || mParentSuspended) { 1.2459 + return true; 1.2460 + } 1.2461 + } 1.2462 + 1.2463 +// MOZ_ASSERT(!mParentSuspended, "Suspended more than once!"); 1.2464 + 1.2465 + mParentSuspended = true; 1.2466 + 1.2467 + { 1.2468 + MutexAutoLock lock(mMutex); 1.2469 + 1.2470 + if (mParentStatus >= Terminating) { 1.2471 + return true; 1.2472 + } 1.2473 + } 1.2474 + 1.2475 + nsRefPtr<SuspendRunnable> runnable = 1.2476 + new SuspendRunnable(ParentAsWorkerPrivate()); 1.2477 + if (!runnable->Dispatch(aCx)) { 1.2478 + return false; 1.2479 + } 1.2480 + 1.2481 + return true; 1.2482 +} 1.2483 + 1.2484 +template <class Derived> 1.2485 +bool 1.2486 +WorkerPrivateParent<Derived>::Resume(JSContext* aCx, nsPIDOMWindow* aWindow) 1.2487 +{ 1.2488 + AssertIsOnParentThread(); 1.2489 + MOZ_ASSERT(aCx); 1.2490 + MOZ_ASSERT_IF(!IsSharedWorker(), mParentSuspended); 1.2491 + 1.2492 + // Shared workers are resumed if any of their owning documents are resumed. 1.2493 + if (IsSharedWorker()) { 1.2494 + AssertIsOnMainThread(); 1.2495 + MOZ_ASSERT(mSharedWorkers.Count()); 1.2496 + 1.2497 + struct Closure 1.2498 + { 1.2499 + nsPIDOMWindow* mWindow; 1.2500 + bool mAnyRunning; 1.2501 + 1.2502 + Closure(nsPIDOMWindow* aWindow) 1.2503 + : mWindow(aWindow), mAnyRunning(false) 1.2504 + { 1.2505 + AssertIsOnMainThread(); 1.2506 + // aWindow may be null here. 1.2507 + } 1.2508 + 1.2509 + static PLDHashOperator 1.2510 + Resume(const uint64_t& aKey, 1.2511 + SharedWorker* aSharedWorker, 1.2512 + void* aClosure) 1.2513 + { 1.2514 + AssertIsOnMainThread(); 1.2515 + MOZ_ASSERT(aSharedWorker); 1.2516 + MOZ_ASSERT(aClosure); 1.2517 + 1.2518 + auto closure = static_cast<Closure*>(aClosure); 1.2519 + 1.2520 + if (closure->mWindow && aSharedWorker->GetOwner() == closure->mWindow) { 1.2521 + // Calling Resume() may change the refcount, ensure that the worker 1.2522 + // outlives this call. 1.2523 + nsRefPtr<SharedWorker> kungFuDeathGrip = aSharedWorker; 1.2524 + 1.2525 + aSharedWorker->Resume(); 1.2526 + closure->mAnyRunning = true; 1.2527 + } else { 1.2528 + MOZ_ASSERT_IF(aSharedWorker->GetOwner() && closure->mWindow, 1.2529 + !SameCOMIdentity(aSharedWorker->GetOwner(), 1.2530 + closure->mWindow)); 1.2531 + if (!aSharedWorker->IsSuspended()) { 1.2532 + closure->mAnyRunning = true; 1.2533 + } 1.2534 + } 1.2535 + return PL_DHASH_NEXT; 1.2536 + } 1.2537 + }; 1.2538 + 1.2539 + Closure closure(aWindow); 1.2540 + 1.2541 + mSharedWorkers.EnumerateRead(Closure::Resume, &closure); 1.2542 + 1.2543 + if (!closure.mAnyRunning || !mParentSuspended) { 1.2544 + return true; 1.2545 + } 1.2546 + } 1.2547 + 1.2548 + MOZ_ASSERT(mParentSuspended); 1.2549 + 1.2550 + mParentSuspended = false; 1.2551 + 1.2552 + { 1.2553 + MutexAutoLock lock(mMutex); 1.2554 + 1.2555 + if (mParentStatus >= Terminating) { 1.2556 + return true; 1.2557 + } 1.2558 + } 1.2559 + 1.2560 + // Only top-level workers should have a synchronize runnable. 1.2561 + MOZ_ASSERT_IF(mSynchronizeRunnable.get(), !GetParent()); 1.2562 + mSynchronizeRunnable.Revoke(); 1.2563 + 1.2564 + // Execute queued runnables before waking up the worker, otherwise the worker 1.2565 + // could post new messages before we run those that have been queued. 1.2566 + if (!mQueuedRunnables.IsEmpty()) { 1.2567 + AssertIsOnMainThread(); 1.2568 + MOZ_ASSERT(IsDedicatedWorker()); 1.2569 + 1.2570 + nsTArray<nsCOMPtr<nsIRunnable>> runnables; 1.2571 + mQueuedRunnables.SwapElements(runnables); 1.2572 + 1.2573 + for (uint32_t index = 0; index < runnables.Length(); index++) { 1.2574 + runnables[index]->Run(); 1.2575 + } 1.2576 + } 1.2577 + 1.2578 + nsRefPtr<ResumeRunnable> runnable = 1.2579 + new ResumeRunnable(ParentAsWorkerPrivate()); 1.2580 + if (!runnable->Dispatch(aCx)) { 1.2581 + return false; 1.2582 + } 1.2583 + 1.2584 + return true; 1.2585 +} 1.2586 + 1.2587 +template <class Derived> 1.2588 +bool 1.2589 +WorkerPrivateParent<Derived>::SynchronizeAndResume( 1.2590 + JSContext* aCx, 1.2591 + nsPIDOMWindow* aWindow, 1.2592 + nsIScriptContext* aScriptContext) 1.2593 +{ 1.2594 + AssertIsOnMainThread(); 1.2595 + MOZ_ASSERT(!GetParent()); 1.2596 + MOZ_ASSERT_IF(IsDedicatedWorker(), mParentSuspended); 1.2597 + 1.2598 + // NB: There may be pending unqueued messages. If we resume here we will 1.2599 + // execute those messages out of order. Instead we post an event to the 1.2600 + // end of the event queue, allowing all of the outstanding messages to be 1.2601 + // queued up in order on the worker. Then and only then we execute all of 1.2602 + // the messages. 1.2603 + 1.2604 + nsRefPtr<SynchronizeAndResumeRunnable> runnable = 1.2605 + new SynchronizeAndResumeRunnable(ParentAsWorkerPrivate(), aWindow, 1.2606 + aScriptContext); 1.2607 + if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) { 1.2608 + JS_ReportError(aCx, "Failed to dispatch to current thread!"); 1.2609 + return false; 1.2610 + } 1.2611 + 1.2612 + mSynchronizeRunnable = runnable; 1.2613 + return true; 1.2614 +} 1.2615 + 1.2616 +template <class Derived> 1.2617 +bool 1.2618 +WorkerPrivateParent<Derived>::Close(JSContext* aCx) 1.2619 +{ 1.2620 + AssertIsOnParentThread(); 1.2621 + 1.2622 + { 1.2623 + MutexAutoLock lock(mMutex); 1.2624 + 1.2625 + if (mParentStatus < Closing) { 1.2626 + mParentStatus = Closing; 1.2627 + } 1.2628 + } 1.2629 + 1.2630 + return true; 1.2631 +} 1.2632 + 1.2633 +template <class Derived> 1.2634 +bool 1.2635 +WorkerPrivateParent<Derived>::ModifyBusyCount(JSContext* aCx, bool aIncrease) 1.2636 +{ 1.2637 + AssertIsOnParentThread(); 1.2638 + 1.2639 + NS_ASSERTION(aIncrease || mBusyCount, "Mismatched busy count mods!"); 1.2640 + 1.2641 + if (aIncrease) { 1.2642 + mBusyCount++; 1.2643 + return true; 1.2644 + } 1.2645 + 1.2646 + if (--mBusyCount == 0) { 1.2647 + 1.2648 + bool shouldCancel; 1.2649 + { 1.2650 + MutexAutoLock lock(mMutex); 1.2651 + shouldCancel = mParentStatus == Terminating; 1.2652 + } 1.2653 + 1.2654 + if (shouldCancel && !Cancel(aCx)) { 1.2655 + return false; 1.2656 + } 1.2657 + } 1.2658 + 1.2659 + return true; 1.2660 +} 1.2661 + 1.2662 +template <class Derived> 1.2663 +void 1.2664 +WorkerPrivateParent<Derived>::ForgetMainThreadObjects( 1.2665 + nsTArray<nsCOMPtr<nsISupports> >& aDoomed) 1.2666 +{ 1.2667 + AssertIsOnParentThread(); 1.2668 + MOZ_ASSERT(!mMainThreadObjectsForgotten); 1.2669 + 1.2670 + static const uint32_t kDoomedCount = 7; 1.2671 + 1.2672 + aDoomed.SetCapacity(kDoomedCount); 1.2673 + 1.2674 + SwapToISupportsArray(mLoadInfo.mWindow, aDoomed); 1.2675 + SwapToISupportsArray(mLoadInfo.mScriptContext, aDoomed); 1.2676 + SwapToISupportsArray(mLoadInfo.mBaseURI, aDoomed); 1.2677 + SwapToISupportsArray(mLoadInfo.mResolvedScriptURI, aDoomed); 1.2678 + SwapToISupportsArray(mLoadInfo.mPrincipal, aDoomed); 1.2679 + SwapToISupportsArray(mLoadInfo.mChannel, aDoomed); 1.2680 + SwapToISupportsArray(mLoadInfo.mCSP, aDoomed); 1.2681 + // Before adding anything here update kDoomedCount above! 1.2682 + 1.2683 + MOZ_ASSERT(aDoomed.Length() == kDoomedCount); 1.2684 + 1.2685 + mMainThreadObjectsForgotten = true; 1.2686 +} 1.2687 + 1.2688 +template <class Derived> 1.2689 +void 1.2690 +WorkerPrivateParent<Derived>::PostMessageInternal( 1.2691 + JSContext* aCx, 1.2692 + JS::Handle<JS::Value> aMessage, 1.2693 + const Optional<Sequence<JS::Value> >& aTransferable, 1.2694 + bool aToMessagePort, 1.2695 + uint64_t aMessagePortSerial, 1.2696 + ErrorResult& aRv) 1.2697 +{ 1.2698 + AssertIsOnParentThread(); 1.2699 + 1.2700 + { 1.2701 + MutexAutoLock lock(mMutex); 1.2702 + if (mParentStatus > Running) { 1.2703 + return; 1.2704 + } 1.2705 + } 1.2706 + 1.2707 + JSStructuredCloneCallbacks* callbacks; 1.2708 + if (GetParent()) { 1.2709 + if (IsChromeWorker()) { 1.2710 + callbacks = &gChromeWorkerStructuredCloneCallbacks; 1.2711 + } 1.2712 + else { 1.2713 + callbacks = &gWorkerStructuredCloneCallbacks; 1.2714 + } 1.2715 + } 1.2716 + else { 1.2717 + AssertIsOnMainThread(); 1.2718 + 1.2719 + if (IsChromeWorker()) { 1.2720 + callbacks = &gMainThreadChromeWorkerStructuredCloneCallbacks; 1.2721 + } 1.2722 + else { 1.2723 + callbacks = &gMainThreadWorkerStructuredCloneCallbacks; 1.2724 + } 1.2725 + } 1.2726 + 1.2727 + JS::Rooted<JS::Value> transferable(aCx, JS::UndefinedValue()); 1.2728 + if (aTransferable.WasPassed()) { 1.2729 + const Sequence<JS::Value>& realTransferable = aTransferable.Value(); 1.2730 + 1.2731 + // The input sequence only comes from the generated bindings code, which 1.2732 + // ensures it is rooted. 1.2733 + JS::HandleValueArray elements = 1.2734 + JS::HandleValueArray::fromMarkedLocation(realTransferable.Length(), 1.2735 + realTransferable.Elements()); 1.2736 + 1.2737 + JSObject* array = 1.2738 + JS_NewArrayObject(aCx, elements); 1.2739 + if (!array) { 1.2740 + aRv.Throw(NS_ERROR_OUT_OF_MEMORY); 1.2741 + return; 1.2742 + } 1.2743 + transferable.setObject(*array); 1.2744 + } 1.2745 + 1.2746 + nsTArray<nsCOMPtr<nsISupports> > clonedObjects; 1.2747 + 1.2748 + JSAutoStructuredCloneBuffer buffer; 1.2749 + if (!buffer.write(aCx, aMessage, transferable, callbacks, &clonedObjects)) { 1.2750 + aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR); 1.2751 + return; 1.2752 + } 1.2753 + 1.2754 + nsRefPtr<MessageEventRunnable> runnable = 1.2755 + new MessageEventRunnable(ParentAsWorkerPrivate(), 1.2756 + WorkerRunnable::WorkerThreadModifyBusyCount, 1.2757 + Move(buffer), clonedObjects, aToMessagePort, 1.2758 + aMessagePortSerial); 1.2759 + if (!runnable->Dispatch(aCx)) { 1.2760 + aRv.Throw(NS_ERROR_FAILURE); 1.2761 + } 1.2762 +} 1.2763 + 1.2764 +template <class Derived> 1.2765 +void 1.2766 +WorkerPrivateParent<Derived>::PostMessageToMessagePort( 1.2767 + JSContext* aCx, 1.2768 + uint64_t aMessagePortSerial, 1.2769 + JS::Handle<JS::Value> aMessage, 1.2770 + const Optional<Sequence<JS::Value>>& aTransferable, 1.2771 + ErrorResult& aRv) 1.2772 +{ 1.2773 + AssertIsOnMainThread(); 1.2774 + 1.2775 + PostMessageInternal(aCx, aMessage, aTransferable, true, aMessagePortSerial, 1.2776 + aRv); 1.2777 +} 1.2778 + 1.2779 +template <class Derived> 1.2780 +bool 1.2781 +WorkerPrivateParent<Derived>::DispatchMessageEventToMessagePort( 1.2782 + JSContext* aCx, uint64_t aMessagePortSerial, 1.2783 + JSAutoStructuredCloneBuffer&& aBuffer, 1.2784 + nsTArray<nsCOMPtr<nsISupports>>& aClonedObjects) 1.2785 +{ 1.2786 + AssertIsOnMainThread(); 1.2787 + 1.2788 + JSAutoStructuredCloneBuffer buffer(Move(aBuffer)); 1.2789 + 1.2790 + nsTArray<nsCOMPtr<nsISupports>> clonedObjects; 1.2791 + clonedObjects.SwapElements(aClonedObjects); 1.2792 + 1.2793 + SharedWorker* sharedWorker; 1.2794 + if (!mSharedWorkers.Get(aMessagePortSerial, &sharedWorker)) { 1.2795 + // SharedWorker has already been unregistered? 1.2796 + return true; 1.2797 + } 1.2798 + 1.2799 + nsRefPtr<MessagePort> port = sharedWorker->Port(); 1.2800 + NS_ASSERTION(port, "SharedWorkers always have a port!"); 1.2801 + 1.2802 + if (port->IsClosed()) { 1.2803 + return true; 1.2804 + } 1.2805 + 1.2806 + nsCOMPtr<nsIScriptGlobalObject> sgo; 1.2807 + port->GetParentObject(getter_AddRefs(sgo)); 1.2808 + MOZ_ASSERT(sgo, "Should never happen if IsClosed() returned false!"); 1.2809 + MOZ_ASSERT(sgo->GetGlobalJSObject()); 1.2810 + 1.2811 + nsCOMPtr<nsIScriptContext> scx = sgo->GetContext(); 1.2812 + MOZ_ASSERT_IF(scx, scx->GetNativeContext()); 1.2813 + 1.2814 + AutoPushJSContext cx(scx ? scx->GetNativeContext() : aCx); 1.2815 + JSAutoCompartment(cx, sgo->GetGlobalJSObject()); 1.2816 + 1.2817 + JS::Rooted<JS::Value> data(cx); 1.2818 + if (!buffer.read(cx, &data, WorkerStructuredCloneCallbacks(true))) { 1.2819 + return false; 1.2820 + } 1.2821 + 1.2822 + buffer.clear(); 1.2823 + 1.2824 + nsRefPtr<MessageEvent> event = new MessageEvent(port, nullptr, nullptr); 1.2825 + nsresult rv = 1.2826 + event->InitMessageEvent(NS_LITERAL_STRING("message"), false, false, data, 1.2827 + EmptyString(), EmptyString(), nullptr); 1.2828 + if (NS_FAILED(rv)) { 1.2829 + xpc::Throw(cx, rv); 1.2830 + return false; 1.2831 + } 1.2832 + 1.2833 + event->SetTrusted(true); 1.2834 + 1.2835 + nsTArray<nsRefPtr<MessagePortBase>> ports; 1.2836 + ports.AppendElement(port); 1.2837 + 1.2838 + nsRefPtr<MessagePortList> portList = new MessagePortList(port, ports); 1.2839 + event->SetPorts(portList); 1.2840 + 1.2841 + nsCOMPtr<nsIDOMEvent> domEvent; 1.2842 + CallQueryInterface(event.get(), getter_AddRefs(domEvent)); 1.2843 + NS_ASSERTION(domEvent, "This should never fail!"); 1.2844 + 1.2845 + bool ignored; 1.2846 + rv = port->DispatchEvent(domEvent, &ignored); 1.2847 + if (NS_FAILED(rv)) { 1.2848 + xpc::Throw(cx, rv); 1.2849 + return false; 1.2850 + } 1.2851 + 1.2852 + return true; 1.2853 +} 1.2854 + 1.2855 +template <class Derived> 1.2856 +uint64_t 1.2857 +WorkerPrivateParent<Derived>::GetInnerWindowId() 1.2858 +{ 1.2859 + AssertIsOnMainThread(); 1.2860 + NS_ASSERTION(!mLoadInfo.mWindow || mLoadInfo.mWindow->IsInnerWindow(), 1.2861 + "Outer window?"); 1.2862 + return mLoadInfo.mWindow ? mLoadInfo.mWindow->WindowID() : 0; 1.2863 +} 1.2864 + 1.2865 +template <class Derived> 1.2866 +void 1.2867 +WorkerPrivateParent<Derived>::UpdateRuntimeAndContextOptions( 1.2868 + JSContext* aCx, 1.2869 + const JS::RuntimeOptions& aRuntimeOptions, 1.2870 + const JS::ContextOptions& aContentCxOptions, 1.2871 + const JS::ContextOptions& aChromeCxOptions) 1.2872 +{ 1.2873 + AssertIsOnParentThread(); 1.2874 + 1.2875 + { 1.2876 + MutexAutoLock lock(mMutex); 1.2877 + mJSSettings.runtimeOptions = aRuntimeOptions; 1.2878 + mJSSettings.content.contextOptions = aContentCxOptions; 1.2879 + mJSSettings.chrome.contextOptions = aChromeCxOptions; 1.2880 + } 1.2881 + 1.2882 + nsRefPtr<UpdateRuntimeAndContextOptionsRunnable> runnable = 1.2883 + new UpdateRuntimeAndContextOptionsRunnable(ParentAsWorkerPrivate(), 1.2884 + aRuntimeOptions, 1.2885 + aContentCxOptions, 1.2886 + aChromeCxOptions); 1.2887 + if (!runnable->Dispatch(aCx)) { 1.2888 + NS_WARNING("Failed to update worker context options!"); 1.2889 + JS_ClearPendingException(aCx); 1.2890 + } 1.2891 +} 1.2892 + 1.2893 +template <class Derived> 1.2894 +void 1.2895 +WorkerPrivateParent<Derived>::UpdatePreference(JSContext* aCx, WorkerPreference aPref, bool aValue) 1.2896 +{ 1.2897 + AssertIsOnParentThread(); 1.2898 + MOZ_ASSERT(aPref >= 0 && aPref < WORKERPREF_COUNT); 1.2899 + 1.2900 + nsRefPtr<UpdatePreferenceRunnable> runnable = 1.2901 + new UpdatePreferenceRunnable(ParentAsWorkerPrivate(), aPref, aValue); 1.2902 + if (!runnable->Dispatch(aCx)) { 1.2903 + NS_WARNING("Failed to update worker preferences!"); 1.2904 + JS_ClearPendingException(aCx); 1.2905 + } 1.2906 +} 1.2907 + 1.2908 +template <class Derived> 1.2909 +void 1.2910 +WorkerPrivateParent<Derived>::UpdateJSWorkerMemoryParameter(JSContext* aCx, 1.2911 + JSGCParamKey aKey, 1.2912 + uint32_t aValue) 1.2913 +{ 1.2914 + AssertIsOnParentThread(); 1.2915 + 1.2916 + bool found = false; 1.2917 + 1.2918 + { 1.2919 + MutexAutoLock lock(mMutex); 1.2920 + found = mJSSettings.ApplyGCSetting(aKey, aValue); 1.2921 + } 1.2922 + 1.2923 + if (found) { 1.2924 + nsRefPtr<UpdateJSWorkerMemoryParameterRunnable> runnable = 1.2925 + new UpdateJSWorkerMemoryParameterRunnable(ParentAsWorkerPrivate(), aKey, 1.2926 + aValue); 1.2927 + if (!runnable->Dispatch(aCx)) { 1.2928 + NS_WARNING("Failed to update memory parameter!"); 1.2929 + JS_ClearPendingException(aCx); 1.2930 + } 1.2931 + } 1.2932 +} 1.2933 + 1.2934 +#ifdef JS_GC_ZEAL 1.2935 +template <class Derived> 1.2936 +void 1.2937 +WorkerPrivateParent<Derived>::UpdateGCZeal(JSContext* aCx, uint8_t aGCZeal, 1.2938 + uint32_t aFrequency) 1.2939 +{ 1.2940 + AssertIsOnParentThread(); 1.2941 + 1.2942 + { 1.2943 + MutexAutoLock lock(mMutex); 1.2944 + mJSSettings.gcZeal = aGCZeal; 1.2945 + mJSSettings.gcZealFrequency = aFrequency; 1.2946 + } 1.2947 + 1.2948 + nsRefPtr<UpdateGCZealRunnable> runnable = 1.2949 + new UpdateGCZealRunnable(ParentAsWorkerPrivate(), aGCZeal, aFrequency); 1.2950 + if (!runnable->Dispatch(aCx)) { 1.2951 + NS_WARNING("Failed to update worker gczeal!"); 1.2952 + JS_ClearPendingException(aCx); 1.2953 + } 1.2954 +} 1.2955 +#endif 1.2956 + 1.2957 +template <class Derived> 1.2958 +void 1.2959 +WorkerPrivateParent<Derived>::GarbageCollect(JSContext* aCx, bool aShrinking) 1.2960 +{ 1.2961 + AssertIsOnParentThread(); 1.2962 + 1.2963 + nsRefPtr<GarbageCollectRunnable> runnable = 1.2964 + new GarbageCollectRunnable(ParentAsWorkerPrivate(), aShrinking, 1.2965 + /* collectChildren = */ true); 1.2966 + if (!runnable->Dispatch(aCx)) { 1.2967 + NS_WARNING("Failed to GC worker!"); 1.2968 + JS_ClearPendingException(aCx); 1.2969 + } 1.2970 +} 1.2971 + 1.2972 +template <class Derived> 1.2973 +void 1.2974 +WorkerPrivateParent<Derived>::CycleCollect(JSContext* aCx, bool aDummy) 1.2975 +{ 1.2976 + AssertIsOnParentThread(); 1.2977 + 1.2978 + nsRefPtr<CycleCollectRunnable> runnable = 1.2979 + new CycleCollectRunnable(ParentAsWorkerPrivate(), 1.2980 + /* collectChildren = */ true); 1.2981 + if (!runnable->Dispatch(aCx)) { 1.2982 + NS_WARNING("Failed to CC worker!"); 1.2983 + JS_ClearPendingException(aCx); 1.2984 + } 1.2985 +} 1.2986 + 1.2987 +template <class Derived> 1.2988 +void 1.2989 +WorkerPrivateParent<Derived>::OfflineStatusChangeEvent(JSContext* aCx, bool aIsOffline) 1.2990 +{ 1.2991 + AssertIsOnParentThread(); 1.2992 + 1.2993 + nsRefPtr<OfflineStatusChangeRunnable> runnable = 1.2994 + new OfflineStatusChangeRunnable(ParentAsWorkerPrivate(), aIsOffline); 1.2995 + if (!runnable->Dispatch(aCx)) { 1.2996 + NS_WARNING("Failed to dispatch offline status change event!"); 1.2997 + JS_ClearPendingException(aCx); 1.2998 + } 1.2999 +} 1.3000 + 1.3001 +void 1.3002 +WorkerPrivate::OfflineStatusChangeEventInternal(JSContext* aCx, bool aIsOffline) 1.3003 +{ 1.3004 + AssertIsOnWorkerThread(); 1.3005 + 1.3006 + for (uint32_t index = 0; index < mChildWorkers.Length(); ++index) { 1.3007 + mChildWorkers[index]->OfflineStatusChangeEvent(aCx, aIsOffline); 1.3008 + } 1.3009 + 1.3010 + mOnLine = !aIsOffline; 1.3011 + WorkerGlobalScope* globalScope = GlobalScope(); 1.3012 + nsRefPtr<WorkerNavigator> nav = globalScope->GetExistingNavigator(); 1.3013 + if (nav) { 1.3014 + nav->SetOnLine(mOnLine); 1.3015 + } 1.3016 + 1.3017 + nsString eventType; 1.3018 + if (aIsOffline) { 1.3019 + eventType.AssignLiteral("offline"); 1.3020 + } else { 1.3021 + eventType.AssignLiteral("online"); 1.3022 + } 1.3023 + 1.3024 + nsCOMPtr<nsIDOMEvent> event; 1.3025 + nsresult rv = 1.3026 + NS_NewDOMEvent(getter_AddRefs(event), globalScope, nullptr, nullptr); 1.3027 + NS_ENSURE_SUCCESS_VOID(rv); 1.3028 + 1.3029 + rv = event->InitEvent(eventType, false, false); 1.3030 + NS_ENSURE_SUCCESS_VOID(rv); 1.3031 + 1.3032 + event->SetTrusted(true); 1.3033 + 1.3034 + globalScope->DispatchDOMEvent(nullptr, event, nullptr, nullptr); 1.3035 +} 1.3036 + 1.3037 +template <class Derived> 1.3038 +bool 1.3039 +WorkerPrivateParent<Derived>::RegisterSharedWorker(JSContext* aCx, 1.3040 + SharedWorker* aSharedWorker) 1.3041 +{ 1.3042 + AssertIsOnMainThread(); 1.3043 + MOZ_ASSERT(aSharedWorker); 1.3044 + MOZ_ASSERT(IsSharedWorker()); 1.3045 + MOZ_ASSERT(!mSharedWorkers.Get(aSharedWorker->Serial())); 1.3046 + 1.3047 + nsRefPtr<MessagePortRunnable> runnable = 1.3048 + new MessagePortRunnable(ParentAsWorkerPrivate(), aSharedWorker->Serial(), 1.3049 + true); 1.3050 + if (!runnable->Dispatch(aCx)) { 1.3051 + return false; 1.3052 + } 1.3053 + 1.3054 + mSharedWorkers.Put(aSharedWorker->Serial(), aSharedWorker); 1.3055 + 1.3056 + // If there were other SharedWorker objects attached to this worker then they 1.3057 + // may all have been suspended and this worker would need to be resumed. 1.3058 + if (mSharedWorkers.Count() > 1 && !Resume(aCx, nullptr)) { 1.3059 + return false; 1.3060 + } 1.3061 + 1.3062 + return true; 1.3063 +} 1.3064 + 1.3065 +template <class Derived> 1.3066 +void 1.3067 +WorkerPrivateParent<Derived>::UnregisterSharedWorker( 1.3068 + JSContext* aCx, 1.3069 + SharedWorker* aSharedWorker) 1.3070 +{ 1.3071 + AssertIsOnMainThread(); 1.3072 + MOZ_ASSERT(aSharedWorker); 1.3073 + MOZ_ASSERT(IsSharedWorker()); 1.3074 + MOZ_ASSERT(mSharedWorkers.Get(aSharedWorker->Serial())); 1.3075 + 1.3076 + nsRefPtr<MessagePortRunnable> runnable = 1.3077 + new MessagePortRunnable(ParentAsWorkerPrivate(), aSharedWorker->Serial(), 1.3078 + false); 1.3079 + if (!runnable->Dispatch(aCx)) { 1.3080 + JS_ReportPendingException(aCx); 1.3081 + } 1.3082 + 1.3083 + mSharedWorkers.Remove(aSharedWorker->Serial()); 1.3084 + 1.3085 + // If there are still SharedWorker objects attached to this worker then they 1.3086 + // may all be suspended and this worker would need to be suspended. Otherwise, 1.3087 + // if that was the last SharedWorker then it's time to cancel this worker. 1.3088 + if (mSharedWorkers.Count()) { 1.3089 + if (!Suspend(aCx, nullptr)) { 1.3090 + JS_ReportPendingException(aCx); 1.3091 + } 1.3092 + } else if (!Cancel(aCx)) { 1.3093 + JS_ReportPendingException(aCx); 1.3094 + } 1.3095 +} 1.3096 + 1.3097 +template <class Derived> 1.3098 +void 1.3099 +WorkerPrivateParent<Derived>::BroadcastErrorToSharedWorkers( 1.3100 + JSContext* aCx, 1.3101 + const nsAString& aMessage, 1.3102 + const nsAString& aFilename, 1.3103 + const nsAString& aLine, 1.3104 + uint32_t aLineNumber, 1.3105 + uint32_t aColumnNumber, 1.3106 + uint32_t aFlags) 1.3107 +{ 1.3108 + AssertIsOnMainThread(); 1.3109 + 1.3110 + nsAutoTArray<nsRefPtr<SharedWorker>, 10> sharedWorkers; 1.3111 + GetAllSharedWorkers(sharedWorkers); 1.3112 + 1.3113 + if (sharedWorkers.IsEmpty()) { 1.3114 + return; 1.3115 + } 1.3116 + 1.3117 + nsAutoTArray<WindowAction, 10> windowActions; 1.3118 + nsresult rv; 1.3119 + 1.3120 + // First fire the error event at all SharedWorker objects. This may include 1.3121 + // multiple objects in a single window as well as objects in different 1.3122 + // windows. 1.3123 + for (uint32_t index = 0; index < sharedWorkers.Length(); index++) { 1.3124 + nsRefPtr<SharedWorker>& sharedWorker = sharedWorkers[index]; 1.3125 + 1.3126 + // May be null. 1.3127 + nsPIDOMWindow* window = sharedWorker->GetOwner(); 1.3128 + 1.3129 + uint32_t actionsIndex = windowActions.LastIndexOf(WindowAction(window)); 1.3130 + 1.3131 + nsIGlobalObject* global = sharedWorker->GetParentObject(); 1.3132 + AutoJSAPIWithErrorsReportedToWindow jsapi(global); 1.3133 + JSContext* cx = jsapi.cx(); 1.3134 + JSAutoCompartment ac(cx, global->GetGlobalJSObject()); 1.3135 + 1.3136 + RootedDictionary<ErrorEventInit> errorInit(aCx); 1.3137 + errorInit.mBubbles = false; 1.3138 + errorInit.mCancelable = true; 1.3139 + errorInit.mMessage = aMessage; 1.3140 + errorInit.mFilename = aFilename; 1.3141 + errorInit.mLineno = aLineNumber; 1.3142 + errorInit.mColno = aColumnNumber; 1.3143 + 1.3144 + nsRefPtr<ErrorEvent> errorEvent = 1.3145 + ErrorEvent::Constructor(sharedWorker, NS_LITERAL_STRING("error"), 1.3146 + errorInit); 1.3147 + if (!errorEvent) { 1.3148 + Throw(cx, NS_ERROR_UNEXPECTED); 1.3149 + JS_ReportPendingException(cx); 1.3150 + continue; 1.3151 + } 1.3152 + 1.3153 + errorEvent->SetTrusted(true); 1.3154 + 1.3155 + bool defaultActionEnabled; 1.3156 + nsresult rv = sharedWorker->DispatchEvent(errorEvent, &defaultActionEnabled); 1.3157 + if (NS_FAILED(rv)) { 1.3158 + Throw(cx, rv); 1.3159 + JS_ReportPendingException(cx); 1.3160 + continue; 1.3161 + } 1.3162 + 1.3163 + if (defaultActionEnabled) { 1.3164 + // Add the owning window to our list so that we will fire an error event 1.3165 + // at it later. 1.3166 + if (!windowActions.Contains(window)) { 1.3167 + windowActions.AppendElement(WindowAction(window, cx)); 1.3168 + } 1.3169 + } else if (actionsIndex != windowActions.NoIndex) { 1.3170 + // Any listener that calls preventDefault() will prevent the window from 1.3171 + // receiving the error event. 1.3172 + windowActions[actionsIndex].mDefaultAction = false; 1.3173 + } 1.3174 + } 1.3175 + 1.3176 + // If there are no windows to consider further then we're done. 1.3177 + if (windowActions.IsEmpty()) { 1.3178 + return; 1.3179 + } 1.3180 + 1.3181 + bool shouldLogErrorToConsole = true; 1.3182 + 1.3183 + // Now fire error events at all the windows remaining. 1.3184 + for (uint32_t index = 0; index < windowActions.Length(); index++) { 1.3185 + WindowAction& windowAction = windowActions[index]; 1.3186 + 1.3187 + // If there is no window or the script already called preventDefault then 1.3188 + // skip this window. 1.3189 + if (!windowAction.mWindow || !windowAction.mDefaultAction) { 1.3190 + continue; 1.3191 + } 1.3192 + 1.3193 + JSContext* cx = windowAction.mJSContext; 1.3194 + 1.3195 + AutoCxPusher autoPush(cx); 1.3196 + 1.3197 + nsCOMPtr<nsIScriptGlobalObject> sgo = 1.3198 + do_QueryInterface(windowAction.mWindow); 1.3199 + MOZ_ASSERT(sgo); 1.3200 + 1.3201 + MOZ_ASSERT(NS_IsMainThread()); 1.3202 + RootedDictionary<ErrorEventInit> init(aCx); 1.3203 + init.mLineno = aLineNumber; 1.3204 + init.mFilename = aFilename; 1.3205 + init.mMessage = aMessage; 1.3206 + init.mCancelable = true; 1.3207 + init.mBubbles = true; 1.3208 + 1.3209 + nsEventStatus status = nsEventStatus_eIgnore; 1.3210 + rv = sgo->HandleScriptError(init, &status); 1.3211 + if (NS_FAILED(rv)) { 1.3212 + Throw(cx, rv); 1.3213 + JS_ReportPendingException(cx); 1.3214 + continue; 1.3215 + } 1.3216 + 1.3217 + if (status == nsEventStatus_eConsumeNoDefault) { 1.3218 + shouldLogErrorToConsole = false; 1.3219 + } 1.3220 + } 1.3221 + 1.3222 + // Finally log a warning in the console if no window tried to prevent it. 1.3223 + if (shouldLogErrorToConsole) { 1.3224 + LogErrorToConsole(aMessage, aFilename, aLine, aLineNumber, aColumnNumber, 1.3225 + aFlags, 0); 1.3226 + } 1.3227 +} 1.3228 + 1.3229 +template <class Derived> 1.3230 +void 1.3231 +WorkerPrivateParent<Derived>::GetAllSharedWorkers( 1.3232 + nsTArray<nsRefPtr<SharedWorker>>& aSharedWorkers) 1.3233 +{ 1.3234 + AssertIsOnMainThread(); 1.3235 + MOZ_ASSERT(IsSharedWorker()); 1.3236 + 1.3237 + struct Helper 1.3238 + { 1.3239 + static PLDHashOperator 1.3240 + Collect(const uint64_t& aKey, 1.3241 + SharedWorker* aSharedWorker, 1.3242 + void* aClosure) 1.3243 + { 1.3244 + AssertIsOnMainThread(); 1.3245 + MOZ_ASSERT(aSharedWorker); 1.3246 + MOZ_ASSERT(aClosure); 1.3247 + 1.3248 + auto array = static_cast<nsTArray<nsRefPtr<SharedWorker>>*>(aClosure); 1.3249 + array->AppendElement(aSharedWorker); 1.3250 + 1.3251 + return PL_DHASH_NEXT; 1.3252 + } 1.3253 + }; 1.3254 + 1.3255 + if (!aSharedWorkers.IsEmpty()) { 1.3256 + aSharedWorkers.Clear(); 1.3257 + } 1.3258 + 1.3259 + mSharedWorkers.EnumerateRead(Helper::Collect, &aSharedWorkers); 1.3260 +} 1.3261 + 1.3262 +template <class Derived> 1.3263 +void 1.3264 +WorkerPrivateParent<Derived>::CloseSharedWorkersForWindow( 1.3265 + nsPIDOMWindow* aWindow) 1.3266 +{ 1.3267 + AssertIsOnMainThread(); 1.3268 + MOZ_ASSERT(IsSharedWorker()); 1.3269 + MOZ_ASSERT(aWindow); 1.3270 + 1.3271 + struct Closure 1.3272 + { 1.3273 + nsPIDOMWindow* mWindow; 1.3274 + nsAutoTArray<nsRefPtr<SharedWorker>, 10> mSharedWorkers; 1.3275 + 1.3276 + Closure(nsPIDOMWindow* aWindow) 1.3277 + : mWindow(aWindow) 1.3278 + { 1.3279 + AssertIsOnMainThread(); 1.3280 + MOZ_ASSERT(aWindow); 1.3281 + } 1.3282 + 1.3283 + static PLDHashOperator 1.3284 + Collect(const uint64_t& aKey, 1.3285 + SharedWorker* aSharedWorker, 1.3286 + void* aClosure) 1.3287 + { 1.3288 + AssertIsOnMainThread(); 1.3289 + MOZ_ASSERT(aSharedWorker); 1.3290 + MOZ_ASSERT(aClosure); 1.3291 + 1.3292 + auto closure = static_cast<Closure*>(aClosure); 1.3293 + MOZ_ASSERT(closure->mWindow); 1.3294 + 1.3295 + if (aSharedWorker->GetOwner() == closure->mWindow) { 1.3296 + closure->mSharedWorkers.AppendElement(aSharedWorker); 1.3297 + } else { 1.3298 + MOZ_ASSERT(!SameCOMIdentity(aSharedWorker->GetOwner(), 1.3299 + closure->mWindow)); 1.3300 + } 1.3301 + 1.3302 + return PL_DHASH_NEXT; 1.3303 + } 1.3304 + }; 1.3305 + 1.3306 + Closure closure(aWindow); 1.3307 + 1.3308 + mSharedWorkers.EnumerateRead(Closure::Collect, &closure); 1.3309 + 1.3310 + for (uint32_t index = 0; index < closure.mSharedWorkers.Length(); index++) { 1.3311 + closure.mSharedWorkers[index]->Close(); 1.3312 + } 1.3313 +} 1.3314 + 1.3315 +template <class Derived> 1.3316 +void 1.3317 +WorkerPrivateParent<Derived>::WorkerScriptLoaded() 1.3318 +{ 1.3319 + AssertIsOnMainThread(); 1.3320 + 1.3321 + if (IsSharedWorker()) { 1.3322 + // No longer need to hold references to the window or document we came from. 1.3323 + mLoadInfo.mWindow = nullptr; 1.3324 + mLoadInfo.mScriptContext = nullptr; 1.3325 + } 1.3326 +} 1.3327 + 1.3328 +template <class Derived> 1.3329 +void 1.3330 +WorkerPrivateParent<Derived>::SetBaseURI(nsIURI* aBaseURI) 1.3331 +{ 1.3332 + AssertIsOnMainThread(); 1.3333 + 1.3334 + if (!mLoadInfo.mBaseURI) { 1.3335 + NS_ASSERTION(GetParent(), "Shouldn't happen without a parent!"); 1.3336 + mLoadInfo.mResolvedScriptURI = aBaseURI; 1.3337 + } 1.3338 + 1.3339 + mLoadInfo.mBaseURI = aBaseURI; 1.3340 + 1.3341 + if (NS_FAILED(aBaseURI->GetSpec(mLocationInfo.mHref))) { 1.3342 + mLocationInfo.mHref.Truncate(); 1.3343 + } 1.3344 + 1.3345 + if (NS_FAILED(aBaseURI->GetHost(mLocationInfo.mHostname))) { 1.3346 + mLocationInfo.mHostname.Truncate(); 1.3347 + } 1.3348 + 1.3349 + if (NS_FAILED(aBaseURI->GetPath(mLocationInfo.mPathname))) { 1.3350 + mLocationInfo.mPathname.Truncate(); 1.3351 + } 1.3352 + 1.3353 + nsCString temp; 1.3354 + 1.3355 + nsCOMPtr<nsIURL> url(do_QueryInterface(aBaseURI)); 1.3356 + if (url && NS_SUCCEEDED(url->GetQuery(temp)) && !temp.IsEmpty()) { 1.3357 + mLocationInfo.mSearch.AssignLiteral("?"); 1.3358 + mLocationInfo.mSearch.Append(temp); 1.3359 + } 1.3360 + 1.3361 + if (NS_SUCCEEDED(aBaseURI->GetRef(temp)) && !temp.IsEmpty()) { 1.3362 + nsCOMPtr<nsITextToSubURI> converter = 1.3363 + do_GetService(NS_ITEXTTOSUBURI_CONTRACTID); 1.3364 + if (converter) { 1.3365 + nsCString charset; 1.3366 + nsAutoString unicodeRef; 1.3367 + if (NS_SUCCEEDED(aBaseURI->GetOriginCharset(charset)) && 1.3368 + NS_SUCCEEDED(converter->UnEscapeURIForUI(charset, temp, 1.3369 + unicodeRef))) { 1.3370 + mLocationInfo.mHash.AssignLiteral("#"); 1.3371 + mLocationInfo.mHash.Append(NS_ConvertUTF16toUTF8(unicodeRef)); 1.3372 + } 1.3373 + } 1.3374 + 1.3375 + if (mLocationInfo.mHash.IsEmpty()) { 1.3376 + mLocationInfo.mHash.AssignLiteral("#"); 1.3377 + mLocationInfo.mHash.Append(temp); 1.3378 + } 1.3379 + } 1.3380 + 1.3381 + if (NS_SUCCEEDED(aBaseURI->GetScheme(mLocationInfo.mProtocol))) { 1.3382 + mLocationInfo.mProtocol.AppendLiteral(":"); 1.3383 + } 1.3384 + else { 1.3385 + mLocationInfo.mProtocol.Truncate(); 1.3386 + } 1.3387 + 1.3388 + int32_t port; 1.3389 + if (NS_SUCCEEDED(aBaseURI->GetPort(&port)) && port != -1) { 1.3390 + mLocationInfo.mPort.AppendInt(port); 1.3391 + 1.3392 + nsAutoCString host(mLocationInfo.mHostname); 1.3393 + host.AppendLiteral(":"); 1.3394 + host.Append(mLocationInfo.mPort); 1.3395 + 1.3396 + mLocationInfo.mHost.Assign(host); 1.3397 + } 1.3398 + else { 1.3399 + mLocationInfo.mHost.Assign(mLocationInfo.mHostname); 1.3400 + } 1.3401 + 1.3402 + nsContentUtils::GetUTFNonNullOrigin(aBaseURI, mLocationInfo.mOrigin); 1.3403 +} 1.3404 + 1.3405 +template <class Derived> 1.3406 +void 1.3407 +WorkerPrivateParent<Derived>::SetPrincipal(nsIPrincipal* aPrincipal) 1.3408 +{ 1.3409 + AssertIsOnMainThread(); 1.3410 + 1.3411 + mLoadInfo.mPrincipal = aPrincipal; 1.3412 + mLoadInfo.mPrincipalIsSystem = nsContentUtils::IsSystemPrincipal(aPrincipal); 1.3413 + uint16_t appStatus = aPrincipal->GetAppStatus(); 1.3414 + mLoadInfo.mIsInPrivilegedApp = 1.3415 + (appStatus == nsIPrincipal::APP_STATUS_CERTIFIED || 1.3416 + appStatus == nsIPrincipal::APP_STATUS_PRIVILEGED); 1.3417 + mLoadInfo.mIsInCertifiedApp = (appStatus == nsIPrincipal::APP_STATUS_CERTIFIED); 1.3418 +} 1.3419 + 1.3420 +template <class Derived> 1.3421 +JSContext* 1.3422 +WorkerPrivateParent<Derived>::ParentJSContext() const 1.3423 +{ 1.3424 + AssertIsOnParentThread(); 1.3425 + 1.3426 + if (mParent) { 1.3427 + return mParent->GetJSContext(); 1.3428 + } 1.3429 + 1.3430 + AssertIsOnMainThread(); 1.3431 + 1.3432 + return mLoadInfo.mScriptContext ? 1.3433 + mLoadInfo.mScriptContext->GetNativeContext() : 1.3434 + nsContentUtils::GetSafeJSContext(); 1.3435 +} 1.3436 + 1.3437 +template <class Derived> 1.3438 +void 1.3439 +WorkerPrivateParent<Derived>::RegisterHostObjectURI(const nsACString& aURI) 1.3440 +{ 1.3441 + AssertIsOnMainThread(); 1.3442 + mHostObjectURIs.AppendElement(aURI); 1.3443 +} 1.3444 + 1.3445 +template <class Derived> 1.3446 +void 1.3447 +WorkerPrivateParent<Derived>::UnregisterHostObjectURI(const nsACString& aURI) 1.3448 +{ 1.3449 + AssertIsOnMainThread(); 1.3450 + mHostObjectURIs.RemoveElement(aURI); 1.3451 +} 1.3452 + 1.3453 +template <class Derived> 1.3454 +void 1.3455 +WorkerPrivateParent<Derived>::StealHostObjectURIs(nsTArray<nsCString>& aArray) 1.3456 +{ 1.3457 + aArray.SwapElements(mHostObjectURIs); 1.3458 +} 1.3459 + 1.3460 +template <class Derived> 1.3461 +NS_IMPL_ADDREF_INHERITED(WorkerPrivateParent<Derived>, DOMEventTargetHelper) 1.3462 + 1.3463 +template <class Derived> 1.3464 +NS_IMPL_RELEASE_INHERITED(WorkerPrivateParent<Derived>, DOMEventTargetHelper) 1.3465 + 1.3466 +template <class Derived> 1.3467 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(WorkerPrivateParent<Derived>) 1.3468 +NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) 1.3469 + 1.3470 +template <class Derived> 1.3471 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(WorkerPrivateParent<Derived>, 1.3472 + DOMEventTargetHelper) 1.3473 + tmp->AssertIsOnParentThread(); 1.3474 + 1.3475 + // The WorkerPrivate::mSelfRef has a reference to itself, which is really 1.3476 + // held by the worker thread. We traverse this reference if and only if our 1.3477 + // busy count is zero and we have not released the main thread reference. 1.3478 + // We do not unlink it. This allows the CC to break cycles involving the 1.3479 + // WorkerPrivate and begin shutting it down (which does happen in unlink) but 1.3480 + // ensures that the WorkerPrivate won't be deleted before we're done shutting 1.3481 + // down the thread. 1.3482 + 1.3483 + if (!tmp->mBusyCount && !tmp->mMainThreadObjectsForgotten) { 1.3484 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSelfRef) 1.3485 + } 1.3486 + 1.3487 + // The various strong references in LoadInfo are managed manually and cannot 1.3488 + // be cycle collected. 1.3489 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 1.3490 + 1.3491 +template <class Derived> 1.3492 +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(WorkerPrivateParent<Derived>, 1.3493 + DOMEventTargetHelper) 1.3494 + tmp->Terminate(nullptr); 1.3495 +NS_IMPL_CYCLE_COLLECTION_UNLINK_END 1.3496 + 1.3497 +template <class Derived> 1.3498 +NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(WorkerPrivateParent<Derived>, 1.3499 + DOMEventTargetHelper) 1.3500 + tmp->AssertIsOnParentThread(); 1.3501 +NS_IMPL_CYCLE_COLLECTION_TRACE_END 1.3502 + 1.3503 +#ifdef DEBUG 1.3504 + 1.3505 +template <class Derived> 1.3506 +void 1.3507 +WorkerPrivateParent<Derived>::AssertIsOnParentThread() const 1.3508 +{ 1.3509 + if (GetParent()) { 1.3510 + GetParent()->AssertIsOnWorkerThread(); 1.3511 + } 1.3512 + else { 1.3513 + AssertIsOnMainThread(); 1.3514 + } 1.3515 +} 1.3516 + 1.3517 +template <class Derived> 1.3518 +void 1.3519 +WorkerPrivateParent<Derived>::AssertInnerWindowIsCorrect() const 1.3520 +{ 1.3521 + AssertIsOnParentThread(); 1.3522 + 1.3523 + // Only care about top level workers from windows. 1.3524 + if (mParent || !mLoadInfo.mWindow) { 1.3525 + return; 1.3526 + } 1.3527 + 1.3528 + AssertIsOnMainThread(); 1.3529 + 1.3530 + nsPIDOMWindow* outer = mLoadInfo.mWindow->GetOuterWindow(); 1.3531 + NS_ASSERTION(outer && outer->GetCurrentInnerWindow() == mLoadInfo.mWindow, 1.3532 + "Inner window no longer correct!"); 1.3533 +} 1.3534 + 1.3535 +#endif 1.3536 +WorkerPrivate::WorkerPrivate(JSContext* aCx, 1.3537 + WorkerPrivate* aParent, 1.3538 + const nsAString& aScriptURL, 1.3539 + bool aIsChromeWorker, WorkerType aWorkerType, 1.3540 + const nsACString& aSharedWorkerName, 1.3541 + LoadInfo& aLoadInfo) 1.3542 +: WorkerPrivateParent<WorkerPrivate>(aCx, aParent, aScriptURL, 1.3543 + aIsChromeWorker, aWorkerType, 1.3544 + aSharedWorkerName, aLoadInfo), 1.3545 + mJSContext(nullptr), mErrorHandlerRecursionCount(0), mNextTimeoutId(1), 1.3546 + mStatus(Pending), mSuspended(false), mTimerRunning(false), 1.3547 + mRunningExpiredTimeouts(false), mCloseHandlerStarted(false), 1.3548 + mCloseHandlerFinished(false), mMemoryReporterRunning(false), 1.3549 + mBlockedForMemoryReporter(false), mCancelAllPendingRunnables(false), 1.3550 + mPeriodicGCTimerRunning(false), mIdleGCTimerRunning(false) 1.3551 +#ifdef DEBUG 1.3552 + , mPRThread(nullptr) 1.3553 +#endif 1.3554 +{ 1.3555 + MOZ_ASSERT_IF(IsSharedWorker(), !aSharedWorkerName.IsVoid()); 1.3556 + MOZ_ASSERT_IF(!IsSharedWorker(), aSharedWorkerName.IsEmpty()); 1.3557 + 1.3558 + if (aParent) { 1.3559 + aParent->AssertIsOnWorkerThread(); 1.3560 + aParent->GetAllPreferences(mPreferences); 1.3561 + mOnLine = aParent->OnLine(); 1.3562 + } 1.3563 + else { 1.3564 + AssertIsOnMainThread(); 1.3565 + RuntimeService::GetDefaultPreferences(mPreferences); 1.3566 + mOnLine = !NS_IsOffline(); 1.3567 + } 1.3568 +} 1.3569 + 1.3570 +WorkerPrivate::~WorkerPrivate() 1.3571 +{ 1.3572 +} 1.3573 + 1.3574 +// static 1.3575 +already_AddRefed<WorkerPrivate> 1.3576 +WorkerPrivate::Constructor(const GlobalObject& aGlobal, 1.3577 + const nsAString& aScriptURL, 1.3578 + ErrorResult& aRv) 1.3579 +{ 1.3580 + return WorkerPrivate::Constructor(aGlobal, aScriptURL, false, 1.3581 + WorkerTypeDedicated, EmptyCString(), 1.3582 + nullptr, aRv); 1.3583 +} 1.3584 + 1.3585 +// static 1.3586 +bool 1.3587 +WorkerPrivate::WorkerAvailable(JSContext* /* unused */, JSObject* /* unused */) 1.3588 +{ 1.3589 + // If we're already on a worker workers are clearly enabled. 1.3590 + if (!NS_IsMainThread()) { 1.3591 + return true; 1.3592 + } 1.3593 + 1.3594 + // If our caller is chrome, workers are always available. 1.3595 + if (nsContentUtils::IsCallerChrome()) { 1.3596 + return true; 1.3597 + } 1.3598 + 1.3599 + // Else check the pref. 1.3600 + return Preferences::GetBool(PREF_WORKERS_ENABLED); 1.3601 +} 1.3602 + 1.3603 +// static 1.3604 +already_AddRefed<ChromeWorkerPrivate> 1.3605 +ChromeWorkerPrivate::Constructor(const GlobalObject& aGlobal, 1.3606 + const nsAString& aScriptURL, 1.3607 + ErrorResult& aRv) 1.3608 +{ 1.3609 + return WorkerPrivate::Constructor(aGlobal, aScriptURL, true, 1.3610 + WorkerTypeDedicated, EmptyCString(), 1.3611 + nullptr, aRv) 1.3612 + .downcast<ChromeWorkerPrivate>(); 1.3613 +} 1.3614 + 1.3615 +// static 1.3616 +bool 1.3617 +ChromeWorkerPrivate::WorkerAvailable(JSContext* /* unused */, JSObject* /* unused */) 1.3618 +{ 1.3619 + // Chrome is always allowed to use workers, and content is never allowed to 1.3620 + // use ChromeWorker, so all we have to check is the caller. 1.3621 + return nsContentUtils::ThreadsafeIsCallerChrome(); 1.3622 +} 1.3623 + 1.3624 +// static 1.3625 +already_AddRefed<WorkerPrivate> 1.3626 +WorkerPrivate::Constructor(const GlobalObject& aGlobal, 1.3627 + const nsAString& aScriptURL, 1.3628 + bool aIsChromeWorker, WorkerType aWorkerType, 1.3629 + const nsACString& aSharedWorkerName, 1.3630 + LoadInfo* aLoadInfo, ErrorResult& aRv) 1.3631 +{ 1.3632 + WorkerPrivate* parent = NS_IsMainThread() ? 1.3633 + nullptr : 1.3634 + GetCurrentThreadWorkerPrivate(); 1.3635 + if (parent) { 1.3636 + parent->AssertIsOnWorkerThread(); 1.3637 + } else { 1.3638 + AssertIsOnMainThread(); 1.3639 + } 1.3640 + 1.3641 + JSContext* cx = aGlobal.GetContext(); 1.3642 + 1.3643 + MOZ_ASSERT_IF(aWorkerType == WorkerTypeShared, 1.3644 + !aSharedWorkerName.IsVoid()); 1.3645 + MOZ_ASSERT_IF(aWorkerType != WorkerTypeShared, 1.3646 + aSharedWorkerName.IsEmpty()); 1.3647 + 1.3648 + Maybe<LoadInfo> stackLoadInfo; 1.3649 + if (!aLoadInfo) { 1.3650 + stackLoadInfo.construct(); 1.3651 + 1.3652 + nsresult rv = GetLoadInfo(cx, nullptr, parent, aScriptURL, 1.3653 + aIsChromeWorker, stackLoadInfo.addr()); 1.3654 + if (NS_FAILED(rv)) { 1.3655 + scriptloader::ReportLoadError(cx, aScriptURL, rv, !parent); 1.3656 + aRv.Throw(rv); 1.3657 + return nullptr; 1.3658 + } 1.3659 + 1.3660 + aLoadInfo = stackLoadInfo.addr(); 1.3661 + } 1.3662 + 1.3663 + // NB: This has to be done before creating the WorkerPrivate, because it will 1.3664 + // attempt to use static variables that are initialized in the RuntimeService 1.3665 + // constructor. 1.3666 + RuntimeService* runtimeService; 1.3667 + 1.3668 + if (!parent) { 1.3669 + runtimeService = RuntimeService::GetOrCreateService(); 1.3670 + if (!runtimeService) { 1.3671 + JS_ReportError(cx, "Failed to create runtime service!"); 1.3672 + aRv.Throw(NS_ERROR_FAILURE); 1.3673 + return nullptr; 1.3674 + } 1.3675 + } 1.3676 + else { 1.3677 + runtimeService = RuntimeService::GetService(); 1.3678 + } 1.3679 + 1.3680 + MOZ_ASSERT(runtimeService); 1.3681 + 1.3682 + nsRefPtr<WorkerPrivate> worker = 1.3683 + new WorkerPrivate(cx, parent, aScriptURL, aIsChromeWorker, 1.3684 + aWorkerType, aSharedWorkerName, *aLoadInfo); 1.3685 + 1.3686 + if (!runtimeService->RegisterWorker(cx, worker)) { 1.3687 + aRv.Throw(NS_ERROR_UNEXPECTED); 1.3688 + return nullptr; 1.3689 + } 1.3690 + 1.3691 + nsRefPtr<CompileScriptRunnable> compiler = new CompileScriptRunnable(worker); 1.3692 + if (!compiler->Dispatch(cx)) { 1.3693 + aRv.Throw(NS_ERROR_UNEXPECTED); 1.3694 + return nullptr; 1.3695 + } 1.3696 + 1.3697 + worker->mSelfRef = worker; 1.3698 + 1.3699 + return worker.forget(); 1.3700 +} 1.3701 + 1.3702 +// static 1.3703 +nsresult 1.3704 +WorkerPrivate::GetLoadInfo(JSContext* aCx, nsPIDOMWindow* aWindow, 1.3705 + WorkerPrivate* aParent, const nsAString& aScriptURL, 1.3706 + bool aIsChromeWorker, LoadInfo* aLoadInfo) 1.3707 +{ 1.3708 + using namespace mozilla::dom::workers::scriptloader; 1.3709 + 1.3710 + MOZ_ASSERT(aCx); 1.3711 + 1.3712 + if (aWindow) { 1.3713 + AssertIsOnMainThread(); 1.3714 + } 1.3715 + 1.3716 + LoadInfo loadInfo; 1.3717 + nsresult rv; 1.3718 + 1.3719 + if (aParent) { 1.3720 + aParent->AssertIsOnWorkerThread(); 1.3721 + 1.3722 + // If the parent is going away give up now. 1.3723 + Status parentStatus; 1.3724 + { 1.3725 + MutexAutoLock lock(aParent->mMutex); 1.3726 + parentStatus = aParent->mStatus; 1.3727 + } 1.3728 + 1.3729 + if (parentStatus > Running) { 1.3730 + NS_WARNING("Cannot create child workers from the close handler!"); 1.3731 + return NS_ERROR_FAILURE; 1.3732 + } 1.3733 + 1.3734 + // StartAssignment() is used instead getter_AddRefs because, getter_AddRefs 1.3735 + // does QI in debug build and, if this worker runs in a child process, 1.3736 + // HttpChannelChild will crash because it's not thread-safe. 1.3737 + rv = ChannelFromScriptURLWorkerThread(aCx, aParent, aScriptURL, 1.3738 + loadInfo.mChannel.StartAssignment()); 1.3739 + NS_ENSURE_SUCCESS(rv, rv); 1.3740 + 1.3741 + // Now that we've spun the loop there's no guarantee that our parent is 1.3742 + // still alive. We may have received control messages initiating shutdown. 1.3743 + { 1.3744 + MutexAutoLock lock(aParent->mMutex); 1.3745 + parentStatus = aParent->mStatus; 1.3746 + } 1.3747 + 1.3748 + if (parentStatus > Running) { 1.3749 + nsCOMPtr<nsIThread> mainThread; 1.3750 + if (NS_FAILED(NS_GetMainThread(getter_AddRefs(mainThread))) || 1.3751 + NS_FAILED(NS_ProxyRelease(mainThread, loadInfo.mChannel))) { 1.3752 + NS_WARNING("Failed to proxy release of channel, leaking instead!"); 1.3753 + } 1.3754 + return NS_ERROR_FAILURE; 1.3755 + } 1.3756 + 1.3757 + loadInfo.mDomain = aParent->Domain(); 1.3758 + } else { 1.3759 + AssertIsOnMainThread(); 1.3760 + 1.3761 + nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager(); 1.3762 + MOZ_ASSERT(ssm); 1.3763 + 1.3764 + bool isChrome = nsContentUtils::IsCallerChrome(); 1.3765 + 1.3766 + // First check to make sure the caller has permission to make a privileged 1.3767 + // worker if they called the ChromeWorker/ChromeSharedWorker constructor. 1.3768 + if (aIsChromeWorker && !isChrome) { 1.3769 + return NS_ERROR_DOM_SECURITY_ERR; 1.3770 + } 1.3771 + 1.3772 + // Chrome callers (whether ChromeWorker of Worker) always get the system 1.3773 + // principal here as they're allowed to load anything. The script loader may 1.3774 + // change the principal later depending on the script uri. 1.3775 + if (isChrome) { 1.3776 + rv = ssm->GetSystemPrincipal(getter_AddRefs(loadInfo.mPrincipal)); 1.3777 + NS_ENSURE_SUCCESS(rv, rv); 1.3778 + } 1.3779 + 1.3780 + // See if we're being called from a window. 1.3781 + nsCOMPtr<nsPIDOMWindow> globalWindow = aWindow; 1.3782 + if (!globalWindow) { 1.3783 + nsCOMPtr<nsIScriptGlobalObject> scriptGlobal = 1.3784 + nsJSUtils::GetStaticScriptGlobal(JS::CurrentGlobalOrNull(aCx)); 1.3785 + if (scriptGlobal) { 1.3786 + globalWindow = do_QueryInterface(scriptGlobal); 1.3787 + MOZ_ASSERT(globalWindow); 1.3788 + } 1.3789 + } 1.3790 + 1.3791 + nsCOMPtr<nsIDocument> document; 1.3792 + 1.3793 + if (globalWindow) { 1.3794 + // Only use the current inner window, and only use it if the caller can 1.3795 + // access it. 1.3796 + nsPIDOMWindow* outerWindow = globalWindow->GetOuterWindow(); 1.3797 + if (outerWindow) { 1.3798 + loadInfo.mWindow = outerWindow->GetCurrentInnerWindow(); 1.3799 + } 1.3800 + 1.3801 + if (!loadInfo.mWindow || 1.3802 + (globalWindow != loadInfo.mWindow && 1.3803 + !nsContentUtils::CanCallerAccess(loadInfo.mWindow))) { 1.3804 + return NS_ERROR_DOM_SECURITY_ERR; 1.3805 + } 1.3806 + 1.3807 + nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(loadInfo.mWindow); 1.3808 + MOZ_ASSERT(sgo); 1.3809 + 1.3810 + loadInfo.mScriptContext = sgo->GetContext(); 1.3811 + NS_ENSURE_TRUE(loadInfo.mScriptContext, NS_ERROR_FAILURE); 1.3812 + 1.3813 + // If we're called from a window then we can dig out the principal and URI 1.3814 + // from the document. 1.3815 + document = loadInfo.mWindow->GetExtantDoc(); 1.3816 + NS_ENSURE_TRUE(document, NS_ERROR_FAILURE); 1.3817 + 1.3818 + loadInfo.mBaseURI = document->GetDocBaseURI(); 1.3819 + 1.3820 + // Use the document's NodePrincipal as our principal if we're not being 1.3821 + // called from chrome. 1.3822 + if (!loadInfo.mPrincipal) { 1.3823 + loadInfo.mPrincipal = document->NodePrincipal(); 1.3824 + NS_ENSURE_TRUE(loadInfo.mPrincipal, NS_ERROR_FAILURE); 1.3825 + 1.3826 + // We use the document's base domain to limit the number of workers 1.3827 + // each domain can create. For sandboxed documents, we use the domain 1.3828 + // of their first non-sandboxed document, walking up until we find 1.3829 + // one. If we can't find one, we fall back to using the GUID of the 1.3830 + // null principal as the base domain. 1.3831 + if (document->GetSandboxFlags() & SANDBOXED_ORIGIN) { 1.3832 + nsCOMPtr<nsIDocument> tmpDoc = document; 1.3833 + do { 1.3834 + tmpDoc = tmpDoc->GetParentDocument(); 1.3835 + } while (tmpDoc && tmpDoc->GetSandboxFlags() & SANDBOXED_ORIGIN); 1.3836 + 1.3837 + if (tmpDoc) { 1.3838 + // There was an unsandboxed ancestor, yay! 1.3839 + nsCOMPtr<nsIPrincipal> tmpPrincipal = tmpDoc->NodePrincipal(); 1.3840 + rv = tmpPrincipal->GetBaseDomain(loadInfo.mDomain); 1.3841 + NS_ENSURE_SUCCESS(rv, rv); 1.3842 + } else { 1.3843 + // No unsandboxed ancestor, use our GUID. 1.3844 + rv = loadInfo.mPrincipal->GetBaseDomain(loadInfo.mDomain); 1.3845 + NS_ENSURE_SUCCESS(rv, rv); 1.3846 + } 1.3847 + } else { 1.3848 + // Document creating the worker is not sandboxed. 1.3849 + rv = loadInfo.mPrincipal->GetBaseDomain(loadInfo.mDomain); 1.3850 + NS_ENSURE_SUCCESS(rv, rv); 1.3851 + } 1.3852 + } 1.3853 + 1.3854 + nsCOMPtr<nsIPermissionManager> permMgr = 1.3855 + do_GetService(NS_PERMISSIONMANAGER_CONTRACTID, &rv); 1.3856 + NS_ENSURE_SUCCESS(rv, rv); 1.3857 + 1.3858 + uint32_t perm; 1.3859 + rv = permMgr->TestPermissionFromPrincipal(loadInfo.mPrincipal, "systemXHR", 1.3860 + &perm); 1.3861 + NS_ENSURE_SUCCESS(rv, rv); 1.3862 + 1.3863 + loadInfo.mXHRParamsAllowed = perm == nsIPermissionManager::ALLOW_ACTION; 1.3864 + } else { 1.3865 + // Not a window 1.3866 + MOZ_ASSERT(isChrome); 1.3867 + 1.3868 + // We're being created outside of a window. Need to figure out the script 1.3869 + // that is creating us in order for us to use relative URIs later on. 1.3870 + JS::AutoFilename fileName; 1.3871 + if (JS::DescribeScriptedCaller(aCx, &fileName)) { 1.3872 + // In most cases, fileName is URI. In a few other cases 1.3873 + // (e.g. xpcshell), fileName is a file path. Ideally, we would 1.3874 + // prefer testing whether fileName parses as an URI and fallback 1.3875 + // to file path in case of error, but Windows file paths have 1.3876 + // the interesting property that they can be parsed as bogus 1.3877 + // URIs (e.g. C:/Windows/Tmp is interpreted as scheme "C", 1.3878 + // hostname "Windows", path "Tmp"), which defeats this algorithm. 1.3879 + // Therefore, we adopt the opposite convention. 1.3880 + nsCOMPtr<nsIFile> scriptFile = 1.3881 + do_CreateInstance("@mozilla.org/file/local;1", &rv); 1.3882 + if (NS_FAILED(rv)) { 1.3883 + return rv; 1.3884 + } 1.3885 + 1.3886 + rv = scriptFile->InitWithPath(NS_ConvertUTF8toUTF16(fileName.get())); 1.3887 + if (NS_SUCCEEDED(rv)) { 1.3888 + rv = NS_NewFileURI(getter_AddRefs(loadInfo.mBaseURI), 1.3889 + scriptFile); 1.3890 + } 1.3891 + if (NS_FAILED(rv)) { 1.3892 + // As expected, fileName is not a path, so proceed with 1.3893 + // a uri. 1.3894 + rv = NS_NewURI(getter_AddRefs(loadInfo.mBaseURI), 1.3895 + fileName.get()); 1.3896 + } 1.3897 + if (NS_FAILED(rv)) { 1.3898 + return rv; 1.3899 + } 1.3900 + } 1.3901 + loadInfo.mXHRParamsAllowed = true; 1.3902 + } 1.3903 + 1.3904 + MOZ_ASSERT(loadInfo.mPrincipal); 1.3905 + MOZ_ASSERT(isChrome || !loadInfo.mDomain.IsEmpty()); 1.3906 + 1.3907 + // XXXbent Use subject principal here instead of the one we already have? 1.3908 + nsCOMPtr<nsIPrincipal> subjectPrincipal = ssm->GetCxSubjectPrincipal(aCx); 1.3909 + MOZ_ASSERT(subjectPrincipal); 1.3910 + 1.3911 + if (!nsContentUtils::GetContentSecurityPolicy(aCx, 1.3912 + getter_AddRefs(loadInfo.mCSP))) { 1.3913 + NS_WARNING("Failed to get CSP!"); 1.3914 + return NS_ERROR_FAILURE; 1.3915 + } 1.3916 + 1.3917 + if (loadInfo.mCSP) { 1.3918 + rv = loadInfo.mCSP->GetAllowsEval(&loadInfo.mReportCSPViolations, 1.3919 + &loadInfo.mEvalAllowed); 1.3920 + NS_ENSURE_SUCCESS(rv, rv); 1.3921 + } else { 1.3922 + loadInfo.mEvalAllowed = true; 1.3923 + loadInfo.mReportCSPViolations = false; 1.3924 + } 1.3925 + 1.3926 + rv = ChannelFromScriptURLMainThread(loadInfo.mPrincipal, loadInfo.mBaseURI, 1.3927 + document, aScriptURL, 1.3928 + getter_AddRefs(loadInfo.mChannel)); 1.3929 + NS_ENSURE_SUCCESS(rv, rv); 1.3930 + 1.3931 + rv = NS_GetFinalChannelURI(loadInfo.mChannel, 1.3932 + getter_AddRefs(loadInfo.mResolvedScriptURI)); 1.3933 + NS_ENSURE_SUCCESS(rv, rv); 1.3934 + } 1.3935 + 1.3936 + aLoadInfo->StealFrom(loadInfo); 1.3937 + return NS_OK; 1.3938 +} 1.3939 + 1.3940 +void 1.3941 +WorkerPrivate::DoRunLoop(JSContext* aCx) 1.3942 +{ 1.3943 + AssertIsOnWorkerThread(); 1.3944 + MOZ_ASSERT(mThread); 1.3945 + 1.3946 + { 1.3947 + MutexAutoLock lock(mMutex); 1.3948 + mJSContext = aCx; 1.3949 + 1.3950 + MOZ_ASSERT(mStatus == Pending); 1.3951 + mStatus = Running; 1.3952 + } 1.3953 + 1.3954 + EnableMemoryReporter(); 1.3955 + 1.3956 + InitializeGCTimers(); 1.3957 + 1.3958 + Maybe<JSAutoCompartment> workerCompartment; 1.3959 + 1.3960 + for (;;) { 1.3961 + // Workers lazily create a global object in CompileScriptRunnable. We need 1.3962 + // to enter the global's compartment as soon as it has been created. 1.3963 + if (workerCompartment.empty()) { 1.3964 + if (JSObject* global = js::DefaultObjectForContextOrNull(aCx)) { 1.3965 + workerCompartment.construct(aCx, global); 1.3966 + } 1.3967 + } 1.3968 + 1.3969 + Status currentStatus; 1.3970 + bool normalRunnablesPending = false; 1.3971 + 1.3972 + { 1.3973 + MutexAutoLock lock(mMutex); 1.3974 + 1.3975 + while (mControlQueue.IsEmpty() && 1.3976 + !(normalRunnablesPending = NS_HasPendingEvents(mThread))) { 1.3977 + WaitForWorkerEvents(); 1.3978 + } 1.3979 + 1.3980 + ProcessAllControlRunnablesLocked(); 1.3981 + 1.3982 + currentStatus = mStatus; 1.3983 + } 1.3984 + 1.3985 + // If the close handler has finished and all features are done then we can 1.3986 + // kill this thread. 1.3987 + if (currentStatus != Running && !HasActiveFeatures()) { 1.3988 + if (mCloseHandlerFinished && currentStatus != Killing) { 1.3989 + if (!NotifyInternal(aCx, Killing)) { 1.3990 + JS_ReportPendingException(aCx); 1.3991 + } 1.3992 +#ifdef DEBUG 1.3993 + { 1.3994 + MutexAutoLock lock(mMutex); 1.3995 + currentStatus = mStatus; 1.3996 + } 1.3997 + MOZ_ASSERT(currentStatus == Killing); 1.3998 +#else 1.3999 + currentStatus = Killing; 1.4000 +#endif 1.4001 + } 1.4002 + 1.4003 + // If we're supposed to die then we should exit the loop. 1.4004 + if (currentStatus == Killing) { 1.4005 + ShutdownGCTimers(); 1.4006 + 1.4007 + DisableMemoryReporter(); 1.4008 + 1.4009 + { 1.4010 + MutexAutoLock lock(mMutex); 1.4011 + 1.4012 + mStatus = Dead; 1.4013 + mJSContext = nullptr; 1.4014 + } 1.4015 + 1.4016 + // After mStatus is set to Dead there can be no more 1.4017 + // WorkerControlRunnables so no need to lock here. 1.4018 + if (!mControlQueue.IsEmpty()) { 1.4019 + WorkerControlRunnable* runnable; 1.4020 + while (mControlQueue.Pop(runnable)) { 1.4021 + runnable->Cancel(); 1.4022 + runnable->Release(); 1.4023 + } 1.4024 + } 1.4025 + 1.4026 + // Clear away our MessagePorts. 1.4027 + mWorkerPorts.Clear(); 1.4028 + 1.4029 + // Unroot the global 1.4030 + mScope = nullptr; 1.4031 + 1.4032 + return; 1.4033 + } 1.4034 + } 1.4035 + 1.4036 + // Nothing to do here if we don't have any runnables in the main queue. 1.4037 + if (!normalRunnablesPending) { 1.4038 + SetGCTimerMode(IdleTimer); 1.4039 + continue; 1.4040 + } 1.4041 + 1.4042 + MOZ_ASSERT(NS_HasPendingEvents(mThread)); 1.4043 + 1.4044 + // Start the periodic GC timer if it is not already running. 1.4045 + SetGCTimerMode(PeriodicTimer); 1.4046 + 1.4047 + // Process a single runnable from the main queue. 1.4048 + MOZ_ALWAYS_TRUE(NS_ProcessNextEvent(mThread, false)); 1.4049 + 1.4050 + if (NS_HasPendingEvents(mThread)) { 1.4051 + // Now *might* be a good time to GC. Let the JS engine make the decision. 1.4052 + if (!workerCompartment.empty()) { 1.4053 + JS_MaybeGC(aCx); 1.4054 + } 1.4055 + } 1.4056 + else { 1.4057 + // The normal event queue has been exhausted, cancel the periodic GC timer 1.4058 + // and schedule the idle GC timer. 1.4059 + SetGCTimerMode(IdleTimer); 1.4060 + } 1.4061 + } 1.4062 + 1.4063 + MOZ_ASSUME_UNREACHABLE("Shouldn't get here!"); 1.4064 +} 1.4065 + 1.4066 +void 1.4067 +WorkerPrivate::OnProcessNextEvent(uint32_t aRecursionDepth) 1.4068 +{ 1.4069 + AssertIsOnWorkerThread(); 1.4070 + MOZ_ASSERT(aRecursionDepth); 1.4071 + 1.4072 + // Normally we process control runnables in DoRunLoop or RunCurrentSyncLoop. 1.4073 + // However, it's possible that non-worker C++ could spin its own nested event 1.4074 + // loop, and in that case we must ensure that we continue to process control 1.4075 + // runnables here. 1.4076 + if (aRecursionDepth > 1 && 1.4077 + mSyncLoopStack.Length() < aRecursionDepth - 1) { 1.4078 + ProcessAllControlRunnables(); 1.4079 + } 1.4080 +} 1.4081 + 1.4082 +void 1.4083 +WorkerPrivate::AfterProcessNextEvent(uint32_t aRecursionDepth) 1.4084 +{ 1.4085 + AssertIsOnWorkerThread(); 1.4086 + MOZ_ASSERT(aRecursionDepth); 1.4087 +} 1.4088 + 1.4089 +void 1.4090 +WorkerPrivate::InitializeGCTimers() 1.4091 +{ 1.4092 + AssertIsOnWorkerThread(); 1.4093 + 1.4094 + // We need a timer for GC. The basic plan is to run a non-shrinking GC 1.4095 + // periodically (PERIODIC_GC_TIMER_DELAY_SEC) while the worker is running. 1.4096 + // Once the worker goes idle we set a short (IDLE_GC_TIMER_DELAY_SEC) timer to 1.4097 + // run a shrinking GC. If the worker receives more messages then the short 1.4098 + // timer is canceled and the periodic timer resumes. 1.4099 + mGCTimer = do_CreateInstance(NS_TIMER_CONTRACTID); 1.4100 + MOZ_ASSERT(mGCTimer); 1.4101 + 1.4102 + nsRefPtr<GarbageCollectRunnable> runnable = 1.4103 + new GarbageCollectRunnable(this, false, false); 1.4104 + mPeriodicGCTimerTarget = new TimerThreadEventTarget(this, runnable); 1.4105 + 1.4106 + runnable = new GarbageCollectRunnable(this, true, false); 1.4107 + mIdleGCTimerTarget = new TimerThreadEventTarget(this, runnable); 1.4108 + 1.4109 + mPeriodicGCTimerRunning = false; 1.4110 + mIdleGCTimerRunning = false; 1.4111 +} 1.4112 + 1.4113 +void 1.4114 +WorkerPrivate::SetGCTimerMode(GCTimerMode aMode) 1.4115 +{ 1.4116 + AssertIsOnWorkerThread(); 1.4117 + MOZ_ASSERT(mGCTimer); 1.4118 + MOZ_ASSERT(mPeriodicGCTimerTarget); 1.4119 + MOZ_ASSERT(mIdleGCTimerTarget); 1.4120 + 1.4121 + if ((aMode == PeriodicTimer && mPeriodicGCTimerRunning) || 1.4122 + (aMode == IdleTimer && mIdleGCTimerRunning)) { 1.4123 + return; 1.4124 + } 1.4125 + 1.4126 + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mGCTimer->Cancel())); 1.4127 + 1.4128 + mPeriodicGCTimerRunning = false; 1.4129 + mIdleGCTimerRunning = false; 1.4130 + 1.4131 + LOG(("Worker %p canceled GC timer because %s\n", this, 1.4132 + aMode == PeriodicTimer ? 1.4133 + "periodic" : 1.4134 + aMode == IdleTimer ? "idle" : "none")); 1.4135 + 1.4136 + if (aMode == NoTimer) { 1.4137 + return; 1.4138 + } 1.4139 + 1.4140 + MOZ_ASSERT(aMode == PeriodicTimer || aMode == IdleTimer); 1.4141 + 1.4142 + nsIEventTarget* target; 1.4143 + uint32_t delay; 1.4144 + int16_t type; 1.4145 + 1.4146 + if (aMode == PeriodicTimer) { 1.4147 + target = mPeriodicGCTimerTarget; 1.4148 + delay = PERIODIC_GC_TIMER_DELAY_SEC * 1000; 1.4149 + type = nsITimer::TYPE_REPEATING_SLACK; 1.4150 + } 1.4151 + else { 1.4152 + target = mIdleGCTimerTarget; 1.4153 + delay = IDLE_GC_TIMER_DELAY_SEC * 1000; 1.4154 + type = nsITimer::TYPE_ONE_SHOT; 1.4155 + } 1.4156 + 1.4157 + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mGCTimer->SetTarget(target))); 1.4158 + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mGCTimer->InitWithFuncCallback(DummyCallback, 1.4159 + nullptr, delay, 1.4160 + type))); 1.4161 + 1.4162 + if (aMode == PeriodicTimer) { 1.4163 + LOG(("Worker %p scheduled periodic GC timer\n", this)); 1.4164 + mPeriodicGCTimerRunning = true; 1.4165 + } 1.4166 + else { 1.4167 + LOG(("Worker %p scheduled idle GC timer\n", this)); 1.4168 + mIdleGCTimerRunning = true; 1.4169 + } 1.4170 +} 1.4171 + 1.4172 +void 1.4173 +WorkerPrivate::ShutdownGCTimers() 1.4174 +{ 1.4175 + AssertIsOnWorkerThread(); 1.4176 + 1.4177 + MOZ_ASSERT(mGCTimer); 1.4178 + 1.4179 + // Always make sure the timer is canceled. 1.4180 + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mGCTimer->Cancel())); 1.4181 + 1.4182 + LOG(("Worker %p killed the GC timer\n", this)); 1.4183 + 1.4184 + mGCTimer = nullptr; 1.4185 + mPeriodicGCTimerTarget = nullptr; 1.4186 + mIdleGCTimerTarget = nullptr; 1.4187 + mPeriodicGCTimerRunning = false; 1.4188 + mIdleGCTimerRunning = false; 1.4189 +} 1.4190 + 1.4191 +bool 1.4192 +WorkerPrivate::InterruptCallback(JSContext* aCx) 1.4193 +{ 1.4194 + AssertIsOnWorkerThread(); 1.4195 + 1.4196 + bool mayContinue = true; 1.4197 + bool scheduledIdleGC = false; 1.4198 + 1.4199 + for (;;) { 1.4200 + // Run all control events now. 1.4201 + mayContinue = ProcessAllControlRunnables(); 1.4202 + 1.4203 + bool maySuspend = mSuspended; 1.4204 + if (maySuspend) { 1.4205 + MutexAutoLock lock(mMutex); 1.4206 + maySuspend = mStatus <= Running; 1.4207 + } 1.4208 + 1.4209 + if (!mayContinue || !maySuspend) { 1.4210 + break; 1.4211 + } 1.4212 + 1.4213 + // Cancel the periodic GC timer here before suspending. The idle GC timer 1.4214 + // will clean everything up once it runs. 1.4215 + if (!scheduledIdleGC) { 1.4216 + SetGCTimerMode(IdleTimer); 1.4217 + scheduledIdleGC = true; 1.4218 + } 1.4219 + 1.4220 + while ((mayContinue = MayContinueRunning())) { 1.4221 + MutexAutoLock lock(mMutex); 1.4222 + if (!mControlQueue.IsEmpty()) { 1.4223 + break; 1.4224 + } 1.4225 + 1.4226 + WaitForWorkerEvents(PR_MillisecondsToInterval(RemainingRunTimeMS())); 1.4227 + } 1.4228 + } 1.4229 + 1.4230 + if (!mayContinue) { 1.4231 + // We want only uncatchable exceptions here. 1.4232 + NS_ASSERTION(!JS_IsExceptionPending(aCx), 1.4233 + "Should not have an exception set here!"); 1.4234 + return false; 1.4235 + } 1.4236 + 1.4237 + // Make sure the periodic timer gets turned back on here. 1.4238 + SetGCTimerMode(PeriodicTimer); 1.4239 + 1.4240 + return true; 1.4241 +} 1.4242 + 1.4243 +nsresult 1.4244 +WorkerPrivate::IsOnCurrentThread(bool* aIsOnCurrentThread) 1.4245 +{ 1.4246 + // May be called on any thread! 1.4247 + 1.4248 + MOZ_ASSERT(aIsOnCurrentThread); 1.4249 + 1.4250 + nsCOMPtr<nsIThread> thread; 1.4251 + { 1.4252 + MutexAutoLock lock(mMutex); 1.4253 + thread = mThread; 1.4254 + } 1.4255 + 1.4256 + if (!thread) { 1.4257 + NS_WARNING("Trying to test thread correctness after the worker has " 1.4258 + "released its thread!"); 1.4259 + return NS_ERROR_FAILURE; 1.4260 + } 1.4261 + 1.4262 + nsresult rv = thread->IsOnCurrentThread(aIsOnCurrentThread); 1.4263 + if (NS_WARN_IF(NS_FAILED(rv))) { 1.4264 + return rv; 1.4265 + } 1.4266 + 1.4267 + return NS_OK; 1.4268 +} 1.4269 + 1.4270 +void 1.4271 +WorkerPrivate::ScheduleDeletion(WorkerRanOrNot aRanOrNot) 1.4272 +{ 1.4273 + AssertIsOnWorkerThread(); 1.4274 + MOZ_ASSERT(mChildWorkers.IsEmpty()); 1.4275 + MOZ_ASSERT(mSyncLoopStack.IsEmpty()); 1.4276 + 1.4277 + ClearMainEventQueue(aRanOrNot); 1.4278 + 1.4279 + if (WorkerPrivate* parent = GetParent()) { 1.4280 + nsRefPtr<WorkerFinishedRunnable> runnable = 1.4281 + new WorkerFinishedRunnable(parent, this); 1.4282 + if (!runnable->Dispatch(nullptr)) { 1.4283 + NS_WARNING("Failed to dispatch runnable!"); 1.4284 + } 1.4285 + } 1.4286 + else { 1.4287 + nsRefPtr<TopLevelWorkerFinishedRunnable> runnable = 1.4288 + new TopLevelWorkerFinishedRunnable(this); 1.4289 + if (NS_FAILED(NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL))) { 1.4290 + NS_WARNING("Failed to dispatch runnable!"); 1.4291 + } 1.4292 + } 1.4293 +} 1.4294 + 1.4295 +bool 1.4296 +WorkerPrivate::BlockAndCollectRuntimeStats(JS::RuntimeStats* aRtStats) 1.4297 +{ 1.4298 + AssertIsOnMainThread(); 1.4299 + mMutex.AssertCurrentThreadOwns(); 1.4300 + NS_ASSERTION(aRtStats, "Null RuntimeStats!"); 1.4301 + 1.4302 + NS_ASSERTION(!mMemoryReporterRunning, "How can we get reentered here?!"); 1.4303 + 1.4304 + // This signals the worker that it should block itself as soon as possible. 1.4305 + mMemoryReporterRunning = true; 1.4306 + 1.4307 + NS_ASSERTION(mJSContext, "This must never be null!"); 1.4308 + JSRuntime* rt = JS_GetRuntime(mJSContext); 1.4309 + 1.4310 + // If the worker is not already blocked (e.g. waiting for a worker event or 1.4311 + // currently in a ctypes call) then we need to trigger the interrupt 1.4312 + // callback to trap the worker. 1.4313 + if (!mBlockedForMemoryReporter) { 1.4314 + JS_RequestInterruptCallback(rt); 1.4315 + 1.4316 + // Wait until the worker actually blocks. 1.4317 + while (!mBlockedForMemoryReporter) { 1.4318 + mMemoryReportCondVar.Wait(); 1.4319 + } 1.4320 + } 1.4321 + 1.4322 + bool succeeded = false; 1.4323 + 1.4324 + // If mMemoryReporter is still set then we can do the actual report. Otherwise 1.4325 + // we're trying to shut down and we don't want to do anything but clean up. 1.4326 + if (mMemoryReporter) { 1.4327 + // Don't hold the lock while doing the actual report. 1.4328 + MutexAutoUnlock unlock(mMutex); 1.4329 + succeeded = JS::CollectRuntimeStats(rt, aRtStats, nullptr); 1.4330 + } 1.4331 + 1.4332 + NS_ASSERTION(mMemoryReporterRunning, "This isn't possible!"); 1.4333 + NS_ASSERTION(mBlockedForMemoryReporter, "Somehow we got unblocked!"); 1.4334 + 1.4335 + // Tell the worker that it can now continue its execution. 1.4336 + mMemoryReporterRunning = false; 1.4337 + 1.4338 + // The worker may be waiting so we must notify. 1.4339 + mMemoryReportCondVar.Notify(); 1.4340 + 1.4341 + return succeeded; 1.4342 +} 1.4343 + 1.4344 +void 1.4345 +WorkerPrivate::EnableMemoryReporter() 1.4346 +{ 1.4347 + AssertIsOnWorkerThread(); 1.4348 + MOZ_ASSERT(!mMemoryReporter); 1.4349 + 1.4350 + // No need to lock here since the main thread can't race until we've 1.4351 + // successfully registered the reporter. 1.4352 + mMemoryReporter = new MemoryReporter(this); 1.4353 + 1.4354 + if (NS_FAILED(RegisterWeakMemoryReporter(mMemoryReporter))) { 1.4355 + NS_WARNING("Failed to register memory reporter!"); 1.4356 + // No need to lock here since a failed registration means our memory 1.4357 + // reporter can't start running. Just clean up. 1.4358 + mMemoryReporter = nullptr; 1.4359 + } 1.4360 +} 1.4361 + 1.4362 +void 1.4363 +WorkerPrivate::DisableMemoryReporter() 1.4364 +{ 1.4365 + AssertIsOnWorkerThread(); 1.4366 + 1.4367 + nsRefPtr<MemoryReporter> memoryReporter; 1.4368 + { 1.4369 + MutexAutoLock lock(mMutex); 1.4370 + 1.4371 + // There is nothing to do here if the memory reporter was never successfully 1.4372 + // registered. 1.4373 + if (!mMemoryReporter) { 1.4374 + return; 1.4375 + } 1.4376 + 1.4377 + // We don't need this set any longer. Swap it out so that we can unregister 1.4378 + // below. 1.4379 + mMemoryReporter.swap(memoryReporter); 1.4380 + 1.4381 + // Next disable the memory reporter so that the main thread stops trying to 1.4382 + // signal us. 1.4383 + memoryReporter->Disable(); 1.4384 + 1.4385 + // If the memory reporter is waiting to start then we need to wait for it to 1.4386 + // finish. 1.4387 + if (mMemoryReporterRunning) { 1.4388 + NS_ASSERTION(!mBlockedForMemoryReporter, 1.4389 + "Can't be blocked in more than one place at the same time!"); 1.4390 + mBlockedForMemoryReporter = true; 1.4391 + 1.4392 + // Tell the main thread that we're blocked. 1.4393 + mMemoryReportCondVar.Notify(); 1.4394 + 1.4395 + // Wait for it the main thread to finish. Since we swapped out 1.4396 + // mMemoryReporter above the main thread should respond quickly. 1.4397 + while (mMemoryReporterRunning) { 1.4398 + mMemoryReportCondVar.Wait(); 1.4399 + } 1.4400 + 1.4401 + NS_ASSERTION(mBlockedForMemoryReporter, "Somehow we got unblocked!"); 1.4402 + mBlockedForMemoryReporter = false; 1.4403 + } 1.4404 + } 1.4405 + 1.4406 + // Finally unregister the memory reporter. 1.4407 + if (NS_FAILED(UnregisterWeakMemoryReporter(memoryReporter))) { 1.4408 + NS_WARNING("Failed to unregister memory reporter!"); 1.4409 + } 1.4410 +} 1.4411 + 1.4412 +void 1.4413 +WorkerPrivate::WaitForWorkerEvents(PRIntervalTime aInterval) 1.4414 +{ 1.4415 + AssertIsOnWorkerThread(); 1.4416 + mMutex.AssertCurrentThreadOwns(); 1.4417 + 1.4418 + NS_ASSERTION(!mBlockedForMemoryReporter, 1.4419 + "Can't be blocked in more than one place at the same time!"); 1.4420 + 1.4421 + // Let the main thread know that the worker is blocked and that memory 1.4422 + // reporting may proceed. 1.4423 + mBlockedForMemoryReporter = true; 1.4424 + 1.4425 + // The main thread may be waiting so we must notify. 1.4426 + mMemoryReportCondVar.Notify(); 1.4427 + 1.4428 + // Now wait for an actual worker event. 1.4429 + mCondVar.Wait(aInterval); 1.4430 + 1.4431 + // We've gotten some kind of signal but we can't continue until the memory 1.4432 + // reporter has finished. Wait again. 1.4433 + while (mMemoryReporterRunning) { 1.4434 + mMemoryReportCondVar.Wait(); 1.4435 + } 1.4436 + 1.4437 + NS_ASSERTION(mBlockedForMemoryReporter, "Somehow we got unblocked!"); 1.4438 + 1.4439 + // No need to notify here as the main thread isn't watching for this state. 1.4440 + mBlockedForMemoryReporter = false; 1.4441 +} 1.4442 + 1.4443 +bool 1.4444 +WorkerPrivate::ProcessAllControlRunnablesLocked() 1.4445 +{ 1.4446 + AssertIsOnWorkerThread(); 1.4447 + mMutex.AssertCurrentThreadOwns(); 1.4448 + 1.4449 + bool result = true; 1.4450 + 1.4451 + for (;;) { 1.4452 + // Block here if the memory reporter is trying to run. 1.4453 + if (mMemoryReporterRunning) { 1.4454 + MOZ_ASSERT(!mBlockedForMemoryReporter); 1.4455 + 1.4456 + // Let the main thread know that we've received the block request and 1.4457 + // that memory reporting may proceed. 1.4458 + mBlockedForMemoryReporter = true; 1.4459 + 1.4460 + // The main thread is almost certainly waiting so we must notify here. 1.4461 + mMemoryReportCondVar.Notify(); 1.4462 + 1.4463 + // Wait for the memory report to finish. 1.4464 + while (mMemoryReporterRunning) { 1.4465 + mMemoryReportCondVar.Wait(); 1.4466 + } 1.4467 + 1.4468 + MOZ_ASSERT(mBlockedForMemoryReporter); 1.4469 + 1.4470 + // No need to notify here as the main thread isn't watching for this 1.4471 + // state. 1.4472 + mBlockedForMemoryReporter = false; 1.4473 + } 1.4474 + 1.4475 + WorkerControlRunnable* event; 1.4476 + if (!mControlQueue.Pop(event)) { 1.4477 + break; 1.4478 + } 1.4479 + 1.4480 + MutexAutoUnlock unlock(mMutex); 1.4481 + 1.4482 + MOZ_ASSERT(event); 1.4483 + if (NS_FAILED(static_cast<nsIRunnable*>(event)->Run())) { 1.4484 + result = false; 1.4485 + } 1.4486 + 1.4487 + event->Release(); 1.4488 + } 1.4489 + 1.4490 + return result; 1.4491 +} 1.4492 + 1.4493 +void 1.4494 +WorkerPrivate::ClearMainEventQueue(WorkerRanOrNot aRanOrNot) 1.4495 +{ 1.4496 + AssertIsOnWorkerThread(); 1.4497 + 1.4498 + MOZ_ASSERT(!mCancelAllPendingRunnables); 1.4499 + mCancelAllPendingRunnables = true; 1.4500 + 1.4501 + if (WorkerNeverRan == aRanOrNot) { 1.4502 + for (uint32_t count = mPreStartRunnables.Length(), index = 0; 1.4503 + index < count; 1.4504 + index++) { 1.4505 + nsRefPtr<WorkerRunnable> runnable = mPreStartRunnables[index].forget(); 1.4506 + static_cast<nsIRunnable*>(runnable.get())->Run(); 1.4507 + } 1.4508 + } else { 1.4509 + nsIThread* currentThread = NS_GetCurrentThread(); 1.4510 + MOZ_ASSERT(currentThread); 1.4511 + 1.4512 + NS_ProcessPendingEvents(currentThread); 1.4513 + MOZ_ASSERT(!NS_HasPendingEvents(currentThread)); 1.4514 + } 1.4515 + 1.4516 + MOZ_ASSERT(mCancelAllPendingRunnables); 1.4517 + mCancelAllPendingRunnables = false; 1.4518 +} 1.4519 + 1.4520 +uint32_t 1.4521 +WorkerPrivate::RemainingRunTimeMS() const 1.4522 +{ 1.4523 + if (mKillTime.IsNull()) { 1.4524 + return UINT32_MAX; 1.4525 + } 1.4526 + TimeDuration runtime = mKillTime - TimeStamp::Now(); 1.4527 + double ms = runtime > TimeDuration(0) ? runtime.ToMilliseconds() : 0; 1.4528 + return ms > double(UINT32_MAX) ? UINT32_MAX : uint32_t(ms); 1.4529 +} 1.4530 + 1.4531 +bool 1.4532 +WorkerPrivate::SuspendInternal(JSContext* aCx) 1.4533 +{ 1.4534 + AssertIsOnWorkerThread(); 1.4535 + 1.4536 + NS_ASSERTION(!mSuspended, "Already suspended!"); 1.4537 + 1.4538 + mSuspended = true; 1.4539 + return true; 1.4540 +} 1.4541 + 1.4542 +bool 1.4543 +WorkerPrivate::ResumeInternal(JSContext* aCx) 1.4544 +{ 1.4545 + AssertIsOnWorkerThread(); 1.4546 + 1.4547 + NS_ASSERTION(mSuspended, "Not yet suspended!"); 1.4548 + 1.4549 + mSuspended = false; 1.4550 + return true; 1.4551 +} 1.4552 + 1.4553 +void 1.4554 +WorkerPrivate::TraceTimeouts(const TraceCallbacks& aCallbacks, 1.4555 + void* aClosure) const 1.4556 +{ 1.4557 + AssertIsOnWorkerThread(); 1.4558 + 1.4559 + for (uint32_t index = 0; index < mTimeouts.Length(); index++) { 1.4560 + TimeoutInfo* info = mTimeouts[index]; 1.4561 + 1.4562 + if (info->mTimeoutCallable.isUndefined()) { 1.4563 + continue; 1.4564 + } 1.4565 + 1.4566 + aCallbacks.Trace(&info->mTimeoutCallable, "mTimeoutCallable", aClosure); 1.4567 + for (uint32_t index2 = 0; index2 < info->mExtraArgVals.Length(); index2++) { 1.4568 + aCallbacks.Trace(&info->mExtraArgVals[index2], "mExtraArgVals[i]", aClosure); 1.4569 + } 1.4570 + } 1.4571 +} 1.4572 + 1.4573 +bool 1.4574 +WorkerPrivate::ModifyBusyCountFromWorker(JSContext* aCx, bool aIncrease) 1.4575 +{ 1.4576 + AssertIsOnWorkerThread(); 1.4577 + 1.4578 + { 1.4579 + MutexAutoLock lock(mMutex); 1.4580 + 1.4581 + // If we're in shutdown then the busy count is no longer being considered so 1.4582 + // just return now. 1.4583 + if (mStatus >= Killing) { 1.4584 + return true; 1.4585 + } 1.4586 + } 1.4587 + 1.4588 + nsRefPtr<ModifyBusyCountRunnable> runnable = 1.4589 + new ModifyBusyCountRunnable(this, aIncrease); 1.4590 + return runnable->Dispatch(aCx); 1.4591 +} 1.4592 + 1.4593 +bool 1.4594 +WorkerPrivate::AddChildWorker(JSContext* aCx, ParentType* aChildWorker) 1.4595 +{ 1.4596 + AssertIsOnWorkerThread(); 1.4597 + 1.4598 +#ifdef DEBUG 1.4599 + { 1.4600 + Status currentStatus; 1.4601 + { 1.4602 + MutexAutoLock lock(mMutex); 1.4603 + currentStatus = mStatus; 1.4604 + } 1.4605 + 1.4606 + MOZ_ASSERT(currentStatus == Running); 1.4607 + } 1.4608 +#endif 1.4609 + 1.4610 + NS_ASSERTION(!mChildWorkers.Contains(aChildWorker), 1.4611 + "Already know about this one!"); 1.4612 + mChildWorkers.AppendElement(aChildWorker); 1.4613 + 1.4614 + return mChildWorkers.Length() == 1 ? 1.4615 + ModifyBusyCountFromWorker(aCx, true) : 1.4616 + true; 1.4617 +} 1.4618 + 1.4619 +void 1.4620 +WorkerPrivate::RemoveChildWorker(JSContext* aCx, ParentType* aChildWorker) 1.4621 +{ 1.4622 + AssertIsOnWorkerThread(); 1.4623 + 1.4624 + NS_ASSERTION(mChildWorkers.Contains(aChildWorker), 1.4625 + "Didn't know about this one!"); 1.4626 + mChildWorkers.RemoveElement(aChildWorker); 1.4627 + 1.4628 + if (mChildWorkers.IsEmpty() && !ModifyBusyCountFromWorker(aCx, false)) { 1.4629 + NS_WARNING("Failed to modify busy count!"); 1.4630 + } 1.4631 +} 1.4632 + 1.4633 +bool 1.4634 +WorkerPrivate::AddFeature(JSContext* aCx, WorkerFeature* aFeature) 1.4635 +{ 1.4636 + AssertIsOnWorkerThread(); 1.4637 + 1.4638 + { 1.4639 + MutexAutoLock lock(mMutex); 1.4640 + 1.4641 + if (mStatus >= Canceling) { 1.4642 + return false; 1.4643 + } 1.4644 + } 1.4645 + 1.4646 + NS_ASSERTION(!mFeatures.Contains(aFeature), "Already know about this one!"); 1.4647 + mFeatures.AppendElement(aFeature); 1.4648 + 1.4649 + return mFeatures.Length() == 1 ? 1.4650 + ModifyBusyCountFromWorker(aCx, true) : 1.4651 + true; 1.4652 +} 1.4653 + 1.4654 +void 1.4655 +WorkerPrivate::RemoveFeature(JSContext* aCx, WorkerFeature* aFeature) 1.4656 +{ 1.4657 + AssertIsOnWorkerThread(); 1.4658 + 1.4659 + NS_ASSERTION(mFeatures.Contains(aFeature), "Didn't know about this one!"); 1.4660 + mFeatures.RemoveElement(aFeature); 1.4661 + 1.4662 + if (mFeatures.IsEmpty() && !ModifyBusyCountFromWorker(aCx, false)) { 1.4663 + NS_WARNING("Failed to modify busy count!"); 1.4664 + } 1.4665 +} 1.4666 + 1.4667 +void 1.4668 +WorkerPrivate::NotifyFeatures(JSContext* aCx, Status aStatus) 1.4669 +{ 1.4670 + AssertIsOnWorkerThread(); 1.4671 + 1.4672 + NS_ASSERTION(aStatus > Running, "Bad status!"); 1.4673 + 1.4674 + if (aStatus >= Closing) { 1.4675 + CancelAllTimeouts(aCx); 1.4676 + } 1.4677 + 1.4678 + nsAutoTArray<WorkerFeature*, 30> features; 1.4679 + features.AppendElements(mFeatures); 1.4680 + 1.4681 + for (uint32_t index = 0; index < features.Length(); index++) { 1.4682 + if (!features[index]->Notify(aCx, aStatus)) { 1.4683 + NS_WARNING("Failed to notify feature!"); 1.4684 + } 1.4685 + } 1.4686 + 1.4687 + nsAutoTArray<ParentType*, 10> children; 1.4688 + children.AppendElements(mChildWorkers); 1.4689 + 1.4690 + for (uint32_t index = 0; index < children.Length(); index++) { 1.4691 + if (!children[index]->Notify(aCx, aStatus)) { 1.4692 + NS_WARNING("Failed to notify child worker!"); 1.4693 + } 1.4694 + } 1.4695 +} 1.4696 + 1.4697 +void 1.4698 +WorkerPrivate::CancelAllTimeouts(JSContext* aCx) 1.4699 +{ 1.4700 + AssertIsOnWorkerThread(); 1.4701 + 1.4702 + if (mTimerRunning) { 1.4703 + NS_ASSERTION(mTimer, "Huh?!"); 1.4704 + NS_ASSERTION(!mTimeouts.IsEmpty(), "Huh?!"); 1.4705 + 1.4706 + if (NS_FAILED(mTimer->Cancel())) { 1.4707 + NS_WARNING("Failed to cancel timer!"); 1.4708 + } 1.4709 + 1.4710 + for (uint32_t index = 0; index < mTimeouts.Length(); index++) { 1.4711 + mTimeouts[index]->mCanceled = true; 1.4712 + } 1.4713 + 1.4714 + if (!RunExpiredTimeouts(aCx)) { 1.4715 + JS_ReportPendingException(aCx); 1.4716 + } 1.4717 + 1.4718 + mTimerRunning = false; 1.4719 + } 1.4720 +#ifdef DEBUG 1.4721 + else if (!mRunningExpiredTimeouts) { 1.4722 + NS_ASSERTION(mTimeouts.IsEmpty(), "Huh?!"); 1.4723 + } 1.4724 +#endif 1.4725 + 1.4726 + mTimer = nullptr; 1.4727 +} 1.4728 + 1.4729 +already_AddRefed<nsIEventTarget> 1.4730 +WorkerPrivate::CreateNewSyncLoop() 1.4731 +{ 1.4732 + AssertIsOnWorkerThread(); 1.4733 + 1.4734 + nsCOMPtr<nsIThreadInternal> thread = do_QueryInterface(NS_GetCurrentThread()); 1.4735 + MOZ_ASSERT(thread); 1.4736 + 1.4737 + nsCOMPtr<nsIEventTarget> realEventTarget; 1.4738 + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(thread->PushEventQueue( 1.4739 + getter_AddRefs(realEventTarget)))); 1.4740 + 1.4741 + nsRefPtr<EventTarget> workerEventTarget = 1.4742 + new EventTarget(this, realEventTarget); 1.4743 + 1.4744 + { 1.4745 + // Modifications must be protected by mMutex in DEBUG builds, see comment 1.4746 + // about mSyncLoopStack in WorkerPrivate.h. 1.4747 +#ifdef DEBUG 1.4748 + MutexAutoLock lock(mMutex); 1.4749 +#endif 1.4750 + 1.4751 + mSyncLoopStack.AppendElement(new SyncLoopInfo(workerEventTarget)); 1.4752 + } 1.4753 + 1.4754 + return workerEventTarget.forget(); 1.4755 +} 1.4756 + 1.4757 +bool 1.4758 +WorkerPrivate::RunCurrentSyncLoop() 1.4759 +{ 1.4760 + AssertIsOnWorkerThread(); 1.4761 + 1.4762 + JSContext* cx = GetJSContext(); 1.4763 + MOZ_ASSERT(cx); 1.4764 + 1.4765 + // This should not change between now and the time we finish running this sync 1.4766 + // loop. 1.4767 + uint32_t currentLoopIndex = mSyncLoopStack.Length() - 1; 1.4768 + 1.4769 + SyncLoopInfo* loopInfo = mSyncLoopStack[currentLoopIndex]; 1.4770 + 1.4771 + MOZ_ASSERT(loopInfo); 1.4772 + MOZ_ASSERT(!loopInfo->mHasRun); 1.4773 + MOZ_ASSERT(!loopInfo->mCompleted); 1.4774 + 1.4775 +#ifdef DEBUG 1.4776 + loopInfo->mHasRun = true; 1.4777 +#endif 1.4778 + 1.4779 + nsCOMPtr<nsIThreadInternal> thread = do_QueryInterface(mThread); 1.4780 + MOZ_ASSERT(thread); 1.4781 + 1.4782 + while (!loopInfo->mCompleted) { 1.4783 + bool normalRunnablesPending = false; 1.4784 + 1.4785 + // Don't block with the periodic GC timer running. 1.4786 + if (!NS_HasPendingEvents(thread)) { 1.4787 + SetGCTimerMode(IdleTimer); 1.4788 + } 1.4789 + 1.4790 + // Wait for something to do. 1.4791 + { 1.4792 + MutexAutoLock lock(mMutex); 1.4793 + 1.4794 + for (;;) { 1.4795 + while (mControlQueue.IsEmpty() && 1.4796 + !normalRunnablesPending && 1.4797 + !(normalRunnablesPending = NS_HasPendingEvents(thread))) { 1.4798 + WaitForWorkerEvents(); 1.4799 + } 1.4800 + 1.4801 + ProcessAllControlRunnablesLocked(); 1.4802 + 1.4803 + // NB: If we processed a NotifyRunnable, we might have run non-control 1.4804 + // runnables, one of which may have shut down the sync loop. 1.4805 + if (normalRunnablesPending || loopInfo->mCompleted) { 1.4806 + break; 1.4807 + } 1.4808 + } 1.4809 + } 1.4810 + 1.4811 + if (normalRunnablesPending) { 1.4812 + // Make sure the periodic timer is running before we continue. 1.4813 + SetGCTimerMode(PeriodicTimer); 1.4814 + 1.4815 + MOZ_ALWAYS_TRUE(NS_ProcessNextEvent(thread, false)); 1.4816 + 1.4817 + // Now *might* be a good time to GC. Let the JS engine make the decision. 1.4818 + JS_MaybeGC(cx); 1.4819 + } 1.4820 + } 1.4821 + 1.4822 + // Make sure that the stack didn't change underneath us. 1.4823 + MOZ_ASSERT(mSyncLoopStack[currentLoopIndex] == loopInfo); 1.4824 + 1.4825 + return DestroySyncLoop(currentLoopIndex); 1.4826 +} 1.4827 + 1.4828 +bool 1.4829 +WorkerPrivate::DestroySyncLoop(uint32_t aLoopIndex, nsIThreadInternal* aThread) 1.4830 +{ 1.4831 + MOZ_ASSERT(!mSyncLoopStack.IsEmpty()); 1.4832 + MOZ_ASSERT(mSyncLoopStack.Length() - 1 == aLoopIndex); 1.4833 + 1.4834 + if (!aThread) { 1.4835 + nsCOMPtr<nsIThreadInternal> thread = do_QueryInterface(mThread); 1.4836 + MOZ_ASSERT(thread); 1.4837 + 1.4838 + aThread = thread.get(); 1.4839 + } 1.4840 + 1.4841 + // We're about to delete the loop, stash its event target and result. 1.4842 + SyncLoopInfo* loopInfo = mSyncLoopStack[aLoopIndex]; 1.4843 + nsIEventTarget* nestedEventTarget = 1.4844 + loopInfo->mEventTarget->GetWeakNestedEventTarget(); 1.4845 + MOZ_ASSERT(nestedEventTarget); 1.4846 + 1.4847 + bool result = loopInfo->mResult; 1.4848 + 1.4849 + { 1.4850 + // Modifications must be protected by mMutex in DEBUG builds, see comment 1.4851 + // about mSyncLoopStack in WorkerPrivate.h. 1.4852 +#ifdef DEBUG 1.4853 + MutexAutoLock lock(mMutex); 1.4854 +#endif 1.4855 + 1.4856 + // This will delete |loopInfo|! 1.4857 + mSyncLoopStack.RemoveElementAt(aLoopIndex); 1.4858 + } 1.4859 + 1.4860 + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(aThread->PopEventQueue(nestedEventTarget))); 1.4861 + 1.4862 + return result; 1.4863 +} 1.4864 + 1.4865 +void 1.4866 +WorkerPrivate::StopSyncLoop(nsIEventTarget* aSyncLoopTarget, bool aResult) 1.4867 +{ 1.4868 + AssertIsOnWorkerThread(); 1.4869 + AssertValidSyncLoop(aSyncLoopTarget); 1.4870 + 1.4871 + MOZ_ASSERT(!mSyncLoopStack.IsEmpty()); 1.4872 + 1.4873 + for (uint32_t index = mSyncLoopStack.Length(); index > 0; index--) { 1.4874 + nsAutoPtr<SyncLoopInfo>& loopInfo = mSyncLoopStack[index - 1]; 1.4875 + MOZ_ASSERT(loopInfo); 1.4876 + MOZ_ASSERT(loopInfo->mEventTarget); 1.4877 + 1.4878 + if (loopInfo->mEventTarget == aSyncLoopTarget) { 1.4879 + // Can't assert |loop->mHasRun| here because dispatch failures can cause 1.4880 + // us to bail out early. 1.4881 + MOZ_ASSERT(!loopInfo->mCompleted); 1.4882 + 1.4883 + loopInfo->mResult = aResult; 1.4884 + loopInfo->mCompleted = true; 1.4885 + 1.4886 + loopInfo->mEventTarget->Disable(); 1.4887 + 1.4888 + return; 1.4889 + } 1.4890 + 1.4891 + MOZ_ASSERT(!SameCOMIdentity(loopInfo->mEventTarget, aSyncLoopTarget)); 1.4892 + } 1.4893 + 1.4894 + MOZ_CRASH("Unknown sync loop!"); 1.4895 +} 1.4896 + 1.4897 +#ifdef DEBUG 1.4898 +void 1.4899 +WorkerPrivate::AssertValidSyncLoop(nsIEventTarget* aSyncLoopTarget) 1.4900 +{ 1.4901 + MOZ_ASSERT(aSyncLoopTarget); 1.4902 + 1.4903 + EventTarget* workerTarget; 1.4904 + nsresult rv = 1.4905 + aSyncLoopTarget->QueryInterface(kDEBUGWorkerEventTargetIID, 1.4906 + reinterpret_cast<void**>(&workerTarget)); 1.4907 + MOZ_ASSERT(NS_SUCCEEDED(rv)); 1.4908 + MOZ_ASSERT(workerTarget); 1.4909 + 1.4910 + bool valid = false; 1.4911 + 1.4912 + { 1.4913 + MutexAutoLock lock(mMutex); 1.4914 + 1.4915 + for (uint32_t index = 0; index < mSyncLoopStack.Length(); index++) { 1.4916 + nsAutoPtr<SyncLoopInfo>& loopInfo = mSyncLoopStack[index]; 1.4917 + MOZ_ASSERT(loopInfo); 1.4918 + MOZ_ASSERT(loopInfo->mEventTarget); 1.4919 + 1.4920 + if (loopInfo->mEventTarget == aSyncLoopTarget) { 1.4921 + valid = true; 1.4922 + break; 1.4923 + } 1.4924 + 1.4925 + MOZ_ASSERT(!SameCOMIdentity(loopInfo->mEventTarget, aSyncLoopTarget)); 1.4926 + } 1.4927 + } 1.4928 + 1.4929 + MOZ_ASSERT(valid); 1.4930 +} 1.4931 +#endif 1.4932 + 1.4933 +void 1.4934 +WorkerPrivate::PostMessageToParentInternal( 1.4935 + JSContext* aCx, 1.4936 + JS::Handle<JS::Value> aMessage, 1.4937 + const Optional<Sequence<JS::Value>>& aTransferable, 1.4938 + bool aToMessagePort, 1.4939 + uint64_t aMessagePortSerial, 1.4940 + ErrorResult& aRv) 1.4941 +{ 1.4942 + AssertIsOnWorkerThread(); 1.4943 + 1.4944 + JS::Rooted<JS::Value> transferable(aCx, JS::UndefinedValue()); 1.4945 + if (aTransferable.WasPassed()) { 1.4946 + const Sequence<JS::Value>& realTransferable = aTransferable.Value(); 1.4947 + 1.4948 + // The input sequence only comes from the generated bindings code, which 1.4949 + // ensures it is rooted. 1.4950 + JS::HandleValueArray elements = 1.4951 + JS::HandleValueArray::fromMarkedLocation(realTransferable.Length(), 1.4952 + realTransferable.Elements()); 1.4953 + 1.4954 + JSObject* array = JS_NewArrayObject(aCx, elements); 1.4955 + if (!array) { 1.4956 + aRv = NS_ERROR_OUT_OF_MEMORY; 1.4957 + return; 1.4958 + } 1.4959 + transferable.setObject(*array); 1.4960 + } 1.4961 + 1.4962 + JSStructuredCloneCallbacks* callbacks = 1.4963 + IsChromeWorker() ? 1.4964 + &gChromeWorkerStructuredCloneCallbacks : 1.4965 + &gWorkerStructuredCloneCallbacks; 1.4966 + 1.4967 + nsTArray<nsCOMPtr<nsISupports>> clonedObjects; 1.4968 + 1.4969 + JSAutoStructuredCloneBuffer buffer; 1.4970 + if (!buffer.write(aCx, aMessage, transferable, callbacks, &clonedObjects)) { 1.4971 + aRv = NS_ERROR_DOM_DATA_CLONE_ERR; 1.4972 + return; 1.4973 + } 1.4974 + 1.4975 + nsRefPtr<MessageEventRunnable> runnable = 1.4976 + new MessageEventRunnable(this, 1.4977 + WorkerRunnable::ParentThreadUnchangedBusyCount, 1.4978 + Move(buffer), clonedObjects, aToMessagePort, 1.4979 + aMessagePortSerial); 1.4980 + if (!runnable->Dispatch(aCx)) { 1.4981 + aRv = NS_ERROR_FAILURE; 1.4982 + } 1.4983 +} 1.4984 + 1.4985 +void 1.4986 +WorkerPrivate::PostMessageToParentMessagePort( 1.4987 + JSContext* aCx, 1.4988 + uint64_t aMessagePortSerial, 1.4989 + JS::Handle<JS::Value> aMessage, 1.4990 + const Optional<Sequence<JS::Value>>& aTransferable, 1.4991 + ErrorResult& aRv) 1.4992 +{ 1.4993 + AssertIsOnWorkerThread(); 1.4994 + 1.4995 + if (!mWorkerPorts.GetWeak(aMessagePortSerial)) { 1.4996 + // This port has been closed from the main thread. There's no point in 1.4997 + // sending this message so just bail. 1.4998 + return; 1.4999 + } 1.5000 + 1.5001 + PostMessageToParentInternal(aCx, aMessage, aTransferable, true, 1.5002 + aMessagePortSerial, aRv); 1.5003 +} 1.5004 + 1.5005 +bool 1.5006 +WorkerPrivate::NotifyInternal(JSContext* aCx, Status aStatus) 1.5007 +{ 1.5008 + AssertIsOnWorkerThread(); 1.5009 + 1.5010 + NS_ASSERTION(aStatus > Running && aStatus < Dead, "Bad status!"); 1.5011 + 1.5012 + nsRefPtr<EventTarget> eventTarget; 1.5013 + 1.5014 + // Save the old status and set the new status. 1.5015 + Status previousStatus; 1.5016 + { 1.5017 + MutexAutoLock lock(mMutex); 1.5018 + 1.5019 + if (mStatus >= aStatus) { 1.5020 + MOZ_ASSERT(!mEventTarget); 1.5021 + return true; 1.5022 + } 1.5023 + 1.5024 + previousStatus = mStatus; 1.5025 + mStatus = aStatus; 1.5026 + 1.5027 + mEventTarget.swap(eventTarget); 1.5028 + } 1.5029 + 1.5030 + // Now that mStatus > Running, no-one can create a new WorkerEventTarget or 1.5031 + // WorkerCrossThreadDispatcher if we don't already have one. 1.5032 + if (eventTarget) { 1.5033 + // Since we'll no longer process events, make sure we no longer allow anyone 1.5034 + // to post them. We have to do this without mMutex held, since our mutex 1.5035 + // must be acquired *after* the WorkerEventTarget's mutex when they're both 1.5036 + // held. 1.5037 + eventTarget->Disable(); 1.5038 + eventTarget = nullptr; 1.5039 + } 1.5040 + 1.5041 + if (mCrossThreadDispatcher) { 1.5042 + // Since we'll no longer process events, make sure we no longer allow 1.5043 + // anyone to post them. We have to do this without mMutex held, since our 1.5044 + // mutex must be acquired *after* mCrossThreadDispatcher's mutex when 1.5045 + // they're both held. 1.5046 + mCrossThreadDispatcher->Forget(); 1.5047 + mCrossThreadDispatcher = nullptr; 1.5048 + } 1.5049 + 1.5050 + MOZ_ASSERT(previousStatus != Pending); 1.5051 + 1.5052 + MOZ_ASSERT(previousStatus >= Canceling || mKillTime.IsNull()); 1.5053 + 1.5054 + // Let all our features know the new status. 1.5055 + NotifyFeatures(aCx, aStatus); 1.5056 + 1.5057 + // If this is the first time our status has changed then we need to clear the 1.5058 + // main event queue. 1.5059 + if (previousStatus == Running) { 1.5060 + ClearMainEventQueue(WorkerRan); 1.5061 + } 1.5062 + 1.5063 + // If we've run the close handler, we don't need to do anything else. 1.5064 + if (mCloseHandlerFinished) { 1.5065 + return true; 1.5066 + } 1.5067 + 1.5068 + // If the worker script never ran, or failed to compile, we don't need to do 1.5069 + // anything else, except pretend that we ran the close handler. 1.5070 + if (!JS::CurrentGlobalOrNull(aCx)) { 1.5071 + mCloseHandlerStarted = true; 1.5072 + mCloseHandlerFinished = true; 1.5073 + return true; 1.5074 + } 1.5075 + 1.5076 + // If this is the first time our status has changed we also need to schedule 1.5077 + // the close handler unless we're being shut down. 1.5078 + if (previousStatus == Running && aStatus != Killing) { 1.5079 + MOZ_ASSERT(!mCloseHandlerStarted && !mCloseHandlerFinished); 1.5080 + 1.5081 + nsRefPtr<CloseEventRunnable> closeRunnable = new CloseEventRunnable(this); 1.5082 + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToCurrentThread(closeRunnable))); 1.5083 + } 1.5084 + 1.5085 + if (aStatus == Closing) { 1.5086 + // Notify parent to stop sending us messages and balance our busy count. 1.5087 + nsRefPtr<CloseRunnable> runnable = new CloseRunnable(this); 1.5088 + if (!runnable->Dispatch(aCx)) { 1.5089 + return false; 1.5090 + } 1.5091 + 1.5092 + // Don't abort the script. 1.5093 + return true; 1.5094 + } 1.5095 + 1.5096 + if (aStatus == Terminating) { 1.5097 + // Only abort the script if we're not yet running the close handler. 1.5098 + return mCloseHandlerStarted; 1.5099 + } 1.5100 + 1.5101 + if (aStatus == Canceling) { 1.5102 + // We need to enforce a timeout on the close handler. 1.5103 + MOZ_ASSERT(previousStatus >= Running && previousStatus <= Terminating); 1.5104 + 1.5105 + uint32_t killSeconds = IsChromeWorker() ? 1.5106 + RuntimeService::GetChromeCloseHandlerTimeoutSeconds() : 1.5107 + RuntimeService::GetContentCloseHandlerTimeoutSeconds(); 1.5108 + 1.5109 + if (killSeconds) { 1.5110 + mKillTime = TimeStamp::Now() + TimeDuration::FromSeconds(killSeconds); 1.5111 + 1.5112 + if (!mCloseHandlerFinished && !ScheduleKillCloseEventRunnable(aCx)) { 1.5113 + return false; 1.5114 + } 1.5115 + } 1.5116 + 1.5117 + // Only abort the script if we're not yet running the close handler. 1.5118 + return mCloseHandlerStarted; 1.5119 + } 1.5120 + 1.5121 + MOZ_ASSERT(aStatus == Killing); 1.5122 + 1.5123 + mKillTime = TimeStamp::Now(); 1.5124 + 1.5125 + if (mCloseHandlerStarted && !mCloseHandlerFinished) { 1.5126 + ScheduleKillCloseEventRunnable(aCx); 1.5127 + } 1.5128 + 1.5129 + // Always abort the script. 1.5130 + return false; 1.5131 +} 1.5132 + 1.5133 +bool 1.5134 +WorkerPrivate::ScheduleKillCloseEventRunnable(JSContext* aCx) 1.5135 +{ 1.5136 + AssertIsOnWorkerThread(); 1.5137 + MOZ_ASSERT(!mKillTime.IsNull()); 1.5138 + 1.5139 + nsRefPtr<KillCloseEventRunnable> killCloseEventRunnable = 1.5140 + new KillCloseEventRunnable(this); 1.5141 + if (!killCloseEventRunnable->SetTimeout(aCx, RemainingRunTimeMS())) { 1.5142 + return false; 1.5143 + } 1.5144 + 1.5145 + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToCurrentThread( 1.5146 + killCloseEventRunnable))); 1.5147 + 1.5148 + return true; 1.5149 +} 1.5150 + 1.5151 +void 1.5152 +WorkerPrivate::ReportError(JSContext* aCx, const char* aMessage, 1.5153 + JSErrorReport* aReport) 1.5154 +{ 1.5155 + AssertIsOnWorkerThread(); 1.5156 + 1.5157 + if (!MayContinueRunning() || mErrorHandlerRecursionCount == 2) { 1.5158 + return; 1.5159 + } 1.5160 + 1.5161 + NS_ASSERTION(mErrorHandlerRecursionCount == 0 || 1.5162 + mErrorHandlerRecursionCount == 1, 1.5163 + "Bad recursion logic!"); 1.5164 + 1.5165 + JS_ClearPendingException(aCx); 1.5166 + 1.5167 + nsString message, filename, line; 1.5168 + uint32_t lineNumber, columnNumber, flags, errorNumber; 1.5169 + 1.5170 + if (aReport) { 1.5171 + // ErrorEvent objects don't have a |name| field the way ES |Error| objects 1.5172 + // do. Traditionally (and mostly by accident), the |message| field of 1.5173 + // ErrorEvent has corresponded to |Name: Message| of the original Error 1.5174 + // object. Things have been cleaned up in the JS engine, so now we need to 1.5175 + // format this string explicitly. 1.5176 + JS::Rooted<JSString*> messageStr(aCx, 1.5177 + js::ErrorReportToString(aCx, aReport)); 1.5178 + if (messageStr) { 1.5179 + nsDependentJSString depStr; 1.5180 + if (depStr.init(aCx, messageStr)) { 1.5181 + message = depStr; 1.5182 + } 1.5183 + } 1.5184 + filename = NS_ConvertUTF8toUTF16(aReport->filename); 1.5185 + line = aReport->uclinebuf; 1.5186 + lineNumber = aReport->lineno; 1.5187 + columnNumber = aReport->uctokenptr - aReport->uclinebuf; 1.5188 + flags = aReport->flags; 1.5189 + errorNumber = aReport->errorNumber; 1.5190 + } 1.5191 + else { 1.5192 + lineNumber = columnNumber = errorNumber = 0; 1.5193 + flags = nsIScriptError::errorFlag | nsIScriptError::exceptionFlag; 1.5194 + } 1.5195 + 1.5196 + if (message.IsEmpty()) { 1.5197 + message = NS_ConvertUTF8toUTF16(aMessage); 1.5198 + } 1.5199 + 1.5200 + mErrorHandlerRecursionCount++; 1.5201 + 1.5202 + // Don't want to run the scope's error handler if this is a recursive error or 1.5203 + // if there was an error in the close handler or if we ran out of memory. 1.5204 + bool fireAtScope = mErrorHandlerRecursionCount == 1 && 1.5205 + !mCloseHandlerStarted && 1.5206 + errorNumber != JSMSG_OUT_OF_MEMORY; 1.5207 + 1.5208 + if (!ReportErrorRunnable::ReportError(aCx, this, fireAtScope, nullptr, message, 1.5209 + filename, line, lineNumber, 1.5210 + columnNumber, flags, errorNumber, 0)) { 1.5211 + JS_ReportPendingException(aCx); 1.5212 + } 1.5213 + 1.5214 + mErrorHandlerRecursionCount--; 1.5215 +} 1.5216 + 1.5217 +int32_t 1.5218 +WorkerPrivate::SetTimeout(JSContext* aCx, 1.5219 + Function* aHandler, 1.5220 + const nsAString& aStringHandler, 1.5221 + int32_t aTimeout, 1.5222 + const Sequence<JS::Value>& aArguments, 1.5223 + bool aIsInterval, 1.5224 + ErrorResult& aRv) 1.5225 +{ 1.5226 + AssertIsOnWorkerThread(); 1.5227 + 1.5228 + const int32_t timerId = mNextTimeoutId++; 1.5229 + 1.5230 + Status currentStatus; 1.5231 + { 1.5232 + MutexAutoLock lock(mMutex); 1.5233 + currentStatus = mStatus; 1.5234 + } 1.5235 + 1.5236 + // It's a script bug if setTimeout/setInterval are called from a close handler 1.5237 + // so throw an exception. 1.5238 + if (currentStatus == Closing) { 1.5239 + JS_ReportError(aCx, "Cannot schedule timeouts from the close handler!"); 1.5240 + } 1.5241 + 1.5242 + // If the worker is trying to call setTimeout/setInterval and the parent 1.5243 + // thread has initiated the close process then just silently fail. 1.5244 + if (currentStatus >= Closing) { 1.5245 + aRv.Throw(NS_ERROR_FAILURE); 1.5246 + return 0; 1.5247 + } 1.5248 + 1.5249 + nsAutoPtr<TimeoutInfo> newInfo(new TimeoutInfo()); 1.5250 + newInfo->mIsInterval = aIsInterval; 1.5251 + newInfo->mId = timerId; 1.5252 + 1.5253 + if (MOZ_UNLIKELY(timerId == INT32_MAX)) { 1.5254 + NS_WARNING("Timeout ids overflowed!"); 1.5255 + mNextTimeoutId = 1; 1.5256 + } 1.5257 + 1.5258 + // Take care of the main argument. 1.5259 + if (aHandler) { 1.5260 + newInfo->mTimeoutCallable = JS::ObjectValue(*aHandler->Callable()); 1.5261 + } 1.5262 + else if (!aStringHandler.IsEmpty()) { 1.5263 + newInfo->mTimeoutString = aStringHandler; 1.5264 + } 1.5265 + else { 1.5266 + JS_ReportError(aCx, "Useless %s call (missing quotes around argument?)", 1.5267 + aIsInterval ? "setInterval" : "setTimeout"); 1.5268 + return 0; 1.5269 + } 1.5270 + 1.5271 + // See if any of the optional arguments were passed. 1.5272 + aTimeout = std::max(0, aTimeout); 1.5273 + newInfo->mInterval = TimeDuration::FromMilliseconds(aTimeout); 1.5274 + 1.5275 + uint32_t argc = aArguments.Length(); 1.5276 + if (argc && !newInfo->mTimeoutCallable.isUndefined()) { 1.5277 + nsTArray<JS::Heap<JS::Value>> extraArgVals(argc); 1.5278 + for (uint32_t index = 0; index < argc; index++) { 1.5279 + extraArgVals.AppendElement(aArguments[index]); 1.5280 + } 1.5281 + newInfo->mExtraArgVals.SwapElements(extraArgVals); 1.5282 + } 1.5283 + 1.5284 + newInfo->mTargetTime = TimeStamp::Now() + newInfo->mInterval; 1.5285 + 1.5286 + if (!newInfo->mTimeoutString.IsEmpty()) { 1.5287 + const char* filenameChars; 1.5288 + uint32_t lineNumber; 1.5289 + if (nsJSUtils::GetCallingLocation(aCx, &filenameChars, &lineNumber)) { 1.5290 + newInfo->mFilename = filenameChars; 1.5291 + newInfo->mLineNumber = lineNumber; 1.5292 + } 1.5293 + else { 1.5294 + NS_WARNING("Failed to get calling location!"); 1.5295 + } 1.5296 + } 1.5297 + 1.5298 + nsAutoPtr<TimeoutInfo>* insertedInfo = 1.5299 + mTimeouts.InsertElementSorted(newInfo.forget(), GetAutoPtrComparator(mTimeouts)); 1.5300 + 1.5301 + // If the timeout we just made is set to fire next then we need to update the 1.5302 + // timer. 1.5303 + if (insertedInfo == mTimeouts.Elements()) { 1.5304 + nsresult rv; 1.5305 + 1.5306 + if (!mTimer) { 1.5307 + nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv); 1.5308 + if (NS_FAILED(rv)) { 1.5309 + aRv.Throw(rv); 1.5310 + return 0; 1.5311 + } 1.5312 + 1.5313 + nsRefPtr<TimerRunnable> runnable = new TimerRunnable(this); 1.5314 + 1.5315 + nsRefPtr<TimerThreadEventTarget> target = 1.5316 + new TimerThreadEventTarget(this, runnable); 1.5317 + 1.5318 + rv = timer->SetTarget(target); 1.5319 + if (NS_FAILED(rv)) { 1.5320 + aRv.Throw(rv); 1.5321 + return 0; 1.5322 + } 1.5323 + 1.5324 + timer.swap(mTimer); 1.5325 + } 1.5326 + 1.5327 + if (!mTimerRunning) { 1.5328 + if (!ModifyBusyCountFromWorker(aCx, true)) { 1.5329 + aRv.Throw(NS_ERROR_FAILURE); 1.5330 + return 0; 1.5331 + } 1.5332 + mTimerRunning = true; 1.5333 + } 1.5334 + 1.5335 + if (!RescheduleTimeoutTimer(aCx)) { 1.5336 + aRv.Throw(NS_ERROR_FAILURE); 1.5337 + return 0; 1.5338 + } 1.5339 + } 1.5340 + 1.5341 + return timerId; 1.5342 +} 1.5343 + 1.5344 +void 1.5345 +WorkerPrivate::ClearTimeout(int32_t aId) 1.5346 +{ 1.5347 + AssertIsOnWorkerThread(); 1.5348 + 1.5349 + if (!mTimeouts.IsEmpty()) { 1.5350 + NS_ASSERTION(mTimerRunning, "Huh?!"); 1.5351 + 1.5352 + for (uint32_t index = 0; index < mTimeouts.Length(); index++) { 1.5353 + nsAutoPtr<TimeoutInfo>& info = mTimeouts[index]; 1.5354 + if (info->mId == aId) { 1.5355 + info->mCanceled = true; 1.5356 + break; 1.5357 + } 1.5358 + } 1.5359 + } 1.5360 +} 1.5361 + 1.5362 +bool 1.5363 +WorkerPrivate::RunExpiredTimeouts(JSContext* aCx) 1.5364 +{ 1.5365 + AssertIsOnWorkerThread(); 1.5366 + 1.5367 + // We may be called recursively (e.g. close() inside a timeout) or we could 1.5368 + // have been canceled while this event was pending, bail out if there is 1.5369 + // nothing to do. 1.5370 + if (mRunningExpiredTimeouts || !mTimerRunning) { 1.5371 + return true; 1.5372 + } 1.5373 + 1.5374 + NS_ASSERTION(mTimer, "Must have a timer!"); 1.5375 + NS_ASSERTION(!mTimeouts.IsEmpty(), "Should have some work to do!"); 1.5376 + 1.5377 + bool retval = true; 1.5378 + 1.5379 + AutoPtrComparator<TimeoutInfo> comparator = GetAutoPtrComparator(mTimeouts); 1.5380 + JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx)); 1.5381 + 1.5382 + // We want to make sure to run *something*, even if the timer fired a little 1.5383 + // early. Fudge the value of now to at least include the first timeout. 1.5384 + const TimeStamp now = std::max(TimeStamp::Now(), mTimeouts[0]->mTargetTime); 1.5385 + 1.5386 + nsAutoTArray<TimeoutInfo*, 10> expiredTimeouts; 1.5387 + for (uint32_t index = 0; index < mTimeouts.Length(); index++) { 1.5388 + nsAutoPtr<TimeoutInfo>& info = mTimeouts[index]; 1.5389 + if (info->mTargetTime > now) { 1.5390 + break; 1.5391 + } 1.5392 + expiredTimeouts.AppendElement(info); 1.5393 + } 1.5394 + 1.5395 + // Guard against recursion. 1.5396 + mRunningExpiredTimeouts = true; 1.5397 + 1.5398 + // Run expired timeouts. 1.5399 + for (uint32_t index = 0; index < expiredTimeouts.Length(); index++) { 1.5400 + TimeoutInfo*& info = expiredTimeouts[index]; 1.5401 + 1.5402 + if (info->mCanceled) { 1.5403 + continue; 1.5404 + } 1.5405 + 1.5406 + // Always call JS_ReportPendingException if something fails, and if 1.5407 + // JS_ReportPendingException returns false (i.e. uncatchable exception) then 1.5408 + // break out of the loop. 1.5409 + 1.5410 + if (!info->mTimeoutCallable.isUndefined()) { 1.5411 + JS::Rooted<JS::Value> rval(aCx); 1.5412 + JS::HandleValueArray args = 1.5413 + JS::HandleValueArray::fromMarkedLocation(info->mExtraArgVals.Length(), 1.5414 + info->mExtraArgVals.Elements()->address()); 1.5415 + JS::Rooted<JS::Value> callable(aCx, info->mTimeoutCallable); 1.5416 + if (!JS_CallFunctionValue(aCx, global, callable, args, &rval) && 1.5417 + !JS_ReportPendingException(aCx)) { 1.5418 + retval = false; 1.5419 + break; 1.5420 + } 1.5421 + } 1.5422 + else { 1.5423 + nsString expression = info->mTimeoutString; 1.5424 + 1.5425 + JS::CompileOptions options(aCx); 1.5426 + options.setFileAndLine(info->mFilename.get(), info->mLineNumber); 1.5427 + 1.5428 + if ((expression.IsEmpty() || 1.5429 + !JS::Evaluate(aCx, global, options, expression.get(), expression.Length())) && 1.5430 + !JS_ReportPendingException(aCx)) { 1.5431 + retval = false; 1.5432 + break; 1.5433 + } 1.5434 + } 1.5435 + 1.5436 + NS_ASSERTION(mRunningExpiredTimeouts, "Someone changed this!"); 1.5437 + } 1.5438 + 1.5439 + // No longer possible to be called recursively. 1.5440 + mRunningExpiredTimeouts = false; 1.5441 + 1.5442 + // Now remove canceled and expired timeouts from the main list. 1.5443 + // NB: The timeouts present in expiredTimeouts must have the same order 1.5444 + // with respect to each other in mTimeouts. That is, mTimeouts is just 1.5445 + // expiredTimeouts with extra elements inserted. There may be unexpired 1.5446 + // timeouts that have been inserted between the expired timeouts if the 1.5447 + // timeout event handler called setTimeout/setInterval. 1.5448 + for (uint32_t index = 0, expiredTimeoutIndex = 0, 1.5449 + expiredTimeoutLength = expiredTimeouts.Length(); 1.5450 + index < mTimeouts.Length(); ) { 1.5451 + nsAutoPtr<TimeoutInfo>& info = mTimeouts[index]; 1.5452 + if ((expiredTimeoutIndex < expiredTimeoutLength && 1.5453 + info == expiredTimeouts[expiredTimeoutIndex] && 1.5454 + ++expiredTimeoutIndex) || 1.5455 + info->mCanceled) { 1.5456 + if (info->mIsInterval && !info->mCanceled) { 1.5457 + // Reschedule intervals. 1.5458 + info->mTargetTime = info->mTargetTime + info->mInterval; 1.5459 + // Don't resort the list here, we'll do that at the end. 1.5460 + ++index; 1.5461 + } 1.5462 + else { 1.5463 + mTimeouts.RemoveElement(info); 1.5464 + } 1.5465 + } 1.5466 + else { 1.5467 + // If info did not match the current entry in expiredTimeouts, it 1.5468 + // shouldn't be there at all. 1.5469 + NS_ASSERTION(!expiredTimeouts.Contains(info), 1.5470 + "Our timeouts are out of order!"); 1.5471 + ++index; 1.5472 + } 1.5473 + } 1.5474 + 1.5475 + mTimeouts.Sort(comparator); 1.5476 + 1.5477 + // Either signal the parent that we're no longer using timeouts or reschedule 1.5478 + // the timer. 1.5479 + if (mTimeouts.IsEmpty()) { 1.5480 + if (!ModifyBusyCountFromWorker(aCx, false)) { 1.5481 + retval = false; 1.5482 + } 1.5483 + mTimerRunning = false; 1.5484 + } 1.5485 + else if (retval && !RescheduleTimeoutTimer(aCx)) { 1.5486 + retval = false; 1.5487 + } 1.5488 + 1.5489 + return retval; 1.5490 +} 1.5491 + 1.5492 +bool 1.5493 +WorkerPrivate::RescheduleTimeoutTimer(JSContext* aCx) 1.5494 +{ 1.5495 + AssertIsOnWorkerThread(); 1.5496 + NS_ASSERTION(!mTimeouts.IsEmpty(), "Should have some timeouts!"); 1.5497 + NS_ASSERTION(mTimer, "Should have a timer!"); 1.5498 + 1.5499 + double delta = 1.5500 + (mTimeouts[0]->mTargetTime - TimeStamp::Now()).ToMilliseconds(); 1.5501 + uint32_t delay = delta > 0 ? std::min(delta, double(UINT32_MAX)) : 0; 1.5502 + 1.5503 + nsresult rv = mTimer->InitWithFuncCallback(DummyCallback, nullptr, delay, 1.5504 + nsITimer::TYPE_ONE_SHOT); 1.5505 + if (NS_FAILED(rv)) { 1.5506 + JS_ReportError(aCx, "Failed to start timer!"); 1.5507 + return false; 1.5508 + } 1.5509 + 1.5510 + return true; 1.5511 +} 1.5512 + 1.5513 +void 1.5514 +WorkerPrivate::UpdateRuntimeAndContextOptionsInternal( 1.5515 + JSContext* aCx, 1.5516 + const JS::RuntimeOptions& aRuntimeOptions, 1.5517 + const JS::ContextOptions& aContentCxOptions, 1.5518 + const JS::ContextOptions& aChromeCxOptions) 1.5519 +{ 1.5520 + AssertIsOnWorkerThread(); 1.5521 + 1.5522 + JS::RuntimeOptionsRef(aCx) = aRuntimeOptions; 1.5523 + JS::ContextOptionsRef(aCx) = IsChromeWorker() ? aChromeCxOptions : aContentCxOptions; 1.5524 + 1.5525 + for (uint32_t index = 0; index < mChildWorkers.Length(); index++) { 1.5526 + mChildWorkers[index]->UpdateRuntimeAndContextOptions(aCx, aRuntimeOptions, 1.5527 + aContentCxOptions, 1.5528 + aChromeCxOptions); 1.5529 + } 1.5530 +} 1.5531 + 1.5532 +void 1.5533 +WorkerPrivate::UpdatePreferenceInternal(JSContext* aCx, WorkerPreference aPref, bool aValue) 1.5534 +{ 1.5535 + AssertIsOnWorkerThread(); 1.5536 + MOZ_ASSERT(aPref >= 0 && aPref < WORKERPREF_COUNT); 1.5537 + 1.5538 + mPreferences[aPref] = aValue; 1.5539 + 1.5540 + for (uint32_t index = 0; index < mChildWorkers.Length(); index++) { 1.5541 + mChildWorkers[index]->UpdatePreference(aCx, aPref, aValue); 1.5542 + } 1.5543 +} 1.5544 + 1.5545 +void 1.5546 +WorkerPrivate::UpdateJSWorkerMemoryParameterInternal(JSContext* aCx, 1.5547 + JSGCParamKey aKey, 1.5548 + uint32_t aValue) 1.5549 +{ 1.5550 + AssertIsOnWorkerThread(); 1.5551 + 1.5552 + // XXX aValue might be 0 here (telling us to unset a previous value for child 1.5553 + // workers). Calling JS_SetGCParameter with a value of 0 isn't actually 1.5554 + // supported though. We really need some way to revert to a default value 1.5555 + // here. 1.5556 + if (aValue) { 1.5557 + JS_SetGCParameter(JS_GetRuntime(aCx), aKey, aValue); 1.5558 + } 1.5559 + 1.5560 + for (uint32_t index = 0; index < mChildWorkers.Length(); index++) { 1.5561 + mChildWorkers[index]->UpdateJSWorkerMemoryParameter(aCx, aKey, aValue); 1.5562 + } 1.5563 +} 1.5564 + 1.5565 +#ifdef JS_GC_ZEAL 1.5566 +void 1.5567 +WorkerPrivate::UpdateGCZealInternal(JSContext* aCx, uint8_t aGCZeal, 1.5568 + uint32_t aFrequency) 1.5569 +{ 1.5570 + AssertIsOnWorkerThread(); 1.5571 + 1.5572 + JS_SetGCZeal(aCx, aGCZeal, aFrequency); 1.5573 + 1.5574 + for (uint32_t index = 0; index < mChildWorkers.Length(); index++) { 1.5575 + mChildWorkers[index]->UpdateGCZeal(aCx, aGCZeal, aFrequency); 1.5576 + } 1.5577 +} 1.5578 +#endif 1.5579 + 1.5580 +void 1.5581 +WorkerPrivate::GarbageCollectInternal(JSContext* aCx, bool aShrinking, 1.5582 + bool aCollectChildren) 1.5583 +{ 1.5584 + AssertIsOnWorkerThread(); 1.5585 + 1.5586 + if (!JS::CurrentGlobalOrNull(aCx)) { 1.5587 + // We haven't compiled anything yet. Just bail out. 1.5588 + return; 1.5589 + } 1.5590 + 1.5591 + if (aShrinking || aCollectChildren) { 1.5592 + JSRuntime* rt = JS_GetRuntime(aCx); 1.5593 + JS::PrepareForFullGC(rt); 1.5594 + 1.5595 + if (aShrinking) { 1.5596 + JS::ShrinkingGC(rt, JS::gcreason::DOM_WORKER); 1.5597 + 1.5598 + if (!aCollectChildren) { 1.5599 + LOG(("Worker %p collected idle garbage\n", this)); 1.5600 + } 1.5601 + } 1.5602 + else { 1.5603 + JS::GCForReason(rt, JS::gcreason::DOM_WORKER); 1.5604 + LOG(("Worker %p collected garbage\n", this)); 1.5605 + } 1.5606 + } 1.5607 + else { 1.5608 + JS_MaybeGC(aCx); 1.5609 + LOG(("Worker %p collected periodic garbage\n", this)); 1.5610 + } 1.5611 + 1.5612 + if (aCollectChildren) { 1.5613 + for (uint32_t index = 0; index < mChildWorkers.Length(); index++) { 1.5614 + mChildWorkers[index]->GarbageCollect(aCx, aShrinking); 1.5615 + } 1.5616 + } 1.5617 +} 1.5618 + 1.5619 +void 1.5620 +WorkerPrivate::CycleCollectInternal(JSContext* aCx, bool aCollectChildren) 1.5621 +{ 1.5622 + AssertIsOnWorkerThread(); 1.5623 + 1.5624 + nsCycleCollector_collect(nullptr); 1.5625 + 1.5626 + if (aCollectChildren) { 1.5627 + for (uint32_t index = 0; index < mChildWorkers.Length(); index++) { 1.5628 + mChildWorkers[index]->CycleCollect(aCx, /* dummy = */ false); 1.5629 + } 1.5630 + } 1.5631 +} 1.5632 + 1.5633 +void 1.5634 +WorkerPrivate::SetThread(nsIThread* aThread) 1.5635 +{ 1.5636 +#ifdef DEBUG 1.5637 + if (aThread) { 1.5638 + bool isOnCurrentThread; 1.5639 + MOZ_ASSERT(NS_SUCCEEDED(aThread->IsOnCurrentThread(&isOnCurrentThread))); 1.5640 + MOZ_ASSERT(isOnCurrentThread); 1.5641 + 1.5642 + MOZ_ASSERT(!mPRThread); 1.5643 + mPRThread = PRThreadFromThread(aThread); 1.5644 + MOZ_ASSERT(mPRThread); 1.5645 + } 1.5646 + else { 1.5647 + MOZ_ASSERT(mPRThread); 1.5648 + } 1.5649 +#endif 1.5650 + 1.5651 + nsCOMPtr<nsIThread> doomedThread; 1.5652 + 1.5653 + { // Scope so that |doomedThread| is released without holding the lock. 1.5654 + MutexAutoLock lock(mMutex); 1.5655 + 1.5656 + if (aThread) { 1.5657 + MOZ_ASSERT(!mThread); 1.5658 + MOZ_ASSERT(mStatus == Pending); 1.5659 + 1.5660 + mThread = aThread; 1.5661 + 1.5662 + if (!mPreStartRunnables.IsEmpty()) { 1.5663 + for (uint32_t index = 0; index < mPreStartRunnables.Length(); index++) { 1.5664 + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mThread->Dispatch( 1.5665 + mPreStartRunnables[index], 1.5666 + NS_DISPATCH_NORMAL))); 1.5667 + } 1.5668 + mPreStartRunnables.Clear(); 1.5669 + } 1.5670 + } 1.5671 + else { 1.5672 + MOZ_ASSERT(mThread); 1.5673 + mThread.swap(doomedThread); 1.5674 + } 1.5675 + } 1.5676 +} 1.5677 + 1.5678 +WorkerCrossThreadDispatcher* 1.5679 +WorkerPrivate::GetCrossThreadDispatcher() 1.5680 +{ 1.5681 + MutexAutoLock lock(mMutex); 1.5682 + 1.5683 + if (!mCrossThreadDispatcher && mStatus <= Running) { 1.5684 + mCrossThreadDispatcher = new WorkerCrossThreadDispatcher(this); 1.5685 + } 1.5686 + 1.5687 + return mCrossThreadDispatcher; 1.5688 +} 1.5689 + 1.5690 +void 1.5691 +WorkerPrivate::BeginCTypesCall() 1.5692 +{ 1.5693 + AssertIsOnWorkerThread(); 1.5694 + 1.5695 + // Don't try to GC while we're blocked in a ctypes call. 1.5696 + SetGCTimerMode(NoTimer); 1.5697 + 1.5698 + MutexAutoLock lock(mMutex); 1.5699 + 1.5700 + NS_ASSERTION(!mBlockedForMemoryReporter, 1.5701 + "Can't be blocked in more than one place at the same time!"); 1.5702 + 1.5703 + // Let the main thread know that the worker is effectively blocked while in 1.5704 + // this ctypes call. It isn't technically true (obviously the call could do 1.5705 + // non-blocking things), but we're assuming that ctypes can't call back into 1.5706 + // JSAPI here and therefore any work the ctypes call does will not alter the 1.5707 + // data structures of this JS runtime. 1.5708 + mBlockedForMemoryReporter = true; 1.5709 + 1.5710 + // The main thread may be waiting on us so it must be notified. 1.5711 + mMemoryReportCondVar.Notify(); 1.5712 +} 1.5713 + 1.5714 +void 1.5715 +WorkerPrivate::EndCTypesCall() 1.5716 +{ 1.5717 + AssertIsOnWorkerThread(); 1.5718 + 1.5719 + { 1.5720 + MutexAutoLock lock(mMutex); 1.5721 + 1.5722 + NS_ASSERTION(mBlockedForMemoryReporter, "Somehow we got unblocked!"); 1.5723 + 1.5724 + // Don't continue until the memory reporter has finished. 1.5725 + while (mMemoryReporterRunning) { 1.5726 + mMemoryReportCondVar.Wait(); 1.5727 + } 1.5728 + 1.5729 + // No need to notify the main thread here as it shouldn't be waiting to see 1.5730 + // this state. 1.5731 + mBlockedForMemoryReporter = false; 1.5732 + } 1.5733 + 1.5734 + // Make sure the periodic timer is running before we start running JS again. 1.5735 + SetGCTimerMode(PeriodicTimer); 1.5736 +} 1.5737 + 1.5738 +bool 1.5739 +WorkerPrivate::ConnectMessagePort(JSContext* aCx, uint64_t aMessagePortSerial) 1.5740 +{ 1.5741 + AssertIsOnWorkerThread(); 1.5742 + 1.5743 + NS_ASSERTION(!mWorkerPorts.GetWeak(aMessagePortSerial), 1.5744 + "Already have this port registered!"); 1.5745 + 1.5746 + WorkerGlobalScope* globalScope = GlobalScope(); 1.5747 + 1.5748 + JS::Rooted<JSObject*> jsGlobal(aCx, globalScope->GetWrapper()); 1.5749 + MOZ_ASSERT(jsGlobal); 1.5750 + 1.5751 + nsRefPtr<MessagePort> port = new MessagePort(this, aMessagePortSerial); 1.5752 + 1.5753 + GlobalObject globalObject(aCx, jsGlobal); 1.5754 + if (globalObject.Failed()) { 1.5755 + return false; 1.5756 + } 1.5757 + 1.5758 + RootedDictionary<MessageEventInit> init(aCx); 1.5759 + init.mBubbles = false; 1.5760 + init.mCancelable = false; 1.5761 + init.mSource.SetValue().SetAsMessagePort() = port; 1.5762 + 1.5763 + ErrorResult rv; 1.5764 + 1.5765 + nsRefPtr<MessageEvent> event = 1.5766 + MessageEvent::Constructor(globalObject, aCx, 1.5767 + NS_LITERAL_STRING("connect"), init, rv); 1.5768 + 1.5769 + event->SetTrusted(true); 1.5770 + 1.5771 + nsTArray<nsRefPtr<MessagePortBase>> ports; 1.5772 + ports.AppendElement(port); 1.5773 + 1.5774 + nsRefPtr<MessagePortList> portList = 1.5775 + new MessagePortList(static_cast<nsIDOMEventTarget*>(globalScope), ports); 1.5776 + event->SetPorts(portList); 1.5777 + 1.5778 + mWorkerPorts.Put(aMessagePortSerial, port); 1.5779 + 1.5780 + nsCOMPtr<nsIDOMEvent> domEvent = do_QueryObject(event); 1.5781 + 1.5782 + nsEventStatus dummy = nsEventStatus_eIgnore; 1.5783 + globalScope->DispatchDOMEvent(nullptr, domEvent, nullptr, &dummy); 1.5784 + return true; 1.5785 +} 1.5786 + 1.5787 +void 1.5788 +WorkerPrivate::DisconnectMessagePort(uint64_t aMessagePortSerial) 1.5789 +{ 1.5790 + AssertIsOnWorkerThread(); 1.5791 + 1.5792 + mWorkerPorts.Remove(aMessagePortSerial); 1.5793 +} 1.5794 + 1.5795 +workers::MessagePort* 1.5796 +WorkerPrivate::GetMessagePort(uint64_t aMessagePortSerial) 1.5797 +{ 1.5798 + AssertIsOnWorkerThread(); 1.5799 + 1.5800 + nsRefPtr<MessagePort> port; 1.5801 + if (mWorkerPorts.Get(aMessagePortSerial, getter_AddRefs(port))) { 1.5802 + return port; 1.5803 + } 1.5804 + 1.5805 + return nullptr; 1.5806 +} 1.5807 + 1.5808 +JSObject* 1.5809 +WorkerPrivate::CreateGlobalScope(JSContext* aCx) 1.5810 +{ 1.5811 + AssertIsOnWorkerThread(); 1.5812 + 1.5813 + nsRefPtr<WorkerGlobalScope> globalScope; 1.5814 + if (IsSharedWorker()) { 1.5815 + globalScope = new SharedWorkerGlobalScope(this, SharedWorkerName()); 1.5816 + } 1.5817 + else { 1.5818 + globalScope = new DedicatedWorkerGlobalScope(this); 1.5819 + } 1.5820 + 1.5821 + JS::Rooted<JSObject*> global(aCx, globalScope->WrapGlobalObject(aCx)); 1.5822 + NS_ENSURE_TRUE(global, nullptr); 1.5823 + 1.5824 + JSAutoCompartment ac(aCx, global); 1.5825 + 1.5826 + if (!RegisterBindings(aCx, global)) { 1.5827 + return nullptr; 1.5828 + } 1.5829 + 1.5830 + mScope = globalScope.forget(); 1.5831 + 1.5832 + JS_FireOnNewGlobalObject(aCx, global); 1.5833 + 1.5834 + return global; 1.5835 +} 1.5836 + 1.5837 +#ifdef DEBUG 1.5838 + 1.5839 +void 1.5840 +WorkerPrivate::AssertIsOnWorkerThread() const 1.5841 +{ 1.5842 + // This is much more complicated than it needs to be but we can't use mThread 1.5843 + // because it must be protected by mMutex and sometimes this method is called 1.5844 + // when mMutex is already locked. This method should always work. 1.5845 + MOZ_ASSERT(mPRThread, 1.5846 + "AssertIsOnWorkerThread() called before a thread was assigned!"); 1.5847 + 1.5848 + MOZ_ASSERT(nsThreadManager::get()); 1.5849 + 1.5850 + nsCOMPtr<nsIThread> thread; 1.5851 + nsresult rv = 1.5852 + nsThreadManager::get()->GetThreadFromPRThread(mPRThread, 1.5853 + getter_AddRefs(thread)); 1.5854 + MOZ_ASSERT(NS_SUCCEEDED(rv)); 1.5855 + MOZ_ASSERT(thread); 1.5856 + 1.5857 + bool current; 1.5858 + rv = thread->IsOnCurrentThread(¤t); 1.5859 + MOZ_ASSERT(NS_SUCCEEDED(rv)); 1.5860 + MOZ_ASSERT(current, "Wrong thread!"); 1.5861 +} 1.5862 + 1.5863 +#endif // DEBUG 1.5864 + 1.5865 +NS_IMPL_ISUPPORTS_INHERITED0(ExternalRunnableWrapper, WorkerRunnable) 1.5866 + 1.5867 +template <class Derived> 1.5868 +NS_IMPL_ADDREF(WorkerPrivateParent<Derived>::EventTarget) 1.5869 + 1.5870 +template <class Derived> 1.5871 +NS_IMPL_RELEASE(WorkerPrivateParent<Derived>::EventTarget) 1.5872 + 1.5873 +template <class Derived> 1.5874 +NS_INTERFACE_MAP_BEGIN(WorkerPrivateParent<Derived>::EventTarget) 1.5875 + NS_INTERFACE_MAP_ENTRY(nsIEventTarget) 1.5876 + NS_INTERFACE_MAP_ENTRY(nsISupports) 1.5877 +#ifdef DEBUG 1.5878 + // kDEBUGWorkerEventTargetIID is special in that it does not AddRef its 1.5879 + // result. 1.5880 + if (aIID.Equals(kDEBUGWorkerEventTargetIID)) { 1.5881 + *aInstancePtr = this; 1.5882 + return NS_OK; 1.5883 + } 1.5884 + else 1.5885 +#endif 1.5886 +NS_INTERFACE_MAP_END 1.5887 + 1.5888 +template <class Derived> 1.5889 +NS_IMETHODIMP 1.5890 +WorkerPrivateParent<Derived>:: 1.5891 +EventTarget::Dispatch(nsIRunnable* aRunnable, uint32_t aFlags) 1.5892 +{ 1.5893 + // May be called on any thread! 1.5894 + 1.5895 + // Workers only support asynchronous dispatch for now. 1.5896 + if (NS_WARN_IF(aFlags != NS_DISPATCH_NORMAL)) { 1.5897 + return NS_ERROR_UNEXPECTED; 1.5898 + } 1.5899 + 1.5900 + nsRefPtr<WorkerRunnable> workerRunnable; 1.5901 + 1.5902 + MutexAutoLock lock(mMutex); 1.5903 + 1.5904 + if (!mWorkerPrivate) { 1.5905 + NS_WARNING("A runnable was posted to a worker that is already shutting " 1.5906 + "down!"); 1.5907 + return NS_ERROR_UNEXPECTED; 1.5908 + } 1.5909 + 1.5910 + if (aRunnable) { 1.5911 + workerRunnable = mWorkerPrivate->MaybeWrapAsWorkerRunnable(aRunnable); 1.5912 + } 1.5913 + 1.5914 + nsresult rv = 1.5915 + mWorkerPrivate->DispatchPrivate(workerRunnable, mNestedEventTarget); 1.5916 + if (NS_WARN_IF(NS_FAILED(rv))) { 1.5917 + return rv; 1.5918 + } 1.5919 + 1.5920 + return NS_OK; 1.5921 +} 1.5922 + 1.5923 +template <class Derived> 1.5924 +NS_IMETHODIMP 1.5925 +WorkerPrivateParent<Derived>:: 1.5926 +EventTarget::IsOnCurrentThread(bool* aIsOnCurrentThread) 1.5927 +{ 1.5928 + // May be called on any thread! 1.5929 + 1.5930 + MOZ_ASSERT(aIsOnCurrentThread); 1.5931 + 1.5932 + MutexAutoLock lock(mMutex); 1.5933 + 1.5934 + if (!mWorkerPrivate) { 1.5935 + NS_WARNING("A worker's event target was used after the worker has !"); 1.5936 + return NS_ERROR_UNEXPECTED; 1.5937 + } 1.5938 + 1.5939 + nsresult rv = mWorkerPrivate->IsOnCurrentThread(aIsOnCurrentThread); 1.5940 + if (NS_WARN_IF(NS_FAILED(rv))) { 1.5941 + return rv; 1.5942 + } 1.5943 + 1.5944 + return NS_OK; 1.5945 +} 1.5946 + 1.5947 +BEGIN_WORKERS_NAMESPACE 1.5948 + 1.5949 +WorkerCrossThreadDispatcher* 1.5950 +GetWorkerCrossThreadDispatcher(JSContext* aCx, JS::Value aWorker) 1.5951 +{ 1.5952 + if (!aWorker.isObject()) { 1.5953 + return nullptr; 1.5954 + } 1.5955 + 1.5956 + WorkerPrivate* w = nullptr; 1.5957 + UNWRAP_OBJECT(Worker, &aWorker.toObject(), w); 1.5958 + MOZ_ASSERT(w); 1.5959 + return w->GetCrossThreadDispatcher(); 1.5960 +} 1.5961 + 1.5962 +JSStructuredCloneCallbacks* 1.5963 +WorkerStructuredCloneCallbacks(bool aMainRuntime) 1.5964 +{ 1.5965 + return aMainRuntime ? 1.5966 + &gMainThreadWorkerStructuredCloneCallbacks : 1.5967 + &gWorkerStructuredCloneCallbacks; 1.5968 +} 1.5969 + 1.5970 +JSStructuredCloneCallbacks* 1.5971 +ChromeWorkerStructuredCloneCallbacks(bool aMainRuntime) 1.5972 +{ 1.5973 + return aMainRuntime ? 1.5974 + &gMainThreadChromeWorkerStructuredCloneCallbacks : 1.5975 + &gChromeWorkerStructuredCloneCallbacks; 1.5976 +} 1.5977 + 1.5978 +// Force instantiation. 1.5979 +template class WorkerPrivateParent<WorkerPrivate>; 1.5980 + 1.5981 +END_WORKERS_NAMESPACE