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 "Page.h"
43#include "PublicURLManager.h"
44#include "RejectedPromiseTracker.h"
45#include "ResourceRequest.h"
46#include "SWClientConnection.h"
47#include "SWContextManager.h"
48#include "SchemeRegistry.h"
49#include "ScriptController.h"
50#include "ScriptDisallowedScope.h"
51#include "ScriptState.h"
52#include "ServiceWorker.h"
53#include "ServiceWorkerGlobalScope.h"
54#include "ServiceWorkerProvider.h"
55#include "Settings.h"
56#include "WorkerGlobalScope.h"
57#include "WorkerNavigator.h"
58#include "WorkerThread.h"
59#include "WorkletGlobalScope.h"
60#include "WorkletScriptController.h"
61#include <JavaScriptCore/CatchScope.h>
62#include <JavaScriptCore/Exception.h>
63#include <JavaScriptCore/JSPromise.h>
64#include <JavaScriptCore/ScriptCallStack.h>
65#include <JavaScriptCore/StrongInlines.h>
66#include <wtf/MainThread.h>
67#include <wtf/Ref.h>
68#include <wtf/SetForScope.h>
69
70namespace WebCore {
71using namespace Inspector;
72
73static Lock allScriptExecutionContextsMapLock;
74static HashMap<ScriptExecutionContextIdentifier, ScriptExecutionContext*>& allScriptExecutionContextsMap()
75{
76 static NeverDestroyed<HashMap<ScriptExecutionContextIdentifier, ScriptExecutionContext*>> contexts;
77 ASSERT(allScriptExecutionContextsMapLock.isLocked());
78 return contexts;
79}
80
81struct ScriptExecutionContext::PendingException {
82 WTF_MAKE_FAST_ALLOCATED;
83public:
84 PendingException(const String& errorMessage, int lineNumber, int columnNumber, const String& sourceURL, RefPtr<ScriptCallStack>&& callStack)
85 : m_errorMessage(errorMessage)
86 , m_lineNumber(lineNumber)
87 , m_columnNumber(columnNumber)
88 , m_sourceURL(sourceURL)
89 , m_callStack(WTFMove(callStack))
90 {
91 }
92 String m_errorMessage;
93 int m_lineNumber;
94 int m_columnNumber;
95 String m_sourceURL;
96 RefPtr<ScriptCallStack> m_callStack;
97};
98
99ScriptExecutionContext::ScriptExecutionContext()
100{
101}
102
103ScriptExecutionContextIdentifier ScriptExecutionContext::contextIdentifier() const
104{
105 ASSERT(isContextThread());
106 if (!m_contextIdentifier) {
107 Locker<Lock> locker(allScriptExecutionContextsMapLock);
108
109 m_contextIdentifier = ScriptExecutionContextIdentifier::generate();
110
111 ASSERT(!allScriptExecutionContextsMap().contains(m_contextIdentifier));
112 allScriptExecutionContextsMap().add(m_contextIdentifier, const_cast<ScriptExecutionContext*>(this));
113 }
114 return m_contextIdentifier;
115}
116
117void ScriptExecutionContext::removeFromContextsMap()
118{
119 if (m_contextIdentifier) {
120 Locker<Lock> locker(allScriptExecutionContextsMapLock);
121 ASSERT(allScriptExecutionContextsMap().contains(m_contextIdentifier));
122 allScriptExecutionContextsMap().remove(m_contextIdentifier);
123 }
124}
125
126#if ASSERT_DISABLED
127
128inline void ScriptExecutionContext::checkConsistency() const
129{
130}
131
132#else
133
134void ScriptExecutionContext::checkConsistency() const
135{
136 for (auto* messagePort : m_messagePorts)
137 ASSERT(messagePort->scriptExecutionContext() == this);
138
139 for (auto* destructionObserver : m_destructionObservers)
140 ASSERT(destructionObserver->scriptExecutionContext() == this);
141
142 for (auto* activeDOMObject : m_activeDOMObjects) {
143 ASSERT(activeDOMObject->scriptExecutionContext() == this);
144 activeDOMObject->assertSuspendIfNeededWasCalled();
145 }
146}
147
148#endif
149
150ScriptExecutionContext::~ScriptExecutionContext()
151{
152 checkConsistency();
153
154#if !ASSERT_DISABLED
155 if (m_contextIdentifier) {
156 Locker<Lock> locker(allScriptExecutionContextsMapLock);
157 ASSERT_WITH_MESSAGE(!allScriptExecutionContextsMap().contains(m_contextIdentifier),
158 "A ScriptExecutionContext subclass instance implementing postTask should have already removed itself from the map");
159 }
160
161 m_inScriptExecutionContextDestructor = true;
162#endif
163
164#if ENABLE(SERVICE_WORKER)
165 setActiveServiceWorker(nullptr);
166#endif
167
168 while (auto* destructionObserver = m_destructionObservers.takeAny())
169 destructionObserver->contextDestroyed();
170
171#if !ASSERT_DISABLED
172 m_inScriptExecutionContextDestructor = false;
173#endif
174}
175
176void ScriptExecutionContext::processMessageWithMessagePortsSoon()
177{
178 if (m_willprocessMessageWithMessagePortsSoon)
179 return;
180
181 m_willprocessMessageWithMessagePortsSoon = true;
182 postTask([] (ScriptExecutionContext& context) {
183 context.dispatchMessagePortEvents();
184 });
185}
186
187void ScriptExecutionContext::dispatchMessagePortEvents()
188{
189 checkConsistency();
190
191 Ref<ScriptExecutionContext> protectedThis(*this);
192 ASSERT(m_willprocessMessageWithMessagePortsSoon);
193 m_willprocessMessageWithMessagePortsSoon = false;
194
195 // Make a frozen copy of the ports so we can iterate while new ones might be added or destroyed.
196 for (auto* messagePort : copyToVector(m_messagePorts)) {
197 // The port may be destroyed, and another one created at the same address,
198 // but this is harmless. The worst that can happen as a result is that
199 // dispatchMessages() will be called needlessly.
200 if (m_messagePorts.contains(messagePort) && messagePort->started())
201 messagePort->dispatchMessages();
202 }
203}
204
205void ScriptExecutionContext::createdMessagePort(MessagePort& messagePort)
206{
207 ASSERT((is<Document>(*this) && isMainThread())
208 || (is<WorkerGlobalScope>(*this) && downcast<WorkerGlobalScope>(*this).thread().thread() == &Thread::current()));
209
210 m_messagePorts.add(&messagePort);
211}
212
213void ScriptExecutionContext::destroyedMessagePort(MessagePort& messagePort)
214{
215 ASSERT((is<Document>(*this) && isMainThread())
216 || (is<WorkerGlobalScope>(*this) && downcast<WorkerGlobalScope>(*this).thread().thread() == &Thread::current()));
217
218 m_messagePorts.remove(&messagePort);
219}
220
221void ScriptExecutionContext::didLoadResourceSynchronously()
222{
223}
224
225bool ScriptExecutionContext::canSuspendActiveDOMObjectsForDocumentSuspension(Vector<ActiveDOMObject*>* unsuspendableObjects)
226{
227 checkConsistency();
228
229 bool canSuspend = true;
230
231 forEachActiveDOMObject([&](auto& activeDOMObject) {
232 if (!activeDOMObject.canSuspendForDocumentSuspension()) {
233 canSuspend = false;
234 if (unsuspendableObjects)
235 unsuspendableObjects->append(&activeDOMObject);
236 else
237 return ShouldContinue::No;
238 }
239 return ShouldContinue::Yes;
240 });
241
242 if (unsuspendableObjects) {
243 // Remove activeDOMObjects that have been destroyed while we were iterating above.
244 unsuspendableObjects->removeAllMatching([&](auto* activeDOMObject) {
245 return !m_activeDOMObjects.contains(activeDOMObject);
246 });
247 }
248
249 return canSuspend;
250}
251
252void ScriptExecutionContext::forEachActiveDOMObject(const Function<ShouldContinue(ActiveDOMObject&)>& apply) const
253{
254 // It is not allowed to run arbitrary script or construct new ActiveDOMObjects while we are iterating over ActiveDOMObjects.
255 // An ASSERT_WITH_SECURITY_IMPLICATION or RELEASE_ASSERT will fire if this happens, but it's important to code
256 // canSuspendActiveDOMObjectsForDocumentSuspension() / suspend() / resume() / stop() functions so it will not happen!
257 ScriptDisallowedScope scriptDisallowedScope;
258 SetForScope<bool> activeDOMObjectAdditionForbiddenScope(m_activeDOMObjectAdditionForbidden, true);
259
260 // Make a frozen copy of the objects so we can iterate while new ones might be destroyed.
261 auto possibleActiveDOMObjects = copyToVector(m_activeDOMObjects);
262
263 for (auto* activeDOMObject : possibleActiveDOMObjects) {
264 // Check if this object was deleted already. If so, just skip it.
265 // Calling contains on a possibly-already-deleted object is OK because we guarantee
266 // no new object can be added, so even if a new object ends up allocated with the
267 // same address, that will be *after* this function exits.
268 if (!m_activeDOMObjects.contains(activeDOMObject))
269 continue;
270
271 if (apply(*activeDOMObject) == ShouldContinue::No)
272 break;
273 }
274}
275
276void ScriptExecutionContext::suspendActiveDOMObjects(ReasonForSuspension why)
277{
278 checkConsistency();
279
280 if (m_activeDOMObjectsAreSuspended) {
281 // A page may subsequently suspend DOM objects, say as part of entering the page cache, after the embedding
282 // client requested the page be suspended. We ignore such requests so long as the embedding client requested
283 // the suspension first. See <rdar://problem/13754896> for more details.
284 ASSERT(m_reasonForSuspendingActiveDOMObjects == ReasonForSuspension::PageWillBeSuspended);
285 return;
286 }
287
288 m_activeDOMObjectsAreSuspended = true;
289
290 forEachActiveDOMObject([why](auto& activeDOMObject) {
291 activeDOMObject.suspend(why);
292 return ShouldContinue::Yes;
293 });
294
295 m_reasonForSuspendingActiveDOMObjects = why;
296}
297
298void ScriptExecutionContext::resumeActiveDOMObjects(ReasonForSuspension why)
299{
300 checkConsistency();
301
302 if (m_reasonForSuspendingActiveDOMObjects != why)
303 return;
304 m_activeDOMObjectsAreSuspended = false;
305
306 forEachActiveDOMObject([](auto& activeDOMObject) {
307 activeDOMObject.resume();
308 return ShouldContinue::Yes;
309 });
310}
311
312void ScriptExecutionContext::stopActiveDOMObjects()
313{
314 checkConsistency();
315
316 if (m_activeDOMObjectsAreStopped)
317 return;
318 m_activeDOMObjectsAreStopped = true;
319
320 forEachActiveDOMObject([](auto& activeDOMObject) {
321 activeDOMObject.stop();
322 return ShouldContinue::Yes;
323 });
324}
325
326void ScriptExecutionContext::suspendActiveDOMObjectIfNeeded(ActiveDOMObject& activeDOMObject)
327{
328 ASSERT(m_activeDOMObjects.contains(&activeDOMObject));
329 if (m_activeDOMObjectsAreSuspended)
330 activeDOMObject.suspend(m_reasonForSuspendingActiveDOMObjects);
331 if (m_activeDOMObjectsAreStopped)
332 activeDOMObject.stop();
333}
334
335void ScriptExecutionContext::didCreateActiveDOMObject(ActiveDOMObject& activeDOMObject)
336{
337 // The m_activeDOMObjectAdditionForbidden check is a RELEASE_ASSERT because of the
338 // consequences of having an ActiveDOMObject that is not correctly reflected in the set.
339 // If we do have one of those, it can possibly be a security vulnerability. So we'd
340 // rather have a crash than continue running with the set possibly compromised.
341 ASSERT(!m_inScriptExecutionContextDestructor);
342 RELEASE_ASSERT(!m_activeDOMObjectAdditionForbidden);
343 m_activeDOMObjects.add(&activeDOMObject);
344}
345
346void ScriptExecutionContext::willDestroyActiveDOMObject(ActiveDOMObject& activeDOMObject)
347{
348 m_activeDOMObjects.remove(&activeDOMObject);
349}
350
351void ScriptExecutionContext::didCreateDestructionObserver(ContextDestructionObserver& observer)
352{
353 ASSERT(!m_inScriptExecutionContextDestructor);
354 m_destructionObservers.add(&observer);
355}
356
357void ScriptExecutionContext::willDestroyDestructionObserver(ContextDestructionObserver& observer)
358{
359 m_destructionObservers.remove(&observer);
360}
361
362// FIXME: Should this function be in SecurityContext or SecurityOrigin instead?
363bool ScriptExecutionContext::canIncludeErrorDetails(CachedScript* script, const String& sourceURL)
364{
365 ASSERT(securityOrigin());
366 if (script) {
367 ASSERT(script->origin());
368 ASSERT(securityOrigin()->toString() == script->origin()->toString());
369 return script->isCORSSameOrigin();
370 }
371 return securityOrigin()->canRequest(completeURL(sourceURL));
372}
373
374void 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
395void ScriptExecutionContext::reportUnhandledPromiseRejection(JSC::ExecState& state, JSC::JSPromise& promise, RefPtr<Inspector::ScriptCallStack>&& callStack)
396{
397 Page* page = nullptr;
398 if (is<Document>(this))
399 page = downcast<Document>(this)->page();
400 // FIXME: allow Workers to mute unhandled promise rejection messages.
401
402 if (page && !page->settings().unhandledPromiseRejectionToConsoleEnabled())
403 return;
404
405 JSC::VM& vm = state.vm();
406 auto scope = DECLARE_CATCH_SCOPE(vm);
407
408 JSC::JSValue result = promise.result(vm);
409 String resultMessage = retrieveErrorMessage(state, vm, result, scope);
410 String errorMessage = makeString("Unhandled Promise Rejection: ", resultMessage);
411 std::unique_ptr<Inspector::ConsoleMessage> message;
412 if (callStack)
413 message = std::make_unique<Inspector::ConsoleMessage>(MessageSource::JS, MessageType::Log, MessageLevel::Error, errorMessage, callStack.releaseNonNull());
414 else
415 message = std::make_unique<Inspector::ConsoleMessage>(MessageSource::JS, MessageType::Log, MessageLevel::Error, errorMessage);
416 addConsoleMessage(WTFMove(message));
417}
418
419void ScriptExecutionContext::addConsoleMessage(MessageSource source, MessageLevel level, const String& message, const String& sourceURL, unsigned lineNumber, unsigned columnNumber, JSC::ExecState* state, unsigned long requestIdentifier)
420{
421 addMessage(source, level, message, sourceURL, lineNumber, columnNumber, 0, state, requestIdentifier);
422}
423
424bool ScriptExecutionContext::dispatchErrorEvent(const String& errorMessage, int lineNumber, int columnNumber, const String& sourceURL, JSC::Exception* exception, CachedScript* cachedScript)
425{
426 auto* target = errorEventTarget();
427 if (!target)
428 return false;
429
430 RefPtr<ErrorEvent> errorEvent;
431 if (canIncludeErrorDetails(cachedScript, sourceURL))
432 errorEvent = ErrorEvent::create(errorMessage, sourceURL, lineNumber, columnNumber, { vm(), exception ? exception->value() : JSC::jsNull() });
433 else
434 errorEvent = ErrorEvent::create("Script error."_s, { }, 0, 0, { });
435
436 ASSERT(!m_inDispatchErrorEvent);
437 m_inDispatchErrorEvent = true;
438 target->dispatchEvent(*errorEvent);
439 m_inDispatchErrorEvent = false;
440 return errorEvent->defaultPrevented();
441}
442
443int ScriptExecutionContext::circularSequentialID()
444{
445 ++m_circularSequentialID;
446 if (m_circularSequentialID <= 0)
447 m_circularSequentialID = 1;
448 return m_circularSequentialID;
449}
450
451PublicURLManager& ScriptExecutionContext::publicURLManager()
452{
453 if (!m_publicURLManager)
454 m_publicURLManager = PublicURLManager::create(this);
455 return *m_publicURLManager;
456}
457
458void ScriptExecutionContext::adjustMinimumDOMTimerInterval(Seconds oldMinimumTimerInterval)
459{
460 if (minimumDOMTimerInterval() != oldMinimumTimerInterval) {
461 for (auto& timer : m_timeouts.values())
462 timer->updateTimerIntervalIfNecessary();
463 }
464}
465
466Seconds ScriptExecutionContext::minimumDOMTimerInterval() const
467{
468 // The default implementation returns the DOMTimer's default
469 // minimum timer interval. FIXME: to make it work with dedicated
470 // workers, we will have to override it in the appropriate
471 // subclass, and provide a way to enumerate a Document's dedicated
472 // workers so we can update them all.
473 return DOMTimer::defaultMinimumInterval();
474}
475
476void ScriptExecutionContext::didChangeTimerAlignmentInterval()
477{
478 for (auto& timer : m_timeouts.values())
479 timer->didChangeAlignmentInterval();
480}
481
482Seconds ScriptExecutionContext::domTimerAlignmentInterval(bool) const
483{
484 return DOMTimer::defaultAlignmentInterval();
485}
486
487JSC::VM& ScriptExecutionContext::vm()
488{
489 if (is<Document>(*this))
490 return commonVM();
491 if (is<WorkerGlobalScope>(*this))
492 return downcast<WorkerGlobalScope>(*this).script()->vm();
493#if ENABLE(CSS_PAINTING_API)
494 if (is<WorkletGlobalScope>(*this))
495 return downcast<WorkletGlobalScope>(*this).script()->vm();
496#endif
497
498 RELEASE_ASSERT_NOT_REACHED();
499 return commonVM();
500}
501
502RejectedPromiseTracker& ScriptExecutionContext::ensureRejectedPromiseTrackerSlow()
503{
504 // ScriptExecutionContext::vm() in Worker is only available after WorkerGlobalScope initialization is done.
505 // When initializing ScriptExecutionContext, vm() is not ready.
506
507 ASSERT(!m_rejectedPromiseTracker);
508 m_rejectedPromiseTracker = std::make_unique<RejectedPromiseTracker>(*this, vm());
509 return *m_rejectedPromiseTracker.get();
510}
511
512void ScriptExecutionContext::removeRejectedPromiseTracker()
513{
514 m_rejectedPromiseTracker = nullptr;
515}
516
517void ScriptExecutionContext::setDatabaseContext(DatabaseContext* databaseContext)
518{
519 m_databaseContext = databaseContext;
520}
521
522bool ScriptExecutionContext::hasPendingActivity() const
523{
524 checkConsistency();
525
526 for (auto* activeDOMObject : m_activeDOMObjects) {
527 if (activeDOMObject->hasPendingActivity())
528 return true;
529 }
530
531 return false;
532}
533
534JSC::ExecState* ScriptExecutionContext::execState()
535{
536 if (is<Document>(*this)) {
537 Document& document = downcast<Document>(*this);
538 auto* frame = document.frame();
539 return frame ? frame->script().globalObject(mainThreadNormalWorld())->globalExec() : nullptr;
540 }
541
542 if (is<WorkerGlobalScope>(*this))
543 return execStateFromWorkerGlobalScope(downcast<WorkerGlobalScope>(*this));
544#if ENABLE(CSS_PAINTING_API)
545 if (is<WorkletGlobalScope>(*this))
546 return execStateFromWorkletGlobalScope(downcast<WorkletGlobalScope>(*this));
547#endif
548
549 ASSERT_NOT_REACHED();
550 return nullptr;
551}
552
553String ScriptExecutionContext::domainForCachePartition() const
554{
555 return m_domainForCachePartition.isNull() ? topOrigin().domainForCachePartition() : m_domainForCachePartition;
556}
557
558bool ScriptExecutionContext::allowsMediaDevices() const
559{
560#if ENABLE(MEDIA_STREAM)
561 if (!is<Document>(*this))
562 return false;
563 auto page = downcast<Document>(*this).page();
564 return page ? !page->settings().mediaCaptureRequiresSecureConnection() : false;
565#else
566 return false;
567#endif
568}
569
570bool ScriptExecutionContext::hasServiceWorkerScheme() const
571{
572 ASSERT(securityOrigin());
573 return SchemeRegistry::isServiceWorkerContainerCustomScheme(securityOrigin()->protocol());
574}
575
576#if ENABLE(SERVICE_WORKER)
577
578ServiceWorker* ScriptExecutionContext::activeServiceWorker() const
579{
580 return m_activeServiceWorker.get();
581}
582
583void ScriptExecutionContext::setActiveServiceWorker(RefPtr<ServiceWorker>&& serviceWorker)
584{
585 m_activeServiceWorker = WTFMove(serviceWorker);
586}
587
588void ScriptExecutionContext::registerServiceWorker(ServiceWorker& serviceWorker)
589{
590 auto addResult = m_serviceWorkers.add(serviceWorker.identifier(), &serviceWorker);
591 ASSERT_UNUSED(addResult, addResult.isNewEntry);
592}
593
594void ScriptExecutionContext::unregisterServiceWorker(ServiceWorker& serviceWorker)
595{
596 m_serviceWorkers.remove(serviceWorker.identifier());
597}
598
599ServiceWorkerContainer* ScriptExecutionContext::serviceWorkerContainer()
600{
601 NavigatorBase* navigator = nullptr;
602 if (is<Document>(*this)) {
603 if (auto* window = downcast<Document>(*this).domWindow())
604 navigator = window->optionalNavigator();
605 } else
606 navigator = downcast<WorkerGlobalScope>(*this).optionalNavigator();
607
608 return navigator ? &navigator->serviceWorker() : nullptr;
609}
610
611bool ScriptExecutionContext::postTaskTo(const DocumentOrWorkerIdentifier& contextIdentifier, WTF::Function<void(ScriptExecutionContext&)>&& task)
612{
613 ASSERT(isMainThread());
614
615 bool wasPosted = false;
616 switchOn(contextIdentifier, [&] (DocumentIdentifier identifier) {
617 auto* document = Document::allDocumentsMap().get(identifier);
618 if (!document)
619 return;
620 document->postTask([task = WTFMove(task)](auto& scope) {
621 task(scope);
622 });
623 wasPosted= true;
624 }, [&](ServiceWorkerIdentifier identifier) {
625 wasPosted = SWContextManager::singleton().postTaskToServiceWorker(identifier, [task = WTFMove(task)](auto& scope) {
626 task(scope);
627 });
628 });
629 return wasPosted;
630}
631
632#endif
633
634bool ScriptExecutionContext::postTaskTo(ScriptExecutionContextIdentifier identifier, Task&& task)
635{
636 Locker<Lock> locker(allScriptExecutionContextsMapLock);
637 auto* context = allScriptExecutionContextsMap().get(identifier);
638
639 if (!context)
640 return false;
641
642 context->postTask(WTFMove(task));
643 return true;
644}
645
646} // namespace WebCore
647