globalThis.queueMicrotask() should report thrown exceptions
https://bugs.webkit.org/show_bug.cgi?id=235614

Reviewed by Geoff Garen.

LayoutTests/imported/w3c:

Rebaseline WPT tests that are now passing.

* web-platform-tests/html/webappapis/microtask-queuing/queue-microtask-exceptions.any-expected.txt:
* web-platform-tests/html/webappapis/microtask-queuing/queue-microtask-exceptions.any.serviceworker-expected.txt:
* web-platform-tests/html/webappapis/microtask-queuing/queue-microtask-exceptions.any.worker-expected.txt:

Source/JavaScriptCore:

1. Move JSGlobalObject::hasDebugger() to the header to facilitate
   inlining as it's used in a rather hot Microtask execution code.
2. Export a few Debugger methods for JSDOMMicrotask::run().
3. Remove the now-unused createJSMicrotask() overload.

* debugger/Debugger.h:
* runtime/JSGlobalObject.cpp:
(JSC::JSGlobalObject::hasDebugger const): Deleted.
* runtime/JSGlobalObject.h:
(JSC::JSGlobalObject::hasDebugger const):
* runtime/JSMicrotask.cpp:
* runtime/JSMicrotask.h:

Source/WebCore:

This patch introduces WebCore::JSDOMMicrotask so the queueMicrotask() could report
a thrown exception as per spec [1] and to avoid invocation of a userland function
from detached <iframe>, which aligns WebKit with Blink and Gecko.

Both this changes align the callback of queueMicrotask() with generated callback
functions / interfaces (see bug 232387) that are implemented via JSCallbackData.

[1] https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#microtask-queuing:report-the-exception

Tests: imported/w3c/web-platform-tests/html/webappapis/microtask-queuing/queue-microtask-exceptions.any.js
       fast/dom/callback-function-detached-frame-queue-microtask.html

* Headers.cmake:
* Sources.txt:
* WebCore.xcodeproj/project.pbxproj:
* bindings/js/JSDOMMicrotask.cpp: Added.
(WebCore::createJSDOMMicrotask):
(WebCore::JSDOMMicrotask::run):
* bindings/js/JSDOMMicrotask.h: Added.
* bindings/js/JSDOMWindowCustom.cpp:
(WebCore::JSDOMWindow::queueMicrotask):
* bindings/js/JSWorkerGlobalScopeCustom.cpp:
(WebCore::JSWorkerGlobalScope::queueMicrotask):
* dom/EventLoop.h:

LayoutTests:

* TestExpectations: Unskip 3 WPT tests that are now passing.
* fast/dom/callback-function-detached-frame-queue-microtask-expected.txt: Added.
* fast/dom/callback-function-detached-frame-queue-microtask.html: Added.
* fast/dom/resources/callback-function-detached-frame-queue-microtask-iframe.html: Added.
* inspector/debugger/break-on-uncaught-exception-expected.txt:
* platform/mac-wk1/TestExpectations: Skip Service Worker test.


git-svn-id: http://svn.webkit.org/repository/webkit/trunk@288640 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/LayoutTests/ChangeLog b/LayoutTests/ChangeLog
index 322ccf3..4dcb2c5 100644
--- a/LayoutTests/ChangeLog
+++ b/LayoutTests/ChangeLog
@@ -1,3 +1,17 @@
+2022-01-26  Alexey Shvayka  <ashvayka@apple.com>
+
+        globalThis.queueMicrotask() should report thrown exceptions
+        https://bugs.webkit.org/show_bug.cgi?id=235614
+
+        Reviewed by Geoff Garen.
+
+        * TestExpectations: Unskip 3 WPT tests that are now passing.
+        * fast/dom/callback-function-detached-frame-queue-microtask-expected.txt: Added.
+        * fast/dom/callback-function-detached-frame-queue-microtask.html: Added.
+        * fast/dom/resources/callback-function-detached-frame-queue-microtask-iframe.html: Added.
+        * inspector/debugger/break-on-uncaught-exception-expected.txt:
+        * platform/mac-wk1/TestExpectations: Skip Service Worker test.
+
 2022-01-26  Chris Dumez  <cdumez@apple.com>
 
         Unreviewed, revert r286855 as it caused form submission issues
diff --git a/LayoutTests/TestExpectations b/LayoutTests/TestExpectations
index be463d3..cdd69e2 100644
--- a/LayoutTests/TestExpectations
+++ b/LayoutTests/TestExpectations
@@ -619,9 +619,6 @@
 imported/w3c/web-platform-tests/html/user-activation/navigation-state-reset-sameorigin.tentative.html [ Skip ]
 imported/w3c/web-platform-tests/html/user-activation/propagation-crossorigin.sub.tentative.html [ Skip ]
 imported/w3c/web-platform-tests/html/user-activation/propagation-sameorigin.tentative.html [ Skip ]
