dom/workers/WorkerPrivate.cpp

Fri, 16 Jan 2015 18:13:44 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 18:13:44 +0100
branch
TOR_BUG_9701
changeset 14
925c144e1f1f
permissions
-rw-r--r--

Integrate suggestion from review to improve consistency with existing code.

michael@0 1 /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
michael@0 2 /* vim: set ts=2 et sw=2 tw=80: */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #include "WorkerPrivate.h"
michael@0 8
michael@0 9 #include "amIAddonManager.h"
michael@0 10 #include "nsIClassInfo.h"
michael@0 11 #include "nsIContentSecurityPolicy.h"
michael@0 12 #include "nsIConsoleService.h"
michael@0 13 #include "nsIDOMDOMException.h"
michael@0 14 #include "nsIDOMEvent.h"
michael@0 15 #include "nsIDOMFile.h"
michael@0 16 #include "nsIDOMMessageEvent.h"
michael@0 17 #include "nsIDocument.h"
michael@0 18 #include "nsIDocShell.h"
michael@0 19 #include "nsIMemoryReporter.h"
michael@0 20 #include "nsIPermissionManager.h"
michael@0 21 #include "nsIScriptError.h"
michael@0 22 #include "nsIScriptGlobalObject.h"
michael@0 23 #include "nsIScriptSecurityManager.h"
michael@0 24 #include "nsPIDOMWindow.h"
michael@0 25 #include "nsITextToSubURI.h"
michael@0 26 #include "nsIThreadInternal.h"
michael@0 27 #include "nsITimer.h"
michael@0 28 #include "nsIURI.h"
michael@0 29 #include "nsIURL.h"
michael@0 30 #include "nsIXPConnect.h"
michael@0 31
michael@0 32 #include <algorithm>
michael@0 33 #include "jsfriendapi.h"
michael@0 34 #include "js/OldDebugAPI.h"
michael@0 35 #include "js/MemoryMetrics.h"
michael@0 36 #include "mozilla/Assertions.h"
michael@0 37 #include "mozilla/ContentEvents.h"
michael@0 38 #include "mozilla/EventDispatcher.h"
michael@0 39 #include "mozilla/Likely.h"
michael@0 40 #include "mozilla/dom/BindingUtils.h"
michael@0 41 #include "mozilla/dom/ErrorEvent.h"
michael@0 42 #include "mozilla/dom/ErrorEventBinding.h"
michael@0 43 #include "mozilla/dom/Exceptions.h"
michael@0 44 #include "mozilla/dom/FunctionBinding.h"
michael@0 45 #include "mozilla/dom/ImageData.h"
michael@0 46 #include "mozilla/dom/ImageDataBinding.h"
michael@0 47 #include "mozilla/dom/MessageEvent.h"
michael@0 48 #include "mozilla/dom/MessageEventBinding.h"
michael@0 49 #include "mozilla/dom/MessagePortList.h"
michael@0 50 #include "mozilla/dom/WorkerBinding.h"
michael@0 51 #include "mozilla/Preferences.h"
michael@0 52 #include "nsAlgorithm.h"
michael@0 53 #include "nsContentUtils.h"
michael@0 54 #include "nsCxPusher.h"
michael@0 55 #include "nsError.h"
michael@0 56 #include "nsDOMJSUtils.h"
michael@0 57 #include "nsHostObjectProtocolHandler.h"
michael@0 58 #include "nsJSEnvironment.h"
michael@0 59 #include "nsJSUtils.h"
michael@0 60 #include "nsNetUtil.h"
michael@0 61 #include "nsPrintfCString.h"
michael@0 62 #include "nsProxyRelease.h"
michael@0 63 #include "nsSandboxFlags.h"
michael@0 64 #include "xpcpublic.h"
michael@0 65
michael@0 66 #ifdef ANDROID
michael@0 67 #include <android/log.h>
michael@0 68 #endif
michael@0 69
michael@0 70 #ifdef DEBUG
michael@0 71 #include "nsThreadManager.h"
michael@0 72 #endif
michael@0 73
michael@0 74 #include "File.h"
michael@0 75 #include "MessagePort.h"
michael@0 76 #include "Navigator.h"
michael@0 77 #include "Principal.h"
michael@0 78 #include "RuntimeService.h"
michael@0 79 #include "ScriptLoader.h"
michael@0 80 #include "SharedWorker.h"
michael@0 81 #include "WorkerFeature.h"
michael@0 82 #include "WorkerRunnable.h"
michael@0 83 #include "WorkerScope.h"
michael@0 84
michael@0 85 // JS_MaybeGC will run once every second during normal execution.
michael@0 86 #define PERIODIC_GC_TIMER_DELAY_SEC 1
michael@0 87
michael@0 88 // A shrinking GC will run five seconds after the last event is processed.
michael@0 89 #define IDLE_GC_TIMER_DELAY_SEC 5
michael@0 90
michael@0 91 #define PREF_WORKERS_ENABLED "dom.workers.enabled"
michael@0 92
michael@0 93 #ifdef WORKER_LOGGING
michael@0 94 #define LOG(_args) do { printf _args ; fflush(stdout); } while (0)
michael@0 95 #else
michael@0 96 #define LOG(_args) do { } while (0)
michael@0 97 #endif
michael@0 98
michael@0 99 using namespace mozilla;
michael@0 100 using namespace mozilla::dom;
michael@0 101 USING_WORKERS_NAMESPACE
michael@0 102
michael@0 103 MOZ_DEFINE_MALLOC_SIZE_OF(JsWorkerMallocSizeOf)
michael@0 104
michael@0 105 #ifdef DEBUG
michael@0 106
michael@0 107 BEGIN_WORKERS_NAMESPACE
michael@0 108
michael@0 109 void
michael@0 110 AssertIsOnMainThread()
michael@0 111 {
michael@0 112 MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
michael@0 113 }
michael@0 114
michael@0 115 END_WORKERS_NAMESPACE
michael@0 116
michael@0 117 #endif
michael@0 118
michael@0 119 namespace {
michael@0 120
michael@0 121 #ifdef DEBUG
michael@0 122
michael@0 123 const nsIID kDEBUGWorkerEventTargetIID = {
michael@0 124 0xccaba3fa, 0x5be2, 0x4de2, { 0xba, 0x87, 0x3b, 0x3b, 0x5b, 0x1d, 0x5, 0xfb }
michael@0 125 };
michael@0 126
michael@0 127 #endif
michael@0 128
michael@0 129 template <class T>
michael@0 130 class AutoPtrComparator
michael@0 131 {
michael@0 132 typedef nsAutoPtr<T> A;
michael@0 133 typedef T* B;
michael@0 134
michael@0 135 public:
michael@0 136 bool Equals(const A& a, const B& b) const {
michael@0 137 return a && b ? *a == *b : !a && !b ? true : false;
michael@0 138 }
michael@0 139 bool LessThan(const A& a, const B& b) const {
michael@0 140 return a && b ? *a < *b : b ? true : false;
michael@0 141 }
michael@0 142 };
michael@0 143
michael@0 144 template <class T>
michael@0 145 inline AutoPtrComparator<T>
michael@0 146 GetAutoPtrComparator(const nsTArray<nsAutoPtr<T> >&)
michael@0 147 {
michael@0 148 return AutoPtrComparator<T>();
michael@0 149 }
michael@0 150
michael@0 151 // Specialize this if there's some class that has multiple nsISupports bases.
michael@0 152 template <class T>
michael@0 153 struct ISupportsBaseInfo
michael@0 154 {
michael@0 155 typedef T ISupportsBase;
michael@0 156 };
michael@0 157
michael@0 158 template <template <class> class SmartPtr, class T>
michael@0 159 inline void
michael@0 160 SwapToISupportsArray(SmartPtr<T>& aSrc,
michael@0 161 nsTArray<nsCOMPtr<nsISupports> >& aDest)
michael@0 162 {
michael@0 163 nsCOMPtr<nsISupports>* dest = aDest.AppendElement();
michael@0 164
michael@0 165 T* raw = nullptr;
michael@0 166 aSrc.swap(raw);
michael@0 167
michael@0 168 nsISupports* rawSupports =
michael@0 169 static_cast<typename ISupportsBaseInfo<T>::ISupportsBase*>(raw);
michael@0 170 dest->swap(rawSupports);
michael@0 171 }
michael@0 172
michael@0 173 // This class is used to wrap any runnables that the worker receives via the
michael@0 174 // nsIEventTarget::Dispatch() method (either from NS_DispatchToCurrentThread or
michael@0 175 // from the worker's EventTarget).
michael@0 176 class ExternalRunnableWrapper MOZ_FINAL : public WorkerRunnable
michael@0 177 {
michael@0 178 nsCOMPtr<nsICancelableRunnable> mWrappedRunnable;
michael@0 179
michael@0 180 public:
michael@0 181 ExternalRunnableWrapper(WorkerPrivate* aWorkerPrivate,
michael@0 182 nsICancelableRunnable* aWrappedRunnable)
michael@0 183 : WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
michael@0 184 mWrappedRunnable(aWrappedRunnable)
michael@0 185 {
michael@0 186 MOZ_ASSERT(aWorkerPrivate);
michael@0 187 MOZ_ASSERT(aWrappedRunnable);
michael@0 188 }
michael@0 189
michael@0 190 NS_DECL_ISUPPORTS_INHERITED
michael@0 191
michael@0 192 private:
michael@0 193 ~ExternalRunnableWrapper()
michael@0 194 { }
michael@0 195
michael@0 196 virtual bool
michael@0 197 WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
michael@0 198 {
michael@0 199 nsresult rv = mWrappedRunnable->Run();
michael@0 200 if (NS_FAILED(rv)) {
michael@0 201 if (!JS_IsExceptionPending(aCx)) {
michael@0 202 Throw(aCx, rv);
michael@0 203 }
michael@0 204 return false;
michael@0 205 }
michael@0 206 return true;
michael@0 207 }
michael@0 208
michael@0 209 NS_IMETHOD
michael@0 210 Cancel() MOZ_OVERRIDE
michael@0 211 {
michael@0 212 nsresult rv = mWrappedRunnable->Cancel();
michael@0 213 nsresult rv2 = WorkerRunnable::Cancel();
michael@0 214 return NS_FAILED(rv) ? rv : rv2;
michael@0 215 }
michael@0 216 };
michael@0 217
michael@0 218 struct WindowAction
michael@0 219 {
michael@0 220 nsPIDOMWindow* mWindow;
michael@0 221 JSContext* mJSContext;
michael@0 222 bool mDefaultAction;
michael@0 223
michael@0 224 WindowAction(nsPIDOMWindow* aWindow, JSContext* aJSContext)
michael@0 225 : mWindow(aWindow), mJSContext(aJSContext), mDefaultAction(true)
michael@0 226 {
michael@0 227 MOZ_ASSERT(aJSContext);
michael@0 228 }
michael@0 229
michael@0 230 WindowAction(nsPIDOMWindow* aWindow)
michael@0 231 : mWindow(aWindow), mJSContext(nullptr), mDefaultAction(true)
michael@0 232 { }
michael@0 233
michael@0 234 bool
michael@0 235 operator==(const WindowAction& aOther) const
michael@0 236 {
michael@0 237 return mWindow == aOther.mWindow;
michael@0 238 }
michael@0 239 };
michael@0 240
michael@0 241 void
michael@0 242 LogErrorToConsole(const nsAString& aMessage,
michael@0 243 const nsAString& aFilename,
michael@0 244 const nsAString& aLine,
michael@0 245 uint32_t aLineNumber,
michael@0 246 uint32_t aColumnNumber,
michael@0 247 uint32_t aFlags,
michael@0 248 uint64_t aInnerWindowId)
michael@0 249 {
michael@0 250 AssertIsOnMainThread();
michael@0 251
michael@0 252 nsCOMPtr<nsIScriptError> scriptError =
michael@0 253 do_CreateInstance(NS_SCRIPTERROR_CONTRACTID);
michael@0 254 NS_WARN_IF_FALSE(scriptError, "Failed to create script error!");
michael@0 255
michael@0 256 if (scriptError) {
michael@0 257 if (NS_FAILED(scriptError->InitWithWindowID(aMessage, aFilename, aLine,
michael@0 258 aLineNumber, aColumnNumber,
michael@0 259 aFlags, "Web Worker",
michael@0 260 aInnerWindowId))) {
michael@0 261 NS_WARNING("Failed to init script error!");
michael@0 262 scriptError = nullptr;
michael@0 263 }
michael@0 264 }
michael@0 265
michael@0 266 nsCOMPtr<nsIConsoleService> consoleService =
michael@0 267 do_GetService(NS_CONSOLESERVICE_CONTRACTID);
michael@0 268 NS_WARN_IF_FALSE(consoleService, "Failed to get console service!");
michael@0 269
michael@0 270 if (consoleService) {
michael@0 271 if (scriptError) {
michael@0 272 if (NS_SUCCEEDED(consoleService->LogMessage(scriptError))) {
michael@0 273 return;
michael@0 274 }
michael@0 275 NS_WARNING("LogMessage failed!");
michael@0 276 } else if (NS_SUCCEEDED(consoleService->LogStringMessage(
michael@0 277 aMessage.BeginReading()))) {
michael@0 278 return;
michael@0 279 }
michael@0 280 NS_WARNING("LogStringMessage failed!");
michael@0 281 }
michael@0 282
michael@0 283 NS_ConvertUTF16toUTF8 msg(aMessage);
michael@0 284 NS_ConvertUTF16toUTF8 filename(aFilename);
michael@0 285
michael@0 286 static const char kErrorString[] = "JS error in Web Worker: %s [%s:%u]";
michael@0 287
michael@0 288 #ifdef ANDROID
michael@0 289 __android_log_print(ANDROID_LOG_INFO, "Gecko", kErrorString, msg.get(),
michael@0 290 filename.get(), aLineNumber);
michael@0 291 #endif
michael@0 292
michael@0 293 fprintf(stderr, kErrorString, msg.get(), filename.get(), aLineNumber);
michael@0 294 fflush(stderr);
michael@0 295 }
michael@0 296
michael@0 297 struct WorkerStructuredCloneCallbacks
michael@0 298 {
michael@0 299 static JSObject*
michael@0 300 Read(JSContext* aCx, JSStructuredCloneReader* aReader, uint32_t aTag,
michael@0 301 uint32_t aData, void* aClosure)
michael@0 302 {
michael@0 303 // See if object is a nsIDOMFile pointer.
michael@0 304 if (aTag == DOMWORKER_SCTAG_FILE) {
michael@0 305 MOZ_ASSERT(!aData);
michael@0 306
michael@0 307 nsIDOMFile* file;
michael@0 308 if (JS_ReadBytes(aReader, &file, sizeof(file))) {
michael@0 309 MOZ_ASSERT(file);
michael@0 310
michael@0 311 #ifdef DEBUG
michael@0 312 {
michael@0 313 // File should not be mutable.
michael@0 314 nsCOMPtr<nsIMutable> mutableFile = do_QueryInterface(file);
michael@0 315 bool isMutable;
michael@0 316 NS_ASSERTION(NS_SUCCEEDED(mutableFile->GetMutable(&isMutable)) &&
michael@0 317 !isMutable,
michael@0 318 "Only immutable file should be passed to worker");
michael@0 319 }
michael@0 320 #endif
michael@0 321
michael@0 322 // nsIDOMFiles should be threadsafe, thus we will use the same instance
michael@0 323 // in the worker.
michael@0 324 JSObject* jsFile = file::CreateFile(aCx, file);
michael@0 325 return jsFile;
michael@0 326 }
michael@0 327 }
michael@0 328 // See if object is a nsIDOMBlob pointer.
michael@0 329 else if (aTag == DOMWORKER_SCTAG_BLOB) {
michael@0 330 MOZ_ASSERT(!aData);
michael@0 331
michael@0 332 nsIDOMBlob* blob;
michael@0 333 if (JS_ReadBytes(aReader, &blob, sizeof(blob))) {
michael@0 334 MOZ_ASSERT(blob);
michael@0 335
michael@0 336 #ifdef DEBUG
michael@0 337 {
michael@0 338 // Blob should not be mutable.
michael@0 339 nsCOMPtr<nsIMutable> mutableBlob = do_QueryInterface(blob);
michael@0 340 bool isMutable;
michael@0 341 NS_ASSERTION(NS_SUCCEEDED(mutableBlob->GetMutable(&isMutable)) &&
michael@0 342 !isMutable,
michael@0 343 "Only immutable blob should be passed to worker");
michael@0 344 }
michael@0 345 #endif
michael@0 346
michael@0 347 // nsIDOMBlob should be threadsafe, thus we will use the same instance
michael@0 348 // in the worker.
michael@0 349 JSObject* jsBlob = file::CreateBlob(aCx, blob);
michael@0 350 return jsBlob;
michael@0 351 }
michael@0 352 }
michael@0 353 // See if the object is an ImageData.
michael@0 354 else if (aTag == SCTAG_DOM_IMAGEDATA) {
michael@0 355 MOZ_ASSERT(!aData);
michael@0 356
michael@0 357 // Read the information out of the stream.
michael@0 358 uint32_t width, height;
michael@0 359 JS::Rooted<JS::Value> dataArray(aCx);
michael@0 360 if (!JS_ReadUint32Pair(aReader, &width, &height) ||
michael@0 361 !JS_ReadTypedArray(aReader, &dataArray))
michael@0 362 {
michael@0 363 return nullptr;
michael@0 364 }
michael@0 365 MOZ_ASSERT(dataArray.isObject());
michael@0 366
michael@0 367 JS::Rooted<JSObject*> result(aCx);
michael@0 368 {
michael@0 369 // Construct the ImageData.
michael@0 370 nsRefPtr<ImageData> imageData = new ImageData(width, height,
michael@0 371 dataArray.toObject());
michael@0 372 // Wrap it in a JS::Value, protected from a moving GC during ~nsRefPtr.
michael@0 373 result = imageData->WrapObject(aCx);
michael@0 374 }
michael@0 375 return result;
michael@0 376 }
michael@0 377
michael@0 378 Error(aCx, 0);
michael@0 379 return nullptr;
michael@0 380 }
michael@0 381
michael@0 382 static bool
michael@0 383 Write(JSContext* aCx, JSStructuredCloneWriter* aWriter,
michael@0 384 JS::Handle<JSObject*> aObj, void* aClosure)
michael@0 385 {
michael@0 386 NS_ASSERTION(aClosure, "Null pointer!");
michael@0 387
michael@0 388 // We'll stash any nsISupports pointers that need to be AddRef'd here.
michael@0 389 nsTArray<nsCOMPtr<nsISupports> >* clonedObjects =
michael@0 390 static_cast<nsTArray<nsCOMPtr<nsISupports> >*>(aClosure);
michael@0 391
michael@0 392 // See if this is a File object.
michael@0 393 {
michael@0 394 nsIDOMFile* file = file::GetDOMFileFromJSObject(aObj);
michael@0 395 if (file) {
michael@0 396 if (JS_WriteUint32Pair(aWriter, DOMWORKER_SCTAG_FILE, 0) &&
michael@0 397 JS_WriteBytes(aWriter, &file, sizeof(file))) {
michael@0 398 clonedObjects->AppendElement(file);
michael@0 399 return true;
michael@0 400 }
michael@0 401 }
michael@0 402 }
michael@0 403
michael@0 404 // See if this is a Blob object.
michael@0 405 {
michael@0 406 nsIDOMBlob* blob = file::GetDOMBlobFromJSObject(aObj);
michael@0 407 if (blob) {
michael@0 408 nsCOMPtr<nsIMutable> mutableBlob = do_QueryInterface(blob);
michael@0 409 if (mutableBlob && NS_SUCCEEDED(mutableBlob->SetMutable(false)) &&
michael@0 410 JS_WriteUint32Pair(aWriter, DOMWORKER_SCTAG_BLOB, 0) &&
michael@0 411 JS_WriteBytes(aWriter, &blob, sizeof(blob))) {
michael@0 412 clonedObjects->AppendElement(blob);
michael@0 413 return true;
michael@0 414 }
michael@0 415 }
michael@0 416 }
michael@0 417
michael@0 418 // See if this is an ImageData object.
michael@0 419 {
michael@0 420 ImageData* imageData = nullptr;
michael@0 421 if (NS_SUCCEEDED(UNWRAP_OBJECT(ImageData, aObj, imageData))) {
michael@0 422 // Prepare the ImageData internals.
michael@0 423 uint32_t width = imageData->Width();
michael@0 424 uint32_t height = imageData->Height();
michael@0 425 JS::Rooted<JSObject*> dataArray(aCx, imageData->GetDataObject());
michael@0 426
michael@0 427 // Write the internals to the stream.
michael@0 428 JSAutoCompartment ac(aCx, dataArray);
michael@0 429 JS::Rooted<JS::Value> arrayValue(aCx, JS::ObjectValue(*dataArray));
michael@0 430 return JS_WriteUint32Pair(aWriter, SCTAG_DOM_IMAGEDATA, 0) &&
michael@0 431 JS_WriteUint32Pair(aWriter, width, height) &&
michael@0 432 JS_WriteTypedArray(aWriter, arrayValue);
michael@0 433 }
michael@0 434 }
michael@0 435
michael@0 436 Error(aCx, 0);
michael@0 437 return false;
michael@0 438 }
michael@0 439
michael@0 440 static void
michael@0 441 Error(JSContext* aCx, uint32_t /* aErrorId */)
michael@0 442 {
michael@0 443 Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
michael@0 444 }
michael@0 445 };
michael@0 446
michael@0 447 JSStructuredCloneCallbacks gWorkerStructuredCloneCallbacks = {
michael@0 448 WorkerStructuredCloneCallbacks::Read,
michael@0 449 WorkerStructuredCloneCallbacks::Write,
michael@0 450 WorkerStructuredCloneCallbacks::Error,
michael@0 451 nullptr,
michael@0 452 nullptr,
michael@0 453 nullptr
michael@0 454 };
michael@0 455
michael@0 456 struct MainThreadWorkerStructuredCloneCallbacks
michael@0 457 {
michael@0 458 static JSObject*
michael@0 459 Read(JSContext* aCx, JSStructuredCloneReader* aReader, uint32_t aTag,
michael@0 460 uint32_t aData, void* aClosure)
michael@0 461 {
michael@0 462 AssertIsOnMainThread();
michael@0 463
michael@0 464 // See if object is a nsIDOMFile pointer.
michael@0 465 if (aTag == DOMWORKER_SCTAG_FILE) {
michael@0 466 MOZ_ASSERT(!aData);
michael@0 467
michael@0 468 nsIDOMFile* file;
michael@0 469 if (JS_ReadBytes(aReader, &file, sizeof(file))) {
michael@0 470 MOZ_ASSERT(file);
michael@0 471
michael@0 472 #ifdef DEBUG
michael@0 473 {
michael@0 474 // File should not be mutable.
michael@0 475 nsCOMPtr<nsIMutable> mutableFile = do_QueryInterface(file);
michael@0 476 bool isMutable;
michael@0 477 NS_ASSERTION(NS_SUCCEEDED(mutableFile->GetMutable(&isMutable)) &&
michael@0 478 !isMutable,
michael@0 479 "Only immutable file should be passed to worker");
michael@0 480 }
michael@0 481 #endif
michael@0 482
michael@0 483 // nsIDOMFiles should be threadsafe, thus we will use the same instance
michael@0 484 // on the main thread.
michael@0 485 JS::Rooted<JS::Value> wrappedFile(aCx);
michael@0 486 nsresult rv = nsContentUtils::WrapNative(aCx, file,
michael@0 487 &NS_GET_IID(nsIDOMFile),
michael@0 488 &wrappedFile);
michael@0 489 if (NS_FAILED(rv)) {
michael@0 490 Error(aCx, nsIDOMDOMException::DATA_CLONE_ERR);
michael@0 491 return nullptr;
michael@0 492 }
michael@0 493
michael@0 494 return &wrappedFile.toObject();
michael@0 495 }
michael@0 496 }
michael@0 497 // See if object is a nsIDOMBlob pointer.
michael@0 498 else if (aTag == DOMWORKER_SCTAG_BLOB) {
michael@0 499 MOZ_ASSERT(!aData);
michael@0 500
michael@0 501 nsIDOMBlob* blob;
michael@0 502 if (JS_ReadBytes(aReader, &blob, sizeof(blob))) {
michael@0 503 MOZ_ASSERT(blob);
michael@0 504
michael@0 505 #ifdef DEBUG
michael@0 506 {
michael@0 507 // Blob should not be mutable.
michael@0 508 nsCOMPtr<nsIMutable> mutableBlob = do_QueryInterface(blob);
michael@0 509 bool isMutable;
michael@0 510 NS_ASSERTION(NS_SUCCEEDED(mutableBlob->GetMutable(&isMutable)) &&
michael@0 511 !isMutable,
michael@0 512 "Only immutable blob should be passed to worker");
michael@0 513 }
michael@0 514 #endif
michael@0 515
michael@0 516 // nsIDOMBlobs should be threadsafe, thus we will use the same instance
michael@0 517 // on the main thread.
michael@0 518 JS::Rooted<JS::Value> wrappedBlob(aCx);
michael@0 519 nsresult rv = nsContentUtils::WrapNative(aCx, blob,
michael@0 520 &NS_GET_IID(nsIDOMBlob),
michael@0 521 &wrappedBlob);
michael@0 522 if (NS_FAILED(rv)) {
michael@0 523 Error(aCx, nsIDOMDOMException::DATA_CLONE_ERR);
michael@0 524 return nullptr;
michael@0 525 }
michael@0 526
michael@0 527 return &wrappedBlob.toObject();
michael@0 528 }
michael@0 529 }
michael@0 530
michael@0 531 JS_ClearPendingException(aCx);
michael@0 532 return NS_DOMReadStructuredClone(aCx, aReader, aTag, aData, nullptr);
michael@0 533 }
michael@0 534
michael@0 535 static bool
michael@0 536 Write(JSContext* aCx, JSStructuredCloneWriter* aWriter,
michael@0 537 JS::Handle<JSObject*> aObj, void* aClosure)
michael@0 538 {
michael@0 539 AssertIsOnMainThread();
michael@0 540
michael@0 541 NS_ASSERTION(aClosure, "Null pointer!");
michael@0 542
michael@0 543 // We'll stash any nsISupports pointers that need to be AddRef'd here.
michael@0 544 nsTArray<nsCOMPtr<nsISupports> >* clonedObjects =
michael@0 545 static_cast<nsTArray<nsCOMPtr<nsISupports> >*>(aClosure);
michael@0 546
michael@0 547 // See if this is a wrapped native.
michael@0 548 nsCOMPtr<nsIXPConnectWrappedNative> wrappedNative;
michael@0 549 nsContentUtils::XPConnect()->
michael@0 550 GetWrappedNativeOfJSObject(aCx, aObj, getter_AddRefs(wrappedNative));
michael@0 551
michael@0 552 if (wrappedNative) {
michael@0 553 // Get the raw nsISupports out of it.
michael@0 554 nsISupports* wrappedObject = wrappedNative->Native();
michael@0 555 NS_ASSERTION(wrappedObject, "Null pointer?!");
michael@0 556
michael@0 557 nsISupports* ccISupports = nullptr;
michael@0 558 wrappedObject->QueryInterface(NS_GET_IID(nsCycleCollectionISupports),
michael@0 559 reinterpret_cast<void**>(&ccISupports));
michael@0 560 if (ccISupports) {
michael@0 561 NS_WARNING("Cycle collected objects are not supported!");
michael@0 562 }
michael@0 563 else {
michael@0 564 // See if the wrapped native is a nsIDOMFile.
michael@0 565 nsCOMPtr<nsIDOMFile> file = do_QueryInterface(wrappedObject);
michael@0 566 if (file) {
michael@0 567 nsCOMPtr<nsIMutable> mutableFile = do_QueryInterface(file);
michael@0 568 if (mutableFile && NS_SUCCEEDED(mutableFile->SetMutable(false))) {
michael@0 569 nsIDOMFile* filePtr = file;
michael@0 570 if (JS_WriteUint32Pair(aWriter, DOMWORKER_SCTAG_FILE, 0) &&
michael@0 571 JS_WriteBytes(aWriter, &filePtr, sizeof(filePtr))) {
michael@0 572 clonedObjects->AppendElement(file);
michael@0 573 return true;
michael@0 574 }
michael@0 575 }
michael@0 576 }
michael@0 577
michael@0 578 // See if the wrapped native is a nsIDOMBlob.
michael@0 579 nsCOMPtr<nsIDOMBlob> blob = do_QueryInterface(wrappedObject);
michael@0 580 if (blob) {
michael@0 581 nsCOMPtr<nsIMutable> mutableBlob = do_QueryInterface(blob);
michael@0 582 if (mutableBlob && NS_SUCCEEDED(mutableBlob->SetMutable(false))) {
michael@0 583 nsIDOMBlob* blobPtr = blob;
michael@0 584 if (JS_WriteUint32Pair(aWriter, DOMWORKER_SCTAG_BLOB, 0) &&
michael@0 585 JS_WriteBytes(aWriter, &blobPtr, sizeof(blobPtr))) {
michael@0 586 clonedObjects->AppendElement(blob);
michael@0 587 return true;
michael@0 588 }
michael@0 589 }
michael@0 590 }
michael@0 591 }
michael@0 592 }
michael@0 593
michael@0 594 JS_ClearPendingException(aCx);
michael@0 595 return NS_DOMWriteStructuredClone(aCx, aWriter, aObj, nullptr);
michael@0 596 }
michael@0 597
michael@0 598 static void
michael@0 599 Error(JSContext* aCx, uint32_t aErrorId)
michael@0 600 {
michael@0 601 AssertIsOnMainThread();
michael@0 602
michael@0 603 NS_DOMStructuredCloneError(aCx, aErrorId);
michael@0 604 }
michael@0 605 };
michael@0 606
michael@0 607 JSStructuredCloneCallbacks gMainThreadWorkerStructuredCloneCallbacks = {
michael@0 608 MainThreadWorkerStructuredCloneCallbacks::Read,
michael@0 609 MainThreadWorkerStructuredCloneCallbacks::Write,
michael@0 610 MainThreadWorkerStructuredCloneCallbacks::Error,
michael@0 611 nullptr,
michael@0 612 nullptr,
michael@0 613 nullptr
michael@0 614 };
michael@0 615
michael@0 616 struct ChromeWorkerStructuredCloneCallbacks
michael@0 617 {
michael@0 618 static JSObject*
michael@0 619 Read(JSContext* aCx, JSStructuredCloneReader* aReader, uint32_t aTag,
michael@0 620 uint32_t aData, void* aClosure)
michael@0 621 {
michael@0 622 return WorkerStructuredCloneCallbacks::Read(aCx, aReader, aTag, aData,
michael@0 623 aClosure);
michael@0 624 }
michael@0 625
michael@0 626 static bool
michael@0 627 Write(JSContext* aCx, JSStructuredCloneWriter* aWriter,
michael@0 628 JS::Handle<JSObject*> aObj, void* aClosure)
michael@0 629 {
michael@0 630 return WorkerStructuredCloneCallbacks::Write(aCx, aWriter, aObj, aClosure);
michael@0 631 }
michael@0 632
michael@0 633 static void
michael@0 634 Error(JSContext* aCx, uint32_t aErrorId)
michael@0 635 {
michael@0 636 return WorkerStructuredCloneCallbacks::Error(aCx, aErrorId);
michael@0 637 }
michael@0 638 };
michael@0 639
michael@0 640 JSStructuredCloneCallbacks gChromeWorkerStructuredCloneCallbacks = {
michael@0 641 ChromeWorkerStructuredCloneCallbacks::Read,
michael@0 642 ChromeWorkerStructuredCloneCallbacks::Write,
michael@0 643 ChromeWorkerStructuredCloneCallbacks::Error,
michael@0 644 nullptr,
michael@0 645 nullptr,
michael@0 646 nullptr
michael@0 647 };
michael@0 648
michael@0 649 struct MainThreadChromeWorkerStructuredCloneCallbacks
michael@0 650 {
michael@0 651 static JSObject*
michael@0 652 Read(JSContext* aCx, JSStructuredCloneReader* aReader, uint32_t aTag,
michael@0 653 uint32_t aData, void* aClosure)
michael@0 654 {
michael@0 655 AssertIsOnMainThread();
michael@0 656
michael@0 657 JSObject* clone =
michael@0 658 MainThreadWorkerStructuredCloneCallbacks::Read(aCx, aReader, aTag, aData,
michael@0 659 aClosure);
michael@0 660 if (clone) {
michael@0 661 return clone;
michael@0 662 }
michael@0 663
michael@0 664 clone =
michael@0 665 ChromeWorkerStructuredCloneCallbacks::Read(aCx, aReader, aTag, aData,
michael@0 666 aClosure);
michael@0 667 if (clone) {
michael@0 668 return clone;
michael@0 669 }
michael@0 670
michael@0 671 JS_ClearPendingException(aCx);
michael@0 672 return NS_DOMReadStructuredClone(aCx, aReader, aTag, aData, nullptr);
michael@0 673 }
michael@0 674
michael@0 675 static bool
michael@0 676 Write(JSContext* aCx, JSStructuredCloneWriter* aWriter,
michael@0 677 JS::Handle<JSObject*> aObj, void* aClosure)
michael@0 678 {
michael@0 679 AssertIsOnMainThread();
michael@0 680
michael@0 681 if (MainThreadWorkerStructuredCloneCallbacks::Write(aCx, aWriter, aObj,
michael@0 682 aClosure) ||
michael@0 683 ChromeWorkerStructuredCloneCallbacks::Write(aCx, aWriter, aObj,
michael@0 684 aClosure) ||
michael@0 685 NS_DOMWriteStructuredClone(aCx, aWriter, aObj, nullptr)) {
michael@0 686 return true;
michael@0 687 }
michael@0 688
michael@0 689 return false;
michael@0 690 }
michael@0 691
michael@0 692 static void
michael@0 693 Error(JSContext* aCx, uint32_t aErrorId)
michael@0 694 {
michael@0 695 AssertIsOnMainThread();
michael@0 696
michael@0 697 NS_DOMStructuredCloneError(aCx, aErrorId);
michael@0 698 }
michael@0 699 };
michael@0 700
michael@0 701 JSStructuredCloneCallbacks gMainThreadChromeWorkerStructuredCloneCallbacks = {
michael@0 702 MainThreadChromeWorkerStructuredCloneCallbacks::Read,
michael@0 703 MainThreadChromeWorkerStructuredCloneCallbacks::Write,
michael@0 704 MainThreadChromeWorkerStructuredCloneCallbacks::Error,
michael@0 705 nullptr,
michael@0 706 nullptr,
michael@0 707 nullptr
michael@0 708 };
michael@0 709
michael@0 710 class MainThreadReleaseRunnable MOZ_FINAL : public nsRunnable
michael@0 711 {
michael@0 712 nsTArray<nsCOMPtr<nsISupports>> mDoomed;
michael@0 713 nsTArray<nsCString> mHostObjectURIs;
michael@0 714
michael@0 715 public:
michael@0 716 MainThreadReleaseRunnable(nsTArray<nsCOMPtr<nsISupports>>& aDoomed,
michael@0 717 nsTArray<nsCString>& aHostObjectURIs)
michael@0 718 {
michael@0 719 mDoomed.SwapElements(aDoomed);
michael@0 720 mHostObjectURIs.SwapElements(aHostObjectURIs);
michael@0 721 }
michael@0 722
michael@0 723 NS_DECL_ISUPPORTS_INHERITED
michael@0 724
michael@0 725 NS_IMETHOD
michael@0 726 Run() MOZ_OVERRIDE
michael@0 727 {
michael@0 728 mDoomed.Clear();
michael@0 729
michael@0 730 for (uint32_t index = 0; index < mHostObjectURIs.Length(); index++) {
michael@0 731 nsHostObjectProtocolHandler::RemoveDataEntry(mHostObjectURIs[index]);
michael@0 732 }
michael@0 733
michael@0 734 return NS_OK;
michael@0 735 }
michael@0 736
michael@0 737 private:
michael@0 738 ~MainThreadReleaseRunnable()
michael@0 739 { }
michael@0 740 };
michael@0 741
michael@0 742 class WorkerFinishedRunnable MOZ_FINAL : public WorkerControlRunnable
michael@0 743 {
michael@0 744 WorkerPrivate* mFinishedWorker;
michael@0 745
michael@0 746 public:
michael@0 747 WorkerFinishedRunnable(WorkerPrivate* aWorkerPrivate,
michael@0 748 WorkerPrivate* aFinishedWorker)
michael@0 749 : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
michael@0 750 mFinishedWorker(aFinishedWorker)
michael@0 751 { }
michael@0 752
michael@0 753 private:
michael@0 754 virtual bool
michael@0 755 PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
michael@0 756 {
michael@0 757 // Silence bad assertions.
michael@0 758 return true;
michael@0 759 }
michael@0 760
michael@0 761 virtual void
michael@0 762 PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
michael@0 763 bool aDispatchResult) MOZ_OVERRIDE
michael@0 764 {
michael@0 765 // Silence bad assertions.
michael@0 766 }
michael@0 767
michael@0 768 virtual bool
michael@0 769 WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
michael@0 770 {
michael@0 771 nsTArray<nsCOMPtr<nsISupports>> doomed;
michael@0 772 mFinishedWorker->ForgetMainThreadObjects(doomed);
michael@0 773
michael@0 774 nsTArray<nsCString> hostObjectURIs;
michael@0 775 mFinishedWorker->StealHostObjectURIs(hostObjectURIs);
michael@0 776
michael@0 777 nsRefPtr<MainThreadReleaseRunnable> runnable =
michael@0 778 new MainThreadReleaseRunnable(doomed, hostObjectURIs);
michael@0 779 if (NS_FAILED(NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL))) {
michael@0 780 NS_WARNING("Failed to dispatch, going to leak!");
michael@0 781 }
michael@0 782
michael@0 783 RuntimeService* runtime = RuntimeService::GetService();
michael@0 784 NS_ASSERTION(runtime, "This should never be null!");
michael@0 785
michael@0 786 runtime->UnregisterWorker(aCx, mFinishedWorker);
michael@0 787
michael@0 788 mFinishedWorker->ClearSelfRef();
michael@0 789 return true;
michael@0 790 }
michael@0 791 };
michael@0 792
michael@0 793 class TopLevelWorkerFinishedRunnable MOZ_FINAL : public nsRunnable
michael@0 794 {
michael@0 795 WorkerPrivate* mFinishedWorker;
michael@0 796
michael@0 797 public:
michael@0 798 TopLevelWorkerFinishedRunnable(WorkerPrivate* aFinishedWorker)
michael@0 799 : mFinishedWorker(aFinishedWorker)
michael@0 800 {
michael@0 801 aFinishedWorker->AssertIsOnWorkerThread();
michael@0 802 }
michael@0 803
michael@0 804 NS_DECL_ISUPPORTS_INHERITED
michael@0 805
michael@0 806 private:
michael@0 807 NS_IMETHOD
michael@0 808 Run() MOZ_OVERRIDE
michael@0 809 {
michael@0 810 AssertIsOnMainThread();
michael@0 811
michael@0 812 RuntimeService* runtime = RuntimeService::GetService();
michael@0 813 MOZ_ASSERT(runtime);
michael@0 814
michael@0 815 AutoSafeJSContext cx;
michael@0 816 JSAutoRequest ar(cx);
michael@0 817
michael@0 818 runtime->UnregisterWorker(cx, mFinishedWorker);
michael@0 819
michael@0 820 nsTArray<nsCOMPtr<nsISupports> > doomed;
michael@0 821 mFinishedWorker->ForgetMainThreadObjects(doomed);
michael@0 822
michael@0 823 nsTArray<nsCString> hostObjectURIs;
michael@0 824 mFinishedWorker->StealHostObjectURIs(hostObjectURIs);
michael@0 825
michael@0 826 nsRefPtr<MainThreadReleaseRunnable> runnable =
michael@0 827 new MainThreadReleaseRunnable(doomed, hostObjectURIs);
michael@0 828 if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) {
michael@0 829 NS_WARNING("Failed to dispatch, going to leak!");
michael@0 830 }
michael@0 831
michael@0 832 mFinishedWorker->ClearSelfRef();
michael@0 833 return NS_OK;
michael@0 834 }
michael@0 835 };
michael@0 836
michael@0 837 class ModifyBusyCountRunnable MOZ_FINAL : public WorkerControlRunnable
michael@0 838 {
michael@0 839 bool mIncrease;
michael@0 840
michael@0 841 public:
michael@0 842 ModifyBusyCountRunnable(WorkerPrivate* aWorkerPrivate, bool aIncrease)
michael@0 843 : WorkerControlRunnable(aWorkerPrivate, ParentThreadUnchangedBusyCount),
michael@0 844 mIncrease(aIncrease)
michael@0 845 { }
michael@0 846
michael@0 847 private:
michael@0 848 virtual bool
michael@0 849 WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
michael@0 850 {
michael@0 851 return aWorkerPrivate->ModifyBusyCount(aCx, mIncrease);
michael@0 852 }
michael@0 853
michael@0 854 virtual void
michael@0 855 PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate, bool aRunResult)
michael@0 856 MOZ_OVERRIDE
michael@0 857 {
michael@0 858 if (mIncrease) {
michael@0 859 WorkerControlRunnable::PostRun(aCx, aWorkerPrivate, aRunResult);
michael@0 860 return;
michael@0 861 }
michael@0 862 // Don't do anything here as it's possible that aWorkerPrivate has been
michael@0 863 // deleted.
michael@0 864 }
michael@0 865 };
michael@0 866
michael@0 867 class CompileScriptRunnable MOZ_FINAL : public WorkerRunnable
michael@0 868 {
michael@0 869 public:
michael@0 870 CompileScriptRunnable(WorkerPrivate* aWorkerPrivate)
michael@0 871 : WorkerRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount)
michael@0 872 { }
michael@0 873
michael@0 874 private:
michael@0 875 virtual bool
michael@0 876 WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
michael@0 877 {
michael@0 878 JS::Rooted<JSObject*> global(aCx,
michael@0 879 aWorkerPrivate->CreateGlobalScope(aCx));
michael@0 880 if (!global) {
michael@0 881 NS_WARNING("Failed to make global!");
michael@0 882 return false;
michael@0 883 }
michael@0 884
michael@0 885 JSAutoCompartment ac(aCx, global);
michael@0 886 return scriptloader::LoadWorkerScript(aCx);
michael@0 887 }
michael@0 888 };
michael@0 889
michael@0 890 class CloseEventRunnable MOZ_FINAL : public WorkerRunnable
michael@0 891 {
michael@0 892 public:
michael@0 893 CloseEventRunnable(WorkerPrivate* aWorkerPrivate)
michael@0 894 : WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
michael@0 895 { }
michael@0 896
michael@0 897 private:
michael@0 898 virtual bool
michael@0 899 PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
michael@0 900 {
michael@0 901 MOZ_ASSUME_UNREACHABLE("Don't call Dispatch() on CloseEventRunnable!");
michael@0 902 }
michael@0 903
michael@0 904 virtual void
michael@0 905 PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
michael@0 906 bool aDispatchResult) MOZ_OVERRIDE
michael@0 907 {
michael@0 908 MOZ_ASSUME_UNREACHABLE("Don't call Dispatch() on CloseEventRunnable!");
michael@0 909 }
michael@0 910
michael@0 911 virtual bool
michael@0 912 WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
michael@0 913 {
michael@0 914 JS::Rooted<JSObject*> target(aCx, JS::CurrentGlobalOrNull(aCx));
michael@0 915 NS_ASSERTION(target, "This must never be null!");
michael@0 916
michael@0 917 aWorkerPrivate->CloseHandlerStarted();
michael@0 918
michael@0 919 WorkerGlobalScope* globalScope = aWorkerPrivate->GlobalScope();
michael@0 920
michael@0 921 nsCOMPtr<nsIDOMEvent> event;
michael@0 922 nsresult rv =
michael@0 923 NS_NewDOMEvent(getter_AddRefs(event), globalScope, nullptr, nullptr);
michael@0 924 if (NS_FAILED(rv)) {
michael@0 925 Throw(aCx, rv);
michael@0 926 return false;
michael@0 927 }
michael@0 928
michael@0 929 rv = event->InitEvent(NS_LITERAL_STRING("close"), false, false);
michael@0 930 if (NS_FAILED(rv)) {
michael@0 931 Throw(aCx, rv);
michael@0 932 return false;
michael@0 933 }
michael@0 934
michael@0 935 event->SetTrusted(true);
michael@0 936
michael@0 937 globalScope->DispatchDOMEvent(nullptr, event, nullptr, nullptr);
michael@0 938
michael@0 939 return true;
michael@0 940 }
michael@0 941
michael@0 942 virtual void
michael@0 943 PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate, bool aRunResult)
michael@0 944 MOZ_OVERRIDE
michael@0 945 {
michael@0 946 // Report errors.
michael@0 947 WorkerRunnable::PostRun(aCx, aWorkerPrivate, aRunResult);
michael@0 948
michael@0 949 // Match the busy count increase from NotifyRunnable.
michael@0 950 if (!aWorkerPrivate->ModifyBusyCountFromWorker(aCx, false)) {
michael@0 951 JS_ReportPendingException(aCx);
michael@0 952 }
michael@0 953
michael@0 954 aWorkerPrivate->CloseHandlerFinished();
michael@0 955 }
michael@0 956 };
michael@0 957
michael@0 958 class MessageEventRunnable MOZ_FINAL : public WorkerRunnable
michael@0 959 {
michael@0 960 JSAutoStructuredCloneBuffer mBuffer;
michael@0 961 nsTArray<nsCOMPtr<nsISupports> > mClonedObjects;
michael@0 962 uint64_t mMessagePortSerial;
michael@0 963 bool mToMessagePort;
michael@0 964
michael@0 965 public:
michael@0 966 MessageEventRunnable(WorkerPrivate* aWorkerPrivate,
michael@0 967 TargetAndBusyBehavior aBehavior,
michael@0 968 JSAutoStructuredCloneBuffer&& aData,
michael@0 969 nsTArray<nsCOMPtr<nsISupports> >& aClonedObjects,
michael@0 970 bool aToMessagePort, uint64_t aMessagePortSerial)
michael@0 971 : WorkerRunnable(aWorkerPrivate, aBehavior)
michael@0 972 , mBuffer(Move(aData))
michael@0 973 , mMessagePortSerial(aMessagePortSerial)
michael@0 974 , mToMessagePort(aToMessagePort)
michael@0 975 {
michael@0 976 mClonedObjects.SwapElements(aClonedObjects);
michael@0 977 }
michael@0 978
michael@0 979 bool
michael@0 980 DispatchDOMEvent(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
michael@0 981 DOMEventTargetHelper* aTarget, bool aIsMainThread)
michael@0 982 {
michael@0 983 // Release reference to objects that were AddRef'd for
michael@0 984 // cloning into worker when array goes out of scope.
michael@0 985 nsTArray<nsCOMPtr<nsISupports>> clonedObjects;
michael@0 986 clonedObjects.SwapElements(mClonedObjects);
michael@0 987
michael@0 988 JS::Rooted<JS::Value> messageData(aCx);
michael@0 989 if (!mBuffer.read(aCx, &messageData,
michael@0 990 workers::WorkerStructuredCloneCallbacks(aIsMainThread))) {
michael@0 991 xpc::Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
michael@0 992 return false;
michael@0 993 }
michael@0 994
michael@0 995 nsRefPtr<MessageEvent> event = new MessageEvent(aTarget, nullptr, nullptr);
michael@0 996 nsresult rv =
michael@0 997 event->InitMessageEvent(NS_LITERAL_STRING("message"),
michael@0 998 false /* non-bubbling */,
michael@0 999 true /* cancelable */,
michael@0 1000 messageData,
michael@0 1001 EmptyString(),
michael@0 1002 EmptyString(),
michael@0 1003 nullptr);
michael@0 1004 if (NS_FAILED(rv)) {
michael@0 1005 xpc::Throw(aCx, rv);
michael@0 1006 return false;
michael@0 1007 }
michael@0 1008
michael@0 1009 event->SetTrusted(true);
michael@0 1010
michael@0 1011 nsCOMPtr<nsIDOMEvent> domEvent = do_QueryObject(event);
michael@0 1012
michael@0 1013 nsEventStatus dummy = nsEventStatus_eIgnore;
michael@0 1014 aTarget->DispatchDOMEvent(nullptr, domEvent, nullptr, &dummy);
michael@0 1015 return true;
michael@0 1016 }
michael@0 1017
michael@0 1018 private:
michael@0 1019 virtual bool
michael@0 1020 WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
michael@0 1021 {
michael@0 1022 MOZ_ASSERT_IF(mToMessagePort, aWorkerPrivate->IsSharedWorker());
michael@0 1023
michael@0 1024 if (mBehavior == ParentThreadUnchangedBusyCount) {
michael@0 1025 // Don't fire this event if the JS object has been disconnected from the
michael@0 1026 // private object.
michael@0 1027 if (!aWorkerPrivate->IsAcceptingEvents()) {
michael@0 1028 return true;
michael@0 1029 }
michael@0 1030
michael@0 1031 if (mToMessagePort) {
michael@0 1032 return
michael@0 1033 aWorkerPrivate->DispatchMessageEventToMessagePort(aCx,
michael@0 1034 mMessagePortSerial,
michael@0 1035 Move(mBuffer),
michael@0 1036 mClonedObjects);
michael@0 1037 }
michael@0 1038
michael@0 1039 if (aWorkerPrivate->IsSuspended()) {
michael@0 1040 aWorkerPrivate->QueueRunnable(this);
michael@0 1041 return true;
michael@0 1042 }
michael@0 1043
michael@0 1044 aWorkerPrivate->AssertInnerWindowIsCorrect();
michael@0 1045
michael@0 1046 return DispatchDOMEvent(aCx, aWorkerPrivate, aWorkerPrivate,
michael@0 1047 !aWorkerPrivate->GetParent());
michael@0 1048 }
michael@0 1049
michael@0 1050 MOZ_ASSERT(aWorkerPrivate == GetWorkerPrivateFromContext(aCx));
michael@0 1051
michael@0 1052 if (mToMessagePort) {
michael@0 1053 nsRefPtr<workers::MessagePort> port =
michael@0 1054 aWorkerPrivate->GetMessagePort(mMessagePortSerial);
michael@0 1055 if (!port) {
michael@0 1056 // Must have been closed already.
michael@0 1057 return true;
michael@0 1058 }
michael@0 1059 return DispatchDOMEvent(aCx, aWorkerPrivate, port, false);
michael@0 1060 }
michael@0 1061
michael@0 1062 return DispatchDOMEvent(aCx, aWorkerPrivate, aWorkerPrivate->GlobalScope(),
michael@0 1063 false);
michael@0 1064 }
michael@0 1065 };
michael@0 1066
michael@0 1067 class NotifyRunnable MOZ_FINAL : public WorkerControlRunnable
michael@0 1068 {
michael@0 1069 Status mStatus;
michael@0 1070
michael@0 1071 public:
michael@0 1072 NotifyRunnable(WorkerPrivate* aWorkerPrivate, Status aStatus)
michael@0 1073 : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
michael@0 1074 mStatus(aStatus)
michael@0 1075 {
michael@0 1076 MOZ_ASSERT(aStatus == Closing || aStatus == Terminating ||
michael@0 1077 aStatus == Canceling || aStatus == Killing);
michael@0 1078 }
michael@0 1079
michael@0 1080 private:
michael@0 1081 virtual bool
michael@0 1082 PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
michael@0 1083 {
michael@0 1084 // Modify here, but not in PostRun! This busy count addition will be matched
michael@0 1085 // by the CloseEventRunnable.
michael@0 1086 return aWorkerPrivate->ModifyBusyCount(aCx, true);
michael@0 1087 }
michael@0 1088
michael@0 1089 virtual void
michael@0 1090 PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
michael@0 1091 bool aDispatchResult) MOZ_OVERRIDE
michael@0 1092 {
michael@0 1093 if (!aDispatchResult) {
michael@0 1094 // We couldn't dispatch to the worker, which means it's already dead.
michael@0 1095 // Undo the busy count modification.
michael@0 1096 aWorkerPrivate->ModifyBusyCount(aCx, false);
michael@0 1097 }
michael@0 1098 }
michael@0 1099
michael@0 1100 virtual bool
michael@0 1101 WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
michael@0 1102 {
michael@0 1103 return aWorkerPrivate->NotifyInternal(aCx, mStatus);
michael@0 1104 }
michael@0 1105 };
michael@0 1106
michael@0 1107 class CloseRunnable MOZ_FINAL : public WorkerControlRunnable
michael@0 1108 {
michael@0 1109 public:
michael@0 1110 CloseRunnable(WorkerPrivate* aWorkerPrivate)
michael@0 1111 : WorkerControlRunnable(aWorkerPrivate, ParentThreadUnchangedBusyCount)
michael@0 1112 { }
michael@0 1113
michael@0 1114 private:
michael@0 1115 virtual bool
michael@0 1116 WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
michael@0 1117 {
michael@0 1118 // This busy count will be matched by the CloseEventRunnable.
michael@0 1119 return aWorkerPrivate->ModifyBusyCount(aCx, true) &&
michael@0 1120 aWorkerPrivate->Close(aCx);
michael@0 1121 }
michael@0 1122 };
michael@0 1123
michael@0 1124 class SuspendRunnable MOZ_FINAL : public WorkerControlRunnable
michael@0 1125 {
michael@0 1126 public:
michael@0 1127 SuspendRunnable(WorkerPrivate* aWorkerPrivate)
michael@0 1128 : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
michael@0 1129 { }
michael@0 1130
michael@0 1131 private:
michael@0 1132 virtual bool
michael@0 1133 WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
michael@0 1134 {
michael@0 1135 return aWorkerPrivate->SuspendInternal(aCx);
michael@0 1136 }
michael@0 1137 };
michael@0 1138
michael@0 1139 class ResumeRunnable MOZ_FINAL : public WorkerControlRunnable
michael@0 1140 {
michael@0 1141 public:
michael@0 1142 ResumeRunnable(WorkerPrivate* aWorkerPrivate)
michael@0 1143 : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
michael@0 1144 { }
michael@0 1145
michael@0 1146 private:
michael@0 1147 virtual bool
michael@0 1148 WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
michael@0 1149 {
michael@0 1150 return aWorkerPrivate->ResumeInternal(aCx);
michael@0 1151 }
michael@0 1152 };
michael@0 1153
michael@0 1154 class ReportErrorRunnable MOZ_FINAL : public WorkerRunnable
michael@0 1155 {
michael@0 1156 nsString mMessage;
michael@0 1157 nsString mFilename;
michael@0 1158 nsString mLine;
michael@0 1159 uint32_t mLineNumber;
michael@0 1160 uint32_t mColumnNumber;
michael@0 1161 uint32_t mFlags;
michael@0 1162 uint32_t mErrorNumber;
michael@0 1163
michael@0 1164 public:
michael@0 1165 // aWorkerPrivate is the worker thread we're on (or the main thread, if null)
michael@0 1166 // aTarget is the worker object that we are going to fire an error at
michael@0 1167 // (if any).
michael@0 1168 static bool
michael@0 1169 ReportError(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
michael@0 1170 bool aFireAtScope, WorkerPrivate* aTarget,
michael@0 1171 const nsString& aMessage, const nsString& aFilename,
michael@0 1172 const nsString& aLine, uint32_t aLineNumber,
michael@0 1173 uint32_t aColumnNumber, uint32_t aFlags,
michael@0 1174 uint32_t aErrorNumber, uint64_t aInnerWindowId)
michael@0 1175 {
michael@0 1176 if (aWorkerPrivate) {
michael@0 1177 aWorkerPrivate->AssertIsOnWorkerThread();
michael@0 1178 }
michael@0 1179 else {
michael@0 1180 AssertIsOnMainThread();
michael@0 1181 }
michael@0 1182
michael@0 1183 JS::Rooted<JSString*> message(aCx, JS_NewUCStringCopyN(aCx, aMessage.get(),
michael@0 1184 aMessage.Length()));
michael@0 1185 if (!message) {
michael@0 1186 return false;
michael@0 1187 }
michael@0 1188
michael@0 1189 JS::Rooted<JSString*> filename(aCx, JS_NewUCStringCopyN(aCx, aFilename.get(),
michael@0 1190 aFilename.Length()));
michael@0 1191 if (!filename) {
michael@0 1192 return false;
michael@0 1193 }
michael@0 1194
michael@0 1195 // We should not fire error events for warnings but instead make sure that
michael@0 1196 // they show up in the error console.
michael@0 1197 if (!JSREPORT_IS_WARNING(aFlags)) {
michael@0 1198 // First fire an ErrorEvent at the worker.
michael@0 1199 RootedDictionary<ErrorEventInit> init(aCx);
michael@0 1200 init.mMessage = aMessage;
michael@0 1201 init.mFilename = aFilename;
michael@0 1202 init.mLineno = aLineNumber;
michael@0 1203 init.mCancelable = true;
michael@0 1204 init.mBubbles = true;
michael@0 1205
michael@0 1206 if (aTarget) {
michael@0 1207 nsRefPtr<ErrorEvent> event =
michael@0 1208 ErrorEvent::Constructor(aTarget, NS_LITERAL_STRING("error"), init);
michael@0 1209 event->SetTrusted(true);
michael@0 1210
michael@0 1211 nsEventStatus status = nsEventStatus_eIgnore;
michael@0 1212 aTarget->DispatchDOMEvent(nullptr, event, nullptr, &status);
michael@0 1213
michael@0 1214 if (status == nsEventStatus_eConsumeNoDefault) {
michael@0 1215 return true;
michael@0 1216 }
michael@0 1217 }
michael@0 1218
michael@0 1219 // Now fire an event at the global object, but don't do that if the error
michael@0 1220 // code is too much recursion and this is the same script threw the error.
michael@0 1221 if (aFireAtScope && (aTarget || aErrorNumber != JSMSG_OVER_RECURSED)) {
michael@0 1222 JS::Rooted<JSObject*> target(aCx, JS::CurrentGlobalOrNull(aCx));
michael@0 1223 NS_ASSERTION(target, "This should never be null!");
michael@0 1224
michael@0 1225 nsEventStatus status = nsEventStatus_eIgnore;
michael@0 1226 nsIScriptGlobalObject* sgo;
michael@0 1227
michael@0 1228 if (aWorkerPrivate) {
michael@0 1229 WorkerGlobalScope* globalTarget = aWorkerPrivate->GlobalScope();
michael@0 1230 MOZ_ASSERT(target == globalTarget->GetWrapperPreserveColor());
michael@0 1231
michael@0 1232 nsRefPtr<ErrorEvent> event =
michael@0 1233 ErrorEvent::Constructor(aTarget, NS_LITERAL_STRING("error"), init);
michael@0 1234 event->SetTrusted(true);
michael@0 1235
michael@0 1236 nsIDOMEventTarget* target = static_cast<nsIDOMEventTarget*>(globalTarget);
michael@0 1237 if (NS_FAILED(EventDispatcher::DispatchDOMEvent(target, nullptr,
michael@0 1238 event, nullptr,
michael@0 1239 &status))) {
michael@0 1240 NS_WARNING("Failed to dispatch worker thread error event!");
michael@0 1241 status = nsEventStatus_eIgnore;
michael@0 1242 }
michael@0 1243 }
michael@0 1244 else if ((sgo = nsJSUtils::GetStaticScriptGlobal(target))) {
michael@0 1245 MOZ_ASSERT(NS_IsMainThread());
michael@0 1246
michael@0 1247 if (NS_FAILED(sgo->HandleScriptError(init, &status))) {
michael@0 1248 NS_WARNING("Failed to dispatch main thread error event!");
michael@0 1249 status = nsEventStatus_eIgnore;
michael@0 1250 }
michael@0 1251 }
michael@0 1252
michael@0 1253 // Was preventDefault() called?
michael@0 1254 if (status == nsEventStatus_eConsumeNoDefault) {
michael@0 1255 return true;
michael@0 1256 }
michael@0 1257 }
michael@0 1258 }
michael@0 1259
michael@0 1260 // Now fire a runnable to do the same on the parent's thread if we can.
michael@0 1261 if (aWorkerPrivate) {
michael@0 1262 nsRefPtr<ReportErrorRunnable> runnable =
michael@0 1263 new ReportErrorRunnable(aWorkerPrivate, aMessage, aFilename, aLine,
michael@0 1264 aLineNumber, aColumnNumber, aFlags,
michael@0 1265 aErrorNumber);
michael@0 1266 return runnable->Dispatch(aCx);
michael@0 1267 }
michael@0 1268
michael@0 1269 // Otherwise log an error to the error console.
michael@0 1270 LogErrorToConsole(aMessage, aFilename, aLine, aLineNumber, aColumnNumber,
michael@0 1271 aFlags, aInnerWindowId);
michael@0 1272 return true;
michael@0 1273 }
michael@0 1274
michael@0 1275 private:
michael@0 1276 ReportErrorRunnable(WorkerPrivate* aWorkerPrivate, const nsString& aMessage,
michael@0 1277 const nsString& aFilename, const nsString& aLine,
michael@0 1278 uint32_t aLineNumber, uint32_t aColumnNumber,
michael@0 1279 uint32_t aFlags, uint32_t aErrorNumber)
michael@0 1280 : WorkerRunnable(aWorkerPrivate, ParentThreadUnchangedBusyCount),
michael@0 1281 mMessage(aMessage), mFilename(aFilename), mLine(aLine),
michael@0 1282 mLineNumber(aLineNumber), mColumnNumber(aColumnNumber), mFlags(aFlags),
michael@0 1283 mErrorNumber(aErrorNumber)
michael@0 1284 { }
michael@0 1285
michael@0 1286 virtual void
michael@0 1287 PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
michael@0 1288 bool aDispatchResult) MOZ_OVERRIDE
michael@0 1289 {
michael@0 1290 aWorkerPrivate->AssertIsOnWorkerThread();
michael@0 1291
michael@0 1292 // Dispatch may fail if the worker was canceled, no need to report that as
michael@0 1293 // an error, so don't call base class PostDispatch.
michael@0 1294 }
michael@0 1295
michael@0 1296 virtual bool
michael@0 1297 WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
michael@0 1298 {
michael@0 1299 // Don't fire this event if the JS object has been disconnected from the
michael@0 1300 // private object.
michael@0 1301 if (!aWorkerPrivate->IsAcceptingEvents()) {
michael@0 1302 return true;
michael@0 1303 }
michael@0 1304
michael@0 1305 JS::Rooted<JSObject*> target(aCx, aWorkerPrivate->GetWrapper());
michael@0 1306
michael@0 1307 uint64_t innerWindowId;
michael@0 1308 bool fireAtScope = true;
michael@0 1309
michael@0 1310 WorkerPrivate* parent = aWorkerPrivate->GetParent();
michael@0 1311 if (parent) {
michael@0 1312 innerWindowId = 0;
michael@0 1313 }
michael@0 1314 else {
michael@0 1315 AssertIsOnMainThread();
michael@0 1316
michael@0 1317 if (aWorkerPrivate->IsSuspended()) {
michael@0 1318 aWorkerPrivate->QueueRunnable(this);
michael@0 1319 return true;
michael@0 1320 }
michael@0 1321
michael@0 1322 if (aWorkerPrivate->IsSharedWorker()) {
michael@0 1323 aWorkerPrivate->BroadcastErrorToSharedWorkers(aCx, mMessage, mFilename,
michael@0 1324 mLine, mLineNumber,
michael@0 1325 mColumnNumber, mFlags);
michael@0 1326 return true;
michael@0 1327 }
michael@0 1328
michael@0 1329 aWorkerPrivate->AssertInnerWindowIsCorrect();
michael@0 1330
michael@0 1331 innerWindowId = aWorkerPrivate->GetInnerWindowId();
michael@0 1332 }
michael@0 1333
michael@0 1334 return ReportError(aCx, parent, fireAtScope, aWorkerPrivate, mMessage,
michael@0 1335 mFilename, mLine, mLineNumber, mColumnNumber, mFlags,
michael@0 1336 mErrorNumber, innerWindowId);
michael@0 1337 }
michael@0 1338 };
michael@0 1339
michael@0 1340 class TimerRunnable MOZ_FINAL : public WorkerRunnable
michael@0 1341 {
michael@0 1342 public:
michael@0 1343 TimerRunnable(WorkerPrivate* aWorkerPrivate)
michael@0 1344 : WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
michael@0 1345 { }
michael@0 1346
michael@0 1347 private:
michael@0 1348 virtual bool
michael@0 1349 PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
michael@0 1350 {
michael@0 1351 // Silence bad assertions.
michael@0 1352 return true;
michael@0 1353 }
michael@0 1354
michael@0 1355 virtual void
michael@0 1356 PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
michael@0 1357 bool aDispatchResult) MOZ_OVERRIDE
michael@0 1358 {
michael@0 1359 // Silence bad assertions.
michael@0 1360 }
michael@0 1361
michael@0 1362 virtual bool
michael@0 1363 WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
michael@0 1364 {
michael@0 1365 return aWorkerPrivate->RunExpiredTimeouts(aCx);
michael@0 1366 }
michael@0 1367 };
michael@0 1368
michael@0 1369 void
michael@0 1370 DummyCallback(nsITimer* aTimer, void* aClosure)
michael@0 1371 {
michael@0 1372 // Nothing!
michael@0 1373 }
michael@0 1374
michael@0 1375 class TimerThreadEventTarget MOZ_FINAL : public nsIEventTarget
michael@0 1376 {
michael@0 1377 WorkerPrivate* mWorkerPrivate;
michael@0 1378 nsRefPtr<WorkerRunnable> mWorkerRunnable;
michael@0 1379
michael@0 1380 public:
michael@0 1381 TimerThreadEventTarget(WorkerPrivate* aWorkerPrivate,
michael@0 1382 WorkerRunnable* aWorkerRunnable)
michael@0 1383 : mWorkerPrivate(aWorkerPrivate), mWorkerRunnable(aWorkerRunnable)
michael@0 1384 {
michael@0 1385 MOZ_ASSERT(aWorkerPrivate);
michael@0 1386 MOZ_ASSERT(aWorkerRunnable);
michael@0 1387 }
michael@0 1388
michael@0 1389 NS_DECL_THREADSAFE_ISUPPORTS
michael@0 1390
michael@0 1391 protected:
michael@0 1392 NS_IMETHOD
michael@0 1393 Dispatch(nsIRunnable* aRunnable, uint32_t aFlags) MOZ_OVERRIDE
michael@0 1394 {
michael@0 1395 // This should only happen on the timer thread.
michael@0 1396 MOZ_ASSERT(!NS_IsMainThread());
michael@0 1397 MOZ_ASSERT(aFlags == nsIEventTarget::DISPATCH_NORMAL);
michael@0 1398
michael@0 1399 nsRefPtr<TimerThreadEventTarget> kungFuDeathGrip = this;
michael@0 1400
michael@0 1401 // Run the runnable we're given now (should just call DummyCallback()),
michael@0 1402 // otherwise the timer thread will leak it... If we run this after
michael@0 1403 // dispatch running the event can race against resetting the timer.
michael@0 1404 aRunnable->Run();
michael@0 1405
michael@0 1406 // This can fail if we're racing to terminate or cancel, should be handled
michael@0 1407 // by the terminate or cancel code.
michael@0 1408 mWorkerRunnable->Dispatch(nullptr);
michael@0 1409
michael@0 1410 return NS_OK;
michael@0 1411 }
michael@0 1412
michael@0 1413 NS_IMETHOD
michael@0 1414 IsOnCurrentThread(bool* aIsOnCurrentThread) MOZ_OVERRIDE
michael@0 1415 {
michael@0 1416 MOZ_ASSERT(aIsOnCurrentThread);
michael@0 1417
michael@0 1418 nsresult rv = mWorkerPrivate->IsOnCurrentThread(aIsOnCurrentThread);
michael@0 1419 if (NS_WARN_IF(NS_FAILED(rv))) {
michael@0 1420 return rv;
michael@0 1421 }
michael@0 1422
michael@0 1423 return NS_OK;
michael@0 1424 }
michael@0 1425 };
michael@0 1426
michael@0 1427 class KillCloseEventRunnable MOZ_FINAL : public WorkerRunnable
michael@0 1428 {
michael@0 1429 nsCOMPtr<nsITimer> mTimer;
michael@0 1430
michael@0 1431 class KillScriptRunnable MOZ_FINAL : public WorkerControlRunnable
michael@0 1432 {
michael@0 1433 public:
michael@0 1434 KillScriptRunnable(WorkerPrivate* aWorkerPrivate)
michael@0 1435 : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
michael@0 1436 { }
michael@0 1437
michael@0 1438 private:
michael@0 1439 virtual bool
michael@0 1440 PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
michael@0 1441 {
michael@0 1442 // Silence bad assertions, this is dispatched from the timer thread.
michael@0 1443 return true;
michael@0 1444 }
michael@0 1445
michael@0 1446 virtual void
michael@0 1447 PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
michael@0 1448 bool aDispatchResult) MOZ_OVERRIDE
michael@0 1449 {
michael@0 1450 // Silence bad assertions, this is dispatched from the timer thread.
michael@0 1451 }
michael@0 1452
michael@0 1453 virtual bool
michael@0 1454 WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
michael@0 1455 {
michael@0 1456 // Kill running script.
michael@0 1457 return false;
michael@0 1458 }
michael@0 1459 };
michael@0 1460
michael@0 1461 public:
michael@0 1462 KillCloseEventRunnable(WorkerPrivate* aWorkerPrivate)
michael@0 1463 : WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
michael@0 1464 { }
michael@0 1465
michael@0 1466 bool
michael@0 1467 SetTimeout(JSContext* aCx, uint32_t aDelayMS)
michael@0 1468 {
michael@0 1469 nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID);
michael@0 1470 if (!timer) {
michael@0 1471 JS_ReportError(aCx, "Failed to create timer!");
michael@0 1472 return false;
michael@0 1473 }
michael@0 1474
michael@0 1475 nsRefPtr<KillScriptRunnable> runnable =
michael@0 1476 new KillScriptRunnable(mWorkerPrivate);
michael@0 1477
michael@0 1478 nsRefPtr<TimerThreadEventTarget> target =
michael@0 1479 new TimerThreadEventTarget(mWorkerPrivate, runnable);
michael@0 1480
michael@0 1481 if (NS_FAILED(timer->SetTarget(target))) {
michael@0 1482 JS_ReportError(aCx, "Failed to set timer's target!");
michael@0 1483 return false;
michael@0 1484 }
michael@0 1485
michael@0 1486 if (NS_FAILED(timer->InitWithFuncCallback(DummyCallback, nullptr, aDelayMS,
michael@0 1487 nsITimer::TYPE_ONE_SHOT))) {
michael@0 1488 JS_ReportError(aCx, "Failed to start timer!");
michael@0 1489 return false;
michael@0 1490 }
michael@0 1491
michael@0 1492 mTimer.swap(timer);
michael@0 1493 return true;
michael@0 1494 }
michael@0 1495
michael@0 1496 private:
michael@0 1497 ~KillCloseEventRunnable()
michael@0 1498 {
michael@0 1499 if (mTimer) {
michael@0 1500 mTimer->Cancel();
michael@0 1501 }
michael@0 1502 }
michael@0 1503
michael@0 1504 virtual bool
michael@0 1505 PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
michael@0 1506 {
michael@0 1507 MOZ_ASSUME_UNREACHABLE("Don't call Dispatch() on KillCloseEventRunnable!");
michael@0 1508 }
michael@0 1509
michael@0 1510 virtual void
michael@0 1511 PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
michael@0 1512 bool aDispatchResult) MOZ_OVERRIDE
michael@0 1513 {
michael@0 1514 MOZ_ASSUME_UNREACHABLE("Don't call Dispatch() on KillCloseEventRunnable!");
michael@0 1515 }
michael@0 1516
michael@0 1517 virtual bool
michael@0 1518 WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
michael@0 1519 {
michael@0 1520 if (mTimer) {
michael@0 1521 mTimer->Cancel();
michael@0 1522 mTimer = nullptr;
michael@0 1523 }
michael@0 1524
michael@0 1525 return true;
michael@0 1526 }
michael@0 1527 };
michael@0 1528
michael@0 1529 class UpdateRuntimeAndContextOptionsRunnable MOZ_FINAL : public WorkerControlRunnable
michael@0 1530 {
michael@0 1531 JS::RuntimeOptions mRuntimeOptions;
michael@0 1532 JS::ContextOptions mContentCxOptions;
michael@0 1533 JS::ContextOptions mChromeCxOptions;
michael@0 1534
michael@0 1535 public:
michael@0 1536 UpdateRuntimeAndContextOptionsRunnable(
michael@0 1537 WorkerPrivate* aWorkerPrivate,
michael@0 1538 const JS::RuntimeOptions& aRuntimeOptions,
michael@0 1539 const JS::ContextOptions& aContentCxOptions,
michael@0 1540 const JS::ContextOptions& aChromeCxOptions)
michael@0 1541 : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
michael@0 1542 mRuntimeOptions(aRuntimeOptions),
michael@0 1543 mContentCxOptions(aContentCxOptions),
michael@0 1544 mChromeCxOptions(aChromeCxOptions)
michael@0 1545 { }
michael@0 1546
michael@0 1547 private:
michael@0 1548 virtual bool
michael@0 1549 WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
michael@0 1550 {
michael@0 1551 aWorkerPrivate->UpdateRuntimeAndContextOptionsInternal(aCx,
michael@0 1552 mRuntimeOptions,
michael@0 1553 mContentCxOptions,
michael@0 1554 mChromeCxOptions);
michael@0 1555 return true;
michael@0 1556 }
michael@0 1557 };
michael@0 1558
michael@0 1559 class UpdatePreferenceRunnable MOZ_FINAL : public WorkerControlRunnable
michael@0 1560 {
michael@0 1561 WorkerPreference mPref;
michael@0 1562 bool mValue;
michael@0 1563
michael@0 1564 public:
michael@0 1565 UpdatePreferenceRunnable(WorkerPrivate* aWorkerPrivate,
michael@0 1566 WorkerPreference aPref,
michael@0 1567 bool aValue)
michael@0 1568 : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
michael@0 1569 mPref(aPref),
michael@0 1570 mValue(aValue)
michael@0 1571 { }
michael@0 1572
michael@0 1573 virtual bool
michael@0 1574 WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
michael@0 1575 {
michael@0 1576 aWorkerPrivate->UpdatePreferenceInternal(aCx, mPref, mValue);
michael@0 1577 return true;
michael@0 1578 }
michael@0 1579 };
michael@0 1580
michael@0 1581 class UpdateJSWorkerMemoryParameterRunnable MOZ_FINAL :
michael@0 1582 public WorkerControlRunnable
michael@0 1583 {
michael@0 1584 uint32_t mValue;
michael@0 1585 JSGCParamKey mKey;
michael@0 1586
michael@0 1587 public:
michael@0 1588 UpdateJSWorkerMemoryParameterRunnable(WorkerPrivate* aWorkerPrivate,
michael@0 1589 JSGCParamKey aKey,
michael@0 1590 uint32_t aValue)
michael@0 1591 : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
michael@0 1592 mValue(aValue), mKey(aKey)
michael@0 1593 { }
michael@0 1594
michael@0 1595 private:
michael@0 1596 virtual bool
michael@0 1597 WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
michael@0 1598 {
michael@0 1599 aWorkerPrivate->UpdateJSWorkerMemoryParameterInternal(aCx, mKey, mValue);
michael@0 1600 return true;
michael@0 1601 }
michael@0 1602 };
michael@0 1603
michael@0 1604 #ifdef JS_GC_ZEAL
michael@0 1605 class UpdateGCZealRunnable MOZ_FINAL : public WorkerControlRunnable
michael@0 1606 {
michael@0 1607 uint8_t mGCZeal;
michael@0 1608 uint32_t mFrequency;
michael@0 1609
michael@0 1610 public:
michael@0 1611 UpdateGCZealRunnable(WorkerPrivate* aWorkerPrivate,
michael@0 1612 uint8_t aGCZeal,
michael@0 1613 uint32_t aFrequency)
michael@0 1614 : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
michael@0 1615 mGCZeal(aGCZeal), mFrequency(aFrequency)
michael@0 1616 { }
michael@0 1617
michael@0 1618 private:
michael@0 1619 virtual bool
michael@0 1620 WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
michael@0 1621 {
michael@0 1622 aWorkerPrivate->UpdateGCZealInternal(aCx, mGCZeal, mFrequency);
michael@0 1623 return true;
michael@0 1624 }
michael@0 1625 };
michael@0 1626 #endif
michael@0 1627
michael@0 1628 class GarbageCollectRunnable MOZ_FINAL : public WorkerControlRunnable
michael@0 1629 {
michael@0 1630 bool mShrinking;
michael@0 1631 bool mCollectChildren;
michael@0 1632
michael@0 1633 public:
michael@0 1634 GarbageCollectRunnable(WorkerPrivate* aWorkerPrivate, bool aShrinking,
michael@0 1635 bool aCollectChildren)
michael@0 1636 : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
michael@0 1637 mShrinking(aShrinking), mCollectChildren(aCollectChildren)
michael@0 1638 { }
michael@0 1639
michael@0 1640 private:
michael@0 1641 virtual bool
michael@0 1642 PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
michael@0 1643 {
michael@0 1644 // Silence bad assertions, this can be dispatched from either the main
michael@0 1645 // thread or the timer thread..
michael@0 1646 return true;
michael@0 1647 }
michael@0 1648
michael@0 1649 virtual void
michael@0 1650 PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
michael@0 1651 bool aDispatchResult) MOZ_OVERRIDE
michael@0 1652 {
michael@0 1653 // Silence bad assertions, this can be dispatched from either the main
michael@0 1654 // thread or the timer thread..
michael@0 1655 }
michael@0 1656
michael@0 1657 virtual bool
michael@0 1658 WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
michael@0 1659 {
michael@0 1660 aWorkerPrivate->GarbageCollectInternal(aCx, mShrinking, mCollectChildren);
michael@0 1661 return true;
michael@0 1662 }
michael@0 1663 };
michael@0 1664
michael@0 1665 class CycleCollectRunnable : public WorkerControlRunnable
michael@0 1666 {
michael@0 1667 bool mCollectChildren;
michael@0 1668
michael@0 1669 public:
michael@0 1670 CycleCollectRunnable(WorkerPrivate* aWorkerPrivate, bool aCollectChildren)
michael@0 1671 : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
michael@0 1672 mCollectChildren(aCollectChildren)
michael@0 1673 { }
michael@0 1674
michael@0 1675 bool
michael@0 1676 WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
michael@0 1677 {
michael@0 1678 aWorkerPrivate->CycleCollectInternal(aCx, mCollectChildren);
michael@0 1679 return true;
michael@0 1680 }
michael@0 1681 };
michael@0 1682
michael@0 1683 class OfflineStatusChangeRunnable : public WorkerRunnable
michael@0 1684 {
michael@0 1685 public:
michael@0 1686 OfflineStatusChangeRunnable(WorkerPrivate* aWorkerPrivate, bool aIsOffline)
michael@0 1687 : WorkerRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount),
michael@0 1688 mIsOffline(aIsOffline)
michael@0 1689 {
michael@0 1690 }
michael@0 1691
michael@0 1692 bool
michael@0 1693 WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
michael@0 1694 {
michael@0 1695 aWorkerPrivate->OfflineStatusChangeEventInternal(aCx, mIsOffline);
michael@0 1696 return true;
michael@0 1697 }
michael@0 1698
michael@0 1699 private:
michael@0 1700 bool mIsOffline;
michael@0 1701 };
michael@0 1702
michael@0 1703 class WorkerJSRuntimeStats : public JS::RuntimeStats
michael@0 1704 {
michael@0 1705 const nsACString& mRtPath;
michael@0 1706
michael@0 1707 public:
michael@0 1708 WorkerJSRuntimeStats(const nsACString& aRtPath)
michael@0 1709 : JS::RuntimeStats(JsWorkerMallocSizeOf), mRtPath(aRtPath)
michael@0 1710 { }
michael@0 1711
michael@0 1712 ~WorkerJSRuntimeStats()
michael@0 1713 {
michael@0 1714 for (size_t i = 0; i != zoneStatsVector.length(); i++) {
michael@0 1715 delete static_cast<xpc::ZoneStatsExtras*>(zoneStatsVector[i].extra);
michael@0 1716 }
michael@0 1717
michael@0 1718 for (size_t i = 0; i != compartmentStatsVector.length(); i++) {
michael@0 1719 delete static_cast<xpc::CompartmentStatsExtras*>(compartmentStatsVector[i].extra);
michael@0 1720 }
michael@0 1721 }
michael@0 1722
michael@0 1723 virtual void
michael@0 1724 initExtraZoneStats(JS::Zone* aZone,
michael@0 1725 JS::ZoneStats* aZoneStats)
michael@0 1726 MOZ_OVERRIDE
michael@0 1727 {
michael@0 1728 MOZ_ASSERT(!aZoneStats->extra);
michael@0 1729
michael@0 1730 // ReportJSRuntimeExplicitTreeStats expects that
michael@0 1731 // aZoneStats->extra is a xpc::ZoneStatsExtras pointer.
michael@0 1732 xpc::ZoneStatsExtras* extras = new xpc::ZoneStatsExtras;
michael@0 1733 extras->pathPrefix = mRtPath;
michael@0 1734 extras->pathPrefix += nsPrintfCString("zone(0x%p)/", (void *)aZone);
michael@0 1735 aZoneStats->extra = extras;
michael@0 1736 }
michael@0 1737
michael@0 1738 virtual void
michael@0 1739 initExtraCompartmentStats(JSCompartment* aCompartment,
michael@0 1740 JS::CompartmentStats* aCompartmentStats)
michael@0 1741 MOZ_OVERRIDE
michael@0 1742 {
michael@0 1743 MOZ_ASSERT(!aCompartmentStats->extra);
michael@0 1744
michael@0 1745 // ReportJSRuntimeExplicitTreeStats expects that
michael@0 1746 // aCompartmentStats->extra is a xpc::CompartmentStatsExtras pointer.
michael@0 1747 xpc::CompartmentStatsExtras* extras = new xpc::CompartmentStatsExtras;
michael@0 1748
michael@0 1749 // This is the |jsPathPrefix|. Each worker has exactly two compartments:
michael@0 1750 // one for atoms, and one for everything else.
michael@0 1751 extras->jsPathPrefix.Assign(mRtPath);
michael@0 1752 extras->jsPathPrefix += nsPrintfCString("zone(0x%p)/",
michael@0 1753 (void *)js::GetCompartmentZone(aCompartment));
michael@0 1754 extras->jsPathPrefix += js::IsAtomsCompartment(aCompartment)
michael@0 1755 ? NS_LITERAL_CSTRING("compartment(web-worker-atoms)/")
michael@0 1756 : NS_LITERAL_CSTRING("compartment(web-worker)/");
michael@0 1757
michael@0 1758 // This should never be used when reporting with workers (hence the "?!").
michael@0 1759 extras->domPathPrefix.AssignLiteral("explicit/workers/?!/");
michael@0 1760
michael@0 1761 aCompartmentStats->extra = extras;
michael@0 1762 }
michael@0 1763 };
michael@0 1764
michael@0 1765 class MessagePortRunnable MOZ_FINAL : public WorkerRunnable
michael@0 1766 {
michael@0 1767 uint64_t mMessagePortSerial;
michael@0 1768 bool mConnect;
michael@0 1769
michael@0 1770 public:
michael@0 1771 MessagePortRunnable(WorkerPrivate* aWorkerPrivate,
michael@0 1772 uint64_t aMessagePortSerial,
michael@0 1773 bool aConnect)
michael@0 1774 : WorkerRunnable(aWorkerPrivate, aConnect ?
michael@0 1775 WorkerThreadModifyBusyCount :
michael@0 1776 WorkerThreadUnchangedBusyCount),
michael@0 1777 mMessagePortSerial(aMessagePortSerial), mConnect(aConnect)
michael@0 1778 { }
michael@0 1779
michael@0 1780 private:
michael@0 1781 ~MessagePortRunnable()
michael@0 1782 { }
michael@0 1783
michael@0 1784 virtual bool
michael@0 1785 WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
michael@0 1786 {
michael@0 1787 if (mConnect) {
michael@0 1788 return aWorkerPrivate->ConnectMessagePort(aCx, mMessagePortSerial);
michael@0 1789 }
michael@0 1790
michael@0 1791 aWorkerPrivate->DisconnectMessagePort(mMessagePortSerial);
michael@0 1792 return true;
michael@0 1793 }
michael@0 1794 };
michael@0 1795
michael@0 1796 #ifdef DEBUG
michael@0 1797
michael@0 1798 PRThread*
michael@0 1799 PRThreadFromThread(nsIThread* aThread)
michael@0 1800 {
michael@0 1801 MOZ_ASSERT(aThread);
michael@0 1802
michael@0 1803 PRThread* result;
michael@0 1804 MOZ_ASSERT(NS_SUCCEEDED(aThread->GetPRThread(&result)));
michael@0 1805 MOZ_ASSERT(result);
michael@0 1806
michael@0 1807 return result;
michael@0 1808 }
michael@0 1809
michael@0 1810 #endif // DEBUG
michael@0 1811
michael@0 1812 } /* anonymous namespace */
michael@0 1813
michael@0 1814 NS_IMPL_ISUPPORTS_INHERITED0(MainThreadReleaseRunnable, nsRunnable)
michael@0 1815
michael@0 1816 NS_IMPL_ISUPPORTS_INHERITED0(TopLevelWorkerFinishedRunnable, nsRunnable)
michael@0 1817
michael@0 1818 NS_IMPL_ISUPPORTS(TimerThreadEventTarget, nsIEventTarget)
michael@0 1819
michael@0 1820 template <class Derived>
michael@0 1821 class WorkerPrivateParent<Derived>::SynchronizeAndResumeRunnable MOZ_FINAL
michael@0 1822 : public nsRunnable
michael@0 1823 {
michael@0 1824 friend class nsRevocableEventPtr<SynchronizeAndResumeRunnable>;
michael@0 1825
michael@0 1826 WorkerPrivate* mWorkerPrivate;
michael@0 1827 nsCOMPtr<nsPIDOMWindow> mWindow;
michael@0 1828 nsCOMPtr<nsIScriptContext> mScriptContext;
michael@0 1829
michael@0 1830 public:
michael@0 1831 SynchronizeAndResumeRunnable(WorkerPrivate* aWorkerPrivate,
michael@0 1832 nsPIDOMWindow* aWindow,
michael@0 1833 nsIScriptContext* aScriptContext)
michael@0 1834 : mWorkerPrivate(aWorkerPrivate), mWindow(aWindow),
michael@0 1835 mScriptContext(aScriptContext)
michael@0 1836 {
michael@0 1837 AssertIsOnMainThread();
michael@0 1838 MOZ_ASSERT(aWorkerPrivate);
michael@0 1839 MOZ_ASSERT(aWindow);
michael@0 1840 MOZ_ASSERT(!aWorkerPrivate->GetParent());
michael@0 1841 }
michael@0 1842
michael@0 1843 private:
michael@0 1844 ~SynchronizeAndResumeRunnable()
michael@0 1845 { }
michael@0 1846
michael@0 1847 NS_IMETHOD
michael@0 1848 Run() MOZ_OVERRIDE
michael@0 1849 {
michael@0 1850 AssertIsOnMainThread();
michael@0 1851
michael@0 1852 if (mWorkerPrivate) {
michael@0 1853 AutoPushJSContext cx(mScriptContext ?
michael@0 1854 mScriptContext->GetNativeContext() :
michael@0 1855 nsContentUtils::GetSafeJSContext());
michael@0 1856
michael@0 1857 if (!mWorkerPrivate->Resume(cx, mWindow)) {
michael@0 1858 JS_ReportPendingException(cx);
michael@0 1859 }
michael@0 1860 }
michael@0 1861
michael@0 1862 return NS_OK;
michael@0 1863 }
michael@0 1864
michael@0 1865 void
michael@0 1866 Revoke()
michael@0 1867 {
michael@0 1868 AssertIsOnMainThread();
michael@0 1869 MOZ_ASSERT(mWorkerPrivate);
michael@0 1870 MOZ_ASSERT(mWindow);
michael@0 1871
michael@0 1872 mWorkerPrivate = nullptr;
michael@0 1873 mWindow = nullptr;
michael@0 1874 mScriptContext = nullptr;
michael@0 1875 }
michael@0 1876 };
michael@0 1877
michael@0 1878 template <class Derived>
michael@0 1879 class WorkerPrivateParent<Derived>::EventTarget MOZ_FINAL
michael@0 1880 : public nsIEventTarget
michael@0 1881 {
michael@0 1882 // This mutex protects mWorkerPrivate and must be acquired *before* the
michael@0 1883 // WorkerPrivate's mutex whenever they must both be held.
michael@0 1884 mozilla::Mutex mMutex;
michael@0 1885 WorkerPrivate* mWorkerPrivate;
michael@0 1886 nsIEventTarget* mWeakNestedEventTarget;
michael@0 1887 nsCOMPtr<nsIEventTarget> mNestedEventTarget;
michael@0 1888
michael@0 1889 public:
michael@0 1890 EventTarget(WorkerPrivate* aWorkerPrivate)
michael@0 1891 : mMutex("WorkerPrivateParent::EventTarget::mMutex"),
michael@0 1892 mWorkerPrivate(aWorkerPrivate), mWeakNestedEventTarget(nullptr)
michael@0 1893 {
michael@0 1894 MOZ_ASSERT(aWorkerPrivate);
michael@0 1895 }
michael@0 1896
michael@0 1897 EventTarget(WorkerPrivate* aWorkerPrivate, nsIEventTarget* aNestedEventTarget)
michael@0 1898 : mMutex("WorkerPrivateParent::EventTarget::mMutex"),
michael@0 1899 mWorkerPrivate(aWorkerPrivate), mWeakNestedEventTarget(aNestedEventTarget),
michael@0 1900 mNestedEventTarget(aNestedEventTarget)
michael@0 1901 {
michael@0 1902 MOZ_ASSERT(aWorkerPrivate);
michael@0 1903 MOZ_ASSERT(aNestedEventTarget);
michael@0 1904 }
michael@0 1905
michael@0 1906 void
michael@0 1907 Disable()
michael@0 1908 {
michael@0 1909 nsCOMPtr<nsIEventTarget> nestedEventTarget;
michael@0 1910 {
michael@0 1911 MutexAutoLock lock(mMutex);
michael@0 1912
michael@0 1913 MOZ_ASSERT(mWorkerPrivate);
michael@0 1914 mWorkerPrivate = nullptr;
michael@0 1915 mNestedEventTarget.swap(nestedEventTarget);
michael@0 1916 }
michael@0 1917 }
michael@0 1918
michael@0 1919 nsIEventTarget*
michael@0 1920 GetWeakNestedEventTarget() const
michael@0 1921 {
michael@0 1922 MOZ_ASSERT(mWeakNestedEventTarget);
michael@0 1923 return mWeakNestedEventTarget;
michael@0 1924 }
michael@0 1925
michael@0 1926 NS_DECL_THREADSAFE_ISUPPORTS
michael@0 1927 NS_DECL_NSIEVENTTARGET
michael@0 1928
michael@0 1929 private:
michael@0 1930 ~EventTarget()
michael@0 1931 { }
michael@0 1932 };
michael@0 1933
michael@0 1934 struct WorkerPrivate::TimeoutInfo
michael@0 1935 {
michael@0 1936 TimeoutInfo()
michael@0 1937 : mTimeoutCallable(JS::UndefinedValue()), mLineNumber(0), mId(0),
michael@0 1938 mIsInterval(false), mCanceled(false)
michael@0 1939 {
michael@0 1940 MOZ_COUNT_CTOR(mozilla::dom::workers::WorkerPrivate::TimeoutInfo);
michael@0 1941 }
michael@0 1942
michael@0 1943 ~TimeoutInfo()
michael@0 1944 {
michael@0 1945 MOZ_COUNT_DTOR(mozilla::dom::workers::WorkerPrivate::TimeoutInfo);
michael@0 1946 }
michael@0 1947
michael@0 1948 bool operator==(const TimeoutInfo& aOther)
michael@0 1949 {
michael@0 1950 return mTargetTime == aOther.mTargetTime;
michael@0 1951 }
michael@0 1952
michael@0 1953 bool operator<(const TimeoutInfo& aOther)
michael@0 1954 {
michael@0 1955 return mTargetTime < aOther.mTargetTime;
michael@0 1956 }
michael@0 1957
michael@0 1958 JS::Heap<JS::Value> mTimeoutCallable;
michael@0 1959 nsString mTimeoutString;
michael@0 1960 nsTArray<JS::Heap<JS::Value> > mExtraArgVals;
michael@0 1961 mozilla::TimeStamp mTargetTime;
michael@0 1962 mozilla::TimeDuration mInterval;
michael@0 1963 nsCString mFilename;
michael@0 1964 uint32_t mLineNumber;
michael@0 1965 int32_t mId;
michael@0 1966 bool mIsInterval;
michael@0 1967 bool mCanceled;
michael@0 1968 };
michael@0 1969
michael@0 1970 class WorkerPrivate::MemoryReporter MOZ_FINAL : public nsIMemoryReporter
michael@0 1971 {
michael@0 1972 NS_DECL_THREADSAFE_ISUPPORTS
michael@0 1973
michael@0 1974 friend class WorkerPrivate;
michael@0 1975
michael@0 1976 SharedMutex mMutex;
michael@0 1977 WorkerPrivate* mWorkerPrivate;
michael@0 1978 nsCString mRtPath;
michael@0 1979 bool mAlreadyMappedToAddon;
michael@0 1980
michael@0 1981 public:
michael@0 1982 MemoryReporter(WorkerPrivate* aWorkerPrivate)
michael@0 1983 : mMutex(aWorkerPrivate->mMutex), mWorkerPrivate(aWorkerPrivate),
michael@0 1984 mAlreadyMappedToAddon(false)
michael@0 1985 {
michael@0 1986 aWorkerPrivate->AssertIsOnWorkerThread();
michael@0 1987
michael@0 1988 nsCString escapedDomain(aWorkerPrivate->Domain());
michael@0 1989 escapedDomain.ReplaceChar('/', '\\');
michael@0 1990
michael@0 1991 NS_ConvertUTF16toUTF8 escapedURL(aWorkerPrivate->ScriptURL());
michael@0 1992 escapedURL.ReplaceChar('/', '\\');
michael@0 1993
michael@0 1994 nsAutoCString addressString;
michael@0 1995 addressString.AppendPrintf("0x%p", static_cast<void*>(aWorkerPrivate));
michael@0 1996
michael@0 1997 mRtPath = NS_LITERAL_CSTRING("explicit/workers/workers(") +
michael@0 1998 escapedDomain + NS_LITERAL_CSTRING(")/worker(") +
michael@0 1999 escapedURL + NS_LITERAL_CSTRING(", ") + addressString +
michael@0 2000 NS_LITERAL_CSTRING(")/");
michael@0 2001 }
michael@0 2002
michael@0 2003 NS_IMETHOD
michael@0 2004 CollectReports(nsIMemoryReporterCallback* aCallback,
michael@0 2005 nsISupports* aClosure)
michael@0 2006 {
michael@0 2007 AssertIsOnMainThread();
michael@0 2008
michael@0 2009 // Assumes that WorkerJSRuntimeStats will hold a reference to mRtPath,
michael@0 2010 // and not a copy, as TryToMapAddon() may later modify the string again.
michael@0 2011 WorkerJSRuntimeStats rtStats(mRtPath);
michael@0 2012
michael@0 2013 {
michael@0 2014 MutexAutoLock lock(mMutex);
michael@0 2015
michael@0 2016 TryToMapAddon();
michael@0 2017
michael@0 2018 if (!mWorkerPrivate ||
michael@0 2019 !mWorkerPrivate->BlockAndCollectRuntimeStats(&rtStats)) {
michael@0 2020 // Returning NS_OK here will effectively report 0 memory.
michael@0 2021 return NS_OK;
michael@0 2022 }
michael@0 2023 }
michael@0 2024
michael@0 2025 return xpc::ReportJSRuntimeExplicitTreeStats(rtStats, mRtPath,
michael@0 2026 aCallback, aClosure);
michael@0 2027 }
michael@0 2028
michael@0 2029 private:
michael@0 2030 ~MemoryReporter()
michael@0 2031 { }
michael@0 2032
michael@0 2033 void
michael@0 2034 Disable()
michael@0 2035 {
michael@0 2036 // Called from WorkerPrivate::DisableMemoryReporter.
michael@0 2037 mMutex.AssertCurrentThreadOwns();
michael@0 2038
michael@0 2039 NS_ASSERTION(mWorkerPrivate, "Disabled more than once!");
michael@0 2040 mWorkerPrivate = nullptr;
michael@0 2041 }
michael@0 2042
michael@0 2043 // Only call this from the main thread and under mMutex lock.
michael@0 2044 void
michael@0 2045 TryToMapAddon()
michael@0 2046 {
michael@0 2047 AssertIsOnMainThread();
michael@0 2048 mMutex.AssertCurrentThreadOwns();
michael@0 2049
michael@0 2050 if (mAlreadyMappedToAddon || !mWorkerPrivate) {
michael@0 2051 return;
michael@0 2052 }
michael@0 2053
michael@0 2054 nsCOMPtr<nsIURI> scriptURI;
michael@0 2055 if (NS_FAILED(NS_NewURI(getter_AddRefs(scriptURI),
michael@0 2056 mWorkerPrivate->ScriptURL()))) {
michael@0 2057 return;
michael@0 2058 }
michael@0 2059
michael@0 2060 mAlreadyMappedToAddon = true;
michael@0 2061
michael@0 2062 if (XRE_GetProcessType() != GeckoProcessType_Default) {
michael@0 2063 // Only try to access the service from the main process.
michael@0 2064 return;
michael@0 2065 }
michael@0 2066
michael@0 2067 nsAutoCString addonId;
michael@0 2068 bool ok;
michael@0 2069 nsCOMPtr<amIAddonManager> addonManager =
michael@0 2070 do_GetService("@mozilla.org/addons/integration;1");
michael@0 2071
michael@0 2072 if (!addonManager ||
michael@0 2073 NS_FAILED(addonManager->MapURIToAddonID(scriptURI, addonId, &ok)) ||
michael@0 2074 !ok) {
michael@0 2075 return;
michael@0 2076 }
michael@0 2077
michael@0 2078 static const size_t explicitLength = strlen("explicit/");
michael@0 2079 addonId.Insert(NS_LITERAL_CSTRING("add-ons/"), 0);
michael@0 2080 addonId += "/";
michael@0 2081 mRtPath.Insert(addonId, explicitLength);
michael@0 2082 }
michael@0 2083 };
michael@0 2084
michael@0 2085 NS_IMPL_ISUPPORTS(WorkerPrivate::MemoryReporter, nsIMemoryReporter)
michael@0 2086
michael@0 2087 WorkerPrivate::SyncLoopInfo::SyncLoopInfo(EventTarget* aEventTarget)
michael@0 2088 : mEventTarget(aEventTarget), mCompleted(false), mResult(false)
michael@0 2089 #ifdef DEBUG
michael@0 2090 , mHasRun(false)
michael@0 2091 #endif
michael@0 2092 {
michael@0 2093 }
michael@0 2094
michael@0 2095 // Can't use NS_IMPL_CYCLE_COLLECTION_CLASS(WorkerPrivateParent) because of the
michael@0 2096 // templates.
michael@0 2097 template <class Derived>
michael@0 2098 typename WorkerPrivateParent<Derived>::cycleCollection
michael@0 2099 WorkerPrivateParent<Derived>::_cycleCollectorGlobal =
michael@0 2100 WorkerPrivateParent<Derived>::cycleCollection();
michael@0 2101
michael@0 2102 template <class Derived>
michael@0 2103 WorkerPrivateParent<Derived>::WorkerPrivateParent(
michael@0 2104 JSContext* aCx,
michael@0 2105 WorkerPrivate* aParent,
michael@0 2106 const nsAString& aScriptURL,
michael@0 2107 bool aIsChromeWorker,
michael@0 2108 WorkerType aWorkerType,
michael@0 2109 const nsACString& aSharedWorkerName,
michael@0 2110 LoadInfo& aLoadInfo)
michael@0 2111 : mMutex("WorkerPrivateParent Mutex"),
michael@0 2112 mCondVar(mMutex, "WorkerPrivateParent CondVar"),
michael@0 2113 mMemoryReportCondVar(mMutex, "WorkerPrivateParent Memory Report CondVar"),
michael@0 2114 mParent(aParent), mScriptURL(aScriptURL),
michael@0 2115 mSharedWorkerName(aSharedWorkerName), mBusyCount(0), mMessagePortSerial(0),
michael@0 2116 mParentStatus(Pending), mParentSuspended(false),
michael@0 2117 mIsChromeWorker(aIsChromeWorker), mMainThreadObjectsForgotten(false),
michael@0 2118 mWorkerType(aWorkerType),
michael@0 2119 mCreationTimeStamp(TimeStamp::Now())
michael@0 2120 {
michael@0 2121 SetIsDOMBinding();
michael@0 2122
michael@0 2123 MOZ_ASSERT_IF(IsSharedWorker(), !aSharedWorkerName.IsVoid() &&
michael@0 2124 NS_IsMainThread());
michael@0 2125 MOZ_ASSERT_IF(!IsSharedWorker(), aSharedWorkerName.IsEmpty());
michael@0 2126
michael@0 2127 if (aLoadInfo.mWindow) {
michael@0 2128 AssertIsOnMainThread();
michael@0 2129 MOZ_ASSERT(aLoadInfo.mWindow->IsInnerWindow(),
michael@0 2130 "Should have inner window here!");
michael@0 2131 BindToOwner(aLoadInfo.mWindow);
michael@0 2132 }
michael@0 2133
michael@0 2134 mLoadInfo.StealFrom(aLoadInfo);
michael@0 2135
michael@0 2136 if (aParent) {
michael@0 2137 aParent->AssertIsOnWorkerThread();
michael@0 2138
michael@0 2139 aParent->CopyJSSettings(mJSSettings);
michael@0 2140 }
michael@0 2141 else {
michael@0 2142 AssertIsOnMainThread();
michael@0 2143
michael@0 2144 RuntimeService::GetDefaultJSSettings(mJSSettings);
michael@0 2145 }
michael@0 2146 }
michael@0 2147
michael@0 2148 template <class Derived>
michael@0 2149 WorkerPrivateParent<Derived>::~WorkerPrivateParent()
michael@0 2150 {
michael@0 2151 DropJSObjects(this);
michael@0 2152 }
michael@0 2153
michael@0 2154 template <class Derived>
michael@0 2155 JSObject*
michael@0 2156 WorkerPrivateParent<Derived>::WrapObject(JSContext* aCx)
michael@0 2157 {
michael@0 2158 MOZ_ASSERT(!IsSharedWorker(),
michael@0 2159 "We should never wrap a WorkerPrivate for a SharedWorker");
michael@0 2160
michael@0 2161 AssertIsOnParentThread();
michael@0 2162
michael@0 2163 // XXXkhuey this should not need to be rooted, the analysis is dumb.
michael@0 2164 // See bug 980181.
michael@0 2165 JS::Rooted<JSObject*> wrapper(aCx,
michael@0 2166 WorkerBinding::Wrap(aCx, ParentAsWorkerPrivate()));
michael@0 2167 if (wrapper) {
michael@0 2168 MOZ_ALWAYS_TRUE(TryPreserveWrapper(wrapper));
michael@0 2169 }
michael@0 2170
michael@0 2171 return wrapper;
michael@0 2172 }
michael@0 2173
michael@0 2174 template <class Derived>
michael@0 2175 nsresult
michael@0 2176 WorkerPrivateParent<Derived>::DispatchPrivate(WorkerRunnable* aRunnable,
michael@0 2177 nsIEventTarget* aSyncLoopTarget)
michael@0 2178 {
michael@0 2179 // May be called on any thread!
michael@0 2180
michael@0 2181 WorkerPrivate* self = ParentAsWorkerPrivate();
michael@0 2182
michael@0 2183 {
michael@0 2184 MutexAutoLock lock(mMutex);
michael@0 2185
michael@0 2186 MOZ_ASSERT_IF(aSyncLoopTarget, self->mThread);
michael@0 2187
michael@0 2188 if (!self->mThread) {
michael@0 2189 if (ParentStatus() == Pending || self->mStatus == Pending) {
michael@0 2190 mPreStartRunnables.AppendElement(aRunnable);
michael@0 2191 return NS_OK;
michael@0 2192 }
michael@0 2193
michael@0 2194 NS_WARNING("Using a worker event target after the thread has already"
michael@0 2195 "been released!");
michael@0 2196 return NS_ERROR_UNEXPECTED;
michael@0 2197 }
michael@0 2198
michael@0 2199 if (self->mStatus == Dead ||
michael@0 2200 (!aSyncLoopTarget && ParentStatus() > Running)) {
michael@0 2201 NS_WARNING("A runnable was posted to a worker that is already shutting "
michael@0 2202 "down!");
michael@0 2203 return NS_ERROR_UNEXPECTED;
michael@0 2204 }
michael@0 2205
michael@0 2206 nsCOMPtr<nsIEventTarget> target;
michael@0 2207 if (aSyncLoopTarget) {
michael@0 2208 target = aSyncLoopTarget;
michael@0 2209 }
michael@0 2210 else {
michael@0 2211 target = self->mThread;
michael@0 2212 }
michael@0 2213
michael@0 2214 nsresult rv = target->Dispatch(aRunnable, NS_DISPATCH_NORMAL);
michael@0 2215 if (NS_WARN_IF(NS_FAILED(rv))) {
michael@0 2216 return rv;
michael@0 2217 }
michael@0 2218
michael@0 2219 mCondVar.Notify();
michael@0 2220 }
michael@0 2221
michael@0 2222 return NS_OK;
michael@0 2223 }
michael@0 2224
michael@0 2225 template <class Derived>
michael@0 2226 nsresult
michael@0 2227 WorkerPrivateParent<Derived>::DispatchControlRunnable(
michael@0 2228 WorkerControlRunnable* aWorkerControlRunnable)
michael@0 2229 {
michael@0 2230 // May be called on any thread!
michael@0 2231
michael@0 2232 MOZ_ASSERT(aWorkerControlRunnable);
michael@0 2233
michael@0 2234 nsRefPtr<WorkerControlRunnable> runnable = aWorkerControlRunnable;
michael@0 2235
michael@0 2236 WorkerPrivate* self = ParentAsWorkerPrivate();
michael@0 2237
michael@0 2238 {
michael@0 2239 MutexAutoLock lock(mMutex);
michael@0 2240
michael@0 2241 if (self->mStatus == Dead) {
michael@0 2242 NS_WARNING("A control runnable was posted to a worker that is already "
michael@0 2243 "shutting down!");
michael@0 2244 return NS_ERROR_UNEXPECTED;
michael@0 2245 }
michael@0 2246
michael@0 2247 // Transfer ownership to the control queue.
michael@0 2248 self->mControlQueue.Push(runnable.forget().take());
michael@0 2249
michael@0 2250 if (JSContext* cx = self->mJSContext) {
michael@0 2251 MOZ_ASSERT(self->mThread);
michael@0 2252
michael@0 2253 JSRuntime* rt = JS_GetRuntime(cx);
michael@0 2254 MOZ_ASSERT(rt);
michael@0 2255
michael@0 2256 JS_RequestInterruptCallback(rt);
michael@0 2257 }
michael@0 2258
michael@0 2259 mCondVar.Notify();
michael@0 2260 }
michael@0 2261
michael@0 2262 return NS_OK;
michael@0 2263 }
michael@0 2264
michael@0 2265 template <class Derived>
michael@0 2266 already_AddRefed<WorkerRunnable>
michael@0 2267 WorkerPrivateParent<Derived>::MaybeWrapAsWorkerRunnable(nsIRunnable* aRunnable)
michael@0 2268 {
michael@0 2269 // May be called on any thread!
michael@0 2270
michael@0 2271 MOZ_ASSERT(aRunnable);
michael@0 2272
michael@0 2273 nsRefPtr<WorkerRunnable> workerRunnable =
michael@0 2274 WorkerRunnable::FromRunnable(aRunnable);
michael@0 2275 if (workerRunnable) {
michael@0 2276 return workerRunnable.forget();
michael@0 2277 }
michael@0 2278
michael@0 2279 nsCOMPtr<nsICancelableRunnable> cancelable = do_QueryInterface(aRunnable);
michael@0 2280 if (!cancelable) {
michael@0 2281 MOZ_CRASH("All runnables destined for a worker thread must be cancelable!");
michael@0 2282 }
michael@0 2283
michael@0 2284 workerRunnable =
michael@0 2285 new ExternalRunnableWrapper(ParentAsWorkerPrivate(), cancelable);
michael@0 2286 return workerRunnable.forget();
michael@0 2287 }
michael@0 2288
michael@0 2289 template <class Derived>
michael@0 2290 already_AddRefed<nsIEventTarget>
michael@0 2291 WorkerPrivateParent<Derived>::GetEventTarget()
michael@0 2292 {
michael@0 2293 WorkerPrivate* self = ParentAsWorkerPrivate();
michael@0 2294
michael@0 2295 nsCOMPtr<nsIEventTarget> target;
michael@0 2296
michael@0 2297 {
michael@0 2298 MutexAutoLock lock(mMutex);
michael@0 2299
michael@0 2300 if (!mEventTarget &&
michael@0 2301 ParentStatus() <= Running &&
michael@0 2302 self->mStatus <= Running) {
michael@0 2303 mEventTarget = new EventTarget(self);
michael@0 2304 }
michael@0 2305
michael@0 2306 target = mEventTarget;
michael@0 2307 }
michael@0 2308
michael@0 2309 NS_WARN_IF_FALSE(target,
michael@0 2310 "Requested event target for a worker that is already "
michael@0 2311 "shutting down!");
michael@0 2312
michael@0 2313 return target.forget();
michael@0 2314 }
michael@0 2315
michael@0 2316 template <class Derived>
michael@0 2317 bool
michael@0 2318 WorkerPrivateParent<Derived>::Start()
michael@0 2319 {
michael@0 2320 // May be called on any thread!
michael@0 2321 {
michael@0 2322 MutexAutoLock lock(mMutex);
michael@0 2323
michael@0 2324 NS_ASSERTION(mParentStatus != Running, "How can this be?!");
michael@0 2325
michael@0 2326 if (mParentStatus == Pending) {
michael@0 2327 mParentStatus = Running;
michael@0 2328 return true;
michael@0 2329 }
michael@0 2330 }
michael@0 2331
michael@0 2332 return false;
michael@0 2333 }
michael@0 2334
michael@0 2335 // aCx is null when called from the finalizer
michael@0 2336 template <class Derived>
michael@0 2337 bool
michael@0 2338 WorkerPrivateParent<Derived>::NotifyPrivate(JSContext* aCx, Status aStatus)
michael@0 2339 {
michael@0 2340 AssertIsOnParentThread();
michael@0 2341
michael@0 2342 bool pending;
michael@0 2343 {
michael@0 2344 MutexAutoLock lock(mMutex);
michael@0 2345
michael@0 2346 if (mParentStatus >= aStatus) {
michael@0 2347 return true;
michael@0 2348 }
michael@0 2349
michael@0 2350 pending = mParentStatus == Pending;
michael@0 2351 mParentStatus = aStatus;
michael@0 2352 }
michael@0 2353
michael@0 2354 if (IsSharedWorker()) {
michael@0 2355 RuntimeService* runtime = RuntimeService::GetService();
michael@0 2356 MOZ_ASSERT(runtime);
michael@0 2357
michael@0 2358 runtime->ForgetSharedWorker(ParentAsWorkerPrivate());
michael@0 2359 }
michael@0 2360
michael@0 2361 if (pending) {
michael@0 2362 WorkerPrivate* self = ParentAsWorkerPrivate();
michael@0 2363
michael@0 2364 #ifdef DEBUG
michael@0 2365 {
michael@0 2366 // Fake a thread here just so that our assertions don't go off for no
michael@0 2367 // reason.
michael@0 2368 nsIThread* currentThread = NS_GetCurrentThread();
michael@0 2369 MOZ_ASSERT(currentThread);
michael@0 2370
michael@0 2371 MOZ_ASSERT(!self->mPRThread);
michael@0 2372 self->mPRThread = PRThreadFromThread(currentThread);
michael@0 2373 MOZ_ASSERT(self->mPRThread);
michael@0 2374 }
michael@0 2375 #endif
michael@0 2376
michael@0 2377 // Worker never got a chance to run, go ahead and delete it.
michael@0 2378 self->ScheduleDeletion(WorkerPrivate::WorkerNeverRan);
michael@0 2379 return true;
michael@0 2380 }
michael@0 2381
michael@0 2382 // Only top-level workers should have a synchronize runnable.
michael@0 2383 MOZ_ASSERT_IF(mSynchronizeRunnable.get(), !GetParent());
michael@0 2384 mSynchronizeRunnable.Revoke();
michael@0 2385
michael@0 2386 NS_ASSERTION(aStatus != Terminating || mQueuedRunnables.IsEmpty(),
michael@0 2387 "Shouldn't have anything queued!");
michael@0 2388
michael@0 2389 // Anything queued will be discarded.
michael@0 2390 mQueuedRunnables.Clear();
michael@0 2391
michael@0 2392 nsRefPtr<NotifyRunnable> runnable =
michael@0 2393 new NotifyRunnable(ParentAsWorkerPrivate(), aStatus);
michael@0 2394 return runnable->Dispatch(aCx);
michael@0 2395 }
michael@0 2396
michael@0 2397 template <class Derived>
michael@0 2398 bool
michael@0 2399 WorkerPrivateParent<Derived>::Suspend(JSContext* aCx, nsPIDOMWindow* aWindow)
michael@0 2400 {
michael@0 2401 AssertIsOnParentThread();
michael@0 2402 MOZ_ASSERT(aCx);
michael@0 2403
michael@0 2404 // Shared workers are only suspended if all of their owning documents are
michael@0 2405 // suspended.
michael@0 2406 if (IsSharedWorker()) {
michael@0 2407 AssertIsOnMainThread();
michael@0 2408 MOZ_ASSERT(mSharedWorkers.Count());
michael@0 2409
michael@0 2410 struct Closure
michael@0 2411 {
michael@0 2412 nsPIDOMWindow* mWindow;
michael@0 2413 bool mAllSuspended;
michael@0 2414
michael@0 2415 Closure(nsPIDOMWindow* aWindow)
michael@0 2416 : mWindow(aWindow), mAllSuspended(true)
michael@0 2417 {
michael@0 2418 AssertIsOnMainThread();
michael@0 2419 // aWindow may be null here.
michael@0 2420 }
michael@0 2421
michael@0 2422 static PLDHashOperator
michael@0 2423 Suspend(const uint64_t& aKey,
michael@0 2424 SharedWorker* aSharedWorker,
michael@0 2425 void* aClosure)
michael@0 2426 {
michael@0 2427 AssertIsOnMainThread();
michael@0 2428 MOZ_ASSERT(aSharedWorker);
michael@0 2429 MOZ_ASSERT(aClosure);
michael@0 2430
michael@0 2431 auto closure = static_cast<Closure*>(aClosure);
michael@0 2432
michael@0 2433 if (closure->mWindow && aSharedWorker->GetOwner() == closure->mWindow) {
michael@0 2434 // Calling Suspend() may change the refcount, ensure that the worker
michael@0 2435 // outlives this call.
michael@0 2436 nsRefPtr<SharedWorker> kungFuDeathGrip = aSharedWorker;
michael@0 2437
michael@0 2438 aSharedWorker->Suspend();
michael@0 2439 } else {
michael@0 2440 MOZ_ASSERT_IF(aSharedWorker->GetOwner() && closure->mWindow,
michael@0 2441 !SameCOMIdentity(aSharedWorker->GetOwner(),
michael@0 2442 closure->mWindow));
michael@0 2443 if (!aSharedWorker->IsSuspended()) {
michael@0 2444 closure->mAllSuspended = false;
michael@0 2445 }
michael@0 2446 }
michael@0 2447 return PL_DHASH_NEXT;
michael@0 2448 }
michael@0 2449 };
michael@0 2450
michael@0 2451 Closure closure(aWindow);
michael@0 2452
michael@0 2453 mSharedWorkers.EnumerateRead(Closure::Suspend, &closure);
michael@0 2454
michael@0 2455 if (!closure.mAllSuspended || mParentSuspended) {
michael@0 2456 return true;
michael@0 2457 }
michael@0 2458 }
michael@0 2459
michael@0 2460 // MOZ_ASSERT(!mParentSuspended, "Suspended more than once!");
michael@0 2461
michael@0 2462 mParentSuspended = true;
michael@0 2463
michael@0 2464 {
michael@0 2465 MutexAutoLock lock(mMutex);
michael@0 2466
michael@0 2467 if (mParentStatus >= Terminating) {
michael@0 2468 return true;
michael@0 2469 }
michael@0 2470 }
michael@0 2471
michael@0 2472 nsRefPtr<SuspendRunnable> runnable =
michael@0 2473 new SuspendRunnable(ParentAsWorkerPrivate());
michael@0 2474 if (!runnable->Dispatch(aCx)) {
michael@0 2475 return false;
michael@0 2476 }
michael@0 2477
michael@0 2478 return true;
michael@0 2479 }
michael@0 2480
michael@0 2481 template <class Derived>
michael@0 2482 bool
michael@0 2483 WorkerPrivateParent<Derived>::Resume(JSContext* aCx, nsPIDOMWindow* aWindow)
michael@0 2484 {
michael@0 2485 AssertIsOnParentThread();
michael@0 2486 MOZ_ASSERT(aCx);
michael@0 2487 MOZ_ASSERT_IF(!IsSharedWorker(), mParentSuspended);
michael@0 2488
michael@0 2489 // Shared workers are resumed if any of their owning documents are resumed.
michael@0 2490 if (IsSharedWorker()) {
michael@0 2491 AssertIsOnMainThread();
michael@0 2492 MOZ_ASSERT(mSharedWorkers.Count());
michael@0 2493
michael@0 2494 struct Closure
michael@0 2495 {
michael@0 2496 nsPIDOMWindow* mWindow;
michael@0 2497 bool mAnyRunning;
michael@0 2498
michael@0 2499 Closure(nsPIDOMWindow* aWindow)
michael@0 2500 : mWindow(aWindow), mAnyRunning(false)
michael@0 2501 {
michael@0 2502 AssertIsOnMainThread();
michael@0 2503 // aWindow may be null here.
michael@0 2504 }
michael@0 2505
michael@0 2506 static PLDHashOperator
michael@0 2507 Resume(const uint64_t& aKey,
michael@0 2508 SharedWorker* aSharedWorker,
michael@0 2509 void* aClosure)
michael@0 2510 {
michael@0 2511 AssertIsOnMainThread();
michael@0 2512 MOZ_ASSERT(aSharedWorker);
michael@0 2513 MOZ_ASSERT(aClosure);
michael@0 2514
michael@0 2515 auto closure = static_cast<Closure*>(aClosure);
michael@0 2516
michael@0 2517 if (closure->mWindow && aSharedWorker->GetOwner() == closure->mWindow) {
michael@0 2518 // Calling Resume() may change the refcount, ensure that the worker
michael@0 2519 // outlives this call.
michael@0 2520 nsRefPtr<SharedWorker> kungFuDeathGrip = aSharedWorker;
michael@0 2521
michael@0 2522 aSharedWorker->Resume();
michael@0 2523 closure->mAnyRunning = true;
michael@0 2524 } else {
michael@0 2525 MOZ_ASSERT_IF(aSharedWorker->GetOwner() && closure->mWindow,
michael@0 2526 !SameCOMIdentity(aSharedWorker->GetOwner(),
michael@0 2527 closure->mWindow));
michael@0 2528 if (!aSharedWorker->IsSuspended()) {
michael@0 2529 closure->mAnyRunning = true;
michael@0 2530 }
michael@0 2531 }
michael@0 2532 return PL_DHASH_NEXT;
michael@0 2533 }
michael@0 2534 };
michael@0 2535
michael@0 2536 Closure closure(aWindow);
michael@0 2537
michael@0 2538 mSharedWorkers.EnumerateRead(Closure::Resume, &closure);
michael@0 2539
michael@0 2540 if (!closure.mAnyRunning || !mParentSuspended) {
michael@0 2541 return true;
michael@0 2542 }
michael@0 2543 }
michael@0 2544
michael@0 2545 MOZ_ASSERT(mParentSuspended);
michael@0 2546
michael@0 2547 mParentSuspended = false;
michael@0 2548
michael@0 2549 {
michael@0 2550 MutexAutoLock lock(mMutex);
michael@0 2551
michael@0 2552 if (mParentStatus >= Terminating) {
michael@0 2553 return true;
michael@0 2554 }
michael@0 2555 }
michael@0 2556
michael@0 2557 // Only top-level workers should have a synchronize runnable.
michael@0 2558 MOZ_ASSERT_IF(mSynchronizeRunnable.get(), !GetParent());
michael@0 2559 mSynchronizeRunnable.Revoke();
michael@0 2560
michael@0 2561 // Execute queued runnables before waking up the worker, otherwise the worker
michael@0 2562 // could post new messages before we run those that have been queued.
michael@0 2563 if (!mQueuedRunnables.IsEmpty()) {
michael@0 2564 AssertIsOnMainThread();
michael@0 2565 MOZ_ASSERT(IsDedicatedWorker());
michael@0 2566
michael@0 2567 nsTArray<nsCOMPtr<nsIRunnable>> runnables;
michael@0 2568 mQueuedRunnables.SwapElements(runnables);
michael@0 2569
michael@0 2570 for (uint32_t index = 0; index < runnables.Length(); index++) {
michael@0 2571 runnables[index]->Run();
michael@0 2572 }
michael@0 2573 }
michael@0 2574
michael@0 2575 nsRefPtr<ResumeRunnable> runnable =
michael@0 2576 new ResumeRunnable(ParentAsWorkerPrivate());
michael@0 2577 if (!runnable->Dispatch(aCx)) {
michael@0 2578 return false;
michael@0 2579 }
michael@0 2580
michael@0 2581 return true;
michael@0 2582 }
michael@0 2583
michael@0 2584 template <class Derived>
michael@0 2585 bool
michael@0 2586 WorkerPrivateParent<Derived>::SynchronizeAndResume(
michael@0 2587 JSContext* aCx,
michael@0 2588 nsPIDOMWindow* aWindow,
michael@0 2589 nsIScriptContext* aScriptContext)
michael@0 2590 {
michael@0 2591 AssertIsOnMainThread();
michael@0 2592 MOZ_ASSERT(!GetParent());
michael@0 2593 MOZ_ASSERT_IF(IsDedicatedWorker(), mParentSuspended);
michael@0 2594
michael@0 2595 // NB: There may be pending unqueued messages. If we resume here we will
michael@0 2596 // execute those messages out of order. Instead we post an event to the
michael@0 2597 // end of the event queue, allowing all of the outstanding messages to be
michael@0 2598 // queued up in order on the worker. Then and only then we execute all of
michael@0 2599 // the messages.
michael@0 2600
michael@0 2601 nsRefPtr<SynchronizeAndResumeRunnable> runnable =
michael@0 2602 new SynchronizeAndResumeRunnable(ParentAsWorkerPrivate(), aWindow,
michael@0 2603 aScriptContext);
michael@0 2604 if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) {
michael@0 2605 JS_ReportError(aCx, "Failed to dispatch to current thread!");
michael@0 2606 return false;
michael@0 2607 }
michael@0 2608
michael@0 2609 mSynchronizeRunnable = runnable;
michael@0 2610 return true;
michael@0 2611 }
michael@0 2612
michael@0 2613 template <class Derived>
michael@0 2614 bool
michael@0 2615 WorkerPrivateParent<Derived>::Close(JSContext* aCx)
michael@0 2616 {
michael@0 2617 AssertIsOnParentThread();
michael@0 2618
michael@0 2619 {
michael@0 2620 MutexAutoLock lock(mMutex);
michael@0 2621
michael@0 2622 if (mParentStatus < Closing) {
michael@0 2623 mParentStatus = Closing;
michael@0 2624 }
michael@0 2625 }
michael@0 2626
michael@0 2627 return true;
michael@0 2628 }
michael@0 2629
michael@0 2630 template <class Derived>
michael@0 2631 bool
michael@0 2632 WorkerPrivateParent<Derived>::ModifyBusyCount(JSContext* aCx, bool aIncrease)
michael@0 2633 {
michael@0 2634 AssertIsOnParentThread();
michael@0 2635
michael@0 2636 NS_ASSERTION(aIncrease || mBusyCount, "Mismatched busy count mods!");
michael@0 2637
michael@0 2638 if (aIncrease) {
michael@0 2639 mBusyCount++;
michael@0 2640 return true;
michael@0 2641 }
michael@0 2642
michael@0 2643 if (--mBusyCount == 0) {
michael@0 2644
michael@0 2645 bool shouldCancel;
michael@0 2646 {
michael@0 2647 MutexAutoLock lock(mMutex);
michael@0 2648 shouldCancel = mParentStatus == Terminating;
michael@0 2649 }
michael@0 2650
michael@0 2651 if (shouldCancel && !Cancel(aCx)) {
michael@0 2652 return false;
michael@0 2653 }
michael@0 2654 }
michael@0 2655
michael@0 2656 return true;
michael@0 2657 }
michael@0 2658
michael@0 2659 template <class Derived>
michael@0 2660 void
michael@0 2661 WorkerPrivateParent<Derived>::ForgetMainThreadObjects(
michael@0 2662 nsTArray<nsCOMPtr<nsISupports> >& aDoomed)
michael@0 2663 {
michael@0 2664 AssertIsOnParentThread();
michael@0 2665 MOZ_ASSERT(!mMainThreadObjectsForgotten);
michael@0 2666
michael@0 2667 static const uint32_t kDoomedCount = 7;
michael@0 2668
michael@0 2669 aDoomed.SetCapacity(kDoomedCount);
michael@0 2670
michael@0 2671 SwapToISupportsArray(mLoadInfo.mWindow, aDoomed);
michael@0 2672 SwapToISupportsArray(mLoadInfo.mScriptContext, aDoomed);
michael@0 2673 SwapToISupportsArray(mLoadInfo.mBaseURI, aDoomed);
michael@0 2674 SwapToISupportsArray(mLoadInfo.mResolvedScriptURI, aDoomed);
michael@0 2675 SwapToISupportsArray(mLoadInfo.mPrincipal, aDoomed);
michael@0 2676 SwapToISupportsArray(mLoadInfo.mChannel, aDoomed);
michael@0 2677 SwapToISupportsArray(mLoadInfo.mCSP, aDoomed);
michael@0 2678 // Before adding anything here update kDoomedCount above!
michael@0 2679
michael@0 2680 MOZ_ASSERT(aDoomed.Length() == kDoomedCount);
michael@0 2681
michael@0 2682 mMainThreadObjectsForgotten = true;
michael@0 2683 }
michael@0 2684
michael@0 2685 template <class Derived>
michael@0 2686 void
michael@0 2687 WorkerPrivateParent<Derived>::PostMessageInternal(
michael@0 2688 JSContext* aCx,
michael@0 2689 JS::Handle<JS::Value> aMessage,
michael@0 2690 const Optional<Sequence<JS::Value> >& aTransferable,
michael@0 2691 bool aToMessagePort,
michael@0 2692 uint64_t aMessagePortSerial,
michael@0 2693 ErrorResult& aRv)
michael@0 2694 {
michael@0 2695 AssertIsOnParentThread();
michael@0 2696
michael@0 2697 {
michael@0 2698 MutexAutoLock lock(mMutex);
michael@0 2699 if (mParentStatus > Running) {
michael@0 2700 return;
michael@0 2701 }
michael@0 2702 }
michael@0 2703
michael@0 2704 JSStructuredCloneCallbacks* callbacks;
michael@0 2705 if (GetParent()) {
michael@0 2706 if (IsChromeWorker()) {
michael@0 2707 callbacks = &gChromeWorkerStructuredCloneCallbacks;
michael@0 2708 }
michael@0 2709 else {
michael@0 2710 callbacks = &gWorkerStructuredCloneCallbacks;
michael@0 2711 }
michael@0 2712 }
michael@0 2713 else {
michael@0 2714 AssertIsOnMainThread();
michael@0 2715
michael@0 2716 if (IsChromeWorker()) {
michael@0 2717 callbacks = &gMainThreadChromeWorkerStructuredCloneCallbacks;
michael@0 2718 }
michael@0 2719 else {
michael@0 2720 callbacks = &gMainThreadWorkerStructuredCloneCallbacks;
michael@0 2721 }
michael@0 2722 }
michael@0 2723
michael@0 2724 JS::Rooted<JS::Value> transferable(aCx, JS::UndefinedValue());
michael@0 2725 if (aTransferable.WasPassed()) {
michael@0 2726 const Sequence<JS::Value>& realTransferable = aTransferable.Value();
michael@0 2727
michael@0 2728 // The input sequence only comes from the generated bindings code, which
michael@0 2729 // ensures it is rooted.
michael@0 2730 JS::HandleValueArray elements =
michael@0 2731 JS::HandleValueArray::fromMarkedLocation(realTransferable.Length(),
michael@0 2732 realTransferable.Elements());
michael@0 2733
michael@0 2734 JSObject* array =
michael@0 2735 JS_NewArrayObject(aCx, elements);
michael@0 2736 if (!array) {
michael@0 2737 aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
michael@0 2738 return;
michael@0 2739 }
michael@0 2740 transferable.setObject(*array);
michael@0 2741 }
michael@0 2742
michael@0 2743 nsTArray<nsCOMPtr<nsISupports> > clonedObjects;
michael@0 2744
michael@0 2745 JSAutoStructuredCloneBuffer buffer;
michael@0 2746 if (!buffer.write(aCx, aMessage, transferable, callbacks, &clonedObjects)) {
michael@0 2747 aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
michael@0 2748 return;
michael@0 2749 }
michael@0 2750
michael@0 2751 nsRefPtr<MessageEventRunnable> runnable =
michael@0 2752 new MessageEventRunnable(ParentAsWorkerPrivate(),
michael@0 2753 WorkerRunnable::WorkerThreadModifyBusyCount,
michael@0 2754 Move(buffer), clonedObjects, aToMessagePort,
michael@0 2755 aMessagePortSerial);
michael@0 2756 if (!runnable->Dispatch(aCx)) {
michael@0 2757 aRv.Throw(NS_ERROR_FAILURE);
michael@0 2758 }
michael@0 2759 }
michael@0 2760
michael@0 2761 template <class Derived>
michael@0 2762 void
michael@0 2763 WorkerPrivateParent<Derived>::PostMessageToMessagePort(
michael@0 2764 JSContext* aCx,
michael@0 2765 uint64_t aMessagePortSerial,
michael@0 2766 JS::Handle<JS::Value> aMessage,
michael@0 2767 const Optional<Sequence<JS::Value>>& aTransferable,
michael@0 2768 ErrorResult& aRv)
michael@0 2769 {
michael@0 2770 AssertIsOnMainThread();
michael@0 2771
michael@0 2772 PostMessageInternal(aCx, aMessage, aTransferable, true, aMessagePortSerial,
michael@0 2773 aRv);
michael@0 2774 }
michael@0 2775
michael@0 2776 template <class Derived>
michael@0 2777 bool
michael@0 2778 WorkerPrivateParent<Derived>::DispatchMessageEventToMessagePort(
michael@0 2779 JSContext* aCx, uint64_t aMessagePortSerial,
michael@0 2780 JSAutoStructuredCloneBuffer&& aBuffer,
michael@0 2781 nsTArray<nsCOMPtr<nsISupports>>& aClonedObjects)
michael@0 2782 {
michael@0 2783 AssertIsOnMainThread();
michael@0 2784
michael@0 2785 JSAutoStructuredCloneBuffer buffer(Move(aBuffer));
michael@0 2786
michael@0 2787 nsTArray<nsCOMPtr<nsISupports>> clonedObjects;
michael@0 2788 clonedObjects.SwapElements(aClonedObjects);
michael@0 2789
michael@0 2790 SharedWorker* sharedWorker;
michael@0 2791 if (!mSharedWorkers.Get(aMessagePortSerial, &sharedWorker)) {
michael@0 2792 // SharedWorker has already been unregistered?
michael@0 2793 return true;
michael@0 2794 }
michael@0 2795
michael@0 2796 nsRefPtr<MessagePort> port = sharedWorker->Port();
michael@0 2797 NS_ASSERTION(port, "SharedWorkers always have a port!");
michael@0 2798
michael@0 2799 if (port->IsClosed()) {
michael@0 2800 return true;
michael@0 2801 }
michael@0 2802
michael@0 2803 nsCOMPtr<nsIScriptGlobalObject> sgo;
michael@0 2804 port->GetParentObject(getter_AddRefs(sgo));
michael@0 2805 MOZ_ASSERT(sgo, "Should never happen if IsClosed() returned false!");
michael@0 2806 MOZ_ASSERT(sgo->GetGlobalJSObject());
michael@0 2807
michael@0 2808 nsCOMPtr<nsIScriptContext> scx = sgo->GetContext();
michael@0 2809 MOZ_ASSERT_IF(scx, scx->GetNativeContext());
michael@0 2810
michael@0 2811 AutoPushJSContext cx(scx ? scx->GetNativeContext() : aCx);
michael@0 2812 JSAutoCompartment(cx, sgo->GetGlobalJSObject());
michael@0 2813
michael@0 2814 JS::Rooted<JS::Value> data(cx);
michael@0 2815 if (!buffer.read(cx, &data, WorkerStructuredCloneCallbacks(true))) {
michael@0 2816 return false;
michael@0 2817 }
michael@0 2818
michael@0 2819 buffer.clear();
michael@0 2820
michael@0 2821 nsRefPtr<MessageEvent> event = new MessageEvent(port, nullptr, nullptr);
michael@0 2822 nsresult rv =
michael@0 2823 event->InitMessageEvent(NS_LITERAL_STRING("message"), false, false, data,
michael@0 2824 EmptyString(), EmptyString(), nullptr);
michael@0 2825 if (NS_FAILED(rv)) {
michael@0 2826 xpc::Throw(cx, rv);
michael@0 2827 return false;
michael@0 2828 }
michael@0 2829
michael@0 2830 event->SetTrusted(true);
michael@0 2831
michael@0 2832 nsTArray<nsRefPtr<MessagePortBase>> ports;
michael@0 2833 ports.AppendElement(port);
michael@0 2834
michael@0 2835 nsRefPtr<MessagePortList> portList = new MessagePortList(port, ports);
michael@0 2836 event->SetPorts(portList);
michael@0 2837
michael@0 2838 nsCOMPtr<nsIDOMEvent> domEvent;
michael@0 2839 CallQueryInterface(event.get(), getter_AddRefs(domEvent));
michael@0 2840 NS_ASSERTION(domEvent, "This should never fail!");
michael@0 2841
michael@0 2842 bool ignored;
michael@0 2843 rv = port->DispatchEvent(domEvent, &ignored);
michael@0 2844 if (NS_FAILED(rv)) {
michael@0 2845 xpc::Throw(cx, rv);
michael@0 2846 return false;
michael@0 2847 }
michael@0 2848
michael@0 2849 return true;
michael@0 2850 }
michael@0 2851
michael@0 2852 template <class Derived>
michael@0 2853 uint64_t
michael@0 2854 WorkerPrivateParent<Derived>::GetInnerWindowId()
michael@0 2855 {
michael@0 2856 AssertIsOnMainThread();
michael@0 2857 NS_ASSERTION(!mLoadInfo.mWindow || mLoadInfo.mWindow->IsInnerWindow(),
michael@0 2858 "Outer window?");
michael@0 2859 return mLoadInfo.mWindow ? mLoadInfo.mWindow->WindowID() : 0;
michael@0 2860 }
michael@0 2861
michael@0 2862 template <class Derived>
michael@0 2863 void
michael@0 2864 WorkerPrivateParent<Derived>::UpdateRuntimeAndContextOptions(
michael@0 2865 JSContext* aCx,
michael@0 2866 const JS::RuntimeOptions& aRuntimeOptions,
michael@0 2867 const JS::ContextOptions& aContentCxOptions,
michael@0 2868 const JS::ContextOptions& aChromeCxOptions)
michael@0 2869 {
michael@0 2870 AssertIsOnParentThread();
michael@0 2871
michael@0 2872 {
michael@0 2873 MutexAutoLock lock(mMutex);
michael@0 2874 mJSSettings.runtimeOptions = aRuntimeOptions;
michael@0 2875 mJSSettings.content.contextOptions = aContentCxOptions;
michael@0 2876 mJSSettings.chrome.contextOptions = aChromeCxOptions;
michael@0 2877 }
michael@0 2878
michael@0 2879 nsRefPtr<UpdateRuntimeAndContextOptionsRunnable> runnable =
michael@0 2880 new UpdateRuntimeAndContextOptionsRunnable(ParentAsWorkerPrivate(),
michael@0 2881 aRuntimeOptions,
michael@0 2882 aContentCxOptions,
michael@0 2883 aChromeCxOptions);
michael@0 2884 if (!runnable->Dispatch(aCx)) {
michael@0 2885 NS_WARNING("Failed to update worker context options!");
michael@0 2886 JS_ClearPendingException(aCx);
michael@0 2887 }
michael@0 2888 }
michael@0 2889
michael@0 2890 template <class Derived>
michael@0 2891 void
michael@0 2892 WorkerPrivateParent<Derived>::UpdatePreference(JSContext* aCx, WorkerPreference aPref, bool aValue)
michael@0 2893 {
michael@0 2894 AssertIsOnParentThread();
michael@0 2895 MOZ_ASSERT(aPref >= 0 && aPref < WORKERPREF_COUNT);
michael@0 2896
michael@0 2897 nsRefPtr<UpdatePreferenceRunnable> runnable =
michael@0 2898 new UpdatePreferenceRunnable(ParentAsWorkerPrivate(), aPref, aValue);
michael@0 2899 if (!runnable->Dispatch(aCx)) {
michael@0 2900 NS_WARNING("Failed to update worker preferences!");
michael@0 2901 JS_ClearPendingException(aCx);
michael@0 2902 }
michael@0 2903 }
michael@0 2904
michael@0 2905 template <class Derived>
michael@0 2906 void
michael@0 2907 WorkerPrivateParent<Derived>::UpdateJSWorkerMemoryParameter(JSContext* aCx,
michael@0 2908 JSGCParamKey aKey,
michael@0 2909 uint32_t aValue)
michael@0 2910 {
michael@0 2911 AssertIsOnParentThread();
michael@0 2912
michael@0 2913 bool found = false;
michael@0 2914
michael@0 2915 {
michael@0 2916 MutexAutoLock lock(mMutex);
michael@0 2917 found = mJSSettings.ApplyGCSetting(aKey, aValue);
michael@0 2918 }
michael@0 2919
michael@0 2920 if (found) {
michael@0 2921 nsRefPtr<UpdateJSWorkerMemoryParameterRunnable> runnable =
michael@0 2922 new UpdateJSWorkerMemoryParameterRunnable(ParentAsWorkerPrivate(), aKey,
michael@0 2923 aValue);
michael@0 2924 if (!runnable->Dispatch(aCx)) {
michael@0 2925 NS_WARNING("Failed to update memory parameter!");
michael@0 2926 JS_ClearPendingException(aCx);
michael@0 2927 }
michael@0 2928 }
michael@0 2929 }
michael@0 2930
michael@0 2931 #ifdef JS_GC_ZEAL
michael@0 2932 template <class Derived>
michael@0 2933 void
michael@0 2934 WorkerPrivateParent<Derived>::UpdateGCZeal(JSContext* aCx, uint8_t aGCZeal,
michael@0 2935 uint32_t aFrequency)
michael@0 2936 {
michael@0 2937 AssertIsOnParentThread();
michael@0 2938
michael@0 2939 {
michael@0 2940 MutexAutoLock lock(mMutex);
michael@0 2941 mJSSettings.gcZeal = aGCZeal;
michael@0 2942 mJSSettings.gcZealFrequency = aFrequency;
michael@0 2943 }
michael@0 2944
michael@0 2945 nsRefPtr<UpdateGCZealRunnable> runnable =
michael@0 2946 new UpdateGCZealRunnable(ParentAsWorkerPrivate(), aGCZeal, aFrequency);
michael@0 2947 if (!runnable->Dispatch(aCx)) {
michael@0 2948 NS_WARNING("Failed to update worker gczeal!");
michael@0 2949 JS_ClearPendingException(aCx);
michael@0 2950 }
michael@0 2951 }
michael@0 2952 #endif
michael@0 2953
michael@0 2954 template <class Derived>
michael@0 2955 void
michael@0 2956 WorkerPrivateParent<Derived>::GarbageCollect(JSContext* aCx, bool aShrinking)
michael@0 2957 {
michael@0 2958 AssertIsOnParentThread();
michael@0 2959
michael@0 2960 nsRefPtr<GarbageCollectRunnable> runnable =
michael@0 2961 new GarbageCollectRunnable(ParentAsWorkerPrivate(), aShrinking,
michael@0 2962 /* collectChildren = */ true);
michael@0 2963 if (!runnable->Dispatch(aCx)) {
michael@0 2964 NS_WARNING("Failed to GC worker!");
michael@0 2965 JS_ClearPendingException(aCx);
michael@0 2966 }
michael@0 2967 }
michael@0 2968
michael@0 2969 template <class Derived>
michael@0 2970 void
michael@0 2971 WorkerPrivateParent<Derived>::CycleCollect(JSContext* aCx, bool aDummy)
michael@0 2972 {
michael@0 2973 AssertIsOnParentThread();
michael@0 2974
michael@0 2975 nsRefPtr<CycleCollectRunnable> runnable =
michael@0 2976 new CycleCollectRunnable(ParentAsWorkerPrivate(),
michael@0 2977 /* collectChildren = */ true);
michael@0 2978 if (!runnable->Dispatch(aCx)) {
michael@0 2979 NS_WARNING("Failed to CC worker!");
michael@0 2980 JS_ClearPendingException(aCx);
michael@0 2981 }
michael@0 2982 }
michael@0 2983
michael@0 2984 template <class Derived>
michael@0 2985 void
michael@0 2986 WorkerPrivateParent<Derived>::OfflineStatusChangeEvent(JSContext* aCx, bool aIsOffline)
michael@0 2987 {
michael@0 2988 AssertIsOnParentThread();
michael@0 2989
michael@0 2990 nsRefPtr<OfflineStatusChangeRunnable> runnable =
michael@0 2991 new OfflineStatusChangeRunnable(ParentAsWorkerPrivate(), aIsOffline);
michael@0 2992 if (!runnable->Dispatch(aCx)) {
michael@0 2993 NS_WARNING("Failed to dispatch offline status change event!");
michael@0 2994 JS_ClearPendingException(aCx);
michael@0 2995 }
michael@0 2996 }
michael@0 2997
michael@0 2998 void
michael@0 2999 WorkerPrivate::OfflineStatusChangeEventInternal(JSContext* aCx, bool aIsOffline)
michael@0 3000 {
michael@0 3001 AssertIsOnWorkerThread();
michael@0 3002
michael@0 3003 for (uint32_t index = 0; index < mChildWorkers.Length(); ++index) {
michael@0 3004 mChildWorkers[index]->OfflineStatusChangeEvent(aCx, aIsOffline);
michael@0 3005 }
michael@0 3006
michael@0 3007 mOnLine = !aIsOffline;
michael@0 3008 WorkerGlobalScope* globalScope = GlobalScope();
michael@0 3009 nsRefPtr<WorkerNavigator> nav = globalScope->GetExistingNavigator();
michael@0 3010 if (nav) {
michael@0 3011 nav->SetOnLine(mOnLine);
michael@0 3012 }
michael@0 3013
michael@0 3014 nsString eventType;
michael@0 3015 if (aIsOffline) {
michael@0 3016 eventType.AssignLiteral("offline");
michael@0 3017 } else {
michael@0 3018 eventType.AssignLiteral("online");
michael@0 3019 }
michael@0 3020
michael@0 3021 nsCOMPtr<nsIDOMEvent> event;
michael@0 3022 nsresult rv =
michael@0 3023 NS_NewDOMEvent(getter_AddRefs(event), globalScope, nullptr, nullptr);
michael@0 3024 NS_ENSURE_SUCCESS_VOID(rv);
michael@0 3025
michael@0 3026 rv = event->InitEvent(eventType, false, false);
michael@0 3027 NS_ENSURE_SUCCESS_VOID(rv);
michael@0 3028
michael@0 3029 event->SetTrusted(true);
michael@0 3030
michael@0 3031 globalScope->DispatchDOMEvent(nullptr, event, nullptr, nullptr);
michael@0 3032 }
michael@0 3033
michael@0 3034 template <class Derived>
michael@0 3035 bool
michael@0 3036 WorkerPrivateParent<Derived>::RegisterSharedWorker(JSContext* aCx,
michael@0 3037 SharedWorker* aSharedWorker)
michael@0 3038 {
michael@0 3039 AssertIsOnMainThread();
michael@0 3040 MOZ_ASSERT(aSharedWorker);
michael@0 3041 MOZ_ASSERT(IsSharedWorker());
michael@0 3042 MOZ_ASSERT(!mSharedWorkers.Get(aSharedWorker->Serial()));
michael@0 3043
michael@0 3044 nsRefPtr<MessagePortRunnable> runnable =
michael@0 3045 new MessagePortRunnable(ParentAsWorkerPrivate(), aSharedWorker->Serial(),
michael@0 3046 true);
michael@0 3047 if (!runnable->Dispatch(aCx)) {
michael@0 3048 return false;
michael@0 3049 }
michael@0 3050
michael@0 3051 mSharedWorkers.Put(aSharedWorker->Serial(), aSharedWorker);
michael@0 3052
michael@0 3053 // If there were other SharedWorker objects attached to this worker then they
michael@0 3054 // may all have been suspended and this worker would need to be resumed.
michael@0 3055 if (mSharedWorkers.Count() > 1 && !Resume(aCx, nullptr)) {
michael@0 3056 return false;
michael@0 3057 }
michael@0 3058
michael@0 3059 return true;
michael@0 3060 }
michael@0 3061
michael@0 3062 template <class Derived>
michael@0 3063 void
michael@0 3064 WorkerPrivateParent<Derived>::UnregisterSharedWorker(
michael@0 3065 JSContext* aCx,
michael@0 3066 SharedWorker* aSharedWorker)
michael@0 3067 {
michael@0 3068 AssertIsOnMainThread();
michael@0 3069 MOZ_ASSERT(aSharedWorker);
michael@0 3070 MOZ_ASSERT(IsSharedWorker());
michael@0 3071 MOZ_ASSERT(mSharedWorkers.Get(aSharedWorker->Serial()));
michael@0 3072
michael@0 3073 nsRefPtr<MessagePortRunnable> runnable =
michael@0 3074 new MessagePortRunnable(ParentAsWorkerPrivate(), aSharedWorker->Serial(),
michael@0 3075 false);
michael@0 3076 if (!runnable->Dispatch(aCx)) {
michael@0 3077 JS_ReportPendingException(aCx);
michael@0 3078 }
michael@0 3079
michael@0 3080 mSharedWorkers.Remove(aSharedWorker->Serial());
michael@0 3081
michael@0 3082 // If there are still SharedWorker objects attached to this worker then they
michael@0 3083 // may all be suspended and this worker would need to be suspended. Otherwise,
michael@0 3084 // if that was the last SharedWorker then it's time to cancel this worker.
michael@0 3085 if (mSharedWorkers.Count()) {
michael@0 3086 if (!Suspend(aCx, nullptr)) {
michael@0 3087 JS_ReportPendingException(aCx);
michael@0 3088 }
michael@0 3089 } else if (!Cancel(aCx)) {
michael@0 3090 JS_ReportPendingException(aCx);
michael@0 3091 }
michael@0 3092 }
michael@0 3093
michael@0 3094 template <class Derived>
michael@0 3095 void
michael@0 3096 WorkerPrivateParent<Derived>::BroadcastErrorToSharedWorkers(
michael@0 3097 JSContext* aCx,
michael@0 3098 const nsAString& aMessage,
michael@0 3099 const nsAString& aFilename,
michael@0 3100 const nsAString& aLine,
michael@0 3101 uint32_t aLineNumber,
michael@0 3102 uint32_t aColumnNumber,
michael@0 3103 uint32_t aFlags)
michael@0 3104 {
michael@0 3105 AssertIsOnMainThread();
michael@0 3106
michael@0 3107 nsAutoTArray<nsRefPtr<SharedWorker>, 10> sharedWorkers;
michael@0 3108 GetAllSharedWorkers(sharedWorkers);
michael@0 3109
michael@0 3110 if (sharedWorkers.IsEmpty()) {
michael@0 3111 return;
michael@0 3112 }
michael@0 3113
michael@0 3114 nsAutoTArray<WindowAction, 10> windowActions;
michael@0 3115 nsresult rv;
michael@0 3116
michael@0 3117 // First fire the error event at all SharedWorker objects. This may include
michael@0 3118 // multiple objects in a single window as well as objects in different
michael@0 3119 // windows.
michael@0 3120 for (uint32_t index = 0; index < sharedWorkers.Length(); index++) {
michael@0 3121 nsRefPtr<SharedWorker>& sharedWorker = sharedWorkers[index];
michael@0 3122
michael@0 3123 // May be null.
michael@0 3124 nsPIDOMWindow* window = sharedWorker->GetOwner();
michael@0 3125
michael@0 3126 uint32_t actionsIndex = windowActions.LastIndexOf(WindowAction(window));
michael@0 3127
michael@0 3128 nsIGlobalObject* global = sharedWorker->GetParentObject();
michael@0 3129 AutoJSAPIWithErrorsReportedToWindow jsapi(global);
michael@0 3130 JSContext* cx = jsapi.cx();
michael@0 3131 JSAutoCompartment ac(cx, global->GetGlobalJSObject());
michael@0 3132
michael@0 3133 RootedDictionary<ErrorEventInit> errorInit(aCx);
michael@0 3134 errorInit.mBubbles = false;
michael@0 3135 errorInit.mCancelable = true;
michael@0 3136 errorInit.mMessage = aMessage;
michael@0 3137 errorInit.mFilename = aFilename;
michael@0 3138 errorInit.mLineno = aLineNumber;
michael@0 3139 errorInit.mColno = aColumnNumber;
michael@0 3140
michael@0 3141 nsRefPtr<ErrorEvent> errorEvent =
michael@0 3142 ErrorEvent::Constructor(sharedWorker, NS_LITERAL_STRING("error"),
michael@0 3143 errorInit);
michael@0 3144 if (!errorEvent) {
michael@0 3145 Throw(cx, NS_ERROR_UNEXPECTED);
michael@0 3146 JS_ReportPendingException(cx);
michael@0 3147 continue;
michael@0 3148 }
michael@0 3149
michael@0 3150 errorEvent->SetTrusted(true);
michael@0 3151
michael@0 3152 bool defaultActionEnabled;
michael@0 3153 nsresult rv = sharedWorker->DispatchEvent(errorEvent, &defaultActionEnabled);
michael@0 3154 if (NS_FAILED(rv)) {
michael@0 3155 Throw(cx, rv);
michael@0 3156 JS_ReportPendingException(cx);
michael@0 3157 continue;
michael@0 3158 }
michael@0 3159
michael@0 3160 if (defaultActionEnabled) {
michael@0 3161 // Add the owning window to our list so that we will fire an error event
michael@0 3162 // at it later.
michael@0 3163 if (!windowActions.Contains(window)) {
michael@0 3164 windowActions.AppendElement(WindowAction(window, cx));
michael@0 3165 }
michael@0 3166 } else if (actionsIndex != windowActions.NoIndex) {
michael@0 3167 // Any listener that calls preventDefault() will prevent the window from
michael@0 3168 // receiving the error event.
michael@0 3169 windowActions[actionsIndex].mDefaultAction = false;
michael@0 3170 }
michael@0 3171 }
michael@0 3172
michael@0 3173 // If there are no windows to consider further then we're done.
michael@0 3174 if (windowActions.IsEmpty()) {
michael@0 3175 return;
michael@0 3176 }
michael@0 3177
michael@0 3178 bool shouldLogErrorToConsole = true;
michael@0 3179
michael@0 3180 // Now fire error events at all the windows remaining.
michael@0 3181 for (uint32_t index = 0; index < windowActions.Length(); index++) {
michael@0 3182 WindowAction& windowAction = windowActions[index];
michael@0 3183
michael@0 3184 // If there is no window or the script already called preventDefault then
michael@0 3185 // skip this window.
michael@0 3186 if (!windowAction.mWindow || !windowAction.mDefaultAction) {
michael@0 3187 continue;
michael@0 3188 }
michael@0 3189
michael@0 3190 JSContext* cx = windowAction.mJSContext;
michael@0 3191
michael@0 3192 AutoCxPusher autoPush(cx);
michael@0 3193
michael@0 3194 nsCOMPtr<nsIScriptGlobalObject> sgo =
michael@0 3195 do_QueryInterface(windowAction.mWindow);
michael@0 3196 MOZ_ASSERT(sgo);
michael@0 3197
michael@0 3198 MOZ_ASSERT(NS_IsMainThread());
michael@0 3199 RootedDictionary<ErrorEventInit> init(aCx);
michael@0 3200 init.mLineno = aLineNumber;
michael@0 3201 init.mFilename = aFilename;
michael@0 3202 init.mMessage = aMessage;
michael@0 3203 init.mCancelable = true;
michael@0 3204 init.mBubbles = true;
michael@0 3205
michael@0 3206 nsEventStatus status = nsEventStatus_eIgnore;
michael@0 3207 rv = sgo->HandleScriptError(init, &status);
michael@0 3208 if (NS_FAILED(rv)) {
michael@0 3209 Throw(cx, rv);
michael@0 3210 JS_ReportPendingException(cx);
michael@0 3211 continue;
michael@0 3212 }
michael@0 3213
michael@0 3214 if (status == nsEventStatus_eConsumeNoDefault) {
michael@0 3215 shouldLogErrorToConsole = false;
michael@0 3216 }
michael@0 3217 }
michael@0 3218
michael@0 3219 // Finally log a warning in the console if no window tried to prevent it.
michael@0 3220 if (shouldLogErrorToConsole) {
michael@0 3221 LogErrorToConsole(aMessage, aFilename, aLine, aLineNumber, aColumnNumber,
michael@0 3222 aFlags, 0);
michael@0 3223 }
michael@0 3224 }
michael@0 3225
michael@0 3226 template <class Derived>
michael@0 3227 void
michael@0 3228 WorkerPrivateParent<Derived>::GetAllSharedWorkers(
michael@0 3229 nsTArray<nsRefPtr<SharedWorker>>& aSharedWorkers)
michael@0 3230 {
michael@0 3231 AssertIsOnMainThread();
michael@0 3232 MOZ_ASSERT(IsSharedWorker());
michael@0 3233
michael@0 3234 struct Helper
michael@0 3235 {
michael@0 3236 static PLDHashOperator
michael@0 3237 Collect(const uint64_t& aKey,
michael@0 3238 SharedWorker* aSharedWorker,
michael@0 3239 void* aClosure)
michael@0 3240 {
michael@0 3241 AssertIsOnMainThread();
michael@0 3242 MOZ_ASSERT(aSharedWorker);
michael@0 3243 MOZ_ASSERT(aClosure);
michael@0 3244
michael@0 3245 auto array = static_cast<nsTArray<nsRefPtr<SharedWorker>>*>(aClosure);
michael@0 3246 array->AppendElement(aSharedWorker);
michael@0 3247
michael@0 3248 return PL_DHASH_NEXT;
michael@0 3249 }
michael@0 3250 };
michael@0 3251
michael@0 3252 if (!aSharedWorkers.IsEmpty()) {
michael@0 3253 aSharedWorkers.Clear();
michael@0 3254 }
michael@0 3255
michael@0 3256 mSharedWorkers.EnumerateRead(Helper::Collect, &aSharedWorkers);
michael@0 3257 }
michael@0 3258
michael@0 3259 template <class Derived>
michael@0 3260 void
michael@0 3261 WorkerPrivateParent<Derived>::CloseSharedWorkersForWindow(
michael@0 3262 nsPIDOMWindow* aWindow)
michael@0 3263 {
michael@0 3264 AssertIsOnMainThread();
michael@0 3265 MOZ_ASSERT(IsSharedWorker());
michael@0 3266 MOZ_ASSERT(aWindow);
michael@0 3267
michael@0 3268 struct Closure
michael@0 3269 {
michael@0 3270 nsPIDOMWindow* mWindow;
michael@0 3271 nsAutoTArray<nsRefPtr<SharedWorker>, 10> mSharedWorkers;
michael@0 3272
michael@0 3273 Closure(nsPIDOMWindow* aWindow)
michael@0 3274 : mWindow(aWindow)
michael@0 3275 {
michael@0 3276 AssertIsOnMainThread();
michael@0 3277 MOZ_ASSERT(aWindow);
michael@0 3278 }
michael@0 3279
michael@0 3280 static PLDHashOperator
michael@0 3281 Collect(const uint64_t& aKey,
michael@0 3282 SharedWorker* aSharedWorker,
michael@0 3283 void* aClosure)
michael@0 3284 {
michael@0 3285 AssertIsOnMainThread();
michael@0 3286 MOZ_ASSERT(aSharedWorker);
michael@0 3287 MOZ_ASSERT(aClosure);
michael@0 3288
michael@0 3289 auto closure = static_cast<Closure*>(aClosure);
michael@0 3290 MOZ_ASSERT(closure->mWindow);
michael@0 3291
michael@0 3292 if (aSharedWorker->GetOwner() == closure->mWindow) {
michael@0 3293 closure->mSharedWorkers.AppendElement(aSharedWorker);
michael@0 3294 } else {
michael@0 3295 MOZ_ASSERT(!SameCOMIdentity(aSharedWorker->GetOwner(),
michael@0 3296 closure->mWindow));
michael@0 3297 }
michael@0 3298
michael@0 3299 return PL_DHASH_NEXT;
michael@0 3300 }
michael@0 3301 };
michael@0 3302
michael@0 3303 Closure closure(aWindow);
michael@0 3304
michael@0 3305 mSharedWorkers.EnumerateRead(Closure::Collect, &closure);
michael@0 3306
michael@0 3307 for (uint32_t index = 0; index < closure.mSharedWorkers.Length(); index++) {
michael@0 3308 closure.mSharedWorkers[index]->Close();
michael@0 3309 }
michael@0 3310 }
michael@0 3311
michael@0 3312 template <class Derived>
michael@0 3313 void
michael@0 3314 WorkerPrivateParent<Derived>::WorkerScriptLoaded()
michael@0 3315 {
michael@0 3316 AssertIsOnMainThread();
michael@0 3317
michael@0 3318 if (IsSharedWorker()) {
michael@0 3319 // No longer need to hold references to the window or document we came from.
michael@0 3320 mLoadInfo.mWindow = nullptr;
michael@0 3321 mLoadInfo.mScriptContext = nullptr;
michael@0 3322 }
michael@0 3323 }
michael@0 3324
michael@0 3325 template <class Derived>
michael@0 3326 void
michael@0 3327 WorkerPrivateParent<Derived>::SetBaseURI(nsIURI* aBaseURI)
michael@0 3328 {
michael@0 3329 AssertIsOnMainThread();
michael@0 3330
michael@0 3331 if (!mLoadInfo.mBaseURI) {
michael@0 3332 NS_ASSERTION(GetParent(), "Shouldn't happen without a parent!");
michael@0 3333 mLoadInfo.mResolvedScriptURI = aBaseURI;
michael@0 3334 }
michael@0 3335
michael@0 3336 mLoadInfo.mBaseURI = aBaseURI;
michael@0 3337
michael@0 3338 if (NS_FAILED(aBaseURI->GetSpec(mLocationInfo.mHref))) {
michael@0 3339 mLocationInfo.mHref.Truncate();
michael@0 3340 }
michael@0 3341
michael@0 3342 if (NS_FAILED(aBaseURI->GetHost(mLocationInfo.mHostname))) {
michael@0 3343 mLocationInfo.mHostname.Truncate();
michael@0 3344 }
michael@0 3345
michael@0 3346 if (NS_FAILED(aBaseURI->GetPath(mLocationInfo.mPathname))) {
michael@0 3347 mLocationInfo.mPathname.Truncate();
michael@0 3348 }
michael@0 3349
michael@0 3350 nsCString temp;
michael@0 3351
michael@0 3352 nsCOMPtr<nsIURL> url(do_QueryInterface(aBaseURI));
michael@0 3353 if (url && NS_SUCCEEDED(url->GetQuery(temp)) && !temp.IsEmpty()) {
michael@0 3354 mLocationInfo.mSearch.AssignLiteral("?");
michael@0 3355 mLocationInfo.mSearch.Append(temp);
michael@0 3356 }
michael@0 3357
michael@0 3358 if (NS_SUCCEEDED(aBaseURI->GetRef(temp)) && !temp.IsEmpty()) {
michael@0 3359 nsCOMPtr<nsITextToSubURI> converter =
michael@0 3360 do_GetService(NS_ITEXTTOSUBURI_CONTRACTID);
michael@0 3361 if (converter) {
michael@0 3362 nsCString charset;
michael@0 3363 nsAutoString unicodeRef;
michael@0 3364 if (NS_SUCCEEDED(aBaseURI->GetOriginCharset(charset)) &&
michael@0 3365 NS_SUCCEEDED(converter->UnEscapeURIForUI(charset, temp,
michael@0 3366 unicodeRef))) {
michael@0 3367 mLocationInfo.mHash.AssignLiteral("#");
michael@0 3368 mLocationInfo.mHash.Append(NS_ConvertUTF16toUTF8(unicodeRef));
michael@0 3369 }
michael@0 3370 }
michael@0 3371
michael@0 3372 if (mLocationInfo.mHash.IsEmpty()) {
michael@0 3373 mLocationInfo.mHash.AssignLiteral("#");
michael@0 3374 mLocationInfo.mHash.Append(temp);
michael@0 3375 }
michael@0 3376 }
michael@0 3377
michael@0 3378 if (NS_SUCCEEDED(aBaseURI->GetScheme(mLocationInfo.mProtocol))) {
michael@0 3379 mLocationInfo.mProtocol.AppendLiteral(":");
michael@0 3380 }
michael@0 3381 else {
michael@0 3382 mLocationInfo.mProtocol.Truncate();
michael@0 3383 }
michael@0 3384
michael@0 3385 int32_t port;
michael@0 3386 if (NS_SUCCEEDED(aBaseURI->GetPort(&port)) && port != -1) {
michael@0 3387 mLocationInfo.mPort.AppendInt(port);
michael@0 3388
michael@0 3389 nsAutoCString host(mLocationInfo.mHostname);
michael@0 3390 host.AppendLiteral(":");
michael@0 3391 host.Append(mLocationInfo.mPort);
michael@0 3392
michael@0 3393 mLocationInfo.mHost.Assign(host);
michael@0 3394 }
michael@0 3395 else {
michael@0 3396 mLocationInfo.mHost.Assign(mLocationInfo.mHostname);
michael@0 3397 }
michael@0 3398
michael@0 3399 nsContentUtils::GetUTFNonNullOrigin(aBaseURI, mLocationInfo.mOrigin);
michael@0 3400 }
michael@0 3401
michael@0 3402 template <class Derived>
michael@0 3403 void
michael@0 3404 WorkerPrivateParent<Derived>::SetPrincipal(nsIPrincipal* aPrincipal)
michael@0 3405 {
michael@0 3406 AssertIsOnMainThread();
michael@0 3407
michael@0 3408 mLoadInfo.mPrincipal = aPrincipal;
michael@0 3409 mLoadInfo.mPrincipalIsSystem = nsContentUtils::IsSystemPrincipal(aPrincipal);
michael@0 3410 uint16_t appStatus = aPrincipal->GetAppStatus();
michael@0 3411 mLoadInfo.mIsInPrivilegedApp =
michael@0 3412 (appStatus == nsIPrincipal::APP_STATUS_CERTIFIED ||
michael@0 3413 appStatus == nsIPrincipal::APP_STATUS_PRIVILEGED);
michael@0 3414 mLoadInfo.mIsInCertifiedApp = (appStatus == nsIPrincipal::APP_STATUS_CERTIFIED);
michael@0 3415 }
michael@0 3416
michael@0 3417 template <class Derived>
michael@0 3418 JSContext*
michael@0 3419 WorkerPrivateParent<Derived>::ParentJSContext() const
michael@0 3420 {
michael@0 3421 AssertIsOnParentThread();
michael@0 3422
michael@0 3423 if (mParent) {
michael@0 3424 return mParent->GetJSContext();
michael@0 3425 }
michael@0 3426
michael@0 3427 AssertIsOnMainThread();
michael@0 3428
michael@0 3429 return mLoadInfo.mScriptContext ?
michael@0 3430 mLoadInfo.mScriptContext->GetNativeContext() :
michael@0 3431 nsContentUtils::GetSafeJSContext();
michael@0 3432 }
michael@0 3433
michael@0 3434 template <class Derived>
michael@0 3435 void
michael@0 3436 WorkerPrivateParent<Derived>::RegisterHostObjectURI(const nsACString& aURI)
michael@0 3437 {
michael@0 3438 AssertIsOnMainThread();
michael@0 3439 mHostObjectURIs.AppendElement(aURI);
michael@0 3440 }
michael@0 3441
michael@0 3442 template <class Derived>
michael@0 3443 void
michael@0 3444 WorkerPrivateParent<Derived>::UnregisterHostObjectURI(const nsACString& aURI)
michael@0 3445 {
michael@0 3446 AssertIsOnMainThread();
michael@0 3447 mHostObjectURIs.RemoveElement(aURI);
michael@0 3448 }
michael@0 3449
michael@0 3450 template <class Derived>
michael@0 3451 void
michael@0 3452 WorkerPrivateParent<Derived>::StealHostObjectURIs(nsTArray<nsCString>& aArray)
michael@0 3453 {
michael@0 3454 aArray.SwapElements(mHostObjectURIs);
michael@0 3455 }
michael@0 3456
michael@0 3457 template <class Derived>
michael@0 3458 NS_IMPL_ADDREF_INHERITED(WorkerPrivateParent<Derived>, DOMEventTargetHelper)
michael@0 3459
michael@0 3460 template <class Derived>
michael@0 3461 NS_IMPL_RELEASE_INHERITED(WorkerPrivateParent<Derived>, DOMEventTargetHelper)
michael@0 3462
michael@0 3463 template <class Derived>
michael@0 3464 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(WorkerPrivateParent<Derived>)
michael@0 3465 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
michael@0 3466
michael@0 3467 template <class Derived>
michael@0 3468 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(WorkerPrivateParent<Derived>,
michael@0 3469 DOMEventTargetHelper)
michael@0 3470 tmp->AssertIsOnParentThread();
michael@0 3471
michael@0 3472 // The WorkerPrivate::mSelfRef has a reference to itself, which is really
michael@0 3473 // held by the worker thread. We traverse this reference if and only if our
michael@0 3474 // busy count is zero and we have not released the main thread reference.
michael@0 3475 // We do not unlink it. This allows the CC to break cycles involving the
michael@0 3476 // WorkerPrivate and begin shutting it down (which does happen in unlink) but
michael@0 3477 // ensures that the WorkerPrivate won't be deleted before we're done shutting
michael@0 3478 // down the thread.
michael@0 3479
michael@0 3480 if (!tmp->mBusyCount && !tmp->mMainThreadObjectsForgotten) {
michael@0 3481 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSelfRef)
michael@0 3482 }
michael@0 3483
michael@0 3484 // The various strong references in LoadInfo are managed manually and cannot
michael@0 3485 // be cycle collected.
michael@0 3486 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
michael@0 3487
michael@0 3488 template <class Derived>
michael@0 3489 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(WorkerPrivateParent<Derived>,
michael@0 3490 DOMEventTargetHelper)
michael@0 3491 tmp->Terminate(nullptr);
michael@0 3492 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
michael@0 3493
michael@0 3494 template <class Derived>
michael@0 3495 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(WorkerPrivateParent<Derived>,
michael@0 3496 DOMEventTargetHelper)
michael@0 3497 tmp->AssertIsOnParentThread();
michael@0 3498 NS_IMPL_CYCLE_COLLECTION_TRACE_END
michael@0 3499
michael@0 3500 #ifdef DEBUG
michael@0 3501
michael@0 3502 template <class Derived>
michael@0 3503 void
michael@0 3504 WorkerPrivateParent<Derived>::AssertIsOnParentThread() const
michael@0 3505 {
michael@0 3506 if (GetParent()) {
michael@0 3507 GetParent()->AssertIsOnWorkerThread();
michael@0 3508 }
michael@0 3509 else {
michael@0 3510 AssertIsOnMainThread();
michael@0 3511 }
michael@0 3512 }
michael@0 3513
michael@0 3514 template <class Derived>
michael@0 3515 void
michael@0 3516 WorkerPrivateParent<Derived>::AssertInnerWindowIsCorrect() const
michael@0 3517 {
michael@0 3518 AssertIsOnParentThread();
michael@0 3519
michael@0 3520 // Only care about top level workers from windows.
michael@0 3521 if (mParent || !mLoadInfo.mWindow) {
michael@0 3522 return;
michael@0 3523 }
michael@0 3524
michael@0 3525 AssertIsOnMainThread();
michael@0 3526
michael@0 3527 nsPIDOMWindow* outer = mLoadInfo.mWindow->GetOuterWindow();
michael@0 3528 NS_ASSERTION(outer && outer->GetCurrentInnerWindow() == mLoadInfo.mWindow,
michael@0 3529 "Inner window no longer correct!");
michael@0 3530 }
michael@0 3531
michael@0 3532 #endif
michael@0 3533 WorkerPrivate::WorkerPrivate(JSContext* aCx,
michael@0 3534 WorkerPrivate* aParent,
michael@0 3535 const nsAString& aScriptURL,
michael@0 3536 bool aIsChromeWorker, WorkerType aWorkerType,
michael@0 3537 const nsACString& aSharedWorkerName,
michael@0 3538 LoadInfo& aLoadInfo)
michael@0 3539 : WorkerPrivateParent<WorkerPrivate>(aCx, aParent, aScriptURL,
michael@0 3540 aIsChromeWorker, aWorkerType,
michael@0 3541 aSharedWorkerName, aLoadInfo),
michael@0 3542 mJSContext(nullptr), mErrorHandlerRecursionCount(0), mNextTimeoutId(1),
michael@0 3543 mStatus(Pending), mSuspended(false), mTimerRunning(false),
michael@0 3544 mRunningExpiredTimeouts(false), mCloseHandlerStarted(false),
michael@0 3545 mCloseHandlerFinished(false), mMemoryReporterRunning(false),
michael@0 3546 mBlockedForMemoryReporter(false), mCancelAllPendingRunnables(false),
michael@0 3547 mPeriodicGCTimerRunning(false), mIdleGCTimerRunning(false)
michael@0 3548 #ifdef DEBUG
michael@0 3549 , mPRThread(nullptr)
michael@0 3550 #endif
michael@0 3551 {
michael@0 3552 MOZ_ASSERT_IF(IsSharedWorker(), !aSharedWorkerName.IsVoid());
michael@0 3553 MOZ_ASSERT_IF(!IsSharedWorker(), aSharedWorkerName.IsEmpty());
michael@0 3554
michael@0 3555 if (aParent) {
michael@0 3556 aParent->AssertIsOnWorkerThread();
michael@0 3557 aParent->GetAllPreferences(mPreferences);
michael@0 3558 mOnLine = aParent->OnLine();
michael@0 3559 }
michael@0 3560 else {
michael@0 3561 AssertIsOnMainThread();
michael@0 3562 RuntimeService::GetDefaultPreferences(mPreferences);
michael@0 3563 mOnLine = !NS_IsOffline();
michael@0 3564 }
michael@0 3565 }
michael@0 3566
michael@0 3567 WorkerPrivate::~WorkerPrivate()
michael@0 3568 {
michael@0 3569 }
michael@0 3570
michael@0 3571 // static
michael@0 3572 already_AddRefed<WorkerPrivate>
michael@0 3573 WorkerPrivate::Constructor(const GlobalObject& aGlobal,
michael@0 3574 const nsAString& aScriptURL,
michael@0 3575 ErrorResult& aRv)
michael@0 3576 {
michael@0 3577 return WorkerPrivate::Constructor(aGlobal, aScriptURL, false,
michael@0 3578 WorkerTypeDedicated, EmptyCString(),
michael@0 3579 nullptr, aRv);
michael@0 3580 }
michael@0 3581
michael@0 3582 // static
michael@0 3583 bool
michael@0 3584 WorkerPrivate::WorkerAvailable(JSContext* /* unused */, JSObject* /* unused */)
michael@0 3585 {
michael@0 3586 // If we're already on a worker workers are clearly enabled.
michael@0 3587 if (!NS_IsMainThread()) {
michael@0 3588 return true;
michael@0 3589 }
michael@0 3590
michael@0 3591 // If our caller is chrome, workers are always available.
michael@0 3592 if (nsContentUtils::IsCallerChrome()) {
michael@0 3593 return true;
michael@0 3594 }
michael@0 3595
michael@0 3596 // Else check the pref.
michael@0 3597 return Preferences::GetBool(PREF_WORKERS_ENABLED);
michael@0 3598 }
michael@0 3599
michael@0 3600 // static
michael@0 3601 already_AddRefed<ChromeWorkerPrivate>
michael@0 3602 ChromeWorkerPrivate::Constructor(const GlobalObject& aGlobal,
michael@0 3603 const nsAString& aScriptURL,
michael@0 3604 ErrorResult& aRv)
michael@0 3605 {
michael@0 3606 return WorkerPrivate::Constructor(aGlobal, aScriptURL, true,
michael@0 3607 WorkerTypeDedicated, EmptyCString(),
michael@0 3608 nullptr, aRv)
michael@0 3609 .downcast<ChromeWorkerPrivate>();
michael@0 3610 }
michael@0 3611
michael@0 3612 // static
michael@0 3613 bool
michael@0 3614 ChromeWorkerPrivate::WorkerAvailable(JSContext* /* unused */, JSObject* /* unused */)
michael@0 3615 {
michael@0 3616 // Chrome is always allowed to use workers, and content is never allowed to
michael@0 3617 // use ChromeWorker, so all we have to check is the caller.
michael@0 3618 return nsContentUtils::ThreadsafeIsCallerChrome();
michael@0 3619 }
michael@0 3620
michael@0 3621 // static
michael@0 3622 already_AddRefed<WorkerPrivate>
michael@0 3623 WorkerPrivate::Constructor(const GlobalObject& aGlobal,
michael@0 3624 const nsAString& aScriptURL,
michael@0 3625 bool aIsChromeWorker, WorkerType aWorkerType,
michael@0 3626 const nsACString& aSharedWorkerName,
michael@0 3627 LoadInfo* aLoadInfo, ErrorResult& aRv)
michael@0 3628 {
michael@0 3629 WorkerPrivate* parent = NS_IsMainThread() ?
michael@0 3630 nullptr :
michael@0 3631 GetCurrentThreadWorkerPrivate();
michael@0 3632 if (parent) {
michael@0 3633 parent->AssertIsOnWorkerThread();
michael@0 3634 } else {
michael@0 3635 AssertIsOnMainThread();
michael@0 3636 }
michael@0 3637
michael@0 3638 JSContext* cx = aGlobal.GetContext();
michael@0 3639
michael@0 3640 MOZ_ASSERT_IF(aWorkerType == WorkerTypeShared,
michael@0 3641 !aSharedWorkerName.IsVoid());
michael@0 3642 MOZ_ASSERT_IF(aWorkerType != WorkerTypeShared,
michael@0 3643 aSharedWorkerName.IsEmpty());
michael@0 3644
michael@0 3645 Maybe<LoadInfo> stackLoadInfo;
michael@0 3646 if (!aLoadInfo) {
michael@0 3647 stackLoadInfo.construct();
michael@0 3648
michael@0 3649 nsresult rv = GetLoadInfo(cx, nullptr, parent, aScriptURL,
michael@0 3650 aIsChromeWorker, stackLoadInfo.addr());
michael@0 3651 if (NS_FAILED(rv)) {
michael@0 3652 scriptloader::ReportLoadError(cx, aScriptURL, rv, !parent);
michael@0 3653 aRv.Throw(rv);
michael@0 3654 return nullptr;
michael@0 3655 }
michael@0 3656
michael@0 3657 aLoadInfo = stackLoadInfo.addr();
michael@0 3658 }
michael@0 3659
michael@0 3660 // NB: This has to be done before creating the WorkerPrivate, because it will
michael@0 3661 // attempt to use static variables that are initialized in the RuntimeService
michael@0 3662 // constructor.
michael@0 3663 RuntimeService* runtimeService;
michael@0 3664
michael@0 3665 if (!parent) {
michael@0 3666 runtimeService = RuntimeService::GetOrCreateService();
michael@0 3667 if (!runtimeService) {
michael@0 3668 JS_ReportError(cx, "Failed to create runtime service!");
michael@0 3669 aRv.Throw(NS_ERROR_FAILURE);
michael@0 3670 return nullptr;
michael@0 3671 }
michael@0 3672 }
michael@0 3673 else {
michael@0 3674 runtimeService = RuntimeService::GetService();
michael@0 3675 }
michael@0 3676
michael@0 3677 MOZ_ASSERT(runtimeService);
michael@0 3678
michael@0 3679 nsRefPtr<WorkerPrivate> worker =
michael@0 3680 new WorkerPrivate(cx, parent, aScriptURL, aIsChromeWorker,
michael@0 3681 aWorkerType, aSharedWorkerName, *aLoadInfo);
michael@0 3682
michael@0 3683 if (!runtimeService->RegisterWorker(cx, worker)) {
michael@0 3684 aRv.Throw(NS_ERROR_UNEXPECTED);
michael@0 3685 return nullptr;
michael@0 3686 }
michael@0 3687
michael@0 3688 nsRefPtr<CompileScriptRunnable> compiler = new CompileScriptRunnable(worker);
michael@0 3689 if (!compiler->Dispatch(cx)) {
michael@0 3690 aRv.Throw(NS_ERROR_UNEXPECTED);
michael@0 3691 return nullptr;
michael@0 3692 }
michael@0 3693
michael@0 3694 worker->mSelfRef = worker;
michael@0 3695
michael@0 3696 return worker.forget();
michael@0 3697 }
michael@0 3698
michael@0 3699 // static
michael@0 3700 nsresult
michael@0 3701 WorkerPrivate::GetLoadInfo(JSContext* aCx, nsPIDOMWindow* aWindow,
michael@0 3702 WorkerPrivate* aParent, const nsAString& aScriptURL,
michael@0 3703 bool aIsChromeWorker, LoadInfo* aLoadInfo)
michael@0 3704 {
michael@0 3705 using namespace mozilla::dom::workers::scriptloader;
michael@0 3706
michael@0 3707 MOZ_ASSERT(aCx);
michael@0 3708
michael@0 3709 if (aWindow) {
michael@0 3710 AssertIsOnMainThread();
michael@0 3711 }
michael@0 3712
michael@0 3713 LoadInfo loadInfo;
michael@0 3714 nsresult rv;
michael@0 3715
michael@0 3716 if (aParent) {
michael@0 3717 aParent->AssertIsOnWorkerThread();
michael@0 3718
michael@0 3719 // If the parent is going away give up now.
michael@0 3720 Status parentStatus;
michael@0 3721 {
michael@0 3722 MutexAutoLock lock(aParent->mMutex);
michael@0 3723 parentStatus = aParent->mStatus;
michael@0 3724 }
michael@0 3725
michael@0 3726 if (parentStatus > Running) {
michael@0 3727 NS_WARNING("Cannot create child workers from the close handler!");
michael@0 3728 return NS_ERROR_FAILURE;
michael@0 3729 }
michael@0 3730
michael@0 3731 // StartAssignment() is used instead getter_AddRefs because, getter_AddRefs
michael@0 3732 // does QI in debug build and, if this worker runs in a child process,
michael@0 3733 // HttpChannelChild will crash because it's not thread-safe.
michael@0 3734 rv = ChannelFromScriptURLWorkerThread(aCx, aParent, aScriptURL,
michael@0 3735 loadInfo.mChannel.StartAssignment());
michael@0 3736 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3737
michael@0 3738 // Now that we've spun the loop there's no guarantee that our parent is
michael@0 3739 // still alive. We may have received control messages initiating shutdown.
michael@0 3740 {
michael@0 3741 MutexAutoLock lock(aParent->mMutex);
michael@0 3742 parentStatus = aParent->mStatus;
michael@0 3743 }
michael@0 3744
michael@0 3745 if (parentStatus > Running) {
michael@0 3746 nsCOMPtr<nsIThread> mainThread;
michael@0 3747 if (NS_FAILED(NS_GetMainThread(getter_AddRefs(mainThread))) ||
michael@0 3748 NS_FAILED(NS_ProxyRelease(mainThread, loadInfo.mChannel))) {
michael@0 3749 NS_WARNING("Failed to proxy release of channel, leaking instead!");
michael@0 3750 }
michael@0 3751 return NS_ERROR_FAILURE;
michael@0 3752 }
michael@0 3753
michael@0 3754 loadInfo.mDomain = aParent->Domain();
michael@0 3755 } else {
michael@0 3756 AssertIsOnMainThread();
michael@0 3757
michael@0 3758 nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
michael@0 3759 MOZ_ASSERT(ssm);
michael@0 3760
michael@0 3761 bool isChrome = nsContentUtils::IsCallerChrome();
michael@0 3762
michael@0 3763 // First check to make sure the caller has permission to make a privileged
michael@0 3764 // worker if they called the ChromeWorker/ChromeSharedWorker constructor.
michael@0 3765 if (aIsChromeWorker && !isChrome) {
michael@0 3766 return NS_ERROR_DOM_SECURITY_ERR;
michael@0 3767 }
michael@0 3768
michael@0 3769 // Chrome callers (whether ChromeWorker of Worker) always get the system
michael@0 3770 // principal here as they're allowed to load anything. The script loader may
michael@0 3771 // change the principal later depending on the script uri.
michael@0 3772 if (isChrome) {
michael@0 3773 rv = ssm->GetSystemPrincipal(getter_AddRefs(loadInfo.mPrincipal));
michael@0 3774 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3775 }
michael@0 3776
michael@0 3777 // See if we're being called from a window.
michael@0 3778 nsCOMPtr<nsPIDOMWindow> globalWindow = aWindow;
michael@0 3779 if (!globalWindow) {
michael@0 3780 nsCOMPtr<nsIScriptGlobalObject> scriptGlobal =
michael@0 3781 nsJSUtils::GetStaticScriptGlobal(JS::CurrentGlobalOrNull(aCx));
michael@0 3782 if (scriptGlobal) {
michael@0 3783 globalWindow = do_QueryInterface(scriptGlobal);
michael@0 3784 MOZ_ASSERT(globalWindow);
michael@0 3785 }
michael@0 3786 }
michael@0 3787
michael@0 3788 nsCOMPtr<nsIDocument> document;
michael@0 3789
michael@0 3790 if (globalWindow) {
michael@0 3791 // Only use the current inner window, and only use it if the caller can
michael@0 3792 // access it.
michael@0 3793 nsPIDOMWindow* outerWindow = globalWindow->GetOuterWindow();
michael@0 3794 if (outerWindow) {
michael@0 3795 loadInfo.mWindow = outerWindow->GetCurrentInnerWindow();
michael@0 3796 }
michael@0 3797
michael@0 3798 if (!loadInfo.mWindow ||
michael@0 3799 (globalWindow != loadInfo.mWindow &&
michael@0 3800 !nsContentUtils::CanCallerAccess(loadInfo.mWindow))) {
michael@0 3801 return NS_ERROR_DOM_SECURITY_ERR;
michael@0 3802 }
michael@0 3803
michael@0 3804 nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(loadInfo.mWindow);
michael@0 3805 MOZ_ASSERT(sgo);
michael@0 3806
michael@0 3807 loadInfo.mScriptContext = sgo->GetContext();
michael@0 3808 NS_ENSURE_TRUE(loadInfo.mScriptContext, NS_ERROR_FAILURE);
michael@0 3809
michael@0 3810 // If we're called from a window then we can dig out the principal and URI
michael@0 3811 // from the document.
michael@0 3812 document = loadInfo.mWindow->GetExtantDoc();
michael@0 3813 NS_ENSURE_TRUE(document, NS_ERROR_FAILURE);
michael@0 3814
michael@0 3815 loadInfo.mBaseURI = document->GetDocBaseURI();
michael@0 3816
michael@0 3817 // Use the document's NodePrincipal as our principal if we're not being
michael@0 3818 // called from chrome.
michael@0 3819 if (!loadInfo.mPrincipal) {
michael@0 3820 loadInfo.mPrincipal = document->NodePrincipal();
michael@0 3821 NS_ENSURE_TRUE(loadInfo.mPrincipal, NS_ERROR_FAILURE);
michael@0 3822
michael@0 3823 // We use the document's base domain to limit the number of workers
michael@0 3824 // each domain can create. For sandboxed documents, we use the domain
michael@0 3825 // of their first non-sandboxed document, walking up until we find
michael@0 3826 // one. If we can't find one, we fall back to using the GUID of the
michael@0 3827 // null principal as the base domain.
michael@0 3828 if (document->GetSandboxFlags() & SANDBOXED_ORIGIN) {
michael@0 3829 nsCOMPtr<nsIDocument> tmpDoc = document;
michael@0 3830 do {
michael@0 3831 tmpDoc = tmpDoc->GetParentDocument();
michael@0 3832 } while (tmpDoc && tmpDoc->GetSandboxFlags() & SANDBOXED_ORIGIN);
michael@0 3833
michael@0 3834 if (tmpDoc) {
michael@0 3835 // There was an unsandboxed ancestor, yay!
michael@0 3836 nsCOMPtr<nsIPrincipal> tmpPrincipal = tmpDoc->NodePrincipal();
michael@0 3837 rv = tmpPrincipal->GetBaseDomain(loadInfo.mDomain);
michael@0 3838 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3839 } else {
michael@0 3840 // No unsandboxed ancestor, use our GUID.
michael@0 3841 rv = loadInfo.mPrincipal->GetBaseDomain(loadInfo.mDomain);
michael@0 3842 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3843 }
michael@0 3844 } else {
michael@0 3845 // Document creating the worker is not sandboxed.
michael@0 3846 rv = loadInfo.mPrincipal->GetBaseDomain(loadInfo.mDomain);
michael@0 3847 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3848 }
michael@0 3849 }
michael@0 3850
michael@0 3851 nsCOMPtr<nsIPermissionManager> permMgr =
michael@0 3852 do_GetService(NS_PERMISSIONMANAGER_CONTRACTID, &rv);
michael@0 3853 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3854
michael@0 3855 uint32_t perm;
michael@0 3856 rv = permMgr->TestPermissionFromPrincipal(loadInfo.mPrincipal, "systemXHR",
michael@0 3857 &perm);
michael@0 3858 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3859
michael@0 3860 loadInfo.mXHRParamsAllowed = perm == nsIPermissionManager::ALLOW_ACTION;
michael@0 3861 } else {
michael@0 3862 // Not a window
michael@0 3863 MOZ_ASSERT(isChrome);
michael@0 3864
michael@0 3865 // We're being created outside of a window. Need to figure out the script
michael@0 3866 // that is creating us in order for us to use relative URIs later on.
michael@0 3867 JS::AutoFilename fileName;
michael@0 3868 if (JS::DescribeScriptedCaller(aCx, &fileName)) {
michael@0 3869 // In most cases, fileName is URI. In a few other cases
michael@0 3870 // (e.g. xpcshell), fileName is a file path. Ideally, we would
michael@0 3871 // prefer testing whether fileName parses as an URI and fallback
michael@0 3872 // to file path in case of error, but Windows file paths have
michael@0 3873 // the interesting property that they can be parsed as bogus
michael@0 3874 // URIs (e.g. C:/Windows/Tmp is interpreted as scheme "C",
michael@0 3875 // hostname "Windows", path "Tmp"), which defeats this algorithm.
michael@0 3876 // Therefore, we adopt the opposite convention.
michael@0 3877 nsCOMPtr<nsIFile> scriptFile =
michael@0 3878 do_CreateInstance("@mozilla.org/file/local;1", &rv);
michael@0 3879 if (NS_FAILED(rv)) {
michael@0 3880 return rv;
michael@0 3881 }
michael@0 3882
michael@0 3883 rv = scriptFile->InitWithPath(NS_ConvertUTF8toUTF16(fileName.get()));
michael@0 3884 if (NS_SUCCEEDED(rv)) {
michael@0 3885 rv = NS_NewFileURI(getter_AddRefs(loadInfo.mBaseURI),
michael@0 3886 scriptFile);
michael@0 3887 }
michael@0 3888 if (NS_FAILED(rv)) {
michael@0 3889 // As expected, fileName is not a path, so proceed with
michael@0 3890 // a uri.
michael@0 3891 rv = NS_NewURI(getter_AddRefs(loadInfo.mBaseURI),
michael@0 3892 fileName.get());
michael@0 3893 }
michael@0 3894 if (NS_FAILED(rv)) {
michael@0 3895 return rv;
michael@0 3896 }
michael@0 3897 }
michael@0 3898 loadInfo.mXHRParamsAllowed = true;
michael@0 3899 }
michael@0 3900
michael@0 3901 MOZ_ASSERT(loadInfo.mPrincipal);
michael@0 3902 MOZ_ASSERT(isChrome || !loadInfo.mDomain.IsEmpty());
michael@0 3903
michael@0 3904 // XXXbent Use subject principal here instead of the one we already have?
michael@0 3905 nsCOMPtr<nsIPrincipal> subjectPrincipal = ssm->GetCxSubjectPrincipal(aCx);
michael@0 3906 MOZ_ASSERT(subjectPrincipal);
michael@0 3907
michael@0 3908 if (!nsContentUtils::GetContentSecurityPolicy(aCx,
michael@0 3909 getter_AddRefs(loadInfo.mCSP))) {
michael@0 3910 NS_WARNING("Failed to get CSP!");
michael@0 3911 return NS_ERROR_FAILURE;
michael@0 3912 }
michael@0 3913
michael@0 3914 if (loadInfo.mCSP) {
michael@0 3915 rv = loadInfo.mCSP->GetAllowsEval(&loadInfo.mReportCSPViolations,
michael@0 3916 &loadInfo.mEvalAllowed);
michael@0 3917 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3918 } else {
michael@0 3919 loadInfo.mEvalAllowed = true;
michael@0 3920 loadInfo.mReportCSPViolations = false;
michael@0 3921 }
michael@0 3922
michael@0 3923 rv = ChannelFromScriptURLMainThread(loadInfo.mPrincipal, loadInfo.mBaseURI,
michael@0 3924 document, aScriptURL,
michael@0 3925 getter_AddRefs(loadInfo.mChannel));
michael@0 3926 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3927
michael@0 3928 rv = NS_GetFinalChannelURI(loadInfo.mChannel,
michael@0 3929 getter_AddRefs(loadInfo.mResolvedScriptURI));
michael@0 3930 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3931 }
michael@0 3932
michael@0 3933 aLoadInfo->StealFrom(loadInfo);
michael@0 3934 return NS_OK;
michael@0 3935 }
michael@0 3936
michael@0 3937 void
michael@0 3938 WorkerPrivate::DoRunLoop(JSContext* aCx)
michael@0 3939 {
michael@0 3940 AssertIsOnWorkerThread();
michael@0 3941 MOZ_ASSERT(mThread);
michael@0 3942
michael@0 3943 {
michael@0 3944 MutexAutoLock lock(mMutex);
michael@0 3945 mJSContext = aCx;
michael@0 3946
michael@0 3947 MOZ_ASSERT(mStatus == Pending);
michael@0 3948 mStatus = Running;
michael@0 3949 }
michael@0 3950
michael@0 3951 EnableMemoryReporter();
michael@0 3952
michael@0 3953 InitializeGCTimers();
michael@0 3954
michael@0 3955 Maybe<JSAutoCompartment> workerCompartment;
michael@0 3956
michael@0 3957 for (;;) {
michael@0 3958 // Workers lazily create a global object in CompileScriptRunnable. We need
michael@0 3959 // to enter the global's compartment as soon as it has been created.
michael@0 3960 if (workerCompartment.empty()) {
michael@0 3961 if (JSObject* global = js::DefaultObjectForContextOrNull(aCx)) {
michael@0 3962 workerCompartment.construct(aCx, global);
michael@0 3963 }
michael@0 3964 }
michael@0 3965
michael@0 3966 Status currentStatus;
michael@0 3967 bool normalRunnablesPending = false;
michael@0 3968
michael@0 3969 {
michael@0 3970 MutexAutoLock lock(mMutex);
michael@0 3971
michael@0 3972 while (mControlQueue.IsEmpty() &&
michael@0 3973 !(normalRunnablesPending = NS_HasPendingEvents(mThread))) {
michael@0 3974 WaitForWorkerEvents();
michael@0 3975 }
michael@0 3976
michael@0 3977 ProcessAllControlRunnablesLocked();
michael@0 3978
michael@0 3979 currentStatus = mStatus;
michael@0 3980 }
michael@0 3981
michael@0 3982 // If the close handler has finished and all features are done then we can
michael@0 3983 // kill this thread.
michael@0 3984 if (currentStatus != Running && !HasActiveFeatures()) {
michael@0 3985 if (mCloseHandlerFinished && currentStatus != Killing) {
michael@0 3986 if (!NotifyInternal(aCx, Killing)) {
michael@0 3987 JS_ReportPendingException(aCx);
michael@0 3988 }
michael@0 3989 #ifdef DEBUG
michael@0 3990 {
michael@0 3991 MutexAutoLock lock(mMutex);
michael@0 3992 currentStatus = mStatus;
michael@0 3993 }
michael@0 3994 MOZ_ASSERT(currentStatus == Killing);
michael@0 3995 #else
michael@0 3996 currentStatus = Killing;
michael@0 3997 #endif
michael@0 3998 }
michael@0 3999
michael@0 4000 // If we're supposed to die then we should exit the loop.
michael@0 4001 if (currentStatus == Killing) {
michael@0 4002 ShutdownGCTimers();
michael@0 4003
michael@0 4004 DisableMemoryReporter();
michael@0 4005
michael@0 4006 {
michael@0 4007 MutexAutoLock lock(mMutex);
michael@0 4008
michael@0 4009 mStatus = Dead;
michael@0 4010 mJSContext = nullptr;
michael@0 4011 }
michael@0 4012
michael@0 4013 // After mStatus is set to Dead there can be no more
michael@0 4014 // WorkerControlRunnables so no need to lock here.
michael@0 4015 if (!mControlQueue.IsEmpty()) {
michael@0 4016 WorkerControlRunnable* runnable;
michael@0 4017 while (mControlQueue.Pop(runnable)) {
michael@0 4018 runnable->Cancel();
michael@0 4019 runnable->Release();
michael@0 4020 }
michael@0 4021 }
michael@0 4022
michael@0 4023 // Clear away our MessagePorts.
michael@0 4024 mWorkerPorts.Clear();
michael@0 4025
michael@0 4026 // Unroot the global
michael@0 4027 mScope = nullptr;
michael@0 4028
michael@0 4029 return;
michael@0 4030 }
michael@0 4031 }
michael@0 4032
michael@0 4033 // Nothing to do here if we don't have any runnables in the main queue.
michael@0 4034 if (!normalRunnablesPending) {
michael@0 4035 SetGCTimerMode(IdleTimer);
michael@0 4036 continue;
michael@0 4037 }
michael@0 4038
michael@0 4039 MOZ_ASSERT(NS_HasPendingEvents(mThread));
michael@0 4040
michael@0 4041 // Start the periodic GC timer if it is not already running.
michael@0 4042 SetGCTimerMode(PeriodicTimer);
michael@0 4043
michael@0 4044 // Process a single runnable from the main queue.
michael@0 4045 MOZ_ALWAYS_TRUE(NS_ProcessNextEvent(mThread, false));
michael@0 4046
michael@0 4047 if (NS_HasPendingEvents(mThread)) {
michael@0 4048 // Now *might* be a good time to GC. Let the JS engine make the decision.
michael@0 4049 if (!workerCompartment.empty()) {
michael@0 4050 JS_MaybeGC(aCx);
michael@0 4051 }
michael@0 4052 }
michael@0 4053 else {
michael@0 4054 // The normal event queue has been exhausted, cancel the periodic GC timer
michael@0 4055 // and schedule the idle GC timer.
michael@0 4056 SetGCTimerMode(IdleTimer);
michael@0 4057 }
michael@0 4058 }
michael@0 4059
michael@0 4060 MOZ_ASSUME_UNREACHABLE("Shouldn't get here!");
michael@0 4061 }
michael@0 4062
michael@0 4063 void
michael@0 4064 WorkerPrivate::OnProcessNextEvent(uint32_t aRecursionDepth)
michael@0 4065 {
michael@0 4066 AssertIsOnWorkerThread();
michael@0 4067 MOZ_ASSERT(aRecursionDepth);
michael@0 4068
michael@0 4069 // Normally we process control runnables in DoRunLoop or RunCurrentSyncLoop.
michael@0 4070 // However, it's possible that non-worker C++ could spin its own nested event
michael@0 4071 // loop, and in that case we must ensure that we continue to process control
michael@0 4072 // runnables here.
michael@0 4073 if (aRecursionDepth > 1 &&
michael@0 4074 mSyncLoopStack.Length() < aRecursionDepth - 1) {
michael@0 4075 ProcessAllControlRunnables();
michael@0 4076 }
michael@0 4077 }
michael@0 4078
michael@0 4079 void
michael@0 4080 WorkerPrivate::AfterProcessNextEvent(uint32_t aRecursionDepth)
michael@0 4081 {
michael@0 4082 AssertIsOnWorkerThread();
michael@0 4083 MOZ_ASSERT(aRecursionDepth);
michael@0 4084 }
michael@0 4085
michael@0 4086 void
michael@0 4087 WorkerPrivate::InitializeGCTimers()
michael@0 4088 {
michael@0 4089 AssertIsOnWorkerThread();
michael@0 4090
michael@0 4091 // We need a timer for GC. The basic plan is to run a non-shrinking GC
michael@0 4092 // periodically (PERIODIC_GC_TIMER_DELAY_SEC) while the worker is running.
michael@0 4093 // Once the worker goes idle we set a short (IDLE_GC_TIMER_DELAY_SEC) timer to
michael@0 4094 // run a shrinking GC. If the worker receives more messages then the short
michael@0 4095 // timer is canceled and the periodic timer resumes.
michael@0 4096 mGCTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
michael@0 4097 MOZ_ASSERT(mGCTimer);
michael@0 4098
michael@0 4099 nsRefPtr<GarbageCollectRunnable> runnable =
michael@0 4100 new GarbageCollectRunnable(this, false, false);
michael@0 4101 mPeriodicGCTimerTarget = new TimerThreadEventTarget(this, runnable);
michael@0 4102
michael@0 4103 runnable = new GarbageCollectRunnable(this, true, false);
michael@0 4104 mIdleGCTimerTarget = new TimerThreadEventTarget(this, runnable);
michael@0 4105
michael@0 4106 mPeriodicGCTimerRunning = false;
michael@0 4107 mIdleGCTimerRunning = false;
michael@0 4108 }
michael@0 4109
michael@0 4110 void
michael@0 4111 WorkerPrivate::SetGCTimerMode(GCTimerMode aMode)
michael@0 4112 {
michael@0 4113 AssertIsOnWorkerThread();
michael@0 4114 MOZ_ASSERT(mGCTimer);
michael@0 4115 MOZ_ASSERT(mPeriodicGCTimerTarget);
michael@0 4116 MOZ_ASSERT(mIdleGCTimerTarget);
michael@0 4117
michael@0 4118 if ((aMode == PeriodicTimer && mPeriodicGCTimerRunning) ||
michael@0 4119 (aMode == IdleTimer && mIdleGCTimerRunning)) {
michael@0 4120 return;
michael@0 4121 }
michael@0 4122
michael@0 4123 MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mGCTimer->Cancel()));
michael@0 4124
michael@0 4125 mPeriodicGCTimerRunning = false;
michael@0 4126 mIdleGCTimerRunning = false;
michael@0 4127
michael@0 4128 LOG(("Worker %p canceled GC timer because %s\n", this,
michael@0 4129 aMode == PeriodicTimer ?
michael@0 4130 "periodic" :
michael@0 4131 aMode == IdleTimer ? "idle" : "none"));
michael@0 4132
michael@0 4133 if (aMode == NoTimer) {
michael@0 4134 return;
michael@0 4135 }
michael@0 4136
michael@0 4137 MOZ_ASSERT(aMode == PeriodicTimer || aMode == IdleTimer);
michael@0 4138
michael@0 4139 nsIEventTarget* target;
michael@0 4140 uint32_t delay;
michael@0 4141 int16_t type;
michael@0 4142
michael@0 4143 if (aMode == PeriodicTimer) {
michael@0 4144 target = mPeriodicGCTimerTarget;
michael@0 4145 delay = PERIODIC_GC_TIMER_DELAY_SEC * 1000;
michael@0 4146 type = nsITimer::TYPE_REPEATING_SLACK;
michael@0 4147 }
michael@0 4148 else {
michael@0 4149 target = mIdleGCTimerTarget;
michael@0 4150 delay = IDLE_GC_TIMER_DELAY_SEC * 1000;
michael@0 4151 type = nsITimer::TYPE_ONE_SHOT;
michael@0 4152 }
michael@0 4153
michael@0 4154 MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mGCTimer->SetTarget(target)));
michael@0 4155 MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mGCTimer->InitWithFuncCallback(DummyCallback,
michael@0 4156 nullptr, delay,
michael@0 4157 type)));
michael@0 4158
michael@0 4159 if (aMode == PeriodicTimer) {
michael@0 4160 LOG(("Worker %p scheduled periodic GC timer\n", this));
michael@0 4161 mPeriodicGCTimerRunning = true;
michael@0 4162 }
michael@0 4163 else {
michael@0 4164 LOG(("Worker %p scheduled idle GC timer\n", this));
michael@0 4165 mIdleGCTimerRunning = true;
michael@0 4166 }
michael@0 4167 }
michael@0 4168
michael@0 4169 void
michael@0 4170 WorkerPrivate::ShutdownGCTimers()
michael@0 4171 {
michael@0 4172 AssertIsOnWorkerThread();
michael@0 4173
michael@0 4174 MOZ_ASSERT(mGCTimer);
michael@0 4175
michael@0 4176 // Always make sure the timer is canceled.
michael@0 4177 MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mGCTimer->Cancel()));
michael@0 4178
michael@0 4179 LOG(("Worker %p killed the GC timer\n", this));
michael@0 4180
michael@0 4181 mGCTimer = nullptr;
michael@0 4182 mPeriodicGCTimerTarget = nullptr;
michael@0 4183 mIdleGCTimerTarget = nullptr;
michael@0 4184 mPeriodicGCTimerRunning = false;
michael@0 4185 mIdleGCTimerRunning = false;
michael@0 4186 }
michael@0 4187
michael@0 4188 bool
michael@0 4189 WorkerPrivate::InterruptCallback(JSContext* aCx)
michael@0 4190 {
michael@0 4191 AssertIsOnWorkerThread();
michael@0 4192
michael@0 4193 bool mayContinue = true;
michael@0 4194 bool scheduledIdleGC = false;
michael@0 4195
michael@0 4196 for (;;) {
michael@0 4197 // Run all control events now.
michael@0 4198 mayContinue = ProcessAllControlRunnables();
michael@0 4199
michael@0 4200 bool maySuspend = mSuspended;
michael@0 4201 if (maySuspend) {
michael@0 4202 MutexAutoLock lock(mMutex);
michael@0 4203 maySuspend = mStatus <= Running;
michael@0 4204 }
michael@0 4205
michael@0 4206 if (!mayContinue || !maySuspend) {
michael@0 4207 break;
michael@0 4208 }
michael@0 4209
michael@0 4210 // Cancel the periodic GC timer here before suspending. The idle GC timer
michael@0 4211 // will clean everything up once it runs.
michael@0 4212 if (!scheduledIdleGC) {
michael@0 4213 SetGCTimerMode(IdleTimer);
michael@0 4214 scheduledIdleGC = true;
michael@0 4215 }
michael@0 4216
michael@0 4217 while ((mayContinue = MayContinueRunning())) {
michael@0 4218 MutexAutoLock lock(mMutex);
michael@0 4219 if (!mControlQueue.IsEmpty()) {
michael@0 4220 break;
michael@0 4221 }
michael@0 4222
michael@0 4223 WaitForWorkerEvents(PR_MillisecondsToInterval(RemainingRunTimeMS()));
michael@0 4224 }
michael@0 4225 }
michael@0 4226
michael@0 4227 if (!mayContinue) {
michael@0 4228 // We want only uncatchable exceptions here.
michael@0 4229 NS_ASSERTION(!JS_IsExceptionPending(aCx),
michael@0 4230 "Should not have an exception set here!");
michael@0 4231 return false;
michael@0 4232 }
michael@0 4233
michael@0 4234 // Make sure the periodic timer gets turned back on here.
michael@0 4235 SetGCTimerMode(PeriodicTimer);
michael@0 4236
michael@0 4237 return true;
michael@0 4238 }
michael@0 4239
michael@0 4240 nsresult
michael@0 4241 WorkerPrivate::IsOnCurrentThread(bool* aIsOnCurrentThread)
michael@0 4242 {
michael@0 4243 // May be called on any thread!
michael@0 4244
michael@0 4245 MOZ_ASSERT(aIsOnCurrentThread);
michael@0 4246
michael@0 4247 nsCOMPtr<nsIThread> thread;
michael@0 4248 {
michael@0 4249 MutexAutoLock lock(mMutex);
michael@0 4250 thread = mThread;
michael@0 4251 }
michael@0 4252
michael@0 4253 if (!thread) {
michael@0 4254 NS_WARNING("Trying to test thread correctness after the worker has "
michael@0 4255 "released its thread!");
michael@0 4256 return NS_ERROR_FAILURE;
michael@0 4257 }
michael@0 4258
michael@0 4259 nsresult rv = thread->IsOnCurrentThread(aIsOnCurrentThread);
michael@0 4260 if (NS_WARN_IF(NS_FAILED(rv))) {
michael@0 4261 return rv;
michael@0 4262 }
michael@0 4263
michael@0 4264 return NS_OK;
michael@0 4265 }
michael@0 4266
michael@0 4267 void
michael@0 4268 WorkerPrivate::ScheduleDeletion(WorkerRanOrNot aRanOrNot)
michael@0 4269 {
michael@0 4270 AssertIsOnWorkerThread();
michael@0 4271 MOZ_ASSERT(mChildWorkers.IsEmpty());
michael@0 4272 MOZ_ASSERT(mSyncLoopStack.IsEmpty());
michael@0 4273
michael@0 4274 ClearMainEventQueue(aRanOrNot);
michael@0 4275
michael@0 4276 if (WorkerPrivate* parent = GetParent()) {
michael@0 4277 nsRefPtr<WorkerFinishedRunnable> runnable =
michael@0 4278 new WorkerFinishedRunnable(parent, this);
michael@0 4279 if (!runnable->Dispatch(nullptr)) {
michael@0 4280 NS_WARNING("Failed to dispatch runnable!");
michael@0 4281 }
michael@0 4282 }
michael@0 4283 else {
michael@0 4284 nsRefPtr<TopLevelWorkerFinishedRunnable> runnable =
michael@0 4285 new TopLevelWorkerFinishedRunnable(this);
michael@0 4286 if (NS_FAILED(NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL))) {
michael@0 4287 NS_WARNING("Failed to dispatch runnable!");
michael@0 4288 }
michael@0 4289 }
michael@0 4290 }
michael@0 4291
michael@0 4292 bool
michael@0 4293 WorkerPrivate::BlockAndCollectRuntimeStats(JS::RuntimeStats* aRtStats)
michael@0 4294 {
michael@0 4295 AssertIsOnMainThread();
michael@0 4296 mMutex.AssertCurrentThreadOwns();
michael@0 4297 NS_ASSERTION(aRtStats, "Null RuntimeStats!");
michael@0 4298
michael@0 4299 NS_ASSERTION(!mMemoryReporterRunning, "How can we get reentered here?!");
michael@0 4300
michael@0 4301 // This signals the worker that it should block itself as soon as possible.
michael@0 4302 mMemoryReporterRunning = true;
michael@0 4303
michael@0 4304 NS_ASSERTION(mJSContext, "This must never be null!");
michael@0 4305 JSRuntime* rt = JS_GetRuntime(mJSContext);
michael@0 4306
michael@0 4307 // If the worker is not already blocked (e.g. waiting for a worker event or
michael@0 4308 // currently in a ctypes call) then we need to trigger the interrupt
michael@0 4309 // callback to trap the worker.
michael@0 4310 if (!mBlockedForMemoryReporter) {
michael@0 4311 JS_RequestInterruptCallback(rt);
michael@0 4312
michael@0 4313 // Wait until the worker actually blocks.
michael@0 4314 while (!mBlockedForMemoryReporter) {
michael@0 4315 mMemoryReportCondVar.Wait();
michael@0 4316 }
michael@0 4317 }
michael@0 4318
michael@0 4319 bool succeeded = false;
michael@0 4320
michael@0 4321 // If mMemoryReporter is still set then we can do the actual report. Otherwise
michael@0 4322 // we're trying to shut down and we don't want to do anything but clean up.
michael@0 4323 if (mMemoryReporter) {
michael@0 4324 // Don't hold the lock while doing the actual report.
michael@0 4325 MutexAutoUnlock unlock(mMutex);
michael@0 4326 succeeded = JS::CollectRuntimeStats(rt, aRtStats, nullptr);
michael@0 4327 }
michael@0 4328
michael@0 4329 NS_ASSERTION(mMemoryReporterRunning, "This isn't possible!");
michael@0 4330 NS_ASSERTION(mBlockedForMemoryReporter, "Somehow we got unblocked!");
michael@0 4331
michael@0 4332 // Tell the worker that it can now continue its execution.
michael@0 4333 mMemoryReporterRunning = false;
michael@0 4334
michael@0 4335 // The worker may be waiting so we must notify.
michael@0 4336 mMemoryReportCondVar.Notify();
michael@0 4337
michael@0 4338 return succeeded;
michael@0 4339 }
michael@0 4340
michael@0 4341 void
michael@0 4342 WorkerPrivate::EnableMemoryReporter()
michael@0 4343 {
michael@0 4344 AssertIsOnWorkerThread();
michael@0 4345 MOZ_ASSERT(!mMemoryReporter);
michael@0 4346
michael@0 4347 // No need to lock here since the main thread can't race until we've
michael@0 4348 // successfully registered the reporter.
michael@0 4349 mMemoryReporter = new MemoryReporter(this);
michael@0 4350
michael@0 4351 if (NS_FAILED(RegisterWeakMemoryReporter(mMemoryReporter))) {
michael@0 4352 NS_WARNING("Failed to register memory reporter!");
michael@0 4353 // No need to lock here since a failed registration means our memory
michael@0 4354 // reporter can't start running. Just clean up.
michael@0 4355 mMemoryReporter = nullptr;
michael@0 4356 }
michael@0 4357 }
michael@0 4358
michael@0 4359 void
michael@0 4360 WorkerPrivate::DisableMemoryReporter()
michael@0 4361 {
michael@0 4362 AssertIsOnWorkerThread();
michael@0 4363
michael@0 4364 nsRefPtr<MemoryReporter> memoryReporter;
michael@0 4365 {
michael@0 4366 MutexAutoLock lock(mMutex);
michael@0 4367
michael@0 4368 // There is nothing to do here if the memory reporter was never successfully
michael@0 4369 // registered.
michael@0 4370 if (!mMemoryReporter) {
michael@0 4371 return;
michael@0 4372 }
michael@0 4373
michael@0 4374 // We don't need this set any longer. Swap it out so that we can unregister
michael@0 4375 // below.
michael@0 4376 mMemoryReporter.swap(memoryReporter);
michael@0 4377
michael@0 4378 // Next disable the memory reporter so that the main thread stops trying to
michael@0 4379 // signal us.
michael@0 4380 memoryReporter->Disable();
michael@0 4381
michael@0 4382 // If the memory reporter is waiting to start then we need to wait for it to
michael@0 4383 // finish.
michael@0 4384 if (mMemoryReporterRunning) {
michael@0 4385 NS_ASSERTION(!mBlockedForMemoryReporter,
michael@0 4386 "Can't be blocked in more than one place at the same time!");
michael@0 4387 mBlockedForMemoryReporter = true;
michael@0 4388
michael@0 4389 // Tell the main thread that we're blocked.
michael@0 4390 mMemoryReportCondVar.Notify();
michael@0 4391
michael@0 4392 // Wait for it the main thread to finish. Since we swapped out
michael@0 4393 // mMemoryReporter above the main thread should respond quickly.
michael@0 4394 while (mMemoryReporterRunning) {
michael@0 4395 mMemoryReportCondVar.Wait();
michael@0 4396 }
michael@0 4397
michael@0 4398 NS_ASSERTION(mBlockedForMemoryReporter, "Somehow we got unblocked!");
michael@0 4399 mBlockedForMemoryReporter = false;
michael@0 4400 }
michael@0 4401 }
michael@0 4402
michael@0 4403 // Finally unregister the memory reporter.
michael@0 4404 if (NS_FAILED(UnregisterWeakMemoryReporter(memoryReporter))) {
michael@0 4405 NS_WARNING("Failed to unregister memory reporter!");
michael@0 4406 }
michael@0 4407 }
michael@0 4408
michael@0 4409 void
michael@0 4410 WorkerPrivate::WaitForWorkerEvents(PRIntervalTime aInterval)
michael@0 4411 {
michael@0 4412 AssertIsOnWorkerThread();
michael@0 4413 mMutex.AssertCurrentThreadOwns();
michael@0 4414
michael@0 4415 NS_ASSERTION(!mBlockedForMemoryReporter,
michael@0 4416 "Can't be blocked in more than one place at the same time!");
michael@0 4417
michael@0 4418 // Let the main thread know that the worker is blocked and that memory
michael@0 4419 // reporting may proceed.
michael@0 4420 mBlockedForMemoryReporter = true;
michael@0 4421
michael@0 4422 // The main thread may be waiting so we must notify.
michael@0 4423 mMemoryReportCondVar.Notify();
michael@0 4424
michael@0 4425 // Now wait for an actual worker event.
michael@0 4426 mCondVar.Wait(aInterval);
michael@0 4427
michael@0 4428 // We've gotten some kind of signal but we can't continue until the memory
michael@0 4429 // reporter has finished. Wait again.
michael@0 4430 while (mMemoryReporterRunning) {
michael@0 4431 mMemoryReportCondVar.Wait();
michael@0 4432 }
michael@0 4433
michael@0 4434 NS_ASSERTION(mBlockedForMemoryReporter, "Somehow we got unblocked!");
michael@0 4435
michael@0 4436 // No need to notify here as the main thread isn't watching for this state.
michael@0 4437 mBlockedForMemoryReporter = false;
michael@0 4438 }
michael@0 4439
michael@0 4440 bool
michael@0 4441 WorkerPrivate::ProcessAllControlRunnablesLocked()
michael@0 4442 {
michael@0 4443 AssertIsOnWorkerThread();
michael@0 4444 mMutex.AssertCurrentThreadOwns();
michael@0 4445
michael@0 4446 bool result = true;
michael@0 4447
michael@0 4448 for (;;) {
michael@0 4449 // Block here if the memory reporter is trying to run.
michael@0 4450 if (mMemoryReporterRunning) {
michael@0 4451 MOZ_ASSERT(!mBlockedForMemoryReporter);
michael@0 4452
michael@0 4453 // Let the main thread know that we've received the block request and
michael@0 4454 // that memory reporting may proceed.
michael@0 4455 mBlockedForMemoryReporter = true;
michael@0 4456
michael@0 4457 // The main thread is almost certainly waiting so we must notify here.
michael@0 4458 mMemoryReportCondVar.Notify();
michael@0 4459
michael@0 4460 // Wait for the memory report to finish.
michael@0 4461 while (mMemoryReporterRunning) {
michael@0 4462 mMemoryReportCondVar.Wait();
michael@0 4463 }
michael@0 4464
michael@0 4465 MOZ_ASSERT(mBlockedForMemoryReporter);
michael@0 4466
michael@0 4467 // No need to notify here as the main thread isn't watching for this
michael@0 4468 // state.
michael@0 4469 mBlockedForMemoryReporter = false;
michael@0 4470 }
michael@0 4471
michael@0 4472 WorkerControlRunnable* event;
michael@0 4473 if (!mControlQueue.Pop(event)) {
michael@0 4474 break;
michael@0 4475 }
michael@0 4476
michael@0 4477 MutexAutoUnlock unlock(mMutex);
michael@0 4478
michael@0 4479 MOZ_ASSERT(event);
michael@0 4480 if (NS_FAILED(static_cast<nsIRunnable*>(event)->Run())) {
michael@0 4481 result = false;
michael@0 4482 }
michael@0 4483
michael@0 4484 event->Release();
michael@0 4485 }
michael@0 4486
michael@0 4487 return result;
michael@0 4488 }
michael@0 4489
michael@0 4490 void
michael@0 4491 WorkerPrivate::ClearMainEventQueue(WorkerRanOrNot aRanOrNot)
michael@0 4492 {
michael@0 4493 AssertIsOnWorkerThread();
michael@0 4494
michael@0 4495 MOZ_ASSERT(!mCancelAllPendingRunnables);
michael@0 4496 mCancelAllPendingRunnables = true;
michael@0 4497
michael@0 4498 if (WorkerNeverRan == aRanOrNot) {
michael@0 4499 for (uint32_t count = mPreStartRunnables.Length(), index = 0;
michael@0 4500 index < count;
michael@0 4501 index++) {
michael@0 4502 nsRefPtr<WorkerRunnable> runnable = mPreStartRunnables[index].forget();
michael@0 4503 static_cast<nsIRunnable*>(runnable.get())->Run();
michael@0 4504 }
michael@0 4505 } else {
michael@0 4506 nsIThread* currentThread = NS_GetCurrentThread();
michael@0 4507 MOZ_ASSERT(currentThread);
michael@0 4508
michael@0 4509 NS_ProcessPendingEvents(currentThread);
michael@0 4510 MOZ_ASSERT(!NS_HasPendingEvents(currentThread));
michael@0 4511 }
michael@0 4512
michael@0 4513 MOZ_ASSERT(mCancelAllPendingRunnables);
michael@0 4514 mCancelAllPendingRunnables = false;
michael@0 4515 }
michael@0 4516
michael@0 4517 uint32_t
michael@0 4518 WorkerPrivate::RemainingRunTimeMS() const
michael@0 4519 {
michael@0 4520 if (mKillTime.IsNull()) {
michael@0 4521 return UINT32_MAX;
michael@0 4522 }
michael@0 4523 TimeDuration runtime = mKillTime - TimeStamp::Now();
michael@0 4524 double ms = runtime > TimeDuration(0) ? runtime.ToMilliseconds() : 0;
michael@0 4525 return ms > double(UINT32_MAX) ? UINT32_MAX : uint32_t(ms);
michael@0 4526 }
michael@0 4527
michael@0 4528 bool
michael@0 4529 WorkerPrivate::SuspendInternal(JSContext* aCx)
michael@0 4530 {
michael@0 4531 AssertIsOnWorkerThread();
michael@0 4532
michael@0 4533 NS_ASSERTION(!mSuspended, "Already suspended!");
michael@0 4534
michael@0 4535 mSuspended = true;
michael@0 4536 return true;
michael@0 4537 }
michael@0 4538
michael@0 4539 bool
michael@0 4540 WorkerPrivate::ResumeInternal(JSContext* aCx)
michael@0 4541 {
michael@0 4542 AssertIsOnWorkerThread();
michael@0 4543
michael@0 4544 NS_ASSERTION(mSuspended, "Not yet suspended!");
michael@0 4545
michael@0 4546 mSuspended = false;
michael@0 4547 return true;
michael@0 4548 }
michael@0 4549
michael@0 4550 void
michael@0 4551 WorkerPrivate::TraceTimeouts(const TraceCallbacks& aCallbacks,
michael@0 4552 void* aClosure) const
michael@0 4553 {
michael@0 4554 AssertIsOnWorkerThread();
michael@0 4555
michael@0 4556 for (uint32_t index = 0; index < mTimeouts.Length(); index++) {
michael@0 4557 TimeoutInfo* info = mTimeouts[index];
michael@0 4558
michael@0 4559 if (info->mTimeoutCallable.isUndefined()) {
michael@0 4560 continue;
michael@0 4561 }
michael@0 4562
michael@0 4563 aCallbacks.Trace(&info->mTimeoutCallable, "mTimeoutCallable", aClosure);
michael@0 4564 for (uint32_t index2 = 0; index2 < info->mExtraArgVals.Length(); index2++) {
michael@0 4565 aCallbacks.Trace(&info->mExtraArgVals[index2], "mExtraArgVals[i]", aClosure);
michael@0 4566 }
michael@0 4567 }
michael@0 4568 }
michael@0 4569
michael@0 4570 bool
michael@0 4571 WorkerPrivate::ModifyBusyCountFromWorker(JSContext* aCx, bool aIncrease)
michael@0 4572 {
michael@0 4573 AssertIsOnWorkerThread();
michael@0 4574
michael@0 4575 {
michael@0 4576 MutexAutoLock lock(mMutex);
michael@0 4577
michael@0 4578 // If we're in shutdown then the busy count is no longer being considered so
michael@0 4579 // just return now.
michael@0 4580 if (mStatus >= Killing) {
michael@0 4581 return true;
michael@0 4582 }
michael@0 4583 }
michael@0 4584
michael@0 4585 nsRefPtr<ModifyBusyCountRunnable> runnable =
michael@0 4586 new ModifyBusyCountRunnable(this, aIncrease);
michael@0 4587 return runnable->Dispatch(aCx);
michael@0 4588 }
michael@0 4589
michael@0 4590 bool
michael@0 4591 WorkerPrivate::AddChildWorker(JSContext* aCx, ParentType* aChildWorker)
michael@0 4592 {
michael@0 4593 AssertIsOnWorkerThread();
michael@0 4594
michael@0 4595 #ifdef DEBUG
michael@0 4596 {
michael@0 4597 Status currentStatus;
michael@0 4598 {
michael@0 4599 MutexAutoLock lock(mMutex);
michael@0 4600 currentStatus = mStatus;
michael@0 4601 }
michael@0 4602
michael@0 4603 MOZ_ASSERT(currentStatus == Running);
michael@0 4604 }
michael@0 4605 #endif
michael@0 4606
michael@0 4607 NS_ASSERTION(!mChildWorkers.Contains(aChildWorker),
michael@0 4608 "Already know about this one!");
michael@0 4609 mChildWorkers.AppendElement(aChildWorker);
michael@0 4610
michael@0 4611 return mChildWorkers.Length() == 1 ?
michael@0 4612 ModifyBusyCountFromWorker(aCx, true) :
michael@0 4613 true;
michael@0 4614 }
michael@0 4615
michael@0 4616 void
michael@0 4617 WorkerPrivate::RemoveChildWorker(JSContext* aCx, ParentType* aChildWorker)
michael@0 4618 {
michael@0 4619 AssertIsOnWorkerThread();
michael@0 4620
michael@0 4621 NS_ASSERTION(mChildWorkers.Contains(aChildWorker),
michael@0 4622 "Didn't know about this one!");
michael@0 4623 mChildWorkers.RemoveElement(aChildWorker);
michael@0 4624
michael@0 4625 if (mChildWorkers.IsEmpty() && !ModifyBusyCountFromWorker(aCx, false)) {
michael@0 4626 NS_WARNING("Failed to modify busy count!");
michael@0 4627 }
michael@0 4628 }
michael@0 4629
michael@0 4630 bool
michael@0 4631 WorkerPrivate::AddFeature(JSContext* aCx, WorkerFeature* aFeature)
michael@0 4632 {
michael@0 4633 AssertIsOnWorkerThread();
michael@0 4634
michael@0 4635 {
michael@0 4636 MutexAutoLock lock(mMutex);
michael@0 4637
michael@0 4638 if (mStatus >= Canceling) {
michael@0 4639 return false;
michael@0 4640 }
michael@0 4641 }
michael@0 4642
michael@0 4643 NS_ASSERTION(!mFeatures.Contains(aFeature), "Already know about this one!");
michael@0 4644 mFeatures.AppendElement(aFeature);
michael@0 4645
michael@0 4646 return mFeatures.Length() == 1 ?
michael@0 4647 ModifyBusyCountFromWorker(aCx, true) :
michael@0 4648 true;
michael@0 4649 }
michael@0 4650
michael@0 4651 void
michael@0 4652 WorkerPrivate::RemoveFeature(JSContext* aCx, WorkerFeature* aFeature)
michael@0 4653 {
michael@0 4654 AssertIsOnWorkerThread();
michael@0 4655
michael@0 4656 NS_ASSERTION(mFeatures.Contains(aFeature), "Didn't know about this one!");
michael@0 4657 mFeatures.RemoveElement(aFeature);
michael@0 4658
michael@0 4659 if (mFeatures.IsEmpty() && !ModifyBusyCountFromWorker(aCx, false)) {
michael@0 4660 NS_WARNING("Failed to modify busy count!");
michael@0 4661 }
michael@0 4662 }
michael@0 4663
michael@0 4664 void
michael@0 4665 WorkerPrivate::NotifyFeatures(JSContext* aCx, Status aStatus)
michael@0 4666 {
michael@0 4667 AssertIsOnWorkerThread();
michael@0 4668
michael@0 4669 NS_ASSERTION(aStatus > Running, "Bad status!");
michael@0 4670
michael@0 4671 if (aStatus >= Closing) {
michael@0 4672 CancelAllTimeouts(aCx);
michael@0 4673 }
michael@0 4674
michael@0 4675 nsAutoTArray<WorkerFeature*, 30> features;
michael@0 4676 features.AppendElements(mFeatures);
michael@0 4677
michael@0 4678 for (uint32_t index = 0; index < features.Length(); index++) {
michael@0 4679 if (!features[index]->Notify(aCx, aStatus)) {
michael@0 4680 NS_WARNING("Failed to notify feature!");
michael@0 4681 }
michael@0 4682 }
michael@0 4683
michael@0 4684 nsAutoTArray<ParentType*, 10> children;
michael@0 4685 children.AppendElements(mChildWorkers);
michael@0 4686
michael@0 4687 for (uint32_t index = 0; index < children.Length(); index++) {
michael@0 4688 if (!children[index]->Notify(aCx, aStatus)) {
michael@0 4689 NS_WARNING("Failed to notify child worker!");
michael@0 4690 }
michael@0 4691 }
michael@0 4692 }
michael@0 4693
michael@0 4694 void
michael@0 4695 WorkerPrivate::CancelAllTimeouts(JSContext* aCx)
michael@0 4696 {
michael@0 4697 AssertIsOnWorkerThread();
michael@0 4698
michael@0 4699 if (mTimerRunning) {
michael@0 4700 NS_ASSERTION(mTimer, "Huh?!");
michael@0 4701 NS_ASSERTION(!mTimeouts.IsEmpty(), "Huh?!");
michael@0 4702
michael@0 4703 if (NS_FAILED(mTimer->Cancel())) {
michael@0 4704 NS_WARNING("Failed to cancel timer!");
michael@0 4705 }
michael@0 4706
michael@0 4707 for (uint32_t index = 0; index < mTimeouts.Length(); index++) {
michael@0 4708 mTimeouts[index]->mCanceled = true;
michael@0 4709 }
michael@0 4710
michael@0 4711 if (!RunExpiredTimeouts(aCx)) {
michael@0 4712 JS_ReportPendingException(aCx);
michael@0 4713 }
michael@0 4714
michael@0 4715 mTimerRunning = false;
michael@0 4716 }
michael@0 4717 #ifdef DEBUG
michael@0 4718 else if (!mRunningExpiredTimeouts) {
michael@0 4719 NS_ASSERTION(mTimeouts.IsEmpty(), "Huh?!");
michael@0 4720 }
michael@0 4721 #endif
michael@0 4722
michael@0 4723 mTimer = nullptr;
michael@0 4724 }
michael@0 4725
michael@0 4726 already_AddRefed<nsIEventTarget>
michael@0 4727 WorkerPrivate::CreateNewSyncLoop()
michael@0 4728 {
michael@0 4729 AssertIsOnWorkerThread();
michael@0 4730
michael@0 4731 nsCOMPtr<nsIThreadInternal> thread = do_QueryInterface(NS_GetCurrentThread());
michael@0 4732 MOZ_ASSERT(thread);
michael@0 4733
michael@0 4734 nsCOMPtr<nsIEventTarget> realEventTarget;
michael@0 4735 MOZ_ALWAYS_TRUE(NS_SUCCEEDED(thread->PushEventQueue(
michael@0 4736 getter_AddRefs(realEventTarget))));
michael@0 4737
michael@0 4738 nsRefPtr<EventTarget> workerEventTarget =
michael@0 4739 new EventTarget(this, realEventTarget);
michael@0 4740
michael@0 4741 {
michael@0 4742 // Modifications must be protected by mMutex in DEBUG builds, see comment
michael@0 4743 // about mSyncLoopStack in WorkerPrivate.h.
michael@0 4744 #ifdef DEBUG
michael@0 4745 MutexAutoLock lock(mMutex);
michael@0 4746 #endif
michael@0 4747
michael@0 4748 mSyncLoopStack.AppendElement(new SyncLoopInfo(workerEventTarget));
michael@0 4749 }
michael@0 4750
michael@0 4751 return workerEventTarget.forget();
michael@0 4752 }
michael@0 4753
michael@0 4754 bool
michael@0 4755 WorkerPrivate::RunCurrentSyncLoop()
michael@0 4756 {
michael@0 4757 AssertIsOnWorkerThread();
michael@0 4758
michael@0 4759 JSContext* cx = GetJSContext();
michael@0 4760 MOZ_ASSERT(cx);
michael@0 4761
michael@0 4762 // This should not change between now and the time we finish running this sync
michael@0 4763 // loop.
michael@0 4764 uint32_t currentLoopIndex = mSyncLoopStack.Length() - 1;
michael@0 4765
michael@0 4766 SyncLoopInfo* loopInfo = mSyncLoopStack[currentLoopIndex];
michael@0 4767
michael@0 4768 MOZ_ASSERT(loopInfo);
michael@0 4769 MOZ_ASSERT(!loopInfo->mHasRun);
michael@0 4770 MOZ_ASSERT(!loopInfo->mCompleted);
michael@0 4771
michael@0 4772 #ifdef DEBUG
michael@0 4773 loopInfo->mHasRun = true;
michael@0 4774 #endif
michael@0 4775
michael@0 4776 nsCOMPtr<nsIThreadInternal> thread = do_QueryInterface(mThread);
michael@0 4777 MOZ_ASSERT(thread);
michael@0 4778
michael@0 4779 while (!loopInfo->mCompleted) {
michael@0 4780 bool normalRunnablesPending = false;
michael@0 4781
michael@0 4782 // Don't block with the periodic GC timer running.
michael@0 4783 if (!NS_HasPendingEvents(thread)) {
michael@0 4784 SetGCTimerMode(IdleTimer);
michael@0 4785 }
michael@0 4786
michael@0 4787 // Wait for something to do.
michael@0 4788 {
michael@0 4789 MutexAutoLock lock(mMutex);
michael@0 4790
michael@0 4791 for (;;) {
michael@0 4792 while (mControlQueue.IsEmpty() &&
michael@0 4793 !normalRunnablesPending &&
michael@0 4794 !(normalRunnablesPending = NS_HasPendingEvents(thread))) {
michael@0 4795 WaitForWorkerEvents();
michael@0 4796 }
michael@0 4797
michael@0 4798 ProcessAllControlRunnablesLocked();
michael@0 4799
michael@0 4800 // NB: If we processed a NotifyRunnable, we might have run non-control
michael@0 4801 // runnables, one of which may have shut down the sync loop.
michael@0 4802 if (normalRunnablesPending || loopInfo->mCompleted) {
michael@0 4803 break;
michael@0 4804 }
michael@0 4805 }
michael@0 4806 }
michael@0 4807
michael@0 4808 if (normalRunnablesPending) {
michael@0 4809 // Make sure the periodic timer is running before we continue.
michael@0 4810 SetGCTimerMode(PeriodicTimer);
michael@0 4811
michael@0 4812 MOZ_ALWAYS_TRUE(NS_ProcessNextEvent(thread, false));
michael@0 4813
michael@0 4814 // Now *might* be a good time to GC. Let the JS engine make the decision.
michael@0 4815 JS_MaybeGC(cx);
michael@0 4816 }
michael@0 4817 }
michael@0 4818
michael@0 4819 // Make sure that the stack didn't change underneath us.
michael@0 4820 MOZ_ASSERT(mSyncLoopStack[currentLoopIndex] == loopInfo);
michael@0 4821
michael@0 4822 return DestroySyncLoop(currentLoopIndex);
michael@0 4823 }
michael@0 4824
michael@0 4825 bool
michael@0 4826 WorkerPrivate::DestroySyncLoop(uint32_t aLoopIndex, nsIThreadInternal* aThread)
michael@0 4827 {
michael@0 4828 MOZ_ASSERT(!mSyncLoopStack.IsEmpty());
michael@0 4829 MOZ_ASSERT(mSyncLoopStack.Length() - 1 == aLoopIndex);
michael@0 4830
michael@0 4831 if (!aThread) {
michael@0 4832 nsCOMPtr<nsIThreadInternal> thread = do_QueryInterface(mThread);
michael@0 4833 MOZ_ASSERT(thread);
michael@0 4834
michael@0 4835 aThread = thread.get();
michael@0 4836 }
michael@0 4837
michael@0 4838 // We're about to delete the loop, stash its event target and result.
michael@0 4839 SyncLoopInfo* loopInfo = mSyncLoopStack[aLoopIndex];
michael@0 4840 nsIEventTarget* nestedEventTarget =
michael@0 4841 loopInfo->mEventTarget->GetWeakNestedEventTarget();
michael@0 4842 MOZ_ASSERT(nestedEventTarget);
michael@0 4843
michael@0 4844 bool result = loopInfo->mResult;
michael@0 4845
michael@0 4846 {
michael@0 4847 // Modifications must be protected by mMutex in DEBUG builds, see comment
michael@0 4848 // about mSyncLoopStack in WorkerPrivate.h.
michael@0 4849 #ifdef DEBUG
michael@0 4850 MutexAutoLock lock(mMutex);
michael@0 4851 #endif
michael@0 4852
michael@0 4853 // This will delete |loopInfo|!
michael@0 4854 mSyncLoopStack.RemoveElementAt(aLoopIndex);
michael@0 4855 }
michael@0 4856
michael@0 4857 MOZ_ALWAYS_TRUE(NS_SUCCEEDED(aThread->PopEventQueue(nestedEventTarget)));
michael@0 4858
michael@0 4859 return result;
michael@0 4860 }
michael@0 4861
michael@0 4862 void
michael@0 4863 WorkerPrivate::StopSyncLoop(nsIEventTarget* aSyncLoopTarget, bool aResult)
michael@0 4864 {
michael@0 4865 AssertIsOnWorkerThread();
michael@0 4866 AssertValidSyncLoop(aSyncLoopTarget);
michael@0 4867
michael@0 4868 MOZ_ASSERT(!mSyncLoopStack.IsEmpty());
michael@0 4869
michael@0 4870 for (uint32_t index = mSyncLoopStack.Length(); index > 0; index--) {
michael@0 4871 nsAutoPtr<SyncLoopInfo>& loopInfo = mSyncLoopStack[index - 1];
michael@0 4872 MOZ_ASSERT(loopInfo);
michael@0 4873 MOZ_ASSERT(loopInfo->mEventTarget);
michael@0 4874
michael@0 4875 if (loopInfo->mEventTarget == aSyncLoopTarget) {
michael@0 4876 // Can't assert |loop->mHasRun| here because dispatch failures can cause
michael@0 4877 // us to bail out early.
michael@0 4878 MOZ_ASSERT(!loopInfo->mCompleted);
michael@0 4879
michael@0 4880 loopInfo->mResult = aResult;
michael@0 4881 loopInfo->mCompleted = true;
michael@0 4882
michael@0 4883 loopInfo->mEventTarget->Disable();
michael@0 4884
michael@0 4885 return;
michael@0 4886 }
michael@0 4887
michael@0 4888 MOZ_ASSERT(!SameCOMIdentity(loopInfo->mEventTarget, aSyncLoopTarget));
michael@0 4889 }
michael@0 4890
michael@0 4891 MOZ_CRASH("Unknown sync loop!");
michael@0 4892 }
michael@0 4893
michael@0 4894 #ifdef DEBUG
michael@0 4895 void
michael@0 4896 WorkerPrivate::AssertValidSyncLoop(nsIEventTarget* aSyncLoopTarget)
michael@0 4897 {
michael@0 4898 MOZ_ASSERT(aSyncLoopTarget);
michael@0 4899
michael@0 4900 EventTarget* workerTarget;
michael@0 4901 nsresult rv =
michael@0 4902 aSyncLoopTarget->QueryInterface(kDEBUGWorkerEventTargetIID,
michael@0 4903 reinterpret_cast<void**>(&workerTarget));
michael@0 4904 MOZ_ASSERT(NS_SUCCEEDED(rv));
michael@0 4905 MOZ_ASSERT(workerTarget);
michael@0 4906
michael@0 4907 bool valid = false;
michael@0 4908
michael@0 4909 {
michael@0 4910 MutexAutoLock lock(mMutex);
michael@0 4911
michael@0 4912 for (uint32_t index = 0; index < mSyncLoopStack.Length(); index++) {
michael@0 4913 nsAutoPtr<SyncLoopInfo>& loopInfo = mSyncLoopStack[index];
michael@0 4914 MOZ_ASSERT(loopInfo);
michael@0 4915 MOZ_ASSERT(loopInfo->mEventTarget);
michael@0 4916
michael@0 4917 if (loopInfo->mEventTarget == aSyncLoopTarget) {
michael@0 4918 valid = true;
michael@0 4919 break;
michael@0 4920 }
michael@0 4921
michael@0 4922 MOZ_ASSERT(!SameCOMIdentity(loopInfo->mEventTarget, aSyncLoopTarget));
michael@0 4923 }
michael@0 4924 }
michael@0 4925
michael@0 4926 MOZ_ASSERT(valid);
michael@0 4927 }
michael@0 4928 #endif
michael@0 4929
michael@0 4930 void
michael@0 4931 WorkerPrivate::PostMessageToParentInternal(
michael@0 4932 JSContext* aCx,
michael@0 4933 JS::Handle<JS::Value> aMessage,
michael@0 4934 const Optional<Sequence<JS::Value>>& aTransferable,
michael@0 4935 bool aToMessagePort,
michael@0 4936 uint64_t aMessagePortSerial,
michael@0 4937 ErrorResult& aRv)
michael@0 4938 {
michael@0 4939 AssertIsOnWorkerThread();
michael@0 4940
michael@0 4941 JS::Rooted<JS::Value> transferable(aCx, JS::UndefinedValue());
michael@0 4942 if (aTransferable.WasPassed()) {
michael@0 4943 const Sequence<JS::Value>& realTransferable = aTransferable.Value();
michael@0 4944
michael@0 4945 // The input sequence only comes from the generated bindings code, which
michael@0 4946 // ensures it is rooted.
michael@0 4947 JS::HandleValueArray elements =
michael@0 4948 JS::HandleValueArray::fromMarkedLocation(realTransferable.Length(),
michael@0 4949 realTransferable.Elements());
michael@0 4950
michael@0 4951 JSObject* array = JS_NewArrayObject(aCx, elements);
michael@0 4952 if (!array) {
michael@0 4953 aRv = NS_ERROR_OUT_OF_MEMORY;
michael@0 4954 return;
michael@0 4955 }
michael@0 4956 transferable.setObject(*array);
michael@0 4957 }
michael@0 4958
michael@0 4959 JSStructuredCloneCallbacks* callbacks =
michael@0 4960 IsChromeWorker() ?
michael@0 4961 &gChromeWorkerStructuredCloneCallbacks :
michael@0 4962 &gWorkerStructuredCloneCallbacks;
michael@0 4963
michael@0 4964 nsTArray<nsCOMPtr<nsISupports>> clonedObjects;
michael@0 4965
michael@0 4966 JSAutoStructuredCloneBuffer buffer;
michael@0 4967 if (!buffer.write(aCx, aMessage, transferable, callbacks, &clonedObjects)) {
michael@0 4968 aRv = NS_ERROR_DOM_DATA_CLONE_ERR;
michael@0 4969 return;
michael@0 4970 }
michael@0 4971
michael@0 4972 nsRefPtr<MessageEventRunnable> runnable =
michael@0 4973 new MessageEventRunnable(this,
michael@0 4974 WorkerRunnable::ParentThreadUnchangedBusyCount,
michael@0 4975 Move(buffer), clonedObjects, aToMessagePort,
michael@0 4976 aMessagePortSerial);
michael@0 4977 if (!runnable->Dispatch(aCx)) {
michael@0 4978 aRv = NS_ERROR_FAILURE;
michael@0 4979 }
michael@0 4980 }
michael@0 4981
michael@0 4982 void
michael@0 4983 WorkerPrivate::PostMessageToParentMessagePort(
michael@0 4984 JSContext* aCx,
michael@0 4985 uint64_t aMessagePortSerial,
michael@0 4986 JS::Handle<JS::Value> aMessage,
michael@0 4987 const Optional<Sequence<JS::Value>>& aTransferable,
michael@0 4988 ErrorResult& aRv)
michael@0 4989 {
michael@0 4990 AssertIsOnWorkerThread();
michael@0 4991
michael@0 4992 if (!mWorkerPorts.GetWeak(aMessagePortSerial)) {
michael@0 4993 // This port has been closed from the main thread. There's no point in
michael@0 4994 // sending this message so just bail.
michael@0 4995 return;
michael@0 4996 }
michael@0 4997
michael@0 4998 PostMessageToParentInternal(aCx, aMessage, aTransferable, true,
michael@0 4999 aMessagePortSerial, aRv);
michael@0 5000 }
michael@0 5001
michael@0 5002 bool
michael@0 5003 WorkerPrivate::NotifyInternal(JSContext* aCx, Status aStatus)
michael@0 5004 {
michael@0 5005 AssertIsOnWorkerThread();
michael@0 5006
michael@0 5007 NS_ASSERTION(aStatus > Running && aStatus < Dead, "Bad status!");
michael@0 5008
michael@0 5009 nsRefPtr<EventTarget> eventTarget;
michael@0 5010
michael@0 5011 // Save the old status and set the new status.
michael@0 5012 Status previousStatus;
michael@0 5013 {
michael@0 5014 MutexAutoLock lock(mMutex);
michael@0 5015
michael@0 5016 if (mStatus >= aStatus) {
michael@0 5017 MOZ_ASSERT(!mEventTarget);
michael@0 5018 return true;
michael@0 5019 }
michael@0 5020
michael@0 5021 previousStatus = mStatus;
michael@0 5022 mStatus = aStatus;
michael@0 5023
michael@0 5024 mEventTarget.swap(eventTarget);
michael@0 5025 }
michael@0 5026
michael@0 5027 // Now that mStatus > Running, no-one can create a new WorkerEventTarget or
michael@0 5028 // WorkerCrossThreadDispatcher if we don't already have one.
michael@0 5029 if (eventTarget) {
michael@0 5030 // Since we'll no longer process events, make sure we no longer allow anyone
michael@0 5031 // to post them. We have to do this without mMutex held, since our mutex
michael@0 5032 // must be acquired *after* the WorkerEventTarget's mutex when they're both
michael@0 5033 // held.
michael@0 5034 eventTarget->Disable();
michael@0 5035 eventTarget = nullptr;
michael@0 5036 }
michael@0 5037
michael@0 5038 if (mCrossThreadDispatcher) {
michael@0 5039 // Since we'll no longer process events, make sure we no longer allow
michael@0 5040 // anyone to post them. We have to do this without mMutex held, since our
michael@0 5041 // mutex must be acquired *after* mCrossThreadDispatcher's mutex when
michael@0 5042 // they're both held.
michael@0 5043 mCrossThreadDispatcher->Forget();
michael@0 5044 mCrossThreadDispatcher = nullptr;
michael@0 5045 }
michael@0 5046
michael@0 5047 MOZ_ASSERT(previousStatus != Pending);
michael@0 5048
michael@0 5049 MOZ_ASSERT(previousStatus >= Canceling || mKillTime.IsNull());
michael@0 5050
michael@0 5051 // Let all our features know the new status.
michael@0 5052 NotifyFeatures(aCx, aStatus);
michael@0 5053
michael@0 5054 // If this is the first time our status has changed then we need to clear the
michael@0 5055 // main event queue.
michael@0 5056 if (previousStatus == Running) {
michael@0 5057 ClearMainEventQueue(WorkerRan);
michael@0 5058 }
michael@0 5059
michael@0 5060 // If we've run the close handler, we don't need to do anything else.
michael@0 5061 if (mCloseHandlerFinished) {
michael@0 5062 return true;
michael@0 5063 }
michael@0 5064
michael@0 5065 // If the worker script never ran, or failed to compile, we don't need to do
michael@0 5066 // anything else, except pretend that we ran the close handler.
michael@0 5067 if (!JS::CurrentGlobalOrNull(aCx)) {
michael@0 5068 mCloseHandlerStarted = true;
michael@0 5069 mCloseHandlerFinished = true;
michael@0 5070 return true;
michael@0 5071 }
michael@0 5072
michael@0 5073 // If this is the first time our status has changed we also need to schedule
michael@0 5074 // the close handler unless we're being shut down.
michael@0 5075 if (previousStatus == Running && aStatus != Killing) {
michael@0 5076 MOZ_ASSERT(!mCloseHandlerStarted && !mCloseHandlerFinished);
michael@0 5077
michael@0 5078 nsRefPtr<CloseEventRunnable> closeRunnable = new CloseEventRunnable(this);
michael@0 5079 MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToCurrentThread(closeRunnable)));
michael@0 5080 }
michael@0 5081
michael@0 5082 if (aStatus == Closing) {
michael@0 5083 // Notify parent to stop sending us messages and balance our busy count.
michael@0 5084 nsRefPtr<CloseRunnable> runnable = new CloseRunnable(this);
michael@0 5085 if (!runnable->Dispatch(aCx)) {
michael@0 5086 return false;
michael@0 5087 }
michael@0 5088
michael@0 5089 // Don't abort the script.
michael@0 5090 return true;
michael@0 5091 }
michael@0 5092
michael@0 5093 if (aStatus == Terminating) {
michael@0 5094 // Only abort the script if we're not yet running the close handler.
michael@0 5095 return mCloseHandlerStarted;
michael@0 5096 }
michael@0 5097
michael@0 5098 if (aStatus == Canceling) {
michael@0 5099 // We need to enforce a timeout on the close handler.
michael@0 5100 MOZ_ASSERT(previousStatus >= Running && previousStatus <= Terminating);
michael@0 5101
michael@0 5102 uint32_t killSeconds = IsChromeWorker() ?
michael@0 5103 RuntimeService::GetChromeCloseHandlerTimeoutSeconds() :
michael@0 5104 RuntimeService::GetContentCloseHandlerTimeoutSeconds();
michael@0 5105
michael@0 5106 if (killSeconds) {
michael@0 5107 mKillTime = TimeStamp::Now() + TimeDuration::FromSeconds(killSeconds);
michael@0 5108
michael@0 5109 if (!mCloseHandlerFinished && !ScheduleKillCloseEventRunnable(aCx)) {
michael@0 5110 return false;
michael@0 5111 }
michael@0 5112 }
michael@0 5113
michael@0 5114 // Only abort the script if we're not yet running the close handler.
michael@0 5115 return mCloseHandlerStarted;
michael@0 5116 }
michael@0 5117
michael@0 5118 MOZ_ASSERT(aStatus == Killing);
michael@0 5119
michael@0 5120 mKillTime = TimeStamp::Now();
michael@0 5121
michael@0 5122 if (mCloseHandlerStarted && !mCloseHandlerFinished) {
michael@0 5123 ScheduleKillCloseEventRunnable(aCx);
michael@0 5124 }
michael@0 5125
michael@0 5126 // Always abort the script.
michael@0 5127 return false;
michael@0 5128 }
michael@0 5129
michael@0 5130 bool
michael@0 5131 WorkerPrivate::ScheduleKillCloseEventRunnable(JSContext* aCx)
michael@0 5132 {
michael@0 5133 AssertIsOnWorkerThread();
michael@0 5134 MOZ_ASSERT(!mKillTime.IsNull());
michael@0 5135
michael@0 5136 nsRefPtr<KillCloseEventRunnable> killCloseEventRunnable =
michael@0 5137 new KillCloseEventRunnable(this);
michael@0 5138 if (!killCloseEventRunnable->SetTimeout(aCx, RemainingRunTimeMS())) {
michael@0 5139 return false;
michael@0 5140 }
michael@0 5141
michael@0 5142 MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToCurrentThread(
michael@0 5143 killCloseEventRunnable)));
michael@0 5144
michael@0 5145 return true;
michael@0 5146 }
michael@0 5147
michael@0 5148 void
michael@0 5149 WorkerPrivate::ReportError(JSContext* aCx, const char* aMessage,
michael@0 5150 JSErrorReport* aReport)
michael@0 5151 {
michael@0 5152 AssertIsOnWorkerThread();
michael@0 5153
michael@0 5154 if (!MayContinueRunning() || mErrorHandlerRecursionCount == 2) {
michael@0 5155 return;
michael@0 5156 }
michael@0 5157
michael@0 5158 NS_ASSERTION(mErrorHandlerRecursionCount == 0 ||
michael@0 5159 mErrorHandlerRecursionCount == 1,
michael@0 5160 "Bad recursion logic!");
michael@0 5161
michael@0 5162 JS_ClearPendingException(aCx);
michael@0 5163
michael@0 5164 nsString message, filename, line;
michael@0 5165 uint32_t lineNumber, columnNumber, flags, errorNumber;
michael@0 5166
michael@0 5167 if (aReport) {
michael@0 5168 // ErrorEvent objects don't have a |name| field the way ES |Error| objects
michael@0 5169 // do. Traditionally (and mostly by accident), the |message| field of
michael@0 5170 // ErrorEvent has corresponded to |Name: Message| of the original Error
michael@0 5171 // object. Things have been cleaned up in the JS engine, so now we need to
michael@0 5172 // format this string explicitly.
michael@0 5173 JS::Rooted<JSString*> messageStr(aCx,
michael@0 5174 js::ErrorReportToString(aCx, aReport));
michael@0 5175 if (messageStr) {
michael@0 5176 nsDependentJSString depStr;
michael@0 5177 if (depStr.init(aCx, messageStr)) {
michael@0 5178 message = depStr;
michael@0 5179 }
michael@0 5180 }
michael@0 5181 filename = NS_ConvertUTF8toUTF16(aReport->filename);
michael@0 5182 line = aReport->uclinebuf;
michael@0 5183 lineNumber = aReport->lineno;
michael@0 5184 columnNumber = aReport->uctokenptr - aReport->uclinebuf;
michael@0 5185 flags = aReport->flags;
michael@0 5186 errorNumber = aReport->errorNumber;
michael@0 5187 }
michael@0 5188 else {
michael@0 5189 lineNumber = columnNumber = errorNumber = 0;
michael@0 5190 flags = nsIScriptError::errorFlag | nsIScriptError::exceptionFlag;
michael@0 5191 }
michael@0 5192
michael@0 5193 if (message.IsEmpty()) {
michael@0 5194 message = NS_ConvertUTF8toUTF16(aMessage);
michael@0 5195 }
michael@0 5196
michael@0 5197 mErrorHandlerRecursionCount++;
michael@0 5198
michael@0 5199 // Don't want to run the scope's error handler if this is a recursive error or
michael@0 5200 // if there was an error in the close handler or if we ran out of memory.
michael@0 5201 bool fireAtScope = mErrorHandlerRecursionCount == 1 &&
michael@0 5202 !mCloseHandlerStarted &&
michael@0 5203 errorNumber != JSMSG_OUT_OF_MEMORY;
michael@0 5204
michael@0 5205 if (!ReportErrorRunnable::ReportError(aCx, this, fireAtScope, nullptr, message,
michael@0 5206 filename, line, lineNumber,
michael@0 5207 columnNumber, flags, errorNumber, 0)) {
michael@0 5208 JS_ReportPendingException(aCx);
michael@0 5209 }
michael@0 5210
michael@0 5211 mErrorHandlerRecursionCount--;
michael@0 5212 }
michael@0 5213
michael@0 5214 int32_t
michael@0 5215 WorkerPrivate::SetTimeout(JSContext* aCx,
michael@0 5216 Function* aHandler,
michael@0 5217 const nsAString& aStringHandler,
michael@0 5218 int32_t aTimeout,
michael@0 5219 const Sequence<JS::Value>& aArguments,
michael@0 5220 bool aIsInterval,
michael@0 5221 ErrorResult& aRv)
michael@0 5222 {
michael@0 5223 AssertIsOnWorkerThread();
michael@0 5224
michael@0 5225 const int32_t timerId = mNextTimeoutId++;
michael@0 5226
michael@0 5227 Status currentStatus;
michael@0 5228 {
michael@0 5229 MutexAutoLock lock(mMutex);
michael@0 5230 currentStatus = mStatus;
michael@0 5231 }
michael@0 5232
michael@0 5233 // It's a script bug if setTimeout/setInterval are called from a close handler
michael@0 5234 // so throw an exception.
michael@0 5235 if (currentStatus == Closing) {
michael@0 5236 JS_ReportError(aCx, "Cannot schedule timeouts from the close handler!");
michael@0 5237 }
michael@0 5238
michael@0 5239 // If the worker is trying to call setTimeout/setInterval and the parent
michael@0 5240 // thread has initiated the close process then just silently fail.
michael@0 5241 if (currentStatus >= Closing) {
michael@0 5242 aRv.Throw(NS_ERROR_FAILURE);
michael@0 5243 return 0;
michael@0 5244 }
michael@0 5245
michael@0 5246 nsAutoPtr<TimeoutInfo> newInfo(new TimeoutInfo());
michael@0 5247 newInfo->mIsInterval = aIsInterval;
michael@0 5248 newInfo->mId = timerId;
michael@0 5249
michael@0 5250 if (MOZ_UNLIKELY(timerId == INT32_MAX)) {
michael@0 5251 NS_WARNING("Timeout ids overflowed!");
michael@0 5252 mNextTimeoutId = 1;
michael@0 5253 }
michael@0 5254
michael@0 5255 // Take care of the main argument.
michael@0 5256 if (aHandler) {
michael@0 5257 newInfo->mTimeoutCallable = JS::ObjectValue(*aHandler->Callable());
michael@0 5258 }
michael@0 5259 else if (!aStringHandler.IsEmpty()) {
michael@0 5260 newInfo->mTimeoutString = aStringHandler;
michael@0 5261 }
michael@0 5262 else {
michael@0 5263 JS_ReportError(aCx, "Useless %s call (missing quotes around argument?)",
michael@0 5264 aIsInterval ? "setInterval" : "setTimeout");
michael@0 5265 return 0;
michael@0 5266 }
michael@0 5267
michael@0 5268 // See if any of the optional arguments were passed.
michael@0 5269 aTimeout = std::max(0, aTimeout);
michael@0 5270 newInfo->mInterval = TimeDuration::FromMilliseconds(aTimeout);
michael@0 5271
michael@0 5272 uint32_t argc = aArguments.Length();
michael@0 5273 if (argc && !newInfo->mTimeoutCallable.isUndefined()) {
michael@0 5274 nsTArray<JS::Heap<JS::Value>> extraArgVals(argc);
michael@0 5275 for (uint32_t index = 0; index < argc; index++) {
michael@0 5276 extraArgVals.AppendElement(aArguments[index]);
michael@0 5277 }
michael@0 5278 newInfo->mExtraArgVals.SwapElements(extraArgVals);
michael@0 5279 }
michael@0 5280
michael@0 5281 newInfo->mTargetTime = TimeStamp::Now() + newInfo->mInterval;
michael@0 5282
michael@0 5283 if (!newInfo->mTimeoutString.IsEmpty()) {
michael@0 5284 const char* filenameChars;
michael@0 5285 uint32_t lineNumber;
michael@0 5286 if (nsJSUtils::GetCallingLocation(aCx, &filenameChars, &lineNumber)) {
michael@0 5287 newInfo->mFilename = filenameChars;
michael@0 5288 newInfo->mLineNumber = lineNumber;
michael@0 5289 }
michael@0 5290 else {
michael@0 5291 NS_WARNING("Failed to get calling location!");
michael@0 5292 }
michael@0 5293 }
michael@0 5294
michael@0 5295 nsAutoPtr<TimeoutInfo>* insertedInfo =
michael@0 5296 mTimeouts.InsertElementSorted(newInfo.forget(), GetAutoPtrComparator(mTimeouts));
michael@0 5297
michael@0 5298 // If the timeout we just made is set to fire next then we need to update the
michael@0 5299 // timer.
michael@0 5300 if (insertedInfo == mTimeouts.Elements()) {
michael@0 5301 nsresult rv;
michael@0 5302
michael@0 5303 if (!mTimer) {
michael@0 5304 nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
michael@0 5305 if (NS_FAILED(rv)) {
michael@0 5306 aRv.Throw(rv);
michael@0 5307 return 0;
michael@0 5308 }
michael@0 5309
michael@0 5310 nsRefPtr<TimerRunnable> runnable = new TimerRunnable(this);
michael@0 5311
michael@0 5312 nsRefPtr<TimerThreadEventTarget> target =
michael@0 5313 new TimerThreadEventTarget(this, runnable);
michael@0 5314
michael@0 5315 rv = timer->SetTarget(target);
michael@0 5316 if (NS_FAILED(rv)) {
michael@0 5317 aRv.Throw(rv);
michael@0 5318 return 0;
michael@0 5319 }
michael@0 5320
michael@0 5321 timer.swap(mTimer);
michael@0 5322 }
michael@0 5323
michael@0 5324 if (!mTimerRunning) {
michael@0 5325 if (!ModifyBusyCountFromWorker(aCx, true)) {
michael@0 5326 aRv.Throw(NS_ERROR_FAILURE);
michael@0 5327 return 0;
michael@0 5328 }
michael@0 5329 mTimerRunning = true;
michael@0 5330 }
michael@0 5331
michael@0 5332 if (!RescheduleTimeoutTimer(aCx)) {
michael@0 5333 aRv.Throw(NS_ERROR_FAILURE);
michael@0 5334 return 0;
michael@0 5335 }
michael@0 5336 }
michael@0 5337
michael@0 5338 return timerId;
michael@0 5339 }
michael@0 5340
michael@0 5341 void
michael@0 5342 WorkerPrivate::ClearTimeout(int32_t aId)
michael@0 5343 {
michael@0 5344 AssertIsOnWorkerThread();
michael@0 5345
michael@0 5346 if (!mTimeouts.IsEmpty()) {
michael@0 5347 NS_ASSERTION(mTimerRunning, "Huh?!");
michael@0 5348
michael@0 5349 for (uint32_t index = 0; index < mTimeouts.Length(); index++) {
michael@0 5350 nsAutoPtr<TimeoutInfo>& info = mTimeouts[index];
michael@0 5351 if (info->mId == aId) {
michael@0 5352 info->mCanceled = true;
michael@0 5353 break;
michael@0 5354 }
michael@0 5355 }
michael@0 5356 }
michael@0 5357 }
michael@0 5358
michael@0 5359 bool
michael@0 5360 WorkerPrivate::RunExpiredTimeouts(JSContext* aCx)
michael@0 5361 {
michael@0 5362 AssertIsOnWorkerThread();
michael@0 5363
michael@0 5364 // We may be called recursively (e.g. close() inside a timeout) or we could
michael@0 5365 // have been canceled while this event was pending, bail out if there is
michael@0 5366 // nothing to do.
michael@0 5367 if (mRunningExpiredTimeouts || !mTimerRunning) {
michael@0 5368 return true;
michael@0 5369 }
michael@0 5370
michael@0 5371 NS_ASSERTION(mTimer, "Must have a timer!");
michael@0 5372 NS_ASSERTION(!mTimeouts.IsEmpty(), "Should have some work to do!");
michael@0 5373
michael@0 5374 bool retval = true;
michael@0 5375
michael@0 5376 AutoPtrComparator<TimeoutInfo> comparator = GetAutoPtrComparator(mTimeouts);
michael@0 5377 JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx));
michael@0 5378
michael@0 5379 // We want to make sure to run *something*, even if the timer fired a little
michael@0 5380 // early. Fudge the value of now to at least include the first timeout.
michael@0 5381 const TimeStamp now = std::max(TimeStamp::Now(), mTimeouts[0]->mTargetTime);
michael@0 5382
michael@0 5383 nsAutoTArray<TimeoutInfo*, 10> expiredTimeouts;
michael@0 5384 for (uint32_t index = 0; index < mTimeouts.Length(); index++) {
michael@0 5385 nsAutoPtr<TimeoutInfo>& info = mTimeouts[index];
michael@0 5386 if (info->mTargetTime > now) {
michael@0 5387 break;
michael@0 5388 }
michael@0 5389 expiredTimeouts.AppendElement(info);
michael@0 5390 }
michael@0 5391
michael@0 5392 // Guard against recursion.
michael@0 5393 mRunningExpiredTimeouts = true;
michael@0 5394
michael@0 5395 // Run expired timeouts.
michael@0 5396 for (uint32_t index = 0; index < expiredTimeouts.Length(); index++) {
michael@0 5397 TimeoutInfo*& info = expiredTimeouts[index];
michael@0 5398
michael@0 5399 if (info->mCanceled) {
michael@0 5400 continue;
michael@0 5401 }
michael@0 5402
michael@0 5403 // Always call JS_ReportPendingException if something fails, and if
michael@0 5404 // JS_ReportPendingException returns false (i.e. uncatchable exception) then
michael@0 5405 // break out of the loop.
michael@0 5406
michael@0 5407 if (!info->mTimeoutCallable.isUndefined()) {
michael@0 5408 JS::Rooted<JS::Value> rval(aCx);
michael@0 5409 JS::HandleValueArray args =
michael@0 5410 JS::HandleValueArray::fromMarkedLocation(info->mExtraArgVals.Length(),
michael@0 5411 info->mExtraArgVals.Elements()->address());
michael@0 5412 JS::Rooted<JS::Value> callable(aCx, info->mTimeoutCallable);
michael@0 5413 if (!JS_CallFunctionValue(aCx, global, callable, args, &rval) &&
michael@0 5414 !JS_ReportPendingException(aCx)) {
michael@0 5415 retval = false;
michael@0 5416 break;
michael@0 5417 }
michael@0 5418 }
michael@0 5419 else {
michael@0 5420 nsString expression = info->mTimeoutString;
michael@0 5421
michael@0 5422 JS::CompileOptions options(aCx);
michael@0 5423 options.setFileAndLine(info->mFilename.get(), info->mLineNumber);
michael@0 5424
michael@0 5425 if ((expression.IsEmpty() ||
michael@0 5426 !JS::Evaluate(aCx, global, options, expression.get(), expression.Length())) &&
michael@0 5427 !JS_ReportPendingException(aCx)) {
michael@0 5428 retval = false;
michael@0 5429 break;
michael@0 5430 }
michael@0 5431 }
michael@0 5432
michael@0 5433 NS_ASSERTION(mRunningExpiredTimeouts, "Someone changed this!");
michael@0 5434 }
michael@0 5435
michael@0 5436 // No longer possible to be called recursively.
michael@0 5437 mRunningExpiredTimeouts = false;
michael@0 5438
michael@0 5439 // Now remove canceled and expired timeouts from the main list.
michael@0 5440 // NB: The timeouts present in expiredTimeouts must have the same order
michael@0 5441 // with respect to each other in mTimeouts. That is, mTimeouts is just
michael@0 5442 // expiredTimeouts with extra elements inserted. There may be unexpired
michael@0 5443 // timeouts that have been inserted between the expired timeouts if the
michael@0 5444 // timeout event handler called setTimeout/setInterval.
michael@0 5445 for (uint32_t index = 0, expiredTimeoutIndex = 0,
michael@0 5446 expiredTimeoutLength = expiredTimeouts.Length();
michael@0 5447 index < mTimeouts.Length(); ) {
michael@0 5448 nsAutoPtr<TimeoutInfo>& info = mTimeouts[index];
michael@0 5449 if ((expiredTimeoutIndex < expiredTimeoutLength &&
michael@0 5450 info == expiredTimeouts[expiredTimeoutIndex] &&
michael@0 5451 ++expiredTimeoutIndex) ||
michael@0 5452 info->mCanceled) {
michael@0 5453 if (info->mIsInterval && !info->mCanceled) {
michael@0 5454 // Reschedule intervals.
michael@0 5455 info->mTargetTime = info->mTargetTime + info->mInterval;
michael@0 5456 // Don't resort the list here, we'll do that at the end.
michael@0 5457 ++index;
michael@0 5458 }
michael@0 5459 else {
michael@0 5460 mTimeouts.RemoveElement(info);
michael@0 5461 }
michael@0 5462 }
michael@0 5463 else {
michael@0 5464 // If info did not match the current entry in expiredTimeouts, it
michael@0 5465 // shouldn't be there at all.
michael@0 5466 NS_ASSERTION(!expiredTimeouts.Contains(info),
michael@0 5467 "Our timeouts are out of order!");
michael@0 5468 ++index;
michael@0 5469 }
michael@0 5470 }
michael@0 5471
michael@0 5472 mTimeouts.Sort(comparator);
michael@0 5473
michael@0 5474 // Either signal the parent that we're no longer using timeouts or reschedule
michael@0 5475 // the timer.
michael@0 5476 if (mTimeouts.IsEmpty()) {
michael@0 5477 if (!ModifyBusyCountFromWorker(aCx, false)) {
michael@0 5478 retval = false;
michael@0 5479 }
michael@0 5480 mTimerRunning = false;
michael@0 5481 }
michael@0 5482 else if (retval && !RescheduleTimeoutTimer(aCx)) {
michael@0 5483 retval = false;
michael@0 5484 }
michael@0 5485
michael@0 5486 return retval;
michael@0 5487 }
michael@0 5488
michael@0 5489 bool
michael@0 5490 WorkerPrivate::RescheduleTimeoutTimer(JSContext* aCx)
michael@0 5491 {
michael@0 5492 AssertIsOnWorkerThread();
michael@0 5493 NS_ASSERTION(!mTimeouts.IsEmpty(), "Should have some timeouts!");
michael@0 5494 NS_ASSERTION(mTimer, "Should have a timer!");
michael@0 5495
michael@0 5496 double delta =
michael@0 5497 (mTimeouts[0]->mTargetTime - TimeStamp::Now()).ToMilliseconds();
michael@0 5498 uint32_t delay = delta > 0 ? std::min(delta, double(UINT32_MAX)) : 0;
michael@0 5499
michael@0 5500 nsresult rv = mTimer->InitWithFuncCallback(DummyCallback, nullptr, delay,
michael@0 5501 nsITimer::TYPE_ONE_SHOT);
michael@0 5502 if (NS_FAILED(rv)) {
michael@0 5503 JS_ReportError(aCx, "Failed to start timer!");
michael@0 5504 return false;
michael@0 5505 }
michael@0 5506
michael@0 5507 return true;
michael@0 5508 }
michael@0 5509
michael@0 5510 void
michael@0 5511 WorkerPrivate::UpdateRuntimeAndContextOptionsInternal(
michael@0 5512 JSContext* aCx,
michael@0 5513 const JS::RuntimeOptions& aRuntimeOptions,
michael@0 5514 const JS::ContextOptions& aContentCxOptions,
michael@0 5515 const JS::ContextOptions& aChromeCxOptions)
michael@0 5516 {
michael@0 5517 AssertIsOnWorkerThread();
michael@0 5518
michael@0 5519 JS::RuntimeOptionsRef(aCx) = aRuntimeOptions;
michael@0 5520 JS::ContextOptionsRef(aCx) = IsChromeWorker() ? aChromeCxOptions : aContentCxOptions;
michael@0 5521
michael@0 5522 for (uint32_t index = 0; index < mChildWorkers.Length(); index++) {
michael@0 5523 mChildWorkers[index]->UpdateRuntimeAndContextOptions(aCx, aRuntimeOptions,
michael@0 5524 aContentCxOptions,
michael@0 5525 aChromeCxOptions);
michael@0 5526 }
michael@0 5527 }
michael@0 5528
michael@0 5529 void
michael@0 5530 WorkerPrivate::UpdatePreferenceInternal(JSContext* aCx, WorkerPreference aPref, bool aValue)
michael@0 5531 {
michael@0 5532 AssertIsOnWorkerThread();
michael@0 5533 MOZ_ASSERT(aPref >= 0 && aPref < WORKERPREF_COUNT);
michael@0 5534
michael@0 5535 mPreferences[aPref] = aValue;
michael@0 5536
michael@0 5537 for (uint32_t index = 0; index < mChildWorkers.Length(); index++) {
michael@0 5538 mChildWorkers[index]->UpdatePreference(aCx, aPref, aValue);
michael@0 5539 }
michael@0 5540 }
michael@0 5541
michael@0 5542 void
michael@0 5543 WorkerPrivate::UpdateJSWorkerMemoryParameterInternal(JSContext* aCx,
michael@0 5544 JSGCParamKey aKey,
michael@0 5545 uint32_t aValue)
michael@0 5546 {
michael@0 5547 AssertIsOnWorkerThread();
michael@0 5548
michael@0 5549 // XXX aValue might be 0 here (telling us to unset a previous value for child
michael@0 5550 // workers). Calling JS_SetGCParameter with a value of 0 isn't actually
michael@0 5551 // supported though. We really need some way to revert to a default value
michael@0 5552 // here.
michael@0 5553 if (aValue) {
michael@0 5554 JS_SetGCParameter(JS_GetRuntime(aCx), aKey, aValue);
michael@0 5555 }
michael@0 5556
michael@0 5557 for (uint32_t index = 0; index < mChildWorkers.Length(); index++) {
michael@0 5558 mChildWorkers[index]->UpdateJSWorkerMemoryParameter(aCx, aKey, aValue);
michael@0 5559 }
michael@0 5560 }
michael@0 5561
michael@0 5562 #ifdef JS_GC_ZEAL
michael@0 5563 void
michael@0 5564 WorkerPrivate::UpdateGCZealInternal(JSContext* aCx, uint8_t aGCZeal,
michael@0 5565 uint32_t aFrequency)
michael@0 5566 {
michael@0 5567 AssertIsOnWorkerThread();
michael@0 5568
michael@0 5569 JS_SetGCZeal(aCx, aGCZeal, aFrequency);
michael@0 5570
michael@0 5571 for (uint32_t index = 0; index < mChildWorkers.Length(); index++) {
michael@0 5572 mChildWorkers[index]->UpdateGCZeal(aCx, aGCZeal, aFrequency);
michael@0 5573 }
michael@0 5574 }
michael@0 5575 #endif
michael@0 5576
michael@0 5577 void
michael@0 5578 WorkerPrivate::GarbageCollectInternal(JSContext* aCx, bool aShrinking,
michael@0 5579 bool aCollectChildren)
michael@0 5580 {
michael@0 5581 AssertIsOnWorkerThread();
michael@0 5582
michael@0 5583 if (!JS::CurrentGlobalOrNull(aCx)) {
michael@0 5584 // We haven't compiled anything yet. Just bail out.
michael@0 5585 return;
michael@0 5586 }
michael@0 5587
michael@0 5588 if (aShrinking || aCollectChildren) {
michael@0 5589 JSRuntime* rt = JS_GetRuntime(aCx);
michael@0 5590 JS::PrepareForFullGC(rt);
michael@0 5591
michael@0 5592 if (aShrinking) {
michael@0 5593 JS::ShrinkingGC(rt, JS::gcreason::DOM_WORKER);
michael@0 5594
michael@0 5595 if (!aCollectChildren) {
michael@0 5596 LOG(("Worker %p collected idle garbage\n", this));
michael@0 5597 }
michael@0 5598 }
michael@0 5599 else {
michael@0 5600 JS::GCForReason(rt, JS::gcreason::DOM_WORKER);
michael@0 5601 LOG(("Worker %p collected garbage\n", this));
michael@0 5602 }
michael@0 5603 }
michael@0 5604 else {
michael@0 5605 JS_MaybeGC(aCx);
michael@0 5606 LOG(("Worker %p collected periodic garbage\n", this));
michael@0 5607 }
michael@0 5608
michael@0 5609 if (aCollectChildren) {
michael@0 5610 for (uint32_t index = 0; index < mChildWorkers.Length(); index++) {
michael@0 5611 mChildWorkers[index]->GarbageCollect(aCx, aShrinking);
michael@0 5612 }
michael@0 5613 }
michael@0 5614 }
michael@0 5615
michael@0 5616 void
michael@0 5617 WorkerPrivate::CycleCollectInternal(JSContext* aCx, bool aCollectChildren)
michael@0 5618 {
michael@0 5619 AssertIsOnWorkerThread();
michael@0 5620
michael@0 5621 nsCycleCollector_collect(nullptr);
michael@0 5622
michael@0 5623 if (aCollectChildren) {
michael@0 5624 for (uint32_t index = 0; index < mChildWorkers.Length(); index++) {
michael@0 5625 mChildWorkers[index]->CycleCollect(aCx, /* dummy = */ false);
michael@0 5626 }
michael@0 5627 }
michael@0 5628 }
michael@0 5629
michael@0 5630 void
michael@0 5631 WorkerPrivate::SetThread(nsIThread* aThread)
michael@0 5632 {
michael@0 5633 #ifdef DEBUG
michael@0 5634 if (aThread) {
michael@0 5635 bool isOnCurrentThread;
michael@0 5636 MOZ_ASSERT(NS_SUCCEEDED(aThread->IsOnCurrentThread(&isOnCurrentThread)));
michael@0 5637 MOZ_ASSERT(isOnCurrentThread);
michael@0 5638
michael@0 5639 MOZ_ASSERT(!mPRThread);
michael@0 5640 mPRThread = PRThreadFromThread(aThread);
michael@0 5641 MOZ_ASSERT(mPRThread);
michael@0 5642 }
michael@0 5643 else {
michael@0 5644 MOZ_ASSERT(mPRThread);
michael@0 5645 }
michael@0 5646 #endif
michael@0 5647
michael@0 5648 nsCOMPtr<nsIThread> doomedThread;
michael@0 5649
michael@0 5650 { // Scope so that |doomedThread| is released without holding the lock.
michael@0 5651 MutexAutoLock lock(mMutex);
michael@0 5652
michael@0 5653 if (aThread) {
michael@0 5654 MOZ_ASSERT(!mThread);
michael@0 5655 MOZ_ASSERT(mStatus == Pending);
michael@0 5656
michael@0 5657 mThread = aThread;
michael@0 5658
michael@0 5659 if (!mPreStartRunnables.IsEmpty()) {
michael@0 5660 for (uint32_t index = 0; index < mPreStartRunnables.Length(); index++) {
michael@0 5661 MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mThread->Dispatch(
michael@0 5662 mPreStartRunnables[index],
michael@0 5663 NS_DISPATCH_NORMAL)));
michael@0 5664 }
michael@0 5665 mPreStartRunnables.Clear();
michael@0 5666 }
michael@0 5667 }
michael@0 5668 else {
michael@0 5669 MOZ_ASSERT(mThread);
michael@0 5670 mThread.swap(doomedThread);
michael@0 5671 }
michael@0 5672 }
michael@0 5673 }
michael@0 5674
michael@0 5675 WorkerCrossThreadDispatcher*
michael@0 5676 WorkerPrivate::GetCrossThreadDispatcher()
michael@0 5677 {
michael@0 5678 MutexAutoLock lock(mMutex);
michael@0 5679
michael@0 5680 if (!mCrossThreadDispatcher && mStatus <= Running) {
michael@0 5681 mCrossThreadDispatcher = new WorkerCrossThreadDispatcher(this);
michael@0 5682 }
michael@0 5683
michael@0 5684 return mCrossThreadDispatcher;
michael@0 5685 }
michael@0 5686
michael@0 5687 void
michael@0 5688 WorkerPrivate::BeginCTypesCall()
michael@0 5689 {
michael@0 5690 AssertIsOnWorkerThread();
michael@0 5691
michael@0 5692 // Don't try to GC while we're blocked in a ctypes call.
michael@0 5693 SetGCTimerMode(NoTimer);
michael@0 5694
michael@0 5695 MutexAutoLock lock(mMutex);
michael@0 5696
michael@0 5697 NS_ASSERTION(!mBlockedForMemoryReporter,
michael@0 5698 "Can't be blocked in more than one place at the same time!");
michael@0 5699
michael@0 5700 // Let the main thread know that the worker is effectively blocked while in
michael@0 5701 // this ctypes call. It isn't technically true (obviously the call could do
michael@0 5702 // non-blocking things), but we're assuming that ctypes can't call back into
michael@0 5703 // JSAPI here and therefore any work the ctypes call does will not alter the
michael@0 5704 // data structures of this JS runtime.
michael@0 5705 mBlockedForMemoryReporter = true;
michael@0 5706
michael@0 5707 // The main thread may be waiting on us so it must be notified.
michael@0 5708 mMemoryReportCondVar.Notify();
michael@0 5709 }
michael@0 5710
michael@0 5711 void
michael@0 5712 WorkerPrivate::EndCTypesCall()
michael@0 5713 {
michael@0 5714 AssertIsOnWorkerThread();
michael@0 5715
michael@0 5716 {
michael@0 5717 MutexAutoLock lock(mMutex);
michael@0 5718
michael@0 5719 NS_ASSERTION(mBlockedForMemoryReporter, "Somehow we got unblocked!");
michael@0 5720
michael@0 5721 // Don't continue until the memory reporter has finished.
michael@0 5722 while (mMemoryReporterRunning) {
michael@0 5723 mMemoryReportCondVar.Wait();
michael@0 5724 }
michael@0 5725
michael@0 5726 // No need to notify the main thread here as it shouldn't be waiting to see
michael@0 5727 // this state.
michael@0 5728 mBlockedForMemoryReporter = false;
michael@0 5729 }
michael@0 5730
michael@0 5731 // Make sure the periodic timer is running before we start running JS again.
michael@0 5732 SetGCTimerMode(PeriodicTimer);
michael@0 5733 }
michael@0 5734
michael@0 5735 bool
michael@0 5736 WorkerPrivate::ConnectMessagePort(JSContext* aCx, uint64_t aMessagePortSerial)
michael@0 5737 {
michael@0 5738 AssertIsOnWorkerThread();
michael@0 5739
michael@0 5740 NS_ASSERTION(!mWorkerPorts.GetWeak(aMessagePortSerial),
michael@0 5741 "Already have this port registered!");
michael@0 5742
michael@0 5743 WorkerGlobalScope* globalScope = GlobalScope();
michael@0 5744
michael@0 5745 JS::Rooted<JSObject*> jsGlobal(aCx, globalScope->GetWrapper());
michael@0 5746 MOZ_ASSERT(jsGlobal);
michael@0 5747
michael@0 5748 nsRefPtr<MessagePort> port = new MessagePort(this, aMessagePortSerial);
michael@0 5749
michael@0 5750 GlobalObject globalObject(aCx, jsGlobal);
michael@0 5751 if (globalObject.Failed()) {
michael@0 5752 return false;
michael@0 5753 }
michael@0 5754
michael@0 5755 RootedDictionary<MessageEventInit> init(aCx);
michael@0 5756 init.mBubbles = false;
michael@0 5757 init.mCancelable = false;
michael@0 5758 init.mSource.SetValue().SetAsMessagePort() = port;
michael@0 5759
michael@0 5760 ErrorResult rv;
michael@0 5761
michael@0 5762 nsRefPtr<MessageEvent> event =
michael@0 5763 MessageEvent::Constructor(globalObject, aCx,
michael@0 5764 NS_LITERAL_STRING("connect"), init, rv);
michael@0 5765
michael@0 5766 event->SetTrusted(true);
michael@0 5767
michael@0 5768 nsTArray<nsRefPtr<MessagePortBase>> ports;
michael@0 5769 ports.AppendElement(port);
michael@0 5770
michael@0 5771 nsRefPtr<MessagePortList> portList =
michael@0 5772 new MessagePortList(static_cast<nsIDOMEventTarget*>(globalScope), ports);
michael@0 5773 event->SetPorts(portList);
michael@0 5774
michael@0 5775 mWorkerPorts.Put(aMessagePortSerial, port);
michael@0 5776
michael@0 5777 nsCOMPtr<nsIDOMEvent> domEvent = do_QueryObject(event);
michael@0 5778
michael@0 5779 nsEventStatus dummy = nsEventStatus_eIgnore;
michael@0 5780 globalScope->DispatchDOMEvent(nullptr, domEvent, nullptr, &dummy);
michael@0 5781 return true;
michael@0 5782 }
michael@0 5783
michael@0 5784 void
michael@0 5785 WorkerPrivate::DisconnectMessagePort(uint64_t aMessagePortSerial)
michael@0 5786 {
michael@0 5787 AssertIsOnWorkerThread();
michael@0 5788
michael@0 5789 mWorkerPorts.Remove(aMessagePortSerial);
michael@0 5790 }
michael@0 5791
michael@0 5792 workers::MessagePort*
michael@0 5793 WorkerPrivate::GetMessagePort(uint64_t aMessagePortSerial)
michael@0 5794 {
michael@0 5795 AssertIsOnWorkerThread();
michael@0 5796
michael@0 5797 nsRefPtr<MessagePort> port;
michael@0 5798 if (mWorkerPorts.Get(aMessagePortSerial, getter_AddRefs(port))) {
michael@0 5799 return port;
michael@0 5800 }
michael@0 5801
michael@0 5802 return nullptr;
michael@0 5803 }
michael@0 5804
michael@0 5805 JSObject*
michael@0 5806 WorkerPrivate::CreateGlobalScope(JSContext* aCx)
michael@0 5807 {
michael@0 5808 AssertIsOnWorkerThread();
michael@0 5809
michael@0 5810 nsRefPtr<WorkerGlobalScope> globalScope;
michael@0 5811 if (IsSharedWorker()) {
michael@0 5812 globalScope = new SharedWorkerGlobalScope(this, SharedWorkerName());
michael@0 5813 }
michael@0 5814 else {
michael@0 5815 globalScope = new DedicatedWorkerGlobalScope(this);
michael@0 5816 }
michael@0 5817
michael@0 5818 JS::Rooted<JSObject*> global(aCx, globalScope->WrapGlobalObject(aCx));
michael@0 5819 NS_ENSURE_TRUE(global, nullptr);
michael@0 5820
michael@0 5821 JSAutoCompartment ac(aCx, global);
michael@0 5822
michael@0 5823 if (!RegisterBindings(aCx, global)) {
michael@0 5824 return nullptr;
michael@0 5825 }
michael@0 5826
michael@0 5827 mScope = globalScope.forget();
michael@0 5828
michael@0 5829 JS_FireOnNewGlobalObject(aCx, global);
michael@0 5830
michael@0 5831 return global;
michael@0 5832 }
michael@0 5833
michael@0 5834 #ifdef DEBUG
michael@0 5835
michael@0 5836 void
michael@0 5837 WorkerPrivate::AssertIsOnWorkerThread() const
michael@0 5838 {
michael@0 5839 // This is much more complicated than it needs to be but we can't use mThread
michael@0 5840 // because it must be protected by mMutex and sometimes this method is called
michael@0 5841 // when mMutex is already locked. This method should always work.
michael@0 5842 MOZ_ASSERT(mPRThread,
michael@0 5843 "AssertIsOnWorkerThread() called before a thread was assigned!");
michael@0 5844
michael@0 5845 MOZ_ASSERT(nsThreadManager::get());
michael@0 5846
michael@0 5847 nsCOMPtr<nsIThread> thread;
michael@0 5848 nsresult rv =
michael@0 5849 nsThreadManager::get()->GetThreadFromPRThread(mPRThread,
michael@0 5850 getter_AddRefs(thread));
michael@0 5851 MOZ_ASSERT(NS_SUCCEEDED(rv));
michael@0 5852 MOZ_ASSERT(thread);
michael@0 5853
michael@0 5854 bool current;
michael@0 5855 rv = thread->IsOnCurrentThread(&current);
michael@0 5856 MOZ_ASSERT(NS_SUCCEEDED(rv));
michael@0 5857 MOZ_ASSERT(current, "Wrong thread!");
michael@0 5858 }
michael@0 5859
michael@0 5860 #endif // DEBUG
michael@0 5861
michael@0 5862 NS_IMPL_ISUPPORTS_INHERITED0(ExternalRunnableWrapper, WorkerRunnable)
michael@0 5863
michael@0 5864 template <class Derived>
michael@0 5865 NS_IMPL_ADDREF(WorkerPrivateParent<Derived>::EventTarget)
michael@0 5866
michael@0 5867 template <class Derived>
michael@0 5868 NS_IMPL_RELEASE(WorkerPrivateParent<Derived>::EventTarget)
michael@0 5869
michael@0 5870 template <class Derived>
michael@0 5871 NS_INTERFACE_MAP_BEGIN(WorkerPrivateParent<Derived>::EventTarget)
michael@0 5872 NS_INTERFACE_MAP_ENTRY(nsIEventTarget)
michael@0 5873 NS_INTERFACE_MAP_ENTRY(nsISupports)
michael@0 5874 #ifdef DEBUG
michael@0 5875 // kDEBUGWorkerEventTargetIID is special in that it does not AddRef its
michael@0 5876 // result.
michael@0 5877 if (aIID.Equals(kDEBUGWorkerEventTargetIID)) {
michael@0 5878 *aInstancePtr = this;
michael@0 5879 return NS_OK;
michael@0 5880 }
michael@0 5881 else
michael@0 5882 #endif
michael@0 5883 NS_INTERFACE_MAP_END
michael@0 5884
michael@0 5885 template <class Derived>
michael@0 5886 NS_IMETHODIMP
michael@0 5887 WorkerPrivateParent<Derived>::
michael@0 5888 EventTarget::Dispatch(nsIRunnable* aRunnable, uint32_t aFlags)
michael@0 5889 {
michael@0 5890 // May be called on any thread!
michael@0 5891
michael@0 5892 // Workers only support asynchronous dispatch for now.
michael@0 5893 if (NS_WARN_IF(aFlags != NS_DISPATCH_NORMAL)) {
michael@0 5894 return NS_ERROR_UNEXPECTED;
michael@0 5895 }
michael@0 5896
michael@0 5897 nsRefPtr<WorkerRunnable> workerRunnable;
michael@0 5898
michael@0 5899 MutexAutoLock lock(mMutex);
michael@0 5900
michael@0 5901 if (!mWorkerPrivate) {
michael@0 5902 NS_WARNING("A runnable was posted to a worker that is already shutting "
michael@0 5903 "down!");
michael@0 5904 return NS_ERROR_UNEXPECTED;
michael@0 5905 }
michael@0 5906
michael@0 5907 if (aRunnable) {
michael@0 5908 workerRunnable = mWorkerPrivate->MaybeWrapAsWorkerRunnable(aRunnable);
michael@0 5909 }
michael@0 5910
michael@0 5911 nsresult rv =
michael@0 5912 mWorkerPrivate->DispatchPrivate(workerRunnable, mNestedEventTarget);
michael@0 5913 if (NS_WARN_IF(NS_FAILED(rv))) {
michael@0 5914 return rv;
michael@0 5915 }
michael@0 5916
michael@0 5917 return NS_OK;
michael@0 5918 }
michael@0 5919
michael@0 5920 template <class Derived>
michael@0 5921 NS_IMETHODIMP
michael@0 5922 WorkerPrivateParent<Derived>::
michael@0 5923 EventTarget::IsOnCurrentThread(bool* aIsOnCurrentThread)
michael@0 5924 {
michael@0 5925 // May be called on any thread!
michael@0 5926
michael@0 5927 MOZ_ASSERT(aIsOnCurrentThread);
michael@0 5928
michael@0 5929 MutexAutoLock lock(mMutex);
michael@0 5930
michael@0 5931 if (!mWorkerPrivate) {
michael@0 5932 NS_WARNING("A worker's event target was used after the worker has !");
michael@0 5933 return NS_ERROR_UNEXPECTED;
michael@0 5934 }
michael@0 5935
michael@0 5936 nsresult rv = mWorkerPrivate->IsOnCurrentThread(aIsOnCurrentThread);
michael@0 5937 if (NS_WARN_IF(NS_FAILED(rv))) {
michael@0 5938 return rv;
michael@0 5939 }
michael@0 5940
michael@0 5941 return NS_OK;
michael@0 5942 }
michael@0 5943
michael@0 5944 BEGIN_WORKERS_NAMESPACE
michael@0 5945
michael@0 5946 WorkerCrossThreadDispatcher*
michael@0 5947 GetWorkerCrossThreadDispatcher(JSContext* aCx, JS::Value aWorker)
michael@0 5948 {
michael@0 5949 if (!aWorker.isObject()) {
michael@0 5950 return nullptr;
michael@0 5951 }
michael@0 5952
michael@0 5953 WorkerPrivate* w = nullptr;
michael@0 5954 UNWRAP_OBJECT(Worker, &aWorker.toObject(), w);
michael@0 5955 MOZ_ASSERT(w);
michael@0 5956 return w->GetCrossThreadDispatcher();
michael@0 5957 }
michael@0 5958
michael@0 5959 JSStructuredCloneCallbacks*
michael@0 5960 WorkerStructuredCloneCallbacks(bool aMainRuntime)
michael@0 5961 {
michael@0 5962 return aMainRuntime ?
michael@0 5963 &gMainThreadWorkerStructuredCloneCallbacks :
michael@0 5964 &gWorkerStructuredCloneCallbacks;
michael@0 5965 }
michael@0 5966
michael@0 5967 JSStructuredCloneCallbacks*
michael@0 5968 ChromeWorkerStructuredCloneCallbacks(bool aMainRuntime)
michael@0 5969 {
michael@0 5970 return aMainRuntime ?
michael@0 5971 &gMainThreadChromeWorkerStructuredCloneCallbacks :
michael@0 5972 &gChromeWorkerStructuredCloneCallbacks;
michael@0 5973 }
michael@0 5974
michael@0 5975 // Force instantiation.
michael@0 5976 template class WorkerPrivateParent<WorkerPrivate>;
michael@0 5977
michael@0 5978 END_WORKERS_NAMESPACE

mercurial