Support container.getRegistration() / getRegistrations() inside service workers
[WebKit-https.git] / Source / WebCore / dom / ScriptExecutionContext.cpp
1 /*
2  * Copyright (C) 2008 Apple Inc. All Rights Reserved.
3  * Copyright (C) 2012 Google Inc. All Rights Reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  *
26  */
27
28 #include "config.h"
29 #include "ScriptExecutionContext.h"
30
31 #include "CachedScript.h"
32 #include "CommonVM.h"
33 #include "DOMTimer.h"
34 #include "DOMWindow.h"
35 #include "DatabaseContext.h"
36 #include "Document.h"
37 #include "ErrorEvent.h"
38 #include "JSDOMExceptionHandling.h"
39 #include "JSDOMWindow.h"
40 #include "MessagePort.h"
41 #include "Navigator.h"
42 #include "NoEventDispatchAssertion.h"
43 #include "PublicURLManager.h"
44 #include "RejectedPromiseTracker.h"
45 #include "ResourceRequest.h"
46 #include "SWClientConnection.h"
47 #include "SWContextManager.h"
48 #include "ScriptState.h"
49 #include "ServiceWorker.h"
50 #include "ServiceWorkerGlobalScope.h"
51 #include "ServiceWorkerProvider.h"
52 #include "Settings.h"
53 #include "WorkerGlobalScope.h"
54 #include "WorkerNavigator.h"
55 #include "WorkerThread.h"
56 #include <heap/StrongInlines.h>
57 #include <inspector/ScriptCallStack.h>
58 #include <runtime/CatchScope.h>
59 #include <runtime/Exception.h>
60 #include <runtime/JSPromise.h>
61 #include <wtf/MainThread.h>
62 #include <wtf/Ref.h>
63
64
65 namespace WebCore {
66 using namespace Inspector;
67
68 struct ScriptExecutionContext::PendingException {
69     WTF_MAKE_FAST_ALLOCATED;
70 public:
71     PendingException(const String& errorMessage, int lineNumber, int columnNumber, const String& sourceURL, RefPtr<ScriptCallStack>&& callStack)
72         : m_errorMessage(errorMessage)
73         , m_lineNumber(lineNumber)
74         , m_columnNumber(columnNumber)
75         , m_sourceURL(sourceURL)
76         , m_callStack(WTFMove(callStack))
77     {
78     }
79     String m_errorMessage;
80     int m_lineNumber;
81     int m_columnNumber;
82     String m_sourceURL;
83     RefPtr<ScriptCallStack> m_callStack;
84 };
85
86 ScriptExecutionContext::ScriptExecutionContext()
87 {
88 }
89
90 #if ASSERT_DISABLED
91
92 inline void ScriptExecutionContext::checkConsistency() const
93 {
94 }
95
96 #else
97
98 void ScriptExecutionContext::checkConsistency() const
99 {
100     for (auto* messagePort : m_messagePorts)
101         ASSERT(messagePort->scriptExecutionContext() == this);
102
103     for (auto* destructionObserver : m_destructionObservers)
104         ASSERT(destructionObserver->scriptExecutionContext() == this);
105
106     for (auto* activeDOMObject : m_activeDOMObjects) {
107         ASSERT(activeDOMObject->scriptExecutionContext() == this);
108         activeDOMObject->assertSuspendIfNeededWasCalled();
109     }
110 }
111
112 #endif
113
114 ScriptExecutionContext::~ScriptExecutionContext()
115 {
116     checkConsistency();
117
118 #if !ASSERT_DISABLED
119     m_inScriptExecutionContextDestructor = true;
120 #endif
121
122 #if ENABLE(SERVICE_WORKER)
123     setActiveServiceWorker(nullptr);
124 #endif
125
126     while (auto* destructionObserver = m_destructionObservers.takeAny())
127         destructionObserver->contextDestroyed();
128
129     for (auto* messagePort : m_messagePorts)
130         messagePort->contextDestroyed();
131
132 #if !ASSERT_DISABLED
133     m_inScriptExecutionContextDestructor = false;
134 #endif
135 }
136
137 void ScriptExecutionContext::processMessagePortMessagesSoon()
138 {
139     if (m_willProcessMessagePortMessagesSoon)
140         return;
141
142     m_willProcessMessagePortMessagesSoon = true;
143     postTask([] (ScriptExecutionContext& context) {
144         context.dispatchMessagePortEvents();
145     });
146 }
147
148 void ScriptExecutionContext::dispatchMessagePortEvents()
149 {
150     checkConsistency();
151
152     Ref<ScriptExecutionContext> protectedThis(*this);
153     ASSERT(m_willProcessMessagePortMessagesSoon);
154     m_willProcessMessagePortMessagesSoon = false;
155
156     // Make a frozen copy of the ports so we can iterate while new ones might be added or destroyed.
157     for (auto* messagePort : copyToVector(m_messagePorts)) {
158         // The port may be destroyed, and another one created at the same address,
159         // but this is harmless. The worst that can happen as a result is that
160         // dispatchMessages() will be called needlessly.
161         if (m_messagePorts.contains(messagePort) && messagePort->started())
162             messagePort->dispatchMessages();
163     }
164 }
165
166 void ScriptExecutionContext::createdMessagePort(MessagePort& messagePort)
167 {
168     ASSERT((is<Document>(*this) && isMainThread())
169         || (is<WorkerGlobalScope>(*this) && downcast<WorkerGlobalScope>(*this).thread().thread() == &Thread::current()));
170
171     m_messagePorts.add(&messagePort);
172 }
173
174 void ScriptExecutionContext::destroyedMessagePort(MessagePort& messagePort)
175 {
176     ASSERT((is<Document>(*this) && isMainThread())
177         || (is<WorkerGlobalScope>(*this) && downcast<WorkerGlobalScope>(*this).thread().thread() == &Thread::current()));
178
179     m_messagePorts.remove(&messagePort);
180 }
181
182 void ScriptExecutionContext::didLoadResourceSynchronously()
183 {
184 }
185
186 bool ScriptExecutionContext::canSuspendActiveDOMObjectsForDocumentSuspension(Vector<ActiveDOMObject*>* unsuspendableObjects)
187 {
188     checkConsistency();
189
190     bool canSuspend = true;
191
192     m_activeDOMObjectAdditionForbidden = true;
193 #if !ASSERT_DISABLED || ENABLE(SECURITY_ASSERTIONS)
194     m_activeDOMObjectRemovalForbidden = true;
195 #endif
196
197     // We assume that m_activeDOMObjects will not change during iteration: canSuspend
198     // functions should not add new active DOM objects, nor execute arbitrary JavaScript.
199     // An ASSERT_WITH_SECURITY_IMPLICATION or RELEASE_ASSERT will fire if this happens, but it's important to code
200     // canSuspend functions so it will not happen!
201     NoEventDispatchAssertion::InMainThread assertNoEventDispatch;
202     for (auto* activeDOMObject : m_activeDOMObjects) {
203         if (!activeDOMObject->canSuspendForDocumentSuspension()) {
204             canSuspend = false;
205             if (unsuspendableObjects)
206                 unsuspendableObjects->append(activeDOMObject);
207             else
208                 break;
209         }
210     }
211
212     m_activeDOMObjectAdditionForbidden = false;
213 #if !ASSERT_DISABLED || ENABLE(SECURITY_ASSERTIONS)
214     m_activeDOMObjectRemovalForbidden = false;
215 #endif
216
217     return canSuspend;
218 }
219
220 void ScriptExecutionContext::suspendActiveDOMObjects(ActiveDOMObject::ReasonForSuspension why)
221 {
222     checkConsistency();
223
224     if (m_activeDOMObjectsAreSuspended) {
225         // A page may subsequently suspend DOM objects, say as part of entering the page cache, after the embedding
226         // client requested the page be suspended. We ignore such requests so long as the embedding client requested
227         // the suspension first. See <rdar://problem/13754896> for more details.
228         ASSERT(m_reasonForSuspendingActiveDOMObjects == ActiveDOMObject::PageWillBeSuspended);
229         return;
230     }
231
232     m_activeDOMObjectsAreSuspended = true;
233
234     m_activeDOMObjectAdditionForbidden = true;
235 #if !ASSERT_DISABLED || ENABLE(SECURITY_ASSERTIONS)
236     m_activeDOMObjectRemovalForbidden = true;
237 #endif
238
239     // We assume that m_activeDOMObjects will not change during iteration: suspend
240     // functions should not add new active DOM objects, nor execute arbitrary JavaScript.
241     // An ASSERT_WITH_SECURITY_IMPLICATION or RELEASE_ASSERT will fire if this happens, but it's important to code
242     // suspend functions so it will not happen!
243     NoEventDispatchAssertion::InMainThread assertNoEventDispatch;
244     for (auto* activeDOMObject : m_activeDOMObjects)
245         activeDOMObject->suspend(why);
246
247     m_activeDOMObjectAdditionForbidden = false;
248 #if !ASSERT_DISABLED || ENABLE(SECURITY_ASSERTIONS)
249     m_activeDOMObjectRemovalForbidden = false;
250 #endif
251
252     m_reasonForSuspendingActiveDOMObjects = why;
253 }
254
255 void ScriptExecutionContext::resumeActiveDOMObjects(ActiveDOMObject::ReasonForSuspension why)
256 {
257     checkConsistency();
258
259     if (m_reasonForSuspendingActiveDOMObjects != why)
260         return;
261     m_activeDOMObjectsAreSuspended = false;
262
263     m_activeDOMObjectAdditionForbidden = true;
264 #if !ASSERT_DISABLED || ENABLE(SECURITY_ASSERTIONS)
265     m_activeDOMObjectRemovalForbidden = true;
266 #endif
267
268     // We assume that m_activeDOMObjects will not change during iteration: resume
269     // functions should not add new active DOM objects, nor execute arbitrary JavaScript.
270     // An ASSERT_WITH_SECURITY_IMPLICATION or RELEASE_ASSERT will fire if this happens, but it's important to code
271     // resume functions so it will not happen!
272     NoEventDispatchAssertion::InMainThread assertNoEventDispatch;
273     for (auto* activeDOMObject : m_activeDOMObjects)
274         activeDOMObject->resume();
275
276     m_activeDOMObjectAdditionForbidden = false;
277 #if !ASSERT_DISABLED || ENABLE(SECURITY_ASSERTIONS)
278     m_activeDOMObjectRemovalForbidden = false;
279 #endif
280 }
281
282 void ScriptExecutionContext::stopActiveDOMObjects()
283 {
284     checkConsistency();
285
286     if (m_activeDOMObjectsAreStopped)
287         return;
288     m_activeDOMObjectsAreStopped = true;
289
290     // Make a frozen copy of the objects so we can iterate while new ones might be destroyed.
291     auto possibleActiveDOMObjects = copyToVector(m_activeDOMObjects);
292
293     m_activeDOMObjectAdditionForbidden = true;
294
295     // We assume that new objects will not be added to m_activeDOMObjects during iteration:
296     // stop functions should not add new active DOM objects, nor execute arbitrary JavaScript.
297     // An ASSERT_WITH_SECURITY_IMPLICATION or RELEASE_ASSERT will fire if this happens, but it's important to code stop functions
298     // so it will not happen!
299     NoEventDispatchAssertion assertNoEventDispatch;
300     for (auto* activeDOMObject : possibleActiveDOMObjects) {
301         // Check if this object was deleted already. If so, just skip it.
302         // Calling contains on a possibly-already-deleted object is OK because we guarantee
303         // no new object can be added, so even if a new object ends up allocated with the
304         // same address, that will be *after* this function exits.
305         if (!m_activeDOMObjects.contains(activeDOMObject))
306             continue;
307         activeDOMObject->stop();
308     }
309
310     m_activeDOMObjectAdditionForbidden = false;
311
312     // FIXME: Make message ports be active DOM objects and let them implement stop instead
313     // of having this separate mechanism just for them.
314     for (auto* messagePort : m_messagePorts)
315         messagePort->close();
316 }
317
318 void ScriptExecutionContext::suspendActiveDOMObjectIfNeeded(ActiveDOMObject& activeDOMObject)
319 {
320     ASSERT(m_activeDOMObjects.contains(&activeDOMObject));
321     if (m_activeDOMObjectsAreSuspended)
322         activeDOMObject.suspend(m_reasonForSuspendingActiveDOMObjects);
323     if (m_activeDOMObjectsAreStopped)
324         activeDOMObject.stop();
325 }
326
327 void ScriptExecutionContext::didCreateActiveDOMObject(ActiveDOMObject& activeDOMObject)
328 {
329     // The m_activeDOMObjectAdditionForbidden check is a RELEASE_ASSERT because of the
330     // consequences of having an ActiveDOMObject that is not correctly reflected in the set.
331     // If we do have one of those, it can possibly be a security vulnerability. So we'd
332     // rather have a crash than continue running with the set possibly compromised.
333     ASSERT(!m_inScriptExecutionContextDestructor);
334     RELEASE_ASSERT(!m_activeDOMObjectAdditionForbidden);
335     m_activeDOMObjects.add(&activeDOMObject);
336 }
337
338 void ScriptExecutionContext::willDestroyActiveDOMObject(ActiveDOMObject& activeDOMObject)
339 {
340     ASSERT_WITH_SECURITY_IMPLICATION(!m_activeDOMObjectRemovalForbidden);
341     m_activeDOMObjects.remove(&activeDOMObject);
342 }
343
344 void ScriptExecutionContext::didCreateDestructionObserver(ContextDestructionObserver& observer)
345 {
346     ASSERT(!m_inScriptExecutionContextDestructor);
347     m_destructionObservers.add(&observer);
348 }
349
350 void ScriptExecutionContext::willDestroyDestructionObserver(ContextDestructionObserver& observer)
351 {
352     m_destructionObservers.remove(&observer);
353 }
354
355 bool ScriptExecutionContext::sanitizeScriptError(String& errorMessage, int& lineNumber, int& columnNumber, String& sourceURL, JSC::Strong<JSC::Unknown>& error, CachedScript* cachedScript)
356 {
357     ASSERT(securityOrigin());
358     if (cachedScript) {
359         ASSERT(cachedScript->origin());
360         ASSERT(securityOrigin()->toString() == cachedScript->origin()->toString());
361         if (cachedScript->isCORSSameOrigin())
362             return false;
363     } else if (securityOrigin()->canRequest(completeURL(sourceURL)))
364         return false;
365
366     errorMessage = ASCIILiteral { "Script error." };
367     sourceURL = { };
368     lineNumber = 0;
369     columnNumber = 0;
370     error = { };
371     return true;
372 }
373
374 void ScriptExecutionContext::reportException(const String& errorMessage, int lineNumber, int columnNumber, const String& sourceURL, JSC::Exception* exception, RefPtr<ScriptCallStack>&& callStack, CachedScript* cachedScript)
375 {
376     if (m_inDispatchErrorEvent) {
377         if (!m_pendingExceptions)
378             m_pendingExceptions = std::make_unique<Vector<std::unique_ptr<PendingException>>>();
379         m_pendingExceptions->append(std::make_unique<PendingException>(errorMessage, lineNumber, columnNumber, sourceURL, WTFMove(callStack)));
380         return;
381     }
382
383     // First report the original exception and only then all the nested ones.
384     if (!dispatchErrorEvent(errorMessage, lineNumber, columnNumber, sourceURL, exception, cachedScript))
385         logExceptionToConsole(errorMessage, sourceURL, lineNumber, columnNumber, callStack.copyRef());
386
387     if (!m_pendingExceptions)
388         return;
389
390     auto pendingExceptions = WTFMove(m_pendingExceptions);
391     for (auto& exception : *pendingExceptions)
392         logExceptionToConsole(exception->m_errorMessage, exception->m_sourceURL, exception->m_lineNumber, exception->m_columnNumber, WTFMove(exception->m_callStack));
393 }
394
395 void ScriptExecutionContext::reportUnhandledPromiseRejection(JSC::ExecState& state, JSC::JSPromise& promise, RefPtr<Inspector::ScriptCallStack>&& callStack)
396 {
397     JSC::VM& vm = state.vm();
398     auto scope = DECLARE_CATCH_SCOPE(vm);
399
400     int lineNumber = 0;
401     int columnNumber = 0;
402     String sourceURL;
403
404     JSC::JSValue result = promise.result(vm);
405     String resultMessage = retrieveErrorMessage(state, vm, result, scope);
406     String errorMessage = makeString("Unhandled Promise Rejection: ", resultMessage);
407     if (callStack) {
408         if (const ScriptCallFrame* callFrame = callStack->firstNonNativeCallFrame()) {
409             lineNumber = callFrame->lineNumber();
410             columnNumber = callFrame->columnNumber();
411             sourceURL = callFrame->sourceURL();
412         }
413     }
414
415     logExceptionToConsole(errorMessage, sourceURL, lineNumber, columnNumber, WTFMove(callStack));
416 }
417
418 void ScriptExecutionContext::addConsoleMessage(MessageSource source, MessageLevel level, const String& message, const String& sourceURL, unsigned lineNumber, unsigned columnNumber, JSC::ExecState* state, unsigned long requestIdentifier)
419 {
420     addMessage(source, level, message, sourceURL, lineNumber, columnNumber, 0, state, requestIdentifier);
421 }
422
423 bool ScriptExecutionContext::dispatchErrorEvent(const String& errorMessage, int lineNumber, int columnNumber, const String& sourceURL, JSC::Exception* exception, CachedScript* cachedScript)
424 {
425     EventTarget* target = errorEventTarget();
426     if (!target)
427         return false;
428
429     String message = errorMessage;
430     int line = lineNumber;
431     int column = columnNumber;
432     String sourceName = sourceURL;
433     JSC::Strong<JSC::Unknown> error = exception && exception->value() ? JSC::Strong<JSC::Unknown>(vm(), exception->value()) : JSC::Strong<JSC::Unknown>();
434     sanitizeScriptError(message, line, column, sourceName, error, cachedScript);
435
436     ASSERT(!m_inDispatchErrorEvent);
437     m_inDispatchErrorEvent = true;
438     Ref<ErrorEvent> errorEvent = ErrorEvent::create(message, sourceName, line, column, error);
439     target->dispatchEvent(errorEvent);
440     m_inDispatchErrorEvent = false;
441     return errorEvent->defaultPrevented();
442 }
443
444 int ScriptExecutionContext::circularSequentialID()
445 {
446     ++m_circularSequentialID;
447     if (m_circularSequentialID <= 0)
448         m_circularSequentialID = 1;
449     return m_circularSequentialID;
450 }
451
452 PublicURLManager& ScriptExecutionContext::publicURLManager()
453 {
454     if (!m_publicURLManager)
455         m_publicURLManager = PublicURLManager::create(this);
456     return *m_publicURLManager;
457 }
458
459 void ScriptExecutionContext::adjustMinimumDOMTimerInterval(Seconds oldMinimumTimerInterval)
460 {
461     if (minimumDOMTimerInterval() != oldMinimumTimerInterval) {
462         for (auto& timer : m_timeouts.values())
463             timer->updateTimerIntervalIfNecessary();
464     }
465 }
466
467 Seconds ScriptExecutionContext::minimumDOMTimerInterval() const
468 {
469     // The default implementation returns the DOMTimer's default
470     // minimum timer interval. FIXME: to make it work with dedicated
471     // workers, we will have to override it in the appropriate
472     // subclass, and provide a way to enumerate a Document's dedicated
473     // workers so we can update them all.
474     return DOMTimer::defaultMinimumInterval();
475 }
476
477 void ScriptExecutionContext::didChangeTimerAlignmentInterval()
478 {
479     for (auto& timer : m_timeouts.values())
480         timer->didChangeAlignmentInterval();
481 }
482
483 Seconds ScriptExecutionContext::domTimerAlignmentInterval(bool) const
484 {
485     return DOMTimer::defaultAlignmentInterval();
486 }
487
488 JSC::VM& ScriptExecutionContext::vm()
489 {
490     if (is<Document>(*this))
491         return commonVM();
492
493     return downcast<WorkerGlobalScope>(*this).script()->vm();
494 }
495
496 RejectedPromiseTracker& ScriptExecutionContext::ensureRejectedPromiseTrackerSlow()
497 {
498     // ScriptExecutionContext::vm() in Worker is only available after WorkerGlobalScope initialization is done.
499     // When initializing ScriptExecutionContext, vm() is not ready.
500
501     ASSERT(!m_rejectedPromiseTracker);
502     m_rejectedPromiseTracker = std::make_unique<RejectedPromiseTracker>(*this, vm());
503     return *m_rejectedPromiseTracker.get();
504 }
505
506 void ScriptExecutionContext::setDatabaseContext(DatabaseContext* databaseContext)
507 {
508     m_databaseContext = databaseContext;
509 }
510
511 bool ScriptExecutionContext::hasPendingActivity() const
512 {
513     checkConsistency();
514
515     for (auto* activeDOMObject : m_activeDOMObjects) {
516         if (activeDOMObject->hasPendingActivity())
517             return true;
518     }
519
520     for (auto* messagePort : m_messagePorts) {
521         if (messagePort->hasPendingActivity())
522             return true;
523     }
524
525     return false;
526 }
527
528 JSC::ExecState* ScriptExecutionContext::execState()
529 {
530     if (is<Document>(*this)) {
531         Document& document = downcast<Document>(*this);
532         return execStateFromPage(mainThreadNormalWorld(), document.page());
533     }
534
535     WorkerGlobalScope* workerGlobalScope = static_cast<WorkerGlobalScope*>(this);
536     return execStateFromWorkerGlobalScope(workerGlobalScope);
537 }
538
539 #if ENABLE(SERVICE_WORKER)
540
541 ServiceWorker* ScriptExecutionContext::activeServiceWorker() const
542 {
543     return m_activeServiceWorker.get();
544 }
545
546 void ScriptExecutionContext::setActiveServiceWorker(RefPtr<ServiceWorker>&& serviceWorker)
547 {
548     // Add support for workers.
549     if (!is<Document>(*this))
550         return;
551
552     if (m_activeServiceWorker == serviceWorker)
553         return;
554
555     auto& connection = ServiceWorkerProvider::singleton().serviceWorkerConnectionForSession(sessionID());
556     if (m_activeServiceWorker)
557         connection.serviceWorkerStoppedControllingClient(m_activeServiceWorker->identifier(), m_activeServiceWorker->registrationIdentifier(), downcast<Document>(*this).identifier());
558
559     m_activeServiceWorker = WTFMove(serviceWorker);
560
561     if (m_activeServiceWorker)
562         connection.serviceWorkerStartedControllingClient(m_activeServiceWorker->identifier(), m_activeServiceWorker->registrationIdentifier(), downcast<Document>(*this).identifier());
563 }
564
565 void ScriptExecutionContext::registerServiceWorker(ServiceWorker& serviceWorker)
566 {
567     auto addResult = m_serviceWorkers.add(serviceWorker.identifier(), &serviceWorker);
568     ASSERT_UNUSED(addResult, addResult.isNewEntry);
569 }
570
571 void ScriptExecutionContext::unregisterServiceWorker(ServiceWorker& serviceWorker)
572 {
573     m_serviceWorkers.remove(serviceWorker.identifier());
574 }
575
576 ServiceWorkerContainer* ScriptExecutionContext::serviceWorkerContainer()
577 {
578     NavigatorBase* navigator = nullptr;
579     if (is<Document>(*this)) {
580         if (auto* window = downcast<Document>(*this).domWindow())
581             navigator = window->optionalNavigator();
582     } else
583         navigator = downcast<WorkerGlobalScope>(*this).optionalNavigator();
584
585     return navigator ? &navigator->serviceWorker() : nullptr;
586 }
587
588 void ScriptExecutionContext::postTaskTo(const DocumentOrWorkerIdentifier& contextIdentifier, WTF::Function<void(ScriptExecutionContext&)>&& task)
589 {
590     ASSERT(isMainThread());
591
592     switchOn(contextIdentifier, [&] (DocumentIdentifier identifier) {
593         auto* document = Document::allDocumentsMap().get(identifier);
594         if (!document)
595             return;
596         document->postTask([task = WTFMove(task)](auto& scope) {
597             task(scope);
598         });
599     }, [&](ServiceWorkerIdentifier identifier) {
600         SWContextManager::singleton().postTaskToServiceWorker(identifier, [task = WTFMove(task)](auto& scope) {
601             task(scope);
602         });
603     });
604 }
605 #endif
606
607 } // namespace WebCore