-imported/w3c/web-platform-tests/html/webappapis/microtask-queuing/queue-microtask-exceptions.any.html [ Skip ]
-imported/w3c/web-platform-tests/html/webappapis/microtask-queuing/queue-microtask-exceptions.any.serviceworker.html [ Skip ]
-imported/w3c/web-platform-tests/html/webappapis/microtask-queuing/queue-microtask-exceptions.any.worker.html [ Skip ]
 imported/w3c/web-platform-tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/allow-crossorigin.html [ Skip ]
 imported/w3c/web-platform-tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/disallow-crossorigin.html [ Skip ]
 imported/w3c/web-platform-tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-events.html [ Skip ]
diff --git a/LayoutTests/fast/dom/callback-function-detached-frame-queue-microtask-expected.txt b/LayoutTests/fast/dom/callback-function-detached-frame-queue-microtask-expected.txt
new file mode 100644
index 0000000..ec248e2
--- /dev/null
+++ b/LayoutTests/fast/dom/callback-function-detached-frame-queue-microtask-expected.txt
@@ -0,0 +1,5 @@
+
+PASS Callback function, originated in an <iframe>, is invoked if <iframe> is connected
+PASS Callback function, originated in an <iframe> via main window's Function constructor, is invoked even if <iframe> is disconnected
+PASS Callback function, originated in an <iframe>, is not invoked if <iframe> is disconnected
+
diff --git a/LayoutTests/fast/dom/callback-function-detached-frame-queue-microtask.html b/LayoutTests/fast/dom/callback-function-detached-frame-queue-microtask.html
new file mode 100644
index 0000000..53199d6
--- /dev/null
+++ b/LayoutTests/fast/dom/callback-function-detached-frame-queue-microtask.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="resources/callback-function-detached-frame-common.js"></script>
+
+<body>
+
+<script>
+const IFRAME_SRC = "resources/callback-function-detached-frame-queue-microtask-iframe.html";
+
+promise_test(async t => {
+    window.__qm0Calls__ = 0;
+
+    const iframe = await createIframe(t, IFRAME_SRC);
+    iframe.contentWindow.callQueueMicrotask("__qm0Calls__");
+
+    await sleep(10);
+
+    assert_equals(window.__qm0Calls__, 1);
+}, "Callback function, originated in an <iframe>, is invoked if <iframe> is connected");
+
+promise_test(async t => {
+    window.__qm1Calls__ = 0;
+
+    const iframe = await createIframe(t, IFRAME_SRC);
+    iframe.contentWindow.callQueueMicrotask("__qm1Calls__", Function);
+
+    iframe.remove();
+    assert_equals(iframe.contentWindow, null);
+
+    await sleep(10);
+
+    assert_equals(window.__qm1Calls__, 1);
+}, "Callback function, originated in an <iframe> via main window's Function constructor, is invoked even if <iframe> is disconnected");
+
+promise_test(async t => {
+    window.__qm2Calls__ = 0;
+
+    const iframe = await createIframe(t, IFRAME_SRC);
+    iframe.contentWindow.callQueueMicrotask("__qm2Calls__");
+
+    iframe.remove();
+    assert_equals(iframe.contentWindow, null);
+
+    await sleep(10);
+
+    assert_equals(window.__qm2Calls__, 0);
+}, "Callback function, originated in an <iframe>, is not invoked if <iframe> is disconnected");
+</script>
diff --git a/LayoutTests/fast/dom/resources/callback-function-detached-frame-queue-microtask-iframe.html b/LayoutTests/fast/dom/resources/callback-function-detached-frame-queue-microtask-iframe.html
new file mode 100644
index 0000000..5966b39
--- /dev/null
+++ b/LayoutTests/fast/dom/resources/callback-function-detached-frame-queue-microtask-iframe.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+
+<script>
+function callQueueMicrotask(key, FunctionConstructor = Function) {
+    parent.queueMicrotask(FunctionConstructor(`
+        top["${key}"]++;
+    `));
+}
+</script>
diff --git a/LayoutTests/imported/w3c/ChangeLog b/LayoutTests/imported/w3c/ChangeLog
index 9cbc8a6..b598431 100644
--- a/LayoutTests/imported/w3c/ChangeLog
+++ b/LayoutTests/imported/w3c/ChangeLog
@@ -1,3 +1,16 @@
+2022-01-26  Alexey Shvayka  <ashvayka@apple.com>
+
+        globalThis.queueMicrotask() should report thrown exceptions
+        https://bugs.webkit.org/show_bug.cgi?id=235614
+
+        Reviewed by Geoff Garen.
+
+        Rebaseline WPT tests that are now passing.
+
+        * web-platform-tests/html/webappapis/microtask-queuing/queue-microtask-exceptions.any-expected.txt:
+        * web-platform-tests/html/webappapis/microtask-queuing/queue-microtask-exceptions.any.serviceworker-expected.txt:
+        * web-platform-tests/html/webappapis/microtask-queuing/queue-microtask-exceptions.any.worker-expected.txt:
+
 2022-01-26  Chris Dumez  <cdumez@apple.com>
 
         Unreviewed, revert r286855 as it caused form submission issues
