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 "js/MemoryMetrics.h"
10 : #include "MessageEventRunnable.h"
11 : #include "mozilla/ScopeExit.h"
12 : #include "mozilla/dom/ClientManager.h"
13 : #include "mozilla/dom/ClientSource.h"
14 : #include "mozilla/dom/ClientState.h"
15 : #include "mozilla/dom/Console.h"
16 : #include "mozilla/dom/DOMPrefs.h"
17 : #include "mozilla/dom/DOMTypes.h"
18 : #include "mozilla/dom/ErrorEvent.h"
19 : #include "mozilla/dom/ErrorEventBinding.h"
20 : #include "mozilla/dom/Event.h"
21 : #include "mozilla/dom/FunctionBinding.h"
22 : #include "mozilla/dom/IndexedDatabaseManager.h"
23 : #include "mozilla/dom/MessageEvent.h"
24 : #include "mozilla/dom/MessageEventBinding.h"
25 : #include "mozilla/dom/MessagePort.h"
26 : #include "mozilla/dom/MessagePortBinding.h"
27 : #include "mozilla/dom/nsCSPUtils.h"
28 : #include "mozilla/dom/Performance.h"
29 : #include "mozilla/dom/PerformanceStorageWorker.h"
30 : #include "mozilla/dom/PromiseDebugging.h"
31 : #include "mozilla/dom/WorkerBinding.h"
32 : #include "mozilla/ThreadEventQueue.h"
33 : #include "mozilla/ThrottledEventQueue.h"
34 : #include "mozilla/TimelineConsumers.h"
35 : #include "mozilla/WorkerTimelineMarker.h"
36 : #include "nsCycleCollector.h"
37 : #include "nsNetUtil.h"
38 : #include "nsIMemoryReporter.h"
39 : #include "nsIPermissionManager.h"
40 : #include "nsIRandomGenerator.h"
41 : #include "nsIScriptError.h"
42 : #include "nsIScriptTimeoutHandler.h"
43 : #include "nsIURI.h"
44 : #include "nsIURL.h"
45 : #include "nsPrintfCString.h"
46 : #include "nsQueryObject.h"
47 : #include "nsRFPService.h"
48 : #include "nsSandboxFlags.h"
49 : #include "nsUTF8Utils.h"
50 :
51 : #include "RuntimeService.h"
52 : #include "ScriptLoader.h"
53 : #include "mozilla/dom/ServiceWorkerEvents.h"
54 : #include "mozilla/dom/ServiceWorkerManager.h"
55 : #include "SharedWorker.h"
56 : #include "WorkerDebugger.h"
57 : #include "WorkerDebuggerManager.h"
58 : #include "WorkerError.h"
59 : #include "WorkerEventTarget.h"
60 : #include "WorkerNavigator.h"
61 : #include "WorkerRunnable.h"
62 : #include "WorkerScope.h"
63 : #include "WorkerThread.h"
64 :
65 : #include "nsThreadManager.h"
66 :
67 : #ifdef XP_WIN
68 : #undef PostMessage
69 : #endif
70 :
71 : // JS_MaybeGC will run once every second during normal execution.
72 : #define PERIODIC_GC_TIMER_DELAY_SEC 1
73 :
74 : // A shrinking GC will run five seconds after the last event is processed.
75 : #define IDLE_GC_TIMER_DELAY_SEC 5
76 :
77 : static mozilla::LazyLogModule sWorkerPrivateLog("WorkerPrivate");
78 : static mozilla::LazyLogModule sWorkerTimeoutsLog("WorkerTimeouts");
79 :
80 : mozilla::LogModule*
81 0 : WorkerLog()
82 : {
83 0 : return sWorkerPrivateLog;
84 : }
85 :
86 : mozilla::LogModule*
87 0 : TimeoutsLog()
88 : {
89 0 : return sWorkerTimeoutsLog;
90 : }
91 :
92 : #ifdef LOG
93 : #undef LOG
94 : #endif
95 : #define LOG(log, _args) MOZ_LOG(log, LogLevel::Debug, _args);
96 :
97 : namespace mozilla {
98 :
99 : using namespace ipc;
100 :
101 : namespace dom {
102 :
103 : using namespace workerinternals;
104 :
105 0 : MOZ_DEFINE_MALLOC_SIZE_OF(JsWorkerMallocSizeOf)
106 :
107 : namespace {
108 :
109 : #ifdef DEBUG
110 :
111 : const nsIID kDEBUGWorkerEventTargetIID = {
112 : 0xccaba3fa, 0x5be2, 0x4de2, { 0xba, 0x87, 0x3b, 0x3b, 0x5b, 0x1d, 0x5, 0xfb }
113 : };
114 :
115 : #endif
116 :
117 : template <class T>
118 : class AutoPtrComparator
119 : {
120 : typedef nsAutoPtr<T> A;
121 : typedef T* B;
122 :
123 : public:
124 0 : bool Equals(const A& a, const B& b) const {
125 0 : return a && b ? *a == *b : !a && !b ? true : false;
126 : }
127 0 : bool LessThan(const A& a, const B& b) const {
128 0 : return a && b ? *a < *b : b ? true : false;
129 : }
130 : };
131 :
132 : template <class T>
133 : inline AutoPtrComparator<T>
134 : GetAutoPtrComparator(const nsTArray<nsAutoPtr<T> >&)
135 : {
136 : return AutoPtrComparator<T>();
137 : }
138 :
139 : // This class is used to wrap any runnables that the worker receives via the
140 : // nsIEventTarget::Dispatch() method (either from NS_DispatchToCurrentThread or
141 : // from the worker's EventTarget).
142 : class ExternalRunnableWrapper final : public WorkerRunnable
143 : {
144 : nsCOMPtr<nsIRunnable> mWrappedRunnable;
145 :
146 : public:
147 0 : ExternalRunnableWrapper(WorkerPrivate* aWorkerPrivate,
148 : nsIRunnable* aWrappedRunnable)
149 0 : : WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
150 0 : mWrappedRunnable(aWrappedRunnable)
151 : {
152 0 : MOZ_ASSERT(aWorkerPrivate);
153 0 : MOZ_ASSERT(aWrappedRunnable);
154 0 : }
155 :
156 0 : NS_INLINE_DECL_REFCOUNTING_INHERITED(ExternalRunnableWrapper, WorkerRunnable)
157 :
158 : private:
159 0 : ~ExternalRunnableWrapper()
160 0 : { }
161 :
162 : virtual bool
163 0 : PreDispatch(WorkerPrivate* aWorkerPrivate) override
164 : {
165 : // Silence bad assertions.
166 0 : return true;
167 : }
168 :
169 : virtual void
170 0 : PostDispatch(WorkerPrivate* aWorkerPrivate, bool aDispatchResult) override
171 : {
172 : // Silence bad assertions.
173 0 : }
174 :
175 : virtual bool
176 0 : WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
177 : {
178 0 : nsresult rv = mWrappedRunnable->Run();
179 0 : if (NS_FAILED(rv)) {
180 0 : if (!JS_IsExceptionPending(aCx)) {
181 0 : Throw(aCx, rv);
182 : }
183 : return false;
184 : }
185 : return true;
186 : }
187 :
188 : nsresult
189 0 : Cancel() override
190 : {
191 : nsresult rv;
192 : nsCOMPtr<nsICancelableRunnable> cancelable =
193 0 : do_QueryInterface(mWrappedRunnable);
194 0 : MOZ_ASSERT(cancelable); // We checked this earlier!
195 0 : rv = cancelable->Cancel();
196 0 : nsresult rv2 = WorkerRunnable::Cancel();
197 0 : return NS_FAILED(rv) ? rv : rv2;
198 : }
199 : };
200 :
201 : struct WindowAction
202 : {
203 : nsPIDOMWindowInner* mWindow;
204 : bool mDefaultAction;
205 :
206 : MOZ_IMPLICIT WindowAction(nsPIDOMWindowInner* aWindow)
207 0 : : mWindow(aWindow), mDefaultAction(true)
208 : { }
209 :
210 : bool
211 : operator==(const WindowAction& aOther) const
212 : {
213 : return mWindow == aOther.mWindow;
214 : }
215 : };
216 :
217 0 : class WorkerFinishedRunnable final : public WorkerControlRunnable
218 : {
219 : WorkerPrivate* mFinishedWorker;
220 :
221 : public:
222 0 : WorkerFinishedRunnable(WorkerPrivate* aWorkerPrivate,
223 : WorkerPrivate* aFinishedWorker)
224 0 : : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
225 0 : mFinishedWorker(aFinishedWorker)
226 0 : { }
227 :
228 : private:
229 : virtual bool
230 0 : PreDispatch(WorkerPrivate* aWorkerPrivate) override
231 : {
232 : // Silence bad assertions.
233 0 : return true;
234 : }
235 :
236 : virtual void
237 0 : PostDispatch(WorkerPrivate* aWorkerPrivate, bool aDispatchResult) override
238 : {
239 : // Silence bad assertions.
240 0 : }
241 :
242 : virtual bool
243 0 : WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
244 : {
245 0 : if (!mFinishedWorker->ProxyReleaseMainThreadObjects()) {
246 0 : NS_WARNING("Failed to dispatch, going to leak!");
247 : }
248 :
249 0 : RuntimeService* runtime = RuntimeService::GetService();
250 0 : NS_ASSERTION(runtime, "This should never be null!");
251 :
252 0 : mFinishedWorker->DisableDebugger();
253 :
254 0 : runtime->UnregisterWorker(mFinishedWorker);
255 :
256 0 : mFinishedWorker->ClearSelfAndParentEventTargetRef();
257 0 : return true;
258 : }
259 : };
260 :
261 : class TopLevelWorkerFinishedRunnable final : public Runnable
262 : {
263 : WorkerPrivate* mFinishedWorker;
264 :
265 : public:
266 0 : explicit TopLevelWorkerFinishedRunnable(WorkerPrivate* aFinishedWorker)
267 0 : : mozilla::Runnable("TopLevelWorkerFinishedRunnable")
268 0 : , mFinishedWorker(aFinishedWorker)
269 : {
270 0 : aFinishedWorker->AssertIsOnWorkerThread();
271 0 : }
272 :
273 0 : NS_INLINE_DECL_REFCOUNTING_INHERITED(TopLevelWorkerFinishedRunnable, Runnable)
274 :
275 : private:
276 0 : ~TopLevelWorkerFinishedRunnable() {}
277 :
278 : NS_IMETHOD
279 0 : Run() override
280 : {
281 0 : AssertIsOnMainThread();
282 :
283 0 : RuntimeService* runtime = RuntimeService::GetService();
284 0 : MOZ_ASSERT(runtime);
285 :
286 0 : mFinishedWorker->DisableDebugger();
287 :
288 0 : runtime->UnregisterWorker(mFinishedWorker);
289 :
290 0 : if (!mFinishedWorker->ProxyReleaseMainThreadObjects()) {
291 0 : NS_WARNING("Failed to dispatch, going to leak!");
292 : }
293 :
294 0 : mFinishedWorker->ClearSelfAndParentEventTargetRef();
295 0 : return NS_OK;
296 : }
297 : };
298 :
299 0 : class ModifyBusyCountRunnable final : public WorkerControlRunnable
300 : {
301 : bool mIncrease;
302 :
303 : public:
304 0 : ModifyBusyCountRunnable(WorkerPrivate* aWorkerPrivate, bool aIncrease)
305 0 : : WorkerControlRunnable(aWorkerPrivate, ParentThreadUnchangedBusyCount),
306 0 : mIncrease(aIncrease)
307 0 : { }
308 :
309 : private:
310 : virtual bool
311 0 : WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
312 : {
313 0 : return aWorkerPrivate->ModifyBusyCount(mIncrease);
314 : }
315 :
316 : virtual void
317 0 : PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate, bool aRunResult)
318 : override
319 : {
320 0 : if (mIncrease) {
321 0 : WorkerControlRunnable::PostRun(aCx, aWorkerPrivate, aRunResult);
322 0 : return;
323 : }
324 : // Don't do anything here as it's possible that aWorkerPrivate has been
325 : // deleted.
326 : }
327 : };
328 :
329 0 : class ReportCompileErrorRunnable final : public WorkerRunnable
330 : {
331 : public:
332 : static void
333 0 : CreateAndDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
334 : {
335 0 : MOZ_ASSERT(aWorkerPrivate);
336 0 : aWorkerPrivate->AssertIsOnWorkerThread();
337 :
338 : RefPtr<ReportCompileErrorRunnable> runnable =
339 0 : new ReportCompileErrorRunnable(aCx, aWorkerPrivate);
340 0 : runnable->Dispatch();
341 0 : }
342 :
343 : private:
344 0 : ReportCompileErrorRunnable(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
345 0 : : WorkerRunnable(aWorkerPrivate, ParentThreadUnchangedBusyCount)
346 : {
347 0 : aWorkerPrivate->AssertIsOnWorkerThread();
348 0 : }
349 :
350 : void
351 0 : PostDispatch(WorkerPrivate* aWorkerPrivate, bool aDispatchResult) override
352 : {
353 0 : aWorkerPrivate->AssertIsOnWorkerThread();
354 :
355 : // Dispatch may fail if the worker was canceled, no need to report that as
356 : // an error, so don't call base class PostDispatch.
357 0 : }
358 :
359 : bool
360 0 : WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
361 : {
362 0 : if (aWorkerPrivate->IsFrozen() ||
363 0 : aWorkerPrivate->IsParentWindowPaused()) {
364 0 : MOZ_ASSERT(!IsDebuggerRunnable());
365 0 : aWorkerPrivate->QueueRunnable(this);
366 0 : return true;
367 : }
368 :
369 0 : if (aWorkerPrivate->IsSharedWorker()) {
370 : aWorkerPrivate->BroadcastErrorToSharedWorkers(aCx, nullptr,
371 0 : /* isErrorEvent */ false);
372 0 : return true;
373 : }
374 :
375 0 : if (aWorkerPrivate->IsServiceWorker()) {
376 0 : RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
377 0 : if (swm) {
378 0 : swm->HandleError(aCx, aWorkerPrivate->GetPrincipal(),
379 : aWorkerPrivate->ServiceWorkerScope(),
380 : aWorkerPrivate->ScriptURL(),
381 : EmptyString(), EmptyString(), EmptyString(),
382 0 : 0, 0, JSREPORT_ERROR, JSEXN_ERR);
383 : }
384 : return true;
385 : }
386 :
387 0 : if (!aWorkerPrivate->IsAcceptingEvents()) {
388 : return true;
389 : }
390 :
391 : RefPtr<mozilla::dom::EventTarget> parentEventTarget =
392 0 : aWorkerPrivate->ParentEventTargetRef();
393 : RefPtr<Event> event =
394 0 : Event::Constructor(parentEventTarget, NS_LITERAL_STRING("error"),
395 0 : EventInit());
396 0 : event->SetTrusted(true);
397 :
398 0 : parentEventTarget->DispatchEvent(*event);
399 : return true;
400 : }
401 : };
402 :
403 0 : class CompileScriptRunnable final : public WorkerRunnable
404 : {
405 : nsString mScriptURL;
406 :
407 : public:
408 0 : explicit CompileScriptRunnable(WorkerPrivate* aWorkerPrivate,
409 : const nsAString& aScriptURL)
410 0 : : WorkerRunnable(aWorkerPrivate),
411 0 : mScriptURL(aScriptURL)
412 0 : { }
413 :
414 : private:
415 : // We can't implement PreRun effectively, because at the point when that would
416 : // run we have not yet done our load so don't know things like our final
417 : // principal and whatnot.
418 :
419 : virtual bool
420 0 : WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
421 : {
422 0 : aWorkerPrivate->AssertIsOnWorkerThread();
423 :
424 0 : if (NS_WARN_IF(!aWorkerPrivate->EnsureClientSource())) {
425 : return false;
426 : }
427 :
428 : // PerformanceStorage & PerformanceCounter both need to be initialized
429 : // on the worker thread before being used on main-thread.
430 : // Let's be sure that it is created before any
431 : // content loading.
432 0 : aWorkerPrivate->EnsurePerformanceStorage();
433 0 : if (mozilla::dom::DOMPrefs::SchedulerLoggingEnabled()) {
434 0 : aWorkerPrivate->EnsurePerformanceCounter();
435 : }
436 :
437 0 : ErrorResult rv;
438 0 : workerinternals::LoadMainScript(aWorkerPrivate, mScriptURL, WorkerScript, rv);
439 0 : rv.WouldReportJSException();
440 : // Explicitly ignore NS_BINDING_ABORTED on rv. Or more precisely, still
441 : // return false and don't SetWorkerScriptExecutedSuccessfully() in that
442 : // case, but don't throw anything on aCx. The idea is to not dispatch error
443 : // events if our load is canceled with that error code.
444 0 : if (rv.ErrorCodeIs(NS_BINDING_ABORTED)) {
445 0 : rv.SuppressException();
446 0 : return false;
447 : }
448 :
449 0 : WorkerGlobalScope* globalScope = aWorkerPrivate->GlobalScope();
450 0 : if (NS_WARN_IF(!globalScope)) {
451 : // We never got as far as calling GetOrCreateGlobalScope, or it failed.
452 : // We have no way to enter a compartment, hence no sane way to report this
453 : // error. :(
454 0 : rv.SuppressException();
455 0 : return false;
456 : }
457 :
458 : // Make sure to propagate exceptions from rv onto aCx, so that they will get
459 : // reported after we return. We want to propagate just JS exceptions,
460 : // because all the other errors are handled when the script is loaded.
461 : // See: https://dom.spec.whatwg.org/#concept-event-fire
462 0 : if (rv.Failed() && !rv.IsJSException()) {
463 0 : ReportCompileErrorRunnable::CreateAndDispatch(aCx, aWorkerPrivate);
464 0 : rv.SuppressException();
465 0 : return false;
466 : }
467 :
468 : // This is a little dumb, but aCx is in the null realm here because we
469 : // set it up that way in our Run(), since we had not created the global at
470 : // that point yet. So we need to enter the realm of our global,
471 : // because setting a pending exception on aCx involves wrapping into its
472 : // current compartment. Luckily we have a global now.
473 0 : JSAutoRealm ar(aCx, globalScope->GetGlobalJSObject());
474 0 : if (rv.MaybeSetPendingException(aCx)) {
475 : return false;
476 : }
477 :
478 0 : aWorkerPrivate->SetWorkerScriptExecutedSuccessfully();
479 0 : return true;
480 : }
481 :
482 : void
483 0 : PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate, bool aRunResult) override
484 : {
485 0 : if (!aRunResult) {
486 0 : aWorkerPrivate->CloseInternal();
487 : }
488 0 : WorkerRunnable::PostRun(aCx, aWorkerPrivate, aRunResult);
489 0 : }
490 : };
491 :
492 0 : class NotifyRunnable final : public WorkerControlRunnable
493 : {
494 : WorkerStatus mStatus;
495 :
496 : public:
497 0 : NotifyRunnable(WorkerPrivate* aWorkerPrivate, WorkerStatus aStatus)
498 0 : : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
499 0 : mStatus(aStatus)
500 : {
501 0 : MOZ_ASSERT(aStatus == Closing || aStatus == Terminating ||
502 : aStatus == Canceling || aStatus == Killing);
503 0 : }
504 :
505 : private:
506 : virtual bool
507 0 : PreDispatch(WorkerPrivate* aWorkerPrivate) override
508 : {
509 0 : aWorkerPrivate->AssertIsOnParentThread();
510 0 : return aWorkerPrivate->ModifyBusyCount(true);
511 : }
512 :
513 : virtual void
514 0 : PostDispatch(WorkerPrivate* aWorkerPrivate, bool aDispatchResult) override
515 : {
516 0 : aWorkerPrivate->AssertIsOnParentThread();
517 0 : if (!aDispatchResult) {
518 : // We couldn't dispatch to the worker, which means it's already dead.
519 : // Undo the busy count modification.
520 0 : aWorkerPrivate->ModifyBusyCount(false);
521 : }
522 0 : }
523 :
524 : virtual void
525 0 : PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate, bool aRunResult)
526 : override
527 : {
528 0 : aWorkerPrivate->ModifyBusyCountFromWorker(false);
529 0 : }
530 :
531 : virtual bool
532 0 : WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
533 : {
534 0 : return aWorkerPrivate->NotifyInternal(mStatus);
535 : }
536 : };
537 :
538 0 : class FreezeRunnable final : public WorkerControlRunnable
539 : {
540 : public:
541 0 : explicit FreezeRunnable(WorkerPrivate* aWorkerPrivate)
542 0 : : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
543 0 : { }
544 :
545 : private:
546 : virtual bool
547 0 : WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
548 : {
549 0 : return aWorkerPrivate->FreezeInternal();
550 : }
551 : };
552 :
553 0 : class ThawRunnable final : public WorkerControlRunnable
554 : {
555 : public:
556 0 : explicit ThawRunnable(WorkerPrivate* aWorkerPrivate)
557 0 : : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
558 0 : { }
559 :
560 : private:
561 : virtual bool
562 0 : WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
563 : {
564 0 : return aWorkerPrivate->ThawInternal();
565 : }
566 : };
567 :
568 0 : class ReportErrorToConsoleRunnable final : public WorkerRunnable
569 : {
570 : const char* mMessage;
571 :
572 : public:
573 : // aWorkerPrivate is the worker thread we're on (or the main thread, if null)
574 : static void
575 0 : Report(WorkerPrivate* aWorkerPrivate, const char* aMessage)
576 : {
577 0 : if (aWorkerPrivate) {
578 0 : aWorkerPrivate->AssertIsOnWorkerThread();
579 : } else {
580 0 : AssertIsOnMainThread();
581 : }
582 :
583 : // Now fire a runnable to do the same on the parent's thread if we can.
584 0 : if (aWorkerPrivate) {
585 : RefPtr<ReportErrorToConsoleRunnable> runnable =
586 0 : new ReportErrorToConsoleRunnable(aWorkerPrivate, aMessage);
587 0 : runnable->Dispatch();
588 : return;
589 : }
590 :
591 : // Log a warning to the console.
592 0 : nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
593 0 : NS_LITERAL_CSTRING("DOM"),
594 : nullptr,
595 : nsContentUtils::eDOM_PROPERTIES,
596 0 : aMessage);
597 : }
598 :
599 : private:
600 0 : ReportErrorToConsoleRunnable(WorkerPrivate* aWorkerPrivate, const char* aMessage)
601 0 : : WorkerRunnable(aWorkerPrivate, ParentThreadUnchangedBusyCount),
602 0 : mMessage(aMessage)
603 0 : { }
604 :
605 : virtual void
606 0 : PostDispatch(WorkerPrivate* aWorkerPrivate, bool aDispatchResult) override
607 : {
608 0 : aWorkerPrivate->AssertIsOnWorkerThread();
609 :
610 : // Dispatch may fail if the worker was canceled, no need to report that as
611 : // an error, so don't call base class PostDispatch.
612 0 : }
613 :
614 : virtual bool
615 0 : WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
616 : {
617 0 : WorkerPrivate* parent = aWorkerPrivate->GetParent();
618 0 : MOZ_ASSERT_IF(!parent, NS_IsMainThread());
619 0 : Report(parent, mMessage);
620 0 : return true;
621 : }
622 : };
623 :
624 : class TimerRunnable final : public WorkerRunnable,
625 : public nsITimerCallback,
626 : public nsINamed
627 : {
628 : public:
629 : NS_DECL_ISUPPORTS_INHERITED
630 :
631 0 : explicit TimerRunnable(WorkerPrivate* aWorkerPrivate)
632 0 : : WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
633 0 : { }
634 :
635 : private:
636 0 : ~TimerRunnable() {}
637 :
638 : virtual bool
639 0 : PreDispatch(WorkerPrivate* aWorkerPrivate) override
640 : {
641 : // Silence bad assertions.
642 0 : return true;
643 : }
644 :
645 : virtual void
646 0 : PostDispatch(WorkerPrivate* aWorkerPrivate, bool aDispatchResult) override
647 : {
648 : // Silence bad assertions.
649 0 : }
650 :
651 : virtual bool
652 0 : WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
653 : {
654 0 : return aWorkerPrivate->RunExpiredTimeouts(aCx);
655 : }
656 :
657 : NS_IMETHOD
658 0 : Notify(nsITimer* aTimer) override
659 : {
660 0 : return Run();
661 : }
662 :
663 : NS_IMETHOD
664 0 : GetName(nsACString& aName) override
665 : {
666 0 : aName.AssignLiteral("TimerRunnable");
667 0 : return NS_OK;
668 : }
669 : };
670 :
671 0 : NS_IMPL_ISUPPORTS_INHERITED(TimerRunnable, WorkerRunnable, nsITimerCallback,
672 : nsINamed)
673 :
674 0 : class DebuggerImmediateRunnable : public WorkerRunnable
675 : {
676 : RefPtr<dom::Function> mHandler;
677 :
678 : public:
679 0 : explicit DebuggerImmediateRunnable(WorkerPrivate* aWorkerPrivate,
680 : dom::Function& aHandler)
681 0 : : WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
682 0 : mHandler(&aHandler)
683 0 : { }
684 :
685 : private:
686 : virtual bool
687 0 : IsDebuggerRunnable() const override
688 : {
689 0 : return true;
690 : }
691 :
692 : virtual bool
693 0 : PreDispatch(WorkerPrivate* aWorkerPrivate) override
694 : {
695 : // Silence bad assertions.
696 0 : return true;
697 : }
698 :
699 : virtual void
700 0 : PostDispatch(WorkerPrivate* aWorkerPrivate, bool aDispatchResult) override
701 : {
702 : // Silence bad assertions.
703 0 : }
704 :
705 : virtual bool
706 0 : WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
707 : {
708 0 : JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx));
709 0 : JS::Rooted<JS::Value> callable(aCx, JS::ObjectOrNullValue(mHandler->CallableOrNull()));
710 0 : JS::HandleValueArray args = JS::HandleValueArray::empty();
711 0 : JS::Rooted<JS::Value> rval(aCx);
712 0 : if (!JS_CallFunctionValue(aCx, global, callable, args, &rval)) {
713 : // Just return false; WorkerRunnable::Run will report the exception.
714 : return false;
715 : }
716 :
717 0 : return true;
718 : }
719 : };
720 :
721 : void
722 0 : PeriodicGCTimerCallback(nsITimer* aTimer, void* aClosure)
723 : {
724 0 : auto workerPrivate = static_cast<WorkerPrivate*>(aClosure);
725 0 : MOZ_DIAGNOSTIC_ASSERT(workerPrivate);
726 0 : workerPrivate->AssertIsOnWorkerThread();
727 0 : workerPrivate->GarbageCollectInternal(workerPrivate->GetJSContext(),
728 : false /* shrinking */,
729 0 : false /* collect children */);
730 0 : }
731 :
732 : void
733 0 : IdleGCTimerCallback(nsITimer* aTimer, void* aClosure)
734 : {
735 0 : auto workerPrivate = static_cast<WorkerPrivate*>(aClosure);
736 0 : MOZ_DIAGNOSTIC_ASSERT(workerPrivate);
737 0 : workerPrivate->AssertIsOnWorkerThread();
738 0 : workerPrivate->GarbageCollectInternal(workerPrivate->GetJSContext(),
739 : true /* shrinking */,
740 0 : false /* collect children */);
741 0 : }
742 :
743 0 : class UpdateContextOptionsRunnable final : public WorkerControlRunnable
744 : {
745 : JS::ContextOptions mContextOptions;
746 :
747 : public:
748 0 : UpdateContextOptionsRunnable(WorkerPrivate* aWorkerPrivate,
749 : const JS::ContextOptions& aContextOptions)
750 0 : : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
751 0 : mContextOptions(aContextOptions)
752 0 : { }
753 :
754 : private:
755 : virtual bool
756 0 : WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
757 : {
758 0 : aWorkerPrivate->UpdateContextOptionsInternal(aCx, mContextOptions);
759 0 : return true;
760 : }
761 : };
762 :
763 0 : class UpdateLanguagesRunnable final : public WorkerRunnable
764 : {
765 : nsTArray<nsString> mLanguages;
766 :
767 : public:
768 0 : UpdateLanguagesRunnable(WorkerPrivate* aWorkerPrivate,
769 : const nsTArray<nsString>& aLanguages)
770 0 : : WorkerRunnable(aWorkerPrivate),
771 0 : mLanguages(aLanguages)
772 0 : { }
773 :
774 : virtual bool
775 0 : WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
776 : {
777 0 : aWorkerPrivate->UpdateLanguagesInternal(mLanguages);
778 0 : return true;
779 : }
780 : };
781 :
782 0 : class UpdateJSWorkerMemoryParameterRunnable final :
783 : public WorkerControlRunnable
784 : {
785 : uint32_t mValue;
786 : JSGCParamKey mKey;
787 :
788 : public:
789 0 : UpdateJSWorkerMemoryParameterRunnable(WorkerPrivate* aWorkerPrivate,
790 : JSGCParamKey aKey,
791 : uint32_t aValue)
792 0 : : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
793 0 : mValue(aValue), mKey(aKey)
794 0 : { }
795 :
796 : private:
797 : virtual bool
798 0 : WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
799 : {
800 0 : aWorkerPrivate->UpdateJSWorkerMemoryParameterInternal(aCx, mKey, mValue);
801 0 : return true;
802 : }
803 : };
804 :
805 : #ifdef JS_GC_ZEAL
806 0 : class UpdateGCZealRunnable final : public WorkerControlRunnable
807 : {
808 : uint8_t mGCZeal;
809 : uint32_t mFrequency;
810 :
811 : public:
812 0 : UpdateGCZealRunnable(WorkerPrivate* aWorkerPrivate,
813 : uint8_t aGCZeal,
814 : uint32_t aFrequency)
815 0 : : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
816 0 : mGCZeal(aGCZeal), mFrequency(aFrequency)
817 0 : { }
818 :
819 : private:
820 : virtual bool
821 0 : WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
822 : {
823 0 : aWorkerPrivate->UpdateGCZealInternal(aCx, mGCZeal, mFrequency);
824 0 : return true;
825 : }
826 : };
827 : #endif
828 :
829 0 : class GarbageCollectRunnable final : public WorkerControlRunnable
830 : {
831 : bool mShrinking;
832 : bool mCollectChildren;
833 :
834 : public:
835 0 : GarbageCollectRunnable(WorkerPrivate* aWorkerPrivate, bool aShrinking,
836 : bool aCollectChildren)
837 0 : : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
838 0 : mShrinking(aShrinking), mCollectChildren(aCollectChildren)
839 0 : { }
840 :
841 : private:
842 : virtual bool
843 0 : PreDispatch(WorkerPrivate* aWorkerPrivate) override
844 : {
845 : // Silence bad assertions, this can be dispatched from either the main
846 : // thread or the timer thread..
847 0 : return true;
848 : }
849 :
850 : virtual void
851 0 : PostDispatch(WorkerPrivate* aWorkerPrivate, bool aDispatchResult) override
852 : {
853 : // Silence bad assertions, this can be dispatched from either the main
854 : // thread or the timer thread..
855 0 : }
856 :
857 : virtual bool
858 0 : WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
859 : {
860 0 : aWorkerPrivate->GarbageCollectInternal(aCx, mShrinking, mCollectChildren);
861 0 : return true;
862 : }
863 : };
864 :
865 0 : class CycleCollectRunnable : public WorkerControlRunnable
866 : {
867 : bool mCollectChildren;
868 :
869 : public:
870 0 : CycleCollectRunnable(WorkerPrivate* aWorkerPrivate, bool aCollectChildren)
871 0 : : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
872 0 : mCollectChildren(aCollectChildren)
873 0 : { }
874 :
875 : bool
876 0 : WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
877 : {
878 0 : aWorkerPrivate->CycleCollectInternal(mCollectChildren);
879 0 : return true;
880 : }
881 : };
882 :
883 0 : class OfflineStatusChangeRunnable : public WorkerRunnable
884 : {
885 : public:
886 0 : OfflineStatusChangeRunnable(WorkerPrivate* aWorkerPrivate, bool aIsOffline)
887 0 : : WorkerRunnable(aWorkerPrivate),
888 0 : mIsOffline(aIsOffline)
889 : {
890 0 : }
891 :
892 : bool
893 0 : WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
894 : {
895 0 : aWorkerPrivate->OfflineStatusChangeEventInternal(mIsOffline);
896 0 : return true;
897 : }
898 :
899 : private:
900 : bool mIsOffline;
901 : };
902 :
903 0 : class MemoryPressureRunnable : public WorkerControlRunnable
904 : {
905 : public:
906 0 : explicit MemoryPressureRunnable(WorkerPrivate* aWorkerPrivate)
907 0 : : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
908 0 : {}
909 :
910 : bool
911 0 : WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
912 : {
913 0 : aWorkerPrivate->MemoryPressureInternal();
914 0 : return true;
915 : }
916 : };
917 :
918 : #ifdef DEBUG
919 : static bool
920 0 : StartsWithExplicit(nsACString& s)
921 : {
922 0 : return StringBeginsWith(s, NS_LITERAL_CSTRING("explicit/"));
923 : }
924 : #endif
925 :
926 : class MessagePortRunnable final : public WorkerRunnable
927 : {
928 : MessagePortIdentifier mPortIdentifier;
929 :
930 : public:
931 0 : MessagePortRunnable(WorkerPrivate* aWorkerPrivate, MessagePort* aPort)
932 0 : : WorkerRunnable(aWorkerPrivate)
933 : {
934 0 : MOZ_ASSERT(aPort);
935 : // In order to move the port from one thread to another one, we have to
936 : // close and disentangle it. The output will be a MessagePortIdentifier that
937 : // will be used to recreate a new MessagePort on the other thread.
938 0 : aPort->CloneAndDisentangle(mPortIdentifier);
939 0 : }
940 :
941 : private:
942 0 : ~MessagePortRunnable()
943 0 : { }
944 :
945 : virtual bool
946 0 : WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
947 : {
948 0 : return aWorkerPrivate->ConnectMessagePort(aCx, mPortIdentifier);
949 : }
950 :
951 : nsresult
952 0 : Cancel() override
953 : {
954 0 : MessagePort::ForceClose(mPortIdentifier);
955 0 : return WorkerRunnable::Cancel();
956 : }
957 : };
958 :
959 : PRThread*
960 0 : PRThreadFromThread(nsIThread* aThread)
961 : {
962 0 : MOZ_ASSERT(aThread);
963 :
964 : PRThread* result;
965 0 : MOZ_ALWAYS_SUCCEEDS(aThread->GetPRThread(&result));
966 0 : MOZ_ASSERT(result);
967 :
968 0 : return result;
969 : }
970 :
971 0 : class SimpleWorkerHolder final : public WorkerHolder
972 : {
973 : public:
974 0 : SimpleWorkerHolder()
975 0 : : WorkerHolder("SimpleWorkerHolder")
976 0 : {}
977 :
978 0 : virtual bool Notify(WorkerStatus aStatus) override { return true; }
979 : };
980 :
981 : // A runnable to cancel the worker from the parent thread when self.close() is
982 : // called. This runnable is executed on the parent process in order to cancel
983 : // the current runnable. It uses a normal WorkerRunnable in order to be sure
984 : // that all the pending WorkerRunnables are executed before this.
985 0 : class CancelingOnParentRunnable final : public WorkerRunnable
986 : {
987 : public:
988 0 : explicit CancelingOnParentRunnable(WorkerPrivate* aWorkerPrivate)
989 0 : : WorkerRunnable(aWorkerPrivate, ParentThreadUnchangedBusyCount)
990 0 : {}
991 :
992 : bool
993 0 : WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
994 : {
995 0 : aWorkerPrivate->Cancel();
996 0 : return true;
997 : }
998 : };
999 :
1000 : // A runnable to cancel the worker from the parent process.
1001 0 : class CancelingWithTimeoutOnParentRunnable final : public WorkerControlRunnable
1002 : {
1003 : public:
1004 0 : explicit CancelingWithTimeoutOnParentRunnable(WorkerPrivate* aWorkerPrivate)
1005 0 : : WorkerControlRunnable(aWorkerPrivate, ParentThreadUnchangedBusyCount)
1006 0 : {}
1007 :
1008 : bool
1009 0 : WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
1010 : {
1011 0 : aWorkerPrivate->AssertIsOnParentThread();
1012 0 : aWorkerPrivate->StartCancelingTimer();
1013 0 : return true;
1014 : }
1015 : };
1016 :
1017 : class CancelingTimerCallback final : public nsITimerCallback
1018 : {
1019 : public:
1020 : NS_DECL_ISUPPORTS
1021 :
1022 0 : explicit CancelingTimerCallback(WorkerPrivate* aWorkerPrivate)
1023 0 : : mWorkerPrivate(aWorkerPrivate)
1024 0 : {}
1025 :
1026 : NS_IMETHOD
1027 0 : Notify(nsITimer* aTimer) override
1028 : {
1029 0 : mWorkerPrivate->AssertIsOnParentThread();
1030 0 : mWorkerPrivate->Cancel();
1031 0 : return NS_OK;
1032 : }
1033 :
1034 : private:
1035 : ~CancelingTimerCallback() = default;
1036 :
1037 : // Raw pointer here is OK because the timer is canceled during the shutdown
1038 : // steps.
1039 : WorkerPrivate* mWorkerPrivate;
1040 : };
1041 :
1042 0 : NS_IMPL_ISUPPORTS(CancelingTimerCallback, nsITimerCallback)
1043 :
1044 : // This runnable starts the canceling of a worker after a self.close().
1045 0 : class CancelingRunnable final : public Runnable
1046 : {
1047 : public:
1048 0 : CancelingRunnable()
1049 0 : : Runnable("CancelingRunnable")
1050 0 : {}
1051 :
1052 : NS_IMETHOD
1053 0 : Run() override
1054 : {
1055 0 : WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
1056 0 : MOZ_ASSERT(workerPrivate);
1057 0 : workerPrivate->AssertIsOnWorkerThread();
1058 :
1059 : // Now we can cancel the this worker from the parent process.
1060 : RefPtr<CancelingOnParentRunnable> r =
1061 0 : new CancelingOnParentRunnable(workerPrivate);
1062 0 : r->Dispatch();
1063 :
1064 0 : return NS_OK;
1065 : }
1066 : };
1067 :
1068 : } /* anonymous namespace */
1069 :
1070 : class WorkerPrivate::EventTarget final : public nsISerialEventTarget
1071 : {
1072 : // This mutex protects mWorkerPrivate and must be acquired *before* the
1073 : // WorkerPrivate's mutex whenever they must both be held.
1074 : mozilla::Mutex mMutex;
1075 : WorkerPrivate* mWorkerPrivate;
1076 : nsIEventTarget* mWeakNestedEventTarget;
1077 : nsCOMPtr<nsIEventTarget> mNestedEventTarget;
1078 :
1079 : public:
1080 : explicit EventTarget(WorkerPrivate* aWorkerPrivate)
1081 : : mMutex("WorkerPrivate::EventTarget::mMutex"),
1082 : mWorkerPrivate(aWorkerPrivate), mWeakNestedEventTarget(nullptr)
1083 : {
1084 : MOZ_ASSERT(aWorkerPrivate);
1085 : }
1086 :
1087 0 : EventTarget(WorkerPrivate* aWorkerPrivate, nsIEventTarget* aNestedEventTarget)
1088 0 : : mMutex("WorkerPrivate::EventTarget::mMutex"),
1089 : mWorkerPrivate(aWorkerPrivate), mWeakNestedEventTarget(aNestedEventTarget),
1090 0 : mNestedEventTarget(aNestedEventTarget)
1091 : {
1092 0 : MOZ_ASSERT(aWorkerPrivate);
1093 0 : MOZ_ASSERT(aNestedEventTarget);
1094 0 : }
1095 :
1096 : void
1097 0 : Disable()
1098 : {
1099 0 : nsCOMPtr<nsIEventTarget> nestedEventTarget;
1100 : {
1101 0 : MutexAutoLock lock(mMutex);
1102 :
1103 : // Note, Disable() can be called more than once safely.
1104 0 : mWorkerPrivate = nullptr;
1105 0 : mNestedEventTarget.swap(nestedEventTarget);
1106 : }
1107 0 : }
1108 :
1109 : nsIEventTarget*
1110 0 : GetWeakNestedEventTarget() const
1111 : {
1112 0 : MOZ_ASSERT(mWeakNestedEventTarget);
1113 0 : return mWeakNestedEventTarget;
1114 : }
1115 :
1116 : NS_DECL_THREADSAFE_ISUPPORTS
1117 : NS_DECL_NSIEVENTTARGET_FULL
1118 :
1119 : private:
1120 0 : ~EventTarget()
1121 0 : { }
1122 : };
1123 :
1124 : struct WorkerPrivate::TimeoutInfo
1125 : {
1126 0 : TimeoutInfo()
1127 0 : : mId(0), mIsInterval(false), mCanceled(false)
1128 : {
1129 0 : MOZ_COUNT_CTOR(mozilla::dom::WorkerPrivate::TimeoutInfo);
1130 0 : }
1131 :
1132 0 : ~TimeoutInfo()
1133 0 : {
1134 0 : MOZ_COUNT_DTOR(mozilla::dom::WorkerPrivate::TimeoutInfo);
1135 0 : }
1136 :
1137 : bool operator==(const TimeoutInfo& aOther)
1138 : {
1139 0 : return mTargetTime == aOther.mTargetTime;
1140 : }
1141 :
1142 : bool operator<(const TimeoutInfo& aOther)
1143 : {
1144 0 : return mTargetTime < aOther.mTargetTime;
1145 : }
1146 :
1147 : nsCOMPtr<nsIScriptTimeoutHandler> mHandler;
1148 : mozilla::TimeStamp mTargetTime;
1149 : mozilla::TimeDuration mInterval;
1150 : int32_t mId;
1151 : bool mIsInterval;
1152 : bool mCanceled;
1153 : };
1154 :
1155 : class WorkerJSContextStats final : public JS::RuntimeStats
1156 : {
1157 : const nsCString mRtPath;
1158 :
1159 : public:
1160 0 : explicit WorkerJSContextStats(const nsACString& aRtPath)
1161 0 : : JS::RuntimeStats(JsWorkerMallocSizeOf), mRtPath(aRtPath)
1162 0 : { }
1163 :
1164 0 : ~WorkerJSContextStats()
1165 0 : {
1166 0 : for (size_t i = 0; i != zoneStatsVector.length(); i++) {
1167 0 : delete static_cast<xpc::ZoneStatsExtras*>(zoneStatsVector[i].extra);
1168 : }
1169 :
1170 0 : for (size_t i = 0; i != realmStatsVector.length(); i++) {
1171 0 : delete static_cast<xpc::RealmStatsExtras*>(realmStatsVector[i].extra);
1172 : }
1173 0 : }
1174 :
1175 : const nsCString& Path() const
1176 : {
1177 0 : return mRtPath;
1178 : }
1179 :
1180 : virtual void
1181 0 : initExtraZoneStats(JS::Zone* aZone,
1182 : JS::ZoneStats* aZoneStats)
1183 : override
1184 : {
1185 0 : MOZ_ASSERT(!aZoneStats->extra);
1186 :
1187 : // ReportJSRuntimeExplicitTreeStats expects that
1188 : // aZoneStats->extra is a xpc::ZoneStatsExtras pointer.
1189 0 : xpc::ZoneStatsExtras* extras = new xpc::ZoneStatsExtras;
1190 0 : extras->pathPrefix = mRtPath;
1191 0 : extras->pathPrefix += nsPrintfCString("zone(0x%p)/", (void *)aZone);
1192 :
1193 0 : MOZ_ASSERT(StartsWithExplicit(extras->pathPrefix));
1194 :
1195 0 : aZoneStats->extra = extras;
1196 0 : }
1197 :
1198 : virtual void
1199 0 : initExtraRealmStats(JS::Handle<JS::Realm*> aRealm,
1200 : JS::RealmStats* aRealmStats)
1201 : override
1202 : {
1203 0 : MOZ_ASSERT(!aRealmStats->extra);
1204 :
1205 : // ReportJSRuntimeExplicitTreeStats expects that
1206 : // aRealmStats->extra is a xpc::RealmStatsExtras pointer.
1207 0 : xpc::RealmStatsExtras* extras = new xpc::RealmStatsExtras;
1208 :
1209 : // This is the |jsPathPrefix|. Each worker has exactly one realm.
1210 0 : extras->jsPathPrefix.Assign(mRtPath);
1211 0 : extras->jsPathPrefix += nsPrintfCString("zone(0x%p)/",
1212 0 : (void *)js::GetRealmZone(aRealm));
1213 0 : extras->jsPathPrefix += NS_LITERAL_CSTRING("realm(web-worker)/");
1214 :
1215 : // This should never be used when reporting with workers (hence the "?!").
1216 0 : extras->domPathPrefix.AssignLiteral("explicit/workers/?!/");
1217 :
1218 0 : MOZ_ASSERT(StartsWithExplicit(extras->jsPathPrefix));
1219 0 : MOZ_ASSERT(StartsWithExplicit(extras->domPathPrefix));
1220 :
1221 0 : extras->location = nullptr;
1222 :
1223 0 : aRealmStats->extra = extras;
1224 0 : }
1225 : };
1226 :
1227 : class WorkerPrivate::MemoryReporter final : public nsIMemoryReporter
1228 : {
1229 : NS_DECL_THREADSAFE_ISUPPORTS
1230 :
1231 : friend class WorkerPrivate;
1232 :
1233 : SharedMutex mMutex;
1234 : WorkerPrivate* mWorkerPrivate;
1235 :
1236 : public:
1237 3 : explicit MemoryReporter(WorkerPrivate* aWorkerPrivate)
1238 0 : : mMutex(aWorkerPrivate->mMutex), mWorkerPrivate(aWorkerPrivate)
1239 : {
1240 3 : aWorkerPrivate->AssertIsOnWorkerThread();
1241 0 : }
1242 :
1243 : NS_IMETHOD
1244 : CollectReports(nsIHandleReportCallback* aHandleReport,
1245 : nsISupports* aData, bool aAnonymize) override;
1246 :
1247 : private:
1248 : class FinishCollectRunnable;
1249 :
1250 : class CollectReportsRunnable final : public MainThreadWorkerControlRunnable
1251 : {
1252 : RefPtr<FinishCollectRunnable> mFinishCollectRunnable;
1253 : const bool mAnonymize;
1254 :
1255 : public:
1256 : CollectReportsRunnable(
1257 : WorkerPrivate* aWorkerPrivate,
1258 : nsIHandleReportCallback* aHandleReport,
1259 : nsISupports* aHandlerData,
1260 : bool aAnonymize,
1261 : const nsACString& aPath);
1262 :
1263 : private:
1264 : bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override;
1265 :
1266 0 : ~CollectReportsRunnable()
1267 0 : {
1268 0 : if (NS_IsMainThread()) {
1269 0 : mFinishCollectRunnable->Run();
1270 0 : return;
1271 : }
1272 :
1273 0 : WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
1274 0 : MOZ_ASSERT(workerPrivate);
1275 0 : MOZ_ALWAYS_SUCCEEDS(
1276 : workerPrivate->DispatchToMainThread(mFinishCollectRunnable.forget()));
1277 0 : }
1278 : };
1279 :
1280 : class FinishCollectRunnable final : public Runnable
1281 : {
1282 : nsCOMPtr<nsIHandleReportCallback> mHandleReport;
1283 : nsCOMPtr<nsISupports> mHandlerData;
1284 : size_t mPerformanceUserEntries;
1285 : size_t mPerformanceResourceEntries;
1286 : const bool mAnonymize;
1287 : bool mSuccess;
1288 :
1289 : public:
1290 : WorkerJSContextStats mCxStats;
1291 :
1292 : explicit FinishCollectRunnable(
1293 : nsIHandleReportCallback* aHandleReport,
1294 : nsISupports* aHandlerData,
1295 : bool aAnonymize,
1296 : const nsACString& aPath);
1297 :
1298 : NS_IMETHOD Run() override;
1299 :
1300 : void SetPerformanceSizes(size_t userEntries, size_t resourceEntries)
1301 : {
1302 0 : mPerformanceUserEntries = userEntries;
1303 0 : mPerformanceResourceEntries = resourceEntries;
1304 : }
1305 :
1306 : void SetSuccess(bool success)
1307 : {
1308 0 : mSuccess = success;
1309 : }
1310 :
1311 : private:
1312 0 : ~FinishCollectRunnable()
1313 0 : {
1314 : // mHandleReport and mHandlerData are released on the main thread.
1315 0 : AssertIsOnMainThread();
1316 0 : }
1317 :
1318 : FinishCollectRunnable(const FinishCollectRunnable&) = delete;
1319 : FinishCollectRunnable& operator=(const FinishCollectRunnable&) = delete;
1320 : FinishCollectRunnable& operator=(const FinishCollectRunnable&&) = delete;
1321 : };
1322 :
1323 0 : ~MemoryReporter()
1324 0 : {
1325 0 : }
1326 :
1327 : void
1328 0 : Disable()
1329 : {
1330 : // Called from WorkerPrivate::DisableMemoryReporter.
1331 0 : mMutex.AssertCurrentThreadOwns();
1332 :
1333 0 : NS_ASSERTION(mWorkerPrivate, "Disabled more than once!");
1334 0 : mWorkerPrivate = nullptr;
1335 0 : }
1336 : };
1337 :
1338 21 : NS_IMPL_ISUPPORTS(WorkerPrivate::MemoryReporter, nsIMemoryReporter)
1339 :
1340 : NS_IMETHODIMP
1341 0 : WorkerPrivate::MemoryReporter::CollectReports(nsIHandleReportCallback* aHandleReport,
1342 : nsISupports* aData,
1343 : bool aAnonymize)
1344 : {
1345 0 : AssertIsOnMainThread();
1346 :
1347 0 : RefPtr<CollectReportsRunnable> runnable;
1348 :
1349 : {
1350 0 : MutexAutoLock lock(mMutex);
1351 :
1352 0 : if (!mWorkerPrivate) {
1353 : // This will effectively report 0 memory.
1354 : nsCOMPtr<nsIMemoryReporterManager> manager =
1355 0 : do_GetService("@mozilla.org/memory-reporter-manager;1");
1356 0 : if (manager) {
1357 0 : manager->EndReport();
1358 : }
1359 : return NS_OK;
1360 : }
1361 :
1362 0 : nsAutoCString path;
1363 0 : path.AppendLiteral("explicit/workers/workers(");
1364 0 : if (aAnonymize && !mWorkerPrivate->Domain().IsEmpty()) {
1365 0 : path.AppendLiteral("<anonymized-domain>)/worker(<anonymized-url>");
1366 : } else {
1367 0 : nsAutoCString escapedDomain(mWorkerPrivate->Domain());
1368 0 : if (escapedDomain.IsEmpty()) {
1369 0 : escapedDomain += "chrome";
1370 : } else {
1371 0 : escapedDomain.ReplaceChar('/', '\\');
1372 : }
1373 0 : path.Append(escapedDomain);
1374 0 : path.AppendLiteral(")/worker(");
1375 0 : NS_ConvertUTF16toUTF8 escapedURL(mWorkerPrivate->ScriptURL());
1376 0 : escapedURL.ReplaceChar('/', '\\');
1377 0 : path.Append(escapedURL);
1378 : }
1379 0 : path.AppendPrintf(", 0x%p)/", static_cast<void*>(mWorkerPrivate));
1380 :
1381 : runnable =
1382 0 : new CollectReportsRunnable(mWorkerPrivate, aHandleReport, aData, aAnonymize, path);
1383 : }
1384 :
1385 0 : if (!runnable->Dispatch()) {
1386 : return NS_ERROR_UNEXPECTED;
1387 : }
1388 :
1389 0 : return NS_OK;
1390 : }
1391 :
1392 0 : WorkerPrivate::MemoryReporter::CollectReportsRunnable::CollectReportsRunnable(
1393 : WorkerPrivate* aWorkerPrivate,
1394 : nsIHandleReportCallback* aHandleReport,
1395 : nsISupports* aHandlerData,
1396 : bool aAnonymize,
1397 0 : const nsACString& aPath)
1398 : : MainThreadWorkerControlRunnable(aWorkerPrivate),
1399 : mFinishCollectRunnable(
1400 0 : new FinishCollectRunnable(aHandleReport, aHandlerData, aAnonymize, aPath)),
1401 0 : mAnonymize(aAnonymize)
1402 0 : { }
1403 :
1404 : bool
1405 0 : WorkerPrivate::MemoryReporter::CollectReportsRunnable::WorkerRun(JSContext* aCx,
1406 : WorkerPrivate* aWorkerPrivate)
1407 : {
1408 0 : aWorkerPrivate->AssertIsOnWorkerThread();
1409 :
1410 0 : RefPtr<WorkerGlobalScope> scope = aWorkerPrivate->GlobalScope();
1411 0 : RefPtr<Performance> performance = scope ? scope->GetPerformanceIfExists()
1412 0 : : nullptr;
1413 0 : if (performance) {
1414 0 : size_t userEntries = performance->SizeOfUserEntries(JsWorkerMallocSizeOf);
1415 : size_t resourceEntries =
1416 0 : performance->SizeOfResourceEntries(JsWorkerMallocSizeOf);
1417 0 : mFinishCollectRunnable->SetPerformanceSizes(userEntries, resourceEntries);
1418 : }
1419 :
1420 0 : mFinishCollectRunnable->SetSuccess(
1421 0 : aWorkerPrivate->CollectRuntimeStats(&mFinishCollectRunnable->mCxStats, mAnonymize));
1422 :
1423 0 : return true;
1424 : }
1425 :
1426 0 : WorkerPrivate::MemoryReporter::FinishCollectRunnable::FinishCollectRunnable(
1427 : nsIHandleReportCallback* aHandleReport,
1428 : nsISupports* aHandlerData,
1429 : bool aAnonymize,
1430 0 : const nsACString& aPath)
1431 : : mozilla::Runnable("dom::WorkerPrivate::MemoryReporter::FinishCollectRunnable")
1432 : , mHandleReport(aHandleReport)
1433 : , mHandlerData(aHandlerData)
1434 : , mPerformanceUserEntries(0)
1435 : , mPerformanceResourceEntries(0)
1436 : , mAnonymize(aAnonymize)
1437 : , mSuccess(false)
1438 0 : , mCxStats(aPath)
1439 0 : { }
1440 :
1441 : NS_IMETHODIMP
1442 0 : WorkerPrivate::MemoryReporter::FinishCollectRunnable::Run()
1443 : {
1444 0 : AssertIsOnMainThread();
1445 :
1446 : nsCOMPtr<nsIMemoryReporterManager> manager =
1447 0 : do_GetService("@mozilla.org/memory-reporter-manager;1");
1448 :
1449 0 : if (!manager)
1450 : return NS_OK;
1451 :
1452 0 : if (mSuccess) {
1453 0 : xpc::ReportJSRuntimeExplicitTreeStats(mCxStats, mCxStats.Path(),
1454 : mHandleReport, mHandlerData,
1455 0 : mAnonymize);
1456 :
1457 0 : if (mPerformanceUserEntries) {
1458 0 : nsCString path = mCxStats.Path();
1459 0 : path.AppendLiteral("dom/performance/user-entries");
1460 0 : mHandleReport->Callback(EmptyCString(), path,
1461 : nsIMemoryReporter::KIND_HEAP,
1462 : nsIMemoryReporter::UNITS_BYTES,
1463 0 : mPerformanceUserEntries,
1464 0 : NS_LITERAL_CSTRING("Memory used for performance user entries."),
1465 0 : mHandlerData);
1466 : }
1467 :
1468 0 : if (mPerformanceResourceEntries) {
1469 0 : nsCString path = mCxStats.Path();
1470 0 : path.AppendLiteral("dom/performance/resource-entries");
1471 0 : mHandleReport->Callback(EmptyCString(), path,
1472 : nsIMemoryReporter::KIND_HEAP,
1473 : nsIMemoryReporter::UNITS_BYTES,
1474 0 : mPerformanceResourceEntries,
1475 0 : NS_LITERAL_CSTRING("Memory used for performance resource entries."),
1476 0 : mHandlerData);
1477 : }
1478 : }
1479 :
1480 0 : manager->EndReport();
1481 :
1482 0 : return NS_OK;
1483 : }
1484 :
1485 91 : WorkerPrivate::SyncLoopInfo::SyncLoopInfo(EventTarget* aEventTarget)
1486 : : mEventTarget(aEventTarget), mCompleted(false), mResult(false)
1487 : #ifdef DEBUG
1488 91 : , mHasRun(false)
1489 : #endif
1490 : {
1491 91 : }
1492 :
1493 : nsIDocument*
1494 48 : WorkerPrivate::GetDocument() const
1495 : {
1496 48 : AssertIsOnMainThread();
1497 0 : if (mLoadInfo.mWindow) {
1498 0 : return mLoadInfo.mWindow->GetExtantDoc();
1499 : }
1500 : // if we don't have a document, we should query the document
1501 : // from the parent in case of a nested worker
1502 48 : WorkerPrivate* parent = mParent;
1503 0 : while (parent) {
1504 0 : if (parent->mLoadInfo.mWindow) {
1505 0 : return parent->mLoadInfo.mWindow->GetExtantDoc();
1506 : }
1507 0 : parent = parent->GetParent();
1508 : }
1509 : // couldn't query a document, give up and return nullptr
1510 : return nullptr;
1511 : }
1512 :
1513 : void
1514 0 : WorkerPrivate::SetCSP(nsIContentSecurityPolicy* aCSP)
1515 : {
1516 0 : AssertIsOnMainThread();
1517 0 : if (!aCSP) {
1518 : return;
1519 : }
1520 0 : aCSP->EnsureEventTarget(mMainThreadEventTarget);
1521 0 : mLoadInfo.mCSP = aCSP;
1522 : }
1523 :
1524 : nsresult
1525 3 : WorkerPrivate::SetCSPFromHeaderValues(const nsACString& aCSPHeaderValue,
1526 : const nsACString& aCSPReportOnlyHeaderValue)
1527 : {
1528 3 : AssertIsOnMainThread();
1529 0 : MOZ_DIAGNOSTIC_ASSERT(!mLoadInfo.mCSP);
1530 :
1531 6 : NS_ConvertASCIItoUTF16 cspHeaderValue(aCSPHeaderValue);
1532 0 : NS_ConvertASCIItoUTF16 cspROHeaderValue(aCSPReportOnlyHeaderValue);
1533 :
1534 6 : nsCOMPtr<nsIContentSecurityPolicy> csp;
1535 0 : nsresult rv = mLoadInfo.mPrincipal->EnsureCSP(nullptr, getter_AddRefs(csp));
1536 0 : if (!csp) {
1537 : return NS_OK;
1538 : }
1539 :
1540 0 : csp->EnsureEventTarget(mMainThreadEventTarget);
1541 :
1542 : // If there's a CSP header, apply it.
1543 0 : if (!cspHeaderValue.IsEmpty()) {
1544 0 : rv = CSP_AppendCSPFromHeader(csp, cspHeaderValue, false);
1545 0 : NS_ENSURE_SUCCESS(rv, rv);
1546 : }
1547 : // If there's a report-only CSP header, apply it.
1548 0 : if (!cspROHeaderValue.IsEmpty()) {
1549 0 : rv = CSP_AppendCSPFromHeader(csp, cspROHeaderValue, true);
1550 0 : NS_ENSURE_SUCCESS(rv, rv);
1551 : }
1552 :
1553 : // Set evalAllowed, default value is set in GetAllowsEval
1554 0 : bool evalAllowed = false;
1555 0 : bool reportEvalViolations = false;
1556 0 : rv = csp->GetAllowsEval(&reportEvalViolations, &evalAllowed);
1557 0 : NS_ENSURE_SUCCESS(rv, rv);
1558 :
1559 0 : mLoadInfo.mCSP = csp;
1560 0 : mLoadInfo.mEvalAllowed = evalAllowed;
1561 0 : mLoadInfo.mReportCSPViolations = reportEvalViolations;
1562 :
1563 0 : return NS_OK;
1564 : }
1565 :
1566 : void
1567 3 : WorkerPrivate::SetReferrerPolicyFromHeaderValue(const nsACString& aReferrerPolicyHeaderValue)
1568 : {
1569 3 : NS_ConvertUTF8toUTF16 headerValue(aReferrerPolicyHeaderValue);
1570 :
1571 3 : if (headerValue.IsEmpty()) {
1572 0 : return;
1573 : }
1574 :
1575 : net::ReferrerPolicy policy =
1576 0 : nsContentUtils::GetReferrerPolicyFromHeader(headerValue);
1577 0 : if (policy == net::RP_Unset) {
1578 : return;
1579 : }
1580 :
1581 0 : SetReferrerPolicy(policy);
1582 : }
1583 :
1584 : void
1585 6 : WorkerPrivate::Traverse(nsCycleCollectionTraversalCallback& aCb)
1586 : {
1587 6 : AssertIsOnParentThread();
1588 :
1589 : // The WorkerPrivate::mParentEventTargetRef has a reference to the exposed
1590 : // Worker object, which is really held by the worker thread. We traverse this
1591 : // reference if and only if our busy count is zero and we have not released
1592 : // the main thread reference. We do not unlink it. This allows the CC to
1593 : // break cycles involving the Worker and begin shutting it down (which does
1594 : // happen in unlink) but ensures that the WorkerPrivate won't be deleted
1595 : // before we're done shutting down the thread.
1596 12 : if (!mBusyCount && !mMainThreadObjectsForgotten) {
1597 0 : nsCycleCollectionTraversalCallback& cb = aCb;
1598 0 : WorkerPrivate* tmp = this;
1599 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParentEventTargetRef);
1600 : }
1601 6 : }
1602 :
1603 : nsresult
1604 295 : WorkerPrivate::Dispatch(already_AddRefed<WorkerRunnable> aRunnable,
1605 : nsIEventTarget* aSyncLoopTarget)
1606 : {
1607 : // May be called on any thread!
1608 590 : RefPtr<WorkerRunnable> runnable(aRunnable);
1609 :
1610 : {
1611 879 : MutexAutoLock lock(mMutex);
1612 :
1613 535 : MOZ_ASSERT_IF(aSyncLoopTarget, mThread);
1614 :
1615 590 : if (!mThread) {
1616 0 : if (ParentStatus() == Pending || mStatus == Pending) {
1617 0 : mPreStartRunnables.AppendElement(runnable);
1618 0 : return NS_OK;
1619 : }
1620 :
1621 : NS_WARNING("Using a worker event target after the thread has already"
1622 0 : "been released!");
1623 0 : return NS_ERROR_UNEXPECTED;
1624 : }
1625 :
1626 578 : if (mStatus == Dead ||
1627 0 : (!aSyncLoopTarget && ParentStatus() > Running)) {
1628 : NS_WARNING("A runnable was posted to a worker that is already shutting "
1629 0 : "down!");
1630 0 : return NS_ERROR_UNEXPECTED;
1631 : }
1632 :
1633 : nsresult rv;
1634 289 : if (aSyncLoopTarget) {
1635 0 : rv = aSyncLoopTarget->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
1636 : } else {
1637 49 : rv = mThread->DispatchAnyThread(WorkerThreadFriendKey(), runnable.forget());
1638 : }
1639 :
1640 289 : if (NS_WARN_IF(NS_FAILED(rv))) {
1641 : return rv;
1642 : }
1643 :
1644 578 : mCondVar.Notify();
1645 : }
1646 :
1647 289 : return NS_OK;
1648 : }
1649 :
1650 : void
1651 3 : WorkerPrivate::EnableDebugger()
1652 : {
1653 3 : AssertIsOnParentThread();
1654 :
1655 3 : if (NS_FAILED(RegisterWorkerDebugger(this))) {
1656 0 : NS_WARNING("Failed to register worker debugger!");
1657 0 : return;
1658 : }
1659 : }
1660 :
1661 : void
1662 0 : WorkerPrivate::DisableDebugger()
1663 : {
1664 0 : AssertIsOnParentThread();
1665 :
1666 0 : if (NS_FAILED(UnregisterWorkerDebugger(this))) {
1667 0 : NS_WARNING("Failed to unregister worker debugger!");
1668 : }
1669 0 : }
1670 :
1671 : nsresult
1672 0 : WorkerPrivate::DispatchControlRunnable(already_AddRefed<WorkerControlRunnable> aWorkerControlRunnable)
1673 : {
1674 : // May be called on any thread!
1675 0 : RefPtr<WorkerControlRunnable> runnable(aWorkerControlRunnable);
1676 0 : MOZ_ASSERT(runnable);
1677 :
1678 : {
1679 0 : MutexAutoLock lock(mMutex);
1680 :
1681 0 : if (mStatus == Dead) {
1682 0 : return NS_ERROR_UNEXPECTED;
1683 : }
1684 :
1685 : // Transfer ownership to the control queue.
1686 0 : mControlQueue.Push(runnable.forget().take());
1687 :
1688 0 : if (JSContext* cx = mJSContext) {
1689 0 : MOZ_ASSERT(mThread);
1690 0 : JS_RequestInterruptCallback(cx);
1691 : }
1692 :
1693 0 : mCondVar.Notify();
1694 : }
1695 :
1696 0 : return NS_OK;
1697 : }
1698 :
1699 : nsresult
1700 0 : WorkerPrivate::DispatchDebuggerRunnable(already_AddRefed<WorkerRunnable> aDebuggerRunnable)
1701 : {
1702 : // May be called on any thread!
1703 :
1704 0 : RefPtr<WorkerRunnable> runnable(aDebuggerRunnable);
1705 :
1706 0 : MOZ_ASSERT(runnable);
1707 :
1708 : {
1709 0 : MutexAutoLock lock(mMutex);
1710 :
1711 0 : if (mStatus == Dead) {
1712 : NS_WARNING("A debugger runnable was posted to a worker that is already "
1713 0 : "shutting down!");
1714 0 : return NS_ERROR_UNEXPECTED;
1715 : }
1716 :
1717 : // Transfer ownership to the debugger queue.
1718 0 : mDebuggerQueue.Push(runnable.forget().take());
1719 :
1720 0 : mCondVar.Notify();
1721 : }
1722 :
1723 0 : return NS_OK;
1724 : }
1725 :
1726 : already_AddRefed<WorkerRunnable>
1727 240 : WorkerPrivate::MaybeWrapAsWorkerRunnable(already_AddRefed<nsIRunnable> aRunnable)
1728 : {
1729 : // May be called on any thread!
1730 :
1731 480 : nsCOMPtr<nsIRunnable> runnable(aRunnable);
1732 0 : MOZ_ASSERT(runnable);
1733 :
1734 : RefPtr<WorkerRunnable> workerRunnable =
1735 480 : WorkerRunnable::FromRunnable(runnable);
1736 0 : if (workerRunnable) {
1737 : return workerRunnable.forget();
1738 : }
1739 :
1740 0 : nsCOMPtr<nsICancelableRunnable> cancelable = do_QueryInterface(runnable);
1741 0 : if (!cancelable) {
1742 0 : MOZ_CRASH("All runnables destined for a worker thread must be cancelable!");
1743 : }
1744 :
1745 0 : workerRunnable = new ExternalRunnableWrapper(this, runnable);
1746 0 : return workerRunnable.forget();
1747 : }
1748 :
1749 : bool
1750 3 : WorkerPrivate::Start()
1751 : {
1752 : // May be called on any thread!
1753 : {
1754 6 : MutexAutoLock lock(mMutex);
1755 0 : NS_ASSERTION(mParentStatus != Running, "How can this be?!");
1756 :
1757 3 : if (mParentStatus == Pending) {
1758 0 : mParentStatus = Running;
1759 0 : return true;
1760 : }
1761 : }
1762 :
1763 0 : return false;
1764 : }
1765 :
1766 : // aCx is null when called from the finalizer
1767 : bool
1768 0 : WorkerPrivate::Notify(WorkerStatus aStatus)
1769 : {
1770 0 : AssertIsOnParentThread();
1771 :
1772 : bool pending;
1773 : {
1774 0 : MutexAutoLock lock(mMutex);
1775 :
1776 0 : if (mParentStatus >= aStatus) {
1777 0 : return true;
1778 : }
1779 :
1780 0 : pending = mParentStatus == Pending;
1781 0 : mParentStatus = aStatus;
1782 : }
1783 :
1784 0 : if (IsSharedWorker()) {
1785 0 : RuntimeService* runtime = RuntimeService::GetService();
1786 0 : MOZ_ASSERT(runtime);
1787 :
1788 0 : runtime->ForgetSharedWorker(this);
1789 : }
1790 :
1791 0 : if (pending) {
1792 : #ifdef DEBUG
1793 : {
1794 : // Fake a thread here just so that our assertions don't go off for no
1795 : // reason.
1796 0 : nsIThread* currentThread = NS_GetCurrentThread();
1797 0 : MOZ_ASSERT(currentThread);
1798 :
1799 0 : MOZ_ASSERT(!mPRThread);
1800 0 : mPRThread = PRThreadFromThread(currentThread);
1801 0 : MOZ_ASSERT(mPRThread);
1802 : }
1803 : #endif
1804 :
1805 : // Worker never got a chance to run, go ahead and delete it.
1806 0 : ScheduleDeletion(WorkerPrivate::WorkerNeverRan);
1807 0 : return true;
1808 : }
1809 :
1810 0 : NS_ASSERTION(aStatus != Terminating || mQueuedRunnables.IsEmpty(),
1811 : "Shouldn't have anything queued!");
1812 :
1813 : // Anything queued will be discarded.
1814 0 : mQueuedRunnables.Clear();
1815 :
1816 : // No Canceling timeout is needed.
1817 0 : if (mCancelingTimer) {
1818 0 : mCancelingTimer->Cancel();
1819 0 : mCancelingTimer = nullptr;
1820 : }
1821 :
1822 0 : RefPtr<NotifyRunnable> runnable = new NotifyRunnable(this, aStatus);
1823 0 : return runnable->Dispatch();
1824 : }
1825 :
1826 : bool
1827 0 : WorkerPrivate::Freeze(nsPIDOMWindowInner* aWindow)
1828 : {
1829 0 : AssertIsOnParentThread();
1830 :
1831 : // Shared workers are only frozen if all of their owning documents are
1832 : // frozen. It can happen that mSharedWorkers is empty but this thread has
1833 : // not been unregistered yet.
1834 0 : if ((IsSharedWorker() || IsServiceWorker()) && !mSharedWorkers.IsEmpty()) {
1835 0 : AssertIsOnMainThread();
1836 :
1837 0 : bool allFrozen = true;
1838 :
1839 0 : for (uint32_t i = 0; i < mSharedWorkers.Length(); ++i) {
1840 0 : if (aWindow && mSharedWorkers[i]->GetOwner() == aWindow) {
1841 : // Calling Freeze() may change the refcount, ensure that the worker
1842 : // outlives this call.
1843 0 : RefPtr<SharedWorker> kungFuDeathGrip = mSharedWorkers[i];
1844 :
1845 0 : kungFuDeathGrip->Freeze();
1846 : } else {
1847 0 : MOZ_ASSERT_IF(mSharedWorkers[i]->GetOwner() && aWindow,
1848 : !SameCOMIdentity(mSharedWorkers[i]->GetOwner(), aWindow));
1849 0 : if (!mSharedWorkers[i]->IsFrozen()) {
1850 0 : allFrozen = false;
1851 : }
1852 : }
1853 : }
1854 :
1855 0 : if (!allFrozen || mParentFrozen) {
1856 : return true;
1857 : }
1858 : }
1859 :
1860 0 : mParentFrozen = true;
1861 :
1862 : {
1863 0 : MutexAutoLock lock(mMutex);
1864 :
1865 0 : if (mParentStatus >= Terminating) {
1866 0 : return true;
1867 : }
1868 : }
1869 :
1870 0 : DisableDebugger();
1871 :
1872 0 : RefPtr<FreezeRunnable> runnable = new FreezeRunnable(this);
1873 0 : if (!runnable->Dispatch()) {
1874 : return false;
1875 : }
1876 :
1877 0 : return true;
1878 : }
1879 :
1880 : bool
1881 0 : WorkerPrivate::Thaw(nsPIDOMWindowInner* aWindow)
1882 : {
1883 0 : AssertIsOnParentThread();
1884 :
1885 0 : MOZ_ASSERT(mParentFrozen);
1886 :
1887 : // Shared workers are resumed if any of their owning documents are thawed.
1888 : // It can happen that mSharedWorkers is empty but this thread has not been
1889 : // unregistered yet.
1890 0 : if ((IsSharedWorker() || IsServiceWorker()) && !mSharedWorkers.IsEmpty()) {
1891 0 : AssertIsOnMainThread();
1892 :
1893 0 : bool anyRunning = false;
1894 :
1895 0 : for (uint32_t i = 0; i < mSharedWorkers.Length(); ++i) {
1896 0 : if (aWindow && mSharedWorkers[i]->GetOwner() == aWindow) {
1897 : // Calling Thaw() may change the refcount, ensure that the worker
1898 : // outlives this call.
1899 0 : RefPtr<SharedWorker> kungFuDeathGrip = mSharedWorkers[i];
1900 :
1901 0 : kungFuDeathGrip->Thaw();
1902 0 : anyRunning = true;
1903 : } else {
1904 0 : MOZ_ASSERT_IF(mSharedWorkers[i]->GetOwner() && aWindow,
1905 : !SameCOMIdentity(mSharedWorkers[i]->GetOwner(), aWindow));
1906 0 : if (!mSharedWorkers[i]->IsFrozen()) {
1907 0 : anyRunning = true;
1908 : }
1909 : }
1910 : }
1911 :
1912 0 : if (!anyRunning || !mParentFrozen) {
1913 : return true;
1914 : }
1915 : }
1916 :
1917 0 : MOZ_ASSERT(mParentFrozen);
1918 :
1919 0 : mParentFrozen = false;
1920 :
1921 : {
1922 0 : MutexAutoLock lock(mMutex);
1923 :
1924 0 : if (mParentStatus >= Terminating) {
1925 0 : return true;
1926 : }
1927 : }
1928 :
1929 0 : EnableDebugger();
1930 :
1931 : // Execute queued runnables before waking up the worker, otherwise the worker
1932 : // could post new messages before we run those that have been queued.
1933 0 : if (!IsParentWindowPaused() && !mQueuedRunnables.IsEmpty()) {
1934 0 : MOZ_ASSERT(IsDedicatedWorker());
1935 :
1936 0 : nsTArray<nsCOMPtr<nsIRunnable>> runnables;
1937 0 : mQueuedRunnables.SwapElements(runnables);
1938 :
1939 0 : for (uint32_t index = 0; index < runnables.Length(); index++) {
1940 0 : runnables[index]->Run();
1941 : }
1942 : }
1943 :
1944 0 : RefPtr<ThawRunnable> runnable = new ThawRunnable(this);
1945 0 : if (!runnable->Dispatch()) {
1946 : return false;
1947 : }
1948 :
1949 0 : return true;
1950 : }
1951 :
1952 : void
1953 0 : WorkerPrivate::ParentWindowPaused()
1954 : {
1955 0 : AssertIsOnMainThread();
1956 0 : MOZ_ASSERT_IF(IsDedicatedWorker(), mParentWindowPausedDepth == 0);
1957 0 : mParentWindowPausedDepth += 1;
1958 0 : }
1959 :
1960 : void
1961 0 : WorkerPrivate::ParentWindowResumed()
1962 : {
1963 0 : AssertIsOnMainThread();
1964 :
1965 0 : MOZ_ASSERT(mParentWindowPausedDepth > 0);
1966 0 : MOZ_ASSERT_IF(IsDedicatedWorker(), mParentWindowPausedDepth == 1);
1967 0 : mParentWindowPausedDepth -= 1;
1968 0 : if (mParentWindowPausedDepth > 0) {
1969 : return;
1970 : }
1971 :
1972 : {
1973 0 : MutexAutoLock lock(mMutex);
1974 :
1975 0 : if (mParentStatus >= Terminating) {
1976 0 : return;
1977 : }
1978 : }
1979 :
1980 : // Execute queued runnables before waking up, otherwise the worker could post
1981 : // new messages before we run those that have been queued.
1982 0 : if (!IsFrozen() && !mQueuedRunnables.IsEmpty()) {
1983 0 : MOZ_ASSERT(IsDedicatedWorker());
1984 :
1985 0 : nsTArray<nsCOMPtr<nsIRunnable>> runnables;
1986 0 : mQueuedRunnables.SwapElements(runnables);
1987 :
1988 0 : for (uint32_t index = 0; index < runnables.Length(); index++) {
1989 0 : runnables[index]->Run();
1990 : }
1991 : }
1992 : }
1993 :
1994 : bool
1995 0 : WorkerPrivate::Close()
1996 : {
1997 0 : mMutex.AssertCurrentThreadOwns();
1998 0 : if (mParentStatus < Closing) {
1999 0 : mParentStatus = Closing;
2000 : }
2001 :
2002 0 : return true;
2003 : }
2004 :
2005 : bool
2006 109 : WorkerPrivate::ModifyBusyCount(bool aIncrease)
2007 : {
2008 109 : AssertIsOnParentThread();
2009 :
2010 160 : NS_ASSERTION(aIncrease || mBusyCount, "Mismatched busy count mods!");
2011 :
2012 109 : if (aIncrease) {
2013 0 : mBusyCount++;
2014 0 : return true;
2015 : }
2016 :
2017 51 : if (--mBusyCount == 0) {
2018 :
2019 : bool shouldCancel;
2020 : {
2021 27 : MutexAutoLock lock(mMutex);
2022 0 : shouldCancel = mParentStatus == Terminating;
2023 : }
2024 :
2025 9 : if (shouldCancel && !Cancel()) {
2026 : return false;
2027 : }
2028 : }
2029 :
2030 : return true;
2031 : }
2032 :
2033 : bool
2034 0 : WorkerPrivate::ProxyReleaseMainThreadObjects()
2035 : {
2036 0 : AssertIsOnParentThread();
2037 0 : MOZ_ASSERT(!mMainThreadObjectsForgotten);
2038 :
2039 0 : nsCOMPtr<nsILoadGroup> loadGroupToCancel;
2040 : // If we're not overriden, then do nothing here. Let the load group get
2041 : // handled in ForgetMainThreadObjects().
2042 0 : if (mLoadInfo.mInterfaceRequestor) {
2043 0 : mLoadInfo.mLoadGroup.swap(loadGroupToCancel);
2044 : }
2045 :
2046 0 : bool result = mLoadInfo.ProxyReleaseMainThreadObjects(this, loadGroupToCancel);
2047 :
2048 0 : mMainThreadObjectsForgotten = true;
2049 :
2050 0 : return result;
2051 : }
2052 :
2053 : void
2054 0 : WorkerPrivate::UpdateContextOptions(const JS::ContextOptions& aContextOptions)
2055 : {
2056 0 : AssertIsOnParentThread();
2057 :
2058 : {
2059 0 : MutexAutoLock lock(mMutex);
2060 0 : mJSSettings.contextOptions = aContextOptions;
2061 : }
2062 :
2063 : RefPtr<UpdateContextOptionsRunnable> runnable =
2064 0 : new UpdateContextOptionsRunnable(this, aContextOptions);
2065 0 : if (!runnable->Dispatch()) {
2066 0 : NS_WARNING("Failed to update worker context options!");
2067 : }
2068 0 : }
2069 :
2070 : void
2071 0 : WorkerPrivate::UpdateLanguages(const nsTArray<nsString>& aLanguages)
2072 : {
2073 0 : AssertIsOnParentThread();
2074 :
2075 : RefPtr<UpdateLanguagesRunnable> runnable =
2076 0 : new UpdateLanguagesRunnable(this, aLanguages);
2077 0 : if (!runnable->Dispatch()) {
2078 0 : NS_WARNING("Failed to update worker languages!");
2079 : }
2080 0 : }
2081 :
2082 : void
2083 0 : WorkerPrivate::UpdateJSWorkerMemoryParameter(JSGCParamKey aKey, uint32_t aValue)
2084 : {
2085 0 : AssertIsOnParentThread();
2086 :
2087 0 : bool found = false;
2088 :
2089 : {
2090 0 : MutexAutoLock lock(mMutex);
2091 0 : found = mJSSettings.ApplyGCSetting(aKey, aValue);
2092 : }
2093 :
2094 0 : if (found) {
2095 : RefPtr<UpdateJSWorkerMemoryParameterRunnable> runnable =
2096 0 : new UpdateJSWorkerMemoryParameterRunnable(this, aKey, aValue);
2097 0 : if (!runnable->Dispatch()) {
2098 0 : NS_WARNING("Failed to update memory parameter!");
2099 : }
2100 : }
2101 0 : }
2102 :
2103 : #ifdef JS_GC_ZEAL
2104 : void
2105 0 : WorkerPrivate::UpdateGCZeal(uint8_t aGCZeal, uint32_t aFrequency)
2106 : {
2107 0 : AssertIsOnParentThread();
2108 :
2109 : {
2110 0 : MutexAutoLock lock(mMutex);
2111 0 : mJSSettings.gcZeal = aGCZeal;
2112 0 : mJSSettings.gcZealFrequency = aFrequency;
2113 : }
2114 :
2115 : RefPtr<UpdateGCZealRunnable> runnable =
2116 0 : new UpdateGCZealRunnable(this, aGCZeal, aFrequency);
2117 0 : if (!runnable->Dispatch()) {
2118 0 : NS_WARNING("Failed to update worker gczeal!");
2119 : }
2120 0 : }
2121 : #endif
2122 :
2123 : void
2124 0 : WorkerPrivate::GarbageCollect(bool aShrinking)
2125 : {
2126 0 : AssertIsOnParentThread();
2127 :
2128 : RefPtr<GarbageCollectRunnable> runnable =
2129 0 : new GarbageCollectRunnable(this, aShrinking, /* collectChildren = */ true);
2130 0 : if (!runnable->Dispatch()) {
2131 0 : NS_WARNING("Failed to GC worker!");
2132 : }
2133 0 : }
2134 :
2135 : void
2136 0 : WorkerPrivate::CycleCollect(bool aDummy)
2137 : {
2138 0 : AssertIsOnParentThread();
2139 :
2140 : RefPtr<CycleCollectRunnable> runnable =
2141 0 : new CycleCollectRunnable(this, /* collectChildren = */ true);
2142 0 : if (!runnable->Dispatch()) {
2143 0 : NS_WARNING("Failed to CC worker!");
2144 : }
2145 0 : }
2146 :
2147 : void
2148 0 : WorkerPrivate::OfflineStatusChangeEvent(bool aIsOffline)
2149 : {
2150 0 : AssertIsOnParentThread();
2151 :
2152 : RefPtr<OfflineStatusChangeRunnable> runnable =
2153 0 : new OfflineStatusChangeRunnable(this, aIsOffline);
2154 0 : if (!runnable->Dispatch()) {
2155 0 : NS_WARNING("Failed to dispatch offline status change event!");
2156 : }
2157 0 : }
2158 :
2159 : void
2160 0 : WorkerPrivate::OfflineStatusChangeEventInternal(bool aIsOffline)
2161 : {
2162 0 : AssertIsOnWorkerThread();
2163 :
2164 : // The worker is already in this state. No need to dispatch an event.
2165 0 : if (mOnLine == !aIsOffline) {
2166 0 : return;
2167 : }
2168 :
2169 0 : for (uint32_t index = 0; index < mChildWorkers.Length(); ++index) {
2170 0 : mChildWorkers[index]->OfflineStatusChangeEvent(aIsOffline);
2171 : }
2172 :
2173 0 : mOnLine = !aIsOffline;
2174 0 : WorkerGlobalScope* globalScope = GlobalScope();
2175 0 : RefPtr<WorkerNavigator> nav = globalScope->GetExistingNavigator();
2176 0 : if (nav) {
2177 0 : nav->SetOnLine(mOnLine);
2178 : }
2179 :
2180 0 : nsString eventType;
2181 0 : if (aIsOffline) {
2182 0 : eventType.AssignLiteral("offline");
2183 : } else {
2184 0 : eventType.AssignLiteral("online");
2185 : }
2186 :
2187 0 : RefPtr<Event> event = NS_NewDOMEvent(globalScope, nullptr, nullptr);
2188 :
2189 0 : event->InitEvent(eventType, false, false);
2190 0 : event->SetTrusted(true);
2191 :
2192 0 : globalScope->DispatchEvent(*event);
2193 : }
2194 :
2195 : void
2196 0 : WorkerPrivate::MemoryPressure(bool aDummy)
2197 : {
2198 0 : AssertIsOnParentThread();
2199 :
2200 0 : RefPtr<MemoryPressureRunnable> runnable = new MemoryPressureRunnable(this);
2201 0 : Unused << NS_WARN_IF(!runnable->Dispatch());
2202 0 : }
2203 :
2204 : bool
2205 0 : WorkerPrivate::RegisterSharedWorker(SharedWorker* aSharedWorker,
2206 : MessagePort* aPort)
2207 : {
2208 0 : AssertIsOnMainThread();
2209 0 : MOZ_ASSERT(aSharedWorker);
2210 0 : MOZ_ASSERT(IsSharedWorker());
2211 0 : MOZ_ASSERT(!mSharedWorkers.Contains(aSharedWorker));
2212 :
2213 0 : if (IsSharedWorker()) {
2214 0 : RefPtr<MessagePortRunnable> runnable = new MessagePortRunnable(this, aPort);
2215 0 : if (!runnable->Dispatch()) {
2216 0 : return false;
2217 : }
2218 : }
2219 :
2220 0 : mSharedWorkers.AppendElement(aSharedWorker);
2221 :
2222 : // If there were other SharedWorker objects attached to this worker then they
2223 : // may all have been frozen and this worker would need to be thawed.
2224 0 : if (mSharedWorkers.Length() > 1 && IsFrozen() && !Thaw(nullptr)) {
2225 : return false;
2226 : }
2227 :
2228 0 : return true;
2229 : }
2230 :
2231 : void
2232 0 : WorkerPrivate::BroadcastErrorToSharedWorkers(
2233 : JSContext* aCx,
2234 : const WorkerErrorReport* aReport,
2235 : bool aIsErrorEvent)
2236 : {
2237 0 : AssertIsOnMainThread();
2238 :
2239 0 : if (aIsErrorEvent && JSREPORT_IS_WARNING(aReport->mFlags)) {
2240 : // Don't fire any events anywhere. Just log to console.
2241 : // XXXbz should we log to all the consoles of all the relevant windows?
2242 0 : MOZ_ASSERT(aReport);
2243 0 : WorkerErrorReport::LogErrorToConsole(*aReport, 0);
2244 0 : return;
2245 : }
2246 :
2247 0 : AutoTArray<RefPtr<SharedWorker>, 10> sharedWorkers;
2248 0 : GetAllSharedWorkers(sharedWorkers);
2249 :
2250 0 : if (sharedWorkers.IsEmpty()) {
2251 : return;
2252 : }
2253 :
2254 0 : AutoTArray<WindowAction, 10> windowActions;
2255 : nsresult rv;
2256 :
2257 : // First fire the error event at all SharedWorker objects. This may include
2258 : // multiple objects in a single window as well as objects in different
2259 : // windows.
2260 0 : for (size_t index = 0; index < sharedWorkers.Length(); index++) {
2261 0 : RefPtr<SharedWorker>& sharedWorker = sharedWorkers[index];
2262 :
2263 : // May be null.
2264 0 : nsPIDOMWindowInner* window = sharedWorker->GetOwner();
2265 :
2266 0 : RefPtr<Event> event;
2267 :
2268 0 : if (aIsErrorEvent) {
2269 0 : RootedDictionary<ErrorEventInit> errorInit(aCx);
2270 0 : errorInit.mBubbles = false;
2271 0 : errorInit.mCancelable = true;
2272 0 : errorInit.mMessage = aReport->mMessage;
2273 0 : errorInit.mFilename = aReport->mFilename;
2274 0 : errorInit.mLineno = aReport->mLineNumber;
2275 0 : errorInit.mColno = aReport->mColumnNumber;
2276 :
2277 0 : event = ErrorEvent::Constructor(sharedWorker, NS_LITERAL_STRING("error"),
2278 0 : errorInit);
2279 : } else {
2280 0 : event = Event::Constructor(sharedWorker, NS_LITERAL_STRING("error"),
2281 0 : EventInit());
2282 : }
2283 :
2284 0 : if (!event) {
2285 0 : ThrowAndReport(window, NS_ERROR_UNEXPECTED);
2286 0 : continue;
2287 : }
2288 :
2289 0 : event->SetTrusted(true);
2290 :
2291 0 : ErrorResult res;
2292 : bool defaultActionEnabled =
2293 0 : sharedWorker->DispatchEvent(*event, CallerType::System, res);
2294 0 : if (res.Failed()) {
2295 0 : ThrowAndReport(window, res.StealNSResult());
2296 0 : continue;
2297 : }
2298 :
2299 0 : if (!aIsErrorEvent) {
2300 : continue;
2301 : }
2302 :
2303 0 : if (defaultActionEnabled) {
2304 : // Add the owning window to our list so that we will fire an error event
2305 : // at it later.
2306 0 : if (!windowActions.Contains(window)) {
2307 0 : windowActions.AppendElement(WindowAction(window));
2308 : }
2309 : } else {
2310 0 : size_t actionsIndex = windowActions.LastIndexOf(WindowAction(window));
2311 0 : if (actionsIndex != windowActions.NoIndex) {
2312 : // Any listener that calls preventDefault() will prevent the window from
2313 : // receiving the error event.
2314 0 : windowActions[actionsIndex].mDefaultAction = false;
2315 : }
2316 : }
2317 : }
2318 :
2319 : // If there are no windows to consider further then we're done.
2320 0 : if (windowActions.IsEmpty()) {
2321 : return;
2322 : }
2323 :
2324 : bool shouldLogErrorToConsole = true;
2325 :
2326 : // Now fire error events at all the windows remaining.
2327 0 : for (uint32_t index = 0; index < windowActions.Length(); index++) {
2328 0 : WindowAction& windowAction = windowActions[index];
2329 :
2330 : // If there is no window or the script already called preventDefault then
2331 : // skip this window.
2332 0 : if (!windowAction.mWindow || !windowAction.mDefaultAction) {
2333 0 : continue;
2334 : }
2335 :
2336 : nsCOMPtr<nsIScriptGlobalObject> sgo =
2337 0 : do_QueryInterface(windowAction.mWindow);
2338 0 : MOZ_ASSERT(sgo);
2339 :
2340 0 : MOZ_ASSERT(NS_IsMainThread());
2341 0 : RootedDictionary<ErrorEventInit> init(aCx);
2342 0 : init.mLineno = aReport->mLineNumber;
2343 0 : init.mFilename = aReport->mFilename;
2344 0 : init.mMessage = aReport->mMessage;
2345 0 : init.mCancelable = true;
2346 0 : init.mBubbles = true;
2347 :
2348 0 : nsEventStatus status = nsEventStatus_eIgnore;
2349 0 : rv = sgo->HandleScriptError(init, &status);
2350 0 : if (NS_FAILED(rv)) {
2351 0 : ThrowAndReport(windowAction.mWindow, rv);
2352 0 : continue;
2353 : }
2354 :
2355 0 : if (status == nsEventStatus_eConsumeNoDefault) {
2356 0 : shouldLogErrorToConsole = false;
2357 : }
2358 : }
2359 :
2360 : // Finally log a warning in the console if no window tried to prevent it.
2361 0 : if (shouldLogErrorToConsole) {
2362 0 : MOZ_ASSERT(aReport);
2363 0 : WorkerErrorReport::LogErrorToConsole(*aReport, 0);
2364 : }
2365 : }
2366 :
2367 : void
2368 0 : WorkerPrivate::GetAllSharedWorkers(nsTArray<RefPtr<SharedWorker>>& aSharedWorkers)
2369 : {
2370 0 : AssertIsOnMainThread();
2371 0 : MOZ_ASSERT(IsSharedWorker() || IsServiceWorker());
2372 :
2373 0 : if (!aSharedWorkers.IsEmpty()) {
2374 0 : aSharedWorkers.Clear();
2375 : }
2376 :
2377 0 : for (uint32_t i = 0; i < mSharedWorkers.Length(); ++i) {
2378 0 : aSharedWorkers.AppendElement(mSharedWorkers[i]);
2379 : }
2380 0 : }
2381 :
2382 : void
2383 0 : WorkerPrivate::CloseSharedWorkersForWindow(nsPIDOMWindowInner* aWindow)
2384 : {
2385 0 : AssertIsOnMainThread();
2386 0 : MOZ_ASSERT(IsSharedWorker() || IsServiceWorker());
2387 0 : MOZ_ASSERT(aWindow);
2388 :
2389 : bool someRemoved = false;
2390 :
2391 0 : for (uint32_t i = 0; i < mSharedWorkers.Length();) {
2392 0 : if (mSharedWorkers[i]->GetOwner() == aWindow) {
2393 0 : mSharedWorkers[i]->Close();
2394 0 : mSharedWorkers.RemoveElementAt(i);
2395 0 : someRemoved = true;
2396 : } else {
2397 0 : MOZ_ASSERT(!SameCOMIdentity(mSharedWorkers[i]->GetOwner(), aWindow));
2398 0 : ++i;
2399 : }
2400 : }
2401 :
2402 0 : if (!someRemoved) {
2403 : return;
2404 : }
2405 :
2406 : // If there are still SharedWorker objects attached to this worker then they
2407 : // may all be frozen and this worker would need to be frozen. Otherwise,
2408 : // if that was the last SharedWorker then it's time to cancel this worker.
2409 :
2410 0 : if (!mSharedWorkers.IsEmpty()) {
2411 0 : Freeze(nullptr);
2412 : } else {
2413 0 : Cancel();
2414 : }
2415 : }
2416 :
2417 : void
2418 0 : WorkerPrivate::CloseAllSharedWorkers()
2419 : {
2420 0 : AssertIsOnMainThread();
2421 0 : MOZ_ASSERT(IsSharedWorker() || IsServiceWorker());
2422 :
2423 0 : for (uint32_t i = 0; i < mSharedWorkers.Length(); ++i) {
2424 0 : mSharedWorkers[i]->Close();
2425 : }
2426 :
2427 0 : mSharedWorkers.Clear();
2428 :
2429 0 : Cancel();
2430 0 : }
2431 :
2432 : void
2433 3 : WorkerPrivate::WorkerScriptLoaded()
2434 : {
2435 3 : AssertIsOnMainThread();
2436 :
2437 3 : if (IsSharedWorker() || IsServiceWorker()) {
2438 : // No longer need to hold references to the window or document we came from.
2439 0 : mLoadInfo.mWindow = nullptr;
2440 0 : mLoadInfo.mScriptContext = nullptr;
2441 : }
2442 3 : }
2443 :
2444 : void
2445 3 : WorkerPrivate::SetBaseURI(nsIURI* aBaseURI)
2446 : {
2447 3 : AssertIsOnMainThread();
2448 :
2449 6 : if (!mLoadInfo.mBaseURI) {
2450 0 : NS_ASSERTION(GetParent(), "Shouldn't happen without a parent!");
2451 0 : mLoadInfo.mResolvedScriptURI = aBaseURI;
2452 : }
2453 :
2454 3 : mLoadInfo.mBaseURI = aBaseURI;
2455 :
2456 3 : if (NS_FAILED(aBaseURI->GetSpec(mLocationInfo.mHref))) {
2457 0 : mLocationInfo.mHref.Truncate();
2458 : }
2459 :
2460 3 : mLocationInfo.mHostname.Truncate();
2461 0 : nsContentUtils::GetHostOrIPv6WithBrackets(aBaseURI, mLocationInfo.mHostname);
2462 :
2463 6 : nsCOMPtr<nsIURL> url(do_QueryInterface(aBaseURI));
2464 0 : if (!url || NS_FAILED(url->GetFilePath(mLocationInfo.mPathname))) {
2465 0 : mLocationInfo.mPathname.Truncate();
2466 : }
2467 :
2468 6 : nsCString temp;
2469 :
2470 3 : if (url && NS_SUCCEEDED(url->GetQuery(temp)) && !temp.IsEmpty()) {
2471 0 : mLocationInfo.mSearch.Assign('?');
2472 0 : mLocationInfo.mSearch.Append(temp);
2473 : }
2474 :
2475 3 : if (NS_SUCCEEDED(aBaseURI->GetRef(temp)) && !temp.IsEmpty()) {
2476 0 : if (mLocationInfo.mHash.IsEmpty()) {
2477 0 : mLocationInfo.mHash.Assign('#');
2478 0 : mLocationInfo.mHash.Append(temp);
2479 : }
2480 : }
2481 :
2482 3 : if (NS_SUCCEEDED(aBaseURI->GetScheme(mLocationInfo.mProtocol))) {
2483 0 : mLocationInfo.mProtocol.Append(':');
2484 : }
2485 : else {
2486 0 : mLocationInfo.mProtocol.Truncate();
2487 : }
2488 :
2489 : int32_t port;
2490 3 : if (NS_SUCCEEDED(aBaseURI->GetPort(&port)) && port != -1) {
2491 0 : mLocationInfo.mPort.AppendInt(port);
2492 :
2493 0 : nsAutoCString host(mLocationInfo.mHostname);
2494 0 : host.Append(':');
2495 0 : host.Append(mLocationInfo.mPort);
2496 :
2497 0 : mLocationInfo.mHost.Assign(host);
2498 : }
2499 : else {
2500 3 : mLocationInfo.mHost.Assign(mLocationInfo.mHostname);
2501 : }
2502 :
2503 3 : nsContentUtils::GetUTFOrigin(aBaseURI, mLocationInfo.mOrigin);
2504 0 : }
2505 :
2506 : nsresult
2507 0 : WorkerPrivate::SetPrincipalOnMainThread(nsIPrincipal* aPrincipal,
2508 : nsILoadGroup* aLoadGroup)
2509 : {
2510 0 : return mLoadInfo.SetPrincipalOnMainThread(aPrincipal, aLoadGroup);
2511 : }
2512 :
2513 : nsresult
2514 3 : WorkerPrivate::SetPrincipalFromChannel(nsIChannel* aChannel)
2515 : {
2516 3 : return mLoadInfo.SetPrincipalFromChannel(aChannel);
2517 : }
2518 :
2519 : bool
2520 3 : WorkerPrivate::FinalChannelPrincipalIsValid(nsIChannel* aChannel)
2521 : {
2522 3 : return mLoadInfo.FinalChannelPrincipalIsValid(aChannel);
2523 : }
2524 :
2525 : #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
2526 : bool
2527 3 : WorkerPrivate::PrincipalURIMatchesScriptURL()
2528 : {
2529 3 : return mLoadInfo.PrincipalURIMatchesScriptURL();
2530 : }
2531 : #endif
2532 :
2533 : void
2534 0 : WorkerPrivate::UpdateOverridenLoadGroup(nsILoadGroup* aBaseLoadGroup)
2535 : {
2536 0 : AssertIsOnMainThread();
2537 :
2538 : // The load group should have been overriden at init time.
2539 0 : mLoadInfo.mInterfaceRequestor->MaybeAddTabChild(aBaseLoadGroup);
2540 0 : }
2541 :
2542 : void
2543 0 : WorkerPrivate::FlushReportsToSharedWorkers(nsIConsoleReportCollector* aReporter)
2544 : {
2545 0 : AssertIsOnMainThread();
2546 :
2547 0 : AutoTArray<RefPtr<SharedWorker>, 10> sharedWorkers;
2548 0 : AutoTArray<WindowAction, 10> windowActions;
2549 0 : GetAllSharedWorkers(sharedWorkers);
2550 :
2551 : // First find out all the shared workers' window.
2552 0 : for (size_t index = 0; index < sharedWorkers.Length(); index++) {
2553 0 : RefPtr<SharedWorker>& sharedWorker = sharedWorkers[index];
2554 :
2555 : // May be null.
2556 0 : nsPIDOMWindowInner* window = sharedWorker->GetOwner();
2557 :
2558 : // Add the owning window to our list so that we will flush the reports later.
2559 0 : if (window && !windowActions.Contains(window)) {
2560 0 : windowActions.AppendElement(WindowAction(window));
2561 : }
2562 : }
2563 :
2564 : bool reportErrorToBrowserConsole = true;
2565 :
2566 : // Flush the reports.
2567 0 : for (uint32_t index = 0; index < windowActions.Length(); index++) {
2568 0 : WindowAction& windowAction = windowActions[index];
2569 :
2570 0 : aReporter->FlushReportsToConsole(
2571 0 : windowAction.mWindow->WindowID(),
2572 0 : nsIConsoleReportCollector::ReportAction::Save);
2573 0 : reportErrorToBrowserConsole = false;
2574 : }
2575 :
2576 : // Finally report to browser console if there is no any window or shared
2577 : // worker.
2578 0 : if (reportErrorToBrowserConsole) {
2579 0 : aReporter->FlushReportsToConsole(0);
2580 0 : return;
2581 : }
2582 :
2583 0 : aReporter->ClearConsoleReports();
2584 : }
2585 :
2586 : #ifdef DEBUG
2587 :
2588 : void
2589 637 : WorkerPrivate::AssertIsOnParentThread() const
2590 : {
2591 637 : if (GetParent()) {
2592 0 : GetParent()->AssertIsOnWorkerThread();
2593 : } else {
2594 637 : AssertIsOnMainThread();
2595 : }
2596 637 : }
2597 :
2598 : void
2599 49 : WorkerPrivate::AssertInnerWindowIsCorrect() const
2600 : {
2601 49 : AssertIsOnParentThread();
2602 :
2603 : // Only care about top level workers from windows.
2604 98 : if (mParent || !mLoadInfo.mWindow) {
2605 : return;
2606 : }
2607 :
2608 0 : AssertIsOnMainThread();
2609 :
2610 0 : nsPIDOMWindowOuter* outer = mLoadInfo.mWindow->GetOuterWindow();
2611 0 : NS_ASSERTION(outer && outer->GetCurrentInnerWindow() == mLoadInfo.mWindow,
2612 : "Inner window no longer correct!");
2613 : }
2614 :
2615 : #endif
2616 :
2617 : #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
2618 : bool
2619 0 : WorkerPrivate::PrincipalIsValid() const
2620 : {
2621 3 : return mLoadInfo.PrincipalIsValid();
2622 : }
2623 : #endif
2624 :
2625 3 : WorkerPrivate::WorkerPrivate(WorkerPrivate* aParent,
2626 : const nsAString& aScriptURL,
2627 : bool aIsChromeWorker, WorkerType aWorkerType,
2628 : const nsAString& aWorkerName,
2629 : const nsACString& aServiceWorkerScope,
2630 3 : WorkerLoadInfo& aLoadInfo)
2631 : : mMutex("WorkerPrivate Mutex")
2632 : , mCondVar(mMutex, "WorkerPrivate CondVar")
2633 : , mParent(aParent)
2634 : , mScriptURL(aScriptURL)
2635 : , mWorkerName(aWorkerName)
2636 : , mWorkerType(aWorkerType)
2637 : , mDebugger(nullptr)
2638 : , mJSContext(nullptr)
2639 : , mPRThread(nullptr)
2640 : , mMainThreadEventTarget(GetMainThreadEventTarget())
2641 : , mWorkerControlEventTarget(new WorkerEventTarget(this,
2642 3 : WorkerEventTarget::Behavior::ControlOnly))
2643 : , mWorkerHybridEventTarget(new WorkerEventTarget(this,
2644 3 : WorkerEventTarget::Behavior::Hybrid))
2645 : , mParentStatus(Pending)
2646 : , mStatus(Pending)
2647 : , mBusyCount(0)
2648 : , mLoadingWorkerScript(false)
2649 : , mCreationTimeStamp(TimeStamp::Now())
2650 3 : , mCreationTimeHighRes((double)PR_Now() / PR_USEC_PER_MSEC)
2651 : , mNumHoldersPreventingShutdownStart(0)
2652 : , mDebuggerEventLoopLevel(0)
2653 : , mErrorHandlerRecursionCount(0)
2654 : , mNextTimeoutId(1)
2655 : , mParentWindowPausedDepth(0)
2656 : , mFrozen(false)
2657 : , mTimerRunning(false)
2658 : , mRunningExpiredTimeouts(false)
2659 : , mPendingEventQueueClearing(false)
2660 : , mCancelAllPendingRunnables(false)
2661 : , mPeriodicGCTimerRunning(false)
2662 : , mIdleGCTimerRunning(false)
2663 : , mWorkerScriptExecutedSuccessfully(false)
2664 : , mFetchHandlerWasAdded(false)
2665 : , mOnLine(false)
2666 : , mMainThreadObjectsForgotten(false)
2667 : , mIsChromeWorker(aIsChromeWorker)
2668 : , mParentFrozen(false)
2669 : , mIsSecureContext(false)
2670 : , mDebuggerRegistered(false)
2671 : , mIsInAutomation(false)
2672 99 : , mPerformanceCounter(nullptr)
2673 : {
2674 3 : MOZ_ASSERT_IF(!IsDedicatedWorker(), NS_IsMainThread());
2675 0 : mLoadInfo.StealFrom(aLoadInfo);
2676 :
2677 3 : if (aParent) {
2678 0 : aParent->AssertIsOnWorkerThread();
2679 :
2680 : // Note that this copies our parent's secure context state into mJSSettings.
2681 0 : aParent->CopyJSSettings(mJSSettings);
2682 :
2683 : // And manually set our mIsSecureContext, though it's not really relevant to
2684 : // dedicated workers...
2685 0 : mIsSecureContext = aParent->IsSecureContext();
2686 0 : MOZ_ASSERT_IF(mIsChromeWorker, mIsSecureContext);
2687 :
2688 0 : mIsInAutomation = aParent->IsInAutomation();
2689 :
2690 0 : MOZ_ASSERT(IsDedicatedWorker());
2691 :
2692 0 : if (aParent->mParentFrozen) {
2693 0 : Freeze(nullptr);
2694 : }
2695 :
2696 0 : mOnLine = aParent->OnLine();
2697 : }
2698 : else {
2699 3 : AssertIsOnMainThread();
2700 :
2701 3 : RuntimeService::GetDefaultJSSettings(mJSSettings);
2702 :
2703 : // Our secure context state depends on the kind of worker we have.
2704 3 : if (UsesSystemPrincipal() || IsServiceWorker()) {
2705 0 : mIsSecureContext = true;
2706 0 : } else if (mLoadInfo.mWindow) {
2707 : // Shared and dedicated workers both inherit the loading window's secure
2708 : // context state. Shared workers then prevent windows with a different
2709 : // secure context state from attaching to them.
2710 0 : mIsSecureContext = mLoadInfo.mWindow->IsSecureContext();
2711 : } else {
2712 0 : MOZ_ASSERT_UNREACHABLE("non-chrome worker that is not a service worker "
2713 : "that has no parent and no associated window");
2714 : }
2715 :
2716 3 : if (mIsSecureContext) {
2717 : mJSSettings.chrome.realmOptions
2718 6 : .creationOptions().setSecureContext(true);
2719 : mJSSettings.chrome.realmOptions
2720 6 : .creationOptions().setClampAndJitterTime(false);
2721 : mJSSettings.content.realmOptions
2722 6 : .creationOptions().setSecureContext(true);
2723 : mJSSettings.content.realmOptions
2724 3 : .creationOptions().setClampAndJitterTime(false);
2725 : }
2726 :
2727 3 : mIsInAutomation = xpc::IsInAutomation();
2728 :
2729 : // Our parent can get suspended after it initiates the async creation
2730 : // of a new worker thread. In this case suspend the new worker as well.
2731 6 : if (mLoadInfo.mWindow && mLoadInfo.mWindow->IsSuspended()) {
2732 0 : ParentWindowPaused();
2733 : }
2734 :
2735 6 : if (mLoadInfo.mWindow && mLoadInfo.mWindow->IsFrozen()) {
2736 0 : Freeze(mLoadInfo.mWindow);
2737 : }
2738 :
2739 3 : mOnLine = !NS_IsOffline();
2740 : }
2741 :
2742 6 : nsCOMPtr<nsISerialEventTarget> target;
2743 :
2744 : // A child worker just inherits the parent workers ThrottledEventQueue
2745 : // and main thread target for now. This is mainly due to the restriction
2746 : // that ThrottledEventQueue can only be created on the main thread at the
2747 : // moment.
2748 3 : if (aParent) {
2749 0 : mMainThreadThrottledEventQueue = aParent->mMainThreadThrottledEventQueue;
2750 0 : mMainThreadEventTarget = aParent->mMainThreadEventTarget;
2751 0 : return;
2752 : }
2753 :
2754 3 : MOZ_ASSERT(NS_IsMainThread());
2755 0 : target = GetWindow() ? GetWindow()->EventTargetFor(TaskCategory::Worker) : nullptr;
2756 :
2757 3 : if (!target) {
2758 0 : target = GetMainThreadSerialEventTarget();
2759 0 : MOZ_DIAGNOSTIC_ASSERT(target);
2760 : }
2761 :
2762 : // Throttle events to the main thread using a ThrottledEventQueue specific to
2763 : // this worker thread. This may return nullptr during shutdown.
2764 3 : mMainThreadThrottledEventQueue = ThrottledEventQueue::Create(target);
2765 :
2766 : // If we were able to creat the throttled event queue, then use it for
2767 : // dispatching our main thread runnables. Otherwise use our underlying
2768 : // base target.
2769 6 : if (mMainThreadThrottledEventQueue) {
2770 0 : mMainThreadEventTarget = mMainThreadThrottledEventQueue;
2771 : } else {
2772 0 : mMainThreadEventTarget = target.forget();
2773 : }
2774 : }
2775 :
2776 0 : WorkerPrivate::~WorkerPrivate()
2777 : {
2778 0 : DropJSObjects(this);
2779 :
2780 0 : mWorkerControlEventTarget->ForgetWorkerPrivate(this);
2781 :
2782 : // We force the hybrid event target to forget the thread when we
2783 : // enter the Killing state, but we do it again here to be safe.
2784 : // Its possible that we may be created and destroyed without progressing
2785 : // to Killing via some obscure code path.
2786 0 : mWorkerHybridEventTarget->ForgetWorkerPrivate(this);
2787 0 : }
2788 :
2789 : // static
2790 : already_AddRefed<WorkerPrivate>
2791 3 : WorkerPrivate::Constructor(JSContext* aCx,
2792 : const nsAString& aScriptURL,
2793 : bool aIsChromeWorker, WorkerType aWorkerType,
2794 : const nsAString& aWorkerName,
2795 : const nsACString& aServiceWorkerScope,
2796 : WorkerLoadInfo* aLoadInfo, ErrorResult& aRv)
2797 : {
2798 : // If this is a sub-worker, we need to keep the parent worker alive until this
2799 : // one is registered.
2800 6 : UniquePtr<SimpleWorkerHolder> holder;
2801 :
2802 3 : WorkerPrivate* parent = NS_IsMainThread() ?
2803 : nullptr :
2804 3 : GetCurrentThreadWorkerPrivate();
2805 0 : if (parent) {
2806 0 : parent->AssertIsOnWorkerThread();
2807 :
2808 0 : holder.reset(new SimpleWorkerHolder());
2809 0 : if (!holder->HoldWorker(parent, Canceling)) {
2810 0 : aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
2811 : return nullptr;
2812 : }
2813 : } else {
2814 3 : AssertIsOnMainThread();
2815 : }
2816 :
2817 3 : Maybe<WorkerLoadInfo> stackLoadInfo;
2818 0 : if (!aLoadInfo) {
2819 0 : stackLoadInfo.emplace();
2820 :
2821 3 : nsresult rv = GetLoadInfo(aCx, nullptr, parent, aScriptURL,
2822 : aIsChromeWorker, InheritLoadGroup,
2823 3 : aWorkerType, stackLoadInfo.ptr());
2824 0 : aRv.MightThrowJSException();
2825 0 : if (NS_FAILED(rv)) {
2826 0 : workerinternals::ReportLoadError(aRv, rv, aScriptURL);
2827 : return nullptr;
2828 : }
2829 :
2830 3 : aLoadInfo = stackLoadInfo.ptr();
2831 : }
2832 :
2833 : // NB: This has to be done before creating the WorkerPrivate, because it will
2834 : // attempt to use static variables that are initialized in the RuntimeService
2835 : // constructor.
2836 : RuntimeService* runtimeService;
2837 :
2838 3 : if (!parent) {
2839 0 : runtimeService = RuntimeService::GetOrCreateService();
2840 0 : if (!runtimeService) {
2841 0 : aRv.Throw(NS_ERROR_FAILURE);
2842 : return nullptr;
2843 : }
2844 : }
2845 : else {
2846 0 : runtimeService = RuntimeService::GetService();
2847 : }
2848 :
2849 3 : MOZ_ASSERT(runtimeService);
2850 :
2851 : RefPtr<WorkerPrivate> worker =
2852 : new WorkerPrivate(parent, aScriptURL, aIsChromeWorker,
2853 : aWorkerType, aWorkerName, aServiceWorkerScope,
2854 6 : *aLoadInfo);
2855 :
2856 : // Gecko contexts always have an explicitly-set default locale (set by
2857 : // XPJSRuntime::Initialize for the main thread, set by
2858 : // WorkerThreadPrimaryRunnable::Run for workers just before running worker
2859 : // code), so this is never SpiderMonkey's builtin default locale.
2860 6 : JS::UniqueChars defaultLocale = JS_GetDefaultLocale(aCx);
2861 0 : if (NS_WARN_IF(!defaultLocale)) {
2862 0 : aRv.Throw(NS_ERROR_UNEXPECTED);
2863 : return nullptr;
2864 : }
2865 :
2866 3 : worker->mDefaultLocale = std::move(defaultLocale);
2867 :
2868 3 : if (!runtimeService->RegisterWorker(worker)) {
2869 0 : aRv.Throw(NS_ERROR_UNEXPECTED);
2870 : return nullptr;
2871 : }
2872 :
2873 3 : worker->EnableDebugger();
2874 :
2875 6 : MOZ_DIAGNOSTIC_ASSERT(worker->PrincipalIsValid());
2876 :
2877 : RefPtr<CompileScriptRunnable> compiler =
2878 9 : new CompileScriptRunnable(worker, aScriptURL);
2879 0 : if (!compiler->Dispatch()) {
2880 0 : aRv.Throw(NS_ERROR_UNEXPECTED);
2881 : return nullptr;
2882 : }
2883 :
2884 3 : worker->mSelfRef = worker;
2885 :
2886 : return worker.forget();
2887 : }
2888 :
2889 : // static
2890 : nsresult
2891 3 : WorkerPrivate::GetLoadInfo(JSContext* aCx, nsPIDOMWindowInner* aWindow,
2892 : WorkerPrivate* aParent, const nsAString& aScriptURL,
2893 : bool aIsChromeWorker,
2894 : LoadGroupBehavior aLoadGroupBehavior,
2895 : WorkerType aWorkerType,
2896 : WorkerLoadInfo* aLoadInfo)
2897 : {
2898 : using namespace mozilla::dom::workerinternals;
2899 :
2900 3 : MOZ_ASSERT(aCx);
2901 0 : MOZ_ASSERT_IF(NS_IsMainThread(), aCx == nsContentUtils::GetCurrentJSContext());
2902 :
2903 3 : if (aWindow) {
2904 0 : AssertIsOnMainThread();
2905 : }
2906 :
2907 6 : WorkerLoadInfo loadInfo;
2908 : nsresult rv;
2909 :
2910 3 : if (aParent) {
2911 0 : aParent->AssertIsOnWorkerThread();
2912 :
2913 : // If the parent is going away give up now.
2914 : WorkerStatus parentStatus;
2915 : {
2916 0 : MutexAutoLock lock(aParent->mMutex);
2917 0 : parentStatus = aParent->mStatus;
2918 : }
2919 :
2920 0 : if (parentStatus > Running) {
2921 : return NS_ERROR_FAILURE;
2922 : }
2923 :
2924 : // Passing a pointer to our stack loadInfo is safe here because this
2925 : // method uses a sync runnable to get the channel from the main thread.
2926 0 : rv = ChannelFromScriptURLWorkerThread(aCx, aParent, aScriptURL,
2927 : loadInfo);
2928 0 : if (NS_FAILED(rv)) {
2929 0 : MOZ_ALWAYS_TRUE(loadInfo.ProxyReleaseMainThreadObjects(aParent));
2930 0 : return rv;
2931 : }
2932 :
2933 : // Now that we've spun the loop there's no guarantee that our parent is
2934 : // still alive. We may have received control messages initiating shutdown.
2935 : {
2936 0 : MutexAutoLock lock(aParent->mMutex);
2937 0 : parentStatus = aParent->mStatus;
2938 : }
2939 :
2940 0 : if (parentStatus > Running) {
2941 0 : MOZ_ALWAYS_TRUE(loadInfo.ProxyReleaseMainThreadObjects(aParent));
2942 : return NS_ERROR_FAILURE;
2943 : }
2944 :
2945 0 : loadInfo.mDomain = aParent->Domain();
2946 0 : loadInfo.mFromWindow = aParent->IsFromWindow();
2947 0 : loadInfo.mWindowID = aParent->WindowID();
2948 0 : loadInfo.mStorageAllowed = aParent->IsStorageAllowed();
2949 0 : loadInfo.mOriginAttributes = aParent->GetOriginAttributes();
2950 0 : loadInfo.mServiceWorkersTestingInWindow =
2951 0 : aParent->ServiceWorkersTestingInWindow();
2952 0 : loadInfo.mParentController = aParent->GetController();
2953 : } else {
2954 3 : AssertIsOnMainThread();
2955 :
2956 : // Make sure that the IndexedDatabaseManager is set up
2957 3 : Unused << NS_WARN_IF(!IndexedDatabaseManager::GetOrCreate());
2958 :
2959 3 : nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
2960 0 : MOZ_ASSERT(ssm);
2961 :
2962 3 : bool isChrome = nsContentUtils::IsSystemCaller(aCx);
2963 :
2964 : // First check to make sure the caller has permission to make a privileged
2965 : // worker if they called the ChromeWorker/ChromeSharedWorker constructor.
2966 3 : if (aIsChromeWorker && !isChrome) {
2967 0 : return NS_ERROR_DOM_SECURITY_ERR;
2968 : }
2969 :
2970 : // Chrome callers (whether creating a ChromeWorker or Worker) always get the
2971 : // system principal here as they're allowed to load anything. The script
2972 : // loader will refuse to run any script that does not also have the system
2973 : // principal.
2974 3 : if (isChrome) {
2975 0 : rv = ssm->GetSystemPrincipal(getter_AddRefs(loadInfo.mLoadingPrincipal));
2976 0 : NS_ENSURE_SUCCESS(rv, rv);
2977 :
2978 3 : loadInfo.mPrincipalIsSystem = true;
2979 : }
2980 :
2981 : // See if we're being called from a window.
2982 6 : nsCOMPtr<nsPIDOMWindowInner> globalWindow = aWindow;
2983 0 : if (!globalWindow) {
2984 : nsCOMPtr<nsIScriptGlobalObject> scriptGlobal =
2985 6 : nsJSUtils::GetStaticScriptGlobal(JS::CurrentGlobalOrNull(aCx));
2986 0 : if (scriptGlobal) {
2987 0 : globalWindow = do_QueryInterface(scriptGlobal);
2988 0 : MOZ_ASSERT(globalWindow);
2989 : }
2990 : }
2991 :
2992 6 : nsCOMPtr<nsIDocument> document;
2993 0 : Maybe<ClientInfo> clientInfo;
2994 :
2995 3 : if (globalWindow) {
2996 : // Only use the current inner window, and only use it if the caller can
2997 : // access it.
2998 0 : if (nsPIDOMWindowOuter* outerWindow = globalWindow->GetOuterWindow()) {
2999 0 : loadInfo.mWindow = outerWindow->GetCurrentInnerWindow();
3000 : // TODO: fix this for SharedWorkers with multiple documents (bug 1177935)
3001 0 : loadInfo.mServiceWorkersTestingInWindow =
3002 0 : outerWindow->GetServiceWorkersTestingEnabled();
3003 : }
3004 :
3005 0 : if (!loadInfo.mWindow ||
3006 0 : (globalWindow != loadInfo.mWindow &&
3007 0 : !nsContentUtils::CanCallerAccess(loadInfo.mWindow))) {
3008 0 : return NS_ERROR_DOM_SECURITY_ERR;
3009 : }
3010 :
3011 0 : nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(loadInfo.mWindow);
3012 0 : MOZ_ASSERT(sgo);
3013 :
3014 0 : loadInfo.mScriptContext = sgo->GetContext();
3015 0 : NS_ENSURE_TRUE(loadInfo.mScriptContext, NS_ERROR_FAILURE);
3016 :
3017 : // If we're called from a window then we can dig out the principal and URI
3018 : // from the document.
3019 0 : document = loadInfo.mWindow->GetExtantDoc();
3020 0 : NS_ENSURE_TRUE(document, NS_ERROR_FAILURE);
3021 :
3022 0 : loadInfo.mBaseURI = document->GetDocBaseURI();
3023 0 : loadInfo.mLoadGroup = document->GetDocumentLoadGroup();
3024 0 : NS_ENSURE_TRUE(loadInfo.mLoadGroup, NS_ERROR_FAILURE);
3025 :
3026 0 : clientInfo = globalWindow->GetClientInfo();
3027 :
3028 : // Use the document's NodePrincipal as loading principal if we're not being
3029 : // called from chrome.
3030 0 : if (!loadInfo.mLoadingPrincipal) {
3031 0 : loadInfo.mLoadingPrincipal = document->NodePrincipal();
3032 0 : NS_ENSURE_TRUE(loadInfo.mLoadingPrincipal, NS_ERROR_FAILURE);
3033 :
3034 : // We use the document's base domain to limit the number of workers
3035 : // each domain can create. For sandboxed documents, we use the domain
3036 : // of their first non-sandboxed document, walking up until we find
3037 : // one. If we can't find one, we fall back to using the GUID of the
3038 : // null principal as the base domain.
3039 0 : if (document->GetSandboxFlags() & SANDBOXED_ORIGIN) {
3040 0 : nsCOMPtr<nsIDocument> tmpDoc = document;
3041 0 : do {
3042 0 : tmpDoc = tmpDoc->GetParentDocument();
3043 0 : } while (tmpDoc && tmpDoc->GetSandboxFlags() & SANDBOXED_ORIGIN);
3044 :
3045 0 : if (tmpDoc) {
3046 : // There was an unsandboxed ancestor, yay!
3047 0 : nsCOMPtr<nsIPrincipal> tmpPrincipal = tmpDoc->NodePrincipal();
3048 0 : rv = tmpPrincipal->GetBaseDomain(loadInfo.mDomain);
3049 0 : NS_ENSURE_SUCCESS(rv, rv);
3050 : } else {
3051 : // No unsandboxed ancestor, use our GUID.
3052 0 : rv = loadInfo.mLoadingPrincipal->GetBaseDomain(loadInfo.mDomain);
3053 0 : NS_ENSURE_SUCCESS(rv, rv);
3054 : }
3055 : } else {
3056 : // Document creating the worker is not sandboxed.
3057 0 : rv = loadInfo.mLoadingPrincipal->GetBaseDomain(loadInfo.mDomain);
3058 0 : NS_ENSURE_SUCCESS(rv, rv);
3059 : }
3060 : }
3061 :
3062 0 : NS_ENSURE_TRUE(NS_LoadGroupMatchesPrincipal(loadInfo.mLoadGroup,
3063 : loadInfo.mLoadingPrincipal),
3064 : NS_ERROR_FAILURE);
3065 :
3066 : nsCOMPtr<nsIPermissionManager> permMgr =
3067 0 : do_GetService(NS_PERMISSIONMANAGER_CONTRACTID, &rv);
3068 0 : NS_ENSURE_SUCCESS(rv, rv);
3069 :
3070 : uint32_t perm;
3071 0 : rv = permMgr->TestPermissionFromPrincipal(loadInfo.mLoadingPrincipal,
3072 0 : "systemXHR", &perm);
3073 0 : NS_ENSURE_SUCCESS(rv, rv);
3074 :
3075 0 : loadInfo.mXHRParamsAllowed = perm == nsIPermissionManager::ALLOW_ACTION;
3076 :
3077 0 : loadInfo.mFromWindow = true;
3078 0 : loadInfo.mWindowID = globalWindow->WindowID();
3079 : nsContentUtils::StorageAccess access =
3080 0 : nsContentUtils::StorageAllowedForWindow(globalWindow);
3081 0 : loadInfo.mStorageAllowed = access > nsContentUtils::StorageAccess::eDeny;
3082 0 : loadInfo.mOriginAttributes = nsContentUtils::GetOriginAttributes(document);
3083 0 : loadInfo.mParentController = globalWindow->GetController();
3084 : } else {
3085 : // Not a window
3086 3 : MOZ_ASSERT(isChrome);
3087 :
3088 : // We're being created outside of a window. Need to figure out the script
3089 : // that is creating us in order for us to use relative URIs later on.
3090 6 : JS::AutoFilename fileName;
3091 0 : if (JS::DescribeScriptedCaller(aCx, &fileName)) {
3092 : // In most cases, fileName is URI. In a few other cases
3093 : // (e.g. xpcshell), fileName is a file path. Ideally, we would
3094 : // prefer testing whether fileName parses as an URI and fallback
3095 : // to file path in case of error, but Windows file paths have
3096 : // the interesting property that they can be parsed as bogus
3097 : // URIs (e.g. C:/Windows/Tmp is interpreted as scheme "C",
3098 : // hostname "Windows", path "Tmp"), which defeats this algorithm.
3099 : // Therefore, we adopt the opposite convention.
3100 : nsCOMPtr<nsIFile> scriptFile =
3101 6 : do_CreateInstance("@mozilla.org/file/local;1", &rv);
3102 0 : if (NS_FAILED(rv)) {
3103 0 : return rv;
3104 : }
3105 :
3106 6 : rv = scriptFile->InitWithPath(NS_ConvertUTF8toUTF16(fileName.get()));
3107 0 : if (NS_SUCCEEDED(rv)) {
3108 0 : rv = NS_NewFileURI(getter_AddRefs(loadInfo.mBaseURI),
3109 : scriptFile);
3110 : }
3111 3 : if (NS_FAILED(rv)) {
3112 : // As expected, fileName is not a path, so proceed with
3113 : // a uri.
3114 6 : rv = NS_NewURI(getter_AddRefs(loadInfo.mBaseURI),
3115 : fileName.get());
3116 : }
3117 3 : if (NS_FAILED(rv)) {
3118 : return rv;
3119 : }
3120 : }
3121 3 : loadInfo.mXHRParamsAllowed = true;
3122 0 : loadInfo.mFromWindow = false;
3123 0 : loadInfo.mWindowID = UINT64_MAX;
3124 0 : loadInfo.mStorageAllowed = true;
3125 0 : loadInfo.mOriginAttributes = OriginAttributes();
3126 : }
3127 :
3128 3 : MOZ_ASSERT(loadInfo.mLoadingPrincipal);
3129 0 : MOZ_ASSERT(isChrome || !loadInfo.mDomain.IsEmpty());
3130 :
3131 3 : if (!loadInfo.mLoadGroup || aLoadGroupBehavior == OverrideLoadGroup) {
3132 0 : OverrideLoadInfoLoadGroup(loadInfo, loadInfo.mLoadingPrincipal);
3133 : }
3134 6 : MOZ_ASSERT(NS_LoadGroupMatchesPrincipal(loadInfo.mLoadGroup,
3135 : loadInfo.mLoadingPrincipal));
3136 :
3137 : // Top level workers' main script use the document charset for the script
3138 : // uri encoding.
3139 3 : bool useDefaultEncoding = false;
3140 0 : rv = ChannelFromScriptURLMainThread(loadInfo.mLoadingPrincipal,
3141 : loadInfo.mBaseURI,
3142 : document, loadInfo.mLoadGroup,
3143 : aScriptURL,
3144 : clientInfo,
3145 : ContentPolicyType(aWorkerType),
3146 : useDefaultEncoding,
3147 6 : getter_AddRefs(loadInfo.mChannel));
3148 0 : NS_ENSURE_SUCCESS(rv, rv);
3149 :
3150 6 : rv = NS_GetFinalChannelURI(loadInfo.mChannel,
3151 0 : getter_AddRefs(loadInfo.mResolvedScriptURI));
3152 0 : NS_ENSURE_SUCCESS(rv, rv);
3153 :
3154 3 : rv = loadInfo.SetPrincipalFromChannel(loadInfo.mChannel);
3155 0 : NS_ENSURE_SUCCESS(rv, rv);
3156 : }
3157 :
3158 3 : MOZ_DIAGNOSTIC_ASSERT(loadInfo.mLoadingPrincipal);
3159 0 : MOZ_DIAGNOSTIC_ASSERT(loadInfo.PrincipalIsValid());
3160 :
3161 3 : aLoadInfo->StealFrom(loadInfo);
3162 0 : return NS_OK;
3163 : }
3164 :
3165 : // static
3166 : void
3167 3 : WorkerPrivate::OverrideLoadInfoLoadGroup(WorkerLoadInfo& aLoadInfo,
3168 : nsIPrincipal* aPrincipal)
3169 : {
3170 6 : MOZ_ASSERT(!aLoadInfo.mInterfaceRequestor);
3171 0 : MOZ_ASSERT(aLoadInfo.mLoadingPrincipal == aPrincipal);
3172 :
3173 : aLoadInfo.mInterfaceRequestor =
3174 12 : new WorkerLoadInfo::InterfaceRequestor(aPrincipal, aLoadInfo.mLoadGroup);
3175 0 : aLoadInfo.mInterfaceRequestor->MaybeAddTabChild(aLoadInfo.mLoadGroup);
3176 :
3177 : // NOTE: this defaults the load context to:
3178 : // - private browsing = false
3179 : // - content = true
3180 : // - use remote tabs = false
3181 : nsCOMPtr<nsILoadGroup> loadGroup =
3182 6 : do_CreateInstance(NS_LOADGROUP_CONTRACTID);
3183 :
3184 : nsresult rv =
3185 6 : loadGroup->SetNotificationCallbacks(aLoadInfo.mInterfaceRequestor);
3186 0 : MOZ_ALWAYS_SUCCEEDS(rv);
3187 :
3188 3 : aLoadInfo.mLoadGroup = loadGroup.forget();
3189 :
3190 6 : MOZ_ASSERT(NS_LoadGroupMatchesPrincipal(aLoadInfo.mLoadGroup, aPrincipal));
3191 0 : }
3192 :
3193 : void
3194 3 : WorkerPrivate::DoRunLoop(JSContext* aCx)
3195 : {
3196 3 : AssertIsOnWorkerThread();
3197 0 : MOZ_ASSERT(mThread);
3198 :
3199 : {
3200 9 : MutexAutoLock lock(mMutex);
3201 0 : mJSContext = aCx;
3202 :
3203 3 : MOZ_ASSERT(mStatus == Pending);
3204 0 : mStatus = Running;
3205 : }
3206 :
3207 : // Now that we've done that, we can go ahead and set up our AutoJSAPI. We
3208 : // can't before this point, because it can't find the right JSContext before
3209 : // then, since it gets it from our mJSContext.
3210 3 : AutoJSAPI jsapi;
3211 0 : jsapi.Init();
3212 0 : MOZ_ASSERT(jsapi.cx() == aCx);
3213 :
3214 3 : EnableMemoryReporter();
3215 :
3216 3 : InitializeGCTimers();
3217 :
3218 0 : Maybe<JSAutoRealm> workerCompartment;
3219 :
3220 : for (;;) {
3221 : WorkerStatus currentStatus, previousStatus;
3222 56 : bool debuggerRunnablesPending = false;
3223 0 : bool normalRunnablesPending = false;
3224 :
3225 : {
3226 166 : MutexAutoLock lock(mMutex);
3227 0 : previousStatus = mStatus;
3228 :
3229 364 : while (mControlQueue.IsEmpty() &&
3230 0 : !(debuggerRunnablesPending = !mDebuggerQueue.IsEmpty()) &&
3231 0 : !(normalRunnablesPending = NS_HasPendingEvents(mThread))) {
3232 0 : WaitForWorkerEvents();
3233 : }
3234 :
3235 54 : auto result = ProcessAllControlRunnablesLocked();
3236 0 : if (result != ProcessAllControlRunnablesResult::Nothing) {
3237 : // NB: There's no JS on the stack here, so Abort vs MayContinue is
3238 : // irrelevant
3239 :
3240 : // The state of the world may have changed, recheck it.
3241 0 : normalRunnablesPending = NS_HasPendingEvents(mThread);
3242 : // The debugger queue doesn't get cleared, so we can ignore that.
3243 : }
3244 :
3245 54 : currentStatus = mStatus;
3246 : }
3247 :
3248 : // if all holders are done then we can kill this thread.
3249 54 : if (currentStatus != Running && !HasActiveHolders()) {
3250 :
3251 : // If we just changed status, we must schedule the current runnables.
3252 0 : if (previousStatus != Running && currentStatus != Killing) {
3253 0 : NotifyInternal(Killing);
3254 :
3255 : #ifdef DEBUG
3256 : {
3257 0 : MutexAutoLock lock(mMutex);
3258 0 : currentStatus = mStatus;
3259 : }
3260 0 : MOZ_ASSERT(currentStatus == Killing);
3261 : #else
3262 : currentStatus = Killing;
3263 : #endif
3264 : }
3265 :
3266 : // If we're supposed to die then we should exit the loop.
3267 0 : if (currentStatus == Killing) {
3268 : // The ClientSource should be cleared in NotifyInternal() when we reach
3269 : // or pass Terminating.
3270 0 : MOZ_DIAGNOSTIC_ASSERT(!mClientSource);
3271 :
3272 : // Flush uncaught rejections immediately, without
3273 : // waiting for a next tick.
3274 0 : PromiseDebugging::FlushUncaughtRejections();
3275 :
3276 0 : ShutdownGCTimers();
3277 :
3278 0 : DisableMemoryReporter();
3279 :
3280 : {
3281 0 : MutexAutoLock lock(mMutex);
3282 :
3283 0 : mStatus = Dead;
3284 0 : mJSContext = nullptr;
3285 : }
3286 :
3287 : // After mStatus is set to Dead there can be no more
3288 : // WorkerControlRunnables so no need to lock here.
3289 0 : if (!mControlQueue.IsEmpty()) {
3290 0 : WorkerControlRunnable* runnable = nullptr;
3291 0 : while (mControlQueue.Pop(runnable)) {
3292 0 : runnable->Cancel();
3293 0 : runnable->Release();
3294 : }
3295 : }
3296 :
3297 : // Unroot the globals
3298 0 : mScope = nullptr;
3299 0 : mDebuggerScope = nullptr;
3300 :
3301 0 : return;
3302 : }
3303 : }
3304 :
3305 54 : if (debuggerRunnablesPending || normalRunnablesPending) {
3306 : // Start the periodic GC timer if it is not already running.
3307 54 : SetGCTimerMode(PeriodicTimer);
3308 : }
3309 :
3310 54 : if (debuggerRunnablesPending) {
3311 0 : WorkerRunnable* runnable = nullptr;
3312 :
3313 : {
3314 0 : MutexAutoLock lock(mMutex);
3315 :
3316 0 : mDebuggerQueue.Pop(runnable);
3317 0 : debuggerRunnablesPending = !mDebuggerQueue.IsEmpty();
3318 : }
3319 :
3320 0 : MOZ_ASSERT(runnable);
3321 0 : static_cast<nsIRunnable*>(runnable)->Run();
3322 0 : runnable->Release();
3323 :
3324 0 : CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get();
3325 0 : ccjs->PerformDebuggerMicroTaskCheckpoint();
3326 :
3327 0 : if (debuggerRunnablesPending) {
3328 0 : WorkerDebuggerGlobalScope* globalScope = DebuggerGlobalScope();
3329 0 : MOZ_ASSERT(globalScope);
3330 :
3331 : // Now *might* be a good time to GC. Let the JS engine make the decision.
3332 0 : JSAutoRealm ar(aCx, globalScope->GetGlobalJSObject());
3333 0 : JS_MaybeGC(aCx);
3334 : }
3335 54 : } else if (normalRunnablesPending) {
3336 : // Process a single runnable from the main queue.
3337 108 : NS_ProcessNextEvent(mThread, false);
3338 :
3339 106 : normalRunnablesPending = NS_HasPendingEvents(mThread);
3340 0 : if (normalRunnablesPending && GlobalScope()) {
3341 : // Now *might* be a good time to GC. Let the JS engine make the decision.
3342 6 : JSAutoRealm ar(aCx, GlobalScope()->GetGlobalJSObject());
3343 0 : JS_MaybeGC(aCx);
3344 : }
3345 : }
3346 :
3347 53 : if (!debuggerRunnablesPending && !normalRunnablesPending) {
3348 : // Both the debugger event queue and the normal event queue has been
3349 : // exhausted, cancel the periodic GC timer and schedule the idle GC timer.
3350 51 : SetGCTimerMode(IdleTimer);
3351 : }
3352 :
3353 : // If the worker thread is spamming the main thread faster than it can
3354 : // process the work, then pause the worker thread until the MT catches
3355 : // up.
3356 106 : if (mMainThreadThrottledEventQueue &&
3357 0 : mMainThreadThrottledEventQueue->Length() > 5000) {
3358 0 : mMainThreadThrottledEventQueue->AwaitIdle();
3359 : }
3360 : }
3361 :
3362 : MOZ_CRASH("Shouldn't get here!");
3363 : }
3364 :
3365 : void
3366 293 : WorkerPrivate::OnProcessNextEvent()
3367 : {
3368 293 : AssertIsOnWorkerThread();
3369 :
3370 293 : uint32_t recursionDepth = CycleCollectedJSContext::Get()->RecursionDepth();
3371 0 : MOZ_ASSERT(recursionDepth);
3372 :
3373 : // Normally we process control runnables in DoRunLoop or RunCurrentSyncLoop.
3374 : // However, it's possible that non-worker C++ could spin its own nested event
3375 : // loop, and in that case we must ensure that we continue to process control
3376 : // runnables here.
3377 586 : if (recursionDepth > 1 &&
3378 0 : mSyncLoopStack.Length() < recursionDepth - 1) {
3379 0 : Unused << ProcessAllControlRunnables();
3380 : // There's no running JS, and no state to revalidate, so we can ignore the
3381 : // return value.
3382 : }
3383 293 : }
3384 :
3385 : void
3386 289 : WorkerPrivate::AfterProcessNextEvent()
3387 : {
3388 289 : AssertIsOnWorkerThread();
3389 0 : MOZ_ASSERT(CycleCollectedJSContext::Get()->RecursionDepth());
3390 0 : }
3391 :
3392 : nsIEventTarget*
3393 0 : WorkerPrivate::MainThreadEventTarget()
3394 : {
3395 0 : return mMainThreadEventTarget;
3396 : }
3397 :
3398 : nsresult
3399 73 : WorkerPrivate::DispatchToMainThread(nsIRunnable* aRunnable, uint32_t aFlags)
3400 : {
3401 146 : nsCOMPtr<nsIRunnable> r = aRunnable;
3402 0 : return DispatchToMainThread(r.forget(), aFlags);
3403 : }
3404 :
3405 : nsresult
3406 182 : WorkerPrivate::DispatchToMainThread(already_AddRefed<nsIRunnable> aRunnable,
3407 : uint32_t aFlags)
3408 : {
3409 364 : return mMainThreadEventTarget->Dispatch(std::move(aRunnable), aFlags);
3410 : }
3411 :
3412 : nsISerialEventTarget*
3413 0 : WorkerPrivate::ControlEventTarget()
3414 : {
3415 0 : return mWorkerControlEventTarget;
3416 : }
3417 :
3418 : nsISerialEventTarget*
3419 3 : WorkerPrivate::HybridEventTarget()
3420 : {
3421 12 : return mWorkerHybridEventTarget;
3422 : }
3423 :
3424 : bool
3425 3 : WorkerPrivate::EnsureClientSource()
3426 : {
3427 3 : AssertIsOnWorkerThread();
3428 :
3429 6 : if (mClientSource) {
3430 : return true;
3431 : }
3432 :
3433 : ClientType type;
3434 3 : switch(Type()) {
3435 : case WorkerTypeDedicated:
3436 : type = ClientType::Worker;
3437 : break;
3438 : case WorkerTypeShared:
3439 0 : type = ClientType::Sharedworker;
3440 0 : break;
3441 : case WorkerTypeService:
3442 0 : type = ClientType::Serviceworker;
3443 0 : break;
3444 : default:
3445 0 : MOZ_CRASH("unknown worker type!");
3446 : }
3447 :
3448 9 : mClientSource = ClientManager::CreateSource(type, mWorkerHybridEventTarget,
3449 0 : GetPrincipalInfo());
3450 0 : MOZ_DIAGNOSTIC_ASSERT(mClientSource);
3451 :
3452 3 : if (mFrozen) {
3453 0 : mClientSource->Freeze();
3454 : }
3455 :
3456 : // Shortly after the client is reserved we will try loading the main script
3457 : // for the worker. This may get intercepted by the ServiceWorkerManager
3458 : // which will then try to create a ClientHandle. Its actually possible for
3459 : // the main thread to create this ClientHandle before our IPC message creating
3460 : // the ClientSource completes. To avoid this race we synchronously ping our
3461 : // parent Client actor here. This ensure the worker ClientSource is created
3462 : // in the parent before the main thread might try reaching it with a
3463 : // ClientHandle.
3464 : //
3465 : // An alternative solution would have been to handle the out-of-order operations
3466 : // on the parent side. We could have created a small window where we allow
3467 : // ClientHandle objects to exist without a ClientSource. We would then time
3468 : // out these handles if they stayed orphaned for too long. This approach would
3469 : // be much more complex, but also avoid this extra bit of latency when starting
3470 : // workers.
3471 : //
3472 : // Note, we only have to do this for workers that can be controlled by a
3473 : // service worker. So avoid the sync overhead here if we are starting a
3474 : // service worker or a chrome worker.
3475 3 : if (Type() != WorkerTypeService && !IsChromeWorker()) {
3476 0 : mClientSource->WorkerSyncPing(this);
3477 : }
3478 :
3479 : return true;
3480 : }
3481 :
3482 : void
3483 3 : WorkerPrivate::EnsurePerformanceStorage()
3484 : {
3485 3 : AssertIsOnWorkerThread();
3486 :
3487 6 : if (!mPerformanceStorage) {
3488 0 : mPerformanceStorage = PerformanceStorageWorker::Create(this);
3489 : }
3490 3 : }
3491 :
3492 : Maybe<ClientInfo>
3493 37 : WorkerPrivate::GetClientInfo() const
3494 : {
3495 37 : AssertIsOnWorkerThread();
3496 0 : Maybe<ClientInfo> clientInfo;
3497 0 : if (!mClientSource) {
3498 0 : MOZ_DIAGNOSTIC_ASSERT(mStatus >= Terminating);
3499 : return clientInfo;
3500 : }
3501 37 : clientInfo.emplace(mClientSource->Info());
3502 0 : return clientInfo;
3503 : }
3504 :
3505 : const ClientState
3506 0 : WorkerPrivate::GetClientState() const
3507 : {
3508 0 : AssertIsOnWorkerThread();
3509 0 : MOZ_DIAGNOSTIC_ASSERT(mClientSource);
3510 0 : ClientState state;
3511 0 : mClientSource->SnapshotState(&state);
3512 0 : return state;
3513 : }
3514 :
3515 : const Maybe<ServiceWorkerDescriptor>
3516 34 : WorkerPrivate::GetController()
3517 : {
3518 34 : AssertIsOnWorkerThread();
3519 : {
3520 102 : MutexAutoLock lock(mMutex);
3521 0 : if (mStatus >= Terminating) {
3522 0 : return Maybe<ServiceWorkerDescriptor>();
3523 : }
3524 : }
3525 68 : MOZ_DIAGNOSTIC_ASSERT(mClientSource);
3526 0 : return mClientSource->GetController();
3527 : }
3528 :
3529 : void
3530 0 : WorkerPrivate::Control(const ServiceWorkerDescriptor& aServiceWorker)
3531 : {
3532 0 : AssertIsOnWorkerThread();
3533 0 : MOZ_DIAGNOSTIC_ASSERT(!IsChromeWorker());
3534 0 : MOZ_DIAGNOSTIC_ASSERT(Type() != WorkerTypeService);
3535 : {
3536 0 : MutexAutoLock lock(mMutex);
3537 0 : if (mStatus >= Terminating) {
3538 0 : return;
3539 : }
3540 : }
3541 0 : MOZ_DIAGNOSTIC_ASSERT(mClientSource);
3542 0 : mClientSource->SetController(aServiceWorker);
3543 : }
3544 :
3545 : void
3546 3 : WorkerPrivate::ExecutionReady()
3547 : {
3548 3 : AssertIsOnWorkerThread();
3549 : {
3550 9 : MutexAutoLock lock(mMutex);
3551 0 : if (mStatus >= Terminating) {
3552 0 : return;
3553 : }
3554 : }
3555 6 : MOZ_DIAGNOSTIC_ASSERT(mClientSource);
3556 0 : mClientSource->WorkerExecutionReady(this);
3557 : }
3558 :
3559 : void
3560 3 : WorkerPrivate::InitializeGCTimers()
3561 : {
3562 3 : AssertIsOnWorkerThread();
3563 :
3564 : // We need a timer for GC. The basic plan is to run a non-shrinking GC
3565 : // periodically (PERIODIC_GC_TIMER_DELAY_SEC) while the worker is running.
3566 : // Once the worker goes idle we set a short (IDLE_GC_TIMER_DELAY_SEC) timer to
3567 : // run a shrinking GC. If the worker receives more messages then the short
3568 : // timer is canceled and the periodic timer resumes.
3569 3 : mGCTimer = NS_NewTimer();
3570 0 : MOZ_ASSERT(mGCTimer);
3571 :
3572 3 : mPeriodicGCTimerRunning = false;
3573 0 : mIdleGCTimerRunning = false;
3574 0 : }
3575 :
3576 : void
3577 606 : WorkerPrivate::SetGCTimerMode(GCTimerMode aMode)
3578 : {
3579 606 : AssertIsOnWorkerThread();
3580 0 : MOZ_ASSERT(mGCTimer);
3581 :
3582 606 : if ((aMode == PeriodicTimer && mPeriodicGCTimerRunning) ||
3583 0 : (aMode == IdleTimer && mIdleGCTimerRunning)) {
3584 : return;
3585 : }
3586 :
3587 510 : MOZ_ALWAYS_SUCCEEDS(mGCTimer->Cancel());
3588 :
3589 510 : mPeriodicGCTimerRunning = false;
3590 0 : mIdleGCTimerRunning = false;
3591 0 : LOG(WorkerLog(),
3592 : ("Worker %p canceled GC timer because %s\n", this,
3593 : aMode == PeriodicTimer ?
3594 : "periodic" :
3595 : aMode == IdleTimer ? "idle" : "none"));
3596 :
3597 510 : if (aMode == NoTimer) {
3598 : return;
3599 : }
3600 :
3601 452 : MOZ_ASSERT(aMode == PeriodicTimer || aMode == IdleTimer);
3602 :
3603 452 : uint32_t delay = 0;
3604 0 : int16_t type = nsITimer::TYPE_ONE_SHOT;
3605 0 : nsTimerCallbackFunc callback = nullptr;
3606 0 : const char* name = nullptr;
3607 :
3608 452 : if (aMode == PeriodicTimer) {
3609 : delay = PERIODIC_GC_TIMER_DELAY_SEC * 1000;
3610 : type = nsITimer::TYPE_REPEATING_SLACK;
3611 : callback = PeriodicGCTimerCallback;
3612 : name = "dom::PeriodicGCTimerCallback";
3613 : }
3614 : else {
3615 197 : delay = IDLE_GC_TIMER_DELAY_SEC * 1000;
3616 0 : type = nsITimer::TYPE_ONE_SHOT;
3617 0 : callback = IdleGCTimerCallback;
3618 0 : name = "dom::IdleGCTimerCallback";
3619 : }
3620 :
3621 904 : MOZ_ALWAYS_SUCCEEDS(mGCTimer->SetTarget(mWorkerControlEventTarget));
3622 0 : MOZ_ALWAYS_SUCCEEDS(
3623 : mGCTimer->InitWithNamedFuncCallback(callback, this, delay, type, name));
3624 :
3625 452 : if (aMode == PeriodicTimer) {
3626 0 : LOG(WorkerLog(), ("Worker %p scheduled periodic GC timer\n", this));
3627 0 : mPeriodicGCTimerRunning = true;
3628 : }
3629 : else {
3630 197 : LOG(WorkerLog(), ("Worker %p scheduled idle GC timer\n", this));
3631 0 : mIdleGCTimerRunning = true;
3632 : }
3633 : }
3634 :
3635 : void
3636 0 : WorkerPrivate::ShutdownGCTimers()
3637 : {
3638 0 : AssertIsOnWorkerThread();
3639 :
3640 0 : MOZ_ASSERT(mGCTimer);
3641 :
3642 : // Always make sure the timer is canceled.
3643 0 : MOZ_ALWAYS_SUCCEEDS(mGCTimer->Cancel());
3644 :
3645 0 : LOG(WorkerLog(), ("Worker %p killed the GC timer\n", this));
3646 :
3647 0 : mGCTimer = nullptr;
3648 0 : mPeriodicGCTimerRunning = false;
3649 0 : mIdleGCTimerRunning = false;
3650 0 : }
3651 :
3652 : bool
3653 0 : WorkerPrivate::InterruptCallback(JSContext* aCx)
3654 : {
3655 0 : AssertIsOnWorkerThread();
3656 :
3657 0 : MOZ_ASSERT(!JS_IsExceptionPending(aCx));
3658 :
3659 : bool mayContinue = true;
3660 : bool scheduledIdleGC = false;
3661 :
3662 : for (;;) {
3663 : // Run all control events now.
3664 0 : auto result = ProcessAllControlRunnables();
3665 0 : if (result == ProcessAllControlRunnablesResult::Abort) {
3666 0 : mayContinue = false;
3667 : }
3668 :
3669 0 : bool mayFreeze = mFrozen;
3670 0 : if (mayFreeze) {
3671 0 : MutexAutoLock lock(mMutex);
3672 0 : mayFreeze = mStatus <= Running;
3673 : }
3674 :
3675 0 : if (!mayContinue || !mayFreeze) {
3676 : break;
3677 : }
3678 :
3679 : // Cancel the periodic GC timer here before freezing. The idle GC timer
3680 : // will clean everything up once it runs.
3681 0 : if (!scheduledIdleGC) {
3682 0 : SetGCTimerMode(IdleTimer);
3683 0 : scheduledIdleGC = true;
3684 : }
3685 :
3686 0 : while ((mayContinue = MayContinueRunning())) {
3687 0 : MutexAutoLock lock(mMutex);
3688 0 : if (!mControlQueue.IsEmpty()) {
3689 : break;
3690 : }
3691 :
3692 0 : WaitForWorkerEvents();
3693 : }
3694 : }
3695 :
3696 0 : if (!mayContinue) {
3697 : // We want only uncatchable exceptions here.
3698 0 : NS_ASSERTION(!JS_IsExceptionPending(aCx),
3699 : "Should not have an exception set here!");
3700 : return false;
3701 : }
3702 :
3703 : // Make sure the periodic timer gets turned back on here.
3704 0 : SetGCTimerMode(PeriodicTimer);
3705 :
3706 0 : return true;
3707 : }
3708 :
3709 : void
3710 0 : WorkerPrivate::CloseInternal()
3711 : {
3712 0 : AssertIsOnWorkerThread();
3713 0 : NotifyInternal(Closing);
3714 0 : }
3715 :
3716 : bool
3717 0 : WorkerPrivate::IsOnCurrentThread()
3718 : {
3719 : // May be called on any thread!
3720 :
3721 0 : MOZ_ASSERT(mPRThread);
3722 0 : return PR_GetCurrentThread() == mPRThread;
3723 : }
3724 :
3725 : void
3726 0 : WorkerPrivate::ScheduleDeletion(WorkerRanOrNot aRanOrNot)
3727 : {
3728 0 : AssertIsOnWorkerThread();
3729 0 : MOZ_ASSERT(mChildWorkers.IsEmpty());
3730 0 : MOZ_ASSERT(mSyncLoopStack.IsEmpty());
3731 0 : MOZ_ASSERT(!mPendingEventQueueClearing);
3732 :
3733 0 : ClearMainEventQueue(aRanOrNot);
3734 : #ifdef DEBUG
3735 0 : if (WorkerRan == aRanOrNot) {
3736 0 : nsIThread* currentThread = NS_GetCurrentThread();
3737 0 : MOZ_ASSERT(currentThread);
3738 0 : MOZ_ASSERT(!NS_HasPendingEvents(currentThread));
3739 : }
3740 : #endif
3741 :
3742 0 : if (WorkerPrivate* parent = GetParent()) {
3743 : RefPtr<WorkerFinishedRunnable> runnable =
3744 0 : new WorkerFinishedRunnable(parent, this);
3745 0 : if (!runnable->Dispatch()) {
3746 0 : NS_WARNING("Failed to dispatch runnable!");
3747 : }
3748 : }
3749 : else {
3750 : RefPtr<TopLevelWorkerFinishedRunnable> runnable =
3751 0 : new TopLevelWorkerFinishedRunnable(this);
3752 0 : if (NS_FAILED(DispatchToMainThread(runnable.forget()))) {
3753 0 : NS_WARNING("Failed to dispatch runnable!");
3754 : }
3755 : }
3756 0 : }
3757 :
3758 : bool
3759 0 : WorkerPrivate::CollectRuntimeStats(JS::RuntimeStats* aRtStats,
3760 : bool aAnonymize)
3761 : {
3762 0 : AssertIsOnWorkerThread();
3763 0 : NS_ASSERTION(aRtStats, "Null RuntimeStats!");
3764 0 : NS_ASSERTION(mJSContext, "This must never be null!");
3765 :
3766 0 : return JS::CollectRuntimeStats(mJSContext, aRtStats, nullptr, aAnonymize);
3767 : }
3768 :
3769 : void
3770 3 : WorkerPrivate::EnableMemoryReporter()
3771 : {
3772 3 : AssertIsOnWorkerThread();
3773 0 : MOZ_ASSERT(!mMemoryReporter);
3774 :
3775 : // No need to lock here since the main thread can't race until we've
3776 : // successfully registered the reporter.
3777 6 : mMemoryReporter = new MemoryReporter(this);
3778 :
3779 6 : if (NS_FAILED(RegisterWeakAsyncMemoryReporter(mMemoryReporter))) {
3780 0 : NS_WARNING("Failed to register memory reporter!");
3781 : // No need to lock here since a failed registration means our memory
3782 : // reporter can't start running. Just clean up.
3783 0 : mMemoryReporter = nullptr;
3784 : }
3785 3 : }
3786 :
3787 : void
3788 0 : WorkerPrivate::DisableMemoryReporter()
3789 : {
3790 0 : AssertIsOnWorkerThread();
3791 :
3792 0 : RefPtr<MemoryReporter> memoryReporter;
3793 : {
3794 : // Mutex protectes MemoryReporter::mWorkerPrivate which is cleared by
3795 : // MemoryReporter::Disable() below.
3796 0 : MutexAutoLock lock(mMutex);
3797 :
3798 : // There is nothing to do here if the memory reporter was never successfully
3799 : // registered.
3800 0 : if (!mMemoryReporter) {
3801 0 : return;
3802 : }
3803 :
3804 : // We don't need this set any longer. Swap it out so that we can unregister
3805 : // below.
3806 0 : mMemoryReporter.swap(memoryReporter);
3807 :
3808 : // Next disable the memory reporter so that the main thread stops trying to
3809 : // signal us.
3810 0 : memoryReporter->Disable();
3811 : }
3812 :
3813 : // Finally unregister the memory reporter.
3814 0 : if (NS_FAILED(UnregisterWeakMemoryReporter(memoryReporter))) {
3815 0 : NS_WARNING("Failed to unregister memory reporter!");
3816 : }
3817 : }
3818 :
3819 : void
3820 182 : WorkerPrivate::WaitForWorkerEvents()
3821 : {
3822 361 : AUTO_PROFILER_LABEL("WorkerPrivate::WaitForWorkerEvents", IDLE);
3823 :
3824 0 : AssertIsOnWorkerThread();
3825 182 : mMutex.AssertCurrentThreadOwns();
3826 :
3827 : // Wait for a worker event.
3828 0 : mCondVar.Wait();
3829 179 : }
3830 :
3831 : WorkerPrivate::ProcessAllControlRunnablesResult
3832 550 : WorkerPrivate::ProcessAllControlRunnablesLocked()
3833 : {
3834 0 : AssertIsOnWorkerThread();
3835 550 : mMutex.AssertCurrentThreadOwns();
3836 :
3837 550 : auto result = ProcessAllControlRunnablesResult::Nothing;
3838 :
3839 : for (;;) {
3840 : WorkerControlRunnable* event;
3841 550 : if (!mControlQueue.Pop(event)) {
3842 : break;
3843 : }
3844 :
3845 0 : MutexAutoUnlock unlock(mMutex);
3846 :
3847 0 : MOZ_ASSERT(event);
3848 0 : if (NS_FAILED(static_cast<nsIRunnable*>(event)->Run())) {
3849 0 : result = ProcessAllControlRunnablesResult::Abort;
3850 : }
3851 :
3852 0 : if (result == ProcessAllControlRunnablesResult::Nothing) {
3853 : // We ran at least one thing.
3854 0 : result = ProcessAllControlRunnablesResult::MayContinue;
3855 : }
3856 0 : event->Release();
3857 0 : }
3858 :
3859 550 : return result;
3860 : }
3861 :
3862 : void
3863 0 : WorkerPrivate::ClearMainEventQueue(WorkerRanOrNot aRanOrNot)
3864 : {
3865 0 : AssertIsOnWorkerThread();
3866 :
3867 0 : MOZ_ASSERT(mSyncLoopStack.IsEmpty());
3868 0 : MOZ_ASSERT(!mCancelAllPendingRunnables);
3869 0 : mCancelAllPendingRunnables = true;
3870 :
3871 0 : if (WorkerNeverRan == aRanOrNot) {
3872 0 : for (uint32_t count = mPreStartRunnables.Length(), index = 0;
3873 0 : index < count;
3874 : index++) {
3875 0 : RefPtr<WorkerRunnable> runnable = mPreStartRunnables[index].forget();
3876 0 : static_cast<nsIRunnable*>(runnable.get())->Run();
3877 : }
3878 : } else {
3879 0 : nsIThread* currentThread = NS_GetCurrentThread();
3880 0 : MOZ_ASSERT(currentThread);
3881 :
3882 0 : NS_ProcessPendingEvents(currentThread);
3883 : }
3884 :
3885 0 : MOZ_ASSERT(mCancelAllPendingRunnables);
3886 0 : mCancelAllPendingRunnables = false;
3887 0 : }
3888 :
3889 : void
3890 0 : WorkerPrivate::ClearDebuggerEventQueue()
3891 : {
3892 0 : while (!mDebuggerQueue.IsEmpty()) {
3893 0 : WorkerRunnable* runnable = nullptr;
3894 0 : mDebuggerQueue.Pop(runnable);
3895 : // It should be ok to simply release the runnable, without running it.
3896 0 : runnable->Release();
3897 : }
3898 0 : }
3899 :
3900 : bool
3901 0 : WorkerPrivate::FreezeInternal()
3902 : {
3903 0 : AssertIsOnWorkerThread();
3904 :
3905 0 : NS_ASSERTION(!mFrozen, "Already frozen!");
3906 :
3907 0 : if (mClientSource) {
3908 0 : mClientSource->Freeze();
3909 : }
3910 :
3911 0 : mFrozen = true;
3912 :
3913 0 : for (uint32_t index = 0; index < mChildWorkers.Length(); index++) {
3914 0 : mChildWorkers[index]->Freeze(nullptr);
3915 : }
3916 :
3917 0 : return true;
3918 : }
3919 :
3920 : bool
3921 0 : WorkerPrivate::ThawInternal()
3922 : {
3923 0 : AssertIsOnWorkerThread();
3924 :
3925 0 : NS_ASSERTION(mFrozen, "Not yet frozen!");
3926 :
3927 0 : for (uint32_t index = 0; index < mChildWorkers.Length(); index++) {
3928 0 : mChildWorkers[index]->Thaw(nullptr);
3929 : }
3930 :
3931 0 : mFrozen = false;
3932 :
3933 0 : if (mClientSource) {
3934 0 : mClientSource->Thaw();
3935 : }
3936 :
3937 0 : return true;
3938 : }
3939 :
3940 : void
3941 3 : WorkerPrivate::TraverseTimeouts(nsCycleCollectionTraversalCallback& cb)
3942 : {
3943 1 : for (uint32_t i = 0; i < mTimeouts.Length(); ++i) {
3944 0 : TimeoutInfo* tmp = mTimeouts[i];
3945 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mHandler)
3946 : }
3947 3 : }
3948 :
3949 : void
3950 0 : WorkerPrivate::UnlinkTimeouts()
3951 : {
3952 0 : mTimeouts.Clear();
3953 0 : }
3954 :
3955 : bool
3956 58 : WorkerPrivate::ModifyBusyCountFromWorker(bool aIncrease)
3957 : {
3958 58 : AssertIsOnWorkerThread();
3959 :
3960 : {
3961 174 : MutexAutoLock lock(mMutex);
3962 :
3963 : // If we're in shutdown then the busy count is no longer being considered so
3964 : // just return now.
3965 0 : if (mStatus >= Killing) {
3966 0 : return true;
3967 : }
3968 : }
3969 :
3970 : RefPtr<ModifyBusyCountRunnable> runnable =
3971 0 : new ModifyBusyCountRunnable(this, aIncrease);
3972 58 : return runnable->Dispatch();
3973 : }
3974 :
3975 : bool
3976 0 : WorkerPrivate::AddChildWorker(WorkerPrivate* aChildWorker)
3977 : {
3978 0 : AssertIsOnWorkerThread();
3979 :
3980 : #ifdef DEBUG
3981 : {
3982 : WorkerStatus currentStatus;
3983 : {
3984 0 : MutexAutoLock lock(mMutex);
3985 0 : currentStatus = mStatus;
3986 : }
3987 :
3988 0 : MOZ_ASSERT(currentStatus == Running);
3989 : }
3990 : #endif
3991 :
3992 0 : NS_ASSERTION(!mChildWorkers.Contains(aChildWorker),
3993 : "Already know about this one!");
3994 0 : mChildWorkers.AppendElement(aChildWorker);
3995 :
3996 0 : return mChildWorkers.Length() == 1 ?
3997 : ModifyBusyCountFromWorker(true) :
3998 0 : true;
3999 : }
4000 :
4001 : void
4002 0 : WorkerPrivate::RemoveChildWorker(WorkerPrivate* aChildWorker)
4003 : {
4004 0 : AssertIsOnWorkerThread();
4005 :
4006 0 : NS_ASSERTION(mChildWorkers.Contains(aChildWorker),
4007 : "Didn't know about this one!");
4008 0 : mChildWorkers.RemoveElement(aChildWorker);
4009 :
4010 0 : if (mChildWorkers.IsEmpty() && !ModifyBusyCountFromWorker(false)) {
4011 0 : NS_WARNING("Failed to modify busy count!");
4012 : }
4013 0 : }
4014 :
4015 : bool
4016 42 : WorkerPrivate::AddHolder(WorkerHolder* aHolder, WorkerStatus aFailStatus)
4017 : {
4018 42 : AssertIsOnWorkerThread();
4019 :
4020 : {
4021 126 : MutexAutoLock lock(mMutex);
4022 :
4023 0 : if (mStatus >= aFailStatus) {
4024 0 : return false;
4025 : }
4026 : }
4027 :
4028 42 : MOZ_ASSERT(!mHolders.Contains(aHolder), "Already know about this one!");
4029 :
4030 0 : if (aHolder->GetBehavior() == WorkerHolder::PreventIdleShutdownStart) {
4031 36 : if (!mNumHoldersPreventingShutdownStart && !ModifyBusyCountFromWorker(true)) {
4032 : return false;
4033 : }
4034 36 : mNumHoldersPreventingShutdownStart += 1;
4035 : }
4036 :
4037 0 : mHolders.AppendElement(aHolder);
4038 42 : return true;
4039 : }
4040 :
4041 : void
4042 33 : WorkerPrivate::RemoveHolder(WorkerHolder* aHolder)
4043 : {
4044 33 : AssertIsOnWorkerThread();
4045 :
4046 0 : MOZ_ASSERT(mHolders.Contains(aHolder), "Didn't know about this one!");
4047 33 : mHolders.RemoveElement(aHolder);
4048 :
4049 0 : if (aHolder->GetBehavior() == WorkerHolder::PreventIdleShutdownStart) {
4050 0 : mNumHoldersPreventingShutdownStart -= 1;
4051 1 : if (!mNumHoldersPreventingShutdownStart && !ModifyBusyCountFromWorker(false)) {
4052 0 : NS_WARNING("Failed to modify busy count!");
4053 : }
4054 : }
4055 33 : }
4056 :
4057 : void
4058 0 : WorkerPrivate::NotifyHolders(WorkerStatus aStatus)
4059 : {
4060 0 : AssertIsOnWorkerThread();
4061 :
4062 0 : NS_ASSERTION(aStatus > Closing, "Bad status!");
4063 :
4064 0 : nsTObserverArray<WorkerHolder*>::ForwardIterator iter(mHolders);
4065 0 : while (iter.HasMore()) {
4066 0 : WorkerHolder* holder = iter.GetNext();
4067 0 : if (!holder->Notify(aStatus)) {
4068 0 : NS_WARNING("Failed to notify holder!");
4069 : }
4070 : }
4071 :
4072 0 : AutoTArray<WorkerPrivate*, 10> children;
4073 0 : children.AppendElements(mChildWorkers);
4074 :
4075 0 : for (uint32_t index = 0; index < children.Length(); index++) {
4076 0 : if (!children[index]->Notify(aStatus)) {
4077 0 : NS_WARNING("Failed to notify child worker!");
4078 : }
4079 : }
4080 0 : }
4081 :
4082 : void
4083 0 : WorkerPrivate::CancelAllTimeouts()
4084 : {
4085 0 : AssertIsOnWorkerThread();
4086 :
4087 0 : LOG(TimeoutsLog(), ("Worker %p CancelAllTimeouts.\n", this));
4088 :
4089 0 : if (mTimerRunning) {
4090 0 : NS_ASSERTION(mTimer && mTimerRunnable, "Huh?!");
4091 0 : NS_ASSERTION(!mTimeouts.IsEmpty(), "Huh?!");
4092 :
4093 0 : if (NS_FAILED(mTimer->Cancel())) {
4094 0 : NS_WARNING("Failed to cancel timer!");
4095 : }
4096 :
4097 0 : for (uint32_t index = 0; index < mTimeouts.Length(); index++) {
4098 0 : mTimeouts[index]->mCanceled = true;
4099 : }
4100 :
4101 : // If mRunningExpiredTimeouts, then the fact that they are all canceled now
4102 : // means that the currently executing RunExpiredTimeouts will deal with
4103 : // them. Otherwise, we need to clean them up ourselves.
4104 0 : if (!mRunningExpiredTimeouts) {
4105 0 : mTimeouts.Clear();
4106 0 : ModifyBusyCountFromWorker(false);
4107 : }
4108 :
4109 : // Set mTimerRunning false even if mRunningExpiredTimeouts is true, so that
4110 : // if we get reentered under this same RunExpiredTimeouts call we don't
4111 : // assert above that !mTimeouts().IsEmpty(), because that's clearly false
4112 : // now.
4113 0 : mTimerRunning = false;
4114 : }
4115 : #ifdef DEBUG
4116 0 : else if (!mRunningExpiredTimeouts) {
4117 0 : NS_ASSERTION(mTimeouts.IsEmpty(), "Huh?!");
4118 : }
4119 : #endif
4120 :
4121 0 : mTimer = nullptr;
4122 0 : mTimerRunnable = nullptr;
4123 0 : }
4124 :
4125 : already_AddRefed<nsIEventTarget>
4126 91 : WorkerPrivate::CreateNewSyncLoop(WorkerStatus aFailStatus)
4127 : {
4128 0 : AssertIsOnWorkerThread();
4129 91 : MOZ_ASSERT(aFailStatus >= Terminating,
4130 : "Sync loops can be created when the worker is in Running/Closing state!");
4131 :
4132 : {
4133 273 : MutexAutoLock lock(mMutex);
4134 :
4135 0 : if (mStatus >= aFailStatus) {
4136 0 : return nullptr;
4137 : }
4138 : }
4139 :
4140 0 : auto queue = static_cast<ThreadEventQueue<EventQueue>*>(mThread->EventQueue());
4141 0 : nsCOMPtr<nsISerialEventTarget> realEventTarget = queue->PushEventQueue();
4142 91 : MOZ_ASSERT(realEventTarget);
4143 :
4144 : RefPtr<EventTarget> workerEventTarget =
4145 273 : new EventTarget(this, realEventTarget);
4146 :
4147 : {
4148 : // Modifications must be protected by mMutex in DEBUG builds, see comment
4149 : // about mSyncLoopStack in WorkerPrivate.h.
4150 : #ifdef DEBUG
4151 273 : MutexAutoLock lock(mMutex);
4152 : #endif
4153 :
4154 182 : mSyncLoopStack.AppendElement(new SyncLoopInfo(workerEventTarget));
4155 : }
4156 :
4157 182 : return workerEventTarget.forget();
4158 : }
4159 :
4160 : bool
4161 91 : WorkerPrivate::RunCurrentSyncLoop()
4162 : {
4163 91 : AssertIsOnWorkerThread();
4164 :
4165 0 : JSContext* cx = GetJSContext();
4166 91 : MOZ_ASSERT(cx);
4167 :
4168 : // This should not change between now and the time we finish running this sync
4169 : // loop.
4170 182 : uint32_t currentLoopIndex = mSyncLoopStack.Length() - 1;
4171 :
4172 273 : SyncLoopInfo* loopInfo = mSyncLoopStack[currentLoopIndex];
4173 :
4174 0 : MOZ_ASSERT(loopInfo);
4175 0 : MOZ_ASSERT(!loopInfo->mHasRun);
4176 91 : MOZ_ASSERT(!loopInfo->mCompleted);
4177 :
4178 : #ifdef DEBUG
4179 91 : loopInfo->mHasRun = true;
4180 : #endif
4181 :
4182 0 : while (!loopInfo->mCompleted) {
4183 240 : bool normalRunnablesPending = false;
4184 :
4185 : // Don't block with the periodic GC timer running.
4186 0 : if (!NS_HasPendingEvents(mThread)) {
4187 146 : SetGCTimerMode(IdleTimer);
4188 : }
4189 :
4190 : // Wait for something to do.
4191 : {
4192 719 : MutexAutoLock lock(mMutex);
4193 :
4194 : for (;;) {
4195 0 : while (mControlQueue.IsEmpty() &&
4196 0 : !normalRunnablesPending &&
4197 0 : !(normalRunnablesPending = NS_HasPendingEvents(mThread))) {
4198 131 : WaitForWorkerEvents();
4199 : }
4200 :
4201 0 : auto result = ProcessAllControlRunnablesLocked();
4202 239 : if (result != ProcessAllControlRunnablesResult::Nothing) {
4203 : // XXXkhuey how should we handle Abort here? See Bug 1003730.
4204 :
4205 : // The state of the world may have changed. Recheck it.
4206 0 : normalRunnablesPending = NS_HasPendingEvents(mThread);
4207 :
4208 : // NB: If we processed a NotifyRunnable, we might have run
4209 : // non-control runnables, one of which may have shut down the
4210 : // sync loop.
4211 0 : if (loopInfo->mCompleted) {
4212 : break;
4213 : }
4214 : }
4215 :
4216 : // If we *didn't* run any control runnables, this should be unchanged.
4217 239 : MOZ_ASSERT(!loopInfo->mCompleted);
4218 :
4219 239 : if (normalRunnablesPending) {
4220 : break;
4221 : }
4222 : }
4223 : }
4224 :
4225 239 : if (normalRunnablesPending) {
4226 : // Make sure the periodic timer is running before we continue.
4227 239 : SetGCTimerMode(PeriodicTimer);
4228 :
4229 478 : MOZ_ALWAYS_TRUE(NS_ProcessNextEvent(mThread, false));
4230 :
4231 : // Now *might* be a good time to GC. Let the JS engine make the decision.
4232 0 : if (JS::CurrentGlobalOrNull(cx)) {
4233 234 : JS_MaybeGC(cx);
4234 : }
4235 : }
4236 : }
4237 :
4238 : // Make sure that the stack didn't change underneath us.
4239 261 : MOZ_ASSERT(mSyncLoopStack[currentLoopIndex] == loopInfo);
4240 :
4241 87 : return DestroySyncLoop(currentLoopIndex);
4242 : }
4243 :
4244 : bool
4245 87 : WorkerPrivate::DestroySyncLoop(uint32_t aLoopIndex)
4246 : {
4247 0 : MOZ_ASSERT(!mSyncLoopStack.IsEmpty());
4248 87 : MOZ_ASSERT(mSyncLoopStack.Length() - 1 == aLoopIndex);
4249 :
4250 : // We're about to delete the loop, stash its event target and result.
4251 261 : SyncLoopInfo* loopInfo = mSyncLoopStack[aLoopIndex];
4252 : nsIEventTarget* nestedEventTarget =
4253 0 : loopInfo->mEventTarget->GetWeakNestedEventTarget();
4254 87 : MOZ_ASSERT(nestedEventTarget);
4255 :
4256 87 : bool result = loopInfo->mResult;
4257 :
4258 : {
4259 : // Modifications must be protected by mMutex in DEBUG builds, see comment
4260 : // about mSyncLoopStack in WorkerPrivate.h.
4261 : #ifdef DEBUG
4262 261 : MutexAutoLock lock(mMutex);
4263 : #endif
4264 :
4265 : // This will delete |loopInfo|!
4266 87 : mSyncLoopStack.RemoveElementAt(aLoopIndex);
4267 : }
4268 :
4269 0 : auto queue = static_cast<ThreadEventQueue<EventQueue>*>(mThread->EventQueue());
4270 87 : queue->PopEventQueue(nestedEventTarget);
4271 :
4272 0 : if (mSyncLoopStack.IsEmpty() && mPendingEventQueueClearing) {
4273 0 : mPendingEventQueueClearing = false;
4274 0 : ClearMainEventQueue(WorkerRan);
4275 : }
4276 :
4277 87 : return result;
4278 : }
4279 :
4280 : void
4281 87 : WorkerPrivate::StopSyncLoop(nsIEventTarget* aSyncLoopTarget, bool aResult)
4282 : {
4283 0 : AssertIsOnWorkerThread();
4284 87 : AssertValidSyncLoop(aSyncLoopTarget);
4285 :
4286 174 : MOZ_ASSERT(!mSyncLoopStack.IsEmpty());
4287 :
4288 0 : for (uint32_t index = mSyncLoopStack.Length(); index > 0; index--) {
4289 0 : nsAutoPtr<SyncLoopInfo>& loopInfo = mSyncLoopStack[index - 1];
4290 0 : MOZ_ASSERT(loopInfo);
4291 174 : MOZ_ASSERT(loopInfo->mEventTarget);
4292 :
4293 174 : if (loopInfo->mEventTarget == aSyncLoopTarget) {
4294 : // Can't assert |loop->mHasRun| here because dispatch failures can cause
4295 : // us to bail out early.
4296 87 : MOZ_ASSERT(!loopInfo->mCompleted);
4297 :
4298 0 : loopInfo->mResult = aResult;
4299 87 : loopInfo->mCompleted = true;
4300 :
4301 87 : loopInfo->mEventTarget->Disable();
4302 :
4303 87 : return;
4304 : }
4305 :
4306 0 : MOZ_ASSERT(!SameCOMIdentity(loopInfo->mEventTarget, aSyncLoopTarget));
4307 : }
4308 :
4309 0 : MOZ_CRASH("Unknown sync loop!");
4310 : }
4311 :
4312 : #ifdef DEBUG
4313 : void
4314 381 : WorkerPrivate::AssertValidSyncLoop(nsIEventTarget* aSyncLoopTarget)
4315 : {
4316 381 : MOZ_ASSERT(aSyncLoopTarget);
4317 :
4318 : EventTarget* workerTarget;
4319 : nsresult rv =
4320 0 : aSyncLoopTarget->QueryInterface(kDEBUGWorkerEventTargetIID,
4321 0 : reinterpret_cast<void**>(&workerTarget));
4322 0 : MOZ_ASSERT(NS_SUCCEEDED(rv));
4323 381 : MOZ_ASSERT(workerTarget);
4324 :
4325 381 : bool valid = false;
4326 :
4327 : {
4328 1143 : MutexAutoLock lock(mMutex);
4329 :
4330 0 : for (uint32_t index = 0; index < mSyncLoopStack.Length(); index++) {
4331 0 : nsAutoPtr<SyncLoopInfo>& loopInfo = mSyncLoopStack[index];
4332 0 : MOZ_ASSERT(loopInfo);
4333 2856 : MOZ_ASSERT(loopInfo->mEventTarget);
4334 :
4335 2856 : if (loopInfo->mEventTarget == aSyncLoopTarget) {
4336 : valid = true;
4337 : break;
4338 : }
4339 :
4340 2094 : MOZ_ASSERT(!SameCOMIdentity(loopInfo->mEventTarget, aSyncLoopTarget));
4341 : }
4342 : }
4343 :
4344 0 : MOZ_ASSERT(valid);
4345 381 : }
4346 : #endif
4347 :
4348 : void
4349 51 : WorkerPrivate::PostMessageToParent(
4350 : JSContext* aCx,
4351 : JS::Handle<JS::Value> aMessage,
4352 : const Sequence<JSObject*>& aTransferable,
4353 : ErrorResult& aRv)
4354 : {
4355 51 : AssertIsOnWorkerThread();
4356 :
4357 102 : JS::Rooted<JS::Value> transferable(aCx, JS::UndefinedValue());
4358 :
4359 0 : aRv = nsContentUtils::CreateJSValueFromSequenceOfObject(aCx, aTransferable,
4360 0 : &transferable);
4361 0 : if (NS_WARN_IF(aRv.Failed())) {
4362 0 : return;
4363 : }
4364 :
4365 : RefPtr<MessageEventRunnable> runnable =
4366 : new MessageEventRunnable(this,
4367 102 : WorkerRunnable::ParentThreadUnchangedBusyCount);
4368 :
4369 0 : UniquePtr<AbstractTimelineMarker> start;
4370 0 : UniquePtr<AbstractTimelineMarker> end;
4371 0 : RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
4372 51 : bool isTimelineRecording = timelines && !timelines->IsEmpty();
4373 :
4374 1 : if (isTimelineRecording) {
4375 0 : start = MakeUnique<WorkerTimelineMarker>(NS_IsMainThread()
4376 0 : ? ProfileTimelineWorkerOperationType::SerializeDataOnMainThread
4377 : : ProfileTimelineWorkerOperationType::SerializeDataOffMainThread,
4378 0 : MarkerTracingType::START);
4379 : }
4380 :
4381 102 : runnable->Write(aCx, aMessage, transferable, JS::CloneDataPolicy(), aRv);
4382 :
4383 1 : if (isTimelineRecording) {
4384 0 : end = MakeUnique<WorkerTimelineMarker>(NS_IsMainThread()
4385 0 : ? ProfileTimelineWorkerOperationType::SerializeDataOnMainThread
4386 : : ProfileTimelineWorkerOperationType::SerializeDataOffMainThread,
4387 0 : MarkerTracingType::END);
4388 0 : timelines->AddMarkerForAllObservedDocShells(start);
4389 0 : timelines->AddMarkerForAllObservedDocShells(end);
4390 : }
4391 :
4392 0 : if (NS_WARN_IF(aRv.Failed())) {
4393 0 : return;
4394 : }
4395 :
4396 51 : if (!runnable->Dispatch()) {
4397 : aRv = NS_ERROR_FAILURE;
4398 : }
4399 : }
4400 :
4401 : void
4402 0 : WorkerPrivate::EnterDebuggerEventLoop()
4403 : {
4404 0 : AssertIsOnWorkerThread();
4405 :
4406 0 : JSContext* cx = GetJSContext();
4407 0 : MOZ_ASSERT(cx);
4408 0 : CycleCollectedJSContext* ccjscx = CycleCollectedJSContext::Get();
4409 :
4410 0 : uint32_t currentEventLoopLevel = ++mDebuggerEventLoopLevel;
4411 :
4412 0 : while (currentEventLoopLevel <= mDebuggerEventLoopLevel) {
4413 :
4414 0 : bool debuggerRunnablesPending = false;
4415 :
4416 : {
4417 0 : MutexAutoLock lock(mMutex);
4418 :
4419 0 : debuggerRunnablesPending = !mDebuggerQueue.IsEmpty();
4420 : }
4421 :
4422 : // Don't block with the periodic GC timer running.
4423 0 : if (!debuggerRunnablesPending) {
4424 0 : SetGCTimerMode(IdleTimer);
4425 : }
4426 :
4427 : // Wait for something to do
4428 : {
4429 0 : MutexAutoLock lock(mMutex);
4430 :
4431 : std::queue<RefPtr<MicroTaskRunnable>>& debuggerMtQueue =
4432 0 : ccjscx->GetDebuggerMicroTaskQueue();
4433 0 : while (mControlQueue.IsEmpty() &&
4434 0 : !(debuggerRunnablesPending = !mDebuggerQueue.IsEmpty()) &&
4435 0 : debuggerMtQueue.empty()) {
4436 0 : WaitForWorkerEvents();
4437 : }
4438 :
4439 0 : ProcessAllControlRunnablesLocked();
4440 :
4441 : // XXXkhuey should we abort JS on the stack here if we got Abort above?
4442 : }
4443 0 : ccjscx->PerformDebuggerMicroTaskCheckpoint();
4444 0 : if (debuggerRunnablesPending) {
4445 : // Start the periodic GC timer if it is not already running.
4446 0 : SetGCTimerMode(PeriodicTimer);
4447 :
4448 0 : WorkerRunnable* runnable = nullptr;
4449 :
4450 : {
4451 0 : MutexAutoLock lock(mMutex);
4452 :
4453 0 : mDebuggerQueue.Pop(runnable);
4454 : }
4455 :
4456 0 : MOZ_ASSERT(runnable);
4457 0 : static_cast<nsIRunnable*>(runnable)->Run();
4458 0 : runnable->Release();
4459 :
4460 0 : ccjscx->PerformDebuggerMicroTaskCheckpoint();
4461 :
4462 : // Now *might* be a good time to GC. Let the JS engine make the decision.
4463 0 : if (JS::CurrentGlobalOrNull(cx)) {
4464 0 : JS_MaybeGC(cx);
4465 : }
4466 : }
4467 : }
4468 0 : }
4469 :
4470 : void
4471 0 : WorkerPrivate::LeaveDebuggerEventLoop()
4472 : {
4473 0 : AssertIsOnWorkerThread();
4474 :
4475 0 : MutexAutoLock lock(mMutex);
4476 :
4477 0 : if (mDebuggerEventLoopLevel > 0) {
4478 0 : --mDebuggerEventLoopLevel;
4479 : }
4480 0 : }
4481 :
4482 : void
4483 0 : WorkerPrivate::PostMessageToDebugger(const nsAString& aMessage)
4484 : {
4485 0 : mDebugger->PostMessageToDebugger(aMessage);
4486 0 : }
4487 :
4488 : void
4489 0 : WorkerPrivate::SetDebuggerImmediate(dom::Function& aHandler, ErrorResult& aRv)
4490 : {
4491 0 : AssertIsOnWorkerThread();
4492 :
4493 : RefPtr<DebuggerImmediateRunnable> runnable =
4494 0 : new DebuggerImmediateRunnable(this, aHandler);
4495 0 : if (!runnable->Dispatch()) {
4496 0 : aRv.Throw(NS_ERROR_FAILURE);
4497 : }
4498 0 : }
4499 :
4500 : void
4501 0 : WorkerPrivate::ReportErrorToDebugger(const nsAString& aFilename,
4502 : uint32_t aLineno,
4503 : const nsAString& aMessage)
4504 : {
4505 0 : mDebugger->ReportErrorToDebugger(aFilename, aLineno, aMessage);
4506 0 : }
4507 :
4508 : bool
4509 0 : WorkerPrivate::NotifyInternal(WorkerStatus aStatus)
4510 : {
4511 0 : AssertIsOnWorkerThread();
4512 :
4513 0 : NS_ASSERTION(aStatus > Running && aStatus < Dead, "Bad status!");
4514 :
4515 0 : RefPtr<EventTarget> eventTarget;
4516 :
4517 : // Save the old status and set the new status.
4518 : WorkerStatus previousStatus;
4519 : {
4520 0 : MutexAutoLock lock(mMutex);
4521 :
4522 0 : if (mStatus >= aStatus) {
4523 0 : return true;
4524 : }
4525 :
4526 0 : if (aStatus >= Terminating) {
4527 0 : MutexAutoUnlock unlock(mMutex);
4528 0 : mClientSource.reset();
4529 0 : if (mScope) {
4530 0 : mScope->NoteTerminating();
4531 : }
4532 : }
4533 :
4534 : // Make sure the hybrid event target stops dispatching runnables
4535 : // once we reaching the killing state.
4536 0 : if (aStatus == Killing) {
4537 : // To avoid deadlock we always acquire the event target mutex before the
4538 : // worker private mutex. (We do it in this order because this is what
4539 : // workers best for event dispatching.) To enforce that order here we
4540 : // need to unlock the worker private mutex before we lock the event target
4541 : // mutex in ForgetWorkerPrivate.
4542 : {
4543 0 : MutexAutoUnlock unlock(mMutex);
4544 0 : mWorkerHybridEventTarget->ForgetWorkerPrivate(this);
4545 : }
4546 :
4547 : // Check the status code again in case another NotifyInternal came in
4548 : // while we were unlocked above.
4549 0 : if (mStatus >= aStatus) {
4550 : return true;
4551 : }
4552 : }
4553 :
4554 0 : previousStatus = mStatus;
4555 0 : mStatus = aStatus;
4556 :
4557 : // Mark parent status as closing immediately to avoid new events being
4558 : // dispatched after we clear the queue below.
4559 0 : if (aStatus == Closing) {
4560 0 : Close();
4561 : }
4562 : }
4563 :
4564 0 : MOZ_ASSERT(previousStatus != Pending);
4565 :
4566 0 : if (aStatus >= Closing) {
4567 0 : CancelAllTimeouts();
4568 : }
4569 :
4570 : // Let all our holders know the new status.
4571 0 : if (aStatus > Closing) {
4572 0 : NotifyHolders(aStatus);
4573 : }
4574 :
4575 : // If this is the first time our status has changed then we need to clear the
4576 : // main event queue.
4577 0 : if (previousStatus == Running) {
4578 : // NB: If we're in a sync loop, we can't clear the queue immediately,
4579 : // because this is the wrong queue. So we have to defer it until later.
4580 0 : if (!mSyncLoopStack.IsEmpty()) {
4581 0 : mPendingEventQueueClearing = true;
4582 : } else {
4583 0 : ClearMainEventQueue(WorkerRan);
4584 : }
4585 : }
4586 :
4587 : // If the worker script never ran, or failed to compile, we don't need to do
4588 : // anything else.
4589 0 : if (!GlobalScope()) {
4590 : return true;
4591 : }
4592 :
4593 : // Don't abort the script now, but we dispatch a runnable to do it when the
4594 : // current JS frame is executed.
4595 0 : if (aStatus == Closing) {
4596 0 : if (mSyncLoopStack.IsEmpty()) {
4597 : // Here we use a normal runnable to know when the current JS chunk of code
4598 : // is finished. We cannot use a WorkerRunnable because they are not
4599 : // accepted any more by the worker, and we do not want to use a
4600 : // WorkerControlRunnable because they are immediately executed.
4601 0 : RefPtr<CancelingRunnable> r = new CancelingRunnable();
4602 0 : mThread->nsThread::Dispatch(r.forget(), NS_DISPATCH_NORMAL);
4603 :
4604 : // At the same time, we want to be sure that we interrupt infinite loops.
4605 : // The following runnable starts a timer that cancel the worker, from the
4606 : // parent thread, after CANCELING_TIMEOUT millseconds.
4607 : RefPtr<CancelingWithTimeoutOnParentRunnable> rr =
4608 0 : new CancelingWithTimeoutOnParentRunnable(this);
4609 0 : rr->Dispatch();
4610 : }
4611 : return true;
4612 : }
4613 :
4614 0 : MOZ_ASSERT(aStatus == Terminating ||
4615 : aStatus == Canceling ||
4616 : aStatus == Killing);
4617 :
4618 : // Always abort the script.
4619 : return false;
4620 : }
4621 :
4622 : void
4623 0 : WorkerPrivate::ReportError(JSContext* aCx, JS::ConstUTF8CharsZ aToStringResult,
4624 : JSErrorReport* aReport)
4625 : {
4626 0 : AssertIsOnWorkerThread();
4627 :
4628 0 : if (!MayContinueRunning() || mErrorHandlerRecursionCount == 2) {
4629 0 : return;
4630 : }
4631 :
4632 0 : NS_ASSERTION(mErrorHandlerRecursionCount == 0 ||
4633 : mErrorHandlerRecursionCount == 1,
4634 : "Bad recursion logic!");
4635 :
4636 0 : JS::Rooted<JS::Value> exn(aCx);
4637 0 : if (!JS_GetPendingException(aCx, &exn)) {
4638 : // Probably shouldn't actually happen? But let's go ahead and just use null
4639 : // for lack of anything better.
4640 : exn.setNull();
4641 : }
4642 0 : JS_ClearPendingException(aCx);
4643 :
4644 0 : WorkerErrorReport report;
4645 0 : if (aReport) {
4646 0 : report.AssignErrorReport(aReport);
4647 : }
4648 : else {
4649 0 : report.mFlags = nsIScriptError::errorFlag | nsIScriptError::exceptionFlag;
4650 : }
4651 :
4652 0 : if (report.mMessage.IsEmpty() && aToStringResult) {
4653 0 : nsDependentCString toStringResult(aToStringResult.c_str());
4654 0 : if (!AppendUTF8toUTF16(toStringResult, report.mMessage, mozilla::fallible)) {
4655 : // Try again, with only a 1 KB string. Do this infallibly this time.
4656 : // If the user doesn't have 1 KB to spare we're done anyways.
4657 0 : uint32_t index = std::min(uint32_t(1024), toStringResult.Length());
4658 :
4659 : // Drop the last code point that may be cropped.
4660 0 : index = RewindToPriorUTF8Codepoint(toStringResult.BeginReading(), index);
4661 :
4662 : nsDependentCString truncatedToStringResult(aToStringResult.c_str(),
4663 0 : index);
4664 0 : AppendUTF8toUTF16(truncatedToStringResult, report.mMessage);
4665 : }
4666 : }
4667 :
4668 0 : mErrorHandlerRecursionCount++;
4669 :
4670 : // Don't want to run the scope's error handler if this is a recursive error or
4671 : // if we ran out of memory.
4672 0 : bool fireAtScope = mErrorHandlerRecursionCount == 1 &&
4673 0 : report.mErrorNumber != JSMSG_OUT_OF_MEMORY &&
4674 0 : JS::CurrentGlobalOrNull(aCx);
4675 :
4676 0 : WorkerErrorReport::ReportError(aCx, this, fireAtScope, nullptr, report, 0,
4677 0 : exn);
4678 :
4679 0 : mErrorHandlerRecursionCount--;
4680 : }
4681 :
4682 : // static
4683 : void
4684 0 : WorkerPrivate::ReportErrorToConsole(const char* aMessage)
4685 : {
4686 0 : WorkerPrivate* wp = nullptr;
4687 0 : if (!NS_IsMainThread()) {
4688 0 : wp = GetCurrentThreadWorkerPrivate();
4689 : }
4690 :
4691 0 : ReportErrorToConsoleRunnable::Report(wp, aMessage);
4692 0 : }
4693 :
4694 : int32_t
4695 0 : WorkerPrivate::SetTimeout(JSContext* aCx,
4696 : nsIScriptTimeoutHandler* aHandler,
4697 : int32_t aTimeout, bool aIsInterval,
4698 : ErrorResult& aRv)
4699 : {
4700 0 : AssertIsOnWorkerThread();
4701 0 : MOZ_ASSERT(aHandler);
4702 :
4703 0 : const int32_t timerId = mNextTimeoutId++;
4704 :
4705 : WorkerStatus currentStatus;
4706 : {
4707 0 : MutexAutoLock lock(mMutex);
4708 0 : currentStatus = mStatus;
4709 : }
4710 :
4711 : // If the worker is trying to call setTimeout/setInterval and the parent
4712 : // thread has initiated the close process then just silently fail.
4713 0 : if (currentStatus >= Closing) {
4714 : return timerId;
4715 : }
4716 :
4717 0 : nsAutoPtr<TimeoutInfo> newInfo(new TimeoutInfo());
4718 0 : newInfo->mIsInterval = aIsInterval;
4719 0 : newInfo->mId = timerId;
4720 :
4721 0 : if (MOZ_UNLIKELY(timerId == INT32_MAX)) {
4722 0 : NS_WARNING("Timeout ids overflowed!");
4723 0 : mNextTimeoutId = 1;
4724 : }
4725 :
4726 0 : newInfo->mHandler = aHandler;
4727 :
4728 : // See if any of the optional arguments were passed.
4729 0 : aTimeout = std::max(0, aTimeout);
4730 0 : newInfo->mInterval = TimeDuration::FromMilliseconds(aTimeout);
4731 :
4732 0 : newInfo->mTargetTime = TimeStamp::Now() + newInfo->mInterval;
4733 :
4734 : nsAutoPtr<TimeoutInfo>* insertedInfo =
4735 0 : mTimeouts.InsertElementSorted(newInfo.forget(), GetAutoPtrComparator(mTimeouts));
4736 :
4737 0 : LOG(TimeoutsLog(), ("Worker %p has new timeout: delay=%d interval=%s\n",
4738 : this, aTimeout, aIsInterval ? "yes" : "no"));
4739 :
4740 : // If the timeout we just made is set to fire next then we need to update the
4741 : // timer, unless we're currently running timeouts.
4742 0 : if (insertedInfo == mTimeouts.Elements() && !mRunningExpiredTimeouts) {
4743 0 : if (!mTimer) {
4744 0 : mTimer = NS_NewTimer();
4745 0 : if (!mTimer) {
4746 0 : aRv.Throw(NS_ERROR_UNEXPECTED);
4747 0 : return 0;
4748 : }
4749 :
4750 0 : mTimerRunnable = new TimerRunnable(this);
4751 : }
4752 :
4753 0 : if (!mTimerRunning) {
4754 0 : if (!ModifyBusyCountFromWorker(true)) {
4755 0 : aRv.Throw(NS_ERROR_FAILURE);
4756 0 : return 0;
4757 : }
4758 0 : mTimerRunning = true;
4759 : }
4760 :
4761 0 : if (!RescheduleTimeoutTimer(aCx)) {
4762 0 : aRv.Throw(NS_ERROR_FAILURE);
4763 0 : return 0;
4764 : }
4765 : }
4766 :
4767 : return timerId;
4768 : }
4769 :
4770 : void
4771 0 : WorkerPrivate::ClearTimeout(int32_t aId)
4772 : {
4773 0 : AssertIsOnWorkerThread();
4774 :
4775 0 : if (!mTimeouts.IsEmpty()) {
4776 0 : NS_ASSERTION(mTimerRunning, "Huh?!");
4777 :
4778 0 : for (uint32_t index = 0; index < mTimeouts.Length(); index++) {
4779 0 : nsAutoPtr<TimeoutInfo>& info = mTimeouts[index];
4780 0 : if (info->mId == aId) {
4781 0 : info->mCanceled = true;
4782 0 : break;
4783 : }
4784 : }
4785 : }
4786 0 : }
4787 :
4788 : bool
4789 0 : WorkerPrivate::RunExpiredTimeouts(JSContext* aCx)
4790 : {
4791 0 : AssertIsOnWorkerThread();
4792 :
4793 : // We may be called recursively (e.g. close() inside a timeout) or we could
4794 : // have been canceled while this event was pending, bail out if there is
4795 : // nothing to do.
4796 0 : if (mRunningExpiredTimeouts || !mTimerRunning) {
4797 : return true;
4798 : }
4799 :
4800 0 : NS_ASSERTION(mTimer && mTimerRunnable, "Must have a timer!");
4801 0 : NS_ASSERTION(!mTimeouts.IsEmpty(), "Should have some work to do!");
4802 :
4803 0 : bool retval = true;
4804 :
4805 0 : AutoPtrComparator<TimeoutInfo> comparator = GetAutoPtrComparator(mTimeouts);
4806 0 : JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx));
4807 :
4808 : // We want to make sure to run *something*, even if the timer fired a little
4809 : // early. Fudge the value of now to at least include the first timeout.
4810 0 : const TimeStamp actual_now = TimeStamp::Now();
4811 0 : const TimeStamp now = std::max(actual_now, mTimeouts[0]->mTargetTime);
4812 :
4813 0 : if (now != actual_now) {
4814 0 : LOG(TimeoutsLog(), ("Worker %p fudged timeout by %f ms.\n", this,
4815 : (now - actual_now).ToMilliseconds()));
4816 : }
4817 :
4818 0 : AutoTArray<TimeoutInfo*, 10> expiredTimeouts;
4819 0 : for (uint32_t index = 0; index < mTimeouts.Length(); index++) {
4820 0 : nsAutoPtr<TimeoutInfo>& info = mTimeouts[index];
4821 0 : if (info->mTargetTime > now) {
4822 : break;
4823 : }
4824 0 : expiredTimeouts.AppendElement(info);
4825 : }
4826 :
4827 : // Guard against recursion.
4828 0 : mRunningExpiredTimeouts = true;
4829 :
4830 : // Run expired timeouts.
4831 0 : for (uint32_t index = 0; index < expiredTimeouts.Length(); index++) {
4832 0 : TimeoutInfo*& info = expiredTimeouts[index];
4833 :
4834 0 : if (info->mCanceled) {
4835 0 : continue;
4836 : }
4837 :
4838 0 : LOG(TimeoutsLog(), ("Worker %p executing timeout with original delay %f ms.\n",
4839 : this, info->mInterval.ToMilliseconds()));
4840 :
4841 : // Always check JS_IsExceptionPending if something fails, and if
4842 : // JS_IsExceptionPending returns false (i.e. uncatchable exception) then
4843 : // break out of the loop.
4844 : const char *reason;
4845 0 : if (info->mIsInterval) {
4846 : reason = "setInterval handler";
4847 : } else {
4848 0 : reason = "setTimeout handler";
4849 : }
4850 :
4851 0 : RefPtr<Function> callback = info->mHandler->GetCallback();
4852 0 : if (!callback) {
4853 0 : nsAutoMicroTask mt;
4854 :
4855 0 : AutoEntryScript aes(global, reason, false);
4856 :
4857 : // Evaluate the timeout expression.
4858 0 : const nsAString& script = info->mHandler->GetHandlerText();
4859 :
4860 0 : const char* filename = nullptr;
4861 0 : uint32_t lineNo = 0, dummyColumn = 0;
4862 0 : info->mHandler->GetLocation(&filename, &lineNo, &dummyColumn);
4863 :
4864 0 : JS::CompileOptions options(aes.cx());
4865 0 : options.setFileAndLine(filename, lineNo).setNoScriptRval(true);
4866 :
4867 0 : JS::Rooted<JS::Value> unused(aes.cx());
4868 :
4869 0 : if (!JS::Evaluate(aes.cx(), options, script.BeginReading(),
4870 0 : script.Length(), &unused) &&
4871 0 : !JS_IsExceptionPending(aCx)) {
4872 0 : retval = false;
4873 0 : break;
4874 : }
4875 : } else {
4876 0 : ErrorResult rv;
4877 0 : JS::Rooted<JS::Value> ignoredVal(aCx);
4878 0 : callback->Call(GlobalScope(), info->mHandler->GetArgs(), &ignoredVal, rv,
4879 0 : reason);
4880 0 : if (rv.IsUncatchableException()) {
4881 0 : rv.SuppressException();
4882 0 : retval = false;
4883 0 : break;
4884 : }
4885 :
4886 0 : rv.SuppressException();
4887 : }
4888 :
4889 0 : NS_ASSERTION(mRunningExpiredTimeouts, "Someone changed this!");
4890 : }
4891 :
4892 : // No longer possible to be called recursively.
4893 0 : mRunningExpiredTimeouts = false;
4894 :
4895 : // Now remove canceled and expired timeouts from the main list.
4896 : // NB: The timeouts present in expiredTimeouts must have the same order
4897 : // with respect to each other in mTimeouts. That is, mTimeouts is just
4898 : // expiredTimeouts with extra elements inserted. There may be unexpired
4899 : // timeouts that have been inserted between the expired timeouts if the
4900 : // timeout event handler called setTimeout/setInterval.
4901 0 : for (uint32_t index = 0, expiredTimeoutIndex = 0,
4902 0 : expiredTimeoutLength = expiredTimeouts.Length();
4903 0 : index < mTimeouts.Length(); ) {
4904 0 : nsAutoPtr<TimeoutInfo>& info = mTimeouts[index];
4905 0 : if ((expiredTimeoutIndex < expiredTimeoutLength &&
4906 0 : info == expiredTimeouts[expiredTimeoutIndex] &&
4907 0 : ++expiredTimeoutIndex) ||
4908 0 : info->mCanceled) {
4909 0 : if (info->mIsInterval && !info->mCanceled) {
4910 : // Reschedule intervals.
4911 0 : info->mTargetTime = info->mTargetTime + info->mInterval;
4912 : // Don't resort the list here, we'll do that at the end.
4913 0 : ++index;
4914 : }
4915 : else {
4916 0 : mTimeouts.RemoveElement(info);
4917 : }
4918 : }
4919 : else {
4920 : // If info did not match the current entry in expiredTimeouts, it
4921 : // shouldn't be there at all.
4922 0 : NS_ASSERTION(!expiredTimeouts.Contains(info),
4923 : "Our timeouts are out of order!");
4924 0 : ++index;
4925 : }
4926 : }
4927 :
4928 0 : mTimeouts.Sort(comparator);
4929 :
4930 : // Either signal the parent that we're no longer using timeouts or reschedule
4931 : // the timer.
4932 0 : if (mTimeouts.IsEmpty()) {
4933 0 : if (!ModifyBusyCountFromWorker(false)) {
4934 0 : retval = false;
4935 : }
4936 0 : mTimerRunning = false;
4937 : }
4938 0 : else if (retval && !RescheduleTimeoutTimer(aCx)) {
4939 0 : retval = false;
4940 : }
4941 :
4942 0 : return retval;
4943 : }
4944 :
4945 : bool
4946 0 : WorkerPrivate::RescheduleTimeoutTimer(JSContext* aCx)
4947 : {
4948 0 : AssertIsOnWorkerThread();
4949 0 : MOZ_ASSERT(!mRunningExpiredTimeouts);
4950 0 : NS_ASSERTION(!mTimeouts.IsEmpty(), "Should have some timeouts!");
4951 0 : NS_ASSERTION(mTimer && mTimerRunnable, "Should have a timer!");
4952 :
4953 : // NB: This is important! The timer may have already fired, e.g. if a timeout
4954 : // callback itself calls setTimeout for a short duration and then takes longer
4955 : // than that to finish executing. If that has happened, it's very important
4956 : // that we don't execute the event that is now pending in our event queue, or
4957 : // our code in RunExpiredTimeouts to "fudge" the timeout value will unleash an
4958 : // early timeout when we execute the event we're about to queue.
4959 0 : mTimer->Cancel();
4960 :
4961 : double delta =
4962 0 : (mTimeouts[0]->mTargetTime - TimeStamp::Now()).ToMilliseconds();
4963 0 : uint32_t delay = delta > 0 ? std::min(delta, double(UINT32_MAX)) : 0;
4964 :
4965 0 : LOG(TimeoutsLog(), ("Worker %p scheduled timer for %d ms, %zu pending timeouts\n",
4966 : this, delay, mTimeouts.Length()));
4967 :
4968 0 : nsresult rv = mTimer->InitWithCallback(mTimerRunnable, delay, nsITimer::TYPE_ONE_SHOT);
4969 0 : if (NS_FAILED(rv)) {
4970 0 : JS_ReportErrorASCII(aCx, "Failed to start timer!");
4971 0 : return false;
4972 : }
4973 :
4974 : return true;
4975 : }
4976 :
4977 : void
4978 0 : WorkerPrivate::StartCancelingTimer()
4979 : {
4980 0 : AssertIsOnParentThread();
4981 :
4982 0 : auto errorCleanup = MakeScopeExit([&] {
4983 0 : mCancelingTimer = nullptr;
4984 0 : });
4985 :
4986 0 : MOZ_ASSERT(!mCancelingTimer);
4987 :
4988 0 : if (WorkerPrivate* parent = GetParent()) {
4989 0 : mCancelingTimer = NS_NewTimer(parent->ControlEventTarget());
4990 : } else {
4991 0 : mCancelingTimer = NS_NewTimer();
4992 : }
4993 :
4994 0 : if (NS_WARN_IF(!mCancelingTimer)) {
4995 0 : return;
4996 : }
4997 :
4998 : // This is not needed if we are already in an advanced shutdown state.
4999 : {
5000 0 : MutexAutoLock lock(mMutex);
5001 0 : if (ParentStatus() >= Terminating) {
5002 0 : return;
5003 : }
5004 : }
5005 :
5006 0 : uint32_t cancelingTimeoutMillis = DOMPrefs::WorkerCancelingTimeoutMillis();
5007 :
5008 0 : RefPtr<CancelingTimerCallback> callback = new CancelingTimerCallback(this);
5009 0 : nsresult rv = mCancelingTimer->InitWithCallback(callback,
5010 : cancelingTimeoutMillis,
5011 0 : nsITimer::TYPE_ONE_SHOT);
5012 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
5013 0 : return;
5014 : }
5015 :
5016 0 : errorCleanup.release();
5017 : }
5018 :
5019 : void
5020 0 : WorkerPrivate::UpdateContextOptionsInternal(
5021 : JSContext* aCx,
5022 : const JS::ContextOptions& aContextOptions)
5023 : {
5024 0 : AssertIsOnWorkerThread();
5025 :
5026 0 : JS::ContextOptionsRef(aCx) = aContextOptions;
5027 :
5028 0 : for (uint32_t index = 0; index < mChildWorkers.Length(); index++) {
5029 0 : mChildWorkers[index]->UpdateContextOptions(aContextOptions);
5030 : }
5031 0 : }
5032 :
5033 : void
5034 0 : WorkerPrivate::UpdateLanguagesInternal(const nsTArray<nsString>& aLanguages)
5035 : {
5036 0 : WorkerGlobalScope* globalScope = GlobalScope();
5037 0 : if (globalScope) {
5038 0 : RefPtr<WorkerNavigator> nav = globalScope->GetExistingNavigator();
5039 0 : if (nav) {
5040 0 : nav->SetLanguages(aLanguages);
5041 : }
5042 : }
5043 :
5044 0 : for (uint32_t index = 0; index < mChildWorkers.Length(); index++) {
5045 0 : mChildWorkers[index]->UpdateLanguages(aLanguages);
5046 : }
5047 0 : }
5048 :
5049 : void
5050 0 : WorkerPrivate::UpdateJSWorkerMemoryParameterInternal(JSContext* aCx,
5051 : JSGCParamKey aKey,
5052 : uint32_t aValue)
5053 : {
5054 0 : AssertIsOnWorkerThread();
5055 :
5056 : // XXX aValue might be 0 here (telling us to unset a previous value for child
5057 : // workers). Calling JS_SetGCParameter with a value of 0 isn't actually
5058 : // supported though. We really need some way to revert to a default value
5059 : // here.
5060 0 : if (aValue) {
5061 0 : JS_SetGCParameter(aCx, aKey, aValue);
5062 : }
5063 :
5064 0 : for (uint32_t index = 0; index < mChildWorkers.Length(); index++) {
5065 0 : mChildWorkers[index]->UpdateJSWorkerMemoryParameter(aKey, aValue);
5066 : }
5067 0 : }
5068 :
5069 : #ifdef JS_GC_ZEAL
5070 : void
5071 0 : WorkerPrivate::UpdateGCZealInternal(JSContext* aCx, uint8_t aGCZeal,
5072 : uint32_t aFrequency)
5073 : {
5074 0 : AssertIsOnWorkerThread();
5075 :
5076 0 : JS_SetGCZeal(aCx, aGCZeal, aFrequency);
5077 :
5078 0 : for (uint32_t index = 0; index < mChildWorkers.Length(); index++) {
5079 0 : mChildWorkers[index]->UpdateGCZeal(aGCZeal, aFrequency);
5080 : }
5081 0 : }
5082 : #endif
5083 :
5084 : void
5085 0 : WorkerPrivate::GarbageCollectInternal(JSContext* aCx, bool aShrinking,
5086 : bool aCollectChildren)
5087 : {
5088 0 : AssertIsOnWorkerThread();
5089 :
5090 0 : if (!GlobalScope()) {
5091 : // We haven't compiled anything yet. Just bail out.
5092 : return;
5093 : }
5094 :
5095 0 : if (aShrinking || aCollectChildren) {
5096 0 : JS::PrepareForFullGC(aCx);
5097 :
5098 0 : if (aShrinking) {
5099 0 : JS::NonIncrementalGC(aCx, GC_SHRINK, JS::gcreason::DOM_WORKER);
5100 :
5101 0 : if (!aCollectChildren) {
5102 0 : LOG(WorkerLog(), ("Worker %p collected idle garbage\n", this));
5103 : }
5104 : }
5105 : else {
5106 0 : JS::NonIncrementalGC(aCx, GC_NORMAL, JS::gcreason::DOM_WORKER);
5107 0 : LOG(WorkerLog(), ("Worker %p collected garbage\n", this));
5108 : }
5109 : }
5110 : else {
5111 0 : JS_MaybeGC(aCx);
5112 0 : LOG(WorkerLog(), ("Worker %p collected periodic garbage\n", this));
5113 : }
5114 :
5115 0 : if (aCollectChildren) {
5116 0 : for (uint32_t index = 0; index < mChildWorkers.Length(); index++) {
5117 0 : mChildWorkers[index]->GarbageCollect(aShrinking);
5118 : }
5119 : }
5120 : }
5121 :
5122 : void
5123 0 : WorkerPrivate::CycleCollectInternal(bool aCollectChildren)
5124 : {
5125 0 : AssertIsOnWorkerThread();
5126 :
5127 0 : nsCycleCollector_collect(nullptr);
5128 :
5129 0 : if (aCollectChildren) {
5130 0 : for (uint32_t index = 0; index < mChildWorkers.Length(); index++) {
5131 0 : mChildWorkers[index]->CycleCollect(/* dummy = */ false);
5132 : }
5133 : }
5134 0 : }
5135 :
5136 : void
5137 0 : WorkerPrivate::MemoryPressureInternal()
5138 : {
5139 0 : AssertIsOnWorkerThread();
5140 :
5141 0 : if (mScope) {
5142 0 : RefPtr<Console> console = mScope->GetConsoleIfExists();
5143 0 : if (console) {
5144 0 : console->ClearStorage();
5145 : }
5146 :
5147 0 : RefPtr<Performance> performance = mScope->GetPerformanceIfExists();
5148 0 : if (performance) {
5149 0 : performance->MemoryPressure();
5150 : }
5151 : }
5152 :
5153 0 : if (mDebuggerScope) {
5154 0 : RefPtr<Console> console = mDebuggerScope->GetConsoleIfExists();
5155 0 : if (console) {
5156 0 : console->ClearStorage();
5157 : }
5158 : }
5159 :
5160 0 : for (uint32_t index = 0; index < mChildWorkers.Length(); index++) {
5161 0 : mChildWorkers[index]->MemoryPressure(false);
5162 : }
5163 0 : }
5164 :
5165 : void
5166 3 : WorkerPrivate::SetThread(WorkerThread* aThread)
5167 : {
5168 3 : if (aThread) {
5169 : #ifdef DEBUG
5170 : {
5171 : bool isOnCurrentThread;
5172 0 : MOZ_ASSERT(NS_SUCCEEDED(aThread->IsOnCurrentThread(&isOnCurrentThread)));
5173 3 : MOZ_ASSERT(isOnCurrentThread);
5174 : }
5175 : #endif
5176 :
5177 0 : MOZ_ASSERT(!mPRThread);
5178 0 : mPRThread = PRThreadFromThread(aThread);
5179 3 : MOZ_ASSERT(mPRThread);
5180 : }
5181 : else {
5182 0 : MOZ_ASSERT(mPRThread);
5183 : }
5184 :
5185 6 : const WorkerThreadFriendKey friendKey;
5186 :
5187 6 : RefPtr<WorkerThread> doomedThread;
5188 :
5189 : { // Scope so that |doomedThread| is released without holding the lock.
5190 9 : MutexAutoLock lock(mMutex);
5191 :
5192 0 : if (aThread) {
5193 0 : MOZ_ASSERT(!mThread);
5194 3 : MOZ_ASSERT(mStatus == Pending);
5195 :
5196 0 : mThread = aThread;
5197 3 : mThread->SetWorker(friendKey, this);
5198 :
5199 0 : if (!mPreStartRunnables.IsEmpty()) {
5200 0 : for (uint32_t index = 0; index < mPreStartRunnables.Length(); index++) {
5201 18 : MOZ_ALWAYS_SUCCEEDS(
5202 : mThread->DispatchAnyThread(friendKey, mPreStartRunnables[index].forget()));
5203 : }
5204 3 : mPreStartRunnables.Clear();
5205 : }
5206 : }
5207 : else {
5208 0 : MOZ_ASSERT(mThread);
5209 :
5210 0 : mThread->SetWorker(friendKey, nullptr);
5211 :
5212 0 : mThread.swap(doomedThread);
5213 : }
5214 : }
5215 3 : }
5216 :
5217 : void
5218 58 : WorkerPrivate::BeginCTypesCall()
5219 : {
5220 58 : AssertIsOnWorkerThread();
5221 :
5222 : // Don't try to GC while we're blocked in a ctypes call.
5223 0 : SetGCTimerMode(NoTimer);
5224 58 : }
5225 :
5226 : void
5227 58 : WorkerPrivate::EndCTypesCall()
5228 : {
5229 58 : AssertIsOnWorkerThread();
5230 :
5231 : // Make sure the periodic timer is running before we start running JS again.
5232 0 : SetGCTimerMode(PeriodicTimer);
5233 58 : }
5234 :
5235 : bool
5236 0 : WorkerPrivate::ConnectMessagePort(JSContext* aCx,
5237 : MessagePortIdentifier& aIdentifier)
5238 : {
5239 0 : AssertIsOnWorkerThread();
5240 :
5241 0 : WorkerGlobalScope* globalScope = GlobalScope();
5242 :
5243 0 : JS::Rooted<JSObject*> jsGlobal(aCx, globalScope->GetWrapper());
5244 0 : MOZ_ASSERT(jsGlobal);
5245 :
5246 : // This MessagePortIdentifier is used to create a new port, still connected
5247 : // with the other one, but in the worker thread.
5248 0 : ErrorResult rv;
5249 0 : RefPtr<MessagePort> port = MessagePort::Create(globalScope, aIdentifier, rv);
5250 0 : if (NS_WARN_IF(rv.Failed())) {
5251 0 : rv.SuppressException();
5252 0 : return false;
5253 : }
5254 :
5255 0 : GlobalObject globalObject(aCx, jsGlobal);
5256 0 : if (globalObject.Failed()) {
5257 : return false;
5258 : }
5259 :
5260 0 : RootedDictionary<MessageEventInit> init(aCx);
5261 0 : init.mBubbles = false;
5262 0 : init.mCancelable = false;
5263 0 : init.mSource.SetValue().SetAsMessagePort() = port;
5264 0 : if (!init.mPorts.AppendElement(port.forget(), fallible)) {
5265 : return false;
5266 : }
5267 :
5268 : RefPtr<MessageEvent> event =
5269 0 : MessageEvent::Constructor(globalObject,
5270 0 : NS_LITERAL_STRING("connect"), init, rv);
5271 :
5272 0 : event->SetTrusted(true);
5273 :
5274 0 : globalScope->DispatchEvent(*event);
5275 :
5276 : return true;
5277 : }
5278 :
5279 : WorkerGlobalScope*
5280 3 : WorkerPrivate::GetOrCreateGlobalScope(JSContext* aCx)
5281 : {
5282 3 : AssertIsOnWorkerThread();
5283 :
5284 0 : if (!mScope) {
5285 0 : RefPtr<WorkerGlobalScope> globalScope;
5286 0 : if (IsSharedWorker()) {
5287 0 : globalScope = new SharedWorkerGlobalScope(this, WorkerName());
5288 3 : } else if (IsServiceWorker()) {
5289 : globalScope =
5290 : new ServiceWorkerGlobalScope(this,
5291 0 : GetServiceWorkerRegistrationDescriptor());
5292 : } else {
5293 6 : globalScope = new DedicatedWorkerGlobalScope(this, WorkerName());
5294 : }
5295 :
5296 0 : JS::Rooted<JSObject*> global(aCx);
5297 6 : NS_ENSURE_TRUE(globalScope->WrapGlobalObject(aCx, &global), nullptr);
5298 :
5299 9 : JSAutoRealm ar(aCx, global);
5300 :
5301 : // RegisterBindings() can spin a nested event loop so we have to set mScope
5302 : // before calling it, and we have to make sure to unset mScope if it fails.
5303 3 : mScope = std::move(globalScope);
5304 :
5305 1 : if (!RegisterBindings(aCx, global)) {
5306 0 : mScope = nullptr;
5307 0 : return nullptr;
5308 : }
5309 :
5310 3 : JS_FireOnNewGlobalObject(aCx, global);
5311 : }
5312 :
5313 6 : return mScope;
5314 : }
5315 :
5316 : WorkerDebuggerGlobalScope*
5317 0 : WorkerPrivate::CreateDebuggerGlobalScope(JSContext* aCx)
5318 : {
5319 0 : AssertIsOnWorkerThread();
5320 :
5321 0 : MOZ_ASSERT(!mDebuggerScope);
5322 :
5323 : RefPtr<WorkerDebuggerGlobalScope> globalScope =
5324 0 : new WorkerDebuggerGlobalScope(this);
5325 :
5326 0 : JS::Rooted<JSObject*> global(aCx);
5327 0 : NS_ENSURE_TRUE(globalScope->WrapGlobalObject(aCx, &global), nullptr);
5328 :
5329 0 : JSAutoRealm ar(aCx, global);
5330 :
5331 : // RegisterDebuggerBindings() can spin a nested event loop so we have to set
5332 : // mDebuggerScope before calling it, and we have to make sure to unset
5333 : // mDebuggerScope if it fails.
5334 0 : mDebuggerScope = std::move(globalScope);
5335 :
5336 0 : if (!RegisterDebuggerBindings(aCx, global)) {
5337 0 : mDebuggerScope = nullptr;
5338 0 : return nullptr;
5339 : }
5340 :
5341 0 : JS_FireOnNewGlobalObject(aCx, global);
5342 :
5343 0 : return mDebuggerScope;
5344 : }
5345 :
5346 : bool
5347 5406 : WorkerPrivate::IsOnWorkerThread() const
5348 : {
5349 : // This is much more complicated than it needs to be but we can't use mThread
5350 : // because it must be protected by mMutex and sometimes this method is called
5351 : // when mMutex is already locked. This method should always work.
5352 5406 : MOZ_ASSERT(mPRThread,
5353 : "AssertIsOnWorkerThread() called before a thread was assigned!");
5354 :
5355 10812 : nsCOMPtr<nsIThread> thread;
5356 : nsresult rv =
5357 0 : nsThreadManager::get().GetThreadFromPRThread(mPRThread,
5358 0 : getter_AddRefs(thread));
5359 0 : MOZ_ASSERT(NS_SUCCEEDED(rv));
5360 5406 : MOZ_ASSERT(thread);
5361 :
5362 : bool current;
5363 0 : rv = thread->IsOnCurrentThread(¤t);
5364 10812 : return NS_SUCCEEDED(rv) && current;
5365 : }
5366 :
5367 : #ifdef DEBUG
5368 : void
5369 5405 : WorkerPrivate::AssertIsOnWorkerThread() const
5370 : {
5371 0 : MOZ_ASSERT(IsOnWorkerThread());
5372 5406 : }
5373 : #endif // DEBUG
5374 :
5375 : void
5376 0 : WorkerPrivate::DumpCrashInformation(nsACString& aString)
5377 : {
5378 0 : AssertIsOnWorkerThread();
5379 :
5380 0 : nsTObserverArray<WorkerHolder*>::ForwardIterator iter(mHolders);
5381 0 : while (iter.HasMore()) {
5382 0 : WorkerHolder* holder = iter.GetNext();
5383 0 : aString.Append("|");
5384 0 : aString.Append(holder->Name());
5385 : }
5386 0 : }
5387 :
5388 : void
5389 0 : WorkerPrivate::EnsurePerformanceCounter()
5390 : {
5391 0 : AssertIsOnWorkerThread();
5392 0 : MOZ_ASSERT(mozilla::dom::DOMPrefs::SchedulerLoggingEnabled());
5393 0 : if (!mPerformanceCounter) {
5394 0 : mPerformanceCounter = new PerformanceCounter(NS_ConvertUTF16toUTF8(mWorkerName));
5395 : }
5396 0 : }
5397 :
5398 : PerformanceCounter*
5399 0 : WorkerPrivate::GetPerformanceCounter()
5400 : {
5401 0 : MOZ_ASSERT(mPerformanceCounter);
5402 0 : return mPerformanceCounter;
5403 : }
5404 :
5405 : PerformanceStorage*
5406 39 : WorkerPrivate::GetPerformanceStorage()
5407 : {
5408 0 : AssertIsOnMainThread();
5409 0 : MOZ_ASSERT(mPerformanceStorage);
5410 78 : return mPerformanceStorage;
5411 : }
5412 :
5413 0 : NS_IMPL_ADDREF(WorkerPrivate::EventTarget)
5414 6249 : NS_IMPL_RELEASE(WorkerPrivate::EventTarget)
5415 :
5416 0 : NS_INTERFACE_MAP_BEGIN(WorkerPrivate::EventTarget)
5417 0 : NS_INTERFACE_MAP_ENTRY(nsISerialEventTarget)
5418 0 : NS_INTERFACE_MAP_ENTRY(nsIEventTarget)
5419 2475 : NS_INTERFACE_MAP_ENTRY(nsISupports)
5420 : #ifdef DEBUG
5421 : // kDEBUGWorkerEventTargetIID is special in that it does not AddRef its
5422 : // result.
5423 0 : if (aIID.Equals(kDEBUGWorkerEventTargetIID)) {
5424 0 : *aInstancePtr = this;
5425 381 : return NS_OK;
5426 : }
5427 : else
5428 : #endif
5429 2552 : NS_INTERFACE_MAP_END
5430 :
5431 : NS_IMETHODIMP
5432 0 : WorkerPrivate::EventTarget::DispatchFromScript(nsIRunnable* aRunnable,
5433 : uint32_t aFlags)
5434 : {
5435 0 : nsCOMPtr<nsIRunnable> event(aRunnable);
5436 0 : return Dispatch(event.forget(), aFlags);
5437 : }
5438 :
5439 : NS_IMETHODIMP
5440 240 : WorkerPrivate::EventTarget::Dispatch(already_AddRefed<nsIRunnable> aRunnable,
5441 : uint32_t aFlags)
5442 : {
5443 : // May be called on any thread!
5444 480 : nsCOMPtr<nsIRunnable> event(aRunnable);
5445 :
5446 : // Workers only support asynchronous dispatch for now.
5447 240 : if (NS_WARN_IF(aFlags != NS_DISPATCH_NORMAL)) {
5448 : return NS_ERROR_UNEXPECTED;
5449 : }
5450 :
5451 480 : RefPtr<WorkerRunnable> workerRunnable;
5452 :
5453 480 : MutexAutoLock lock(mMutex);
5454 :
5455 240 : if (!mWorkerPrivate) {
5456 : NS_WARNING("A runnable was posted to a worker that is already shutting "
5457 0 : "down!");
5458 0 : return NS_ERROR_UNEXPECTED;
5459 : }
5460 :
5461 0 : if (event) {
5462 240 : workerRunnable = mWorkerPrivate->MaybeWrapAsWorkerRunnable(event.forget());
5463 : }
5464 :
5465 : nsresult rv =
5466 0 : mWorkerPrivate->Dispatch(workerRunnable.forget(), mNestedEventTarget);
5467 240 : if (NS_WARN_IF(NS_FAILED(rv))) {
5468 : return rv;
5469 : }
5470 :
5471 240 : return NS_OK;
5472 : }
5473 :
5474 : NS_IMETHODIMP
5475 0 : WorkerPrivate::EventTarget::DelayedDispatch(already_AddRefed<nsIRunnable>,
5476 : uint32_t)
5477 :
5478 : {
5479 0 : return NS_ERROR_NOT_IMPLEMENTED;
5480 : }
5481 :
5482 : NS_IMETHODIMP
5483 0 : WorkerPrivate::EventTarget::IsOnCurrentThread(bool* aIsOnCurrentThread)
5484 : {
5485 : // May be called on any thread!
5486 :
5487 0 : MOZ_ASSERT(aIsOnCurrentThread);
5488 :
5489 0 : MutexAutoLock lock(mMutex);
5490 :
5491 0 : if (!mWorkerPrivate) {
5492 0 : NS_WARNING("A worker's event target was used after the worker has !");
5493 0 : return NS_ERROR_UNEXPECTED;
5494 : }
5495 :
5496 0 : *aIsOnCurrentThread = mWorkerPrivate->IsOnCurrentThread();
5497 0 : return NS_OK;
5498 : }
5499 :
5500 : NS_IMETHODIMP_(bool)
5501 0 : WorkerPrivate::EventTarget::IsOnCurrentThreadInfallible()
5502 : {
5503 : // May be called on any thread!
5504 :
5505 0 : MutexAutoLock lock(mMutex);
5506 :
5507 0 : if (!mWorkerPrivate) {
5508 0 : NS_WARNING("A worker's event target was used after the worker has !");
5509 0 : return false;
5510 : }
5511 :
5512 : return mWorkerPrivate->IsOnCurrentThread();
5513 : }
5514 :
5515 : } // dom namespace
5516 : } // mozilla namespace
|