Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "WorkerPrivate.h"
8 :
9 : #include "amIAddonManager.h"
10 : #include "nsIClassInfo.h"
11 : #include "nsIContentSecurityPolicy.h"
12 : #include "nsIConsoleService.h"
13 : #include "nsIDOMDOMException.h"
14 : #include "nsIDOMEvent.h"
15 : #include "nsIDocument.h"
16 : #include "nsIDocShell.h"
17 : #include "nsIInterfaceRequestor.h"
18 : #include "nsIMemoryReporter.h"
19 : #include "nsINetworkInterceptController.h"
20 : #include "nsIPermissionManager.h"
21 : #include "nsIScriptError.h"
22 : #include "nsIScriptGlobalObject.h"
23 : #include "nsIScriptSecurityManager.h"
24 : #include "nsIScriptTimeoutHandler.h"
25 : #include "nsITabChild.h"
26 : #include "nsITextToSubURI.h"
27 : #include "nsIThreadInternal.h"
28 : #include "nsITimer.h"
29 : #include "nsIURI.h"
30 : #include "nsIURL.h"
31 : #include "nsIWeakReferenceUtils.h"
32 : #include "nsIWorkerDebugger.h"
33 : #include "nsIXPConnect.h"
34 : #include "nsPIDOMWindow.h"
35 : #include "nsGlobalWindow.h"
36 :
37 : #include <algorithm>
38 : #include "ImageContainer.h"
39 : #include "jsfriendapi.h"
40 : #include "js/MemoryMetrics.h"
41 : #include "mozilla/Assertions.h"
42 : #include "mozilla/Attributes.h"
43 : #include "mozilla/ContentEvents.h"
44 : #include "mozilla/EventDispatcher.h"
45 : #include "mozilla/Likely.h"
46 : #include "mozilla/LoadContext.h"
47 : #include "mozilla/Move.h"
48 : #include "mozilla/dom/BindingUtils.h"
49 : #include "mozilla/dom/Console.h"
50 : #include "mozilla/dom/DocGroup.h"
51 : #include "mozilla/dom/ErrorEvent.h"
52 : #include "mozilla/dom/ErrorEventBinding.h"
53 : #include "mozilla/dom/Exceptions.h"
54 : #include "mozilla/dom/ExtendableMessageEventBinding.h"
55 : #include "mozilla/dom/FunctionBinding.h"
56 : #include "mozilla/dom/IndexedDatabaseManager.h"
57 : #include "mozilla/dom/MessageEvent.h"
58 : #include "mozilla/dom/MessageEventBinding.h"
59 : #include "mozilla/dom/MessagePort.h"
60 : #include "mozilla/dom/MessagePortBinding.h"
61 : #include "mozilla/dom/nsCSPUtils.h"
62 : #include "mozilla/dom/Performance.h"
63 : #include "mozilla/dom/PMessagePort.h"
64 : #include "mozilla/dom/Promise.h"
65 : #include "mozilla/dom/PromiseDebugging.h"
66 : #include "mozilla/dom/PromiseNativeHandler.h"
67 : #include "mozilla/dom/SimpleGlobalObject.h"
68 : #include "mozilla/dom/ScriptSettings.h"
69 : #include "mozilla/dom/StructuredCloneHolder.h"
70 : #include "mozilla/dom/TabChild.h"
71 : #include "mozilla/dom/WorkerBinding.h"
72 : #include "mozilla/dom/WorkerDebuggerGlobalScopeBinding.h"
73 : #include "mozilla/dom/WorkerGlobalScopeBinding.h"
74 : #include "mozilla/Preferences.h"
75 : #include "mozilla/SizePrintfMacros.h"
76 : #include "mozilla/ThrottledEventQueue.h"
77 : #include "mozilla/TimelineConsumers.h"
78 : #include "mozilla/WorkerTimelineMarker.h"
79 : #include "nsAlgorithm.h"
80 : #include "nsContentUtils.h"
81 : #include "nsCycleCollector.h"
82 : #include "nsError.h"
83 : #include "nsDOMJSUtils.h"
84 : #include "nsHostObjectProtocolHandler.h"
85 : #include "nsJSEnvironment.h"
86 : #include "nsJSUtils.h"
87 : #include "nsNetUtil.h"
88 : #include "nsPrintfCString.h"
89 : #include "nsProxyRelease.h"
90 : #include "nsQueryObject.h"
91 : #include "nsSandboxFlags.h"
92 : #include "nsScriptError.h"
93 : #include "nsUTF8Utils.h"
94 : #include "prthread.h"
95 : #include "xpcpublic.h"
96 :
97 : #ifdef ANDROID
98 : #include <android/log.h>
99 : #endif
100 :
101 : #ifdef DEBUG
102 : #include "nsThreadManager.h"
103 : #endif
104 :
105 : #include "Navigator.h"
106 : #include "Principal.h"
107 : #include "RuntimeService.h"
108 : #include "ScriptLoader.h"
109 : #include "ServiceWorkerEvents.h"
110 : #include "ServiceWorkerManager.h"
111 : #include "SharedWorker.h"
112 : #include "WorkerDebuggerManager.h"
113 : #include "WorkerHolder.h"
114 : #include "WorkerNavigator.h"
115 : #include "WorkerRunnable.h"
116 : #include "WorkerScope.h"
117 : #include "WorkerThread.h"
118 :
119 : // JS_MaybeGC will run once every second during normal execution.
120 : #define PERIODIC_GC_TIMER_DELAY_SEC 1
121 :
122 : // A shrinking GC will run five seconds after the last event is processed.
123 : #define IDLE_GC_TIMER_DELAY_SEC 5
124 :
125 : #define PREF_WORKERS_ENABLED "dom.workers.enabled"
126 :
127 : static mozilla::LazyLogModule sWorkerPrivateLog("WorkerPrivate");
128 : static mozilla::LazyLogModule sWorkerTimeoutsLog("WorkerTimeouts");
129 :
130 : mozilla::LogModule*
131 104 : WorkerLog()
132 : {
133 104 : return sWorkerPrivateLog;
134 : }
135 :
136 : mozilla::LogModule*
137 0 : TimeoutsLog()
138 : {
139 0 : return sWorkerTimeoutsLog;
140 : }
141 :
142 : #define LOG(log, _args) MOZ_LOG(log, LogLevel::Debug, _args);
143 :
144 : using namespace mozilla;
145 : using namespace mozilla::dom;
146 : using namespace mozilla::ipc;
147 :
148 : USING_WORKERS_NAMESPACE
149 :
150 0 : MOZ_DEFINE_MALLOC_SIZE_OF(JsWorkerMallocSizeOf)
151 :
152 : #ifdef DEBUG
153 :
154 : BEGIN_WORKERS_NAMESPACE
155 :
156 : void
157 555 : AssertIsOnMainThread()
158 : {
159 555 : MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
160 555 : }
161 :
162 : END_WORKERS_NAMESPACE
163 :
164 : #endif
165 :
166 : namespace {
167 :
168 : #ifdef DEBUG
169 :
170 : const nsIID kDEBUGWorkerEventTargetIID = {
171 : 0xccaba3fa, 0x5be2, 0x4de2, { 0xba, 0x87, 0x3b, 0x3b, 0x5b, 0x1d, 0x5, 0xfb }
172 : };
173 :
174 : #endif
175 :
176 : template <class T>
177 : class AutoPtrComparator
178 : {
179 : typedef nsAutoPtr<T> A;
180 : typedef T* B;
181 :
182 : public:
183 0 : bool Equals(const A& a, const B& b) const {
184 0 : return a && b ? *a == *b : !a && !b ? true : false;
185 : }
186 0 : bool LessThan(const A& a, const B& b) const {
187 0 : return a && b ? *a < *b : b ? true : false;
188 : }
189 : };
190 :
191 : template <class T>
192 : inline AutoPtrComparator<T>
193 0 : GetAutoPtrComparator(const nsTArray<nsAutoPtr<T> >&)
194 : {
195 0 : return AutoPtrComparator<T>();
196 : }
197 :
198 : // Specialize this if there's some class that has multiple nsISupports bases.
199 : template <class T>
200 : struct ISupportsBaseInfo
201 : {
202 : typedef T ISupportsBase;
203 : };
204 :
205 : template <template <class> class SmartPtr, class T>
206 : inline void
207 0 : SwapToISupportsArray(SmartPtr<T>& aSrc,
208 : nsTArray<nsCOMPtr<nsISupports> >& aDest)
209 : {
210 0 : nsCOMPtr<nsISupports>* dest = aDest.AppendElement();
211 :
212 0 : T* raw = nullptr;
213 0 : aSrc.swap(raw);
214 :
215 : nsISupports* rawSupports =
216 0 : static_cast<typename ISupportsBaseInfo<T>::ISupportsBase*>(raw);
217 0 : dest->swap(rawSupports);
218 0 : }
219 :
220 : // This class is used to wrap any runnables that the worker receives via the
221 : // nsIEventTarget::Dispatch() method (either from NS_DispatchToCurrentThread or
222 : // from the worker's EventTarget).
223 : class ExternalRunnableWrapper final : public WorkerRunnable
224 : {
225 : nsCOMPtr<nsIRunnable> mWrappedRunnable;
226 :
227 : public:
228 0 : ExternalRunnableWrapper(WorkerPrivate* aWorkerPrivate,
229 : nsIRunnable* aWrappedRunnable)
230 0 : : WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
231 0 : mWrappedRunnable(aWrappedRunnable)
232 : {
233 0 : MOZ_ASSERT(aWorkerPrivate);
234 0 : MOZ_ASSERT(aWrappedRunnable);
235 0 : }
236 :
237 : NS_DECL_ISUPPORTS_INHERITED
238 :
239 : private:
240 0 : ~ExternalRunnableWrapper()
241 0 : { }
242 :
243 : virtual bool
244 0 : WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
245 : {
246 0 : nsresult rv = mWrappedRunnable->Run();
247 0 : if (NS_FAILED(rv)) {
248 0 : if (!JS_IsExceptionPending(aCx)) {
249 0 : Throw(aCx, rv);
250 : }
251 0 : return false;
252 : }
253 0 : return true;
254 : }
255 :
256 : nsresult
257 0 : Cancel() override
258 : {
259 : nsresult rv;
260 : nsCOMPtr<nsICancelableRunnable> cancelable =
261 0 : do_QueryInterface(mWrappedRunnable);
262 0 : MOZ_ASSERT(cancelable); // We checked this earlier!
263 0 : rv = cancelable->Cancel();
264 0 : nsresult rv2 = WorkerRunnable::Cancel();
265 0 : return NS_FAILED(rv) ? rv : rv2;
266 : }
267 : };
268 :
269 : struct WindowAction
270 : {
271 : nsPIDOMWindowInner* mWindow;
272 : bool mDefaultAction;
273 :
274 0 : MOZ_IMPLICIT WindowAction(nsPIDOMWindowInner* aWindow)
275 0 : : mWindow(aWindow), mDefaultAction(true)
276 0 : { }
277 :
278 : bool
279 0 : operator==(const WindowAction& aOther) const
280 : {
281 0 : return mWindow == aOther.mWindow;
282 : }
283 : };
284 :
285 : void
286 0 : LogErrorToConsole(const WorkerErrorReport& aReport, uint64_t aInnerWindowId)
287 : {
288 0 : AssertIsOnMainThread();
289 :
290 0 : RefPtr<nsScriptErrorBase> scriptError = new nsScriptError();
291 0 : NS_WARNING_ASSERTION(scriptError, "Failed to create script error!");
292 :
293 0 : if (scriptError) {
294 0 : nsAutoCString category("Web Worker");
295 0 : if (NS_FAILED(scriptError->InitWithWindowID(aReport.mMessage,
296 : aReport.mFilename,
297 : aReport.mLine,
298 : aReport.mLineNumber,
299 : aReport.mColumnNumber,
300 : aReport.mFlags,
301 : category,
302 : aInnerWindowId))) {
303 0 : NS_WARNING("Failed to init script error!");
304 0 : scriptError = nullptr;
305 : }
306 :
307 0 : for (size_t i = 0, len = aReport.mNotes.Length(); i < len; i++) {
308 0 : const WorkerErrorNote& note = aReport.mNotes.ElementAt(i);
309 :
310 0 : nsScriptErrorNote* noteObject = new nsScriptErrorNote();
311 0 : noteObject->Init(note.mMessage, note.mFilename,
312 0 : note.mLineNumber, note.mColumnNumber);
313 0 : scriptError->AddNote(noteObject);
314 : }
315 : }
316 :
317 : nsCOMPtr<nsIConsoleService> consoleService =
318 0 : do_GetService(NS_CONSOLESERVICE_CONTRACTID);
319 0 : NS_WARNING_ASSERTION(consoleService, "Failed to get console service!");
320 :
321 0 : if (consoleService) {
322 0 : if (scriptError) {
323 0 : if (NS_SUCCEEDED(consoleService->LogMessage(scriptError))) {
324 0 : return;
325 : }
326 0 : NS_WARNING("LogMessage failed!");
327 0 : } else if (NS_SUCCEEDED(consoleService->LogStringMessage(
328 : aReport.mMessage.BeginReading()))) {
329 0 : return;
330 : }
331 0 : NS_WARNING("LogStringMessage failed!");
332 : }
333 :
334 0 : NS_ConvertUTF16toUTF8 msg(aReport.mMessage);
335 0 : NS_ConvertUTF16toUTF8 filename(aReport.mFilename);
336 :
337 : static const char kErrorString[] = "JS error in Web Worker: %s [%s:%u]";
338 :
339 : #ifdef ANDROID
340 : __android_log_print(ANDROID_LOG_INFO, "Gecko", kErrorString, msg.get(),
341 : filename.get(), aReport.mLineNumber);
342 : #endif
343 :
344 0 : fprintf(stderr, kErrorString, msg.get(), filename.get(), aReport.mLineNumber);
345 0 : fflush(stderr);
346 : }
347 :
348 : class MainThreadReleaseRunnable final : public Runnable
349 : {
350 : nsTArray<nsCOMPtr<nsISupports>> mDoomed;
351 : nsCOMPtr<nsILoadGroup> mLoadGroupToCancel;
352 :
353 : public:
354 0 : MainThreadReleaseRunnable(nsTArray<nsCOMPtr<nsISupports>>& aDoomed,
355 : nsCOMPtr<nsILoadGroup>& aLoadGroupToCancel)
356 0 : : mozilla::Runnable("MainThreadReleaseRunnable")
357 : {
358 0 : mDoomed.SwapElements(aDoomed);
359 0 : mLoadGroupToCancel.swap(aLoadGroupToCancel);
360 0 : }
361 :
362 : NS_DECL_ISUPPORTS_INHERITED
363 :
364 : NS_IMETHOD
365 0 : Run() override
366 : {
367 0 : if (mLoadGroupToCancel) {
368 0 : mLoadGroupToCancel->Cancel(NS_BINDING_ABORTED);
369 0 : mLoadGroupToCancel = nullptr;
370 : }
371 :
372 0 : mDoomed.Clear();
373 0 : return NS_OK;
374 : }
375 :
376 : private:
377 0 : ~MainThreadReleaseRunnable()
378 0 : { }
379 : };
380 :
381 0 : class WorkerFinishedRunnable final : public WorkerControlRunnable
382 : {
383 : WorkerPrivate* mFinishedWorker;
384 :
385 : public:
386 0 : WorkerFinishedRunnable(WorkerPrivate* aWorkerPrivate,
387 : WorkerPrivate* aFinishedWorker)
388 0 : : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
389 0 : mFinishedWorker(aFinishedWorker)
390 0 : { }
391 :
392 : private:
393 : virtual bool
394 0 : PreDispatch(WorkerPrivate* aWorkerPrivate) override
395 : {
396 : // Silence bad assertions.
397 0 : return true;
398 : }
399 :
400 : virtual void
401 0 : PostDispatch(WorkerPrivate* aWorkerPrivate, bool aDispatchResult) override
402 : {
403 : // Silence bad assertions.
404 0 : }
405 :
406 : virtual bool
407 0 : WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
408 : {
409 0 : if (!mFinishedWorker->ProxyReleaseMainThreadObjects()) {
410 0 : NS_WARNING("Failed to dispatch, going to leak!");
411 : }
412 :
413 0 : RuntimeService* runtime = RuntimeService::GetService();
414 0 : NS_ASSERTION(runtime, "This should never be null!");
415 :
416 0 : mFinishedWorker->DisableDebugger();
417 :
418 0 : runtime->UnregisterWorker(mFinishedWorker);
419 :
420 0 : mFinishedWorker->ClearSelfRef();
421 0 : return true;
422 : }
423 : };
424 :
425 : class TopLevelWorkerFinishedRunnable final : public Runnable
426 : {
427 : WorkerPrivate* mFinishedWorker;
428 :
429 : public:
430 0 : explicit TopLevelWorkerFinishedRunnable(WorkerPrivate* aFinishedWorker)
431 0 : : mozilla::Runnable("TopLevelWorkerFinishedRunnable")
432 0 : , mFinishedWorker(aFinishedWorker)
433 : {
434 0 : aFinishedWorker->AssertIsOnWorkerThread();
435 0 : }
436 :
437 : NS_DECL_ISUPPORTS_INHERITED
438 :
439 : private:
440 0 : ~TopLevelWorkerFinishedRunnable() {}
441 :
442 : NS_IMETHOD
443 0 : Run() override
444 : {
445 0 : AssertIsOnMainThread();
446 :
447 0 : RuntimeService* runtime = RuntimeService::GetService();
448 0 : MOZ_ASSERT(runtime);
449 :
450 0 : mFinishedWorker->DisableDebugger();
451 :
452 0 : runtime->UnregisterWorker(mFinishedWorker);
453 :
454 0 : if (!mFinishedWorker->ProxyReleaseMainThreadObjects()) {
455 0 : NS_WARNING("Failed to dispatch, going to leak!");
456 : }
457 :
458 0 : mFinishedWorker->ClearSelfRef();
459 0 : return NS_OK;
460 : }
461 : };
462 :
463 3 : class ModifyBusyCountRunnable final : public WorkerControlRunnable
464 : {
465 : bool mIncrease;
466 :
467 : public:
468 1 : ModifyBusyCountRunnable(WorkerPrivate* aWorkerPrivate, bool aIncrease)
469 1 : : WorkerControlRunnable(aWorkerPrivate, ParentThreadUnchangedBusyCount),
470 1 : mIncrease(aIncrease)
471 1 : { }
472 :
473 : private:
474 : virtual bool
475 1 : WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
476 : {
477 1 : return aWorkerPrivate->ModifyBusyCount(mIncrease);
478 : }
479 :
480 : virtual void
481 1 : PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate, bool aRunResult)
482 : override
483 : {
484 1 : if (mIncrease) {
485 1 : WorkerControlRunnable::PostRun(aCx, aWorkerPrivate, aRunResult);
486 1 : return;
487 : }
488 : // Don't do anything here as it's possible that aWorkerPrivate has been
489 : // deleted.
490 : }
491 : };
492 :
493 0 : class ReportCompileErrorRunnable final : public WorkerRunnable
494 : {
495 : public:
496 : static void
497 0 : CreateAndDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
498 : {
499 0 : MOZ_ASSERT(aWorkerPrivate);
500 0 : aWorkerPrivate->AssertIsOnWorkerThread();
501 :
502 : RefPtr<ReportCompileErrorRunnable> runnable =
503 0 : new ReportCompileErrorRunnable(aCx, aWorkerPrivate);
504 0 : runnable->Dispatch();
505 0 : }
506 :
507 : private:
508 0 : ReportCompileErrorRunnable(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
509 0 : : WorkerRunnable(aWorkerPrivate, ParentThreadUnchangedBusyCount)
510 : {
511 0 : aWorkerPrivate->AssertIsOnWorkerThread();
512 0 : }
513 :
514 : void
515 0 : PostDispatch(WorkerPrivate* aWorkerPrivate, bool aDispatchResult) override
516 : {
517 0 : aWorkerPrivate->AssertIsOnWorkerThread();
518 :
519 : // Dispatch may fail if the worker was canceled, no need to report that as
520 : // an error, so don't call base class PostDispatch.
521 0 : }
522 :
523 : bool
524 0 : WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
525 : {
526 0 : if (aWorkerPrivate->IsFrozen() ||
527 0 : aWorkerPrivate->IsParentWindowPaused()) {
528 0 : MOZ_ASSERT(!IsDebuggerRunnable());
529 0 : aWorkerPrivate->QueueRunnable(this);
530 0 : return true;
531 : }
532 :
533 0 : if (aWorkerPrivate->IsSharedWorker()) {
534 0 : aWorkerPrivate->BroadcastErrorToSharedWorkers(aCx, nullptr,
535 0 : /* isErrorEvent */ false);
536 0 : return true;
537 : }
538 :
539 0 : if (aWorkerPrivate->IsServiceWorker()) {
540 0 : RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
541 0 : if (swm) {
542 0 : swm->HandleError(aCx, aWorkerPrivate->GetPrincipal(),
543 : aWorkerPrivate->ServiceWorkerScope(),
544 : aWorkerPrivate->ScriptURL(),
545 : EmptyString(), EmptyString(), EmptyString(),
546 0 : 0, 0, JSREPORT_ERROR, JSEXN_ERR);
547 : }
548 0 : return true;
549 : }
550 :
551 0 : if (!aWorkerPrivate->IsAcceptingEvents()) {
552 0 : return true;
553 : }
554 :
555 : RefPtr<Event> event =
556 0 : Event::Constructor(aWorkerPrivate, NS_LITERAL_STRING("error"),
557 0 : EventInit());
558 0 : event->SetTrusted(true);
559 :
560 0 : nsEventStatus status = nsEventStatus_eIgnore;
561 0 : aWorkerPrivate->DispatchDOMEvent(nullptr, event, nullptr, &status);
562 0 : return true;
563 : }
564 : };
565 :
566 0 : class CompileScriptRunnable final : public WorkerRunnable
567 : {
568 : nsString mScriptURL;
569 :
570 : public:
571 1 : explicit CompileScriptRunnable(WorkerPrivate* aWorkerPrivate,
572 : const nsAString& aScriptURL)
573 1 : : WorkerRunnable(aWorkerPrivate),
574 1 : mScriptURL(aScriptURL)
575 1 : { }
576 :
577 : private:
578 : // We can't implement PreRun effectively, because at the point when that would
579 : // run we have not yet done our load so don't know things like our final
580 : // principal and whatnot.
581 :
582 : virtual bool
583 1 : WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
584 : {
585 1 : aWorkerPrivate->AssertIsOnWorkerThread();
586 :
587 1 : ErrorResult rv;
588 1 : scriptloader::LoadMainScript(aWorkerPrivate, mScriptURL, WorkerScript, rv);
589 0 : rv.WouldReportJSException();
590 : // Explicitly ignore NS_BINDING_ABORTED on rv. Or more precisely, still
591 : // return false and don't SetWorkerScriptExecutedSuccessfully() in that
592 : // case, but don't throw anything on aCx. The idea is to not dispatch error
593 : // events if our load is canceled with that error code.
594 0 : if (rv.ErrorCodeIs(NS_BINDING_ABORTED)) {
595 0 : rv.SuppressException();
596 0 : return false;
597 : }
598 :
599 0 : WorkerGlobalScope* globalScope = aWorkerPrivate->GlobalScope();
600 0 : if (NS_WARN_IF(!globalScope)) {
601 : // We never got as far as calling GetOrCreateGlobalScope, or it failed.
602 : // We have no way to enter a compartment, hence no sane way to report this
603 : // error. :(
604 0 : rv.SuppressException();
605 0 : return false;
606 : }
607 :
608 : // Make sure to propagate exceptions from rv onto aCx, so that they will get
609 : // reported after we return. We want to propagate just JS exceptions,
610 : // because all the other errors are handled when the script is loaded.
611 : // See: https://dom.spec.whatwg.org/#concept-event-fire
612 0 : if (rv.Failed() && !rv.IsJSException()) {
613 0 : ReportCompileErrorRunnable::CreateAndDispatch(aCx, aWorkerPrivate);
614 0 : rv.SuppressException();
615 0 : return false;
616 : }
617 :
618 : // This is a little dumb, but aCx is in the null compartment here because we
619 : // set it up that way in our Run(), since we had not created the global at
620 : // that point yet. So we need to enter the compartment of our global,
621 : // because setting a pending exception on aCx involves wrapping into its
622 : // current compartment. Luckily we have a global now.
623 0 : JSAutoCompartment ac(aCx, globalScope->GetGlobalJSObject());
624 0 : if (rv.MaybeSetPendingException(aCx)) {
625 0 : return false;
626 : }
627 :
628 0 : aWorkerPrivate->SetWorkerScriptExecutedSuccessfully();
629 0 : return true;
630 : }
631 : };
632 :
633 0 : class CompileDebuggerScriptRunnable final : public WorkerDebuggerRunnable
634 : {
635 : nsString mScriptURL;
636 :
637 : public:
638 0 : CompileDebuggerScriptRunnable(WorkerPrivate* aWorkerPrivate,
639 : const nsAString& aScriptURL)
640 0 : : WorkerDebuggerRunnable(aWorkerPrivate),
641 0 : mScriptURL(aScriptURL)
642 0 : { }
643 :
644 : private:
645 : virtual bool
646 0 : WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
647 : {
648 0 : aWorkerPrivate->AssertIsOnWorkerThread();
649 :
650 : WorkerDebuggerGlobalScope* globalScope =
651 0 : aWorkerPrivate->CreateDebuggerGlobalScope(aCx);
652 0 : if (!globalScope) {
653 0 : NS_WARNING("Failed to make global!");
654 0 : return false;
655 : }
656 :
657 0 : JS::Rooted<JSObject*> global(aCx, globalScope->GetWrapper());
658 :
659 0 : ErrorResult rv;
660 0 : JSAutoCompartment ac(aCx, global);
661 0 : scriptloader::LoadMainScript(aWorkerPrivate, mScriptURL,
662 0 : DebuggerScript, rv);
663 0 : rv.WouldReportJSException();
664 : // Explicitly ignore NS_BINDING_ABORTED on rv. Or more precisely, still
665 : // return false and don't SetWorkerScriptExecutedSuccessfully() in that
666 : // case, but don't throw anything on aCx. The idea is to not dispatch error
667 : // events if our load is canceled with that error code.
668 0 : if (rv.ErrorCodeIs(NS_BINDING_ABORTED)) {
669 0 : rv.SuppressException();
670 0 : return false;
671 : }
672 : // Make sure to propagate exceptions from rv onto aCx, so that they will get
673 : // reported after we return. We do this for all failures on rv, because now
674 : // we're using rv to track all the state we care about.
675 0 : if (rv.MaybeSetPendingException(aCx)) {
676 0 : return false;
677 : }
678 :
679 0 : return true;
680 : }
681 : };
682 :
683 0 : class MessageEventRunnable final : public WorkerRunnable
684 : , public StructuredCloneHolder
685 : {
686 : public:
687 1 : MessageEventRunnable(WorkerPrivate* aWorkerPrivate,
688 : TargetAndBusyBehavior aBehavior)
689 1 : : WorkerRunnable(aWorkerPrivate, aBehavior)
690 : , StructuredCloneHolder(CloningSupported, TransferringSupported,
691 1 : StructuredCloneScope::SameProcessDifferentThread)
692 : {
693 1 : }
694 :
695 : bool
696 0 : DispatchDOMEvent(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
697 : DOMEventTargetHelper* aTarget, bool aIsMainThread)
698 : {
699 0 : nsCOMPtr<nsIGlobalObject> parent = do_QueryInterface(aTarget->GetParentObject());
700 :
701 : // For some workers without window, parent is null and we try to find it
702 : // from the JS Context.
703 0 : if (!parent) {
704 0 : JS::Rooted<JSObject*> globalObject(aCx, JS::CurrentGlobalOrNull(aCx));
705 0 : if (NS_WARN_IF(!globalObject)) {
706 0 : return false;
707 : }
708 :
709 0 : parent = xpc::NativeGlobal(globalObject);
710 0 : if (NS_WARN_IF(!parent)) {
711 0 : return false;
712 : }
713 : }
714 :
715 0 : MOZ_ASSERT(parent);
716 :
717 0 : JS::Rooted<JS::Value> messageData(aCx);
718 0 : ErrorResult rv;
719 :
720 0 : UniquePtr<AbstractTimelineMarker> start;
721 0 : UniquePtr<AbstractTimelineMarker> end;
722 0 : RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
723 0 : bool isTimelineRecording = timelines && !timelines->IsEmpty();
724 :
725 0 : if (isTimelineRecording) {
726 0 : start = MakeUnique<WorkerTimelineMarker>(aIsMainThread
727 0 : ? ProfileTimelineWorkerOperationType::DeserializeDataOnMainThread
728 : : ProfileTimelineWorkerOperationType::DeserializeDataOffMainThread,
729 0 : MarkerTracingType::START);
730 : }
731 :
732 0 : Read(parent, aCx, &messageData, rv);
733 :
734 0 : if (isTimelineRecording) {
735 0 : end = MakeUnique<WorkerTimelineMarker>(aIsMainThread
736 0 : ? ProfileTimelineWorkerOperationType::DeserializeDataOnMainThread
737 : : ProfileTimelineWorkerOperationType::DeserializeDataOffMainThread,
738 0 : MarkerTracingType::END);
739 0 : timelines->AddMarkerForAllObservedDocShells(start);
740 0 : timelines->AddMarkerForAllObservedDocShells(end);
741 : }
742 :
743 0 : if (NS_WARN_IF(rv.Failed())) {
744 0 : xpc::Throw(aCx, rv.StealNSResult());
745 0 : return false;
746 : }
747 :
748 0 : Sequence<OwningNonNull<MessagePort>> ports;
749 0 : if (!TakeTransferredPortsAsSequence(ports)) {
750 0 : return false;
751 : }
752 :
753 0 : nsCOMPtr<nsIDOMEvent> domEvent;
754 0 : RefPtr<MessageEvent> event = new MessageEvent(aTarget, nullptr, nullptr);
755 0 : event->InitMessageEvent(nullptr,
756 0 : NS_LITERAL_STRING("message"),
757 : false /* non-bubbling */,
758 : false /* cancelable */,
759 : messageData,
760 0 : EmptyString(),
761 0 : EmptyString(),
762 : nullptr,
763 0 : ports);
764 0 : domEvent = do_QueryObject(event);
765 :
766 0 : domEvent->SetTrusted(true);
767 :
768 0 : nsEventStatus dummy = nsEventStatus_eIgnore;
769 0 : aTarget->DispatchDOMEvent(nullptr, domEvent, nullptr, &dummy);
770 :
771 0 : return true;
772 : }
773 :
774 : private:
775 : virtual bool
776 0 : WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
777 : {
778 0 : if (mBehavior == ParentThreadUnchangedBusyCount) {
779 : // Don't fire this event if the JS object has been disconnected from the
780 : // private object.
781 0 : if (!aWorkerPrivate->IsAcceptingEvents()) {
782 0 : return true;
783 : }
784 :
785 0 : if (aWorkerPrivate->IsFrozen() ||
786 0 : aWorkerPrivate->IsParentWindowPaused()) {
787 0 : MOZ_ASSERT(!IsDebuggerRunnable());
788 0 : aWorkerPrivate->QueueRunnable(this);
789 0 : return true;
790 : }
791 :
792 0 : aWorkerPrivate->AssertInnerWindowIsCorrect();
793 :
794 0 : return DispatchDOMEvent(aCx, aWorkerPrivate, aWorkerPrivate,
795 0 : !aWorkerPrivate->GetParent());
796 : }
797 :
798 0 : MOZ_ASSERT(aWorkerPrivate == GetWorkerPrivateFromContext(aCx));
799 :
800 0 : return DispatchDOMEvent(aCx, aWorkerPrivate, aWorkerPrivate->GlobalScope(),
801 0 : false);
802 : }
803 : };
804 :
805 0 : class DebuggerMessageEventRunnable : public WorkerDebuggerRunnable {
806 : nsString mMessage;
807 :
808 : public:
809 0 : DebuggerMessageEventRunnable(WorkerPrivate* aWorkerPrivate,
810 : const nsAString& aMessage)
811 0 : : WorkerDebuggerRunnable(aWorkerPrivate),
812 0 : mMessage(aMessage)
813 : {
814 0 : }
815 :
816 : private:
817 : virtual bool
818 0 : WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
819 : {
820 0 : WorkerDebuggerGlobalScope* globalScope = aWorkerPrivate->DebuggerGlobalScope();
821 0 : MOZ_ASSERT(globalScope);
822 :
823 0 : JS::Rooted<JSString*> message(aCx, JS_NewUCStringCopyN(aCx, mMessage.get(),
824 0 : mMessage.Length()));
825 0 : if (!message) {
826 0 : return false;
827 : }
828 0 : JS::Rooted<JS::Value> data(aCx, JS::StringValue(message));
829 :
830 : RefPtr<MessageEvent> event = new MessageEvent(globalScope, nullptr,
831 0 : nullptr);
832 0 : event->InitMessageEvent(nullptr,
833 0 : NS_LITERAL_STRING("message"),
834 : false, // canBubble
835 : true, // cancelable
836 : data,
837 0 : EmptyString(),
838 0 : EmptyString(),
839 : nullptr,
840 0 : Sequence<OwningNonNull<MessagePort>>());
841 0 : event->SetTrusted(true);
842 :
843 0 : nsCOMPtr<nsIDOMEvent> domEvent = do_QueryObject(event);
844 0 : nsEventStatus status = nsEventStatus_eIgnore;
845 0 : globalScope->DispatchDOMEvent(nullptr, domEvent, nullptr, &status);
846 0 : return true;
847 : }
848 : };
849 :
850 0 : class NotifyRunnable final : public WorkerControlRunnable
851 : {
852 : Status mStatus;
853 :
854 : public:
855 0 : NotifyRunnable(WorkerPrivate* aWorkerPrivate, Status aStatus)
856 0 : : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
857 0 : mStatus(aStatus)
858 : {
859 0 : MOZ_ASSERT(aStatus == Closing || aStatus == Terminating ||
860 : aStatus == Canceling || aStatus == Killing);
861 0 : }
862 :
863 : private:
864 : virtual bool
865 0 : PreDispatch(WorkerPrivate* aWorkerPrivate) override
866 : {
867 0 : aWorkerPrivate->AssertIsOnParentThread();
868 0 : return aWorkerPrivate->ModifyBusyCount(true);
869 : }
870 :
871 : virtual void
872 0 : PostDispatch(WorkerPrivate* aWorkerPrivate, bool aDispatchResult) override
873 : {
874 0 : aWorkerPrivate->AssertIsOnParentThread();
875 0 : if (!aDispatchResult) {
876 : // We couldn't dispatch to the worker, which means it's already dead.
877 : // Undo the busy count modification.
878 0 : aWorkerPrivate->ModifyBusyCount(false);
879 : }
880 0 : }
881 :
882 : virtual void
883 0 : PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate, bool aRunResult)
884 : override
885 : {
886 0 : aWorkerPrivate->ModifyBusyCountFromWorker(false);
887 0 : return;
888 : }
889 :
890 : virtual bool
891 0 : WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
892 : {
893 0 : bool ok = aWorkerPrivate->NotifyInternal(aCx, mStatus);
894 0 : MOZ_ASSERT(!JS_IsExceptionPending(aCx));
895 0 : return ok;
896 : }
897 : };
898 :
899 0 : class FreezeRunnable final : public WorkerControlRunnable
900 : {
901 : public:
902 0 : explicit FreezeRunnable(WorkerPrivate* aWorkerPrivate)
903 0 : : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
904 0 : { }
905 :
906 : private:
907 : virtual bool
908 0 : WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
909 : {
910 0 : return aWorkerPrivate->FreezeInternal();
911 : }
912 : };
913 :
914 0 : class ThawRunnable final : public WorkerControlRunnable
915 : {
916 : public:
917 0 : explicit ThawRunnable(WorkerPrivate* aWorkerPrivate)
918 0 : : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
919 0 : { }
920 :
921 : private:
922 : virtual bool
923 0 : WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
924 : {
925 0 : return aWorkerPrivate->ThawInternal();
926 : }
927 : };
928 :
929 0 : class ReportErrorToConsoleRunnable final : public WorkerRunnable
930 : {
931 : const char* mMessage;
932 :
933 : public:
934 : // aWorkerPrivate is the worker thread we're on (or the main thread, if null)
935 : static void
936 0 : Report(WorkerPrivate* aWorkerPrivate, const char* aMessage)
937 : {
938 0 : if (aWorkerPrivate) {
939 0 : aWorkerPrivate->AssertIsOnWorkerThread();
940 : } else {
941 0 : AssertIsOnMainThread();
942 : }
943 :
944 : // Now fire a runnable to do the same on the parent's thread if we can.
945 0 : if (aWorkerPrivate) {
946 : RefPtr<ReportErrorToConsoleRunnable> runnable =
947 0 : new ReportErrorToConsoleRunnable(aWorkerPrivate, aMessage);
948 0 : runnable->Dispatch();
949 0 : return;
950 : }
951 :
952 : // Log a warning to the console.
953 0 : nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
954 0 : NS_LITERAL_CSTRING("DOM"),
955 : nullptr,
956 : nsContentUtils::eDOM_PROPERTIES,
957 0 : aMessage);
958 : }
959 :
960 : private:
961 0 : ReportErrorToConsoleRunnable(WorkerPrivate* aWorkerPrivate, const char* aMessage)
962 0 : : WorkerRunnable(aWorkerPrivate, ParentThreadUnchangedBusyCount),
963 0 : mMessage(aMessage)
964 0 : { }
965 :
966 : virtual void
967 0 : PostDispatch(WorkerPrivate* aWorkerPrivate, bool aDispatchResult) override
968 : {
969 0 : aWorkerPrivate->AssertIsOnWorkerThread();
970 :
971 : // Dispatch may fail if the worker was canceled, no need to report that as
972 : // an error, so don't call base class PostDispatch.
973 0 : }
974 :
975 : virtual bool
976 0 : WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
977 : {
978 0 : WorkerPrivate* parent = aWorkerPrivate->GetParent();
979 0 : MOZ_ASSERT_IF(!parent, NS_IsMainThread());
980 0 : Report(parent, mMessage);
981 0 : return true;
982 : }
983 : };
984 :
985 0 : class ReportErrorRunnable final : public WorkerRunnable
986 : {
987 : WorkerErrorReport mReport;
988 :
989 : public:
990 : // aWorkerPrivate is the worker thread we're on (or the main thread, if null)
991 : // aTarget is the worker object that we are going to fire an error at
992 : // (if any).
993 : static void
994 0 : ReportError(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
995 : bool aFireAtScope, WorkerPrivate* aTarget,
996 : const WorkerErrorReport& aReport, uint64_t aInnerWindowId,
997 : JS::Handle<JS::Value> aException = JS::NullHandleValue)
998 : {
999 0 : if (aWorkerPrivate) {
1000 0 : aWorkerPrivate->AssertIsOnWorkerThread();
1001 : } else {
1002 0 : AssertIsOnMainThread();
1003 : }
1004 :
1005 : // We should not fire error events for warnings but instead make sure that
1006 : // they show up in the error console.
1007 0 : if (!JSREPORT_IS_WARNING(aReport.mFlags)) {
1008 : // First fire an ErrorEvent at the worker.
1009 0 : RootedDictionary<ErrorEventInit> init(aCx);
1010 :
1011 0 : if (aReport.mMutedError) {
1012 0 : init.mMessage.AssignLiteral("Script error.");
1013 : } else {
1014 0 : init.mMessage = aReport.mMessage;
1015 0 : init.mFilename = aReport.mFilename;
1016 0 : init.mLineno = aReport.mLineNumber;
1017 0 : init.mError = aException;
1018 : }
1019 :
1020 0 : init.mCancelable = true;
1021 0 : init.mBubbles = false;
1022 :
1023 0 : if (aTarget) {
1024 : RefPtr<ErrorEvent> event =
1025 0 : ErrorEvent::Constructor(aTarget, NS_LITERAL_STRING("error"), init);
1026 0 : event->SetTrusted(true);
1027 :
1028 0 : nsEventStatus status = nsEventStatus_eIgnore;
1029 0 : aTarget->DispatchDOMEvent(nullptr, event, nullptr, &status);
1030 :
1031 0 : if (status == nsEventStatus_eConsumeNoDefault) {
1032 0 : return;
1033 : }
1034 : }
1035 :
1036 : // Now fire an event at the global object, but don't do that if the error
1037 : // code is too much recursion and this is the same script threw the error.
1038 : // XXXbz the interaction of this with worker errors seems kinda broken.
1039 : // An overrecursion in the debugger or debugger sandbox will get turned
1040 : // into an error event on our parent worker!
1041 : // https://bugzilla.mozilla.org/show_bug.cgi?id=1271441 tracks making this
1042 : // better.
1043 0 : if (aFireAtScope &&
1044 0 : (aTarget || aReport.mErrorNumber != JSMSG_OVER_RECURSED)) {
1045 0 : JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx));
1046 0 : NS_ASSERTION(global, "This should never be null!");
1047 :
1048 0 : nsEventStatus status = nsEventStatus_eIgnore;
1049 : nsIScriptGlobalObject* sgo;
1050 :
1051 0 : if (aWorkerPrivate) {
1052 0 : WorkerGlobalScope* globalScope = nullptr;
1053 0 : UNWRAP_OBJECT(WorkerGlobalScope, &global, globalScope);
1054 :
1055 0 : if (!globalScope) {
1056 0 : WorkerDebuggerGlobalScope* globalScope = nullptr;
1057 0 : UNWRAP_OBJECT(WorkerDebuggerGlobalScope, &global, globalScope);
1058 :
1059 0 : MOZ_ASSERT_IF(globalScope, globalScope->GetWrapperPreserveColor() == global);
1060 0 : if (globalScope || IsDebuggerSandbox(global)) {
1061 0 : aWorkerPrivate->ReportErrorToDebugger(aReport.mFilename, aReport.mLineNumber,
1062 0 : aReport.mMessage);
1063 0 : return;
1064 : }
1065 :
1066 0 : MOZ_ASSERT(SimpleGlobalObject::SimpleGlobalType(global) ==
1067 : SimpleGlobalObject::GlobalType::BindingDetail);
1068 : // XXXbz We should really log this to console, but unwinding out of
1069 : // this stuff without ending up firing any events is ... hard. Just
1070 : // return for now.
1071 : // https://bugzilla.mozilla.org/show_bug.cgi?id=1271441 tracks
1072 : // making this better.
1073 0 : return;
1074 : }
1075 :
1076 0 : MOZ_ASSERT(globalScope->GetWrapperPreserveColor() == global);
1077 0 : nsIDOMEventTarget* target = static_cast<nsIDOMEventTarget*>(globalScope);
1078 :
1079 : RefPtr<ErrorEvent> event =
1080 0 : ErrorEvent::Constructor(aTarget, NS_LITERAL_STRING("error"), init);
1081 0 : event->SetTrusted(true);
1082 :
1083 0 : if (NS_FAILED(EventDispatcher::DispatchDOMEvent(target, nullptr,
1084 : event, nullptr,
1085 : &status))) {
1086 0 : NS_WARNING("Failed to dispatch worker thread error event!");
1087 0 : status = nsEventStatus_eIgnore;
1088 : }
1089 : }
1090 0 : else if ((sgo = nsJSUtils::GetStaticScriptGlobal(global))) {
1091 0 : MOZ_ASSERT(NS_IsMainThread());
1092 :
1093 0 : if (NS_FAILED(sgo->HandleScriptError(init, &status))) {
1094 0 : NS_WARNING("Failed to dispatch main thread error event!");
1095 0 : status = nsEventStatus_eIgnore;
1096 : }
1097 : }
1098 :
1099 : // Was preventDefault() called?
1100 0 : if (status == nsEventStatus_eConsumeNoDefault) {
1101 0 : return;
1102 : }
1103 : }
1104 : }
1105 :
1106 : // Now fire a runnable to do the same on the parent's thread if we can.
1107 0 : if (aWorkerPrivate) {
1108 : RefPtr<ReportErrorRunnable> runnable =
1109 0 : new ReportErrorRunnable(aWorkerPrivate, aReport);
1110 0 : runnable->Dispatch();
1111 0 : return;
1112 : }
1113 :
1114 : // Otherwise log an error to the error console.
1115 0 : LogErrorToConsole(aReport, aInnerWindowId);
1116 : }
1117 :
1118 : private:
1119 0 : ReportErrorRunnable(WorkerPrivate* aWorkerPrivate,
1120 : const WorkerErrorReport& aReport)
1121 0 : : WorkerRunnable(aWorkerPrivate, ParentThreadUnchangedBusyCount),
1122 0 : mReport(aReport)
1123 0 : { }
1124 :
1125 : virtual void
1126 0 : PostDispatch(WorkerPrivate* aWorkerPrivate, bool aDispatchResult) override
1127 : {
1128 0 : aWorkerPrivate->AssertIsOnWorkerThread();
1129 :
1130 : // Dispatch may fail if the worker was canceled, no need to report that as
1131 : // an error, so don't call base class PostDispatch.
1132 0 : }
1133 :
1134 : virtual bool
1135 0 : WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
1136 : {
1137 0 : JS::Rooted<JSObject*> target(aCx, aWorkerPrivate->GetWrapper());
1138 :
1139 : uint64_t innerWindowId;
1140 0 : bool fireAtScope = true;
1141 :
1142 0 : bool workerIsAcceptingEvents = aWorkerPrivate->IsAcceptingEvents();
1143 :
1144 0 : WorkerPrivate* parent = aWorkerPrivate->GetParent();
1145 0 : if (parent) {
1146 0 : innerWindowId = 0;
1147 : }
1148 : else {
1149 0 : AssertIsOnMainThread();
1150 :
1151 0 : if (aWorkerPrivate->IsFrozen() ||
1152 0 : aWorkerPrivate->IsParentWindowPaused()) {
1153 0 : MOZ_ASSERT(!IsDebuggerRunnable());
1154 0 : aWorkerPrivate->QueueRunnable(this);
1155 0 : return true;
1156 : }
1157 :
1158 0 : if (aWorkerPrivate->IsSharedWorker()) {
1159 0 : aWorkerPrivate->BroadcastErrorToSharedWorkers(aCx, &mReport,
1160 0 : /* isErrorEvent */ true);
1161 0 : return true;
1162 : }
1163 :
1164 : // Service workers do not have a main thread parent global, so normal
1165 : // worker error reporting will crash. Instead, pass the error to
1166 : // the ServiceWorkerManager to report on any controlled documents.
1167 0 : if (aWorkerPrivate->IsServiceWorker()) {
1168 0 : RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
1169 0 : if (swm) {
1170 0 : swm->HandleError(aCx, aWorkerPrivate->GetPrincipal(),
1171 : aWorkerPrivate->ServiceWorkerScope(),
1172 : aWorkerPrivate->ScriptURL(),
1173 : mReport.mMessage,
1174 : mReport.mFilename, mReport.mLine, mReport.mLineNumber,
1175 : mReport.mColumnNumber, mReport.mFlags,
1176 0 : mReport.mExnType);
1177 : }
1178 0 : return true;
1179 : }
1180 :
1181 : // The innerWindowId is only required if we are going to ReportError
1182 : // below, which is gated on this condition. The inner window correctness
1183 : // check is only going to succeed when the worker is accepting events.
1184 0 : if (workerIsAcceptingEvents) {
1185 0 : aWorkerPrivate->AssertInnerWindowIsCorrect();
1186 0 : innerWindowId = aWorkerPrivate->WindowID();
1187 : }
1188 : }
1189 :
1190 : // Don't fire this event if the JS object has been disconnected from the
1191 : // private object.
1192 0 : if (!workerIsAcceptingEvents) {
1193 0 : return true;
1194 : }
1195 :
1196 0 : ReportError(aCx, parent, fireAtScope, aWorkerPrivate, mReport,
1197 0 : innerWindowId);
1198 0 : return true;
1199 : }
1200 : };
1201 :
1202 : class TimerRunnable final : public WorkerRunnable,
1203 : public nsITimerCallback
1204 : {
1205 : public:
1206 : NS_DECL_ISUPPORTS_INHERITED
1207 :
1208 0 : explicit TimerRunnable(WorkerPrivate* aWorkerPrivate)
1209 0 : : WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
1210 0 : { }
1211 :
1212 : private:
1213 0 : ~TimerRunnable() {}
1214 :
1215 : virtual bool
1216 0 : PreDispatch(WorkerPrivate* aWorkerPrivate) override
1217 : {
1218 : // Silence bad assertions.
1219 0 : return true;
1220 : }
1221 :
1222 : virtual void
1223 0 : PostDispatch(WorkerPrivate* aWorkerPrivate, bool aDispatchResult) override
1224 : {
1225 : // Silence bad assertions.
1226 0 : }
1227 :
1228 : virtual bool
1229 0 : WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
1230 : {
1231 0 : return aWorkerPrivate->RunExpiredTimeouts(aCx);
1232 : }
1233 :
1234 : NS_IMETHOD
1235 0 : Notify(nsITimer* aTimer) override
1236 : {
1237 0 : return Run();
1238 : }
1239 : };
1240 :
1241 0 : NS_IMPL_ISUPPORTS_INHERITED(TimerRunnable, WorkerRunnable, nsITimerCallback)
1242 :
1243 0 : class DebuggerImmediateRunnable : public WorkerRunnable
1244 : {
1245 : RefPtr<dom::Function> mHandler;
1246 :
1247 : public:
1248 0 : explicit DebuggerImmediateRunnable(WorkerPrivate* aWorkerPrivate,
1249 : dom::Function& aHandler)
1250 0 : : WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
1251 0 : mHandler(&aHandler)
1252 0 : { }
1253 :
1254 : private:
1255 : virtual bool
1256 0 : IsDebuggerRunnable() const override
1257 : {
1258 0 : return true;
1259 : }
1260 :
1261 : virtual bool
1262 0 : PreDispatch(WorkerPrivate* aWorkerPrivate) override
1263 : {
1264 : // Silence bad assertions.
1265 0 : return true;
1266 : }
1267 :
1268 : virtual void
1269 0 : PostDispatch(WorkerPrivate* aWorkerPrivate, bool aDispatchResult) override
1270 : {
1271 : // Silence bad assertions.
1272 0 : }
1273 :
1274 : virtual bool
1275 0 : WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
1276 : {
1277 0 : JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx));
1278 0 : JS::Rooted<JS::Value> callable(aCx, JS::ObjectOrNullValue(mHandler->CallableOrNull()));
1279 0 : JS::HandleValueArray args = JS::HandleValueArray::empty();
1280 0 : JS::Rooted<JS::Value> rval(aCx);
1281 0 : if (!JS_CallFunctionValue(aCx, global, callable, args, &rval)) {
1282 : // Just return false; WorkerRunnable::Run will report the exception.
1283 0 : return false;
1284 : }
1285 :
1286 0 : return true;
1287 : }
1288 : };
1289 :
1290 : void
1291 0 : PeriodicGCTimerCallback(nsITimer* aTimer, void* aClosure)
1292 : {
1293 0 : auto workerPrivate = static_cast<WorkerPrivate*>(aClosure);
1294 0 : MOZ_DIAGNOSTIC_ASSERT(workerPrivate);
1295 0 : workerPrivate->AssertIsOnWorkerThread();
1296 0 : workerPrivate->GarbageCollectInternal(workerPrivate->GetJSContext(),
1297 : false /* shrinking */,
1298 0 : false /* collect children */);
1299 0 : }
1300 :
1301 : void
1302 0 : IdleGCTimerCallback(nsITimer* aTimer, void* aClosure)
1303 : {
1304 0 : auto workerPrivate = static_cast<WorkerPrivate*>(aClosure);
1305 0 : MOZ_DIAGNOSTIC_ASSERT(workerPrivate);
1306 0 : workerPrivate->AssertIsOnWorkerThread();
1307 0 : workerPrivate->GarbageCollectInternal(workerPrivate->GetJSContext(),
1308 : true /* shrinking */,
1309 0 : false /* collect children */);
1310 0 : }
1311 :
1312 0 : class UpdateContextOptionsRunnable final : public WorkerControlRunnable
1313 : {
1314 : JS::ContextOptions mContextOptions;
1315 :
1316 : public:
1317 0 : UpdateContextOptionsRunnable(WorkerPrivate* aWorkerPrivate,
1318 : const JS::ContextOptions& aContextOptions)
1319 0 : : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
1320 0 : mContextOptions(aContextOptions)
1321 0 : { }
1322 :
1323 : private:
1324 : virtual bool
1325 0 : WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
1326 : {
1327 0 : aWorkerPrivate->UpdateContextOptionsInternal(aCx, mContextOptions);
1328 0 : return true;
1329 : }
1330 : };
1331 :
1332 0 : class UpdatePreferenceRunnable final : public WorkerControlRunnable
1333 : {
1334 : WorkerPreference mPref;
1335 : bool mValue;
1336 :
1337 : public:
1338 0 : UpdatePreferenceRunnable(WorkerPrivate* aWorkerPrivate,
1339 : WorkerPreference aPref,
1340 : bool aValue)
1341 0 : : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
1342 : mPref(aPref),
1343 0 : mValue(aValue)
1344 0 : { }
1345 :
1346 : virtual bool
1347 0 : WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
1348 : {
1349 0 : aWorkerPrivate->UpdatePreferenceInternal(mPref, mValue);
1350 0 : return true;
1351 : }
1352 : };
1353 :
1354 0 : class UpdateLanguagesRunnable final : public WorkerRunnable
1355 : {
1356 : nsTArray<nsString> mLanguages;
1357 :
1358 : public:
1359 0 : UpdateLanguagesRunnable(WorkerPrivate* aWorkerPrivate,
1360 : const nsTArray<nsString>& aLanguages)
1361 0 : : WorkerRunnable(aWorkerPrivate),
1362 0 : mLanguages(aLanguages)
1363 0 : { }
1364 :
1365 : virtual bool
1366 0 : WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
1367 : {
1368 0 : aWorkerPrivate->UpdateLanguagesInternal(mLanguages);
1369 0 : return true;
1370 : }
1371 : };
1372 :
1373 0 : class UpdateJSWorkerMemoryParameterRunnable final :
1374 : public WorkerControlRunnable
1375 : {
1376 : uint32_t mValue;
1377 : JSGCParamKey mKey;
1378 :
1379 : public:
1380 0 : UpdateJSWorkerMemoryParameterRunnable(WorkerPrivate* aWorkerPrivate,
1381 : JSGCParamKey aKey,
1382 : uint32_t aValue)
1383 0 : : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
1384 0 : mValue(aValue), mKey(aKey)
1385 0 : { }
1386 :
1387 : private:
1388 : virtual bool
1389 0 : WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
1390 : {
1391 0 : aWorkerPrivate->UpdateJSWorkerMemoryParameterInternal(aCx, mKey, mValue);
1392 0 : return true;
1393 : }
1394 : };
1395 :
1396 : #ifdef JS_GC_ZEAL
1397 0 : class UpdateGCZealRunnable final : public WorkerControlRunnable
1398 : {
1399 : uint8_t mGCZeal;
1400 : uint32_t mFrequency;
1401 :
1402 : public:
1403 0 : UpdateGCZealRunnable(WorkerPrivate* aWorkerPrivate,
1404 : uint8_t aGCZeal,
1405 : uint32_t aFrequency)
1406 0 : : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
1407 0 : mGCZeal(aGCZeal), mFrequency(aFrequency)
1408 0 : { }
1409 :
1410 : private:
1411 : virtual bool
1412 0 : WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
1413 : {
1414 0 : aWorkerPrivate->UpdateGCZealInternal(aCx, mGCZeal, mFrequency);
1415 0 : return true;
1416 : }
1417 : };
1418 : #endif
1419 :
1420 0 : class GarbageCollectRunnable final : public WorkerControlRunnable
1421 : {
1422 : bool mShrinking;
1423 : bool mCollectChildren;
1424 :
1425 : public:
1426 0 : GarbageCollectRunnable(WorkerPrivate* aWorkerPrivate, bool aShrinking,
1427 : bool aCollectChildren)
1428 0 : : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
1429 0 : mShrinking(aShrinking), mCollectChildren(aCollectChildren)
1430 0 : { }
1431 :
1432 : private:
1433 : virtual bool
1434 0 : PreDispatch(WorkerPrivate* aWorkerPrivate) override
1435 : {
1436 : // Silence bad assertions, this can be dispatched from either the main
1437 : // thread or the timer thread..
1438 0 : return true;
1439 : }
1440 :
1441 : virtual void
1442 0 : PostDispatch(WorkerPrivate* aWorkerPrivate, bool aDispatchResult) override
1443 : {
1444 : // Silence bad assertions, this can be dispatched from either the main
1445 : // thread or the timer thread..
1446 0 : }
1447 :
1448 : virtual bool
1449 0 : WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
1450 : {
1451 0 : aWorkerPrivate->GarbageCollectInternal(aCx, mShrinking, mCollectChildren);
1452 0 : return true;
1453 : }
1454 : };
1455 :
1456 0 : class CycleCollectRunnable : public WorkerControlRunnable
1457 : {
1458 : bool mCollectChildren;
1459 :
1460 : public:
1461 0 : CycleCollectRunnable(WorkerPrivate* aWorkerPrivate, bool aCollectChildren)
1462 0 : : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
1463 0 : mCollectChildren(aCollectChildren)
1464 0 : { }
1465 :
1466 : bool
1467 0 : WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
1468 : {
1469 0 : aWorkerPrivate->CycleCollectInternal(mCollectChildren);
1470 0 : return true;
1471 : }
1472 : };
1473 :
1474 0 : class OfflineStatusChangeRunnable : public WorkerRunnable
1475 : {
1476 : public:
1477 0 : OfflineStatusChangeRunnable(WorkerPrivate* aWorkerPrivate, bool aIsOffline)
1478 0 : : WorkerRunnable(aWorkerPrivate),
1479 0 : mIsOffline(aIsOffline)
1480 : {
1481 0 : }
1482 :
1483 : bool
1484 0 : WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
1485 : {
1486 0 : aWorkerPrivate->OfflineStatusChangeEventInternal(mIsOffline);
1487 0 : return true;
1488 : }
1489 :
1490 : private:
1491 : bool mIsOffline;
1492 : };
1493 :
1494 0 : class MemoryPressureRunnable : public WorkerControlRunnable
1495 : {
1496 : public:
1497 0 : explicit MemoryPressureRunnable(WorkerPrivate* aWorkerPrivate)
1498 0 : : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
1499 0 : {}
1500 :
1501 : bool
1502 0 : WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
1503 : {
1504 0 : aWorkerPrivate->MemoryPressureInternal();
1505 0 : return true;
1506 : }
1507 : };
1508 :
1509 : #ifdef DEBUG
1510 : static bool
1511 0 : StartsWithExplicit(nsACString& s)
1512 : {
1513 0 : return StringBeginsWith(s, NS_LITERAL_CSTRING("explicit/"));
1514 : }
1515 : #endif
1516 :
1517 : class MessagePortRunnable final : public WorkerRunnable
1518 : {
1519 : MessagePortIdentifier mPortIdentifier;
1520 :
1521 : public:
1522 0 : MessagePortRunnable(WorkerPrivate* aWorkerPrivate, MessagePort* aPort)
1523 0 : : WorkerRunnable(aWorkerPrivate)
1524 : {
1525 0 : MOZ_ASSERT(aPort);
1526 : // In order to move the port from one thread to another one, we have to
1527 : // close and disentangle it. The output will be a MessagePortIdentifier that
1528 : // will be used to recreate a new MessagePort on the other thread.
1529 0 : aPort->CloneAndDisentangle(mPortIdentifier);
1530 0 : }
1531 :
1532 : private:
1533 0 : ~MessagePortRunnable()
1534 0 : { }
1535 :
1536 : virtual bool
1537 0 : WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
1538 : {
1539 0 : return aWorkerPrivate->ConnectMessagePort(aCx, mPortIdentifier);
1540 : }
1541 :
1542 : nsresult
1543 0 : Cancel() override
1544 : {
1545 0 : MessagePort::ForceClose(mPortIdentifier);
1546 0 : return WorkerRunnable::Cancel();
1547 : }
1548 : };
1549 :
1550 : PRThread*
1551 1 : PRThreadFromThread(nsIThread* aThread)
1552 : {
1553 1 : MOZ_ASSERT(aThread);
1554 :
1555 : PRThread* result;
1556 1 : MOZ_ALWAYS_SUCCEEDS(aThread->GetPRThread(&result));
1557 1 : MOZ_ASSERT(result);
1558 :
1559 1 : return result;
1560 : }
1561 :
1562 0 : class SimpleWorkerHolder final : public WorkerHolder
1563 : {
1564 : public:
1565 0 : virtual bool Notify(Status aStatus) { return true; }
1566 : };
1567 :
1568 : } /* anonymous namespace */
1569 :
1570 0 : NS_IMPL_ISUPPORTS_INHERITED0(MainThreadReleaseRunnable, Runnable)
1571 :
1572 0 : NS_IMPL_ISUPPORTS_INHERITED0(TopLevelWorkerFinishedRunnable, Runnable)
1573 :
1574 : namespace {
1575 :
1576 : class WrappedControlRunnable final : public WorkerControlRunnable
1577 : {
1578 : nsCOMPtr<nsIRunnable> mInner;
1579 :
1580 0 : ~WrappedControlRunnable()
1581 0 : {
1582 0 : }
1583 :
1584 : public:
1585 0 : WrappedControlRunnable(WorkerPrivate* aWorkerPrivate,
1586 : already_AddRefed<nsIRunnable>&& aInner)
1587 0 : : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
1588 0 : , mInner(aInner)
1589 : {
1590 0 : }
1591 :
1592 : virtual bool
1593 0 : PreDispatch(WorkerPrivate* aWorkerPrivate) override
1594 : {
1595 : // Silence bad assertions, this can be dispatched from any thread.
1596 0 : return true;
1597 : }
1598 :
1599 : virtual void
1600 0 : PostDispatch(WorkerPrivate* aWorkerPrivate, bool aDispatchResult) override
1601 : {
1602 : // Silence bad assertions, this can be dispatched from any thread.
1603 0 : }
1604 :
1605 : bool
1606 0 : WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
1607 : {
1608 0 : mInner->Run();
1609 0 : return true;
1610 : }
1611 :
1612 : nsresult
1613 0 : Cancel() override
1614 : {
1615 : // First run the default cancelation code
1616 0 : WorkerControlRunnable::Cancel();
1617 :
1618 : // Attempt to cancel the inner runnable as well
1619 0 : nsCOMPtr<nsICancelableRunnable> cr = do_QueryInterface(mInner);
1620 0 : if (cr) {
1621 0 : return cr->Cancel();
1622 : }
1623 0 : return NS_OK;
1624 : }
1625 : };
1626 :
1627 : } // anonymous namespace
1628 :
1629 : BEGIN_WORKERS_NAMESPACE
1630 :
1631 : class WorkerControlEventTarget final : public nsIEventTarget
1632 : {
1633 : mozilla::Mutex mMutex;
1634 : WorkerPrivate* mWorkerPrivate;
1635 :
1636 0 : ~WorkerControlEventTarget() = default;
1637 :
1638 : public:
1639 1 : explicit WorkerControlEventTarget(WorkerPrivate* aWorkerPrivate)
1640 1 : : mMutex("WorkerControlEventTarget")
1641 1 : , mWorkerPrivate(aWorkerPrivate)
1642 : {
1643 1 : MOZ_DIAGNOSTIC_ASSERT(mWorkerPrivate);
1644 1 : }
1645 :
1646 : void
1647 0 : ForgetWorkerPrivate(WorkerPrivate* aWorkerPrivate)
1648 : {
1649 0 : MutexAutoLock lock(mMutex);
1650 0 : MOZ_DIAGNOSTIC_ASSERT(mWorkerPrivate == aWorkerPrivate);
1651 0 : mWorkerPrivate = nullptr;
1652 0 : }
1653 :
1654 : NS_IMETHOD
1655 0 : DispatchFromScript(nsIRunnable* aRunnable, uint32_t aFlags) override
1656 : {
1657 0 : nsCOMPtr<nsIRunnable> runnable(aRunnable);
1658 0 : return Dispatch(runnable.forget(), aFlags);
1659 : }
1660 :
1661 : NS_IMETHOD
1662 0 : Dispatch(already_AddRefed<nsIRunnable> aRunnable, uint32_t aFlags = NS_DISPATCH_NORMAL) override
1663 : {
1664 0 : MutexAutoLock lock(mMutex);
1665 :
1666 0 : if (!mWorkerPrivate) {
1667 0 : return NS_ERROR_FAILURE;
1668 : }
1669 :
1670 : RefPtr<WorkerControlRunnable> r = new WrappedControlRunnable(mWorkerPrivate,
1671 0 : Move(aRunnable));
1672 0 : if (!r->Dispatch()) {
1673 0 : return NS_ERROR_FAILURE;
1674 : }
1675 :
1676 0 : return NS_OK;
1677 : }
1678 :
1679 : NS_IMETHOD
1680 0 : DelayedDispatch(already_AddRefed<nsIRunnable>, uint32_t) override
1681 : {
1682 0 : return NS_ERROR_NOT_IMPLEMENTED;
1683 : }
1684 :
1685 0 : NS_IMETHOD_(bool) IsOnCurrentThreadInfallible() override
1686 : {
1687 0 : MutexAutoLock lock(mMutex);
1688 :
1689 0 : if (!mWorkerPrivate) {
1690 0 : return false;
1691 : }
1692 :
1693 0 : return mWorkerPrivate->IsOnCurrentThread();
1694 : }
1695 :
1696 : NS_IMETHOD
1697 0 : IsOnCurrentThread(bool* aIsOnCurrentThread) override
1698 : {
1699 0 : MOZ_ASSERT(aIsOnCurrentThread);
1700 0 : *aIsOnCurrentThread = IsOnCurrentThreadInfallible();
1701 0 : return NS_OK;
1702 : }
1703 :
1704 : NS_DECL_THREADSAFE_ISUPPORTS
1705 : };
1706 :
1707 260 : NS_IMPL_ISUPPORTS(WorkerControlEventTarget, nsIEventTarget)
1708 :
1709 : END_WORKERS_NAMESPACE
1710 :
1711 3 : WorkerLoadInfo::WorkerLoadInfo()
1712 : : mLoadFlags(nsIRequest::LOAD_NORMAL)
1713 : , mWindowID(UINT64_MAX)
1714 : , mServiceWorkerID(0)
1715 : , mReferrerPolicy(net::RP_Unset)
1716 : , mFromWindow(false)
1717 : , mEvalAllowed(false)
1718 : , mReportCSPViolations(false)
1719 : , mXHRParamsAllowed(false)
1720 : , mPrincipalIsSystem(false)
1721 : , mStorageAllowed(false)
1722 3 : , mServiceWorkersTestingInWindow(false)
1723 : {
1724 3 : MOZ_COUNT_CTOR(WorkerLoadInfo);
1725 3 : }
1726 :
1727 4 : WorkerLoadInfo::~WorkerLoadInfo()
1728 : {
1729 2 : MOZ_COUNT_DTOR(WorkerLoadInfo);
1730 2 : }
1731 :
1732 : void
1733 2 : WorkerLoadInfo::StealFrom(WorkerLoadInfo& aOther)
1734 : {
1735 2 : MOZ_ASSERT(!mBaseURI);
1736 2 : aOther.mBaseURI.swap(mBaseURI);
1737 :
1738 2 : MOZ_ASSERT(!mResolvedScriptURI);
1739 2 : aOther.mResolvedScriptURI.swap(mResolvedScriptURI);
1740 :
1741 2 : MOZ_ASSERT(!mPrincipal);
1742 2 : aOther.mPrincipal.swap(mPrincipal);
1743 :
1744 2 : MOZ_ASSERT(!mScriptContext);
1745 2 : aOther.mScriptContext.swap(mScriptContext);
1746 :
1747 2 : MOZ_ASSERT(!mWindow);
1748 2 : aOther.mWindow.swap(mWindow);
1749 :
1750 2 : MOZ_ASSERT(!mCSP);
1751 2 : aOther.mCSP.swap(mCSP);
1752 :
1753 2 : MOZ_ASSERT(!mChannel);
1754 2 : aOther.mChannel.swap(mChannel);
1755 :
1756 2 : MOZ_ASSERT(!mLoadGroup);
1757 2 : aOther.mLoadGroup.swap(mLoadGroup);
1758 :
1759 2 : MOZ_ASSERT(!mLoadFailedAsyncRunnable);
1760 2 : aOther.mLoadFailedAsyncRunnable.swap(mLoadFailedAsyncRunnable);
1761 :
1762 2 : MOZ_ASSERT(!mInterfaceRequestor);
1763 2 : aOther.mInterfaceRequestor.swap(mInterfaceRequestor);
1764 :
1765 2 : MOZ_ASSERT(!mPrincipalInfo);
1766 2 : mPrincipalInfo = aOther.mPrincipalInfo.forget();
1767 :
1768 2 : mDomain = aOther.mDomain;
1769 2 : mOrigin = aOther.mOrigin;
1770 2 : mServiceWorkerCacheName = aOther.mServiceWorkerCacheName;
1771 2 : mLoadFlags = aOther.mLoadFlags;
1772 2 : mWindowID = aOther.mWindowID;
1773 2 : mServiceWorkerID = aOther.mServiceWorkerID;
1774 2 : mReferrerPolicy = aOther.mReferrerPolicy;
1775 2 : mFromWindow = aOther.mFromWindow;
1776 2 : mEvalAllowed = aOther.mEvalAllowed;
1777 2 : mReportCSPViolations = aOther.mReportCSPViolations;
1778 2 : mXHRParamsAllowed = aOther.mXHRParamsAllowed;
1779 2 : mPrincipalIsSystem = aOther.mPrincipalIsSystem;
1780 2 : mStorageAllowed = aOther.mStorageAllowed;
1781 2 : mServiceWorkersTestingInWindow = aOther.mServiceWorkersTestingInWindow;
1782 2 : mOriginAttributes = aOther.mOriginAttributes;
1783 2 : }
1784 :
1785 : nsresult
1786 2 : WorkerLoadInfo::SetPrincipalOnMainThread(nsIPrincipal* aPrincipal,
1787 : nsILoadGroup* aLoadGroup)
1788 : {
1789 2 : AssertIsOnMainThread();
1790 2 : MOZ_ASSERT(NS_LoadGroupMatchesPrincipal(aLoadGroup, aPrincipal));
1791 :
1792 2 : mPrincipal = aPrincipal;
1793 2 : mPrincipalIsSystem = nsContentUtils::IsSystemPrincipal(aPrincipal);
1794 :
1795 2 : nsresult rv = aPrincipal->GetCsp(getter_AddRefs(mCSP));
1796 2 : NS_ENSURE_SUCCESS(rv, rv);
1797 :
1798 2 : if (mCSP) {
1799 0 : mCSP->GetAllowsEval(&mReportCSPViolations, &mEvalAllowed);
1800 : // Set ReferrerPolicy
1801 0 : bool hasReferrerPolicy = false;
1802 0 : uint32_t rp = mozilla::net::RP_Unset;
1803 :
1804 0 : rv = mCSP->GetReferrerPolicy(&rp, &hasReferrerPolicy);
1805 0 : NS_ENSURE_SUCCESS(rv, rv);
1806 :
1807 0 : if (hasReferrerPolicy) {
1808 0 : mReferrerPolicy = static_cast<net::ReferrerPolicy>(rp);
1809 : }
1810 : } else {
1811 2 : mEvalAllowed = true;
1812 2 : mReportCSPViolations = false;
1813 : }
1814 :
1815 2 : mLoadGroup = aLoadGroup;
1816 :
1817 2 : mPrincipalInfo = new PrincipalInfo();
1818 2 : mOriginAttributes = nsContentUtils::GetOriginAttributes(aLoadGroup);
1819 :
1820 2 : rv = PrincipalToPrincipalInfo(aPrincipal, mPrincipalInfo);
1821 2 : NS_ENSURE_SUCCESS(rv, rv);
1822 :
1823 2 : rv = nsContentUtils::GetUTFOrigin(aPrincipal, mOrigin);
1824 2 : NS_ENSURE_SUCCESS(rv, rv);
1825 :
1826 2 : return NS_OK;
1827 : }
1828 :
1829 : nsresult
1830 3 : WorkerLoadInfo::GetPrincipalAndLoadGroupFromChannel(nsIChannel* aChannel,
1831 : nsIPrincipal** aPrincipalOut,
1832 : nsILoadGroup** aLoadGroupOut)
1833 : {
1834 3 : AssertIsOnMainThread();
1835 3 : MOZ_DIAGNOSTIC_ASSERT(aChannel);
1836 3 : MOZ_DIAGNOSTIC_ASSERT(aPrincipalOut);
1837 3 : MOZ_DIAGNOSTIC_ASSERT(aLoadGroupOut);
1838 :
1839 : // Initial triggering principal should be set
1840 3 : MOZ_DIAGNOSTIC_ASSERT(mPrincipal);
1841 :
1842 3 : nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
1843 3 : MOZ_DIAGNOSTIC_ASSERT(ssm);
1844 :
1845 6 : nsCOMPtr<nsIPrincipal> channelPrincipal;
1846 3 : nsresult rv = ssm->GetChannelResultPrincipal(aChannel, getter_AddRefs(channelPrincipal));
1847 3 : NS_ENSURE_SUCCESS(rv, rv);
1848 :
1849 6 : nsCOMPtr<nsILoadGroup> channelLoadGroup;
1850 3 : rv = aChannel->GetLoadGroup(getter_AddRefs(channelLoadGroup));
1851 3 : NS_ENSURE_SUCCESS(rv, rv);
1852 3 : MOZ_ASSERT(channelLoadGroup);
1853 :
1854 : // If the load principal is the system principal then the channel
1855 : // principal must also be the system principal (we do not allow chrome
1856 : // code to create workers with non-chrome scripts, and if we ever decide
1857 : // to change this we need to make sure we don't always set
1858 : // mPrincipalIsSystem to true in WorkerPrivate::GetLoadInfo()). Otherwise
1859 : // this channel principal must be same origin with the load principal (we
1860 : // check again here in case redirects changed the location of the script).
1861 3 : if (nsContentUtils::IsSystemPrincipal(mPrincipal)) {
1862 3 : if (!nsContentUtils::IsSystemPrincipal(channelPrincipal)) {
1863 6 : nsCOMPtr<nsIURI> finalURI;
1864 3 : rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(finalURI));
1865 3 : NS_ENSURE_SUCCESS(rv, rv);
1866 :
1867 : // See if this is a resource URI. Since JSMs usually come from
1868 : // resource:// URIs we're currently considering all URIs with the
1869 : // URI_IS_UI_RESOURCE flag as valid for creating privileged workers.
1870 : bool isResource;
1871 3 : rv = NS_URIChainHasFlags(finalURI,
1872 : nsIProtocolHandler::URI_IS_UI_RESOURCE,
1873 3 : &isResource);
1874 3 : NS_ENSURE_SUCCESS(rv, rv);
1875 :
1876 3 : if (isResource) {
1877 : // Assign the system principal to the resource:// worker only if it
1878 : // was loaded from code using the system principal.
1879 3 : channelPrincipal = mPrincipal;
1880 : } else {
1881 0 : return NS_ERROR_DOM_BAD_URI;
1882 : }
1883 : }
1884 : }
1885 :
1886 : // The principal can change, but it should still match the original
1887 : // load group's appId and browser element flag.
1888 3 : MOZ_ASSERT(NS_LoadGroupMatchesPrincipal(channelLoadGroup, channelPrincipal));
1889 :
1890 3 : channelPrincipal.forget(aPrincipalOut);
1891 3 : channelLoadGroup.forget(aLoadGroupOut);
1892 :
1893 3 : return NS_OK;
1894 : }
1895 :
1896 : nsresult
1897 2 : WorkerLoadInfo::SetPrincipalFromChannel(nsIChannel* aChannel)
1898 : {
1899 2 : AssertIsOnMainThread();
1900 :
1901 4 : nsCOMPtr<nsIPrincipal> principal;
1902 4 : nsCOMPtr<nsILoadGroup> loadGroup;
1903 4 : nsresult rv = GetPrincipalAndLoadGroupFromChannel(aChannel,
1904 4 : getter_AddRefs(principal),
1905 6 : getter_AddRefs(loadGroup));
1906 2 : NS_ENSURE_SUCCESS(rv, rv);
1907 :
1908 2 : return SetPrincipalOnMainThread(principal, loadGroup);
1909 : }
1910 :
1911 : #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
1912 : bool
1913 1 : WorkerLoadInfo::FinalChannelPrincipalIsValid(nsIChannel* aChannel)
1914 : {
1915 1 : AssertIsOnMainThread();
1916 :
1917 2 : nsCOMPtr<nsIPrincipal> principal;
1918 2 : nsCOMPtr<nsILoadGroup> loadGroup;
1919 2 : nsresult rv = GetPrincipalAndLoadGroupFromChannel(aChannel,
1920 2 : getter_AddRefs(principal),
1921 3 : getter_AddRefs(loadGroup));
1922 1 : NS_ENSURE_SUCCESS(rv, false);
1923 :
1924 :
1925 : // Verify that the channel is still a null principal. We don't care
1926 : // if these are the exact same null principal object, though. From
1927 : // the worker's perspective its the same effect.
1928 1 : if (principal->GetIsNullPrincipal() && mPrincipal->GetIsNullPrincipal()) {
1929 0 : return true;
1930 : }
1931 :
1932 : // Otherwise we require exact equality. Redirects can happen, but they
1933 : // are not allowed to change our principal.
1934 1 : if (principal->Equals(mPrincipal)) {
1935 1 : return true;
1936 : }
1937 :
1938 0 : return false;
1939 : }
1940 :
1941 : bool
1942 2 : WorkerLoadInfo::PrincipalIsValid() const
1943 : {
1944 6 : return mPrincipal && mPrincipalInfo &&
1945 6 : mPrincipalInfo->type() != PrincipalInfo::T__None &&
1946 4 : mPrincipalInfo->type() <= PrincipalInfo::T__Last;
1947 : }
1948 :
1949 : bool
1950 1 : WorkerLoadInfo::PrincipalURIMatchesScriptURL()
1951 : {
1952 1 : AssertIsOnMainThread();
1953 :
1954 2 : nsAutoCString scheme;
1955 1 : nsresult rv = mBaseURI->GetScheme(scheme);
1956 1 : NS_ENSURE_SUCCESS(rv, false);
1957 :
1958 : // A system principal must either be a blob URL or a resource JSM.
1959 1 : if (mPrincipal->GetIsSystemPrincipal()) {
1960 1 : if (scheme == NS_LITERAL_CSTRING("blob")) {
1961 0 : return true;
1962 : }
1963 :
1964 1 : bool isResource = false;
1965 1 : nsresult rv = NS_URIChainHasFlags(mBaseURI,
1966 : nsIProtocolHandler::URI_IS_UI_RESOURCE,
1967 1 : &isResource);
1968 1 : NS_ENSURE_SUCCESS(rv, false);
1969 :
1970 1 : return isResource;
1971 : }
1972 :
1973 : // A null principal can occur for a data URL worker script or a blob URL
1974 : // worker script from a sandboxed iframe.
1975 0 : if (mPrincipal->GetIsNullPrincipal()) {
1976 0 : return scheme == NS_LITERAL_CSTRING("data") ||
1977 0 : scheme == NS_LITERAL_CSTRING("blob");
1978 : }
1979 :
1980 : // The principal for a blob: URL worker script does not have a matching URL.
1981 : // This is likely a bug in our referer setting logic, but exempt it for now.
1982 : // This is another reason we should fix bug 1340694 so that referer does not
1983 : // depend on the principal URI.
1984 0 : if (scheme == NS_LITERAL_CSTRING("blob")) {
1985 0 : return true;
1986 : }
1987 :
1988 0 : nsCOMPtr<nsIURI> principalURI;
1989 0 : rv = mPrincipal->GetURI(getter_AddRefs(principalURI));
1990 0 : NS_ENSURE_SUCCESS(rv, false);
1991 0 : NS_ENSURE_TRUE(principalURI, false);
1992 :
1993 0 : bool equal = false;
1994 0 : rv = principalURI->Equals(mBaseURI, &equal);
1995 0 : NS_ENSURE_SUCCESS(rv, false);
1996 :
1997 0 : return equal;
1998 : }
1999 : #endif // MOZ_DIAGNOSTIC_ASSERT_ENABLED
2000 :
2001 : bool
2002 0 : WorkerLoadInfo::ProxyReleaseMainThreadObjects(WorkerPrivate* aWorkerPrivate)
2003 : {
2004 0 : nsCOMPtr<nsILoadGroup> nullLoadGroup;
2005 0 : return ProxyReleaseMainThreadObjects(aWorkerPrivate, nullLoadGroup);
2006 : }
2007 :
2008 : bool
2009 0 : WorkerLoadInfo::ProxyReleaseMainThreadObjects(WorkerPrivate* aWorkerPrivate,
2010 : nsCOMPtr<nsILoadGroup>& aLoadGroupToCancel)
2011 : {
2012 :
2013 : static const uint32_t kDoomedCount = 10;
2014 0 : nsTArray<nsCOMPtr<nsISupports>> doomed(kDoomedCount);
2015 :
2016 0 : SwapToISupportsArray(mWindow, doomed);
2017 0 : SwapToISupportsArray(mScriptContext, doomed);
2018 0 : SwapToISupportsArray(mBaseURI, doomed);
2019 0 : SwapToISupportsArray(mResolvedScriptURI, doomed);
2020 0 : SwapToISupportsArray(mPrincipal, doomed);
2021 0 : SwapToISupportsArray(mChannel, doomed);
2022 0 : SwapToISupportsArray(mCSP, doomed);
2023 0 : SwapToISupportsArray(mLoadGroup, doomed);
2024 0 : SwapToISupportsArray(mLoadFailedAsyncRunnable, doomed);
2025 0 : SwapToISupportsArray(mInterfaceRequestor, doomed);
2026 : // Before adding anything here update kDoomedCount above!
2027 :
2028 0 : MOZ_ASSERT(doomed.Length() == kDoomedCount);
2029 :
2030 : RefPtr<MainThreadReleaseRunnable> runnable =
2031 0 : new MainThreadReleaseRunnable(doomed, aLoadGroupToCancel);
2032 0 : return NS_SUCCEEDED(aWorkerPrivate->DispatchToMainThread(runnable.forget()));
2033 : }
2034 :
2035 : template <class Derived>
2036 : class WorkerPrivateParent<Derived>::EventTarget final
2037 : : public nsISerialEventTarget
2038 : {
2039 : // This mutex protects mWorkerPrivate and must be acquired *before* the
2040 : // WorkerPrivate's mutex whenever they must both be held.
2041 : mozilla::Mutex mMutex;
2042 : WorkerPrivate* mWorkerPrivate;
2043 : nsIEventTarget* mWeakNestedEventTarget;
2044 : nsCOMPtr<nsIEventTarget> mNestedEventTarget;
2045 :
2046 : public:
2047 1 : explicit EventTarget(WorkerPrivate* aWorkerPrivate)
2048 : : mMutex("WorkerPrivateParent::EventTarget::mMutex"),
2049 1 : mWorkerPrivate(aWorkerPrivate), mWeakNestedEventTarget(nullptr)
2050 : {
2051 1 : MOZ_ASSERT(aWorkerPrivate);
2052 1 : }
2053 :
2054 17 : EventTarget(WorkerPrivate* aWorkerPrivate, nsIEventTarget* aNestedEventTarget)
2055 : : mMutex("WorkerPrivateParent::EventTarget::mMutex"),
2056 : mWorkerPrivate(aWorkerPrivate), mWeakNestedEventTarget(aNestedEventTarget),
2057 17 : mNestedEventTarget(aNestedEventTarget)
2058 : {
2059 17 : MOZ_ASSERT(aWorkerPrivate);
2060 17 : MOZ_ASSERT(aNestedEventTarget);
2061 17 : }
2062 :
2063 : void
2064 13 : Disable()
2065 : {
2066 26 : nsCOMPtr<nsIEventTarget> nestedEventTarget;
2067 : {
2068 26 : MutexAutoLock lock(mMutex);
2069 :
2070 : // Note, Disable() can be called more than once safely.
2071 13 : mWorkerPrivate = nullptr;
2072 13 : mNestedEventTarget.swap(nestedEventTarget);
2073 : }
2074 13 : }
2075 :
2076 : nsIEventTarget*
2077 13 : GetWeakNestedEventTarget() const
2078 : {
2079 13 : MOZ_ASSERT(mWeakNestedEventTarget);
2080 13 : return mWeakNestedEventTarget;
2081 : }
2082 :
2083 : NS_DECL_THREADSAFE_ISUPPORTS
2084 : NS_DECL_NSIEVENTTARGET_FULL
2085 :
2086 : private:
2087 13 : ~EventTarget()
2088 13 : { }
2089 : };
2090 :
2091 1 : WorkerLoadInfo::
2092 : InterfaceRequestor::InterfaceRequestor(nsIPrincipal* aPrincipal,
2093 1 : nsILoadGroup* aLoadGroup)
2094 : {
2095 1 : MOZ_ASSERT(NS_IsMainThread());
2096 1 : MOZ_ASSERT(aPrincipal);
2097 :
2098 : // Look for an existing LoadContext. This is optional and it's ok if
2099 : // we don't find one.
2100 2 : nsCOMPtr<nsILoadContext> baseContext;
2101 1 : if (aLoadGroup) {
2102 0 : nsCOMPtr<nsIInterfaceRequestor> callbacks;
2103 0 : aLoadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
2104 0 : if (callbacks) {
2105 0 : callbacks->GetInterface(NS_GET_IID(nsILoadContext),
2106 0 : getter_AddRefs(baseContext));
2107 : }
2108 0 : mOuterRequestor = callbacks;
2109 : }
2110 :
2111 2 : mLoadContext = new LoadContext(aPrincipal, baseContext);
2112 1 : }
2113 :
2114 : void
2115 1 : WorkerLoadInfo::
2116 : InterfaceRequestor::MaybeAddTabChild(nsILoadGroup* aLoadGroup)
2117 : {
2118 1 : MOZ_ASSERT(NS_IsMainThread());
2119 :
2120 1 : if (!aLoadGroup) {
2121 2 : return;
2122 : }
2123 :
2124 0 : nsCOMPtr<nsIInterfaceRequestor> callbacks;
2125 0 : aLoadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
2126 0 : if (!callbacks) {
2127 0 : return;
2128 : }
2129 :
2130 0 : nsCOMPtr<nsITabChild> tabChild;
2131 0 : callbacks->GetInterface(NS_GET_IID(nsITabChild), getter_AddRefs(tabChild));
2132 0 : if (!tabChild) {
2133 0 : return;
2134 : }
2135 :
2136 : // Use weak references to the tab child. Holding a strong reference will
2137 : // not prevent an ActorDestroy() from being called on the TabChild.
2138 : // Therefore, we should let the TabChild destroy itself as soon as possible.
2139 0 : mTabChildList.AppendElement(do_GetWeakReference(tabChild));
2140 : }
2141 :
2142 : NS_IMETHODIMP
2143 59 : WorkerLoadInfo::
2144 : InterfaceRequestor::GetInterface(const nsIID& aIID, void** aSink)
2145 : {
2146 59 : MOZ_ASSERT(NS_IsMainThread());
2147 59 : MOZ_ASSERT(mLoadContext);
2148 :
2149 59 : if (aIID.Equals(NS_GET_IID(nsILoadContext))) {
2150 102 : nsCOMPtr<nsILoadContext> ref = mLoadContext;
2151 51 : ref.forget(aSink);
2152 51 : return NS_OK;
2153 : }
2154 :
2155 : // If we still have an active nsITabChild, then return it. Its possible,
2156 : // though, that all of the TabChild objects have been destroyed. In that
2157 : // case we return NS_NOINTERFACE.
2158 8 : if (aIID.Equals(NS_GET_IID(nsITabChild))) {
2159 0 : nsCOMPtr<nsITabChild> tabChild = GetAnyLiveTabChild();
2160 0 : if (!tabChild) {
2161 0 : return NS_NOINTERFACE;
2162 : }
2163 0 : tabChild.forget(aSink);
2164 0 : return NS_OK;
2165 : }
2166 :
2167 8 : if (aIID.Equals(NS_GET_IID(nsINetworkInterceptController)) &&
2168 0 : mOuterRequestor) {
2169 : // If asked for the network intercept controller, ask the outer requestor,
2170 : // which could be the docshell.
2171 0 : return mOuterRequestor->GetInterface(aIID, aSink);
2172 : }
2173 :
2174 8 : return NS_NOINTERFACE;
2175 : }
2176 :
2177 : already_AddRefed<nsITabChild>
2178 0 : WorkerLoadInfo::
2179 : InterfaceRequestor::GetAnyLiveTabChild()
2180 : {
2181 0 : MOZ_ASSERT(NS_IsMainThread());
2182 :
2183 : // Search our list of known TabChild objects for one that still exists.
2184 0 : while (!mTabChildList.IsEmpty()) {
2185 : nsCOMPtr<nsITabChild> tabChild =
2186 0 : do_QueryReferent(mTabChildList.LastElement());
2187 :
2188 : // Does this tab child still exist? If so, return it. We are done. If the
2189 : // PBrowser actor is no longer useful, don't bother returning this tab.
2190 0 : if (tabChild && !static_cast<TabChild*>(tabChild.get())->IsDestroyed()) {
2191 0 : return tabChild.forget();
2192 : }
2193 :
2194 : // Otherwise remove the stale weak reference and check the next one
2195 0 : mTabChildList.RemoveElementAt(mTabChildList.Length() - 1);
2196 : }
2197 :
2198 0 : return nullptr;
2199 : }
2200 :
2201 134 : NS_IMPL_ADDREF(WorkerLoadInfo::InterfaceRequestor)
2202 132 : NS_IMPL_RELEASE(WorkerLoadInfo::InterfaceRequestor)
2203 73 : NS_IMPL_QUERY_INTERFACE(WorkerLoadInfo::InterfaceRequestor, nsIInterfaceRequestor)
2204 :
2205 : struct WorkerPrivate::TimeoutInfo
2206 : {
2207 0 : TimeoutInfo()
2208 0 : : mId(0), mIsInterval(false), mCanceled(false)
2209 : {
2210 0 : MOZ_COUNT_CTOR(mozilla::dom::workers::WorkerPrivate::TimeoutInfo);
2211 0 : }
2212 :
2213 0 : ~TimeoutInfo()
2214 0 : {
2215 0 : MOZ_COUNT_DTOR(mozilla::dom::workers::WorkerPrivate::TimeoutInfo);
2216 0 : }
2217 :
2218 0 : bool operator==(const TimeoutInfo& aOther)
2219 : {
2220 0 : return mTargetTime == aOther.mTargetTime;
2221 : }
2222 :
2223 0 : bool operator<(const TimeoutInfo& aOther)
2224 : {
2225 0 : return mTargetTime < aOther.mTargetTime;
2226 : }
2227 :
2228 : nsCOMPtr<nsIScriptTimeoutHandler> mHandler;
2229 : mozilla::TimeStamp mTargetTime;
2230 : mozilla::TimeDuration mInterval;
2231 : int32_t mId;
2232 : bool mIsInterval;
2233 : bool mCanceled;
2234 : };
2235 :
2236 : class WorkerJSContextStats final : public JS::RuntimeStats
2237 : {
2238 : const nsCString mRtPath;
2239 :
2240 : public:
2241 0 : explicit WorkerJSContextStats(const nsACString& aRtPath)
2242 0 : : JS::RuntimeStats(JsWorkerMallocSizeOf), mRtPath(aRtPath)
2243 0 : { }
2244 :
2245 0 : ~WorkerJSContextStats()
2246 0 : {
2247 0 : for (size_t i = 0; i != zoneStatsVector.length(); i++) {
2248 0 : delete static_cast<xpc::ZoneStatsExtras*>(zoneStatsVector[i].extra);
2249 : }
2250 :
2251 0 : for (size_t i = 0; i != compartmentStatsVector.length(); i++) {
2252 0 : delete static_cast<xpc::CompartmentStatsExtras*>(compartmentStatsVector[i].extra);
2253 : }
2254 0 : }
2255 :
2256 0 : const nsCString& Path() const
2257 : {
2258 0 : return mRtPath;
2259 : }
2260 :
2261 : virtual void
2262 0 : initExtraZoneStats(JS::Zone* aZone,
2263 : JS::ZoneStats* aZoneStats)
2264 : override
2265 : {
2266 0 : MOZ_ASSERT(!aZoneStats->extra);
2267 :
2268 : // ReportJSRuntimeExplicitTreeStats expects that
2269 : // aZoneStats->extra is a xpc::ZoneStatsExtras pointer.
2270 0 : xpc::ZoneStatsExtras* extras = new xpc::ZoneStatsExtras;
2271 0 : extras->pathPrefix = mRtPath;
2272 0 : extras->pathPrefix += nsPrintfCString("zone(0x%p)/", (void *)aZone);
2273 :
2274 0 : MOZ_ASSERT(StartsWithExplicit(extras->pathPrefix));
2275 :
2276 0 : aZoneStats->extra = extras;
2277 0 : }
2278 :
2279 : virtual void
2280 0 : initExtraCompartmentStats(JSCompartment* aCompartment,
2281 : JS::CompartmentStats* aCompartmentStats)
2282 : override
2283 : {
2284 0 : MOZ_ASSERT(!aCompartmentStats->extra);
2285 :
2286 : // ReportJSRuntimeExplicitTreeStats expects that
2287 : // aCompartmentStats->extra is a xpc::CompartmentStatsExtras pointer.
2288 0 : xpc::CompartmentStatsExtras* extras = new xpc::CompartmentStatsExtras;
2289 :
2290 : // This is the |jsPathPrefix|. Each worker has exactly two compartments:
2291 : // one for atoms, and one for everything else.
2292 0 : extras->jsPathPrefix.Assign(mRtPath);
2293 0 : extras->jsPathPrefix += nsPrintfCString("zone(0x%p)/",
2294 0 : (void *)js::GetCompartmentZone(aCompartment));
2295 0 : extras->jsPathPrefix += js::IsAtomsCompartment(aCompartment)
2296 0 : ? NS_LITERAL_CSTRING("compartment(web-worker-atoms)/")
2297 0 : : NS_LITERAL_CSTRING("compartment(web-worker)/");
2298 :
2299 : // This should never be used when reporting with workers (hence the "?!").
2300 0 : extras->domPathPrefix.AssignLiteral("explicit/workers/?!/");
2301 :
2302 0 : MOZ_ASSERT(StartsWithExplicit(extras->jsPathPrefix));
2303 0 : MOZ_ASSERT(StartsWithExplicit(extras->domPathPrefix));
2304 :
2305 0 : extras->location = nullptr;
2306 :
2307 0 : aCompartmentStats->extra = extras;
2308 0 : }
2309 : };
2310 :
2311 : class WorkerPrivate::MemoryReporter final : public nsIMemoryReporter
2312 : {
2313 : NS_DECL_THREADSAFE_ISUPPORTS
2314 :
2315 : friend class WorkerPrivate;
2316 :
2317 : SharedMutex mMutex;
2318 : WorkerPrivate* mWorkerPrivate;
2319 : bool mAlreadyMappedToAddon;
2320 :
2321 : public:
2322 1 : explicit MemoryReporter(WorkerPrivate* aWorkerPrivate)
2323 1 : : mMutex(aWorkerPrivate->mMutex), mWorkerPrivate(aWorkerPrivate),
2324 1 : mAlreadyMappedToAddon(false)
2325 : {
2326 1 : aWorkerPrivate->AssertIsOnWorkerThread();
2327 1 : }
2328 :
2329 : NS_IMETHOD
2330 : CollectReports(nsIHandleReportCallback* aHandleReport,
2331 : nsISupports* aData, bool aAnonymize) override;
2332 :
2333 : private:
2334 : class FinishCollectRunnable;
2335 :
2336 : class CollectReportsRunnable final : public MainThreadWorkerControlRunnable
2337 : {
2338 : RefPtr<FinishCollectRunnable> mFinishCollectRunnable;
2339 : const bool mAnonymize;
2340 :
2341 : public:
2342 : CollectReportsRunnable(
2343 : WorkerPrivate* aWorkerPrivate,
2344 : nsIHandleReportCallback* aHandleReport,
2345 : nsISupports* aHandlerData,
2346 : bool aAnonymize,
2347 : const nsACString& aPath);
2348 :
2349 : private:
2350 : bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override;
2351 :
2352 0 : ~CollectReportsRunnable()
2353 0 : {
2354 0 : if (NS_IsMainThread()) {
2355 0 : mFinishCollectRunnable->Run();
2356 0 : return;
2357 : }
2358 :
2359 0 : WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
2360 0 : MOZ_ASSERT(workerPrivate);
2361 0 : MOZ_ALWAYS_SUCCEEDS(
2362 : workerPrivate->DispatchToMainThread(mFinishCollectRunnable.forget()));
2363 0 : }
2364 : };
2365 :
2366 : class FinishCollectRunnable final : public Runnable
2367 : {
2368 : nsCOMPtr<nsIHandleReportCallback> mHandleReport;
2369 : nsCOMPtr<nsISupports> mHandlerData;
2370 : const bool mAnonymize;
2371 : bool mSuccess;
2372 :
2373 : public:
2374 : WorkerJSContextStats mCxStats;
2375 :
2376 : explicit FinishCollectRunnable(
2377 : nsIHandleReportCallback* aHandleReport,
2378 : nsISupports* aHandlerData,
2379 : bool aAnonymize,
2380 : const nsACString& aPath);
2381 :
2382 : NS_IMETHOD Run() override;
2383 :
2384 0 : void SetSuccess(bool success)
2385 : {
2386 0 : mSuccess = success;
2387 0 : }
2388 :
2389 : private:
2390 0 : ~FinishCollectRunnable()
2391 0 : {
2392 : // mHandleReport and mHandlerData are released on the main thread.
2393 0 : AssertIsOnMainThread();
2394 0 : }
2395 :
2396 : FinishCollectRunnable(const FinishCollectRunnable&) = delete;
2397 : FinishCollectRunnable& operator=(const FinishCollectRunnable&) = delete;
2398 : FinishCollectRunnable& operator=(const FinishCollectRunnable&&) = delete;
2399 : };
2400 :
2401 0 : ~MemoryReporter()
2402 0 : {
2403 0 : }
2404 :
2405 : void
2406 0 : Disable()
2407 : {
2408 : // Called from WorkerPrivate::DisableMemoryReporter.
2409 0 : mMutex.AssertCurrentThreadOwns();
2410 :
2411 0 : NS_ASSERTION(mWorkerPrivate, "Disabled more than once!");
2412 0 : mWorkerPrivate = nullptr;
2413 0 : }
2414 :
2415 : // Only call this from the main thread and under mMutex lock.
2416 : void
2417 : TryToMapAddon(nsACString &path);
2418 : };
2419 :
2420 4 : NS_IMPL_ISUPPORTS(WorkerPrivate::MemoryReporter, nsIMemoryReporter)
2421 :
2422 : NS_IMETHODIMP
2423 0 : WorkerPrivate::MemoryReporter::CollectReports(nsIHandleReportCallback* aHandleReport,
2424 : nsISupports* aData,
2425 : bool aAnonymize)
2426 : {
2427 0 : AssertIsOnMainThread();
2428 :
2429 0 : RefPtr<CollectReportsRunnable> runnable;
2430 :
2431 : {
2432 0 : MutexAutoLock lock(mMutex);
2433 :
2434 0 : if (!mWorkerPrivate) {
2435 : // This will effectively report 0 memory.
2436 : nsCOMPtr<nsIMemoryReporterManager> manager =
2437 0 : do_GetService("@mozilla.org/memory-reporter-manager;1");
2438 0 : if (manager) {
2439 0 : manager->EndReport();
2440 : }
2441 0 : return NS_OK;
2442 : }
2443 :
2444 0 : nsAutoCString path;
2445 0 : path.AppendLiteral("explicit/workers/workers(");
2446 0 : if (aAnonymize && !mWorkerPrivate->Domain().IsEmpty()) {
2447 0 : path.AppendLiteral("<anonymized-domain>)/worker(<anonymized-url>");
2448 : } else {
2449 0 : nsAutoCString escapedDomain(mWorkerPrivate->Domain());
2450 0 : if (escapedDomain.IsEmpty()) {
2451 0 : escapedDomain += "chrome";
2452 : } else {
2453 0 : escapedDomain.ReplaceChar('/', '\\');
2454 : }
2455 0 : path.Append(escapedDomain);
2456 0 : path.AppendLiteral(")/worker(");
2457 0 : NS_ConvertUTF16toUTF8 escapedURL(mWorkerPrivate->ScriptURL());
2458 0 : escapedURL.ReplaceChar('/', '\\');
2459 0 : path.Append(escapedURL);
2460 : }
2461 0 : path.AppendPrintf(", 0x%p)/", static_cast<void*>(mWorkerPrivate));
2462 :
2463 0 : TryToMapAddon(path);
2464 :
2465 : runnable =
2466 0 : new CollectReportsRunnable(mWorkerPrivate, aHandleReport, aData, aAnonymize, path);
2467 : }
2468 :
2469 0 : if (!runnable->Dispatch()) {
2470 0 : return NS_ERROR_UNEXPECTED;
2471 : }
2472 :
2473 0 : return NS_OK;
2474 : }
2475 :
2476 : void
2477 0 : WorkerPrivate::MemoryReporter::TryToMapAddon(nsACString &path)
2478 : {
2479 0 : AssertIsOnMainThread();
2480 0 : mMutex.AssertCurrentThreadOwns();
2481 :
2482 0 : if (mAlreadyMappedToAddon || !mWorkerPrivate) {
2483 0 : return;
2484 : }
2485 :
2486 0 : nsCOMPtr<nsIURI> scriptURI;
2487 0 : if (NS_FAILED(NS_NewURI(getter_AddRefs(scriptURI),
2488 : mWorkerPrivate->ScriptURL()))) {
2489 0 : return;
2490 : }
2491 :
2492 0 : mAlreadyMappedToAddon = true;
2493 :
2494 0 : if (!XRE_IsParentProcess()) {
2495 : // Only try to access the service from the main process.
2496 0 : return;
2497 : }
2498 :
2499 0 : nsAutoCString addonId;
2500 : bool ok;
2501 : nsCOMPtr<amIAddonManager> addonManager =
2502 0 : do_GetService("@mozilla.org/addons/integration;1");
2503 :
2504 0 : if (!addonManager ||
2505 0 : NS_FAILED(addonManager->MapURIToAddonID(scriptURI, addonId, &ok)) ||
2506 0 : !ok) {
2507 0 : return;
2508 : }
2509 :
2510 : static const size_t explicitLength = strlen("explicit/");
2511 0 : addonId.Insert(NS_LITERAL_CSTRING("add-ons/"), 0);
2512 0 : addonId += "/";
2513 0 : path.Insert(addonId, explicitLength);
2514 : }
2515 :
2516 0 : WorkerPrivate::MemoryReporter::CollectReportsRunnable::CollectReportsRunnable(
2517 : WorkerPrivate* aWorkerPrivate,
2518 : nsIHandleReportCallback* aHandleReport,
2519 : nsISupports* aHandlerData,
2520 : bool aAnonymize,
2521 0 : const nsACString& aPath)
2522 : : MainThreadWorkerControlRunnable(aWorkerPrivate),
2523 : mFinishCollectRunnable(
2524 0 : new FinishCollectRunnable(aHandleReport, aHandlerData, aAnonymize, aPath)),
2525 0 : mAnonymize(aAnonymize)
2526 0 : { }
2527 :
2528 : bool
2529 0 : WorkerPrivate::MemoryReporter::CollectReportsRunnable::WorkerRun(JSContext* aCx,
2530 : WorkerPrivate* aWorkerPrivate)
2531 : {
2532 0 : aWorkerPrivate->AssertIsOnWorkerThread();
2533 :
2534 0 : mFinishCollectRunnable->SetSuccess(
2535 0 : aWorkerPrivate->CollectRuntimeStats(&mFinishCollectRunnable->mCxStats, mAnonymize));
2536 :
2537 0 : return true;
2538 : }
2539 :
2540 0 : WorkerPrivate::MemoryReporter::FinishCollectRunnable::FinishCollectRunnable(
2541 : nsIHandleReportCallback* aHandleReport,
2542 : nsISupports* aHandlerData,
2543 : bool aAnonymize,
2544 0 : const nsACString& aPath)
2545 : : mozilla::Runnable(
2546 : "dom::workers::WorkerPrivate::MemoryReporter::FinishCollectRunnable")
2547 : , mHandleReport(aHandleReport)
2548 : , mHandlerData(aHandlerData)
2549 : , mAnonymize(aAnonymize)
2550 : , mSuccess(false)
2551 0 : , mCxStats(aPath)
2552 0 : { }
2553 :
2554 : NS_IMETHODIMP
2555 0 : WorkerPrivate::MemoryReporter::FinishCollectRunnable::Run()
2556 : {
2557 0 : AssertIsOnMainThread();
2558 :
2559 : nsCOMPtr<nsIMemoryReporterManager> manager =
2560 0 : do_GetService("@mozilla.org/memory-reporter-manager;1");
2561 :
2562 0 : if (!manager)
2563 0 : return NS_OK;
2564 :
2565 0 : if (mSuccess) {
2566 0 : xpc::ReportJSRuntimeExplicitTreeStats(mCxStats, mCxStats.Path(),
2567 : mHandleReport, mHandlerData,
2568 0 : mAnonymize);
2569 : }
2570 :
2571 0 : manager->EndReport();
2572 :
2573 0 : return NS_OK;
2574 : }
2575 :
2576 17 : WorkerPrivate::SyncLoopInfo::SyncLoopInfo(EventTarget* aEventTarget)
2577 : : mEventTarget(aEventTarget), mCompleted(false), mResult(false)
2578 : #ifdef DEBUG
2579 17 : , mHasRun(false)
2580 : #endif
2581 : {
2582 17 : }
2583 :
2584 : template <class Derived>
2585 : nsIDocument*
2586 16 : WorkerPrivateParent<Derived>::GetDocument() const
2587 : {
2588 16 : AssertIsOnMainThread();
2589 16 : if (mLoadInfo.mWindow) {
2590 0 : return mLoadInfo.mWindow->GetExtantDoc();
2591 : }
2592 : // if we don't have a document, we should query the document
2593 : // from the parent in case of a nested worker
2594 16 : WorkerPrivate* parent = mParent;
2595 16 : while (parent) {
2596 0 : if (parent->mLoadInfo.mWindow) {
2597 0 : return parent->mLoadInfo.mWindow->GetExtantDoc();
2598 : }
2599 0 : parent = parent->GetParent();
2600 : }
2601 : // couldn't query a document, give up and return nullptr
2602 16 : return nullptr;
2603 : }
2604 :
2605 : template <class Derived>
2606 : void
2607 0 : WorkerPrivateParent<Derived>::SetCSP(nsIContentSecurityPolicy* aCSP)
2608 : {
2609 0 : AssertIsOnMainThread();
2610 0 : if (!aCSP) {
2611 0 : return;
2612 : }
2613 0 : WorkerPrivate* self = ParentAsWorkerPrivate();
2614 0 : aCSP->EnsureEventTarget(self->mMainThreadEventTarget);
2615 0 : mLoadInfo.mCSP = aCSP;
2616 : }
2617 :
2618 : template <class Derived>
2619 : nsresult
2620 1 : WorkerPrivateParent<Derived>::SetCSPFromHeaderValues(const nsACString& aCSPHeaderValue,
2621 : const nsACString& aCSPReportOnlyHeaderValue)
2622 : {
2623 1 : AssertIsOnMainThread();
2624 1 : MOZ_DIAGNOSTIC_ASSERT(!mLoadInfo.mCSP);
2625 :
2626 2 : NS_ConvertASCIItoUTF16 cspHeaderValue(aCSPHeaderValue);
2627 2 : NS_ConvertASCIItoUTF16 cspROHeaderValue(aCSPReportOnlyHeaderValue);
2628 :
2629 2 : nsCOMPtr<nsIContentSecurityPolicy> csp;
2630 1 : nsresult rv = mLoadInfo.mPrincipal->EnsureCSP(nullptr, getter_AddRefs(csp));
2631 1 : if (!csp) {
2632 1 : return NS_OK;
2633 : }
2634 :
2635 0 : WorkerPrivate* self = ParentAsWorkerPrivate();
2636 0 : csp->EnsureEventTarget(self->mMainThreadEventTarget);
2637 :
2638 : // If there's a CSP header, apply it.
2639 0 : if (!cspHeaderValue.IsEmpty()) {
2640 0 : rv = CSP_AppendCSPFromHeader(csp, cspHeaderValue, false);
2641 0 : NS_ENSURE_SUCCESS(rv, rv);
2642 : }
2643 : // If there's a report-only CSP header, apply it.
2644 0 : if (!cspROHeaderValue.IsEmpty()) {
2645 0 : rv = CSP_AppendCSPFromHeader(csp, cspROHeaderValue, true);
2646 0 : NS_ENSURE_SUCCESS(rv, rv);
2647 : }
2648 :
2649 : // Set evalAllowed, default value is set in GetAllowsEval
2650 0 : bool evalAllowed = false;
2651 0 : bool reportEvalViolations = false;
2652 0 : rv = csp->GetAllowsEval(&reportEvalViolations, &evalAllowed);
2653 0 : NS_ENSURE_SUCCESS(rv, rv);
2654 :
2655 : // Set ReferrerPolicy, default value is set in GetReferrerPolicy
2656 0 : bool hasReferrerPolicy = false;
2657 0 : uint32_t rp = mozilla::net::RP_Unset;
2658 0 : rv = csp->GetReferrerPolicy(&rp, &hasReferrerPolicy);
2659 0 : NS_ENSURE_SUCCESS(rv, rv);
2660 :
2661 0 : mLoadInfo.mCSP = csp;
2662 0 : mLoadInfo.mEvalAllowed = evalAllowed;
2663 0 : mLoadInfo.mReportCSPViolations = reportEvalViolations;
2664 :
2665 0 : if (hasReferrerPolicy) {
2666 0 : mLoadInfo.mReferrerPolicy = static_cast<net::ReferrerPolicy>(rp);
2667 : }
2668 :
2669 0 : return NS_OK;
2670 : }
2671 :
2672 : template <class Derived>
2673 : void
2674 1 : WorkerPrivateParent<Derived>::SetReferrerPolicyFromHeaderValue(
2675 : const nsACString& aReferrerPolicyHeaderValue)
2676 : {
2677 1 : NS_ConvertUTF8toUTF16 headerValue(aReferrerPolicyHeaderValue);
2678 :
2679 1 : if (headerValue.IsEmpty()) {
2680 1 : return;
2681 : }
2682 :
2683 : net::ReferrerPolicy policy =
2684 0 : nsContentUtils::GetReferrerPolicyFromHeader(headerValue);
2685 0 : if (policy == net::RP_Unset) {
2686 0 : return;
2687 : }
2688 :
2689 0 : SetReferrerPolicy(policy);
2690 : }
2691 :
2692 :
2693 : // Can't use NS_IMPL_CYCLE_COLLECTION_CLASS(WorkerPrivateParent) because of the
2694 : // templates.
2695 : template <class Derived>
2696 : typename WorkerPrivateParent<Derived>::cycleCollection
2697 : WorkerPrivateParent<Derived>::_cycleCollectorGlobal =
2698 : WorkerPrivateParent<Derived>::cycleCollection();
2699 :
2700 : template <class Derived>
2701 1 : WorkerPrivateParent<Derived>::WorkerPrivateParent(
2702 : WorkerPrivate* aParent,
2703 : const nsAString& aScriptURL,
2704 : bool aIsChromeWorker,
2705 : WorkerType aWorkerType,
2706 : const nsAString& aWorkerName,
2707 : const nsACString& aServiceWorkerScope,
2708 : WorkerLoadInfo& aLoadInfo)
2709 : : mMutex("WorkerPrivateParent Mutex"),
2710 : mCondVar(mMutex, "WorkerPrivateParent CondVar"),
2711 : mParent(aParent), mScriptURL(aScriptURL),
2712 : mWorkerName(aWorkerName), mServiceWorkerScope(aServiceWorkerScope),
2713 : mLoadingWorkerScript(false), mBusyCount(0), mParentWindowPausedDepth(0),
2714 : mParentStatus(Pending), mParentFrozen(false),
2715 : mIsChromeWorker(aIsChromeWorker), mMainThreadObjectsForgotten(false),
2716 : mIsSecureContext(false), mWorkerType(aWorkerType),
2717 : mCreationTimeStamp(TimeStamp::Now()),
2718 1 : mCreationTimeHighRes((double)PR_Now() / PR_USEC_PER_MSEC)
2719 : {
2720 1 : MOZ_ASSERT_IF(!IsDedicatedWorker(), NS_IsMainThread());
2721 :
2722 1 : if (aLoadInfo.mWindow) {
2723 0 : AssertIsOnMainThread();
2724 0 : MOZ_ASSERT(aLoadInfo.mWindow->IsInnerWindow(),
2725 : "Should have inner window here!");
2726 0 : BindToOwner(aLoadInfo.mWindow);
2727 : }
2728 :
2729 1 : mLoadInfo.StealFrom(aLoadInfo);
2730 :
2731 1 : if (aParent) {
2732 0 : aParent->AssertIsOnWorkerThread();
2733 :
2734 : // Note that this copies our parent's secure context state into mJSSettings.
2735 0 : aParent->CopyJSSettings(mJSSettings);
2736 :
2737 : // And manually set our mIsSecureContext, though it's not really relevant to
2738 : // dedicated workers...
2739 0 : mIsSecureContext = aParent->IsSecureContext();
2740 0 : MOZ_ASSERT_IF(mIsChromeWorker, mIsSecureContext);
2741 :
2742 0 : MOZ_ASSERT(IsDedicatedWorker());
2743 :
2744 0 : if (aParent->mParentFrozen) {
2745 0 : Freeze(nullptr);
2746 : }
2747 : }
2748 : else {
2749 1 : AssertIsOnMainThread();
2750 :
2751 1 : RuntimeService::GetDefaultJSSettings(mJSSettings);
2752 :
2753 : // Our secure context state depends on the kind of worker we have.
2754 1 : if (UsesSystemPrincipal() || IsServiceWorker()) {
2755 1 : mIsSecureContext = true;
2756 0 : } else if (mLoadInfo.mWindow) {
2757 : // Shared and dedicated workers both inherit the loading window's secure
2758 : // context state. Shared workers then prevent windows with a different
2759 : // secure context state from attaching to them.
2760 0 : mIsSecureContext = mLoadInfo.mWindow->IsSecureContext();
2761 : } else {
2762 0 : MOZ_ASSERT_UNREACHABLE("non-chrome worker that is not a service worker "
2763 : "that has no parent and no associated window");
2764 : }
2765 :
2766 1 : if (mIsSecureContext) {
2767 1 : mJSSettings.chrome.compartmentOptions
2768 1 : .creationOptions().setSecureContext(true);
2769 1 : mJSSettings.content.compartmentOptions
2770 1 : .creationOptions().setSecureContext(true);
2771 : }
2772 :
2773 : // Our parent can get suspended after it initiates the async creation
2774 : // of a new worker thread. In this case suspend the new worker as well.
2775 1 : if (mLoadInfo.mWindow && mLoadInfo.mWindow->IsSuspended()) {
2776 0 : ParentWindowPaused();
2777 : }
2778 :
2779 1 : if (mLoadInfo.mWindow && mLoadInfo.mWindow->IsFrozen()) {
2780 0 : Freeze(mLoadInfo.mWindow);
2781 : }
2782 : }
2783 1 : }
2784 :
2785 : template <class Derived>
2786 0 : WorkerPrivateParent<Derived>::~WorkerPrivateParent()
2787 : {
2788 0 : DropJSObjects(this);
2789 0 : }
2790 :
2791 : template <class Derived>
2792 : JSObject*
2793 1 : WorkerPrivateParent<Derived>::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
2794 : {
2795 1 : MOZ_ASSERT(!IsSharedWorker(),
2796 : "We should never wrap a WorkerPrivate for a SharedWorker");
2797 :
2798 1 : AssertIsOnParentThread();
2799 :
2800 : // XXXkhuey this should not need to be rooted, the analysis is dumb.
2801 : // See bug 980181.
2802 : JS::Rooted<JSObject*> wrapper(aCx,
2803 2 : WorkerBinding::Wrap(aCx, ParentAsWorkerPrivate(), aGivenProto));
2804 1 : if (wrapper) {
2805 1 : MOZ_ALWAYS_TRUE(TryPreserveWrapper(wrapper));
2806 : }
2807 :
2808 2 : return wrapper;
2809 : }
2810 :
2811 : template <class Derived>
2812 : nsresult
2813 38 : WorkerPrivateParent<Derived>::DispatchPrivate(already_AddRefed<WorkerRunnable> aRunnable,
2814 : nsIEventTarget* aSyncLoopTarget)
2815 : {
2816 : // May be called on any thread!
2817 76 : RefPtr<WorkerRunnable> runnable(aRunnable);
2818 :
2819 38 : WorkerPrivate* self = ParentAsWorkerPrivate();
2820 :
2821 : {
2822 74 : MutexAutoLock lock(mMutex);
2823 :
2824 38 : MOZ_ASSERT_IF(aSyncLoopTarget, self->mThread);
2825 :
2826 38 : if (!self->mThread) {
2827 2 : if (ParentStatus() == Pending || self->mStatus == Pending) {
2828 2 : mPreStartRunnables.AppendElement(runnable);
2829 2 : return NS_OK;
2830 : }
2831 :
2832 0 : NS_WARNING("Using a worker event target after the thread has already"
2833 : "been released!");
2834 0 : return NS_ERROR_UNEXPECTED;
2835 : }
2836 :
2837 36 : if (self->mStatus == Dead ||
2838 0 : (!aSyncLoopTarget && ParentStatus() > Running)) {
2839 0 : NS_WARNING("A runnable was posted to a worker that is already shutting "
2840 : "down!");
2841 0 : return NS_ERROR_UNEXPECTED;
2842 : }
2843 :
2844 : nsresult rv;
2845 36 : if (aSyncLoopTarget) {
2846 36 : rv = aSyncLoopTarget->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
2847 : } else {
2848 0 : rv = self->mThread->DispatchAnyThread(WorkerThreadFriendKey(), runnable.forget());
2849 : }
2850 :
2851 36 : if (NS_WARN_IF(NS_FAILED(rv))) {
2852 0 : return rv;
2853 : }
2854 :
2855 36 : mCondVar.Notify();
2856 : }
2857 :
2858 36 : return NS_OK;
2859 : }
2860 :
2861 : template <class Derived>
2862 : void
2863 1 : WorkerPrivateParent<Derived>::EnableDebugger()
2864 : {
2865 1 : AssertIsOnParentThread();
2866 :
2867 1 : WorkerPrivate* self = ParentAsWorkerPrivate();
2868 :
2869 1 : if (NS_FAILED(RegisterWorkerDebugger(self))) {
2870 0 : NS_WARNING("Failed to register worker debugger!");
2871 0 : return;
2872 : }
2873 : }
2874 :
2875 : template <class Derived>
2876 : void
2877 0 : WorkerPrivateParent<Derived>::DisableDebugger()
2878 : {
2879 0 : AssertIsOnParentThread();
2880 :
2881 0 : WorkerPrivate* self = ParentAsWorkerPrivate();
2882 :
2883 0 : if (NS_FAILED(UnregisterWorkerDebugger(self))) {
2884 0 : NS_WARNING("Failed to unregister worker debugger!");
2885 : }
2886 0 : }
2887 :
2888 : template <class Derived>
2889 : nsresult
2890 0 : WorkerPrivateParent<Derived>::DispatchControlRunnable(
2891 : already_AddRefed<WorkerControlRunnable> aWorkerControlRunnable)
2892 : {
2893 : // May be called on any thread!
2894 0 : RefPtr<WorkerControlRunnable> runnable(aWorkerControlRunnable);
2895 0 : MOZ_ASSERT(runnable);
2896 :
2897 0 : WorkerPrivate* self = ParentAsWorkerPrivate();
2898 :
2899 : {
2900 0 : MutexAutoLock lock(mMutex);
2901 :
2902 0 : if (self->mStatus == Dead) {
2903 0 : return NS_ERROR_UNEXPECTED;
2904 : }
2905 :
2906 : // Transfer ownership to the control queue.
2907 0 : self->mControlQueue.Push(runnable.forget().take());
2908 :
2909 0 : if (JSContext* cx = self->mJSContext) {
2910 0 : MOZ_ASSERT(self->mThread);
2911 0 : JS_RequestInterruptCallback(cx);
2912 : }
2913 :
2914 0 : mCondVar.Notify();
2915 : }
2916 :
2917 0 : return NS_OK;
2918 : }
2919 :
2920 : template <class Derived>
2921 : nsresult
2922 0 : WorkerPrivateParent<Derived>::DispatchDebuggerRunnable(
2923 : already_AddRefed<WorkerRunnable> aDebuggerRunnable)
2924 : {
2925 : // May be called on any thread!
2926 :
2927 0 : RefPtr<WorkerRunnable> runnable(aDebuggerRunnable);
2928 :
2929 0 : MOZ_ASSERT(runnable);
2930 :
2931 0 : WorkerPrivate* self = ParentAsWorkerPrivate();
2932 :
2933 : {
2934 0 : MutexAutoLock lock(mMutex);
2935 :
2936 0 : if (self->mStatus == Dead) {
2937 0 : NS_WARNING("A debugger runnable was posted to a worker that is already "
2938 : "shutting down!");
2939 0 : return NS_ERROR_UNEXPECTED;
2940 : }
2941 :
2942 : // Transfer ownership to the debugger queue.
2943 0 : self->mDebuggerQueue.Push(runnable.forget().take());
2944 :
2945 0 : mCondVar.Notify();
2946 : }
2947 :
2948 0 : return NS_OK;
2949 : }
2950 :
2951 : template <class Derived>
2952 : already_AddRefed<WorkerRunnable>
2953 36 : WorkerPrivateParent<Derived>::MaybeWrapAsWorkerRunnable(already_AddRefed<nsIRunnable> aRunnable)
2954 : {
2955 : // May be called on any thread!
2956 :
2957 72 : nsCOMPtr<nsIRunnable> runnable(aRunnable);
2958 36 : MOZ_ASSERT(runnable);
2959 :
2960 : RefPtr<WorkerRunnable> workerRunnable =
2961 72 : WorkerRunnable::FromRunnable(runnable);
2962 36 : if (workerRunnable) {
2963 36 : return workerRunnable.forget();
2964 : }
2965 :
2966 0 : nsCOMPtr<nsICancelableRunnable> cancelable = do_QueryInterface(runnable);
2967 0 : if (!cancelable) {
2968 0 : MOZ_CRASH("All runnables destined for a worker thread must be cancelable!");
2969 : }
2970 :
2971 0 : workerRunnable =
2972 0 : new ExternalRunnableWrapper(ParentAsWorkerPrivate(), runnable);
2973 0 : return workerRunnable.forget();
2974 : }
2975 :
2976 : template <class Derived>
2977 : already_AddRefed<nsISerialEventTarget>
2978 1 : WorkerPrivateParent<Derived>::GetEventTarget()
2979 : {
2980 1 : WorkerPrivate* self = ParentAsWorkerPrivate();
2981 :
2982 2 : nsCOMPtr<nsISerialEventTarget> target;
2983 :
2984 1 : bool needAutoDisable = false;
2985 :
2986 : {
2987 2 : MutexAutoLock lock(mMutex);
2988 :
2989 1 : if (!mEventTarget) {
2990 1 : mEventTarget = new EventTarget(self);
2991 :
2992 : // If the worker is already shutting down then we want to
2993 : // immediately disable the event target. This will cause
2994 : // the Dispatch() method to fail, but the event target
2995 : // will still exist.
2996 1 : if (self->mStatus > Running) {
2997 0 : needAutoDisable = true;
2998 : }
2999 : }
3000 :
3001 1 : target = mEventTarget;
3002 : }
3003 :
3004 : // Make sure to call Disable() outside of the mutex since it
3005 : // also internally locks a mutex.
3006 1 : if (needAutoDisable) {
3007 0 : mEventTarget->Disable();
3008 : }
3009 :
3010 :
3011 1 : MOZ_DIAGNOSTIC_ASSERT(target);
3012 2 : return target.forget();
3013 : }
3014 :
3015 : template <class Derived>
3016 : bool
3017 1 : WorkerPrivateParent<Derived>::Start()
3018 : {
3019 : // May be called on any thread!
3020 : {
3021 1 : MutexAutoLock lock(mMutex);
3022 :
3023 1 : NS_ASSERTION(mParentStatus != Running, "How can this be?!");
3024 :
3025 1 : if (mParentStatus == Pending) {
3026 1 : mParentStatus = Running;
3027 1 : return true;
3028 : }
3029 : }
3030 :
3031 0 : return false;
3032 : }
3033 :
3034 : // aCx is null when called from the finalizer
3035 : template <class Derived>
3036 : bool
3037 0 : WorkerPrivateParent<Derived>::NotifyPrivate(Status aStatus)
3038 : {
3039 0 : AssertIsOnParentThread();
3040 :
3041 : bool pending;
3042 : {
3043 0 : MutexAutoLock lock(mMutex);
3044 :
3045 0 : if (mParentStatus >= aStatus) {
3046 0 : return true;
3047 : }
3048 :
3049 0 : pending = mParentStatus == Pending;
3050 0 : mParentStatus = aStatus;
3051 : }
3052 :
3053 0 : if (IsSharedWorker()) {
3054 0 : RuntimeService* runtime = RuntimeService::GetService();
3055 0 : MOZ_ASSERT(runtime);
3056 :
3057 0 : runtime->ForgetSharedWorker(ParentAsWorkerPrivate());
3058 : }
3059 :
3060 0 : if (pending) {
3061 0 : WorkerPrivate* self = ParentAsWorkerPrivate();
3062 :
3063 : #ifdef DEBUG
3064 : {
3065 : // Fake a thread here just so that our assertions don't go off for no
3066 : // reason.
3067 0 : nsIThread* currentThread = NS_GetCurrentThread();
3068 0 : MOZ_ASSERT(currentThread);
3069 :
3070 0 : MOZ_ASSERT(!self->mPRThread);
3071 0 : self->mPRThread = PRThreadFromThread(currentThread);
3072 0 : MOZ_ASSERT(self->mPRThread);
3073 : }
3074 : #endif
3075 :
3076 : // Worker never got a chance to run, go ahead and delete it.
3077 0 : self->ScheduleDeletion(WorkerPrivate::WorkerNeverRan);
3078 0 : return true;
3079 : }
3080 :
3081 0 : NS_ASSERTION(aStatus != Terminating || mQueuedRunnables.IsEmpty(),
3082 : "Shouldn't have anything queued!");
3083 :
3084 : // Anything queued will be discarded.
3085 0 : mQueuedRunnables.Clear();
3086 :
3087 : RefPtr<NotifyRunnable> runnable =
3088 0 : new NotifyRunnable(ParentAsWorkerPrivate(), aStatus);
3089 0 : return runnable->Dispatch();
3090 : }
3091 :
3092 : template <class Derived>
3093 : bool
3094 0 : WorkerPrivateParent<Derived>::Freeze(nsPIDOMWindowInner* aWindow)
3095 : {
3096 0 : AssertIsOnParentThread();
3097 :
3098 : // Shared workers are only frozen if all of their owning documents are
3099 : // frozen. It can happen that mSharedWorkers is empty but this thread has
3100 : // not been unregistered yet.
3101 0 : if ((IsSharedWorker() || IsServiceWorker()) && !mSharedWorkers.IsEmpty()) {
3102 0 : AssertIsOnMainThread();
3103 :
3104 0 : bool allFrozen = true;
3105 :
3106 0 : for (uint32_t i = 0; i < mSharedWorkers.Length(); ++i) {
3107 0 : if (aWindow && mSharedWorkers[i]->GetOwner() == aWindow) {
3108 : // Calling Freeze() may change the refcount, ensure that the worker
3109 : // outlives this call.
3110 0 : RefPtr<SharedWorker> kungFuDeathGrip = mSharedWorkers[i];
3111 :
3112 0 : kungFuDeathGrip->Freeze();
3113 : } else {
3114 0 : MOZ_ASSERT_IF(mSharedWorkers[i]->GetOwner() && aWindow,
3115 : !SameCOMIdentity(mSharedWorkers[i]->GetOwner(),
3116 : aWindow));
3117 0 : if (!mSharedWorkers[i]->IsFrozen()) {
3118 0 : allFrozen = false;
3119 : }
3120 : }
3121 : }
3122 :
3123 0 : if (!allFrozen || mParentFrozen) {
3124 0 : return true;
3125 : }
3126 : }
3127 :
3128 0 : mParentFrozen = true;
3129 :
3130 : {
3131 0 : MutexAutoLock lock(mMutex);
3132 :
3133 0 : if (mParentStatus >= Terminating) {
3134 0 : return true;
3135 : }
3136 : }
3137 :
3138 0 : DisableDebugger();
3139 :
3140 : RefPtr<FreezeRunnable> runnable =
3141 0 : new FreezeRunnable(ParentAsWorkerPrivate());
3142 0 : if (!runnable->Dispatch()) {
3143 0 : return false;
3144 : }
3145 :
3146 0 : return true;
3147 : }
3148 :
3149 : template <class Derived>
3150 : bool
3151 0 : WorkerPrivateParent<Derived>::Thaw(nsPIDOMWindowInner* aWindow)
3152 : {
3153 0 : AssertIsOnParentThread();
3154 :
3155 0 : MOZ_ASSERT(mParentFrozen);
3156 :
3157 : // Shared workers are resumed if any of their owning documents are thawed.
3158 : // It can happen that mSharedWorkers is empty but this thread has not been
3159 : // unregistered yet.
3160 0 : if ((IsSharedWorker() || IsServiceWorker()) && !mSharedWorkers.IsEmpty()) {
3161 0 : AssertIsOnMainThread();
3162 :
3163 0 : bool anyRunning = false;
3164 :
3165 0 : for (uint32_t i = 0; i < mSharedWorkers.Length(); ++i) {
3166 0 : if (aWindow && mSharedWorkers[i]->GetOwner() == aWindow) {
3167 : // Calling Thaw() may change the refcount, ensure that the worker
3168 : // outlives this call.
3169 0 : RefPtr<SharedWorker> kungFuDeathGrip = mSharedWorkers[i];
3170 :
3171 0 : kungFuDeathGrip->Thaw();
3172 0 : anyRunning = true;
3173 : } else {
3174 0 : MOZ_ASSERT_IF(mSharedWorkers[i]->GetOwner() && aWindow,
3175 : !SameCOMIdentity(mSharedWorkers[i]->GetOwner(),
3176 : aWindow));
3177 0 : if (!mSharedWorkers[i]->IsFrozen()) {
3178 0 : anyRunning = true;
3179 : }
3180 : }
3181 : }
3182 :
3183 0 : if (!anyRunning || !mParentFrozen) {
3184 0 : return true;
3185 : }
3186 : }
3187 :
3188 0 : MOZ_ASSERT(mParentFrozen);
3189 :
3190 0 : mParentFrozen = false;
3191 :
3192 : {
3193 0 : MutexAutoLock lock(mMutex);
3194 :
3195 0 : if (mParentStatus >= Terminating) {
3196 0 : return true;
3197 : }
3198 : }
3199 :
3200 0 : EnableDebugger();
3201 :
3202 : // Execute queued runnables before waking up the worker, otherwise the worker
3203 : // could post new messages before we run those that have been queued.
3204 0 : if (!IsParentWindowPaused() && !mQueuedRunnables.IsEmpty()) {
3205 0 : MOZ_ASSERT(IsDedicatedWorker());
3206 :
3207 0 : nsTArray<nsCOMPtr<nsIRunnable>> runnables;
3208 0 : mQueuedRunnables.SwapElements(runnables);
3209 :
3210 0 : for (uint32_t index = 0; index < runnables.Length(); index++) {
3211 0 : runnables[index]->Run();
3212 : }
3213 : }
3214 :
3215 : RefPtr<ThawRunnable> runnable =
3216 0 : new ThawRunnable(ParentAsWorkerPrivate());
3217 0 : if (!runnable->Dispatch()) {
3218 0 : return false;
3219 : }
3220 :
3221 0 : return true;
3222 : }
3223 :
3224 : template <class Derived>
3225 : void
3226 0 : WorkerPrivateParent<Derived>::ParentWindowPaused()
3227 : {
3228 0 : AssertIsOnMainThread();
3229 0 : MOZ_ASSERT_IF(IsDedicatedWorker(), mParentWindowPausedDepth == 0);
3230 0 : mParentWindowPausedDepth += 1;
3231 0 : }
3232 :
3233 : template <class Derived>
3234 : void
3235 0 : WorkerPrivateParent<Derived>::ParentWindowResumed()
3236 : {
3237 0 : AssertIsOnMainThread();
3238 :
3239 0 : MOZ_ASSERT(mParentWindowPausedDepth > 0);
3240 0 : MOZ_ASSERT_IF(IsDedicatedWorker(), mParentWindowPausedDepth == 1);
3241 0 : mParentWindowPausedDepth -= 1;
3242 0 : if (mParentWindowPausedDepth > 0) {
3243 0 : return;
3244 : }
3245 :
3246 : {
3247 0 : MutexAutoLock lock(mMutex);
3248 :
3249 0 : if (mParentStatus >= Terminating) {
3250 0 : return;
3251 : }
3252 : }
3253 :
3254 : // Execute queued runnables before waking up, otherwise the worker could post
3255 : // new messages before we run those that have been queued.
3256 0 : if (!IsFrozen() && !mQueuedRunnables.IsEmpty()) {
3257 0 : MOZ_ASSERT(IsDedicatedWorker());
3258 :
3259 0 : nsTArray<nsCOMPtr<nsIRunnable>> runnables;
3260 0 : mQueuedRunnables.SwapElements(runnables);
3261 :
3262 0 : for (uint32_t index = 0; index < runnables.Length(); index++) {
3263 0 : runnables[index]->Run();
3264 : }
3265 : }
3266 : }
3267 :
3268 : template <class Derived>
3269 : bool
3270 0 : WorkerPrivateParent<Derived>::Close()
3271 : {
3272 0 : mMutex.AssertCurrentThreadOwns();
3273 :
3274 0 : if (mParentStatus < Closing) {
3275 0 : mParentStatus = Closing;
3276 : }
3277 :
3278 0 : return true;
3279 : }
3280 :
3281 : template <class Derived>
3282 : bool
3283 3 : WorkerPrivateParent<Derived>::ModifyBusyCount(bool aIncrease)
3284 : {
3285 3 : AssertIsOnParentThread();
3286 :
3287 3 : NS_ASSERTION(aIncrease || mBusyCount, "Mismatched busy count mods!");
3288 :
3289 3 : if (aIncrease) {
3290 3 : mBusyCount++;
3291 3 : return true;
3292 : }
3293 :
3294 0 : if (--mBusyCount == 0) {
3295 :
3296 : bool shouldCancel;
3297 : {
3298 0 : MutexAutoLock lock(mMutex);
3299 0 : shouldCancel = mParentStatus == Terminating;
3300 : }
3301 :
3302 0 : if (shouldCancel && !Cancel()) {
3303 0 : return false;
3304 : }
3305 : }
3306 :
3307 0 : return true;
3308 : }
3309 :
3310 : template <class Derived>
3311 : bool
3312 0 : WorkerPrivateParent<Derived>::ProxyReleaseMainThreadObjects()
3313 : {
3314 0 : AssertIsOnParentThread();
3315 0 : MOZ_ASSERT(!mMainThreadObjectsForgotten);
3316 :
3317 0 : nsCOMPtr<nsILoadGroup> loadGroupToCancel;
3318 : // If we're not overriden, then do nothing here. Let the load group get
3319 : // handled in ForgetMainThreadObjects().
3320 0 : if (mLoadInfo.mInterfaceRequestor) {
3321 0 : mLoadInfo.mLoadGroup.swap(loadGroupToCancel);
3322 : }
3323 :
3324 : bool result = mLoadInfo.ProxyReleaseMainThreadObjects(ParentAsWorkerPrivate(),
3325 0 : loadGroupToCancel);
3326 :
3327 0 : mMainThreadObjectsForgotten = true;
3328 :
3329 0 : return result;
3330 : }
3331 :
3332 : template <class Derived>
3333 : void
3334 1 : WorkerPrivateParent<Derived>::PostMessageInternal(JSContext* aCx,
3335 : JS::Handle<JS::Value> aMessage,
3336 : const Sequence<JSObject*>& aTransferable,
3337 : ErrorResult& aRv)
3338 : {
3339 1 : AssertIsOnParentThread();
3340 :
3341 : {
3342 2 : MutexAutoLock lock(mMutex);
3343 1 : if (mParentStatus > Running) {
3344 0 : return;
3345 : }
3346 : }
3347 :
3348 2 : JS::Rooted<JS::Value> transferable(aCx, JS::UndefinedValue());
3349 :
3350 1 : aRv = nsContentUtils::CreateJSValueFromSequenceOfObject(aCx, aTransferable,
3351 : &transferable);
3352 1 : if (NS_WARN_IF(aRv.Failed())) {
3353 0 : return;
3354 : }
3355 :
3356 : RefPtr<MessageEventRunnable> runnable =
3357 2 : new MessageEventRunnable(ParentAsWorkerPrivate(),
3358 2 : WorkerRunnable::WorkerThreadModifyBusyCount);
3359 :
3360 2 : UniquePtr<AbstractTimelineMarker> start;
3361 2 : UniquePtr<AbstractTimelineMarker> end;
3362 2 : RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
3363 1 : bool isTimelineRecording = timelines && !timelines->IsEmpty();
3364 :
3365 1 : if (isTimelineRecording) {
3366 0 : start = MakeUnique<WorkerTimelineMarker>(NS_IsMainThread()
3367 : ? ProfileTimelineWorkerOperationType::SerializeDataOnMainThread
3368 : : ProfileTimelineWorkerOperationType::SerializeDataOffMainThread,
3369 : MarkerTracingType::START);
3370 : }
3371 :
3372 1 : runnable->Write(aCx, aMessage, transferable, JS::CloneDataPolicy(), aRv);
3373 :
3374 1 : if (isTimelineRecording) {
3375 0 : end = MakeUnique<WorkerTimelineMarker>(NS_IsMainThread()
3376 : ? ProfileTimelineWorkerOperationType::SerializeDataOnMainThread
3377 : : ProfileTimelineWorkerOperationType::SerializeDataOffMainThread,
3378 : MarkerTracingType::END);
3379 0 : timelines->AddMarkerForAllObservedDocShells(start);
3380 0 : timelines->AddMarkerForAllObservedDocShells(end);
3381 : }
3382 :
3383 1 : if (NS_WARN_IF(aRv.Failed())) {
3384 0 : return;
3385 : }
3386 :
3387 1 : if (!runnable->Dispatch()) {
3388 0 : aRv.Throw(NS_ERROR_FAILURE);
3389 : }
3390 : }
3391 :
3392 : template <class Derived>
3393 : void
3394 1 : WorkerPrivateParent<Derived>::PostMessage(
3395 : JSContext* aCx, JS::Handle<JS::Value> aMessage,
3396 : const Sequence<JSObject*>& aTransferable,
3397 : ErrorResult& aRv)
3398 : {
3399 1 : PostMessageInternal(aCx, aMessage, aTransferable, aRv);
3400 1 : }
3401 :
3402 : template <class Derived>
3403 : void
3404 0 : WorkerPrivateParent<Derived>::UpdateContextOptions(
3405 : const JS::ContextOptions& aContextOptions)
3406 : {
3407 0 : AssertIsOnParentThread();
3408 :
3409 : {
3410 0 : MutexAutoLock lock(mMutex);
3411 0 : mJSSettings.contextOptions = aContextOptions;
3412 : }
3413 :
3414 : RefPtr<UpdateContextOptionsRunnable> runnable =
3415 0 : new UpdateContextOptionsRunnable(ParentAsWorkerPrivate(), aContextOptions);
3416 0 : if (!runnable->Dispatch()) {
3417 0 : NS_WARNING("Failed to update worker context options!");
3418 : }
3419 0 : }
3420 :
3421 : template <class Derived>
3422 : void
3423 0 : WorkerPrivateParent<Derived>::UpdatePreference(WorkerPreference aPref, bool aValue)
3424 : {
3425 0 : AssertIsOnParentThread();
3426 0 : MOZ_ASSERT(aPref >= 0 && aPref < WORKERPREF_COUNT);
3427 :
3428 : RefPtr<UpdatePreferenceRunnable> runnable =
3429 0 : new UpdatePreferenceRunnable(ParentAsWorkerPrivate(), aPref, aValue);
3430 0 : if (!runnable->Dispatch()) {
3431 0 : NS_WARNING("Failed to update worker preferences!");
3432 : }
3433 0 : }
3434 :
3435 : template <class Derived>
3436 : void
3437 0 : WorkerPrivateParent<Derived>::UpdateLanguages(const nsTArray<nsString>& aLanguages)
3438 : {
3439 0 : AssertIsOnParentThread();
3440 :
3441 : RefPtr<UpdateLanguagesRunnable> runnable =
3442 0 : new UpdateLanguagesRunnable(ParentAsWorkerPrivate(), aLanguages);
3443 0 : if (!runnable->Dispatch()) {
3444 0 : NS_WARNING("Failed to update worker languages!");
3445 : }
3446 0 : }
3447 :
3448 : template <class Derived>
3449 : void
3450 0 : WorkerPrivateParent<Derived>::UpdateJSWorkerMemoryParameter(JSGCParamKey aKey,
3451 : uint32_t aValue)
3452 : {
3453 0 : AssertIsOnParentThread();
3454 :
3455 0 : bool found = false;
3456 :
3457 : {
3458 0 : MutexAutoLock lock(mMutex);
3459 0 : found = mJSSettings.ApplyGCSetting(aKey, aValue);
3460 : }
3461 :
3462 0 : if (found) {
3463 : RefPtr<UpdateJSWorkerMemoryParameterRunnable> runnable =
3464 0 : new UpdateJSWorkerMemoryParameterRunnable(ParentAsWorkerPrivate(), aKey,
3465 0 : aValue);
3466 0 : if (!runnable->Dispatch()) {
3467 0 : NS_WARNING("Failed to update memory parameter!");
3468 : }
3469 : }
3470 0 : }
3471 :
3472 : #ifdef JS_GC_ZEAL
3473 : template <class Derived>
3474 : void
3475 0 : WorkerPrivateParent<Derived>::UpdateGCZeal(uint8_t aGCZeal, uint32_t aFrequency)
3476 : {
3477 0 : AssertIsOnParentThread();
3478 :
3479 : {
3480 0 : MutexAutoLock lock(mMutex);
3481 0 : mJSSettings.gcZeal = aGCZeal;
3482 0 : mJSSettings.gcZealFrequency = aFrequency;
3483 : }
3484 :
3485 : RefPtr<UpdateGCZealRunnable> runnable =
3486 0 : new UpdateGCZealRunnable(ParentAsWorkerPrivate(), aGCZeal, aFrequency);
3487 0 : if (!runnable->Dispatch()) {
3488 0 : NS_WARNING("Failed to update worker gczeal!");
3489 : }
3490 0 : }
3491 : #endif
3492 :
3493 : template <class Derived>
3494 : void
3495 0 : WorkerPrivateParent<Derived>::GarbageCollect(bool aShrinking)
3496 : {
3497 0 : AssertIsOnParentThread();
3498 :
3499 : RefPtr<GarbageCollectRunnable> runnable =
3500 0 : new GarbageCollectRunnable(ParentAsWorkerPrivate(), aShrinking,
3501 0 : /* collectChildren = */ true);
3502 0 : if (!runnable->Dispatch()) {
3503 0 : NS_WARNING("Failed to GC worker!");
3504 : }
3505 0 : }
3506 :
3507 : template <class Derived>
3508 : void
3509 0 : WorkerPrivateParent<Derived>::CycleCollect(bool aDummy)
3510 : {
3511 0 : AssertIsOnParentThread();
3512 :
3513 : RefPtr<CycleCollectRunnable> runnable =
3514 0 : new CycleCollectRunnable(ParentAsWorkerPrivate(),
3515 0 : /* collectChildren = */ true);
3516 0 : if (!runnable->Dispatch()) {
3517 0 : NS_WARNING("Failed to CC worker!");
3518 : }
3519 0 : }
3520 :
3521 : template <class Derived>
3522 : void
3523 0 : WorkerPrivateParent<Derived>::OfflineStatusChangeEvent(bool aIsOffline)
3524 : {
3525 0 : AssertIsOnParentThread();
3526 :
3527 : RefPtr<OfflineStatusChangeRunnable> runnable =
3528 0 : new OfflineStatusChangeRunnable(ParentAsWorkerPrivate(), aIsOffline);
3529 0 : if (!runnable->Dispatch()) {
3530 0 : NS_WARNING("Failed to dispatch offline status change event!");
3531 : }
3532 0 : }
3533 :
3534 : void
3535 0 : WorkerPrivate::OfflineStatusChangeEventInternal(bool aIsOffline)
3536 : {
3537 0 : AssertIsOnWorkerThread();
3538 :
3539 : // The worker is already in this state. No need to dispatch an event.
3540 0 : if (mOnLine == !aIsOffline) {
3541 0 : return;
3542 : }
3543 :
3544 0 : for (uint32_t index = 0; index < mChildWorkers.Length(); ++index) {
3545 0 : mChildWorkers[index]->OfflineStatusChangeEvent(aIsOffline);
3546 : }
3547 :
3548 0 : mOnLine = !aIsOffline;
3549 0 : WorkerGlobalScope* globalScope = GlobalScope();
3550 0 : RefPtr<WorkerNavigator> nav = globalScope->GetExistingNavigator();
3551 0 : if (nav) {
3552 0 : nav->SetOnLine(mOnLine);
3553 : }
3554 :
3555 0 : nsString eventType;
3556 0 : if (aIsOffline) {
3557 0 : eventType.AssignLiteral("offline");
3558 : } else {
3559 0 : eventType.AssignLiteral("online");
3560 : }
3561 :
3562 0 : RefPtr<Event> event = NS_NewDOMEvent(globalScope, nullptr, nullptr);
3563 :
3564 0 : event->InitEvent(eventType, false, false);
3565 0 : event->SetTrusted(true);
3566 :
3567 0 : globalScope->DispatchDOMEvent(nullptr, event, nullptr, nullptr);
3568 : }
3569 :
3570 : template <class Derived>
3571 : void
3572 0 : WorkerPrivateParent<Derived>::MemoryPressure(bool aDummy)
3573 : {
3574 0 : AssertIsOnParentThread();
3575 :
3576 : RefPtr<MemoryPressureRunnable> runnable =
3577 0 : new MemoryPressureRunnable(ParentAsWorkerPrivate());
3578 0 : Unused << NS_WARN_IF(!runnable->Dispatch());
3579 0 : }
3580 :
3581 : template <class Derived>
3582 : bool
3583 0 : WorkerPrivateParent<Derived>::RegisterSharedWorker(SharedWorker* aSharedWorker,
3584 : MessagePort* aPort)
3585 : {
3586 0 : AssertIsOnMainThread();
3587 0 : MOZ_ASSERT(aSharedWorker);
3588 0 : MOZ_ASSERT(IsSharedWorker());
3589 0 : MOZ_ASSERT(!mSharedWorkers.Contains(aSharedWorker));
3590 :
3591 0 : if (IsSharedWorker()) {
3592 : RefPtr<MessagePortRunnable> runnable =
3593 0 : new MessagePortRunnable(ParentAsWorkerPrivate(), aPort);
3594 0 : if (!runnable->Dispatch()) {
3595 0 : return false;
3596 : }
3597 : }
3598 :
3599 0 : mSharedWorkers.AppendElement(aSharedWorker);
3600 :
3601 : // If there were other SharedWorker objects attached to this worker then they
3602 : // may all have been frozen and this worker would need to be thawed.
3603 0 : if (mSharedWorkers.Length() > 1 && IsFrozen() && !Thaw(nullptr)) {
3604 0 : return false;
3605 : }
3606 :
3607 0 : return true;
3608 : }
3609 :
3610 : template <class Derived>
3611 : void
3612 0 : WorkerPrivateParent<Derived>::BroadcastErrorToSharedWorkers(
3613 : JSContext* aCx,
3614 : const WorkerErrorReport* aReport,
3615 : bool aIsErrorEvent)
3616 : {
3617 0 : AssertIsOnMainThread();
3618 :
3619 0 : if (aIsErrorEvent && JSREPORT_IS_WARNING(aReport->mFlags)) {
3620 : // Don't fire any events anywhere. Just log to console.
3621 : // XXXbz should we log to all the consoles of all the relevant windows?
3622 0 : MOZ_ASSERT(aReport);
3623 0 : LogErrorToConsole(*aReport, 0);
3624 0 : return;
3625 : }
3626 :
3627 0 : AutoTArray<RefPtr<SharedWorker>, 10> sharedWorkers;
3628 0 : GetAllSharedWorkers(sharedWorkers);
3629 :
3630 0 : if (sharedWorkers.IsEmpty()) {
3631 0 : return;
3632 : }
3633 :
3634 0 : AutoTArray<WindowAction, 10> windowActions;
3635 : nsresult rv;
3636 :
3637 : // First fire the error event at all SharedWorker objects. This may include
3638 : // multiple objects in a single window as well as objects in different
3639 : // windows.
3640 0 : for (size_t index = 0; index < sharedWorkers.Length(); index++) {
3641 0 : RefPtr<SharedWorker>& sharedWorker = sharedWorkers[index];
3642 :
3643 : // May be null.
3644 0 : nsPIDOMWindowInner* window = sharedWorker->GetOwner();
3645 :
3646 0 : RefPtr<Event> event;
3647 :
3648 0 : if (aIsErrorEvent) {
3649 0 : RootedDictionary<ErrorEventInit> errorInit(aCx);
3650 0 : errorInit.mBubbles = false;
3651 0 : errorInit.mCancelable = true;
3652 0 : errorInit.mMessage = aReport->mMessage;
3653 0 : errorInit.mFilename = aReport->mFilename;
3654 0 : errorInit.mLineno = aReport->mLineNumber;
3655 0 : errorInit.mColno = aReport->mColumnNumber;
3656 :
3657 0 : event = ErrorEvent::Constructor(sharedWorker, NS_LITERAL_STRING("error"),
3658 : errorInit);
3659 : } else {
3660 0 : event = Event::Constructor(sharedWorker, NS_LITERAL_STRING("error"),
3661 : EventInit());
3662 : }
3663 :
3664 0 : if (!event) {
3665 0 : ThrowAndReport(window, NS_ERROR_UNEXPECTED);
3666 0 : continue;
3667 : }
3668 :
3669 0 : event->SetTrusted(true);
3670 :
3671 : bool defaultActionEnabled;
3672 0 : nsresult rv = sharedWorker->DispatchEvent(event, &defaultActionEnabled);
3673 0 : if (NS_FAILED(rv)) {
3674 0 : ThrowAndReport(window, rv);
3675 0 : continue;
3676 : }
3677 :
3678 0 : if (!aIsErrorEvent) {
3679 0 : continue;
3680 : }
3681 :
3682 0 : if (defaultActionEnabled) {
3683 : // Add the owning window to our list so that we will fire an error event
3684 : // at it later.
3685 0 : if (!windowActions.Contains(window)) {
3686 0 : windowActions.AppendElement(WindowAction(window));
3687 : }
3688 : } else {
3689 0 : size_t actionsIndex = windowActions.LastIndexOf(WindowAction(window));
3690 0 : if (actionsIndex != windowActions.NoIndex) {
3691 : // Any listener that calls preventDefault() will prevent the window from
3692 : // receiving the error event.
3693 0 : windowActions[actionsIndex].mDefaultAction = false;
3694 : }
3695 : }
3696 : }
3697 :
3698 : // If there are no windows to consider further then we're done.
3699 0 : if (windowActions.IsEmpty()) {
3700 0 : return;
3701 : }
3702 :
3703 0 : bool shouldLogErrorToConsole = true;
3704 :
3705 : // Now fire error events at all the windows remaining.
3706 0 : for (uint32_t index = 0; index < windowActions.Length(); index++) {
3707 0 : WindowAction& windowAction = windowActions[index];
3708 :
3709 : // If there is no window or the script already called preventDefault then
3710 : // skip this window.
3711 0 : if (!windowAction.mWindow || !windowAction.mDefaultAction) {
3712 0 : continue;
3713 : }
3714 :
3715 : nsCOMPtr<nsIScriptGlobalObject> sgo =
3716 0 : do_QueryInterface(windowAction.mWindow);
3717 0 : MOZ_ASSERT(sgo);
3718 :
3719 0 : MOZ_ASSERT(NS_IsMainThread());
3720 0 : RootedDictionary<ErrorEventInit> init(aCx);
3721 0 : init.mLineno = aReport->mLineNumber;
3722 0 : init.mFilename = aReport->mFilename;
3723 0 : init.mMessage = aReport->mMessage;
3724 0 : init.mCancelable = true;
3725 0 : init.mBubbles = true;
3726 :
3727 0 : nsEventStatus status = nsEventStatus_eIgnore;
3728 0 : rv = sgo->HandleScriptError(init, &status);
3729 0 : if (NS_FAILED(rv)) {
3730 0 : ThrowAndReport(windowAction.mWindow, rv);
3731 0 : continue;
3732 : }
3733 :
3734 0 : if (status == nsEventStatus_eConsumeNoDefault) {
3735 0 : shouldLogErrorToConsole = false;
3736 : }
3737 : }
3738 :
3739 : // Finally log a warning in the console if no window tried to prevent it.
3740 0 : if (shouldLogErrorToConsole) {
3741 0 : MOZ_ASSERT(aReport);
3742 0 : LogErrorToConsole(*aReport, 0);
3743 : }
3744 : }
3745 :
3746 : template <class Derived>
3747 : void
3748 0 : WorkerPrivateParent<Derived>::GetAllSharedWorkers(
3749 : nsTArray<RefPtr<SharedWorker>>& aSharedWorkers)
3750 : {
3751 0 : AssertIsOnMainThread();
3752 0 : MOZ_ASSERT(IsSharedWorker() || IsServiceWorker());
3753 :
3754 0 : if (!aSharedWorkers.IsEmpty()) {
3755 0 : aSharedWorkers.Clear();
3756 : }
3757 :
3758 0 : for (uint32_t i = 0; i < mSharedWorkers.Length(); ++i) {
3759 0 : aSharedWorkers.AppendElement(mSharedWorkers[i]);
3760 : }
3761 0 : }
3762 :
3763 : template <class Derived>
3764 : void
3765 0 : WorkerPrivateParent<Derived>::CloseSharedWorkersForWindow(
3766 : nsPIDOMWindowInner* aWindow)
3767 : {
3768 0 : AssertIsOnMainThread();
3769 0 : MOZ_ASSERT(IsSharedWorker() || IsServiceWorker());
3770 0 : MOZ_ASSERT(aWindow);
3771 :
3772 0 : bool someRemoved = false;
3773 :
3774 0 : for (uint32_t i = 0; i < mSharedWorkers.Length();) {
3775 0 : if (mSharedWorkers[i]->GetOwner() == aWindow) {
3776 0 : mSharedWorkers[i]->Close();
3777 0 : mSharedWorkers.RemoveElementAt(i);
3778 0 : someRemoved = true;
3779 : } else {
3780 0 : MOZ_ASSERT(!SameCOMIdentity(mSharedWorkers[i]->GetOwner(),
3781 : aWindow));
3782 0 : ++i;
3783 : }
3784 : }
3785 :
3786 0 : if (!someRemoved) {
3787 0 : return;
3788 : }
3789 :
3790 : // If there are still SharedWorker objects attached to this worker then they
3791 : // may all be frozen and this worker would need to be frozen. Otherwise,
3792 : // if that was the last SharedWorker then it's time to cancel this worker.
3793 :
3794 0 : if (!mSharedWorkers.IsEmpty()) {
3795 0 : Freeze(nullptr);
3796 : } else {
3797 0 : Cancel();
3798 : }
3799 : }
3800 :
3801 : template <class Derived>
3802 : void
3803 0 : WorkerPrivateParent<Derived>::CloseAllSharedWorkers()
3804 : {
3805 0 : AssertIsOnMainThread();
3806 0 : MOZ_ASSERT(IsSharedWorker() || IsServiceWorker());
3807 :
3808 0 : for (uint32_t i = 0; i < mSharedWorkers.Length(); ++i) {
3809 0 : mSharedWorkers[i]->Close();
3810 : }
3811 :
3812 0 : mSharedWorkers.Clear();
3813 :
3814 0 : Cancel();
3815 0 : }
3816 :
3817 : template <class Derived>
3818 : void
3819 1 : WorkerPrivateParent<Derived>::WorkerScriptLoaded()
3820 : {
3821 1 : AssertIsOnMainThread();
3822 :
3823 1 : if (IsSharedWorker() || IsServiceWorker()) {
3824 : // No longer need to hold references to the window or document we came from.
3825 0 : mLoadInfo.mWindow = nullptr;
3826 0 : mLoadInfo.mScriptContext = nullptr;
3827 : }
3828 1 : }
3829 :
3830 : template <class Derived>
3831 : void
3832 1 : WorkerPrivateParent<Derived>::SetBaseURI(nsIURI* aBaseURI)
3833 : {
3834 1 : AssertIsOnMainThread();
3835 :
3836 1 : if (!mLoadInfo.mBaseURI) {
3837 0 : NS_ASSERTION(GetParent(), "Shouldn't happen without a parent!");
3838 0 : mLoadInfo.mResolvedScriptURI = aBaseURI;
3839 : }
3840 :
3841 1 : mLoadInfo.mBaseURI = aBaseURI;
3842 :
3843 1 : if (NS_FAILED(aBaseURI->GetSpec(mLocationInfo.mHref))) {
3844 0 : mLocationInfo.mHref.Truncate();
3845 : }
3846 :
3847 1 : mLocationInfo.mHostname.Truncate();
3848 1 : nsContentUtils::GetHostOrIPv6WithBrackets(aBaseURI, mLocationInfo.mHostname);
3849 :
3850 2 : nsCOMPtr<nsIURL> url(do_QueryInterface(aBaseURI));
3851 1 : if (!url || NS_FAILED(url->GetFilePath(mLocationInfo.mPathname))) {
3852 0 : mLocationInfo.mPathname.Truncate();
3853 : }
3854 :
3855 2 : nsCString temp;
3856 :
3857 1 : if (url && NS_SUCCEEDED(url->GetQuery(temp)) && !temp.IsEmpty()) {
3858 0 : mLocationInfo.mSearch.Assign('?');
3859 0 : mLocationInfo.mSearch.Append(temp);
3860 : }
3861 :
3862 1 : if (NS_SUCCEEDED(aBaseURI->GetRef(temp)) && !temp.IsEmpty()) {
3863 0 : if (mLocationInfo.mHash.IsEmpty()) {
3864 0 : mLocationInfo.mHash.Assign('#');
3865 0 : mLocationInfo.mHash.Append(temp);
3866 : }
3867 : }
3868 :
3869 1 : if (NS_SUCCEEDED(aBaseURI->GetScheme(mLocationInfo.mProtocol))) {
3870 1 : mLocationInfo.mProtocol.Append(':');
3871 : }
3872 : else {
3873 0 : mLocationInfo.mProtocol.Truncate();
3874 : }
3875 :
3876 : int32_t port;
3877 1 : if (NS_SUCCEEDED(aBaseURI->GetPort(&port)) && port != -1) {
3878 0 : mLocationInfo.mPort.AppendInt(port);
3879 :
3880 0 : nsAutoCString host(mLocationInfo.mHostname);
3881 0 : host.Append(':');
3882 0 : host.Append(mLocationInfo.mPort);
3883 :
3884 0 : mLocationInfo.mHost.Assign(host);
3885 : }
3886 : else {
3887 1 : mLocationInfo.mHost.Assign(mLocationInfo.mHostname);
3888 : }
3889 :
3890 1 : nsContentUtils::GetUTFOrigin(aBaseURI, mLocationInfo.mOrigin);
3891 1 : }
3892 :
3893 : template <class Derived>
3894 : nsresult
3895 0 : WorkerPrivateParent<Derived>::SetPrincipalOnMainThread(nsIPrincipal* aPrincipal,
3896 : nsILoadGroup* aLoadGroup)
3897 : {
3898 0 : return mLoadInfo.SetPrincipalOnMainThread(aPrincipal, aLoadGroup);
3899 : }
3900 :
3901 : template <class Derived>
3902 : nsresult
3903 1 : WorkerPrivateParent<Derived>::SetPrincipalFromChannel(nsIChannel* aChannel)
3904 : {
3905 1 : return mLoadInfo.SetPrincipalFromChannel(aChannel);
3906 : }
3907 :
3908 : #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
3909 : template <class Derived>
3910 : bool
3911 1 : WorkerPrivateParent<Derived>::FinalChannelPrincipalIsValid(nsIChannel* aChannel)
3912 : {
3913 1 : return mLoadInfo.FinalChannelPrincipalIsValid(aChannel);
3914 : }
3915 :
3916 : template <class Derived>
3917 : bool
3918 1 : WorkerPrivateParent<Derived>::PrincipalURIMatchesScriptURL()
3919 : {
3920 1 : return mLoadInfo.PrincipalURIMatchesScriptURL();
3921 : }
3922 : #endif
3923 :
3924 : template <class Derived>
3925 : void
3926 0 : WorkerPrivateParent<Derived>::UpdateOverridenLoadGroup(nsILoadGroup* aBaseLoadGroup)
3927 : {
3928 0 : AssertIsOnMainThread();
3929 :
3930 : // The load group should have been overriden at init time.
3931 0 : mLoadInfo.mInterfaceRequestor->MaybeAddTabChild(aBaseLoadGroup);
3932 0 : }
3933 :
3934 : template <class Derived>
3935 : void
3936 0 : WorkerPrivateParent<Derived>::FlushReportsToSharedWorkers(
3937 : nsIConsoleReportCollector* aReporter)
3938 : {
3939 0 : AssertIsOnMainThread();
3940 :
3941 0 : AutoTArray<RefPtr<SharedWorker>, 10> sharedWorkers;
3942 0 : AutoTArray<WindowAction, 10> windowActions;
3943 0 : GetAllSharedWorkers(sharedWorkers);
3944 :
3945 : // First find out all the shared workers' window.
3946 0 : for (size_t index = 0; index < sharedWorkers.Length(); index++) {
3947 0 : RefPtr<SharedWorker>& sharedWorker = sharedWorkers[index];
3948 :
3949 : // May be null.
3950 0 : nsPIDOMWindowInner* window = sharedWorker->GetOwner();
3951 :
3952 : // Add the owning window to our list so that we will flush the reports later.
3953 0 : if (window && !windowActions.Contains(window)) {
3954 0 : windowActions.AppendElement(WindowAction(window));
3955 : }
3956 : }
3957 :
3958 0 : bool reportErrorToBrowserConsole = true;
3959 :
3960 : // Flush the reports.
3961 0 : for (uint32_t index = 0; index < windowActions.Length(); index++) {
3962 0 : WindowAction& windowAction = windowActions[index];
3963 :
3964 0 : aReporter->FlushReportsToConsole(
3965 0 : windowAction.mWindow->WindowID(),
3966 : nsIConsoleReportCollector::ReportAction::Save);
3967 0 : reportErrorToBrowserConsole = false;
3968 : }
3969 :
3970 : // Finally report to browser console if there is no any window or shared
3971 : // worker.
3972 0 : if (reportErrorToBrowserConsole) {
3973 0 : aReporter->FlushReportsToConsole(0);
3974 0 : return;
3975 : }
3976 :
3977 0 : aReporter->ClearConsoleReports();
3978 : }
3979 :
3980 : template <class Derived>
3981 6 : NS_IMPL_ADDREF_INHERITED(WorkerPrivateParent<Derived>, DOMEventTargetHelper)
3982 :
3983 : template <class Derived>
3984 4 : NS_IMPL_RELEASE_INHERITED(WorkerPrivateParent<Derived>, DOMEventTargetHelper)
3985 :
3986 : template <class Derived>
3987 37 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(WorkerPrivateParent<Derived>)
3988 20 : NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
3989 :
3990 : template <class Derived>
3991 2 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(WorkerPrivateParent<Derived>,
3992 : DOMEventTargetHelper)
3993 2 : tmp->AssertIsOnParentThread();
3994 :
3995 : // The WorkerPrivate::mSelfRef has a reference to itself, which is really
3996 : // held by the worker thread. We traverse this reference if and only if our
3997 : // busy count is zero and we have not released the main thread reference.
3998 : // We do not unlink it. This allows the CC to break cycles involving the
3999 : // WorkerPrivate and begin shutting it down (which does happen in unlink) but
4000 : // ensures that the WorkerPrivate won't be deleted before we're done shutting
4001 : // down the thread.
4002 :
4003 2 : if (!tmp->mBusyCount && !tmp->mMainThreadObjectsForgotten) {
4004 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSelfRef)
4005 : }
4006 :
4007 : // The various strong references in LoadInfo are managed manually and cannot
4008 : // be cycle collected.
4009 2 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
4010 :
4011 : template <class Derived>
4012 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(WorkerPrivateParent<Derived>,
4013 : DOMEventTargetHelper)
4014 0 : tmp->Terminate();
4015 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
4016 :
4017 : template <class Derived>
4018 5 : NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(WorkerPrivateParent<Derived>,
4019 : DOMEventTargetHelper)
4020 5 : NS_IMPL_CYCLE_COLLECTION_TRACE_END
4021 :
4022 : #ifdef DEBUG
4023 :
4024 : template <class Derived>
4025 : void
4026 16 : WorkerPrivateParent<Derived>::AssertIsOnParentThread() const
4027 : {
4028 16 : if (GetParent()) {
4029 0 : GetParent()->AssertIsOnWorkerThread();
4030 : }
4031 : else {
4032 16 : AssertIsOnMainThread();
4033 : }
4034 16 : }
4035 :
4036 : template <class Derived>
4037 : void
4038 0 : WorkerPrivateParent<Derived>::AssertInnerWindowIsCorrect() const
4039 : {
4040 0 : AssertIsOnParentThread();
4041 :
4042 : // Only care about top level workers from windows.
4043 0 : if (mParent || !mLoadInfo.mWindow) {
4044 0 : return;
4045 : }
4046 :
4047 0 : AssertIsOnMainThread();
4048 :
4049 0 : nsPIDOMWindowOuter* outer = mLoadInfo.mWindow->GetOuterWindow();
4050 0 : NS_ASSERTION(outer && outer->GetCurrentInnerWindow() == mLoadInfo.mWindow,
4051 : "Inner window no longer correct!");
4052 : }
4053 :
4054 : #endif
4055 :
4056 : #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
4057 : template <class Derived>
4058 : bool
4059 1 : WorkerPrivateParent<Derived>::PrincipalIsValid() const
4060 : {
4061 1 : return mLoadInfo.PrincipalIsValid();
4062 : }
4063 : #endif
4064 :
4065 : class PostDebuggerMessageRunnable final : public Runnable
4066 : {
4067 : WorkerDebugger *mDebugger;
4068 : nsString mMessage;
4069 :
4070 : public:
4071 0 : PostDebuggerMessageRunnable(WorkerDebugger* aDebugger,
4072 : const nsAString& aMessage)
4073 0 : : mozilla::Runnable("PostDebuggerMessageRunnable")
4074 : , mDebugger(aDebugger)
4075 0 : , mMessage(aMessage)
4076 : {
4077 0 : }
4078 :
4079 : private:
4080 0 : ~PostDebuggerMessageRunnable()
4081 0 : { }
4082 :
4083 : NS_IMETHOD
4084 0 : Run() override
4085 : {
4086 0 : mDebugger->PostMessageToDebuggerOnMainThread(mMessage);
4087 :
4088 0 : return NS_OK;
4089 : }
4090 : };
4091 :
4092 : class ReportDebuggerErrorRunnable final : public Runnable
4093 : {
4094 : WorkerDebugger *mDebugger;
4095 : nsString mFilename;
4096 : uint32_t mLineno;
4097 : nsString mMessage;
4098 :
4099 : public:
4100 0 : ReportDebuggerErrorRunnable(WorkerDebugger* aDebugger,
4101 : const nsAString& aFilename,
4102 : uint32_t aLineno,
4103 : const nsAString& aMessage)
4104 0 : : mozilla::Runnable("ReportDebuggerErrorRunnable")
4105 : , mDebugger(aDebugger)
4106 : , mFilename(aFilename)
4107 : , mLineno(aLineno)
4108 0 : , mMessage(aMessage)
4109 : {
4110 0 : }
4111 :
4112 : private:
4113 0 : ~ReportDebuggerErrorRunnable()
4114 0 : { }
4115 :
4116 : NS_IMETHOD
4117 0 : Run() override
4118 : {
4119 0 : mDebugger->ReportErrorToDebuggerOnMainThread(mFilename, mLineno, mMessage);
4120 :
4121 0 : return NS_OK;
4122 : }
4123 : };
4124 :
4125 1 : WorkerDebugger::WorkerDebugger(WorkerPrivate* aWorkerPrivate)
4126 : : mWorkerPrivate(aWorkerPrivate),
4127 1 : mIsInitialized(false)
4128 : {
4129 1 : AssertIsOnMainThread();
4130 1 : }
4131 :
4132 0 : WorkerDebugger::~WorkerDebugger()
4133 : {
4134 0 : MOZ_ASSERT(!mWorkerPrivate);
4135 :
4136 0 : if (!NS_IsMainThread()) {
4137 0 : for (size_t index = 0; index < mListeners.Length(); ++index) {
4138 : NS_ReleaseOnMainThread(
4139 0 : "WorkerDebugger::mListeners", mListeners[index].forget());
4140 : }
4141 : }
4142 0 : }
4143 :
4144 3 : NS_IMPL_ISUPPORTS(WorkerDebugger, nsIWorkerDebugger)
4145 :
4146 : NS_IMETHODIMP
4147 0 : WorkerDebugger::GetIsClosed(bool* aResult)
4148 : {
4149 0 : AssertIsOnMainThread();
4150 :
4151 0 : *aResult = !mWorkerPrivate;
4152 0 : return NS_OK;
4153 : }
4154 :
4155 : NS_IMETHODIMP
4156 0 : WorkerDebugger::GetIsChrome(bool* aResult)
4157 : {
4158 0 : AssertIsOnMainThread();
4159 :
4160 0 : if (!mWorkerPrivate) {
4161 0 : return NS_ERROR_UNEXPECTED;
4162 : }
4163 :
4164 0 : *aResult = mWorkerPrivate->IsChromeWorker();
4165 0 : return NS_OK;
4166 : }
4167 :
4168 : NS_IMETHODIMP
4169 0 : WorkerDebugger::GetIsInitialized(bool* aResult)
4170 : {
4171 0 : AssertIsOnMainThread();
4172 :
4173 0 : if (!mWorkerPrivate) {
4174 0 : return NS_ERROR_UNEXPECTED;
4175 : }
4176 :
4177 0 : *aResult = mIsInitialized;
4178 0 : return NS_OK;
4179 : }
4180 :
4181 : NS_IMETHODIMP
4182 0 : WorkerDebugger::GetParent(nsIWorkerDebugger** aResult)
4183 : {
4184 0 : AssertIsOnMainThread();
4185 :
4186 0 : if (!mWorkerPrivate) {
4187 0 : return NS_ERROR_UNEXPECTED;
4188 : }
4189 :
4190 0 : WorkerPrivate* parent = mWorkerPrivate->GetParent();
4191 0 : if (!parent) {
4192 0 : *aResult = nullptr;
4193 0 : return NS_OK;
4194 : }
4195 :
4196 0 : MOZ_ASSERT(mWorkerPrivate->IsDedicatedWorker());
4197 :
4198 0 : nsCOMPtr<nsIWorkerDebugger> debugger = parent->Debugger();
4199 0 : debugger.forget(aResult);
4200 0 : return NS_OK;
4201 : }
4202 :
4203 : NS_IMETHODIMP
4204 0 : WorkerDebugger::GetType(uint32_t* aResult)
4205 : {
4206 0 : AssertIsOnMainThread();
4207 :
4208 0 : if (!mWorkerPrivate) {
4209 0 : return NS_ERROR_UNEXPECTED;
4210 : }
4211 :
4212 0 : *aResult = mWorkerPrivate->Type();
4213 0 : return NS_OK;
4214 : }
4215 :
4216 : NS_IMETHODIMP
4217 0 : WorkerDebugger::GetUrl(nsAString& aResult)
4218 : {
4219 0 : AssertIsOnMainThread();
4220 :
4221 0 : if (!mWorkerPrivate) {
4222 0 : return NS_ERROR_UNEXPECTED;
4223 : }
4224 :
4225 0 : aResult = mWorkerPrivate->ScriptURL();
4226 0 : return NS_OK;
4227 : }
4228 :
4229 : NS_IMETHODIMP
4230 0 : WorkerDebugger::GetWindow(mozIDOMWindow** aResult)
4231 : {
4232 0 : AssertIsOnMainThread();
4233 :
4234 0 : if (!mWorkerPrivate) {
4235 0 : return NS_ERROR_UNEXPECTED;
4236 : }
4237 :
4238 0 : if (mWorkerPrivate->GetParent() || !mWorkerPrivate->IsDedicatedWorker()) {
4239 0 : *aResult = nullptr;
4240 0 : return NS_OK;
4241 : }
4242 :
4243 0 : nsCOMPtr<nsPIDOMWindowInner> window = mWorkerPrivate->GetWindow();
4244 0 : window.forget(aResult);
4245 0 : return NS_OK;
4246 : }
4247 :
4248 : NS_IMETHODIMP
4249 0 : WorkerDebugger::GetPrincipal(nsIPrincipal** aResult)
4250 : {
4251 0 : AssertIsOnMainThread();
4252 0 : MOZ_ASSERT(aResult);
4253 :
4254 0 : if (!mWorkerPrivate) {
4255 0 : return NS_ERROR_UNEXPECTED;
4256 : }
4257 :
4258 0 : nsCOMPtr<nsIPrincipal> prin = mWorkerPrivate->GetPrincipal();
4259 0 : prin.forget(aResult);
4260 :
4261 0 : return NS_OK;
4262 : }
4263 :
4264 : NS_IMETHODIMP
4265 0 : WorkerDebugger::GetServiceWorkerID(uint32_t* aResult)
4266 : {
4267 0 : AssertIsOnMainThread();
4268 0 : MOZ_ASSERT(aResult);
4269 :
4270 0 : if (!mWorkerPrivate || !mWorkerPrivate->IsServiceWorker()) {
4271 0 : return NS_ERROR_UNEXPECTED;
4272 : }
4273 :
4274 0 : *aResult = mWorkerPrivate->ServiceWorkerID();
4275 0 : return NS_OK;
4276 : }
4277 :
4278 : NS_IMETHODIMP
4279 0 : WorkerDebugger::Initialize(const nsAString& aURL)
4280 : {
4281 0 : AssertIsOnMainThread();
4282 :
4283 0 : if (!mWorkerPrivate) {
4284 0 : return NS_ERROR_UNEXPECTED;
4285 : }
4286 :
4287 0 : if (!mIsInitialized) {
4288 : RefPtr<CompileDebuggerScriptRunnable> runnable =
4289 0 : new CompileDebuggerScriptRunnable(mWorkerPrivate, aURL);
4290 0 : if (!runnable->Dispatch()) {
4291 0 : return NS_ERROR_FAILURE;
4292 : }
4293 :
4294 0 : mIsInitialized = true;
4295 : }
4296 :
4297 0 : return NS_OK;
4298 : }
4299 :
4300 : NS_IMETHODIMP
4301 0 : WorkerDebugger::PostMessageMoz(const nsAString& aMessage)
4302 : {
4303 0 : AssertIsOnMainThread();
4304 :
4305 0 : if (!mWorkerPrivate || !mIsInitialized) {
4306 0 : return NS_ERROR_UNEXPECTED;
4307 : }
4308 :
4309 : RefPtr<DebuggerMessageEventRunnable> runnable =
4310 0 : new DebuggerMessageEventRunnable(mWorkerPrivate, aMessage);
4311 0 : if (!runnable->Dispatch()) {
4312 0 : return NS_ERROR_FAILURE;
4313 : }
4314 :
4315 0 : return NS_OK;
4316 : }
4317 :
4318 : NS_IMETHODIMP
4319 0 : WorkerDebugger::AddListener(nsIWorkerDebuggerListener* aListener)
4320 : {
4321 0 : AssertIsOnMainThread();
4322 :
4323 0 : if (mListeners.Contains(aListener)) {
4324 0 : return NS_ERROR_INVALID_ARG;
4325 : }
4326 :
4327 0 : mListeners.AppendElement(aListener);
4328 0 : return NS_OK;
4329 : }
4330 :
4331 : NS_IMETHODIMP
4332 0 : WorkerDebugger::RemoveListener(nsIWorkerDebuggerListener* aListener)
4333 : {
4334 0 : AssertIsOnMainThread();
4335 :
4336 0 : if (!mListeners.Contains(aListener)) {
4337 0 : return NS_ERROR_INVALID_ARG;
4338 : }
4339 :
4340 0 : mListeners.RemoveElement(aListener);
4341 0 : return NS_OK;
4342 : }
4343 :
4344 : void
4345 0 : WorkerDebugger::Close()
4346 : {
4347 0 : MOZ_ASSERT(mWorkerPrivate);
4348 0 : mWorkerPrivate = nullptr;
4349 :
4350 0 : nsTArray<nsCOMPtr<nsIWorkerDebuggerListener>> listeners(mListeners);
4351 0 : for (size_t index = 0; index < listeners.Length(); ++index) {
4352 0 : listeners[index]->OnClose();
4353 : }
4354 0 : }
4355 :
4356 : void
4357 0 : WorkerDebugger::PostMessageToDebugger(const nsAString& aMessage)
4358 : {
4359 0 : mWorkerPrivate->AssertIsOnWorkerThread();
4360 :
4361 : RefPtr<PostDebuggerMessageRunnable> runnable =
4362 0 : new PostDebuggerMessageRunnable(this, aMessage);
4363 0 : if (NS_FAILED(mWorkerPrivate->DispatchToMainThread(runnable.forget()))) {
4364 0 : NS_WARNING("Failed to post message to debugger on main thread!");
4365 : }
4366 0 : }
4367 :
4368 : void
4369 0 : WorkerDebugger::PostMessageToDebuggerOnMainThread(const nsAString& aMessage)
4370 : {
4371 0 : AssertIsOnMainThread();
4372 :
4373 0 : nsTArray<nsCOMPtr<nsIWorkerDebuggerListener>> listeners(mListeners);
4374 0 : for (size_t index = 0; index < listeners.Length(); ++index) {
4375 0 : listeners[index]->OnMessage(aMessage);
4376 : }
4377 0 : }
4378 :
4379 : void
4380 0 : WorkerDebugger::ReportErrorToDebugger(const nsAString& aFilename,
4381 : uint32_t aLineno,
4382 : const nsAString& aMessage)
4383 : {
4384 0 : mWorkerPrivate->AssertIsOnWorkerThread();
4385 :
4386 : RefPtr<ReportDebuggerErrorRunnable> runnable =
4387 0 : new ReportDebuggerErrorRunnable(this, aFilename, aLineno, aMessage);
4388 0 : if (NS_FAILED(mWorkerPrivate->DispatchToMainThread(runnable.forget()))) {
4389 0 : NS_WARNING("Failed to report error to debugger on main thread!");
4390 : }
4391 0 : }
4392 :
4393 : void
4394 0 : WorkerDebugger::ReportErrorToDebuggerOnMainThread(const nsAString& aFilename,
4395 : uint32_t aLineno,
4396 : const nsAString& aMessage)
4397 : {
4398 0 : AssertIsOnMainThread();
4399 :
4400 0 : nsTArray<nsCOMPtr<nsIWorkerDebuggerListener>> listeners(mListeners);
4401 0 : for (size_t index = 0; index < listeners.Length(); ++index) {
4402 0 : listeners[index]->OnError(aFilename, aLineno, aMessage);
4403 : }
4404 :
4405 0 : WorkerErrorReport report;
4406 0 : report.mMessage = aMessage;
4407 0 : report.mFilename = aFilename;
4408 0 : LogErrorToConsole(report, 0);
4409 0 : }
4410 :
4411 1 : WorkerPrivate::WorkerPrivate(WorkerPrivate* aParent,
4412 : const nsAString& aScriptURL,
4413 : bool aIsChromeWorker, WorkerType aWorkerType,
4414 : const nsAString& aWorkerName,
4415 : const nsACString& aServiceWorkerScope,
4416 1 : WorkerLoadInfo& aLoadInfo)
4417 : : WorkerPrivateParent<WorkerPrivate>(aParent, aScriptURL,
4418 : aIsChromeWorker, aWorkerType,
4419 : aWorkerName, aServiceWorkerScope,
4420 : aLoadInfo)
4421 : , mDebuggerRegistered(false)
4422 : , mDebugger(nullptr)
4423 : , mJSContext(nullptr)
4424 : , mPRThread(nullptr)
4425 : , mNumHoldersPreventingShutdownStart(0)
4426 : , mDebuggerEventLoopLevel(0)
4427 : , mMainThreadEventTarget(GetMainThreadEventTarget())
4428 1 : , mWorkerControlEventTarget(new WorkerControlEventTarget(this))
4429 : , mErrorHandlerRecursionCount(0)
4430 : , mNextTimeoutId(1)
4431 : , mStatus(Pending)
4432 : , mFrozen(false)
4433 : , mTimerRunning(false)
4434 : , mRunningExpiredTimeouts(false)
4435 : , mPendingEventQueueClearing(false)
4436 : , mCancelAllPendingRunnables(false)
4437 : , mPeriodicGCTimerRunning(false)
4438 : , mIdleGCTimerRunning(false)
4439 : , mWorkerScriptExecutedSuccessfully(false)
4440 : , mFetchHandlerWasAdded(false)
4441 2 : , mOnLine(false)
4442 : {
4443 1 : if (aParent) {
4444 0 : aParent->AssertIsOnWorkerThread();
4445 0 : aParent->GetAllPreferences(mPreferences);
4446 0 : mOnLine = aParent->OnLine();
4447 : }
4448 : else {
4449 1 : AssertIsOnMainThread();
4450 1 : RuntimeService::GetDefaultPreferences(mPreferences);
4451 1 : mOnLine = !NS_IsOffline();
4452 : }
4453 :
4454 2 : nsCOMPtr<nsISerialEventTarget> target;
4455 :
4456 : // A child worker just inherits the parent workers ThrottledEventQueue
4457 : // and main thread target for now. This is mainly due to the restriction
4458 : // that ThrottledEventQueue can only be created on the main thread at the
4459 : // moment.
4460 1 : if (aParent) {
4461 0 : mMainThreadThrottledEventQueue = aParent->mMainThreadThrottledEventQueue;
4462 0 : mMainThreadEventTarget = aParent->mMainThreadEventTarget;
4463 0 : return;
4464 : }
4465 :
4466 1 : MOZ_ASSERT(NS_IsMainThread());
4467 1 : target = GetWindow() ? GetWindow()->EventTargetFor(TaskCategory::Worker) : nullptr;
4468 :
4469 1 : if (!target) {
4470 1 : target = GetMainThreadSerialEventTarget();
4471 1 : MOZ_DIAGNOSTIC_ASSERT(target);
4472 : }
4473 :
4474 : // Throttle events to the main thread using a ThrottledEventQueue specific to
4475 : // this worker thread. This may return nullptr during shutdown.
4476 1 : mMainThreadThrottledEventQueue = ThrottledEventQueue::Create(target);
4477 :
4478 : // If we were able to creat the throttled event queue, then use it for
4479 : // dispatching our main thread runnables. Otherwise use our underlying
4480 : // base target.
4481 1 : if (mMainThreadThrottledEventQueue) {
4482 1 : mMainThreadEventTarget = mMainThreadThrottledEventQueue;
4483 : } else {
4484 0 : mMainThreadEventTarget = target.forget();
4485 : }
4486 : }
4487 :
4488 0 : WorkerPrivate::~WorkerPrivate()
4489 : {
4490 0 : mWorkerControlEventTarget->ForgetWorkerPrivate(this);
4491 0 : }
4492 :
4493 : // static
4494 : already_AddRefed<WorkerPrivate>
4495 0 : WorkerPrivate::Constructor(const GlobalObject& aGlobal,
4496 : const nsAString& aScriptURL,
4497 : const WorkerOptions& aOptions,
4498 : ErrorResult& aRv)
4499 : {
4500 : return WorkerPrivate::Constructor(aGlobal, aScriptURL, false,
4501 : WorkerTypeDedicated,
4502 0 : aOptions.mName, nullptr, aRv);
4503 : }
4504 :
4505 : // static
4506 : bool
4507 2 : WorkerPrivate::WorkerAvailable(JSContext* aCx, JSObject* /* unused */)
4508 : {
4509 : // If we're already on a worker workers are clearly enabled.
4510 2 : if (!NS_IsMainThread()) {
4511 1 : return true;
4512 : }
4513 :
4514 : // If our caller is chrome, workers are always available.
4515 1 : if (nsContentUtils::IsSystemCaller(aCx)) {
4516 1 : return true;
4517 : }
4518 :
4519 : // Else check the pref.
4520 0 : return Preferences::GetBool(PREF_WORKERS_ENABLED);
4521 : }
4522 :
4523 : // static
4524 : already_AddRefed<ChromeWorkerPrivate>
4525 1 : ChromeWorkerPrivate::Constructor(const GlobalObject& aGlobal,
4526 : const nsAString& aScriptURL,
4527 : ErrorResult& aRv)
4528 : {
4529 2 : return WorkerPrivate::Constructor(aGlobal, aScriptURL, true,
4530 1 : WorkerTypeDedicated, EmptyString(),
4531 : nullptr, aRv)
4532 2 : .downcast<ChromeWorkerPrivate>();
4533 : }
4534 :
4535 : // static
4536 : bool
4537 4 : ChromeWorkerPrivate::WorkerAvailable(JSContext* aCx, JSObject* /* unused */)
4538 : {
4539 : // Chrome is always allowed to use workers, and content is never
4540 : // allowed to use ChromeWorker, so all we have to check is the
4541 : // caller. However, chrome workers apparently might not have a
4542 : // system principal, so we have to check for them manually.
4543 4 : if (NS_IsMainThread()) {
4544 3 : return nsContentUtils::IsSystemCaller(aCx);
4545 : }
4546 :
4547 1 : return GetWorkerPrivateFromContext(aCx)->IsChromeWorker();
4548 : }
4549 :
4550 : // static
4551 : already_AddRefed<WorkerPrivate>
4552 1 : WorkerPrivate::Constructor(const GlobalObject& aGlobal,
4553 : const nsAString& aScriptURL,
4554 : bool aIsChromeWorker, WorkerType aWorkerType,
4555 : const nsAString& aWorkerName,
4556 : WorkerLoadInfo* aLoadInfo, ErrorResult& aRv)
4557 : {
4558 1 : JSContext* cx = aGlobal.Context();
4559 : return Constructor(cx, aScriptURL, aIsChromeWorker, aWorkerType,
4560 1 : aWorkerName, NullCString(), aLoadInfo, aRv);
4561 : }
4562 :
4563 : // static
4564 : already_AddRefed<WorkerPrivate>
4565 1 : WorkerPrivate::Constructor(JSContext* aCx,
4566 : const nsAString& aScriptURL,
4567 : bool aIsChromeWorker, WorkerType aWorkerType,
4568 : const nsAString& aWorkerName,
4569 : const nsACString& aServiceWorkerScope,
4570 : WorkerLoadInfo* aLoadInfo, ErrorResult& aRv)
4571 : {
4572 : // If this is a sub-worker, we need to keep the parent worker alive until this
4573 : // one is registered.
4574 2 : UniquePtr<SimpleWorkerHolder> holder;
4575 :
4576 1 : WorkerPrivate* parent = NS_IsMainThread() ?
4577 : nullptr :
4578 1 : GetCurrentThreadWorkerPrivate();
4579 1 : if (parent) {
4580 0 : parent->AssertIsOnWorkerThread();
4581 :
4582 0 : holder.reset(new SimpleWorkerHolder());
4583 0 : if (!holder->HoldWorker(parent, Canceling)) {
4584 0 : aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
4585 0 : return nullptr;
4586 : }
4587 : } else {
4588 1 : AssertIsOnMainThread();
4589 : }
4590 :
4591 2 : Maybe<WorkerLoadInfo> stackLoadInfo;
4592 1 : if (!aLoadInfo) {
4593 1 : stackLoadInfo.emplace();
4594 :
4595 1 : nsresult rv = GetLoadInfo(aCx, nullptr, parent, aScriptURL,
4596 : aIsChromeWorker, InheritLoadGroup,
4597 1 : aWorkerType, stackLoadInfo.ptr());
4598 1 : aRv.MightThrowJSException();
4599 1 : if (NS_FAILED(rv)) {
4600 0 : scriptloader::ReportLoadError(aRv, rv, aScriptURL);
4601 0 : return nullptr;
4602 : }
4603 :
4604 1 : aLoadInfo = stackLoadInfo.ptr();
4605 : }
4606 :
4607 : // NB: This has to be done before creating the WorkerPrivate, because it will
4608 : // attempt to use static variables that are initialized in the RuntimeService
4609 : // constructor.
4610 : RuntimeService* runtimeService;
4611 :
4612 1 : if (!parent) {
4613 1 : runtimeService = RuntimeService::GetOrCreateService();
4614 1 : if (!runtimeService) {
4615 0 : aRv.Throw(NS_ERROR_FAILURE);
4616 0 : return nullptr;
4617 : }
4618 : }
4619 : else {
4620 0 : runtimeService = RuntimeService::GetService();
4621 : }
4622 :
4623 1 : MOZ_ASSERT(runtimeService);
4624 :
4625 : RefPtr<WorkerPrivate> worker =
4626 : new WorkerPrivate(parent, aScriptURL, aIsChromeWorker,
4627 : aWorkerType, aWorkerName, aServiceWorkerScope,
4628 2 : *aLoadInfo);
4629 :
4630 : // Gecko contexts always have an explicitly-set default locale (set by
4631 : // XPJSRuntime::Initialize for the main thread, set by
4632 : // WorkerThreadPrimaryRunnable::Run for workers just before running worker
4633 : // code), so this is never SpiderMonkey's builtin default locale.
4634 2 : JS::UniqueChars defaultLocale = JS_GetDefaultLocale(aCx);
4635 1 : if (NS_WARN_IF(!defaultLocale)) {
4636 0 : aRv.Throw(NS_ERROR_UNEXPECTED);
4637 0 : return nullptr;
4638 : }
4639 :
4640 1 : worker->mDefaultLocale = Move(defaultLocale);
4641 :
4642 1 : if (!runtimeService->RegisterWorker(worker)) {
4643 0 : aRv.Throw(NS_ERROR_UNEXPECTED);
4644 0 : return nullptr;
4645 : }
4646 :
4647 1 : worker->EnableDebugger();
4648 :
4649 1 : MOZ_DIAGNOSTIC_ASSERT(worker->PrincipalIsValid());
4650 :
4651 : RefPtr<CompileScriptRunnable> compiler =
4652 3 : new CompileScriptRunnable(worker, aScriptURL);
4653 1 : if (!compiler->Dispatch()) {
4654 0 : aRv.Throw(NS_ERROR_UNEXPECTED);
4655 0 : return nullptr;
4656 : }
4657 :
4658 1 : worker->mSelfRef = worker;
4659 :
4660 1 : return worker.forget();
4661 : }
4662 :
4663 : // static
4664 : nsresult
4665 1 : WorkerPrivate::GetLoadInfo(JSContext* aCx, nsPIDOMWindowInner* aWindow,
4666 : WorkerPrivate* aParent, const nsAString& aScriptURL,
4667 : bool aIsChromeWorker,
4668 : LoadGroupBehavior aLoadGroupBehavior,
4669 : WorkerType aWorkerType,
4670 : WorkerLoadInfo* aLoadInfo)
4671 : {
4672 : using namespace mozilla::dom::workers::scriptloader;
4673 :
4674 1 : MOZ_ASSERT(aCx);
4675 1 : MOZ_ASSERT_IF(NS_IsMainThread(), aCx == nsContentUtils::GetCurrentJSContext());
4676 :
4677 1 : if (aWindow) {
4678 0 : AssertIsOnMainThread();
4679 : }
4680 :
4681 2 : WorkerLoadInfo loadInfo;
4682 : nsresult rv;
4683 :
4684 1 : if (aParent) {
4685 0 : aParent->AssertIsOnWorkerThread();
4686 :
4687 : // If the parent is going away give up now.
4688 : Status parentStatus;
4689 : {
4690 0 : MutexAutoLock lock(aParent->mMutex);
4691 0 : parentStatus = aParent->mStatus;
4692 : }
4693 :
4694 0 : if (parentStatus > Running) {
4695 0 : return NS_ERROR_FAILURE;
4696 : }
4697 :
4698 : // Passing a pointer to our stack loadInfo is safe here because this
4699 : // method uses a sync runnable to get the channel from the main thread.
4700 0 : rv = ChannelFromScriptURLWorkerThread(aCx, aParent, aScriptURL,
4701 : loadInfo);
4702 0 : if (NS_FAILED(rv)) {
4703 0 : MOZ_ALWAYS_TRUE(loadInfo.ProxyReleaseMainThreadObjects(aParent));
4704 0 : return rv;
4705 : }
4706 :
4707 : // Now that we've spun the loop there's no guarantee that our parent is
4708 : // still alive. We may have received control messages initiating shutdown.
4709 : {
4710 0 : MutexAutoLock lock(aParent->mMutex);
4711 0 : parentStatus = aParent->mStatus;
4712 : }
4713 :
4714 0 : if (parentStatus > Running) {
4715 0 : MOZ_ALWAYS_TRUE(loadInfo.ProxyReleaseMainThreadObjects(aParent));
4716 0 : return NS_ERROR_FAILURE;
4717 : }
4718 :
4719 0 : loadInfo.mDomain = aParent->Domain();
4720 0 : loadInfo.mFromWindow = aParent->IsFromWindow();
4721 0 : loadInfo.mWindowID = aParent->WindowID();
4722 0 : loadInfo.mStorageAllowed = aParent->IsStorageAllowed();
4723 0 : loadInfo.mOriginAttributes = aParent->GetOriginAttributes();
4724 0 : loadInfo.mServiceWorkersTestingInWindow =
4725 0 : aParent->ServiceWorkersTestingInWindow();
4726 : } else {
4727 1 : AssertIsOnMainThread();
4728 :
4729 : // Make sure that the IndexedDatabaseManager is set up
4730 1 : Unused << NS_WARN_IF(!IndexedDatabaseManager::GetOrCreate());
4731 :
4732 1 : nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
4733 1 : MOZ_ASSERT(ssm);
4734 :
4735 1 : bool isChrome = nsContentUtils::IsSystemCaller(aCx);
4736 :
4737 : // First check to make sure the caller has permission to make a privileged
4738 : // worker if they called the ChromeWorker/ChromeSharedWorker constructor.
4739 1 : if (aIsChromeWorker && !isChrome) {
4740 0 : return NS_ERROR_DOM_SECURITY_ERR;
4741 : }
4742 :
4743 : // Chrome callers (whether creating a ChromeWorker or Worker) always get the
4744 : // system principal here as they're allowed to load anything. The script
4745 : // loader will refuse to run any script that does not also have the system
4746 : // principal.
4747 1 : if (isChrome) {
4748 1 : rv = ssm->GetSystemPrincipal(getter_AddRefs(loadInfo.mPrincipal));
4749 1 : NS_ENSURE_SUCCESS(rv, rv);
4750 :
4751 1 : loadInfo.mPrincipalIsSystem = true;
4752 : }
4753 :
4754 : // See if we're being called from a window.
4755 2 : nsCOMPtr<nsPIDOMWindowInner> globalWindow = aWindow;
4756 1 : if (!globalWindow) {
4757 : nsCOMPtr<nsIScriptGlobalObject> scriptGlobal =
4758 2 : nsJSUtils::GetStaticScriptGlobal(JS::CurrentGlobalOrNull(aCx));
4759 1 : if (scriptGlobal) {
4760 0 : globalWindow = do_QueryInterface(scriptGlobal);
4761 0 : MOZ_ASSERT(globalWindow);
4762 : }
4763 : }
4764 :
4765 2 : nsCOMPtr<nsIDocument> document;
4766 :
4767 1 : if (globalWindow) {
4768 : // Only use the current inner window, and only use it if the caller can
4769 : // access it.
4770 0 : if (nsPIDOMWindowOuter* outerWindow = globalWindow->GetOuterWindow()) {
4771 0 : loadInfo.mWindow = outerWindow->GetCurrentInnerWindow();
4772 : // TODO: fix this for SharedWorkers with multiple documents (bug 1177935)
4773 0 : loadInfo.mServiceWorkersTestingInWindow =
4774 0 : outerWindow->GetServiceWorkersTestingEnabled();
4775 : }
4776 :
4777 0 : if (!loadInfo.mWindow ||
4778 0 : (globalWindow != loadInfo.mWindow &&
4779 0 : !nsContentUtils::CanCallerAccess(loadInfo.mWindow))) {
4780 0 : return NS_ERROR_DOM_SECURITY_ERR;
4781 : }
4782 :
4783 0 : nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(loadInfo.mWindow);
4784 0 : MOZ_ASSERT(sgo);
4785 :
4786 0 : loadInfo.mScriptContext = sgo->GetContext();
4787 0 : NS_ENSURE_TRUE(loadInfo.mScriptContext, NS_ERROR_FAILURE);
4788 :
4789 : // If we're called from a window then we can dig out the principal and URI
4790 : // from the document.
4791 0 : document = loadInfo.mWindow->GetExtantDoc();
4792 0 : NS_ENSURE_TRUE(document, NS_ERROR_FAILURE);
4793 :
4794 0 : loadInfo.mBaseURI = document->GetDocBaseURI();
4795 0 : loadInfo.mLoadGroup = document->GetDocumentLoadGroup();
4796 0 : NS_ENSURE_TRUE(loadInfo.mLoadGroup, NS_ERROR_FAILURE);
4797 :
4798 : // Use the document's NodePrincipal as our principal if we're not being
4799 : // called from chrome.
4800 0 : if (!loadInfo.mPrincipal) {
4801 0 : loadInfo.mPrincipal = document->NodePrincipal();
4802 0 : NS_ENSURE_TRUE(loadInfo.mPrincipal, NS_ERROR_FAILURE);
4803 :
4804 : // We use the document's base domain to limit the number of workers
4805 : // each domain can create. For sandboxed documents, we use the domain
4806 : // of their first non-sandboxed document, walking up until we find
4807 : // one. If we can't find one, we fall back to using the GUID of the
4808 : // null principal as the base domain.
4809 0 : if (document->GetSandboxFlags() & SANDBOXED_ORIGIN) {
4810 0 : nsCOMPtr<nsIDocument> tmpDoc = document;
4811 0 : do {
4812 0 : tmpDoc = tmpDoc->GetParentDocument();
4813 0 : } while (tmpDoc && tmpDoc->GetSandboxFlags() & SANDBOXED_ORIGIN);
4814 :
4815 0 : if (tmpDoc) {
4816 : // There was an unsandboxed ancestor, yay!
4817 0 : nsCOMPtr<nsIPrincipal> tmpPrincipal = tmpDoc->NodePrincipal();
4818 0 : rv = tmpPrincipal->GetBaseDomain(loadInfo.mDomain);
4819 0 : NS_ENSURE_SUCCESS(rv, rv);
4820 : } else {
4821 : // No unsandboxed ancestor, use our GUID.
4822 0 : rv = loadInfo.mPrincipal->GetBaseDomain(loadInfo.mDomain);
4823 0 : NS_ENSURE_SUCCESS(rv, rv);
4824 : }
4825 : } else {
4826 : // Document creating the worker is not sandboxed.
4827 0 : rv = loadInfo.mPrincipal->GetBaseDomain(loadInfo.mDomain);
4828 0 : NS_ENSURE_SUCCESS(rv, rv);
4829 : }
4830 : }
4831 :
4832 0 : NS_ENSURE_TRUE(NS_LoadGroupMatchesPrincipal(loadInfo.mLoadGroup,
4833 : loadInfo.mPrincipal),
4834 : NS_ERROR_FAILURE);
4835 :
4836 : nsCOMPtr<nsIPermissionManager> permMgr =
4837 0 : do_GetService(NS_PERMISSIONMANAGER_CONTRACTID, &rv);
4838 0 : NS_ENSURE_SUCCESS(rv, rv);
4839 :
4840 : uint32_t perm;
4841 0 : rv = permMgr->TestPermissionFromPrincipal(loadInfo.mPrincipal, "systemXHR",
4842 0 : &perm);
4843 0 : NS_ENSURE_SUCCESS(rv, rv);
4844 :
4845 0 : loadInfo.mXHRParamsAllowed = perm == nsIPermissionManager::ALLOW_ACTION;
4846 :
4847 0 : loadInfo.mFromWindow = true;
4848 0 : loadInfo.mWindowID = globalWindow->WindowID();
4849 : nsContentUtils::StorageAccess access =
4850 0 : nsContentUtils::StorageAllowedForWindow(globalWindow);
4851 0 : loadInfo.mStorageAllowed = access > nsContentUtils::StorageAccess::eDeny;
4852 0 : loadInfo.mOriginAttributes = nsContentUtils::GetOriginAttributes(document);
4853 : } else {
4854 : // Not a window
4855 1 : MOZ_ASSERT(isChrome);
4856 :
4857 : // We're being created outside of a window. Need to figure out the script
4858 : // that is creating us in order for us to use relative URIs later on.
4859 2 : JS::AutoFilename fileName;
4860 1 : if (JS::DescribeScriptedCaller(aCx, &fileName)) {
4861 : // In most cases, fileName is URI. In a few other cases
4862 : // (e.g. xpcshell), fileName is a file path. Ideally, we would
4863 : // prefer testing whether fileName parses as an URI and fallback
4864 : // to file path in case of error, but Windows file paths have
4865 : // the interesting property that they can be parsed as bogus
4866 : // URIs (e.g. C:/Windows/Tmp is interpreted as scheme "C",
4867 : // hostname "Windows", path "Tmp"), which defeats this algorithm.
4868 : // Therefore, we adopt the opposite convention.
4869 : nsCOMPtr<nsIFile> scriptFile =
4870 2 : do_CreateInstance("@mozilla.org/file/local;1", &rv);
4871 1 : if (NS_FAILED(rv)) {
4872 0 : return rv;
4873 : }
4874 :
4875 1 : rv = scriptFile->InitWithPath(NS_ConvertUTF8toUTF16(fileName.get()));
4876 1 : if (NS_SUCCEEDED(rv)) {
4877 0 : rv = NS_NewFileURI(getter_AddRefs(loadInfo.mBaseURI),
4878 : scriptFile);
4879 : }
4880 1 : if (NS_FAILED(rv)) {
4881 : // As expected, fileName is not a path, so proceed with
4882 : // a uri.
4883 1 : rv = NS_NewURI(getter_AddRefs(loadInfo.mBaseURI),
4884 : fileName.get());
4885 : }
4886 1 : if (NS_FAILED(rv)) {
4887 0 : return rv;
4888 : }
4889 : }
4890 1 : loadInfo.mXHRParamsAllowed = true;
4891 1 : loadInfo.mFromWindow = false;
4892 1 : loadInfo.mWindowID = UINT64_MAX;
4893 1 : loadInfo.mStorageAllowed = true;
4894 1 : loadInfo.mOriginAttributes = OriginAttributes();
4895 : }
4896 :
4897 1 : MOZ_ASSERT(loadInfo.mPrincipal);
4898 1 : MOZ_ASSERT(isChrome || !loadInfo.mDomain.IsEmpty());
4899 :
4900 1 : if (!loadInfo.mLoadGroup || aLoadGroupBehavior == OverrideLoadGroup) {
4901 1 : OverrideLoadInfoLoadGroup(loadInfo);
4902 : }
4903 1 : MOZ_ASSERT(NS_LoadGroupMatchesPrincipal(loadInfo.mLoadGroup,
4904 : loadInfo.mPrincipal));
4905 :
4906 : // Top level workers' main script use the document charset for the script
4907 : // uri encoding.
4908 1 : bool useDefaultEncoding = false;
4909 1 : rv = ChannelFromScriptURLMainThread(loadInfo.mPrincipal, loadInfo.mBaseURI,
4910 : document, loadInfo.mLoadGroup,
4911 : aScriptURL,
4912 : ContentPolicyType(aWorkerType),
4913 : useDefaultEncoding,
4914 2 : getter_AddRefs(loadInfo.mChannel));
4915 1 : NS_ENSURE_SUCCESS(rv, rv);
4916 :
4917 1 : rv = NS_GetFinalChannelURI(loadInfo.mChannel,
4918 2 : getter_AddRefs(loadInfo.mResolvedScriptURI));
4919 1 : NS_ENSURE_SUCCESS(rv, rv);
4920 :
4921 1 : rv = loadInfo.SetPrincipalFromChannel(loadInfo.mChannel);
4922 1 : NS_ENSURE_SUCCESS(rv, rv);
4923 : }
4924 :
4925 1 : MOZ_DIAGNOSTIC_ASSERT(loadInfo.PrincipalIsValid());
4926 :
4927 1 : aLoadInfo->StealFrom(loadInfo);
4928 1 : return NS_OK;
4929 : }
4930 :
4931 : // static
4932 : void
4933 1 : WorkerPrivate::OverrideLoadInfoLoadGroup(WorkerLoadInfo& aLoadInfo)
4934 : {
4935 1 : MOZ_ASSERT(!aLoadInfo.mInterfaceRequestor);
4936 :
4937 : aLoadInfo.mInterfaceRequestor =
4938 : new WorkerLoadInfo::InterfaceRequestor(aLoadInfo.mPrincipal,
4939 2 : aLoadInfo.mLoadGroup);
4940 1 : aLoadInfo.mInterfaceRequestor->MaybeAddTabChild(aLoadInfo.mLoadGroup);
4941 :
4942 : // NOTE: this defaults the load context to:
4943 : // - private browsing = false
4944 : // - content = true
4945 : // - use remote tabs = false
4946 : nsCOMPtr<nsILoadGroup> loadGroup =
4947 2 : do_CreateInstance(NS_LOADGROUP_CONTRACTID);
4948 :
4949 : nsresult rv =
4950 1 : loadGroup->SetNotificationCallbacks(aLoadInfo.mInterfaceRequestor);
4951 1 : MOZ_ALWAYS_SUCCEEDS(rv);
4952 :
4953 1 : aLoadInfo.mLoadGroup = loadGroup.forget();
4954 :
4955 1 : MOZ_ASSERT(NS_LoadGroupMatchesPrincipal(aLoadInfo.mLoadGroup,
4956 : aLoadInfo.mPrincipal));
4957 1 : }
4958 :
4959 : void
4960 1 : WorkerPrivate::DoRunLoop(JSContext* aCx)
4961 : {
4962 1 : AssertIsOnWorkerThread();
4963 1 : MOZ_ASSERT(mThread);
4964 :
4965 : {
4966 2 : MutexAutoLock lock(mMutex);
4967 1 : mJSContext = aCx;
4968 :
4969 1 : MOZ_ASSERT(mStatus == Pending);
4970 1 : mStatus = Running;
4971 : }
4972 :
4973 : // Now that we've done that, we can go ahead and set up our AutoJSAPI. We
4974 : // can't before this point, because it can't find the right JSContext before
4975 : // then, since it gets it from our mJSContext.
4976 1 : AutoJSAPI jsapi;
4977 1 : jsapi.Init();
4978 1 : MOZ_ASSERT(jsapi.cx() == aCx);
4979 :
4980 1 : EnableMemoryReporter();
4981 :
4982 1 : InitializeGCTimers();
4983 :
4984 1 : Maybe<JSAutoCompartment> workerCompartment;
4985 :
4986 : for (;;) {
4987 : Status currentStatus, previousStatus;
4988 1 : bool debuggerRunnablesPending = false;
4989 1 : bool normalRunnablesPending = false;
4990 :
4991 : {
4992 2 : MutexAutoLock lock(mMutex);
4993 1 : previousStatus = mStatus;
4994 :
4995 3 : while (mControlQueue.IsEmpty() &&
4996 3 : !(debuggerRunnablesPending = !mDebuggerQueue.IsEmpty()) &&
4997 2 : !(normalRunnablesPending = NS_HasPendingEvents(mThread))) {
4998 0 : WaitForWorkerEvents();
4999 : }
5000 :
5001 1 : auto result = ProcessAllControlRunnablesLocked();
5002 1 : if (result != ProcessAllControlRunnablesResult::Nothing) {
5003 : // NB: There's no JS on the stack here, so Abort vs MayContinue is
5004 : // irrelevant
5005 :
5006 : // The state of the world may have changed, recheck it.
5007 0 : normalRunnablesPending = NS_HasPendingEvents(mThread);
5008 : // The debugger queue doesn't get cleared, so we can ignore that.
5009 : }
5010 :
5011 1 : currentStatus = mStatus;
5012 : }
5013 :
5014 : // if all holders are done then we can kill this thread.
5015 1 : if (currentStatus != Running && !HasActiveHolders()) {
5016 :
5017 : // If we just changed status, we must schedule the current runnables.
5018 0 : if (previousStatus != Running && currentStatus != Killing) {
5019 0 : NotifyInternal(aCx, Killing);
5020 0 : MOZ_ASSERT(!JS_IsExceptionPending(aCx));
5021 :
5022 : #ifdef DEBUG
5023 : {
5024 0 : MutexAutoLock lock(mMutex);
5025 0 : currentStatus = mStatus;
5026 : }
5027 0 : MOZ_ASSERT(currentStatus == Killing);
5028 : #else
5029 : currentStatus = Killing;
5030 : #endif
5031 : }
5032 :
5033 : // If we're supposed to die then we should exit the loop.
5034 0 : if (currentStatus == Killing) {
5035 : // Flush uncaught rejections immediately, without
5036 : // waiting for a next tick.
5037 0 : PromiseDebugging::FlushUncaughtRejections();
5038 :
5039 0 : ShutdownGCTimers();
5040 :
5041 0 : DisableMemoryReporter();
5042 :
5043 : {
5044 0 : MutexAutoLock lock(mMutex);
5045 :
5046 0 : mStatus = Dead;
5047 0 : mJSContext = nullptr;
5048 : }
5049 :
5050 : // After mStatus is set to Dead there can be no more
5051 : // WorkerControlRunnables so no need to lock here.
5052 0 : if (!mControlQueue.IsEmpty()) {
5053 : WorkerControlRunnable* runnable;
5054 0 : while (mControlQueue.Pop(runnable)) {
5055 0 : runnable->Cancel();
5056 0 : runnable->Release();
5057 : }
5058 : }
5059 :
5060 : // Unroot the globals
5061 0 : mScope = nullptr;
5062 0 : mDebuggerScope = nullptr;
5063 :
5064 0 : return;
5065 : }
5066 : }
5067 :
5068 1 : if (debuggerRunnablesPending || normalRunnablesPending) {
5069 : // Start the periodic GC timer if it is not already running.
5070 1 : SetGCTimerMode(PeriodicTimer);
5071 : }
5072 :
5073 1 : if (debuggerRunnablesPending) {
5074 : WorkerRunnable* runnable;
5075 :
5076 : {
5077 0 : MutexAutoLock lock(mMutex);
5078 :
5079 0 : mDebuggerQueue.Pop(runnable);
5080 0 : debuggerRunnablesPending = !mDebuggerQueue.IsEmpty();
5081 : }
5082 :
5083 0 : MOZ_ASSERT(runnable);
5084 0 : static_cast<nsIRunnable*>(runnable)->Run();
5085 0 : runnable->Release();
5086 :
5087 : // Flush the promise queue.
5088 0 : Promise::PerformWorkerDebuggerMicroTaskCheckpoint();
5089 :
5090 0 : if (debuggerRunnablesPending) {
5091 0 : WorkerDebuggerGlobalScope* globalScope = DebuggerGlobalScope();
5092 0 : MOZ_ASSERT(globalScope);
5093 :
5094 : // Now *might* be a good time to GC. Let the JS engine make the decision.
5095 0 : JSAutoCompartment ac(aCx, globalScope->GetGlobalJSObject());
5096 0 : JS_MaybeGC(aCx);
5097 : }
5098 1 : } else if (normalRunnablesPending) {
5099 : // Process a single runnable from the main queue.
5100 1 : NS_ProcessNextEvent(mThread, false);
5101 :
5102 0 : normalRunnablesPending = NS_HasPendingEvents(mThread);
5103 0 : if (normalRunnablesPending && GlobalScope()) {
5104 : // Now *might* be a good time to GC. Let the JS engine make the decision.
5105 0 : JSAutoCompartment ac(aCx, GlobalScope()->GetGlobalJSObject());
5106 0 : JS_MaybeGC(aCx);
5107 : }
5108 : }
5109 :
5110 0 : if (!debuggerRunnablesPending && !normalRunnablesPending) {
5111 : // Both the debugger event queue and the normal event queue has been
5112 : // exhausted, cancel the periodic GC timer and schedule the idle GC timer.
5113 0 : SetGCTimerMode(IdleTimer);
5114 : }
5115 :
5116 : // If the worker thread is spamming the main thread faster than it can
5117 : // process the work, then pause the worker thread until the MT catches
5118 : // up.
5119 0 : if (mMainThreadThrottledEventQueue &&
5120 0 : mMainThreadThrottledEventQueue->Length() > 5000) {
5121 0 : mMainThreadThrottledEventQueue->AwaitIdle();
5122 : }
5123 0 : }
5124 :
5125 : MOZ_CRASH("Shouldn't get here!");
5126 : }
5127 :
5128 : void
5129 35 : WorkerPrivate::OnProcessNextEvent()
5130 : {
5131 35 : AssertIsOnWorkerThread();
5132 :
5133 35 : uint32_t recursionDepth = CycleCollectedJSContext::Get()->RecursionDepth();
5134 35 : MOZ_ASSERT(recursionDepth);
5135 :
5136 : // Normally we process control runnables in DoRunLoop or RunCurrentSyncLoop.
5137 : // However, it's possible that non-worker C++ could spin its own nested event
5138 : // loop, and in that case we must ensure that we continue to process control
5139 : // runnables here.
5140 70 : if (recursionDepth > 1 &&
5141 35 : mSyncLoopStack.Length() < recursionDepth - 1) {
5142 31 : Unused << ProcessAllControlRunnables();
5143 : // There's no running JS, and no state to revalidate, so we can ignore the
5144 : // return value.
5145 : }
5146 35 : }
5147 :
5148 : void
5149 31 : WorkerPrivate::AfterProcessNextEvent()
5150 : {
5151 31 : AssertIsOnWorkerThread();
5152 31 : MOZ_ASSERT(CycleCollectedJSContext::Get()->RecursionDepth());
5153 31 : }
5154 :
5155 : void
5156 0 : WorkerPrivate::MaybeDispatchLoadFailedRunnable()
5157 : {
5158 0 : AssertIsOnWorkerThread();
5159 :
5160 0 : nsCOMPtr<nsIRunnable> runnable = StealLoadFailedAsyncRunnable();
5161 0 : if (!runnable) {
5162 0 : return;
5163 : }
5164 :
5165 0 : MOZ_ALWAYS_SUCCEEDS(DispatchToMainThread(runnable.forget()));
5166 : }
5167 :
5168 : nsIEventTarget*
5169 0 : WorkerPrivate::MainThreadEventTarget()
5170 : {
5171 0 : return mMainThreadEventTarget;
5172 : }
5173 :
5174 : nsresult
5175 12 : WorkerPrivate::DispatchToMainThread(nsIRunnable* aRunnable, uint32_t aFlags)
5176 : {
5177 24 : nsCOMPtr<nsIRunnable> r = aRunnable;
5178 24 : return DispatchToMainThread(r.forget(), aFlags);
5179 : }
5180 :
5181 : nsresult
5182 13 : WorkerPrivate::DispatchToMainThread(already_AddRefed<nsIRunnable> aRunnable,
5183 : uint32_t aFlags)
5184 : {
5185 26 : nsCOMPtr<nsIRunnable> runnable = aRunnable;
5186 26 : if (nsCOMPtr<nsINamed> named = do_QueryInterface(runnable)) {
5187 12 : named->SetName("WorkerRunnable");
5188 : }
5189 26 : return mMainThreadEventTarget->Dispatch(runnable.forget(), aFlags);
5190 : }
5191 :
5192 : nsIEventTarget*
5193 0 : WorkerPrivate::ControlEventTarget()
5194 : {
5195 0 : return mWorkerControlEventTarget;
5196 : }
5197 :
5198 : void
5199 1 : WorkerPrivate::InitializeGCTimers()
5200 : {
5201 1 : AssertIsOnWorkerThread();
5202 :
5203 : // We need a timer for GC. The basic plan is to run a non-shrinking GC
5204 : // periodically (PERIODIC_GC_TIMER_DELAY_SEC) while the worker is running.
5205 : // Once the worker goes idle we set a short (IDLE_GC_TIMER_DELAY_SEC) timer to
5206 : // run a shrinking GC. If the worker receives more messages then the short
5207 : // timer is canceled and the periodic timer resumes.
5208 1 : mGCTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
5209 1 : MOZ_ASSERT(mGCTimer);
5210 :
5211 1 : mPeriodicGCTimerRunning = false;
5212 1 : mIdleGCTimerRunning = false;
5213 1 : }
5214 :
5215 : void
5216 61 : WorkerPrivate::SetGCTimerMode(GCTimerMode aMode)
5217 : {
5218 61 : AssertIsOnWorkerThread();
5219 61 : MOZ_ASSERT(mGCTimer);
5220 :
5221 61 : if ((aMode == PeriodicTimer && mPeriodicGCTimerRunning) ||
5222 26 : (aMode == IdleTimer && mIdleGCTimerRunning)) {
5223 9 : return;
5224 : }
5225 :
5226 52 : MOZ_ALWAYS_SUCCEEDS(mGCTimer->Cancel());
5227 :
5228 52 : mPeriodicGCTimerRunning = false;
5229 52 : mIdleGCTimerRunning = false;
5230 52 : LOG(WorkerLog(),
5231 : ("Worker %p canceled GC timer because %s\n", this,
5232 : aMode == PeriodicTimer ?
5233 : "periodic" :
5234 : aMode == IdleTimer ? "idle" : "none"));
5235 :
5236 52 : if (aMode == NoTimer) {
5237 0 : return;
5238 : }
5239 :
5240 52 : MOZ_ASSERT(aMode == PeriodicTimer || aMode == IdleTimer);
5241 :
5242 52 : uint32_t delay = 0;
5243 52 : int16_t type = nsITimer::TYPE_ONE_SHOT;
5244 52 : nsTimerCallbackFunc callback = nullptr;
5245 52 : const char* name = nullptr;
5246 :
5247 52 : if (aMode == PeriodicTimer) {
5248 26 : delay = PERIODIC_GC_TIMER_DELAY_SEC * 1000;
5249 26 : type = nsITimer::TYPE_REPEATING_SLACK;
5250 26 : callback = PeriodicGCTimerCallback;
5251 26 : name = "dom::workers::PeriodicGCTimerCallback";
5252 : }
5253 : else {
5254 26 : delay = IDLE_GC_TIMER_DELAY_SEC * 1000;
5255 26 : type = nsITimer::TYPE_ONE_SHOT;
5256 26 : callback = IdleGCTimerCallback;
5257 26 : name = "dom::workers::IdleGCTimerCallback";
5258 : }
5259 :
5260 52 : MOZ_ALWAYS_SUCCEEDS(mGCTimer->SetTarget(mWorkerControlEventTarget));
5261 52 : MOZ_ALWAYS_SUCCEEDS(
5262 : mGCTimer->InitWithNamedFuncCallback(callback, this, delay, type, name));
5263 :
5264 52 : if (aMode == PeriodicTimer) {
5265 26 : LOG(WorkerLog(), ("Worker %p scheduled periodic GC timer\n", this));
5266 26 : mPeriodicGCTimerRunning = true;
5267 : }
5268 : else {
5269 26 : LOG(WorkerLog(), ("Worker %p scheduled idle GC timer\n", this));
5270 26 : mIdleGCTimerRunning = true;
5271 : }
5272 : }
5273 :
5274 : void
5275 0 : WorkerPrivate::ShutdownGCTimers()
5276 : {
5277 0 : AssertIsOnWorkerThread();
5278 :
5279 0 : MOZ_ASSERT(mGCTimer);
5280 :
5281 : // Always make sure the timer is canceled.
5282 0 : MOZ_ALWAYS_SUCCEEDS(mGCTimer->Cancel());
5283 :
5284 0 : LOG(WorkerLog(), ("Worker %p killed the GC timer\n", this));
5285 :
5286 0 : mGCTimer = nullptr;
5287 0 : mPeriodicGCTimerRunning = false;
5288 0 : mIdleGCTimerRunning = false;
5289 0 : }
5290 :
5291 : bool
5292 0 : WorkerPrivate::InterruptCallback(JSContext* aCx)
5293 : {
5294 0 : AssertIsOnWorkerThread();
5295 :
5296 0 : MOZ_ASSERT(!JS_IsExceptionPending(aCx));
5297 :
5298 0 : bool mayContinue = true;
5299 0 : bool scheduledIdleGC = false;
5300 :
5301 : for (;;) {
5302 : // Run all control events now.
5303 0 : auto result = ProcessAllControlRunnables();
5304 0 : if (result == ProcessAllControlRunnablesResult::Abort) {
5305 0 : mayContinue = false;
5306 : }
5307 :
5308 0 : bool mayFreeze = mFrozen;
5309 0 : if (mayFreeze) {
5310 0 : MutexAutoLock lock(mMutex);
5311 0 : mayFreeze = mStatus <= Running;
5312 : }
5313 :
5314 0 : if (!mayContinue || !mayFreeze) {
5315 : break;
5316 : }
5317 :
5318 : // Cancel the periodic GC timer here before freezing. The idle GC timer
5319 : // will clean everything up once it runs.
5320 0 : if (!scheduledIdleGC) {
5321 0 : SetGCTimerMode(IdleTimer);
5322 0 : scheduledIdleGC = true;
5323 : }
5324 :
5325 0 : while ((mayContinue = MayContinueRunning())) {
5326 0 : MutexAutoLock lock(mMutex);
5327 0 : if (!mControlQueue.IsEmpty()) {
5328 0 : break;
5329 : }
5330 :
5331 0 : WaitForWorkerEvents(PR_MillisecondsToInterval(UINT32_MAX));
5332 : }
5333 0 : }
5334 :
5335 0 : if (!mayContinue) {
5336 : // We want only uncatchable exceptions here.
5337 0 : NS_ASSERTION(!JS_IsExceptionPending(aCx),
5338 : "Should not have an exception set here!");
5339 0 : return false;
5340 : }
5341 :
5342 : // Make sure the periodic timer gets turned back on here.
5343 0 : SetGCTimerMode(PeriodicTimer);
5344 :
5345 0 : return true;
5346 : }
5347 :
5348 : bool
5349 0 : WorkerPrivate::IsOnCurrentThread()
5350 : {
5351 : // May be called on any thread!
5352 :
5353 0 : MOZ_ASSERT(mPRThread);
5354 0 : return PR_GetCurrentThread() == mPRThread;
5355 : }
5356 :
5357 : void
5358 0 : WorkerPrivate::ScheduleDeletion(WorkerRanOrNot aRanOrNot)
5359 : {
5360 0 : AssertIsOnWorkerThread();
5361 0 : MOZ_ASSERT(mChildWorkers.IsEmpty());
5362 0 : MOZ_ASSERT(mSyncLoopStack.IsEmpty());
5363 0 : MOZ_ASSERT(!mPendingEventQueueClearing);
5364 :
5365 0 : ClearMainEventQueue(aRanOrNot);
5366 : #ifdef DEBUG
5367 0 : if (WorkerRan == aRanOrNot) {
5368 0 : nsIThread* currentThread = NS_GetCurrentThread();
5369 0 : MOZ_ASSERT(currentThread);
5370 0 : MOZ_ASSERT(!NS_HasPendingEvents(currentThread));
5371 : }
5372 : #endif
5373 :
5374 0 : if (WorkerPrivate* parent = GetParent()) {
5375 : RefPtr<WorkerFinishedRunnable> runnable =
5376 0 : new WorkerFinishedRunnable(parent, this);
5377 0 : if (!runnable->Dispatch()) {
5378 0 : NS_WARNING("Failed to dispatch runnable!");
5379 : }
5380 : }
5381 : else {
5382 : RefPtr<TopLevelWorkerFinishedRunnable> runnable =
5383 0 : new TopLevelWorkerFinishedRunnable(this);
5384 0 : if (NS_FAILED(DispatchToMainThread(runnable.forget()))) {
5385 0 : NS_WARNING("Failed to dispatch runnable!");
5386 : }
5387 : }
5388 0 : }
5389 :
5390 : bool
5391 0 : WorkerPrivate::CollectRuntimeStats(JS::RuntimeStats* aRtStats,
5392 : bool aAnonymize)
5393 : {
5394 0 : AssertIsOnWorkerThread();
5395 0 : NS_ASSERTION(aRtStats, "Null RuntimeStats!");
5396 0 : NS_ASSERTION(mJSContext, "This must never be null!");
5397 :
5398 0 : return JS::CollectRuntimeStats(mJSContext, aRtStats, nullptr, aAnonymize);
5399 : }
5400 :
5401 : void
5402 1 : WorkerPrivate::EnableMemoryReporter()
5403 : {
5404 1 : AssertIsOnWorkerThread();
5405 1 : MOZ_ASSERT(!mMemoryReporter);
5406 :
5407 : // No need to lock here since the main thread can't race until we've
5408 : // successfully registered the reporter.
5409 1 : mMemoryReporter = new MemoryReporter(this);
5410 :
5411 1 : if (NS_FAILED(RegisterWeakAsyncMemoryReporter(mMemoryReporter))) {
5412 0 : NS_WARNING("Failed to register memory reporter!");
5413 : // No need to lock here since a failed registration means our memory
5414 : // reporter can't start running. Just clean up.
5415 0 : mMemoryReporter = nullptr;
5416 : }
5417 1 : }
5418 :
5419 : void
5420 0 : WorkerPrivate::DisableMemoryReporter()
5421 : {
5422 0 : AssertIsOnWorkerThread();
5423 :
5424 0 : RefPtr<MemoryReporter> memoryReporter;
5425 : {
5426 : // Mutex protectes MemoryReporter::mWorkerPrivate which is cleared by
5427 : // MemoryReporter::Disable() below.
5428 0 : MutexAutoLock lock(mMutex);
5429 :
5430 : // There is nothing to do here if the memory reporter was never successfully
5431 : // registered.
5432 0 : if (!mMemoryReporter) {
5433 0 : return;
5434 : }
5435 :
5436 : // We don't need this set any longer. Swap it out so that we can unregister
5437 : // below.
5438 0 : mMemoryReporter.swap(memoryReporter);
5439 :
5440 : // Next disable the memory reporter so that the main thread stops trying to
5441 : // signal us.
5442 0 : memoryReporter->Disable();
5443 : }
5444 :
5445 : // Finally unregister the memory reporter.
5446 0 : if (NS_FAILED(UnregisterWeakMemoryReporter(memoryReporter))) {
5447 0 : NS_WARNING("Failed to unregister memory reporter!");
5448 : }
5449 : }
5450 :
5451 : void
5452 24 : WorkerPrivate::WaitForWorkerEvents(PRIntervalTime aInterval)
5453 : {
5454 24 : AssertIsOnWorkerThread();
5455 24 : mMutex.AssertCurrentThreadOwns();
5456 :
5457 : // Wait for a worker event.
5458 24 : mCondVar.Wait(aInterval);
5459 23 : }
5460 :
5461 : WorkerPrivate::ProcessAllControlRunnablesResult
5462 66 : WorkerPrivate::ProcessAllControlRunnablesLocked()
5463 : {
5464 66 : AssertIsOnWorkerThread();
5465 66 : mMutex.AssertCurrentThreadOwns();
5466 :
5467 66 : auto result = ProcessAllControlRunnablesResult::Nothing;
5468 :
5469 : for (;;) {
5470 : WorkerControlRunnable* event;
5471 66 : if (!mControlQueue.Pop(event)) {
5472 66 : break;
5473 : }
5474 :
5475 0 : MutexAutoUnlock unlock(mMutex);
5476 :
5477 0 : MOZ_ASSERT(event);
5478 0 : if (NS_FAILED(static_cast<nsIRunnable*>(event)->Run())) {
5479 0 : result = ProcessAllControlRunnablesResult::Abort;
5480 : }
5481 :
5482 0 : if (result == ProcessAllControlRunnablesResult::Nothing) {
5483 : // We ran at least one thing.
5484 0 : result = ProcessAllControlRunnablesResult::MayContinue;
5485 : }
5486 0 : event->Release();
5487 0 : }
5488 :
5489 132 : return result;
5490 : }
5491 :
5492 : void
5493 0 : WorkerPrivate::ClearMainEventQueue(WorkerRanOrNot aRanOrNot)
5494 : {
5495 0 : AssertIsOnWorkerThread();
5496 :
5497 0 : MOZ_ASSERT(mSyncLoopStack.IsEmpty());
5498 0 : MOZ_ASSERT(!mCancelAllPendingRunnables);
5499 0 : mCancelAllPendingRunnables = true;
5500 :
5501 0 : if (WorkerNeverRan == aRanOrNot) {
5502 0 : for (uint32_t count = mPreStartRunnables.Length(), index = 0;
5503 0 : index < count;
5504 : index++) {
5505 0 : RefPtr<WorkerRunnable> runnable = mPreStartRunnables[index].forget();
5506 0 : static_cast<nsIRunnable*>(runnable.get())->Run();
5507 : }
5508 : } else {
5509 0 : nsIThread* currentThread = NS_GetCurrentThread();
5510 0 : MOZ_ASSERT(currentThread);
5511 :
5512 0 : NS_ProcessPendingEvents(currentThread);
5513 : }
5514 :
5515 0 : MOZ_ASSERT(mCancelAllPendingRunnables);
5516 0 : mCancelAllPendingRunnables = false;
5517 0 : }
5518 :
5519 : void
5520 0 : WorkerPrivate::ClearDebuggerEventQueue()
5521 : {
5522 0 : while (!mDebuggerQueue.IsEmpty()) {
5523 : WorkerRunnable* runnable;
5524 0 : mDebuggerQueue.Pop(runnable);
5525 : // It should be ok to simply release the runnable, without running it.
5526 0 : runnable->Release();
5527 : }
5528 0 : }
5529 :
5530 : bool
5531 0 : WorkerPrivate::FreezeInternal()
5532 : {
5533 0 : AssertIsOnWorkerThread();
5534 :
5535 0 : NS_ASSERTION(!mFrozen, "Already frozen!");
5536 :
5537 0 : mFrozen = true;
5538 :
5539 0 : for (uint32_t index = 0; index < mChildWorkers.Length(); index++) {
5540 0 : mChildWorkers[index]->Freeze(nullptr);
5541 : }
5542 :
5543 0 : return true;
5544 : }
5545 :
5546 : bool
5547 0 : WorkerPrivate::ThawInternal()
5548 : {
5549 0 : AssertIsOnWorkerThread();
5550 :
5551 0 : NS_ASSERTION(mFrozen, "Not yet frozen!");
5552 :
5553 0 : for (uint32_t index = 0; index < mChildWorkers.Length(); index++) {
5554 0 : mChildWorkers[index]->Thaw(nullptr);
5555 : }
5556 :
5557 0 : mFrozen = false;
5558 0 : return true;
5559 : }
5560 :
5561 : void
5562 1 : WorkerPrivate::TraverseTimeouts(nsCycleCollectionTraversalCallback& cb)
5563 : {
5564 1 : for (uint32_t i = 0; i < mTimeouts.Length(); ++i) {
5565 0 : TimeoutInfo* tmp = mTimeouts[i];
5566 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mHandler)
5567 : }
5568 1 : }
5569 :
5570 : void
5571 0 : WorkerPrivate::UnlinkTimeouts()
5572 : {
5573 0 : mTimeouts.Clear();
5574 0 : }
5575 :
5576 : bool
5577 1 : WorkerPrivate::ModifyBusyCountFromWorker(bool aIncrease)
5578 : {
5579 1 : AssertIsOnWorkerThread();
5580 :
5581 : {
5582 2 : MutexAutoLock lock(mMutex);
5583 :
5584 : // If we're in shutdown then the busy count is no longer being considered so
5585 : // just return now.
5586 1 : if (mStatus >= Killing) {
5587 0 : return true;
5588 : }
5589 : }
5590 :
5591 : RefPtr<ModifyBusyCountRunnable> runnable =
5592 2 : new ModifyBusyCountRunnable(this, aIncrease);
5593 1 : return runnable->Dispatch();
5594 : }
5595 :
5596 : bool
5597 0 : WorkerPrivate::AddChildWorker(ParentType* aChildWorker)
5598 : {
5599 0 : AssertIsOnWorkerThread();
5600 :
5601 : #ifdef DEBUG
5602 : {
5603 : Status currentStatus;
5604 : {
5605 0 : MutexAutoLock lock(mMutex);
5606 0 : currentStatus = mStatus;
5607 : }
5608 :
5609 0 : MOZ_ASSERT(currentStatus == Running);
5610 : }
5611 : #endif
5612 :
5613 0 : NS_ASSERTION(!mChildWorkers.Contains(aChildWorker),
5614 : "Already know about this one!");
5615 0 : mChildWorkers.AppendElement(aChildWorker);
5616 :
5617 0 : return mChildWorkers.Length() == 1 ?
5618 : ModifyBusyCountFromWorker(true) :
5619 0 : true;
5620 : }
5621 :
5622 : void
5623 0 : WorkerPrivate::RemoveChildWorker(ParentType* aChildWorker)
5624 : {
5625 0 : AssertIsOnWorkerThread();
5626 :
5627 0 : NS_ASSERTION(mChildWorkers.Contains(aChildWorker),
5628 : "Didn't know about this one!");
5629 0 : mChildWorkers.RemoveElement(aChildWorker);
5630 :
5631 0 : if (mChildWorkers.IsEmpty() && !ModifyBusyCountFromWorker(false)) {
5632 0 : NS_WARNING("Failed to modify busy count!");
5633 : }
5634 0 : }
5635 :
5636 : bool
5637 7 : WorkerPrivate::AddHolder(WorkerHolder* aHolder, Status aFailStatus)
5638 : {
5639 7 : AssertIsOnWorkerThread();
5640 :
5641 : {
5642 14 : MutexAutoLock lock(mMutex);
5643 :
5644 7 : if (mStatus >= aFailStatus) {
5645 0 : return false;
5646 : }
5647 : }
5648 :
5649 7 : MOZ_ASSERT(!mHolders.Contains(aHolder), "Already know about this one!");
5650 :
5651 7 : if (aHolder->GetBehavior() == WorkerHolder::PreventIdleShutdownStart) {
5652 7 : if (!mNumHoldersPreventingShutdownStart && !ModifyBusyCountFromWorker(true)) {
5653 0 : return false;
5654 : }
5655 7 : mNumHoldersPreventingShutdownStart += 1;
5656 : }
5657 :
5658 7 : mHolders.AppendElement(aHolder);
5659 7 : return true;
5660 : }
5661 :
5662 : void
5663 4 : WorkerPrivate::RemoveHolder(WorkerHolder* aHolder)
5664 : {
5665 4 : AssertIsOnWorkerThread();
5666 :
5667 4 : MOZ_ASSERT(mHolders.Contains(aHolder), "Didn't know about this one!");
5668 4 : mHolders.RemoveElement(aHolder);
5669 :
5670 4 : if (aHolder->GetBehavior() == WorkerHolder::PreventIdleShutdownStart) {
5671 4 : mNumHoldersPreventingShutdownStart -= 1;
5672 4 : if (!mNumHoldersPreventingShutdownStart && !ModifyBusyCountFromWorker(false)) {
5673 0 : NS_WARNING("Failed to modify busy count!");
5674 : }
5675 : }
5676 4 : }
5677 :
5678 : void
5679 0 : WorkerPrivate::NotifyHolders(JSContext* aCx, Status aStatus)
5680 : {
5681 0 : AssertIsOnWorkerThread();
5682 0 : MOZ_ASSERT(!JS_IsExceptionPending(aCx));
5683 :
5684 0 : NS_ASSERTION(aStatus > Running, "Bad status!");
5685 :
5686 0 : if (aStatus >= Closing) {
5687 0 : CancelAllTimeouts();
5688 : }
5689 :
5690 0 : nsTObserverArray<WorkerHolder*>::ForwardIterator iter(mHolders);
5691 0 : while (iter.HasMore()) {
5692 0 : WorkerHolder* holder = iter.GetNext();
5693 0 : if (!holder->Notify(aStatus)) {
5694 0 : NS_WARNING("Failed to notify holder!");
5695 : }
5696 0 : MOZ_ASSERT(!JS_IsExceptionPending(aCx));
5697 : }
5698 :
5699 0 : AutoTArray<ParentType*, 10> children;
5700 0 : children.AppendElements(mChildWorkers);
5701 :
5702 0 : for (uint32_t index = 0; index < children.Length(); index++) {
5703 0 : if (!children[index]->Notify(aStatus)) {
5704 0 : NS_WARNING("Failed to notify child worker!");
5705 : }
5706 : }
5707 0 : }
5708 :
5709 : void
5710 0 : WorkerPrivate::CancelAllTimeouts()
5711 : {
5712 0 : AssertIsOnWorkerThread();
5713 :
5714 0 : LOG(TimeoutsLog(), ("Worker %p CancelAllTimeouts.\n", this));
5715 :
5716 0 : if (mTimerRunning) {
5717 0 : NS_ASSERTION(mTimer && mTimerRunnable, "Huh?!");
5718 0 : NS_ASSERTION(!mTimeouts.IsEmpty(), "Huh?!");
5719 :
5720 0 : if (NS_FAILED(mTimer->Cancel())) {
5721 0 : NS_WARNING("Failed to cancel timer!");
5722 : }
5723 :
5724 0 : for (uint32_t index = 0; index < mTimeouts.Length(); index++) {
5725 0 : mTimeouts[index]->mCanceled = true;
5726 : }
5727 :
5728 : // If mRunningExpiredTimeouts, then the fact that they are all canceled now
5729 : // means that the currently executing RunExpiredTimeouts will deal with
5730 : // them. Otherwise, we need to clean them up ourselves.
5731 0 : if (!mRunningExpiredTimeouts) {
5732 0 : mTimeouts.Clear();
5733 0 : ModifyBusyCountFromWorker(false);
5734 : }
5735 :
5736 : // Set mTimerRunning false even if mRunningExpiredTimeouts is true, so that
5737 : // if we get reentered under this same RunExpiredTimeouts call we don't
5738 : // assert above that !mTimeouts().IsEmpty(), because that's clearly false
5739 : // now.
5740 0 : mTimerRunning = false;
5741 : }
5742 : #ifdef DEBUG
5743 0 : else if (!mRunningExpiredTimeouts) {
5744 0 : NS_ASSERTION(mTimeouts.IsEmpty(), "Huh?!");
5745 : }
5746 : #endif
5747 :
5748 0 : mTimer = nullptr;
5749 0 : mTimerRunnable = nullptr;
5750 0 : }
5751 :
5752 : already_AddRefed<nsIEventTarget>
5753 17 : WorkerPrivate::CreateNewSyncLoop(Status aFailStatus)
5754 : {
5755 17 : AssertIsOnWorkerThread();
5756 :
5757 : {
5758 34 : MutexAutoLock lock(mMutex);
5759 :
5760 17 : if (mStatus >= aFailStatus) {
5761 0 : return nullptr;
5762 : }
5763 : }
5764 :
5765 34 : nsCOMPtr<nsIThreadInternal> thread = do_QueryInterface(NS_GetCurrentThread());
5766 17 : MOZ_ASSERT(thread);
5767 :
5768 34 : nsCOMPtr<nsIEventTarget> realEventTarget;
5769 17 : MOZ_ALWAYS_SUCCEEDS(thread->PushEventQueue(getter_AddRefs(realEventTarget)));
5770 :
5771 : RefPtr<EventTarget> workerEventTarget =
5772 51 : new EventTarget(this, realEventTarget);
5773 :
5774 : {
5775 : // Modifications must be protected by mMutex in DEBUG builds, see comment
5776 : // about mSyncLoopStack in WorkerPrivate.h.
5777 : #ifdef DEBUG
5778 34 : MutexAutoLock lock(mMutex);
5779 : #endif
5780 :
5781 34 : mSyncLoopStack.AppendElement(new SyncLoopInfo(workerEventTarget));
5782 : }
5783 :
5784 17 : return workerEventTarget.forget();
5785 : }
5786 :
5787 : bool
5788 17 : WorkerPrivate::RunCurrentSyncLoop()
5789 : {
5790 17 : AssertIsOnWorkerThread();
5791 :
5792 17 : JSContext* cx = GetJSContext();
5793 17 : MOZ_ASSERT(cx);
5794 :
5795 : // This should not change between now and the time we finish running this sync
5796 : // loop.
5797 17 : uint32_t currentLoopIndex = mSyncLoopStack.Length() - 1;
5798 :
5799 17 : SyncLoopInfo* loopInfo = mSyncLoopStack[currentLoopIndex];
5800 :
5801 17 : MOZ_ASSERT(loopInfo);
5802 17 : MOZ_ASSERT(!loopInfo->mHasRun);
5803 17 : MOZ_ASSERT(!loopInfo->mCompleted);
5804 :
5805 : #ifdef DEBUG
5806 17 : loopInfo->mHasRun = true;
5807 : #endif
5808 :
5809 79 : while (!loopInfo->mCompleted) {
5810 35 : bool normalRunnablesPending = false;
5811 :
5812 : // Don't block with the periodic GC timer running.
5813 35 : if (!NS_HasPendingEvents(mThread)) {
5814 26 : SetGCTimerMode(IdleTimer);
5815 : }
5816 :
5817 : // Wait for something to do.
5818 : {
5819 69 : MutexAutoLock lock(mMutex);
5820 :
5821 : for (;;) {
5822 197 : while (mControlQueue.IsEmpty() &&
5823 174 : !normalRunnablesPending &&
5824 116 : !(normalRunnablesPending = NS_HasPendingEvents(mThread))) {
5825 24 : WaitForWorkerEvents();
5826 : }
5827 :
5828 34 : auto result = ProcessAllControlRunnablesLocked();
5829 34 : if (result != ProcessAllControlRunnablesResult::Nothing) {
5830 : // XXXkhuey how should we handle Abort here? See Bug 1003730.
5831 :
5832 : // The state of the world may have changed. Recheck it.
5833 0 : normalRunnablesPending = NS_HasPendingEvents(mThread);
5834 :
5835 : // NB: If we processed a NotifyRunnable, we might have run
5836 : // non-control runnables, one of which may have shut down the
5837 : // sync loop.
5838 0 : if (loopInfo->mCompleted) {
5839 0 : break;
5840 : }
5841 : }
5842 :
5843 : // If we *didn't* run any control runnables, this should be unchanged.
5844 34 : MOZ_ASSERT(!loopInfo->mCompleted);
5845 :
5846 34 : if (normalRunnablesPending) {
5847 34 : break;
5848 : }
5849 0 : }
5850 : }
5851 :
5852 34 : if (normalRunnablesPending) {
5853 : // Make sure the periodic timer is running before we continue.
5854 34 : SetGCTimerMode(PeriodicTimer);
5855 :
5856 34 : MOZ_ALWAYS_TRUE(NS_ProcessNextEvent(mThread, false));
5857 :
5858 : // Now *might* be a good time to GC. Let the JS engine make the decision.
5859 31 : if (JS::CurrentGlobalOrNull(cx)) {
5860 31 : JS_MaybeGC(cx);
5861 : }
5862 : }
5863 : }
5864 :
5865 : // Make sure that the stack didn't change underneath us.
5866 13 : MOZ_ASSERT(mSyncLoopStack[currentLoopIndex] == loopInfo);
5867 :
5868 13 : return DestroySyncLoop(currentLoopIndex);
5869 : }
5870 :
5871 : bool
5872 13 : WorkerPrivate::DestroySyncLoop(uint32_t aLoopIndex, nsIThreadInternal* aThread)
5873 : {
5874 13 : MOZ_ASSERT(!mSyncLoopStack.IsEmpty());
5875 13 : MOZ_ASSERT(mSyncLoopStack.Length() - 1 == aLoopIndex);
5876 :
5877 13 : if (!aThread) {
5878 13 : aThread = mThread;
5879 : }
5880 :
5881 : // We're about to delete the loop, stash its event target and result.
5882 13 : SyncLoopInfo* loopInfo = mSyncLoopStack[aLoopIndex];
5883 : nsIEventTarget* nestedEventTarget =
5884 13 : loopInfo->mEventTarget->GetWeakNestedEventTarget();
5885 13 : MOZ_ASSERT(nestedEventTarget);
5886 :
5887 13 : bool result = loopInfo->mResult;
5888 :
5889 : {
5890 : // Modifications must be protected by mMutex in DEBUG builds, see comment
5891 : // about mSyncLoopStack in WorkerPrivate.h.
5892 : #ifdef DEBUG
5893 26 : MutexAutoLock lock(mMutex);
5894 : #endif
5895 :
5896 : // This will delete |loopInfo|!
5897 13 : mSyncLoopStack.RemoveElementAt(aLoopIndex);
5898 : }
5899 :
5900 13 : MOZ_ALWAYS_SUCCEEDS(aThread->PopEventQueue(nestedEventTarget));
5901 :
5902 13 : if (mSyncLoopStack.IsEmpty() && mPendingEventQueueClearing) {
5903 0 : mPendingEventQueueClearing = false;
5904 0 : ClearMainEventQueue(WorkerRan);
5905 : }
5906 :
5907 13 : return result;
5908 : }
5909 :
5910 : void
5911 13 : WorkerPrivate::StopSyncLoop(nsIEventTarget* aSyncLoopTarget, bool aResult)
5912 : {
5913 13 : AssertIsOnWorkerThread();
5914 13 : AssertValidSyncLoop(aSyncLoopTarget);
5915 :
5916 13 : MOZ_ASSERT(!mSyncLoopStack.IsEmpty());
5917 :
5918 13 : for (uint32_t index = mSyncLoopStack.Length(); index > 0; index--) {
5919 13 : nsAutoPtr<SyncLoopInfo>& loopInfo = mSyncLoopStack[index - 1];
5920 13 : MOZ_ASSERT(loopInfo);
5921 13 : MOZ_ASSERT(loopInfo->mEventTarget);
5922 :
5923 13 : if (loopInfo->mEventTarget == aSyncLoopTarget) {
5924 : // Can't assert |loop->mHasRun| here because dispatch failures can cause
5925 : // us to bail out early.
5926 13 : MOZ_ASSERT(!loopInfo->mCompleted);
5927 :
5928 13 : loopInfo->mResult = aResult;
5929 13 : loopInfo->mCompleted = true;
5930 :
5931 13 : loopInfo->mEventTarget->Disable();
5932 :
5933 13 : return;
5934 : }
5935 :
5936 0 : MOZ_ASSERT(!SameCOMIdentity(loopInfo->mEventTarget, aSyncLoopTarget));
5937 : }
5938 :
5939 0 : MOZ_CRASH("Unknown sync loop!");
5940 : }
5941 :
5942 : #ifdef DEBUG
5943 : void
5944 58 : WorkerPrivate::AssertValidSyncLoop(nsIEventTarget* aSyncLoopTarget)
5945 : {
5946 58 : MOZ_ASSERT(aSyncLoopTarget);
5947 :
5948 : EventTarget* workerTarget;
5949 : nsresult rv =
5950 116 : aSyncLoopTarget->QueryInterface(kDEBUGWorkerEventTargetIID,
5951 116 : reinterpret_cast<void**>(&workerTarget));
5952 58 : MOZ_ASSERT(NS_SUCCEEDED(rv));
5953 58 : MOZ_ASSERT(workerTarget);
5954 :
5955 58 : bool valid = false;
5956 :
5957 : {
5958 116 : MutexAutoLock lock(mMutex);
5959 :
5960 219 : for (uint32_t index = 0; index < mSyncLoopStack.Length(); index++) {
5961 219 : nsAutoPtr<SyncLoopInfo>& loopInfo = mSyncLoopStack[index];
5962 219 : MOZ_ASSERT(loopInfo);
5963 219 : MOZ_ASSERT(loopInfo->mEventTarget);
5964 :
5965 219 : if (loopInfo->mEventTarget == aSyncLoopTarget) {
5966 58 : valid = true;
5967 58 : break;
5968 : }
5969 :
5970 161 : MOZ_ASSERT(!SameCOMIdentity(loopInfo->mEventTarget, aSyncLoopTarget));
5971 : }
5972 : }
5973 :
5974 58 : MOZ_ASSERT(valid);
5975 58 : }
5976 : #endif
5977 :
5978 : void
5979 0 : WorkerPrivate::PostMessageToParentInternal(
5980 : JSContext* aCx,
5981 : JS::Handle<JS::Value> aMessage,
5982 : const Sequence<JSObject*>& aTransferable,
5983 : ErrorResult& aRv)
5984 : {
5985 0 : AssertIsOnWorkerThread();
5986 :
5987 0 : JS::Rooted<JS::Value> transferable(aCx, JS::UndefinedValue());
5988 :
5989 0 : aRv = nsContentUtils::CreateJSValueFromSequenceOfObject(aCx, aTransferable,
5990 0 : &transferable);
5991 0 : if (NS_WARN_IF(aRv.Failed())) {
5992 0 : return;
5993 : }
5994 :
5995 : RefPtr<MessageEventRunnable> runnable =
5996 : new MessageEventRunnable(this,
5997 0 : WorkerRunnable::ParentThreadUnchangedBusyCount);
5998 :
5999 0 : UniquePtr<AbstractTimelineMarker> start;
6000 0 : UniquePtr<AbstractTimelineMarker> end;
6001 0 : RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
6002 0 : bool isTimelineRecording = timelines && !timelines->IsEmpty();
6003 :
6004 0 : if (isTimelineRecording) {
6005 0 : start = MakeUnique<WorkerTimelineMarker>(NS_IsMainThread()
6006 0 : ? ProfileTimelineWorkerOperationType::SerializeDataOnMainThread
6007 : : ProfileTimelineWorkerOperationType::SerializeDataOffMainThread,
6008 0 : MarkerTracingType::START);
6009 : }
6010 :
6011 0 : runnable->Write(aCx, aMessage, transferable, JS::CloneDataPolicy(), aRv);
6012 :
6013 0 : if (isTimelineRecording) {
6014 0 : end = MakeUnique<WorkerTimelineMarker>(NS_IsMainThread()
6015 0 : ? ProfileTimelineWorkerOperationType::SerializeDataOnMainThread
6016 : : ProfileTimelineWorkerOperationType::SerializeDataOffMainThread,
6017 0 : MarkerTracingType::END);
6018 0 : timelines->AddMarkerForAllObservedDocShells(start);
6019 0 : timelines->AddMarkerForAllObservedDocShells(end);
6020 : }
6021 :
6022 0 : if (NS_WARN_IF(aRv.Failed())) {
6023 0 : return;
6024 : }
6025 :
6026 0 : if (!runnable->Dispatch()) {
6027 0 : aRv = NS_ERROR_FAILURE;
6028 : }
6029 : }
6030 :
6031 : void
6032 0 : WorkerPrivate::EnterDebuggerEventLoop()
6033 : {
6034 0 : AssertIsOnWorkerThread();
6035 :
6036 0 : JSContext* cx = GetJSContext();
6037 0 : MOZ_ASSERT(cx);
6038 :
6039 0 : uint32_t currentEventLoopLevel = ++mDebuggerEventLoopLevel;
6040 :
6041 0 : while (currentEventLoopLevel <= mDebuggerEventLoopLevel) {
6042 0 : bool debuggerRunnablesPending = false;
6043 :
6044 : {
6045 0 : MutexAutoLock lock(mMutex);
6046 :
6047 0 : debuggerRunnablesPending = !mDebuggerQueue.IsEmpty();
6048 : }
6049 :
6050 : // Don't block with the periodic GC timer running.
6051 0 : if (!debuggerRunnablesPending) {
6052 0 : SetGCTimerMode(IdleTimer);
6053 : }
6054 :
6055 : // Wait for something to do
6056 : {
6057 0 : MutexAutoLock lock(mMutex);
6058 :
6059 0 : while (mControlQueue.IsEmpty() &&
6060 0 : !(debuggerRunnablesPending = !mDebuggerQueue.IsEmpty())) {
6061 0 : WaitForWorkerEvents();
6062 : }
6063 :
6064 0 : ProcessAllControlRunnablesLocked();
6065 :
6066 : // XXXkhuey should we abort JS on the stack here if we got Abort above?
6067 : }
6068 :
6069 0 : if (debuggerRunnablesPending) {
6070 : // Start the periodic GC timer if it is not already running.
6071 0 : SetGCTimerMode(PeriodicTimer);
6072 :
6073 : WorkerRunnable* runnable;
6074 :
6075 : {
6076 0 : MutexAutoLock lock(mMutex);
6077 :
6078 0 : mDebuggerQueue.Pop(runnable);
6079 : }
6080 :
6081 0 : MOZ_ASSERT(runnable);
6082 0 : static_cast<nsIRunnable*>(runnable)->Run();
6083 0 : runnable->Release();
6084 :
6085 : // Flush the promise queue.
6086 0 : Promise::PerformWorkerDebuggerMicroTaskCheckpoint();
6087 :
6088 : // Now *might* be a good time to GC. Let the JS engine make the decision.
6089 0 : if (JS::CurrentGlobalOrNull(cx)) {
6090 0 : JS_MaybeGC(cx);
6091 : }
6092 : }
6093 : }
6094 0 : }
6095 :
6096 : void
6097 0 : WorkerPrivate::LeaveDebuggerEventLoop()
6098 : {
6099 0 : AssertIsOnWorkerThread();
6100 :
6101 0 : MutexAutoLock lock(mMutex);
6102 :
6103 0 : if (mDebuggerEventLoopLevel > 0) {
6104 0 : --mDebuggerEventLoopLevel;
6105 : }
6106 0 : }
6107 :
6108 : void
6109 0 : WorkerPrivate::PostMessageToDebugger(const nsAString& aMessage)
6110 : {
6111 0 : mDebugger->PostMessageToDebugger(aMessage);
6112 0 : }
6113 :
6114 : void
6115 0 : WorkerPrivate::SetDebuggerImmediate(dom::Function& aHandler, ErrorResult& aRv)
6116 : {
6117 0 : AssertIsOnWorkerThread();
6118 :
6119 : RefPtr<DebuggerImmediateRunnable> runnable =
6120 0 : new DebuggerImmediateRunnable(this, aHandler);
6121 0 : if (!runnable->Dispatch()) {
6122 0 : aRv.Throw(NS_ERROR_FAILURE);
6123 : }
6124 0 : }
6125 :
6126 : void
6127 0 : WorkerPrivate::ReportErrorToDebugger(const nsAString& aFilename,
6128 : uint32_t aLineno,
6129 : const nsAString& aMessage)
6130 : {
6131 0 : mDebugger->ReportErrorToDebugger(aFilename, aLineno, aMessage);
6132 0 : }
6133 :
6134 : bool
6135 0 : WorkerPrivate::NotifyInternal(JSContext* aCx, Status aStatus)
6136 : {
6137 0 : AssertIsOnWorkerThread();
6138 :
6139 0 : NS_ASSERTION(aStatus > Running && aStatus < Dead, "Bad status!");
6140 :
6141 0 : RefPtr<EventTarget> eventTarget;
6142 :
6143 : // Save the old status and set the new status.
6144 : Status previousStatus;
6145 : {
6146 0 : MutexAutoLock lock(mMutex);
6147 :
6148 0 : if (mStatus >= aStatus) {
6149 0 : return true;
6150 : }
6151 :
6152 0 : previousStatus = mStatus;
6153 0 : mStatus = aStatus;
6154 :
6155 : // Mark parent status as closing immediately to avoid new events being
6156 : // dispatched after we clear the queue below.
6157 0 : if (aStatus == Closing) {
6158 0 : Close();
6159 : }
6160 :
6161 0 : eventTarget = mEventTarget;
6162 : }
6163 :
6164 : // Disable the event target, if it exists.
6165 0 : if (eventTarget) {
6166 : // Since we'll no longer process events, make sure we no longer allow anyone
6167 : // to post them. We have to do this without mMutex held, since our mutex
6168 : // must be acquired *after* the WorkerEventTarget's mutex when they're both
6169 : // held.
6170 0 : eventTarget->Disable();
6171 : }
6172 :
6173 0 : if (mCrossThreadDispatcher) {
6174 : // Since we'll no longer process events, make sure we no longer allow
6175 : // anyone to post them. We have to do this without mMutex held, since our
6176 : // mutex must be acquired *after* mCrossThreadDispatcher's mutex when
6177 : // they're both held.
6178 0 : mCrossThreadDispatcher->Forget();
6179 0 : mCrossThreadDispatcher = nullptr;
6180 : }
6181 :
6182 0 : MOZ_ASSERT(previousStatus != Pending);
6183 :
6184 : // Let all our holders know the new status.
6185 0 : NotifyHolders(aCx, aStatus);
6186 0 : MOZ_ASSERT(!JS_IsExceptionPending(aCx));
6187 :
6188 : // If this is the first time our status has changed then we need to clear the
6189 : // main event queue.
6190 0 : if (previousStatus == Running) {
6191 : // NB: If we're in a sync loop, we can't clear the queue immediately,
6192 : // because this is the wrong queue. So we have to defer it until later.
6193 0 : if (!mSyncLoopStack.IsEmpty()) {
6194 0 : mPendingEventQueueClearing = true;
6195 : } else {
6196 0 : ClearMainEventQueue(WorkerRan);
6197 : }
6198 : }
6199 :
6200 : // If the worker script never ran, or failed to compile, we don't need to do
6201 : // anything else.
6202 0 : if (!GlobalScope()) {
6203 0 : return true;
6204 : }
6205 :
6206 : // Don't abort the script.
6207 0 : if (aStatus == Closing) {
6208 0 : return true;
6209 : }
6210 :
6211 0 : MOZ_ASSERT(aStatus == Terminating ||
6212 : aStatus == Canceling ||
6213 : aStatus == Killing);
6214 :
6215 : // Always abort the script.
6216 0 : return false;
6217 : }
6218 :
6219 : void
6220 0 : WorkerErrorBase::AssignErrorBase(JSErrorBase* aReport)
6221 : {
6222 0 : mFilename = NS_ConvertUTF8toUTF16(aReport->filename);
6223 0 : mLineNumber = aReport->lineno;
6224 0 : mColumnNumber = aReport->column;
6225 0 : mErrorNumber = aReport->errorNumber;
6226 0 : }
6227 :
6228 : void
6229 0 : WorkerErrorNote::AssignErrorNote(JSErrorNotes::Note* aNote)
6230 : {
6231 0 : WorkerErrorBase::AssignErrorBase(aNote);
6232 0 : xpc::ErrorNote::ErrorNoteToMessageString(aNote, mMessage);
6233 0 : }
6234 :
6235 : void
6236 0 : WorkerErrorReport::AssignErrorReport(JSErrorReport* aReport)
6237 : {
6238 0 : WorkerErrorBase::AssignErrorBase(aReport);
6239 0 : xpc::ErrorReport::ErrorReportToMessageString(aReport, mMessage);
6240 :
6241 0 : mLine.Assign(aReport->linebuf(), aReport->linebufLength());
6242 0 : mFlags = aReport->flags;
6243 0 : MOZ_ASSERT(aReport->exnType >= JSEXN_FIRST && aReport->exnType < JSEXN_LIMIT);
6244 0 : mExnType = JSExnType(aReport->exnType);
6245 0 : mMutedError = aReport->isMuted;
6246 :
6247 0 : if (aReport->notes) {
6248 0 : if (!mNotes.SetLength(aReport->notes->length(), fallible)) {
6249 0 : return;
6250 : }
6251 :
6252 0 : size_t i = 0;
6253 0 : for (auto&& note : *aReport->notes) {
6254 0 : mNotes.ElementAt(i).AssignErrorNote(note.get());
6255 0 : i++;
6256 : }
6257 : }
6258 : }
6259 :
6260 : void
6261 0 : WorkerPrivate::ReportError(JSContext* aCx, JS::ConstUTF8CharsZ aToStringResult,
6262 : JSErrorReport* aReport)
6263 : {
6264 0 : AssertIsOnWorkerThread();
6265 :
6266 0 : if (!MayContinueRunning() || mErrorHandlerRecursionCount == 2) {
6267 0 : return;
6268 : }
6269 :
6270 0 : NS_ASSERTION(mErrorHandlerRecursionCount == 0 ||
6271 : mErrorHandlerRecursionCount == 1,
6272 : "Bad recursion logic!");
6273 :
6274 0 : JS::Rooted<JS::Value> exn(aCx);
6275 0 : if (!JS_GetPendingException(aCx, &exn)) {
6276 : // Probably shouldn't actually happen? But let's go ahead and just use null
6277 : // for lack of anything better.
6278 0 : exn.setNull();
6279 : }
6280 0 : JS_ClearPendingException(aCx);
6281 :
6282 0 : WorkerErrorReport report;
6283 0 : if (aReport) {
6284 0 : report.AssignErrorReport(aReport);
6285 : }
6286 : else {
6287 0 : report.mFlags = nsIScriptError::errorFlag | nsIScriptError::exceptionFlag;
6288 : }
6289 :
6290 0 : if (report.mMessage.IsEmpty() && aToStringResult) {
6291 0 : nsDependentCString toStringResult(aToStringResult.c_str());
6292 0 : if (!AppendUTF8toUTF16(toStringResult, report.mMessage, mozilla::fallible)) {
6293 : // Try again, with only a 1 KB string. Do this infallibly this time.
6294 : // If the user doesn't have 1 KB to spare we're done anyways.
6295 0 : uint32_t index = std::min(uint32_t(1024), toStringResult.Length());
6296 :
6297 : // Drop the last code point that may be cropped.
6298 0 : index = RewindToPriorUTF8Codepoint(toStringResult.BeginReading(), index);
6299 :
6300 : nsDependentCString truncatedToStringResult(aToStringResult.c_str(),
6301 0 : index);
6302 0 : AppendUTF8toUTF16(truncatedToStringResult, report.mMessage);
6303 : }
6304 : }
6305 :
6306 0 : mErrorHandlerRecursionCount++;
6307 :
6308 : // Don't want to run the scope's error handler if this is a recursive error or
6309 : // if we ran out of memory.
6310 0 : bool fireAtScope = mErrorHandlerRecursionCount == 1 &&
6311 0 : report.mErrorNumber != JSMSG_OUT_OF_MEMORY &&
6312 0 : JS::CurrentGlobalOrNull(aCx);
6313 :
6314 0 : ReportErrorRunnable::ReportError(aCx, this, fireAtScope, nullptr, report, 0,
6315 0 : exn);
6316 :
6317 0 : mErrorHandlerRecursionCount--;
6318 : }
6319 :
6320 : // static
6321 : void
6322 0 : WorkerPrivate::ReportErrorToConsole(const char* aMessage)
6323 : {
6324 0 : WorkerPrivate* wp = nullptr;
6325 0 : if (!NS_IsMainThread()) {
6326 0 : wp = GetCurrentThreadWorkerPrivate();
6327 : }
6328 :
6329 0 : ReportErrorToConsoleRunnable::Report(wp, aMessage);
6330 0 : }
6331 :
6332 : int32_t
6333 0 : WorkerPrivate::SetTimeout(JSContext* aCx,
6334 : nsIScriptTimeoutHandler* aHandler,
6335 : int32_t aTimeout, bool aIsInterval,
6336 : ErrorResult& aRv)
6337 : {
6338 0 : AssertIsOnWorkerThread();
6339 0 : MOZ_ASSERT(aHandler);
6340 :
6341 0 : const int32_t timerId = mNextTimeoutId++;
6342 :
6343 : Status currentStatus;
6344 : {
6345 0 : MutexAutoLock lock(mMutex);
6346 0 : currentStatus = mStatus;
6347 : }
6348 :
6349 : // If the worker is trying to call setTimeout/setInterval and the parent
6350 : // thread has initiated the close process then just silently fail.
6351 0 : if (currentStatus >= Closing) {
6352 0 : aRv.Throw(NS_ERROR_FAILURE);
6353 0 : return 0;
6354 : }
6355 :
6356 0 : nsAutoPtr<TimeoutInfo> newInfo(new TimeoutInfo());
6357 0 : newInfo->mIsInterval = aIsInterval;
6358 0 : newInfo->mId = timerId;
6359 :
6360 0 : if (MOZ_UNLIKELY(timerId == INT32_MAX)) {
6361 0 : NS_WARNING("Timeout ids overflowed!");
6362 0 : mNextTimeoutId = 1;
6363 : }
6364 :
6365 0 : newInfo->mHandler = aHandler;
6366 :
6367 : // See if any of the optional arguments were passed.
6368 0 : aTimeout = std::max(0, aTimeout);
6369 0 : newInfo->mInterval = TimeDuration::FromMilliseconds(aTimeout);
6370 :
6371 0 : newInfo->mTargetTime = TimeStamp::Now() + newInfo->mInterval;
6372 :
6373 : nsAutoPtr<TimeoutInfo>* insertedInfo =
6374 0 : mTimeouts.InsertElementSorted(newInfo.forget(), GetAutoPtrComparator(mTimeouts));
6375 :
6376 0 : LOG(TimeoutsLog(), ("Worker %p has new timeout: delay=%d interval=%s\n",
6377 : this, aTimeout, aIsInterval ? "yes" : "no"));
6378 :
6379 : // If the timeout we just made is set to fire next then we need to update the
6380 : // timer, unless we're currently running timeouts.
6381 0 : if (insertedInfo == mTimeouts.Elements() && !mRunningExpiredTimeouts) {
6382 : nsresult rv;
6383 :
6384 0 : if (!mTimer) {
6385 0 : mTimer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
6386 0 : if (NS_FAILED(rv)) {
6387 0 : aRv.Throw(rv);
6388 0 : return 0;
6389 : }
6390 :
6391 0 : mTimerRunnable = new TimerRunnable(this);
6392 : }
6393 :
6394 0 : if (!mTimerRunning) {
6395 0 : if (!ModifyBusyCountFromWorker(true)) {
6396 0 : aRv.Throw(NS_ERROR_FAILURE);
6397 0 : return 0;
6398 : }
6399 0 : mTimerRunning = true;
6400 : }
6401 :
6402 0 : if (!RescheduleTimeoutTimer(aCx)) {
6403 0 : aRv.Throw(NS_ERROR_FAILURE);
6404 0 : return 0;
6405 : }
6406 : }
6407 :
6408 0 : return timerId;
6409 : }
6410 :
6411 : void
6412 0 : WorkerPrivate::ClearTimeout(int32_t aId)
6413 : {
6414 0 : AssertIsOnWorkerThread();
6415 :
6416 0 : if (!mTimeouts.IsEmpty()) {
6417 0 : NS_ASSERTION(mTimerRunning, "Huh?!");
6418 :
6419 0 : for (uint32_t index = 0; index < mTimeouts.Length(); index++) {
6420 0 : nsAutoPtr<TimeoutInfo>& info = mTimeouts[index];
6421 0 : if (info->mId == aId) {
6422 0 : info->mCanceled = true;
6423 0 : break;
6424 : }
6425 : }
6426 : }
6427 0 : }
6428 :
6429 : bool
6430 0 : WorkerPrivate::RunExpiredTimeouts(JSContext* aCx)
6431 : {
6432 0 : AssertIsOnWorkerThread();
6433 :
6434 : // We may be called recursively (e.g. close() inside a timeout) or we could
6435 : // have been canceled while this event was pending, bail out if there is
6436 : // nothing to do.
6437 0 : if (mRunningExpiredTimeouts || !mTimerRunning) {
6438 0 : return true;
6439 : }
6440 :
6441 0 : NS_ASSERTION(mTimer && mTimerRunnable, "Must have a timer!");
6442 0 : NS_ASSERTION(!mTimeouts.IsEmpty(), "Should have some work to do!");
6443 :
6444 0 : bool retval = true;
6445 :
6446 0 : AutoPtrComparator<TimeoutInfo> comparator = GetAutoPtrComparator(mTimeouts);
6447 0 : JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx));
6448 :
6449 : // We want to make sure to run *something*, even if the timer fired a little
6450 : // early. Fudge the value of now to at least include the first timeout.
6451 0 : const TimeStamp actual_now = TimeStamp::Now();
6452 0 : const TimeStamp now = std::max(actual_now, mTimeouts[0]->mTargetTime);
6453 :
6454 0 : if (now != actual_now) {
6455 0 : LOG(TimeoutsLog(), ("Worker %p fudged timeout by %f ms.\n", this,
6456 : (now - actual_now).ToMilliseconds()));
6457 : }
6458 :
6459 0 : AutoTArray<TimeoutInfo*, 10> expiredTimeouts;
6460 0 : for (uint32_t index = 0; index < mTimeouts.Length(); index++) {
6461 0 : nsAutoPtr<TimeoutInfo>& info = mTimeouts[index];
6462 0 : if (info->mTargetTime > now) {
6463 0 : break;
6464 : }
6465 0 : expiredTimeouts.AppendElement(info);
6466 : }
6467 :
6468 : // Guard against recursion.
6469 0 : mRunningExpiredTimeouts = true;
6470 :
6471 : // Run expired timeouts.
6472 0 : for (uint32_t index = 0; index < expiredTimeouts.Length(); index++) {
6473 0 : TimeoutInfo*& info = expiredTimeouts[index];
6474 :
6475 0 : if (info->mCanceled) {
6476 0 : continue;
6477 : }
6478 :
6479 0 : LOG(TimeoutsLog(), ("Worker %p executing timeout with original delay %f ms.\n",
6480 : this, info->mInterval.ToMilliseconds()));
6481 :
6482 : // Always check JS_IsExceptionPending if something fails, and if
6483 : // JS_IsExceptionPending returns false (i.e. uncatchable exception) then
6484 : // break out of the loop.
6485 : const char *reason;
6486 0 : if (info->mIsInterval) {
6487 0 : reason = "setInterval handler";
6488 : } else {
6489 0 : reason = "setTimeout handler";
6490 : }
6491 :
6492 0 : RefPtr<Function> callback = info->mHandler->GetCallback();
6493 0 : if (!callback) {
6494 : // scope for the AutoEntryScript, so it comes off the stack before we do
6495 : // Promise::PerformMicroTaskCheckpoint.
6496 0 : AutoEntryScript aes(global, reason, false);
6497 :
6498 : // Evaluate the timeout expression.
6499 0 : const nsAString& script = info->mHandler->GetHandlerText();
6500 :
6501 0 : const char* filename = nullptr;
6502 0 : uint32_t lineNo = 0, dummyColumn = 0;
6503 0 : info->mHandler->GetLocation(&filename, &lineNo, &dummyColumn);
6504 :
6505 0 : JS::CompileOptions options(aes.cx());
6506 0 : options.setFileAndLine(filename, lineNo).setNoScriptRval(true);
6507 :
6508 0 : JS::Rooted<JS::Value> unused(aes.cx());
6509 :
6510 0 : if (!JS::Evaluate(aes.cx(), options, script.BeginReading(),
6511 0 : script.Length(), &unused) &&
6512 0 : !JS_IsExceptionPending(aCx)) {
6513 0 : retval = false;
6514 0 : break;
6515 : }
6516 : } else {
6517 0 : ErrorResult rv;
6518 0 : JS::Rooted<JS::Value> ignoredVal(aCx);
6519 0 : callback->Call(GlobalScope(), info->mHandler->GetArgs(), &ignoredVal, rv,
6520 0 : reason);
6521 0 : if (rv.IsUncatchableException()) {
6522 0 : rv.SuppressException();
6523 0 : retval = false;
6524 0 : break;
6525 : }
6526 :
6527 0 : rv.SuppressException();
6528 : }
6529 :
6530 : // Since we might be processing more timeouts, go ahead and flush
6531 : // the promise queue now before we do that.
6532 0 : Promise::PerformWorkerMicroTaskCheckpoint();
6533 :
6534 0 : NS_ASSERTION(mRunningExpiredTimeouts, "Someone changed this!");
6535 : }
6536 :
6537 : // No longer possible to be called recursively.
6538 0 : mRunningExpiredTimeouts = false;
6539 :
6540 : // Now remove canceled and expired timeouts from the main list.
6541 : // NB: The timeouts present in expiredTimeouts must have the same order
6542 : // with respect to each other in mTimeouts. That is, mTimeouts is just
6543 : // expiredTimeouts with extra elements inserted. There may be unexpired
6544 : // timeouts that have been inserted between the expired timeouts if the
6545 : // timeout event handler called setTimeout/setInterval.
6546 0 : for (uint32_t index = 0, expiredTimeoutIndex = 0,
6547 0 : expiredTimeoutLength = expiredTimeouts.Length();
6548 0 : index < mTimeouts.Length(); ) {
6549 0 : nsAutoPtr<TimeoutInfo>& info = mTimeouts[index];
6550 0 : if ((expiredTimeoutIndex < expiredTimeoutLength &&
6551 0 : info == expiredTimeouts[expiredTimeoutIndex] &&
6552 0 : ++expiredTimeoutIndex) ||
6553 0 : info->mCanceled) {
6554 0 : if (info->mIsInterval && !info->mCanceled) {
6555 : // Reschedule intervals.
6556 0 : info->mTargetTime = info->mTargetTime + info->mInterval;
6557 : // Don't resort the list here, we'll do that at the end.
6558 0 : ++index;
6559 : }
6560 : else {
6561 0 : mTimeouts.RemoveElement(info);
6562 : }
6563 : }
6564 : else {
6565 : // If info did not match the current entry in expiredTimeouts, it
6566 : // shouldn't be there at all.
6567 0 : NS_ASSERTION(!expiredTimeouts.Contains(info),
6568 : "Our timeouts are out of order!");
6569 0 : ++index;
6570 : }
6571 : }
6572 :
6573 0 : mTimeouts.Sort(comparator);
6574 :
6575 : // Either signal the parent that we're no longer using timeouts or reschedule
6576 : // the timer.
6577 0 : if (mTimeouts.IsEmpty()) {
6578 0 : if (!ModifyBusyCountFromWorker(false)) {
6579 0 : retval = false;
6580 : }
6581 0 : mTimerRunning = false;
6582 : }
6583 0 : else if (retval && !RescheduleTimeoutTimer(aCx)) {
6584 0 : retval = false;
6585 : }
6586 :
6587 0 : return retval;
6588 : }
6589 :
6590 : bool
6591 0 : WorkerPrivate::RescheduleTimeoutTimer(JSContext* aCx)
6592 : {
6593 0 : AssertIsOnWorkerThread();
6594 0 : MOZ_ASSERT(!mRunningExpiredTimeouts);
6595 0 : NS_ASSERTION(!mTimeouts.IsEmpty(), "Should have some timeouts!");
6596 0 : NS_ASSERTION(mTimer && mTimerRunnable, "Should have a timer!");
6597 :
6598 : // NB: This is important! The timer may have already fired, e.g. if a timeout
6599 : // callback itself calls setTimeout for a short duration and then takes longer
6600 : // than that to finish executing. If that has happened, it's very important
6601 : // that we don't execute the event that is now pending in our event queue, or
6602 : // our code in RunExpiredTimeouts to "fudge" the timeout value will unleash an
6603 : // early timeout when we execute the event we're about to queue.
6604 0 : mTimer->Cancel();
6605 :
6606 : double delta =
6607 0 : (mTimeouts[0]->mTargetTime - TimeStamp::Now()).ToMilliseconds();
6608 0 : uint32_t delay = delta > 0 ? std::min(delta, double(UINT32_MAX)) : 0;
6609 :
6610 0 : LOG(TimeoutsLog(), ("Worker %p scheduled timer for %d ms, %" PRIuSIZE " pending timeouts\n",
6611 : this, delay, mTimeouts.Length()));
6612 :
6613 0 : nsresult rv = mTimer->InitWithCallback(mTimerRunnable, delay, nsITimer::TYPE_ONE_SHOT);
6614 0 : if (NS_FAILED(rv)) {
6615 0 : JS_ReportErrorASCII(aCx, "Failed to start timer!");
6616 0 : return false;
6617 : }
6618 :
6619 0 : return true;
6620 : }
6621 :
6622 : void
6623 0 : WorkerPrivate::UpdateContextOptionsInternal(
6624 : JSContext* aCx,
6625 : const JS::ContextOptions& aContextOptions)
6626 : {
6627 0 : AssertIsOnWorkerThread();
6628 :
6629 0 : JS::ContextOptionsRef(aCx) = aContextOptions;
6630 :
6631 0 : for (uint32_t index = 0; index < mChildWorkers.Length(); index++) {
6632 0 : mChildWorkers[index]->UpdateContextOptions(aContextOptions);
6633 : }
6634 0 : }
6635 :
6636 : void
6637 0 : WorkerPrivate::UpdateLanguagesInternal(const nsTArray<nsString>& aLanguages)
6638 : {
6639 0 : WorkerGlobalScope* globalScope = GlobalScope();
6640 0 : if (globalScope) {
6641 0 : RefPtr<WorkerNavigator> nav = globalScope->GetExistingNavigator();
6642 0 : if (nav) {
6643 0 : nav->SetLanguages(aLanguages);
6644 : }
6645 : }
6646 :
6647 0 : for (uint32_t index = 0; index < mChildWorkers.Length(); index++) {
6648 0 : mChildWorkers[index]->UpdateLanguages(aLanguages);
6649 : }
6650 0 : }
6651 :
6652 : void
6653 0 : WorkerPrivate::UpdatePreferenceInternal(WorkerPreference aPref, bool aValue)
6654 : {
6655 0 : AssertIsOnWorkerThread();
6656 0 : MOZ_ASSERT(aPref >= 0 && aPref < WORKERPREF_COUNT);
6657 :
6658 0 : mPreferences[aPref] = aValue;
6659 :
6660 0 : for (uint32_t index = 0; index < mChildWorkers.Length(); index++) {
6661 0 : mChildWorkers[index]->UpdatePreference(aPref, aValue);
6662 : }
6663 0 : }
6664 :
6665 : void
6666 0 : WorkerPrivate::UpdateJSWorkerMemoryParameterInternal(JSContext* aCx,
6667 : JSGCParamKey aKey,
6668 : uint32_t aValue)
6669 : {
6670 0 : AssertIsOnWorkerThread();
6671 :
6672 : // XXX aValue might be 0 here (telling us to unset a previous value for child
6673 : // workers). Calling JS_SetGCParameter with a value of 0 isn't actually
6674 : // supported though. We really need some way to revert to a default value
6675 : // here.
6676 0 : if (aValue) {
6677 0 : JS_SetGCParameter(aCx, aKey, aValue);
6678 : }
6679 :
6680 0 : for (uint32_t index = 0; index < mChildWorkers.Length(); index++) {
6681 0 : mChildWorkers[index]->UpdateJSWorkerMemoryParameter(aKey, aValue);
6682 : }
6683 0 : }
6684 :
6685 : #ifdef JS_GC_ZEAL
6686 : void
6687 0 : WorkerPrivate::UpdateGCZealInternal(JSContext* aCx, uint8_t aGCZeal,
6688 : uint32_t aFrequency)
6689 : {
6690 0 : AssertIsOnWorkerThread();
6691 :
6692 0 : JS_SetGCZeal(aCx, aGCZeal, aFrequency);
6693 :
6694 0 : for (uint32_t index = 0; index < mChildWorkers.Length(); index++) {
6695 0 : mChildWorkers[index]->UpdateGCZeal(aGCZeal, aFrequency);
6696 : }
6697 0 : }
6698 : #endif
6699 :
6700 : void
6701 0 : WorkerPrivate::GarbageCollectInternal(JSContext* aCx, bool aShrinking,
6702 : bool aCollectChildren)
6703 : {
6704 0 : AssertIsOnWorkerThread();
6705 :
6706 0 : if (!GlobalScope()) {
6707 : // We haven't compiled anything yet. Just bail out.
6708 0 : return;
6709 : }
6710 :
6711 0 : if (aShrinking || aCollectChildren) {
6712 0 : JS::PrepareForFullGC(aCx);
6713 :
6714 0 : if (aShrinking) {
6715 0 : JS::GCForReason(aCx, GC_SHRINK, JS::gcreason::DOM_WORKER);
6716 :
6717 0 : if (!aCollectChildren) {
6718 0 : LOG(WorkerLog(), ("Worker %p collected idle garbage\n", this));
6719 : }
6720 : }
6721 : else {
6722 0 : JS::GCForReason(aCx, GC_NORMAL, JS::gcreason::DOM_WORKER);
6723 0 : LOG(WorkerLog(), ("Worker %p collected garbage\n", this));
6724 : }
6725 : }
6726 : else {
6727 0 : JS_MaybeGC(aCx);
6728 0 : LOG(WorkerLog(), ("Worker %p collected periodic garbage\n", this));
6729 : }
6730 :
6731 0 : if (aCollectChildren) {
6732 0 : for (uint32_t index = 0; index < mChildWorkers.Length(); index++) {
6733 0 : mChildWorkers[index]->GarbageCollect(aShrinking);
6734 : }
6735 : }
6736 : }
6737 :
6738 : void
6739 0 : WorkerPrivate::CycleCollectInternal(bool aCollectChildren)
6740 : {
6741 0 : AssertIsOnWorkerThread();
6742 :
6743 0 : nsCycleCollector_collect(nullptr);
6744 :
6745 0 : if (aCollectChildren) {
6746 0 : for (uint32_t index = 0; index < mChildWorkers.Length(); index++) {
6747 0 : mChildWorkers[index]->CycleCollect(/* dummy = */ false);
6748 : }
6749 : }
6750 0 : }
6751 :
6752 : void
6753 0 : WorkerPrivate::MemoryPressureInternal()
6754 : {
6755 0 : AssertIsOnWorkerThread();
6756 :
6757 0 : RefPtr<Console> console = mScope ? mScope->GetConsoleIfExists() : nullptr;
6758 0 : if (console) {
6759 0 : console->ClearStorage();
6760 : }
6761 :
6762 0 : console = mDebuggerScope ? mDebuggerScope->GetConsoleIfExists() : nullptr;
6763 0 : if (console) {
6764 0 : console->ClearStorage();
6765 : }
6766 :
6767 0 : for (uint32_t index = 0; index < mChildWorkers.Length(); index++) {
6768 0 : mChildWorkers[index]->MemoryPressure(false);
6769 : }
6770 0 : }
6771 :
6772 : void
6773 1 : WorkerPrivate::SetThread(WorkerThread* aThread)
6774 : {
6775 1 : if (aThread) {
6776 : #ifdef DEBUG
6777 : {
6778 : bool isOnCurrentThread;
6779 1 : MOZ_ASSERT(NS_SUCCEEDED(aThread->IsOnCurrentThread(&isOnCurrentThread)));
6780 1 : MOZ_ASSERT(isOnCurrentThread);
6781 : }
6782 : #endif
6783 :
6784 1 : MOZ_ASSERT(!mPRThread);
6785 1 : mPRThread = PRThreadFromThread(aThread);
6786 1 : MOZ_ASSERT(mPRThread);
6787 : }
6788 : else {
6789 0 : MOZ_ASSERT(mPRThread);
6790 : }
6791 :
6792 2 : const WorkerThreadFriendKey friendKey;
6793 :
6794 2 : RefPtr<WorkerThread> doomedThread;
6795 :
6796 : { // Scope so that |doomedThread| is released without holding the lock.
6797 2 : MutexAutoLock lock(mMutex);
6798 :
6799 1 : if (aThread) {
6800 1 : MOZ_ASSERT(!mThread);
6801 1 : MOZ_ASSERT(mStatus == Pending);
6802 :
6803 1 : mThread = aThread;
6804 1 : mThread->SetWorker(friendKey, this);
6805 :
6806 1 : if (!mPreStartRunnables.IsEmpty()) {
6807 3 : for (uint32_t index = 0; index < mPreStartRunnables.Length(); index++) {
6808 2 : MOZ_ALWAYS_SUCCEEDS(
6809 : mThread->DispatchAnyThread(friendKey, mPreStartRunnables[index].forget()));
6810 : }
6811 1 : mPreStartRunnables.Clear();
6812 : }
6813 : }
6814 : else {
6815 0 : MOZ_ASSERT(mThread);
6816 :
6817 0 : mThread->SetWorker(friendKey, nullptr);
6818 :
6819 0 : mThread.swap(doomedThread);
6820 : }
6821 : }
6822 1 : }
6823 :
6824 : WorkerCrossThreadDispatcher*
6825 0 : WorkerPrivate::GetCrossThreadDispatcher()
6826 : {
6827 0 : MutexAutoLock lock(mMutex);
6828 :
6829 0 : if (!mCrossThreadDispatcher && mStatus <= Running) {
6830 0 : mCrossThreadDispatcher = new WorkerCrossThreadDispatcher(this);
6831 : }
6832 :
6833 0 : return mCrossThreadDispatcher;
6834 : }
6835 :
6836 : void
6837 0 : WorkerPrivate::BeginCTypesCall()
6838 : {
6839 0 : AssertIsOnWorkerThread();
6840 :
6841 : // Don't try to GC while we're blocked in a ctypes call.
6842 0 : SetGCTimerMode(NoTimer);
6843 0 : }
6844 :
6845 : void
6846 0 : WorkerPrivate::EndCTypesCall()
6847 : {
6848 0 : AssertIsOnWorkerThread();
6849 :
6850 : // Make sure the periodic timer is running before we start running JS again.
6851 0 : SetGCTimerMode(PeriodicTimer);
6852 0 : }
6853 :
6854 : bool
6855 0 : WorkerPrivate::ConnectMessagePort(JSContext* aCx,
6856 : MessagePortIdentifier& aIdentifier)
6857 : {
6858 0 : AssertIsOnWorkerThread();
6859 :
6860 0 : WorkerGlobalScope* globalScope = GlobalScope();
6861 :
6862 0 : JS::Rooted<JSObject*> jsGlobal(aCx, globalScope->GetWrapper());
6863 0 : MOZ_ASSERT(jsGlobal);
6864 :
6865 : // This MessagePortIdentifier is used to create a new port, still connected
6866 : // with the other one, but in the worker thread.
6867 0 : ErrorResult rv;
6868 0 : RefPtr<MessagePort> port = MessagePort::Create(globalScope, aIdentifier, rv);
6869 0 : if (NS_WARN_IF(rv.Failed())) {
6870 0 : rv.SuppressException();
6871 0 : return false;
6872 : }
6873 :
6874 0 : GlobalObject globalObject(aCx, jsGlobal);
6875 0 : if (globalObject.Failed()) {
6876 0 : return false;
6877 : }
6878 :
6879 0 : RootedDictionary<MessageEventInit> init(aCx);
6880 0 : init.mBubbles = false;
6881 0 : init.mCancelable = false;
6882 0 : init.mSource.SetValue().SetAsMessagePort() = port;
6883 0 : if (!init.mPorts.AppendElement(port.forget(), fallible)) {
6884 0 : return false;
6885 : }
6886 :
6887 : RefPtr<MessageEvent> event =
6888 0 : MessageEvent::Constructor(globalObject,
6889 0 : NS_LITERAL_STRING("connect"), init, rv);
6890 :
6891 0 : event->SetTrusted(true);
6892 :
6893 0 : nsCOMPtr<nsIDOMEvent> domEvent = do_QueryObject(event);
6894 :
6895 0 : nsEventStatus dummy = nsEventStatus_eIgnore;
6896 0 : globalScope->DispatchDOMEvent(nullptr, domEvent, nullptr, &dummy);
6897 :
6898 0 : return true;
6899 : }
6900 :
6901 : WorkerGlobalScope*
6902 1 : WorkerPrivate::GetOrCreateGlobalScope(JSContext* aCx)
6903 : {
6904 1 : AssertIsOnWorkerThread();
6905 :
6906 1 : if (!mScope) {
6907 2 : RefPtr<WorkerGlobalScope> globalScope;
6908 1 : if (IsSharedWorker()) {
6909 0 : globalScope = new SharedWorkerGlobalScope(this, WorkerName());
6910 1 : } else if (IsServiceWorker()) {
6911 0 : globalScope = new ServiceWorkerGlobalScope(this, ServiceWorkerScope());
6912 : } else {
6913 2 : globalScope = new DedicatedWorkerGlobalScope(this, WorkerName());
6914 : }
6915 :
6916 2 : JS::Rooted<JSObject*> global(aCx);
6917 1 : NS_ENSURE_TRUE(globalScope->WrapGlobalObject(aCx, &global), nullptr);
6918 :
6919 2 : JSAutoCompartment ac(aCx, global);
6920 :
6921 : // RegisterBindings() can spin a nested event loop so we have to set mScope
6922 : // before calling it, and we have to make sure to unset mScope if it fails.
6923 1 : mScope = Move(globalScope);
6924 :
6925 1 : if (!RegisterBindings(aCx, global)) {
6926 0 : mScope = nullptr;
6927 0 : return nullptr;
6928 : }
6929 :
6930 1 : JS_FireOnNewGlobalObject(aCx, global);
6931 : }
6932 :
6933 1 : return mScope;
6934 : }
6935 :
6936 : WorkerDebuggerGlobalScope*
6937 0 : WorkerPrivate::CreateDebuggerGlobalScope(JSContext* aCx)
6938 : {
6939 0 : AssertIsOnWorkerThread();
6940 :
6941 0 : MOZ_ASSERT(!mDebuggerScope);
6942 :
6943 : RefPtr<WorkerDebuggerGlobalScope> globalScope =
6944 0 : new WorkerDebuggerGlobalScope(this);
6945 :
6946 0 : JS::Rooted<JSObject*> global(aCx);
6947 0 : NS_ENSURE_TRUE(globalScope->WrapGlobalObject(aCx, &global), nullptr);
6948 :
6949 0 : JSAutoCompartment ac(aCx, global);
6950 :
6951 : // RegisterDebuggerBindings() can spin a nested event loop so we have to set
6952 : // mDebuggerScope before calling it, and we have to make sure to unset
6953 : // mDebuggerScope if it fails.
6954 0 : mDebuggerScope = Move(globalScope);
6955 :
6956 0 : if (!RegisterDebuggerBindings(aCx, global)) {
6957 0 : mDebuggerScope = nullptr;
6958 0 : return nullptr;
6959 : }
6960 :
6961 0 : JS_FireOnNewGlobalObject(aCx, global);
6962 :
6963 0 : return mDebuggerScope;
6964 : }
6965 :
6966 : #ifdef DEBUG
6967 :
6968 : void
6969 646 : WorkerPrivate::AssertIsOnWorkerThread() const
6970 : {
6971 : // This is much more complicated than it needs to be but we can't use mThread
6972 : // because it must be protected by mMutex and sometimes this method is called
6973 : // when mMutex is already locked. This method should always work.
6974 646 : MOZ_ASSERT(mPRThread,
6975 : "AssertIsOnWorkerThread() called before a thread was assigned!");
6976 :
6977 1292 : nsCOMPtr<nsIThread> thread;
6978 : nsresult rv =
6979 1292 : nsThreadManager::get().GetThreadFromPRThread(mPRThread,
6980 1292 : getter_AddRefs(thread));
6981 646 : MOZ_ASSERT(NS_SUCCEEDED(rv));
6982 646 : MOZ_ASSERT(thread);
6983 :
6984 : bool current;
6985 646 : rv = thread->IsOnCurrentThread(¤t);
6986 646 : MOZ_ASSERT(NS_SUCCEEDED(rv));
6987 646 : MOZ_ASSERT(current, "Wrong thread!");
6988 646 : }
6989 :
6990 : #endif // DEBUG
6991 :
6992 0 : NS_IMPL_ISUPPORTS_INHERITED0(ExternalRunnableWrapper, WorkerRunnable)
6993 :
6994 : template <class Derived>
6995 495 : NS_IMPL_ADDREF(WorkerPrivateParent<Derived>::EventTarget)
6996 :
6997 : template <class Derived>
6998 486 : NS_IMPL_RELEASE(WorkerPrivateParent<Derived>::EventTarget)
6999 :
7000 : template <class Derived>
7001 459 : NS_INTERFACE_MAP_BEGIN(WorkerPrivateParent<Derived>::EventTarget)
7002 459 : NS_INTERFACE_MAP_ENTRY(nsISerialEventTarget)
7003 457 : NS_INTERFACE_MAP_ENTRY(nsIEventTarget)
7004 380 : NS_INTERFACE_MAP_ENTRY(nsISupports)
7005 : #ifdef DEBUG
7006 : // kDEBUGWorkerEventTargetIID is special in that it does not AddRef its
7007 : // result.
7008 58 : if (aIID.Equals(kDEBUGWorkerEventTargetIID)) {
7009 58 : *aInstancePtr = this;
7010 58 : return NS_OK;
7011 : }
7012 : else
7013 : #endif
7014 0 : NS_INTERFACE_MAP_END
7015 :
7016 : template <class Derived>
7017 : NS_IMETHODIMP
7018 0 : WorkerPrivateParent<Derived>::
7019 : EventTarget::DispatchFromScript(nsIRunnable* aRunnable, uint32_t aFlags)
7020 : {
7021 0 : nsCOMPtr<nsIRunnable> event(aRunnable);
7022 0 : return Dispatch(event.forget(), aFlags);
7023 : }
7024 :
7025 : template <class Derived>
7026 : NS_IMETHODIMP
7027 36 : WorkerPrivateParent<Derived>::
7028 : EventTarget::Dispatch(already_AddRefed<nsIRunnable> aRunnable, uint32_t aFlags)
7029 : {
7030 : // May be called on any thread!
7031 72 : nsCOMPtr<nsIRunnable> event(aRunnable);
7032 :
7033 : // Workers only support asynchronous dispatch for now.
7034 36 : if (NS_WARN_IF(aFlags != NS_DISPATCH_NORMAL)) {
7035 0 : return NS_ERROR_UNEXPECTED;
7036 : }
7037 :
7038 72 : RefPtr<WorkerRunnable> workerRunnable;
7039 :
7040 72 : MutexAutoLock lock(mMutex);
7041 :
7042 36 : if (!mWorkerPrivate) {
7043 0 : NS_WARNING("A runnable was posted to a worker that is already shutting "
7044 : "down!");
7045 0 : return NS_ERROR_UNEXPECTED;
7046 : }
7047 :
7048 36 : if (event) {
7049 36 : workerRunnable = mWorkerPrivate->MaybeWrapAsWorkerRunnable(event.forget());
7050 : }
7051 :
7052 : nsresult rv =
7053 36 : mWorkerPrivate->DispatchPrivate(workerRunnable.forget(), mNestedEventTarget);
7054 36 : if (NS_WARN_IF(NS_FAILED(rv))) {
7055 0 : return rv;
7056 : }
7057 :
7058 36 : return NS_OK;
7059 : }
7060 :
7061 : template <class Derived>
7062 : NS_IMETHODIMP
7063 0 : WorkerPrivateParent<Derived>::
7064 : EventTarget::DelayedDispatch(already_AddRefed<nsIRunnable>, uint32_t)
7065 : {
7066 0 : return NS_ERROR_NOT_IMPLEMENTED;
7067 : }
7068 :
7069 : template <class Derived>
7070 : NS_IMETHODIMP
7071 0 : WorkerPrivateParent<Derived>::
7072 : EventTarget::IsOnCurrentThread(bool* aIsOnCurrentThread)
7073 : {
7074 : // May be called on any thread!
7075 :
7076 0 : MOZ_ASSERT(aIsOnCurrentThread);
7077 :
7078 0 : MutexAutoLock lock(mMutex);
7079 :
7080 0 : if (!mWorkerPrivate) {
7081 0 : NS_WARNING("A worker's event target was used after the worker has !");
7082 0 : return NS_ERROR_UNEXPECTED;
7083 : }
7084 :
7085 0 : *aIsOnCurrentThread = mWorkerPrivate->IsOnCurrentThread();
7086 0 : return NS_OK;
7087 : }
7088 :
7089 : template <class Derived>
7090 : NS_IMETHODIMP_(bool)
7091 0 : WorkerPrivateParent<Derived>::
7092 : EventTarget::IsOnCurrentThreadInfallible()
7093 : {
7094 : // May be called on any thread!
7095 :
7096 0 : MutexAutoLock lock(mMutex);
7097 :
7098 0 : if (!mWorkerPrivate) {
7099 0 : NS_WARNING("A worker's event target was used after the worker has !");
7100 0 : return false;
7101 : }
7102 :
7103 0 : return mWorkerPrivate->IsOnCurrentThread();
7104 : }
7105 :
7106 : BEGIN_WORKERS_NAMESPACE
7107 :
7108 : WorkerCrossThreadDispatcher*
7109 0 : GetWorkerCrossThreadDispatcher(JSContext* aCx, const JS::Value& aWorker)
7110 : {
7111 0 : if (!aWorker.isObject()) {
7112 0 : return nullptr;
7113 : }
7114 :
7115 0 : JS::Rooted<JSObject*> obj(aCx, &aWorker.toObject());
7116 0 : WorkerPrivate* w = nullptr;
7117 0 : UNWRAP_OBJECT(Worker, &obj, w);
7118 0 : MOZ_ASSERT(w);
7119 0 : return w->GetCrossThreadDispatcher();
7120 : }
7121 :
7122 : // Force instantiation.
7123 : template class WorkerPrivateParent<WorkerPrivate>;
7124 :
7125 : END_WORKERS_NAMESPACE
|