diff --git a/LayoutTests/imported/w3c/web-platform-tests/html/webappapis/microtask-queuing/queue-microtask-exceptions.any-expected.txt b/LayoutTests/imported/w3c/web-platform-tests/html/webappapis/microtask-queuing/queue-microtask-exceptions.any-expected.txt
index 968546a..e3cec23 100644
--- a/LayoutTests/imported/w3c/web-platform-tests/html/webappapis/microtask-queuing/queue-microtask-exceptions.any-expected.txt
+++ b/LayoutTests/imported/w3c/web-platform-tests/html/webappapis/microtask-queuing/queue-microtask-exceptions.any-expected.txt
@@ -1,5 +1,4 @@
+CONSOLE MESSAGE: Error: boo
 
-Harness Error (TIMEOUT), message = null
-
-TIMEOUT It rethrows exceptions Test timed out
+PASS It rethrows exceptions
 
diff --git a/LayoutTests/imported/w3c/web-platform-tests/html/webappapis/microtask-queuing/queue-microtask-exceptions.any.serviceworker-expected.txt b/LayoutTests/imported/w3c/web-platform-tests/html/webappapis/microtask-queuing/queue-microtask-exceptions.any.serviceworker-expected.txt
index 968546a..1ede7e8 100644
--- a/LayoutTests/imported/w3c/web-platform-tests/html/webappapis/microtask-queuing/queue-microtask-exceptions.any.serviceworker-expected.txt
+++ b/LayoutTests/imported/w3c/web-platform-tests/html/webappapis/microtask-queuing/queue-microtask-exceptions.any.serviceworker-expected.txt
@@ -1,5 +1,3 @@
 
-Harness Error (TIMEOUT), message = null
-
-TIMEOUT It rethrows exceptions Test timed out
+PASS It rethrows exceptions
 
diff --git a/LayoutTests/imported/w3c/web-platform-tests/html/webappapis/microtask-queuing/queue-microtask-exceptions.any.worker-expected.txt b/LayoutTests/imported/w3c/web-platform-tests/html/webappapis/microtask-queuing/queue-microtask-exceptions.any.worker-expected.txt
index 968546a..e3cec23 100644
--- a/LayoutTests/imported/w3c/web-platform-tests/html/webappapis/microtask-queuing/queue-microtask-exceptions.any.worker-expected.txt
+++ b/LayoutTests/imported/w3c/web-platform-tests/html/webappapis/microtask-queuing/queue-microtask-exceptions.any.worker-expected.txt
@@ -1,5 +1,4 @@
+CONSOLE MESSAGE: Error: boo
 
-Harness Error (TIMEOUT), message = null
-
-TIMEOUT It rethrows exceptions Test timed out
+PASS It rethrows exceptions
 
diff --git a/LayoutTests/inspector/debugger/break-on-uncaught-exception-expected.txt b/LayoutTests/inspector/debugger/break-on-uncaught-exception-expected.txt
index 78567c1..9697a8c 100644
--- a/LayoutTests/inspector/debugger/break-on-uncaught-exception-expected.txt
+++ b/LayoutTests/inspector/debugger/break-on-uncaught-exception-expected.txt
@@ -11,10 +11,34 @@
 CONSOLE MESSAGE: throwing TestError
 CONSOLE MESSAGE: catch TestError
 CONSOLE MESSAGE: DONE
+CONSOLE MESSAGE: TestError
+CONSOLE MESSAGE: TestError
+CONSOLE MESSAGE: TestError
+CONSOLE MESSAGE: TestError
+CONSOLE MESSAGE: TestError
+CONSOLE MESSAGE: TestError
+CONSOLE MESSAGE: TestError
+CONSOLE MESSAGE: TestError
+CONSOLE MESSAGE: TestError
+CONSOLE MESSAGE: TestError
+CONSOLE MESSAGE: TestError
+CONSOLE MESSAGE: TestError
 CONSOLE MESSAGE: BREAKPOINT ACTION LOG 1
+CONSOLE MESSAGE: TestError
 CONSOLE MESSAGE: BREAKPOINT ACTION LOG 2
+CONSOLE MESSAGE: TestError
 CONSOLE MESSAGE: BREAKPOINT ACTION LOG 3
+CONSOLE MESSAGE: TestError
 CONSOLE MESSAGE: BREAKPOINT ACTION LOG 4
