From 37cf833410faba85dfccf6bea74aa20292875fff Mon Sep 17 00:00:00 2001 From: Mark Lam Date: Wed, 24 May 2023 15:42:32 -0700 Subject: [PATCH] imported/w3c/web-platform-tests/background-fetch/abort.https.window.html is flaky. https://bugs.webkit.org/show_bug.cgi?id=253314 Reviewed by Chris Dumez. The test was flakily crashing in debug bots when trying to resolve the promise of BackgroundFetchRegistration::matchAll. The issue is that the toJS call can fail due to an exception. When this happens, it returns an empty value that is used to resolve the promise. To prevent this, we add a check in DeferredPromise::callFunction to handle uncaught exceptions. Also fix all exception check validation failures under imported/w3c/web-platform-tests/background-fetch. * Source/WebCore/bindings/js/JSDOMPromiseDeferred.cpp: (WebCore::DeferredPromise::callFunction): (WebCore::DeferredPromise::whenSettled): (WebCore::DeferredPromise::reject): (WebCore::rejectPromiseWithExceptionIfAny): (WebCore::DeferredPromise::handleTerminationExceptionIfNeeded): * Source/WebCore/bindings/js/JSDOMPromiseDeferred.h: (WebCore::DeferredPromise::resolve): (WebCore::DeferredPromise::resolveWithNewlyCreated): (WebCore::DeferredPromise::resolveCallbackValueWithNewlyCreated): (WebCore::DeferredPromise::reject): (WebCore::DeferredPromise::resolveWithCallback): (WebCore::DeferredPromise::rejectWithCallback): (WebCore::DeferredPromise::needsAbort const): * Source/WebCore/bindings/js/JSMicrotaskCallback.h: (WebCore::JSMicrotaskCallback::call): * Source/WebCore/dom/BroadcastChannel.cpp: (WebCore::BroadcastChannel::dispatchMessage): * Source/WebCore/dom/MessagePort.cpp: (WebCore::MessagePort::dispatchMessages): * Source/WebCore/dom/Microtasks.cpp: (WebCore::MicrotaskQueue::performMicrotaskCheckpoint): * Source/WebCore/page/LocalDOMWindow.cpp: (WebCore::LocalDOMWindow::processPostMessage): * Source/WebCore/workers/WorkerMessagingProxy.cpp: (WebCore::WorkerMessagingProxy::postMessageToWorkerObject): (WebCore::WorkerMessagingProxy::postMessageToWorkerGlobalScope): * Source/WebCore/workers/WorkerRunLoop.cpp: (WebCore::WorkerDedicatedRunLoop::Task::performTask): * Source/WebCore/workers/service/ServiceWorkerContainer.cpp: (WebCore::ServiceWorkerContainer::jobResolvedWithRegistration): (WebCore::ServiceWorkerContainer::postMessage): Canonical link: https://commits.webkit.org/264489@main --- .../bindings/js/JSDOMPromiseDeferred.cpp | 35 ++++++++--- .../bindings/js/JSDOMPromiseDeferred.h | 61 +++++++++++++------ .../WebCore/bindings/js/JSMicrotaskCallback.h | 4 +- Source/WebCore/dom/BroadcastChannel.cpp | 8 +++ Source/WebCore/dom/MessagePort.cpp | 12 +++- Source/WebCore/dom/Microtasks.cpp | 46 +++++++++----- Source/WebCore/page/LocalDOMWindow.cpp | 11 +++- .../WebCore/workers/WorkerMessagingProxy.cpp | 17 +++++- Source/WebCore/workers/WorkerRunLoop.cpp | 20 +++++- .../service/ServiceWorkerContainer.cpp | 16 ++++- 10 files changed, 180 insertions(+), 50 deletions(-) diff --git a/Source/WebCore/bindings/js/JSDOMPromiseDeferred.cpp b/Source/WebCore/bindings/js/JSDOMPromiseDeferred.cpp index 16b58cf817af..e5077a5a452d 100644 --- a/Source/WebCore/bindings/js/JSDOMPromiseDeferred.cpp +++ b/Source/WebCore/bindings/js/JSDOMPromiseDeferred.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2021 Apple Inc. All rights reserved. + * Copyright (C) 2013-2023 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -38,6 +38,7 @@ #include #include #include +#include namespace WebCore { using namespace JSC; @@ -56,6 +57,14 @@ void DeferredPromise::callFunction(JSGlobalObject& lexicalGlobalObject, ResolveM if (shouldIgnoreRequestToFulfill()) return; + JSC::VM& vm = lexicalGlobalObject.vm(); + auto scope = DECLARE_CATCH_SCOPE(vm); + + auto handleExceptionIfNeeded = makeScopeExit([&] { + if (UNLIKELY(scope.exception())) + handleUncaughtException(scope, *jsCast(&lexicalGlobalObject)); + }); + if (activeDOMObjectsAreSuspended()) { JSC::Strong strongResolution(lexicalGlobalObject.vm(), resolution); ASSERT(scriptExecutionContext()->eventLoop().isSuspended()); @@ -100,7 +109,14 @@ void DeferredPromise::whenSettled(Function&& callback) return; } - DOMPromise::whenPromiseIsSettled(globalObject(), deferred(), WTFMove(callback)); + { + auto* globalObject = this->globalObject(); + auto& vm = globalObject->vm(); + JSC::JSLockHolder locker(vm); + auto scope = DECLARE_CATCH_SCOPE(vm); + DOMPromise::whenPromiseIsSettled(globalObject, deferred(), WTFMove(callback)); + DEFERRED_PROMISE_HANDLE_AND_RETURN_IF_EXCEPTION(scope, globalObject); + } } void DeferredPromise::reject(RejectAsHandled rejectAsHandled) @@ -144,10 +160,10 @@ void DeferredPromise::reject(Exception exception, RejectAsHandled rejectAsHandle EXCEPTION_ASSERT(scope.exception()); auto error = scope.exception()->value(); bool isTerminating = handleTerminationExceptionIfNeeded(scope, lexicalGlobalObject); - scope.clearException(); - - if (!isTerminating) + if (!isTerminating) { + scope.clearException(); reject(error, rejectAsHandled); + } return; } @@ -179,10 +195,10 @@ void DeferredPromise::reject(ExceptionCode ec, const String& message, RejectAsHa EXCEPTION_ASSERT(scope.exception()); auto error = scope.exception()->value(); bool isTerminating = handleTerminationExceptionIfNeeded(scope, lexicalGlobalObject); - scope.clearException(); - - if (!isTerminating) + if (!isTerminating) { + scope.clearException(); reject(error, rejectAsHandled); + } return; } @@ -214,6 +230,8 @@ void rejectPromiseWithExceptionIfAny(JSC::JSGlobalObject& lexicalGlobalObject, J UNUSED_PARAM(lexicalGlobalObject); if (LIKELY(!catchScope.exception())) return; + if (catchScope.vm().hasPendingTerminationException()) + return; JSValue error = catchScope.exception()->value(); catchScope.clearException(); @@ -284,6 +302,7 @@ bool DeferredPromise::handleTerminationExceptionIfNeeded(CatchScope& scope, JSDO bool terminatorCausedException = vm.isTerminationException(exception); if (terminatorCausedException || (scriptController && scriptController->isTerminatingExecution())) { scriptController->forbidExecution(); + m_needsAbort = true; return true; } } diff --git a/Source/WebCore/bindings/js/JSDOMPromiseDeferred.h b/Source/WebCore/bindings/js/JSDOMPromiseDeferred.h index 611ee88d676c..fa2d961d1f0a 100644 --- a/Source/WebCore/bindings/js/JSDOMPromiseDeferred.h +++ b/Source/WebCore/bindings/js/JSDOMPromiseDeferred.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2021 Apple Inc. All rights reserved. + * Copyright (C) 2013-2023 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -37,6 +37,14 @@ namespace WebCore { class JSLocalDOMWindow; enum class RejectAsHandled : bool { No, Yes }; +#define DEFERRED_PROMISE_HANDLE_AND_RETURN_IF_EXCEPTION(scope, globalObject) do { \ + if (UNLIKELY(scope.exception())) { \ + handleUncaughtException(scope, *jsCast(globalObject)); \ + return; \ + } \ + } while (false) + + class DeferredPromise : public DOMGuarded { public: enum class Mode { @@ -66,8 +74,13 @@ class DeferredPromise : public DOMGuarded { ASSERT(deferred()); ASSERT(globalObject()); JSC::JSGlobalObject* lexicalGlobalObject = globalObject(); - JSC::JSLockHolder locker(lexicalGlobalObject); - resolve(*lexicalGlobalObject, toJS(*lexicalGlobalObject, *globalObject(), std::forward(value))); + auto& vm = lexicalGlobalObject->vm(); + JSC::JSLockHolder locker(vm); + auto scope = DECLARE_CATCH_SCOPE(vm); + auto jsValue = toJS(*lexicalGlobalObject, *globalObject(), std::forward(value)); + DEFERRED_PROMISE_HANDLE_AND_RETURN_IF_EXCEPTION(scope, lexicalGlobalObject); + resolve(*lexicalGlobalObject, jsValue); + DEFERRED_PROMISE_HANDLE_AND_RETURN_IF_EXCEPTION(scope, lexicalGlobalObject); } void resolveWithJSValue(JSC::JSValue resolution) @@ -103,8 +116,12 @@ class DeferredPromise : public DOMGuarded { ASSERT(deferred()); ASSERT(globalObject()); JSC::JSGlobalObject* lexicalGlobalObject = globalObject(); - JSC::JSLockHolder locker(lexicalGlobalObject); - resolve(*lexicalGlobalObject, toJSNewlyCreated(*lexicalGlobalObject, *globalObject(), std::forward(value))); + auto& vm = lexicalGlobalObject->vm(); + JSC::JSLockHolder locker(vm); + auto scope = DECLARE_CATCH_SCOPE(vm); + auto jsValue = toJSNewlyCreated(*lexicalGlobalObject, *globalObject(), std::forward(value)); + DEFERRED_PROMISE_HANDLE_AND_RETURN_IF_EXCEPTION(scope, lexicalGlobalObject); + resolve(*lexicalGlobalObject, jsValue); } template @@ -116,8 +133,12 @@ class DeferredPromise : public DOMGuarded { ASSERT(deferred()); ASSERT(globalObject()); auto* lexicalGlobalObject = globalObject(); - JSC::JSLockHolder locker(lexicalGlobalObject); - resolve(*lexicalGlobalObject, toJSNewlyCreated(*lexicalGlobalObject, *globalObject(), createValue(*globalObject()->scriptExecutionContext()))); + auto& vm = lexicalGlobalObject->vm(); + JSC::JSLockHolder locker(vm); + auto scope = DECLARE_CATCH_SCOPE(vm); + auto jsValue = toJSNewlyCreated(*lexicalGlobalObject, *globalObject(), createValue(*globalObject()->scriptExecutionContext())); + DEFERRED_PROMISE_HANDLE_AND_RETURN_IF_EXCEPTION(scope, lexicalGlobalObject); + resolve(*lexicalGlobalObject, jsValue); } template @@ -129,8 +150,12 @@ class DeferredPromise : public DOMGuarded { ASSERT(deferred()); ASSERT(globalObject()); JSC::JSGlobalObject* lexicalGlobalObject = globalObject(); - JSC::JSLockHolder locker(lexicalGlobalObject); - reject(*lexicalGlobalObject, toJS(*lexicalGlobalObject, *globalObject(), std::forward(value)), rejectAsHandled); + auto& vm = lexicalGlobalObject->vm(); + JSC::JSLockHolder locker(vm); + auto scope = DECLARE_CATCH_SCOPE(vm); + auto jsValue = toJS(*lexicalGlobalObject, *globalObject(), std::forward(value)); + DEFERRED_PROMISE_HANDLE_AND_RETURN_IF_EXCEPTION(scope, lexicalGlobalObject); + reject(*lexicalGlobalObject, jsValue, rejectAsHandled); } void reject(RejectAsHandled = RejectAsHandled::No); @@ -148,12 +173,12 @@ class DeferredPromise : public DOMGuarded { ASSERT(deferred()); ASSERT(globalObject()); auto* lexicalGlobalObject = globalObject(); - JSC::VM& vm = lexicalGlobalObject->vm(); + auto& vm = lexicalGlobalObject->vm(); JSC::JSLockHolder locker(vm); auto scope = DECLARE_CATCH_SCOPE(vm); - resolve(*lexicalGlobalObject, callback(*globalObject())); - if (UNLIKELY(scope.exception())) - handleUncaughtException(scope, *lexicalGlobalObject); + auto jsValue = callback(*globalObject()); + DEFERRED_PROMISE_HANDLE_AND_RETURN_IF_EXCEPTION(scope, lexicalGlobalObject); + resolve(*lexicalGlobalObject, jsValue); } template @@ -168,14 +193,15 @@ class DeferredPromise : public DOMGuarded { JSC::VM& vm = lexicalGlobalObject->vm(); JSC::JSLockHolder locker(vm); auto scope = DECLARE_CATCH_SCOPE(vm); - reject(*lexicalGlobalObject, callback(*globalObject()), rejectAsHandled); - if (UNLIKELY(scope.exception())) - handleUncaughtException(scope, *lexicalGlobalObject); + auto jsValue = callback(*globalObject()); + DEFERRED_PROMISE_HANDLE_AND_RETURN_IF_EXCEPTION(scope, lexicalGlobalObject); + reject(*lexicalGlobalObject, jsValue, rejectAsHandled); } JSC::JSValue promise() const; void whenSettled(Function&&); + bool needsAbort() const { return m_needsAbort; } private: DeferredPromise(JSDOMGlobalObject& globalObject, JSC::JSPromise& deferred, Mode mode) @@ -198,9 +224,10 @@ class DeferredPromise : public DOMGuarded { } bool handleTerminationExceptionIfNeeded(JSC::CatchScope&, JSDOMGlobalObject& lexicalGlobalObject); - void handleUncaughtException(JSC::CatchScope&, JSDOMGlobalObject& lexicalGlobalObject); + WEBCORE_EXPORT void handleUncaughtException(JSC::CatchScope&, JSDOMGlobalObject& lexicalGlobalObject); Mode m_mode; + bool m_needsAbort { false }; }; class DOMPromiseDeferredBase { diff --git a/Source/WebCore/bindings/js/JSMicrotaskCallback.h b/Source/WebCore/bindings/js/JSMicrotaskCallback.h index b57b2ca2f11f..caeb0357f1ad 100644 --- a/Source/WebCore/bindings/js/JSMicrotaskCallback.h +++ b/Source/WebCore/bindings/js/JSMicrotaskCallback.h @@ -1,6 +1,6 @@ /* * Copyright (C) 2018 Yusuke Suzuki . - * Copyright (C) 2021 Apple Inc. All rights reserved. + * Copyright (C) 2021-2023 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -43,9 +43,7 @@ class JSMicrotaskCallback : public RefCounted { auto protectedThis { Ref { *this } }; JSC::VM& vm = m_globalObject->vm(); JSC::JSLockHolder lock(vm); - auto scope = DECLARE_CATCH_SCOPE(vm); JSExecState::runTask(m_globalObject.get(), m_task); - scope.assertNoExceptionExceptTermination(); } private: diff --git a/Source/WebCore/dom/BroadcastChannel.cpp b/Source/WebCore/dom/BroadcastChannel.cpp index f24492f2a205..a66e853099da 100644 --- a/Source/WebCore/dom/BroadcastChannel.cpp +++ b/Source/WebCore/dom/BroadcastChannel.cpp @@ -248,7 +248,15 @@ void BroadcastChannel::dispatchMessage(Ref&& message) if (!globalObject) return; + auto& vm = globalObject->vm(); + auto scope = DECLARE_CATCH_SCOPE(vm); auto event = MessageEvent::create(*globalObject, WTFMove(message), scriptExecutionContext()->securityOrigin()->toString()); + if (UNLIKELY(scope.exception())) { + // Currently, we assume that the only way we can get here is if we have a termination. + RELEASE_ASSERT(vm.hasPendingTerminationException()); + return; + } + dispatchEvent(event.event); }); } diff --git a/Source/WebCore/dom/MessagePort.cpp b/Source/WebCore/dom/MessagePort.cpp index be82b8a91c5f..1e82521acf7e 100644 --- a/Source/WebCore/dom/MessagePort.cpp +++ b/Source/WebCore/dom/MessagePort.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * Copyright (C) 2008-2023 Apple Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -278,6 +278,9 @@ void MessagePort::dispatchMessages() return; ASSERT(context->isContextThread()); + auto* globalObject = context->globalObject(); + auto& vm = globalObject->vm(); + auto scope = DECLARE_CATCH_SCOPE(vm); bool contextIsWorker = is(*context); for (auto& message : messages) { @@ -286,7 +289,12 @@ void MessagePort::dispatchMessages() return; auto ports = MessagePort::entanglePorts(*context, WTFMove(message.transferredPorts)); - auto event = MessageEvent::create(*context->globalObject(), message.message.releaseNonNull(), { }, { }, { }, WTFMove(ports)); + auto event = MessageEvent::create(*globalObject, message.message.releaseNonNull(), { }, { }, { }, WTFMove(ports)); + if (UNLIKELY(scope.exception())) { + // Currently, we assume that the only way we can get here is if we have a termination. + RELEASE_ASSERT(vm.hasPendingTerminationException()); + return; + } // Per specification, each MessagePort object has a task source called the port message queue. queueTaskKeepingObjectAlive(*this, TaskSource::PostedMessageQueue, [this, event = WTFMove(event)] { diff --git a/Source/WebCore/dom/Microtasks.cpp b/Source/WebCore/dom/Microtasks.cpp index c6b2f97892dd..ec4e8a77977f 100644 --- a/Source/WebCore/dom/Microtasks.cpp +++ b/Source/WebCore/dom/Microtasks.cpp @@ -1,6 +1,7 @@ /* * Copyright (C) 2014 Yoav Weiss (yoav@yoav.ws) * Copyright (C) 2015 Akamai Technologies Inc. All rights reserved. + * Copyright (C) 2023 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -52,10 +53,12 @@ void MicrotaskQueue::performMicrotaskCheckpoint() return; SetForScope change(m_performingMicrotaskCheckpoint, true); - JSC::JSLockHolder locker(vm()); + VM& vm = this->vm(); + JSC::JSLockHolder locker(vm); + auto catchScope = DECLARE_CATCH_SCOPE(vm); Vector> toKeep; - while (!m_microtaskQueue.isEmpty()) { + while (!m_microtaskQueue.isEmpty() && !vm.executionForbidden()) { Vector> queue = WTFMove(m_microtaskQueue); for (auto& task : queue) { auto* group = task->group(); @@ -63,32 +66,45 @@ void MicrotaskQueue::performMicrotaskCheckpoint() continue; if (group->isSuspended()) toKeep.append(WTFMove(task)); - else + else { task->execute(); + if (UNLIKELY(!catchScope.clearExceptionExceptTermination())) + break; // Encountered termination. + } } } - vm().finalizeSynchronousJSExecution(); + vm.finalizeSynchronousJSExecution(); m_microtaskQueue = WTFMove(toKeep); - auto checkpointTasks = std::exchange(m_checkpointTasks, { }); - for (auto& checkpointTask : checkpointTasks) { - auto* group = checkpointTask->group(); - if (!group || group->isStoppedPermanently()) - continue; + if (!vm.executionForbidden()) { + auto checkpointTasks = std::exchange(m_checkpointTasks, { }); + for (auto& checkpointTask : checkpointTasks) { + auto* group = checkpointTask->group(); + if (!group || group->isStoppedPermanently()) + continue; - if (group->isSuspended()) { - m_checkpointTasks.append(WTFMove(checkpointTask)); - continue; - } + if (group->isSuspended()) { + m_checkpointTasks.append(WTFMove(checkpointTask)); + continue; + } - checkpointTask->execute(); + checkpointTask->execute(); + if (UNLIKELY(!catchScope.clearExceptionExceptTermination())) + break; // Encountered termination. + } } // https://html.spec.whatwg.org/multipage/webappapis.html#perform-a-microtask-checkpoint (step 4). - m_eventLoop->forEachAssociatedContext([](auto& context) { + auto* vmPtr = &vm; + m_eventLoop->forEachAssociatedContext([vmPtr](auto& context) { + auto& vm = *vmPtr; + if (UNLIKELY(vm.executionForbidden())) + return; + auto catchScope = DECLARE_CATCH_SCOPE(vm); if (auto* tracker = context.rejectedPromiseTracker()) tracker->processQueueSoon(); + catchScope.clearExceptionExceptTermination(); }); // FIXME: We should cleanup Indexed Database transactions as per: diff --git a/Source/WebCore/page/LocalDOMWindow.cpp b/Source/WebCore/page/LocalDOMWindow.cpp index 936cdbdd5014..bfc9b017bfb9 100644 --- a/Source/WebCore/page/LocalDOMWindow.cpp +++ b/Source/WebCore/page/LocalDOMWindow.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2021 Apple Inc. All rights reserved. + * Copyright (C) 2006-2023 Apple Inc. All rights reserved. * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies) * * Redistribution and use in source and binary forms, with or without @@ -933,11 +933,20 @@ void LocalDOMWindow::processPostMessage(JSC::JSGlobalObject& lexicalGlobalObject if (!globalObject) return; + auto& vm = globalObject->vm(); + auto scope = DECLARE_CATCH_SCOPE(vm); + UserGestureIndicator userGestureIndicator(userGestureToForward); InspectorInstrumentation::willDispatchPostMessage(frame, postMessageIdentifier); auto ports = MessagePort::entanglePorts(*document(), WTFMove(message.transferredPorts)); auto event = MessageEvent::create(*globalObject, message.message.releaseNonNull(), sourceOrigin, { }, incumbentWindowProxy ? std::make_optional(MessageEventSource(WTFMove(incumbentWindowProxy))) : std::nullopt, WTFMove(ports)); + if (UNLIKELY(scope.exception())) { + // Currently, we assume that the only way we can get here is if we have a termination. + RELEASE_ASSERT(vm.hasPendingTerminationException()); + return; + } + dispatchEvent(event.event); InspectorInstrumentation::didDispatchPostMessage(frame, postMessageIdentifier); diff --git a/Source/WebCore/workers/WorkerMessagingProxy.cpp b/Source/WebCore/workers/WorkerMessagingProxy.cpp index 7a21f18f03fe..b379634bdda7 100644 --- a/Source/WebCore/workers/WorkerMessagingProxy.cpp +++ b/Source/WebCore/workers/WorkerMessagingProxy.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008-2017 Apple Inc. All Rights Reserved. + * Copyright (C) 2008-2023 Apple Inc. All Rights Reserved. * Copyright (C) 2009-2022 Google Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without @@ -190,8 +190,15 @@ void WorkerMessagingProxy::postMessageToWorkerObject(MessageWithMessagePorts&& m if (!globalObject) return; + auto& vm = globalObject->vm(); + auto scope = DECLARE_CATCH_SCOPE(vm); UserGestureIndicator userGestureIndicator(userGestureForwarder ? userGestureForwarder->userGestureToForward() : nullptr); auto event = MessageEvent::create(*globalObject, message.message.releaseNonNull(), { }, { }, { }, WTFMove(ports)); + if (UNLIKELY(scope.exception())) { + // Currently, we assume that the only way we can get here is if we have a termination. + RELEASE_ASSERT(vm.hasPendingTerminationException()); + return; + } worker->dispatchEvent(event.event); }); }); @@ -223,6 +230,9 @@ void WorkerMessagingProxy::postMessageToWorkerGlobalScope(MessageWithMessagePort if (!globalObject) return; + auto& vm = globalObject->vm(); + auto scope = DECLARE_CATCH_SCOPE(vm); + // Setting m_userGestureForwarder here, before dispatching the MessageEvent, will allow all calls to // worker.postMessage() made during the handling of that MessageEvent to inherit the UserGestureToken // held by the forwarder; see postMessageToWorkerObject() above. @@ -230,6 +240,11 @@ void WorkerMessagingProxy::postMessageToWorkerGlobalScope(MessageWithMessagePort auto ports = MessagePort::entanglePorts(scriptContext, WTFMove(message.transferredPorts)); auto event = MessageEvent::create(*globalObject, message.message.releaseNonNull(), { }, { }, std::nullopt, WTFMove(ports)); + if (UNLIKELY(scope.exception())) { + // Currently, we assume that the only way we can get here is if we have a termination. + RELEASE_ASSERT(vm.hasPendingTerminationException()); + return; + } context.dispatchEvent(event.event); // Because WorkerUserGestureForwarder is defined as DestructionThread::Main, releasing this Ref diff --git a/Source/WebCore/workers/WorkerRunLoop.cpp b/Source/WebCore/workers/WorkerRunLoop.cpp index a23b709c361e..9fd45aa24e74 100644 --- a/Source/WebCore/workers/WorkerRunLoop.cpp +++ b/Source/WebCore/workers/WorkerRunLoop.cpp @@ -1,6 +1,6 @@ /* * Copyright (C) 2009 Google Inc. All rights reserved. - * Copyright (C) 2016-2021 Apple Inc. All rights reserved. + * Copyright (C) 2016-2023 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -32,6 +32,8 @@ #include "config.h" #include "WorkerRunLoop.h" +#include "JSDOMExceptionHandling.h" +#include "JSDOMGlobalObject.h" #include "ScriptExecutionContext.h" #include "SharedTimer.h" #include "ThreadGlobalData.h" @@ -40,6 +42,7 @@ #include "WorkerOrWorkletGlobalScope.h" #include "WorkerOrWorkletScriptController.h" #include "WorkerThread.h" +#include #include #if USE(GLIB) @@ -274,8 +277,21 @@ void WorkerRunLoop::postDebuggerTask(ScriptExecutionContext::Task&& task) void WorkerDedicatedRunLoop::Task::performTask(WorkerOrWorkletGlobalScope* context) { - if ((!context->isClosing() && context->script() && !context->script()->isTerminatingExecution()) || m_task.isCleanupTask()) + if (m_task.isCleanupTask()) m_task.performTask(*context); + else if (!context->isClosing() && context->script() && !context->script()->isTerminatingExecution()) { + JSC::VM& vm = context->script()->vm(); + auto scope = DECLARE_CATCH_SCOPE(vm); + m_task.performTask(*context); + if (UNLIKELY(context->script() && scope.exception())) { + if (vm.hasPendingTerminationException()) { + context->script()->forbidExecution(); + return; + } + Locker locker(vm.apiLock()); + reportException(context->script()->globalScopeWrapper(), scope.exception()); + } + } } WorkerDedicatedRunLoop::Task::Task(ScriptExecutionContext::Task&& task, const String& mode) diff --git a/Source/WebCore/workers/service/ServiceWorkerContainer.cpp b/Source/WebCore/workers/service/ServiceWorkerContainer.cpp index bf7740066e6a..2aed68b853d8 100644 --- a/Source/WebCore/workers/service/ServiceWorkerContainer.cpp +++ b/Source/WebCore/workers/service/ServiceWorkerContainer.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017 Apple Inc. All rights reserved. + * Copyright (C) 2017-2023 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -443,6 +443,8 @@ void ServiceWorkerContainer::jobResolvedWithRegistration(ServiceWorkerJob& job, notifyRegistrationIsSettled(iterator->value); m_ongoingSettledRegistrations.remove(iterator); }); + if (UNLIKELY(promise->needsAbort())) + return; } promise->resolve>(WTFMove(registration)); @@ -452,13 +454,25 @@ void ServiceWorkerContainer::jobResolvedWithRegistration(ServiceWorkerJob& job, void ServiceWorkerContainer::postMessage(MessageWithMessagePorts&& message, ServiceWorkerData&& sourceData, String&& sourceOrigin) { auto& context = *scriptExecutionContext(); + if (UNLIKELY(context.isJSExecutionForbidden())) + return; + auto* globalObject = context.globalObject(); if (!globalObject) return; + auto& vm = globalObject->vm(); + auto scope = DECLARE_CATCH_SCOPE(vm); + MessageEventSource source = RefPtr { ServiceWorker::getOrCreate(context, WTFMove(sourceData)) }; auto messageEvent = MessageEvent::create(*globalObject, message.message.releaseNonNull(), sourceOrigin, { }, WTFMove(source), MessagePort::entanglePorts(context, WTFMove(message.transferredPorts))); + if (UNLIKELY(scope.exception())) { + // Currently, we assume that the only way we can get here is if we have a termination. + RELEASE_ASSERT(vm.hasPendingTerminationException()); + return; + } + if (m_shouldDeferMessageEvents) m_deferredMessageEvents.append(WTFMove(messageEvent)); else {