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 | |
70 | namespace WebCore { |
71 | using namespace Inspector; |
72 | |
73 | static Lock allScriptExecutionContextsMapLock; |
74 | static HashMap<ScriptExecutionContextIdentifier, ScriptExecutionContext*>& allScriptExecutionContextsMap() |
75 | { |
76 | static NeverDestroyed<HashMap<ScriptExecutionContextIdentifier, ScriptExecutionContext*>> contexts; |
77 | ASSERT(allScriptExecutionContextsMapLock.isLocked()); |
78 | return contexts; |
79 | } |
80 | |
81 | struct ScriptExecutionContext::PendingException { |
82 | WTF_MAKE_FAST_ALLOCATED; |
83 | public: |
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 | |
99 | ScriptExecutionContext::ScriptExecutionContext() |
100 | { |
101 | } |
102 | |
103 | ScriptExecutionContextIdentifier 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 | |
117 | void 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 | |
128 | inline void ScriptExecutionContext::checkConsistency() const |
129 | { |
130 | } |
131 | |
132 | #else |
133 | |
134 | void 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 | |
150 | ScriptExecutionContext::~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 | |
176 | void ScriptExecutionContext::processMessageWithMessagePortsSoon() |
177 | { |
178 | if (m_willprocessMessageWithMessagePortsSoon) |
179 | return; |
180 | |
181 | m_willprocessMessageWithMessagePortsSoon = true; |
182 | postTask([] (ScriptExecutionContext& context) { |
183 | context.dispatchMessagePortEvents(); |
184 | }); |
185 | } |
186 | |
187 | void 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 | |
205 | void 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 | |
213 | void 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 | |
221 | void ScriptExecutionContext::didLoadResourceSynchronously() |
222 | { |
223 | } |
224 | |
225 | bool 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 | |
252 | void 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 | |
276 | void 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 | |
298 | void 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 | |
312 | void 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 | |
326 | void 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 | |
335 | void 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 | |
346 | void ScriptExecutionContext::willDestroyActiveDOMObject(ActiveDOMObject& activeDOMObject) |
347 | { |
348 | m_activeDOMObjects.remove(&activeDOMObject); |
349 | } |
350 | |
351 | void ScriptExecutionContext::didCreateDestructionObserver(ContextDestructionObserver& observer) |
352 | { |
353 | ASSERT(!m_inScriptExecutionContextDestructor); |
354 | m_destructionObservers.add(&observer); |
355 | } |
356 | |
357 | void ScriptExecutionContext::willDestroyDestructionObserver(ContextDestructionObserver& observer) |
358 | { |
359 | m_destructionObservers.remove(&observer); |
360 | } |
361 | |
362 | // FIXME: Should this function be in SecurityContext or SecurityOrigin instead? |
363 | bool 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 | |
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 | 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 | |
419 | void 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 | |
424 | bool 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 | |
443 | int ScriptExecutionContext::circularSequentialID() |
444 | { |
445 | ++m_circularSequentialID; |
446 | if (m_circularSequentialID <= 0) |
447 | m_circularSequentialID = 1; |
448 | return m_circularSequentialID; |
449 | } |
450 | |
451 | PublicURLManager& ScriptExecutionContext::publicURLManager() |
452 | { |
453 | if (!m_publicURLManager) |
454 | m_publicURLManager = PublicURLManager::create(this); |
455 | return *m_publicURLManager; |
456 | } |
457 | |
458 | void ScriptExecutionContext::adjustMinimumDOMTimerInterval(Seconds oldMinimumTimerInterval) |
459 | { |
460 | if (minimumDOMTimerInterval() != oldMinimumTimerInterval) { |
461 | for (auto& timer : m_timeouts.values()) |
462 | timer->updateTimerIntervalIfNecessary(); |
463 | } |
464 | } |
465 | |
466 | Seconds 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 | |
476 | void ScriptExecutionContext::didChangeTimerAlignmentInterval() |
477 | { |
478 | for (auto& timer : m_timeouts.values()) |
479 | timer->didChangeAlignmentInterval(); |
480 | } |
481 | |
482 | Seconds ScriptExecutionContext::domTimerAlignmentInterval(bool) const |
483 | { |
484 | return DOMTimer::defaultAlignmentInterval(); |
485 | } |
486 | |
487 | JSC::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 | |
502 | RejectedPromiseTracker& 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 | |
512 | void ScriptExecutionContext::removeRejectedPromiseTracker() |
513 | { |
514 | m_rejectedPromiseTracker = nullptr; |
515 | } |
516 | |
517 | void ScriptExecutionContext::setDatabaseContext(DatabaseContext* databaseContext) |
518 | { |
519 | m_databaseContext = databaseContext; |
520 | } |
521 | |
522 | bool 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 | |
534 | JSC::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 | |
553 | String ScriptExecutionContext::domainForCachePartition() const |
554 | { |
555 | return m_domainForCachePartition.isNull() ? topOrigin().domainForCachePartition() : m_domainForCachePartition; |
556 | } |
557 | |
558 | bool 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 | |
570 | bool ScriptExecutionContext::hasServiceWorkerScheme() const |
571 | { |
572 | ASSERT(securityOrigin()); |
573 | return SchemeRegistry::isServiceWorkerContainerCustomScheme(securityOrigin()->protocol()); |
574 | } |
575 | |
576 | #if ENABLE(SERVICE_WORKER) |
577 | |
578 | ServiceWorker* ScriptExecutionContext::activeServiceWorker() const |
579 | { |
580 | return m_activeServiceWorker.get(); |
581 | } |
582 | |
583 | void ScriptExecutionContext::setActiveServiceWorker(RefPtr<ServiceWorker>&& serviceWorker) |
584 | { |
585 | m_activeServiceWorker = WTFMove(serviceWorker); |
586 | } |
587 | |
588 | void ScriptExecutionContext::registerServiceWorker(ServiceWorker& serviceWorker) |
589 | { |
590 | auto addResult = m_serviceWorkers.add(serviceWorker.identifier(), &serviceWorker); |
591 | ASSERT_UNUSED(addResult, addResult.isNewEntry); |
592 | } |
593 | |
594 | void ScriptExecutionContext::unregisterServiceWorker(ServiceWorker& serviceWorker) |
595 | { |
596 | m_serviceWorkers.remove(serviceWorker.identifier()); |
597 | } |
598 | |
599 | ServiceWorkerContainer* 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 | |
611 | bool 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 | |
634 | bool 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 | |