+CONSOLE MESSAGE: TestError
+CONSOLE MESSAGE: TestError
+CONSOLE MESSAGE: TestError
+CONSOLE MESSAGE: TestError
+CONSOLE MESSAGE: TestError
+CONSOLE MESSAGE: TestError
+CONSOLE MESSAGE: TestError
+CONSOLE MESSAGE: TestError
+CONSOLE MESSAGE: TestError
 Checking pause locations when pausing on uncaught exceptions.
 
 
@@ -42,17 +66,21 @@
 Setting condition to 'false'...
 
 Triggering breakpoint...
+Uncaught exception in test page: TestError [break-on-uncaught-exception.html:15]
 PASS: Should not pause.
 
 Triggering breakpoint...
+Uncaught exception in test page: TestError [break-on-uncaught-exception.html:15]
 PASS: Should not pause.
 
 Setting condition to 'true'...
 
 Triggering breakpoint...
+Uncaught exception in test page: TestError [break-on-uncaught-exception.html:15]
 PASS: Should pause.
 
 Triggering breakpoint...
+Uncaught exception in test page: TestError [break-on-uncaught-exception.html:15]
 PASS: Should pause.
 
 -- Running test case: BreakOnUncaughtException.Options.Condition.ConsoleCommandLineAPI
@@ -61,18 +89,22 @@
 Setting condition to saved console value...
 
 Triggering breakpoint...
+Uncaught exception in test page: TestError [break-on-uncaught-exception.html:15]
 PASS: Should not pause.
 
 Triggering breakpoint...
+Uncaught exception in test page: TestError [break-on-uncaught-exception.html:15]
 PASS: Should not pause.
 
 Adding saved console value 'true'...
 Setting condition to saved console value...
 
 Triggering breakpoint...
+Uncaught exception in test page: TestError [break-on-uncaught-exception.html:15]
 PASS: Should pause.
 
 Triggering breakpoint...
+Uncaught exception in test page: TestError [break-on-uncaught-exception.html:15]
 PASS: Should pause.
 
 -- Running test case: BreakOnUncaughtException.Options.IgnoreCount
@@ -80,15 +112,19 @@
 Setting ignoreCount to '2'...
 
 Triggering breakpoint...
+Uncaught exception in test page: TestError [break-on-uncaught-exception.html:15]
 PASS: Should not pause.
 
 Triggering breakpoint...
+Uncaught exception in test page: TestError [break-on-uncaught-exception.html:15]
 PASS: Should not pause.
 
 Triggering breakpoint...
+Uncaught exception in test page: TestError [break-on-uncaught-exception.html:15]
 PASS: Should pause.
 
 Triggering breakpoint...
+Uncaught exception in test page: TestError [break-on-uncaught-exception.html:15]
 PASS: Should pause.
 
 -- Running test case: BreakOnUncaughtException.Options.Action.Log
@@ -96,12 +132,14 @@
 Adding log action...
 
 Triggering breakpoint...
+Uncaught exception in test page: TestError [break-on-uncaught-exception.html:15]
 PASS: Should execute breakpoint action.
 PASS: Should pause.
 
 Editing log action...
 
 Triggering breakpoint...
+Uncaught exception in test page: TestError [break-on-uncaught-exception.html:15]
 PASS: Should execute breakpoint action.
 PASS: Should pause.
 
@@ -109,12 +147,14 @@
 Enabling auto-continue...
 
 Triggering breakpoint...
+Uncaught exception in test page: TestError [break-on-uncaught-exception.html:15]
 PASS: Should execute breakpoint action.
 PASS: Should not pause.
 
 Editing log action...
 
 Triggering breakpoint...
+Uncaught exception in test page: TestError [break-on-uncaught-exception.html:15]
 PASS: Should execute breakpoint action.
 PASS: Should not pause.
 
@@ -123,12 +163,14 @@
 Adding evaluate action...
 
 Triggering breakpoint...
+Uncaught exception in test page: TestError [break-on-uncaught-exception.html:15]
 PASS: Should execute breakpoint action.
 PASS: Should pause.
 
 Editing evaluate action...
 
 Triggering breakpoint...
+Uncaught exception in test page: TestError [break-on-uncaught-exception.html:15]
 PASS: Should execute breakpoint action.
 PASS: Should pause.
 
@@ -136,12 +178,14 @@
 Enabling auto-continue...
 
 Triggering breakpoint...
+Uncaught exception in test page: TestError [break-on-uncaught-exception.html:15]
 PASS: Should execute breakpoint action.
 PASS: Should not pause.
 
 Editing evaluate action...
 
 Triggering breakpoint...
+Uncaught exception in test page: TestError [break-on-uncaught-exception.html:15]
 PASS: Should execute breakpoint action.
 PASS: Should not pause.
 
@@ -151,6 +195,7 @@
 Adding evaluate action using saved console value...
 
 Triggering breakpoint...
+Uncaught exception in test page: TestError [break-on-uncaught-exception.html:15]
 PASS: Should execute breakpoint action.
 PASS: Should pause.
 
