dom/workers/WorkerPrivate.cpp

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

mercurial