@@ -158,6 +203,7 @@
 Editing evaluate action using saved console value...
 
 Triggering breakpoint...
+Uncaught exception in test page: TestError [break-on-uncaught-exception.html:15]
 PASS: Should execute breakpoint action.
 PASS: Should pause.
 
@@ -166,6 +212,7 @@
 Enabling auto-continue...
 
 Triggering breakpoint...
+Uncaught exception in test page: TestError [break-on-uncaught-exception.html:15]
 PASS: Should execute breakpoint action.
 PASS: Should not pause.
 
@@ -173,6 +220,7 @@
 Editing evaluate action using saved console value...
 
 Triggering breakpoint...
+Uncaught exception in test page: TestError [break-on-uncaught-exception.html:15]
 PASS: Should execute breakpoint action.
 PASS: Should not pause.
 
diff --git a/LayoutTests/platform/mac-wk1/TestExpectations b/LayoutTests/platform/mac-wk1/TestExpectations
index 7d36901..5682b3a 100644
--- a/LayoutTests/platform/mac-wk1/TestExpectations
+++ b/LayoutTests/platform/mac-wk1/TestExpectations
@@ -378,6 +378,7 @@
 imported/w3c/web-platform-tests/html/infrastructure/safe-passing-of-structured-data/messagechannel.any.serviceworker.html [ Skip ]
 imported/w3c/web-platform-tests/html/semantics/scripting-1/the-script-element/json-module/json-module-service-worker-test.https.html [ Skip ]
 imported/w3c/web-platform-tests/html/webappapis/microtask-queuing/queue-microtask.any.serviceworker.html [ Skip ]
+imported/w3c/web-platform-tests/html/webappapis/microtask-queuing/queue-microtask-exceptions.any.serviceworker.html [ Skip ]
 imported/w3c/web-platform-tests/infrastructure/server/context.any.serviceworker.html [ Skip ]
 imported/w3c/web-platform-tests/infrastructure/server/secure-context.https.any.serviceworker.html [ Skip ]
 imported/w3c/web-platform-tests/IndexedDB/key-generators/reading-autoincrement-indexes-cursors.any.serviceworker.html [ Skip ]
diff --git a/Source/JavaScriptCore/ChangeLog b/Source/JavaScriptCore/ChangeLog
index 0d9d94b..aa5371d 100644
--- a/Source/JavaScriptCore/ChangeLog
+++ b/Source/JavaScriptCore/ChangeLog
@@ -1,3 +1,23 @@
+2022-01-26  Alexey Shvayka  <ashvayka@apple.com>
+
+        globalThis.queueMicrotask() should report thrown exceptions
+        https://bugs.webkit.org/show_bug.cgi?id=235614
+
+        Reviewed by Geoff Garen.
+
+        1. Move JSGlobalObject::hasDebugger() to the header to facilitate
+           inlining as it's used in a rather hot Microtask execution code.
+        2. Export a few Debugger methods for JSDOMMicrotask::run().
+        3. Remove the now-unused createJSMicrotask() overload.
+
+        * debugger/Debugger.h:
+        * runtime/JSGlobalObject.cpp:
+        (JSC::JSGlobalObject::hasDebugger const): Deleted.
+        * runtime/JSGlobalObject.h:
+        (JSC::JSGlobalObject::hasDebugger const):
+        * runtime/JSMicrotask.cpp:
+        * runtime/JSMicrotask.h:
+
 2022-01-26  Antoine Quint  <graouts@webkit.org>
 
         [Web Inspector] Graphics tab should display pseudo-elements for more than ::before and ::after
diff --git a/Source/JavaScriptCore/debugger/Debugger.h b/Source/JavaScriptCore/debugger/Debugger.h
index 9388023..3d13fb0 100644
--- a/Source/JavaScriptCore/debugger/Debugger.h
+++ b/Source/JavaScriptCore/debugger/Debugger.h
@@ -135,8 +135,8 @@
     void didExecuteProgram(CallFrame*);
     void didReachDebuggerStatement(CallFrame*);
 
-    void willRunMicrotask();
-    void didRunMicrotask();
+    JS_EXPORT_PRIVATE void willRunMicrotask();
+    JS_EXPORT_PRIVATE void didRunMicrotask();
 
     void registerCodeBlock(CodeBlock*);
 
diff --git a/Source/JavaScriptCore/runtime/JSGlobalObject.cpp b/Source/JavaScriptCore/runtime/JSGlobalObject.cpp
index 6ed8d20..e397452 100644
--- a/Source/JavaScriptCore/runtime/JSGlobalObject.cpp
+++ b/Source/JavaScriptCore/runtime/JSGlobalObject.cpp
@@ -2583,11 +2583,6 @@
         vm().ensureShadowChicken();
 }
 
-bool JSGlobalObject::hasDebugger() const
-{ 
-    return m_debugger;
-}
-
 bool JSGlobalObject::hasInteractiveDebugger() const 
 { 
     return m_debugger && m_debugger->isInteractivelyDebugging();
diff --git a/Source/JavaScriptCore/runtime/JSGlobalObject.h b/Source/JavaScriptCore/runtime/JSGlobalObject.h
index e2b0eb8..2f3ad76 100644
--- a/Source/JavaScriptCore/runtime/JSGlobalObject.h
+++ b/Source/JavaScriptCore/runtime/JSGlobalObject.h
@@ -629,7 +629,7 @@
 
     DECLARE_EXPORT_INFO;
 
-    bool hasDebugger() const;
+    bool hasDebugger() const { return m_debugger; }
     bool hasInteractiveDebugger() const;
     const RuntimeFlags& runtimeFlags() const { return m_runtimeFlags; }
 
diff --git a/Source/JavaScriptCore/runtime/JSMicrotask.cpp b/Source/JavaScriptCore/runtime/JSMicrotask.cpp
index 3c7af22..12f3609 100644
--- a/Source/JavaScriptCore/runtime/JSMicrotask.cpp
+++ b/Source/JavaScriptCore/runtime/JSMicrotask.cpp
@@ -47,11 +47,6 @@
         m_arguments[3].set(vm, argument3);
     }
 
-    JSMicrotask(VM& vm, JSValue job)
-    {
-        m_job.set(vm, job);
-    }
-
 private:
     void run(JSGlobalObject*) final;
 
@@ -59,11 +54,6 @@
     Strong<Unknown> m_arguments[maxArguments];
 };
 
-Ref<Microtask> createJSMicrotask(VM& vm, JSValue job)
-{
-    return adoptRef(*new JSMicrotask(vm, job));
-}
-
 Ref<Microtask> createJSMicrotask(VM& vm, JSValue job, JSValue argument0, JSValue argument1, JSValue argument2, JSValue argument3)
 {
     return adoptRef(*new JSMicrotask(vm, job, argument0, argument1, argument2, argument3));
diff --git a/Source/JavaScriptCore/runtime/JSMicrotask.h b/Source/JavaScriptCore/runtime/JSMicrotask.h
index f1b848e..a61d8ad 100644
--- a/Source/JavaScriptCore/runtime/JSMicrotask.h
+++ b/Source/JavaScriptCore/runtime/JSMicrotask.h
@@ -33,7 +33,6 @@
 class Microtask;
 class JSArray;
 
-JS_EXPORT_PRIVATE Ref<Microtask> createJSMicrotask(VM&, JSValue job);
 JS_EXPORT_PRIVATE Ref<Microtask> createJSMicrotask(VM&, JSValue job, JSValue, JSValue, JSValue, JSValue);
 
 } // namespace JSC
diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog
index a879241..4203d04 100644
--- a/Source/WebCore/ChangeLog
+++ b/Source/WebCore/ChangeLog
@@ -1,3 +1,35 @@
+2022-01-26  Alexey Shvayka  <ashvayka@apple.com>
+
+        globalThis.queueMicrotask() should report thrown exceptions
+        https://bugs.webkit.org/show_bug.cgi?id=235614
+
+        Reviewed by Geoff Garen.
+
+        This patch introduces WebCore::JSDOMMicrotask so the queueMicrotask() could report
+        a thrown exception as per spec [1] and to avoid invocation of a userland function
+        from detached <iframe>, which aligns WebKit with Blink and Gecko.
+
+        Both this changes align the callback of queueMicrotask() with generated callback
+        functions / interfaces (see bug 232387) that are implemented via JSCallbackData.
+
+        [1] https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#microtask-queuing:report-the-exception
+
+        Tests: imported/w3c/web-platform-tests/html/webappapis/microtask-queuing/queue-microtask-exceptions.any.js
+               fast/dom/callback-function-detached-frame-queue-microtask.html
+
+        * Headers.cmake:
+        * Sources.txt:
+        * WebCore.xcodeproj/project.pbxproj:
+        * bindings/js/JSDOMMicrotask.cpp: Added.
+        (WebCore::createJSDOMMicrotask):
+        (WebCore::JSDOMMicrotask::run):
+        * bindings/js/JSDOMMicrotask.h: Added.
+        * bindings/js/JSDOMWindowCustom.cpp:
+        (WebCore::JSDOMWindow::queueMicrotask):
+        * bindings/js/JSWorkerGlobalScopeCustom.cpp:
+        (WebCore::JSWorkerGlobalScope::queueMicrotask):
+        * dom/EventLoop.h:
+
 2022-01-26  Antoine Quint  <graouts@webkit.org>
 
         [Web Animations] Remove remaining PseudoElement.h includes
diff --git a/Source/WebCore/Headers.cmake b/Source/WebCore/Headers.cmake
index f43ad67..304f97a3 100644
--- a/Source/WebCore/Headers.cmake
+++ b/Source/WebCore/Headers.cmake
@@ -445,6 +445,7 @@
     bindings/js/JSDOMExceptionHandling.h
     bindings/js/JSDOMGlobalObject.h
     bindings/js/JSDOMGuardedObject.h
+    bindings/js/JSDOMMicrotask.h
     bindings/js/JSDOMOperation.h
     bindings/js/JSDOMPromiseDeferred.h
     bindings/js/JSDOMWindowBase.h
diff --git a/Source/WebCore/Sources.txt b/Source/WebCore/Sources.txt
index 776b196..65ab0f9 100644
--- a/Source/WebCore/Sources.txt
+++ b/Source/WebCore/Sources.txt
@@ -528,6 +528,7 @@
 bindings/js/JSDOMGuardedObject.cpp
 bindings/js/JSDOMIterator.cpp
 bindings/js/JSDOMMapLike.cpp
+bindings/js/JSDOMMicrotask.cpp
 bindings/js/JSDOMPromise.cpp
 bindings/js/JSDOMPromiseDeferred.cpp
 bindings/js/JSDOMQuadCustom.cpp
diff --git a/Source/WebCore/WebCore.xcodeproj/project.pbxproj b/Source/WebCore/WebCore.xcodeproj/project.pbxproj
index 9a3cbad..234aba3 100644
--- a/Source/WebCore/WebCore.xcodeproj/project.pbxproj
+++ b/Source/WebCore/WebCore.xcodeproj/project.pbxproj
@@ -12475,6 +12475,8 @@
 		8A9A586F11E84C36008ACFD1 /* JSPerformanceNavigation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSPerformanceNavigation.h; sourceTree = "<group>"; };
 		8A9A587311E84C81008ACFD1 /* JSPerformance.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSPerformance.h; sourceTree = "<group>"; };
 		8A9A587711E84C98008ACFD1 /* JSPerformanceTiming.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSPerformanceTiming.h; sourceTree = "<group>"; };
+		8AA9407E279EEE8100EC0C48 /* JSDOMMicrotask.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JSDOMMicrotask.h; sourceTree = "<group>"; };
+		8AA94080279EEE8100EC0C48 /* JSDOMMicrotask.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = JSDOMMicrotask.cpp; sourceTree = "<group>"; };
 		8AB4BC76126FDB7100DEB727 /* IgnoreDestructiveWriteCountIncrementer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IgnoreDestructiveWriteCountIncrementer.h; sourceTree = "<group>"; };
 		8AF4E55211DC5A36000ED3DE /* PerformanceNavigation.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PerformanceNavigation.cpp; sourceTree = "<group>"; };
 		8AF4E55311DC5A36000ED3DE /* PerformanceNavigation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PerformanceNavigation.h; sourceTree = "<group>"; };
@@ -29770,6 +29772,8 @@
 			children = (
 				1449E286107D4DB400B5793F /* JSCallbackData.cpp */,
 				1449E24A107D4A8400B5793F /* JSCallbackData.h */,
+				8AA94080279EEE8100EC0C48 /* JSDOMMicrotask.cpp */,
+				8AA9407E279EEE8100EC0C48 /* JSDOMMicrotask.h */,
 				F3D461461161D53200CA0D09 /* JSErrorHandler.cpp */,
 				F3D461471161D53200CA0D09 /* JSErrorHandler.h */,
 				93B70D4D09EB0C7C009D8468 /* JSEventListener.cpp */,
diff --git a/Source/WebCore/bindings/js/JSDOMMicrotask.cpp b/Source/WebCore/bindings/js/JSDOMMicrotask.cpp
new file mode 100644
index 0000000..ff0b10d
--- /dev/null
+++ b/Source/WebCore/bindings/js/JSDOMMicrotask.cpp
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2022 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 met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "JSDOMMicrotask.h"
+
+#include "JSDOMExceptionHandling.h"
+#include "JSExecState.h"
+#include <JavaScriptCore/Debugger.h>
+#include <JavaScriptCore/JSGlobalObject.h>
+#include <JavaScriptCore/JSObjectInlines.h>
+#include <JavaScriptCore/StrongInlines.h>
+
+namespace WebCore {
+using namespace JSC;
+
+class JSDOMMicrotask final : public Microtask {
+public:
+    JSDOMMicrotask(VM& vm, JSObject* job)
+        : m_job { vm, job }
+    {
+    }
+
+private:
+    void run(JSGlobalObject*) final;
+
+    Strong<JSObject> m_job;
+};
+
+Ref<Microtask> createJSDOMMicrotask(VM& vm, JSObject* job)
+{
+    return adoptRef(*new JSDOMMicrotask(vm, job));
+}
+
+void JSDOMMicrotask::run(JSGlobalObject* globalObject)
+{
+    auto& vm = globalObject->vm();
+
+    JSObject* job = m_job.get();
+
+    auto* lexicalGlobalObject = job->globalObject(vm);
+    auto* context = jsCast<JSDOMGlobalObject*>(lexicalGlobalObject)->scriptExecutionContext();
+    if (!context || context->activeDOMObjectsAreSuspended() || context->activeDOMObjectsAreStopped())
+        return;
+
+    auto callData = getCallData(vm, job);
+    ASSERT(callData.type != CallData::Type::None);
+
+    if (UNLIKELY(globalObject->hasDebugger()))
+        globalObject->debugger()->willRunMicrotask();
+
+    NakedPtr<JSC::Exception> returnedException = nullptr;
+    JSExecState::profiledCall(lexicalGlobalObject, JSC::ProfilingReason::Microtask, job, callData, jsUndefined(), ArgList(), returnedException);
+    if (returnedException)
+        reportException(lexicalGlobalObject, returnedException);
+
+    if (UNLIKELY(globalObject->hasDebugger()))
+        globalObject->debugger()->didRunMicrotask();
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/bindings/js/JSDOMMicrotask.h b/Source/WebCore/bindings/js/JSDOMMicrotask.h
new file mode 100644
index 0000000..c9036cd
--- /dev/null
+++ b/Source/WebCore/bindings/js/JSDOMMicrotask.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2022 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 met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <JavaScriptCore/JSCJSValue.h>
+#include <JavaScriptCore/Microtask.h>
+
+namespace WebCore {
+
+Ref<JSC::Microtask> createJSDOMMicrotask(JSC::VM&, JSC::JSObject* job);
+
+}
diff --git a/Source/WebCore/bindings/js/JSDOMWindowCustom.cpp b/Source/WebCore/bindings/js/JSDOMWindowCustom.cpp
index b1a258b..0dceb9d 100644
--- a/Source/WebCore/bindings/js/JSDOMWindowCustom.cpp
+++ b/Source/WebCore/bindings/js/JSDOMWindowCustom.cpp
@@ -31,6 +31,7 @@
 #include "JSDOMConvertNullable.h"
 #include "JSDOMConvertNumbers.h"
 #include "JSDOMConvertStrings.h"
+#include "JSDOMMicrotask.h"
 #include "JSDatabase.h"
 #include "JSDatabaseCallback.h"
 #include "JSEvent.h"
@@ -51,7 +52,6 @@
 #include <JavaScriptCore/InternalFunction.h>
 #include <JavaScriptCore/JSCInlines.h>
 #include <JavaScriptCore/JSFunction.h>
-#include <JavaScriptCore/JSMicrotask.h>
 #include <JavaScriptCore/Lookup.h>
 #include <JavaScriptCore/Structure.h>
 
@@ -534,7 +534,7 @@
         return JSValue::decode(throwArgumentMustBeFunctionError(lexicalGlobalObject, scope, 0, "callback", "Window", "queueMicrotask"));
 
     scope.release();
-    Base::queueMicrotask(JSC::createJSMicrotask(vm, functionValue));
+    Base::queueMicrotask(createJSDOMMicrotask(vm, asObject(functionValue)));
     return jsUndefined();
 }
 
diff --git a/Source/WebCore/bindings/js/JSWorkerGlobalScopeCustom.cpp b/Source/WebCore/bindings/js/JSWorkerGlobalScopeCustom.cpp
index ab41d0d..ef40b56 100644
--- a/Source/WebCore/bindings/js/JSWorkerGlobalScopeCustom.cpp
+++ b/Source/WebCore/bindings/js/JSWorkerGlobalScopeCustom.cpp
@@ -27,8 +27,8 @@
 #include "JSWorkerGlobalScope.h"
 
 #include "JSDOMExceptionHandling.h"
+#include "JSDOMMicrotask.h"
 #include "WorkerGlobalScope.h"
-#include <JavaScriptCore/JSMicrotask.h>
 
 namespace WebCore {
 using namespace JSC;
@@ -64,7 +64,7 @@
         return JSValue::decode(throwArgumentMustBeFunctionError(lexicalGlobalObject, scope, 0, "callback", "WorkerGlobalScope", "queueMicrotask"));
 
     scope.release();
-    Base::queueMicrotask(JSC::createJSMicrotask(vm, functionValue));
+    Base::queueMicrotask(createJSDOMMicrotask(vm, asObject(functionValue)));
     return jsUndefined();
 }
 
diff --git a/Source/WebCore/dom/EventLoop.h b/Source/WebCore/dom/EventLoop.h
index 4ec3c25..dd5f522 100644
--- a/Source/WebCore/dom/EventLoop.h
+++ b/Source/WebCore/dom/EventLoop.h
@@ -37,7 +37,6 @@
 class ActiveDOMCallbackMicrotask;
 class EventLoopTaskGroup;
 class EventTarget;
-class Microtask;
 class MicrotaskQueue;
 class ScriptExecutionContext;