summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/html/webappapis/scripting
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /testing/web-platform/tests/html/webappapis/scripting
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/web-platform/tests/html/webappapis/scripting')
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/event-loops/fully_active_document.window.js29
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/event-loops/microtask_after_raf.html57
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/event-loops/microtask_after_script.html55
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/event-loops/resources/common.js20
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/event-loops/resources/iframe.html7
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/event-loops/resources/page-with-frame.html1
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/event-loops/task_microtask_ordering-manual.html64
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/event-loops/task_microtask_ordering.html85
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/body-onload.html20
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/compile-event-handler-lexical-scopes-form-owner.html48
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/compile-event-handler-lexical-scopes.html167
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/compile-event-handler-settings-objects.html69
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/compile-event-handler-symbol-unscopables.html71
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/contextmenu-event-manual.htm21
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/event-handler-all-global-events.html88
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/event-handler-attributes-body-window.html18
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/event-handler-attributes-frameset-window.html30
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/event-handler-attributes-windowless-body.html71
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/event-handler-handleEvent-ignored.html38
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/event-handler-javascript.html20
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/event-handler-onmove-01.tentative.html17
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/event-handler-onmove-02.tentative.html24
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/event-handler-onresize.html38
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/body-element-synthetic-errorevent.html57
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/body-element-synthetic-event.html29
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/document-synthetic-errorevent.html60
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/document-synthetic-event.html30
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/frameset-element-synthetic-errorevent.html64
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/frameset-element-synthetic-event.html36
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/resources/frameset-frame.html4
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/resources/no-op-worker.js1
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/resources/worker-with-syntax-error.js1
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/script-element.html67
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/synthetic-errorevent-click.html78
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/synthetic-errorevent-click.worker.js22
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/window-runtime-error.html48
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/window-synthetic-errorevent.html57
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/window-synthetic-event.html30
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/worker.html63
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/workerglobalscope-runtime-error.worker.js40
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/workerglobalscope-synthetic-errorevent.worker.js49
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/workerglobalscope-synthetic-event.worker.js22
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-manual.html62
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm.html60
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/event-handler-removal.window.js76
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/event-handler-sourcetext.html40
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/event-handler-spec-example.window.js55
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/eventhandler-cancellation.html76
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/inline-event-handler-ordering.html53
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/invalid-uncompiled-raw-handler-compiled-late.window.js16
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/invalid-uncompiled-raw-handler-compiled-once.window.js14
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/invalid-uncompiled-raw-handler-keeps-position.window.js20
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/messageevent-constructor.https.html114
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/onerroreventhandler-frame.html56
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/onerroreventhandler.html11
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/resources/compiled-event-handler-settings-objects-support.html12
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/resources/event-handler-body.js61
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/resources/open-window.html4
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/events/uncompiled_event_handler_with_scripting_disabled.html21
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/addEventListener.html32
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/body-onerror-compile-error-data-url.html37
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/body-onerror-compile-error.html39
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/body-onerror-runtime-error.html39
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error-cross-origin-setInterval.html25
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error-cross-origin-setTimeout.html23
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error-cross-origin.html38
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error-data-url.html36
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error-in-attribute.html39
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error-in-body-onerror.html28
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error-in-setInterval.html39
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error-in-setTimeout.html36
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error-same-origin-with-hash.html36
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error-same-origin.html36
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error.html38
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-agent-formalism/atomics-wait-async.https.any.js25
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-agent-formalism/atomics-wait-async.https.any.js.headers2
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-agent-formalism/requires-failure.https.any.js11
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-agent-formalism/requires-success.any.js9
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-agent-formalism/resources/notify-worker.js5
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-agent-formalism/resources/notify-worker.js.headers1
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/promise-job-entry-different-function-realm.html112
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/promise-job-entry.html101
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/promise-job-incumbent.html164
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/README.md5
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/current/current.html4
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/current/resources/window-to-open.html3
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/function/function.html3
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/function/resources/window-to-open.html3
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/promise-job-entry-incumbent.html15
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/promise-job-incumbent-incumbent.html27
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/promise-job-incumbent-resolver.html9
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/relevant/relevant.html14
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/relevant/resources/window-to-open.html3
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/resources/window-to-open.html3
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/window-to-open.html3
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-cross-origin-setInterval.html25
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-cross-origin-setTimeout.html23
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-cross-origin.html38
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-data-url.html36
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-in-attribute.html39
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-in-body-onerror.html25
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-in-setInterval.html39
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-in-setTimeout.html36
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-in-window-onerror.html29
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-same-origin-with-hash.html36
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-same-origin.html36
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error.html38
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/support/syntax-error-in-setInterval.js8
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/support/syntax-error-in-setTimeout.js7
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/support/syntax-error.js1
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/support/undefined-variable-in-setInterval.js8
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/support/undefined-variable-in-setTimeout.js7
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/support/undefined-variable.js1
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/allow-crossorigin.html30
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/disallow-crossorigin.html96
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-event-constructor.html50
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-event-during-parse.html44
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-events-attached-in-event.html31
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-events-iframe.html146
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-events-onerror.html47
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-events.dedicatedworker.html11
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-events.html8
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-events.serviceworker.https.html12
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-events.sharedworker.html11
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-resolution-order.html70
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/support/promise-access-control.py18
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/support/promise-rejection-events.js961
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/window-onerror-parse-error.html40
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/window-onerror-runtime-error-throw.html43
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/window-onerror-runtime-error.html43
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/window-onerror-with-cross-frame-event-listeners-1.html33
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/window-onerror-with-cross-frame-event-listeners-2.html33
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/window-onerror-with-cross-frame-event-listeners-3.html33
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/window-onerror-with-cross-frame-event-listeners-4.html33
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/processing-model-2/window-onerror-with-cross-frame-event-listeners-5.html25
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/reporterror-cross-realm-method.html30
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/reporterror-in-detached-window-crash.html11
-rw-r--r--testing/web-platform/tests/html/webappapis/scripting/reporterror.any.js49
138 files changed, 5950 insertions, 0 deletions
diff --git a/testing/web-platform/tests/html/webappapis/scripting/event-loops/fully_active_document.window.js b/testing/web-platform/tests/html/webappapis/scripting/event-loops/fully_active_document.window.js
new file mode 100644
index 0000000000..950a8a29ee
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/event-loops/fully_active_document.window.js
@@ -0,0 +1,29 @@
+async_test(t => {
+ const frame = document.body.appendChild(document.createElement("iframe"));
+ t.add_cleanup(() => frame.remove());
+
+ frame.onload = t.step_func(() => {
+ // Right now the doc of the iframe inside "frame" is still "fully-active".
+ // Navigate parent away, making the child iframe's doc "active", not "fully-active".
+ frame.contentWindow.location = "/common/blank.html";
+
+ frame.onload = t.step_func(() => {
+ // The child iframe's doc is "active", not "fully-active", and should not receive the storage notification.
+ sessionStorage.setItem('myCat', 'Tom');
+ t.step_timeout(() => {
+ // The child iframe's hasn't received the storage notification.
+ assert_equals(sessionStorage.getItem("Received storage event"), null);
+ frame.contentWindow.history.go(-1);
+ t.step_timeout(() => {
+ // Now The child iframe's doc is "fully-active" again,
+ // the previously not run storage task should now have been run.
+ assert_equals(sessionStorage.getItem("Received storage event"), "true");
+ t.done();
+ }, 1000);
+ }, 1000);
+ });
+ });
+
+ frame.src = "resources/page-with-frame.html";
+}, "Tasks for documents that are not fully active are stored, and run when the documents becomes fully-active");
+
diff --git a/testing/web-platform/tests/html/webappapis/scripting/event-loops/microtask_after_raf.html b/testing/web-platform/tests/html/webappapis/scripting/event-loops/microtask_after_raf.html
new file mode 100644
index 0000000000..824dbc4b92
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/event-loops/microtask_after_raf.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<head>
+<link rel=author title="Aleks Totic" href="mailto:atotic@chromium.org">
+<link rel=help href="https://html.spec.whatwg.org/#clean-up-after-running-script">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/common.js"></script>
+</head>
+<body style="height:2000px;">
+<script>
+/*
+promise 1, promise 2 execute immediately after rAF
+promise 1 child executes immediately after promise 2.
+
+Relevant specs:
+
+https://html.spec.whatwg.org/#clean-up-after-running-script
+If the JavaScript execution context stack is now empty, perform a microtask checkpoint.
+
+https://html.spec.whatwg.org/#perform-a-microtask-checkpoint
+"perform a microtask checkpoint" runs in a loop until all microtasks have been delivered.
+*/
+
+var test = async_test("Microtask execute immediately after script");
+
+window.requestAnimationFrame( function() {
+ var events = [];
+
+ Promise.resolve()
+ .then(function() {
+ events.push("promise 1");
+ return Promise.resolve();
+ })
+ .then(function() {
+ test.step(function() {
+ events.push("promise 1 child");
+ assert_array_equals(events, ["promise 1", "promise 2", "promise 1 child"]);
+ test.done();
+ });
+ });
+ Promise.resolve()
+ .then(function() {
+ events.push("promise 2");
+ });
+
+ // Set up events that must be executed after Promise.
+ window.setTimeout(function() {
+ events.push('timeout');
+ }, 0);
+ window.addEventListener('scroll', function() {
+ events.push('scroll');
+ });
+ window.scrollBy(0,10);
+
+});
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/event-loops/microtask_after_script.html b/testing/web-platform/tests/html/webappapis/scripting/event-loops/microtask_after_script.html
new file mode 100644
index 0000000000..799a0de605
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/event-loops/microtask_after_script.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<head>
+<link rel=author title="Aleks Totic" href="mailto:atotic@chromium.org">
+<link rel=help href="https://html.spec.whatwg.org/#clean-up-after-running-script">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/common.js"></script>
+</head>
+<body style="height:2000px;">
+<script>
+/*
+promise 1, promise 2 execute immediately after script tag
+promise 1 child executes immediately after promise 2.
+
+Relevant specs:
+
+https://html.spec.whatwg.org/#clean-up-after-running-script
+If the JavaScript execution context stack is now empty, perform a microtask checkpoint.
+
+https://html.spec.whatwg.org/#perform-a-microtask-checkpoint
+"perform a microtask checkpoint" runs in a loop until all microtasks have been delivered.
+*/
+
+var test = async_test("Microtask immediately after script");
+
+var events = [];
+
+Promise.resolve()
+.then(function() {
+ events.push("promise 1");
+ return Promise.resolve();
+})
+.then(function() {
+ test.step(function() {
+ events.push("promise 1 child");
+ assert_array_equals(events, ["promise 1", "promise 2", "promise 1 child"]);
+ test.done();
+ });
+});
+Promise.resolve()
+.then(function() {
+ events.push("promise 2");
+});
+
+// Set up events that must be executed after Promise.
+window.setTimeout(function() {
+ events.push('timeout');
+}, 0);
+window.addEventListener('scroll', function() {
+ events.push('scroll');
+});
+window.scrollBy(0,10);
+
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/event-loops/resources/common.js b/testing/web-platform/tests/html/webappapis/scripting/event-loops/resources/common.js
new file mode 100644
index 0000000000..e2279f93dd
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/event-loops/resources/common.js
@@ -0,0 +1,20 @@
+// Helper for tests that just want to verify the ordering of a series of events.
+// Usage:
+// log_test(function(t, log) {
+// log('first');
+// log('second');
+// }, ['first', 'second'], 'Ordinal numbers are ordinal');
+
+function log_test(func, expected, description) {
+ async_test(function(t) {
+ var actual = [];
+ function log(entry) {
+ actual.push(entry);
+ if (expected.length <= actual.length) {
+ assert_array_equals(actual, expected);
+ t.done();
+ }
+ }
+ func(t, t.step_func(log));
+ }, description);
+}
diff --git a/testing/web-platform/tests/html/webappapis/scripting/event-loops/resources/iframe.html b/testing/web-platform/tests/html/webappapis/scripting/event-loops/resources/iframe.html
new file mode 100644
index 0000000000..32e4862360
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/event-loops/resources/iframe.html
@@ -0,0 +1,7 @@
+<!doctype html>
+<title>Childframe</title>
+<script>
+ window.addEventListener('storage', () => {
+ sessionStorage.setItem("Received storage event", true);
+ });
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/event-loops/resources/page-with-frame.html b/testing/web-platform/tests/html/webappapis/scripting/event-loops/resources/page-with-frame.html
new file mode 100644
index 0000000000..f13170576e
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/event-loops/resources/page-with-frame.html
@@ -0,0 +1 @@
+<iframe src="iframe.html"></iframe>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/event-loops/task_microtask_ordering-manual.html b/testing/web-platform/tests/html/webappapis/scripting/event-loops/task_microtask_ordering-manual.html
new file mode 100644
index 0000000000..ed2f70e196
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/event-loops/task_microtask_ordering-manual.html
@@ -0,0 +1,64 @@
+<!DOCTYPE html>
+<title>Task and Microtask Ordering </title>
+<link rel=author title="Joshua Bell" href="mailto:jsbell@google.com">
+<link rel=help href="https://html.spec.whatwg.org/multipage/#event-loops">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/common.js"></script>
+<style>
+.inner { padding: 46px; width: 0; margin: 0 auto; background: #d4d4d4; }
+.outer { padding: 25px; width: 92px; background: #f1f1f1; }
+</style>
+
+<p>Click on the inner box:</p>
+<div class="outer">
+ <div class="inner"></div>
+</div>
+
+<script>
+
+// Based on: https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/
+
+log_test(function(t, log) {
+ // Let's get hold of those elements
+ var outer = document.querySelector('.outer');
+ var inner = document.querySelector('.inner');
+
+ // Let's listen for attribute changes on the
+ // outer element
+ new MutationObserver(function() {
+ log('mutate');
+ }).observe(outer, {
+ attributes: true
+ });
+
+ // Here's a click listener...
+ function onClick() {
+ log('click');
+
+ setTimeout(function() {
+ log('timeout');
+ }, 0);
+
+ Promise.resolve().then(function() {
+ log('promise');
+ });
+
+ outer.setAttribute('data-random', Math.random());
+ }
+
+ // ...which we'll attach to both elements
+ inner.addEventListener('click', onClick);
+ outer.addEventListener('click', onClick);
+}, [
+ 'click',
+ 'promise',
+ 'mutate',
+ 'click',
+ 'promise',
+ 'mutate',
+ 'timeout',
+ 'timeout'
+], 'Level 1 bossfight (manual click)');
+
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/event-loops/task_microtask_ordering.html b/testing/web-platform/tests/html/webappapis/scripting/event-loops/task_microtask_ordering.html
new file mode 100644
index 0000000000..c14a043b6a
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/event-loops/task_microtask_ordering.html
@@ -0,0 +1,85 @@
+<!DOCTYPE html>
+<title>Task and Microtask Ordering </title>
+<link rel=author title="Joshua Bell" href="mailto:jsbell@google.com">
+<link rel=help href="https://html.spec.whatwg.org/multipage/#event-loops">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/common.js"></script>
+
+<div class="outer">
+ <div class="inner"></div>
+</div>
+
+<script>
+
+// Based on: https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/
+
+log_test(function(t, log) {
+ log('script start');
+
+ setTimeout(function() {
+ log('setTimeout');
+ }, 0);
+
+ Promise.resolve().then(function() {
+ log('promise1');
+ }).then(function() {
+ log('promise2');
+ });
+
+ log('script end');
+}, [
+ 'script start',
+ 'script end',
+ 'promise1',
+ 'promise2',
+ 'setTimeout'
+], 'Basic task and microtask ordering');
+
+log_test(function(t, log) {
+ // Let's get hold of those elements
+ var outer = document.querySelector('.outer');
+ var inner = document.querySelector('.inner');
+
+ // Let's listen for attribute changes on the
+ // outer element
+ new MutationObserver(function() {
+ log('mutate');
+ }).observe(outer, {
+ attributes: true
+ });
+
+ // Here's a click listener...
+ function onClick() {
+ log('click');
+
+ setTimeout(function() {
+ log('timeout');
+ }, 0);
+
+ Promise.resolve().then(function() {
+ log('promise');
+ });
+
+ outer.setAttribute('data-random', Math.random());
+ }
+
+ // ...which we'll attach to both elements
+ inner.addEventListener('click', onClick);
+ outer.addEventListener('click', onClick);
+
+ // Note that this will behave differently than a real click,
+ // since the dispatch is synchronous and microtasks will not
+ // run between event bubbling steps.
+ inner.click();
+}, [
+ 'click',
+ 'click',
+ 'promise',
+ 'mutate',
+ 'promise',
+ 'timeout',
+ 'timeout'
+], 'Level 1 bossfight (synthetic click)');
+
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/body-onload.html b/testing/web-platform/tests/html/webappapis/scripting/events/body-onload.html
new file mode 100644
index 0000000000..1e43d1ccd4
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/body-onload.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<title>HTMLBodyElement.onload</title>
+<link rel="author" title="Boris Zbarsky" href="mailto:bzbarsky@mit.edu">
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#handler-window-onload">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+var t = async_test("body.onload should set the window.onload handler")
+window.onload = t.step_func(function() {
+ assert_unreached("This handler should be overwritten.")
+})
+var b = document.createElement("body")
+b.onload = t.step_func(function(e) {
+ assert_equals(e.currentTarget, window,
+ "The event should be fired at the window.")
+ t.done()
+})
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/compile-event-handler-lexical-scopes-form-owner.html b/testing/web-platform/tests/html/webappapis/scripting/events/compile-event-handler-lexical-scopes-form-owner.html
new file mode 100644
index 0000000000..e31bd2496a
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/compile-event-handler-lexical-scopes-form-owner.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Form's lexical scope is established only for form-associated elements</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/forms.html#form-associated-element">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/webappapis.html#getting-the-current-value-of-the-event-handler">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<form id="form">
+ <input onclick="window.inputOnClickElements = elements;">
+ <img onclick="window.imgOnClickElements = elements;" alt="img">
+ <div onclick="window.divOnClickElements = elements;">div</div>
+ <x-foo onclick="window.xFooOnClickElements = elements;">x-foo</x-foo>
+</form>
+
+<script>
+"use strict";
+
+window.elements = "global_elements";
+
+test(() => {
+ const input = form.querySelector("input");
+ input.click();
+ assert_equals(window.inputOnClickElements, form.elements);
+}, "<input> has a form owner");
+
+test(() => {
+ const img = form.querySelector("img");
+ img.click();
+ assert_equals(window.imgOnClickElements, form.elements);
+}, "<img> has a form owner");
+
+test(() => {
+ const div = form.querySelector("div");
+ div.click();
+ assert_equals(window.divOnClickElements, window.elements);
+}, "<div> doesn't have a form owner");
+
+test(() => {
+ customElements.define("x-foo", class extends HTMLElement {
+ static formAssociated = true;
+ });
+
+ const xFoo = form.querySelector("x-foo");
+ xFoo.click();
+ assert_equals(window.xFooOnClickElements, form.elements);
+}, "form-associated <x-foo> has a form owner");
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/compile-event-handler-lexical-scopes.html b/testing/web-platform/tests/html/webappapis/scripting/events/compile-event-handler-lexical-scopes.html
new file mode 100644
index 0000000000..ed6c006651
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/compile-event-handler-lexical-scopes.html
@@ -0,0 +1,167 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Lexical scopes when compiling an inline event handler</title>
+<link rel="help" href="https://html.spec.whatwg.org/C/#getting-the-current-value-of-the-event-handler">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+setup({allow_uncaught_exception: true});
+</script>
+
+<!-- test case 1: element, document, and window -->
+
+<table>
+ <tr id="test1_outer">
+ <td id="test1_target" onclick="
+window.testResults.complete = typeof(complete);
+window.testResults.cellIndex = typeof(cellIndex);
+window.testResults.cells = typeof(cells);
+window.testResults.domain = typeof(domain);
+window.testResults.print = typeof(print);
+window.testResults.testResults = typeof(testResults);
+window.testResults.target_own_property = typeof(target_own_property);
+window.testResults.inner_own_property = typeof(inner_own_property);
+window.testResults.outer_own_property = typeof(outer_own_property);
+window.testResults.event = typeof(event);
+">
+ <img id="test1_inner">
+ </td>
+ </tr>
+</table>
+
+<script>
+"use strict";
+
+test(() => {
+ const target_element = document.getElementById("test1_target");
+ const inner_element = document.getElementById("test1_inner");
+ const outer_element = document.getElementById("test1_outer");
+
+ target_element.target_own_property = {};
+ inner_element.inner_own_property = {};
+ outer_element.outer_own_property = {};
+
+ const results = window.testResults = {};
+ // Clicking an inner element makes the event target where the event handler is
+ // registered doesn't match the event target that the event is fired. From a
+ // point of view |target_element|, event.target != event.currentTarget.
+ inner_element.click();
+ // Expected scopes are: |target_element|, document, and window.
+ assert_equals(results.complete, "undefined", "HTMLImageElement.prototype.complete");
+ assert_equals(results.cellIndex, "number", "HTMLTableCellElement.prototype.cellIndex");
+ assert_equals(results.cells, "undefined", "HTMLTableRowElement.prototype.cellIndex");
+ assert_equals(results.domain, "string", "Document.prototype.domain");
+ assert_equals(results.print, "function", "window.print");
+ assert_equals(results.testResults, "object");
+ assert_equals(results.target_own_property, "object");
+ assert_equals(results.inner_own_property, "undefined");
+ assert_equals(results.outer_own_property, "undefined");
+ assert_equals(results.event, "object", "The argument of event handler");
+}, "The EventHandler is an element's event handler and has no form owner.");
+</script>
+
+
+<!-- test case 2: element, form owner, document, and window -->
+
+<form id="test2_form_owner" onsubmit="return false;">
+ <!-- 'button' is a form-associated element and has a form owner.
+ https://html.spec.whatwg.org/C/#form-associated-element
+ -->
+ <button id="test2_target" onclick="
+window.testResults.cite = typeof(cite);
+window.testResults.autofocus = typeof(autofocus);
+window.testResults.form = typeof(form);
+window.testResults.encoding = typeof(encoding);
+window.testResults.domain = typeof(domain);
+window.testResults.print = typeof(print);
+window.testResults.testResults = typeof(testResults);
+window.testResults.target_own_property = typeof(target_own_property);
+window.testResults.inner_own_property = typeof(inner_own_property);
+window.testResults.form_owner_own_property = typeof(form_owner_own_property);
+window.testResults.event = typeof(event);
+">
+ <q id="test2_inner"></q>
+ </button>
+</form>
+
+<script>
+"use strict";
+
+test(() => {
+ const target_element = document.getElementById("test2_target");
+ const inner_element = document.getElementById("test2_inner");
+ const form_owner_element = document.getElementById("test2_form_owner");
+
+ target_element.target_own_property = {};
+ inner_element.inner_own_property = {};
+ form_owner_element.form_owner_own_property = {};
+
+ const results = window.testResults = {};
+ // Clicking an inner element makes the event target where the event handler is
+ // registered doesn't match the event target that the event is fired. From a
+ // point of view |target_element|, event.target != event.currentTarget.
+ inner_element.click();
+ // Expected scopes are: |target_element|, form owner, document, and window.
+ assert_equals(results.cite, "undefined", "HTMLQuoteElement.prototype.cite");
+ assert_equals(results.autofocus, "boolean", "HTMLButtonElement.prototype.autofocus");
+ assert_equals(results.form, "object", "HTMLButtonElement.prototype.form");
+ assert_equals(results.encoding, "string", "HTMLFormElement.prototype.encoding");
+ assert_equals(results.domain, "string", "Document.prototype.domain");
+ assert_equals(results.print, "function", "window.print");
+ assert_equals(results.testResults, "object");
+ assert_equals(results.target_own_property, "object");
+ assert_equals(results.inner_own_property, "undefined");
+ assert_equals(results.form_owner_own_property, "object");
+ assert_equals(results.event, "object", "The argument of event handler");
+}, "The EventHandler is an element's event handler and has a form owner.");
+</script>
+
+
+<!-- test case 3: element and window -->
+
+<a id="test3_inner"></a>
+
+<script>
+"use strict";
+
+// This test is placed at last so that it can safely use a global variable
+// without conflicting other tests. Only this test is asynchronous.
+async_test(t => {
+ const target_element = window;
+ const inner_element = document.getElementById("test3_inner");
+
+ target_element.target_own_property = {};
+ inner_element.inner_own_property = {};
+ document.body.body_own_property = {};
+
+ // "onerror" is one of the Window-reflecting body element event handler set.
+ // https://html.spec.whatwg.org/C/#window-reflecting-body-element-event-handler-set
+ // So, the EventHandler is treated as a Window's event handler.
+ document.body.setAttribute("onerror", "\
+window.testResults.ping = typeof(ping); \
+window.testResults.domain = typeof(domain); \
+window.testResults.print = typeof(print); \
+window.testResults.testResults = typeof(testResults); \
+window.testResults.target_own_property = typeof(target_own_property); \
+window.testResults.inner_own_property = typeof(inner_own_property); \
+window.testResults.body_own_property = typeof(body_own_property); \
+window.testResults.event = typeof(event); \
+");
+
+ const results = window.testResults = {};
+ window.addEventListener("error", t.step_func_done(() => {
+ // Expected scopes are: |target_element| and window only.
+ assert_equals(results.domain, "undefined", "Document.prototype.domain");
+ assert_equals(results.print, "function", "window.print");
+ assert_equals(results.testResults, "object");
+ assert_equals(results.target_own_property, "object");
+ assert_equals(results.inner_own_property, "undefined");
+ assert_in_array(results.event, ["object", "string"], "The first argument of onerror event handler");
+ }));
+
+ // Make a compilation error happen in order to invoke onerror event handler.
+ inner_element.setAttribute("onclick", "cause a compilation error");
+ inner_element.click();
+}, "The EventHandler is not an element's event handler (i.e. Window's event handler) and has no form owner.");
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/compile-event-handler-settings-objects.html b/testing/web-platform/tests/html/webappapis/scripting/events/compile-event-handler-settings-objects.html
new file mode 100644
index 0000000000..29ac9b8ced
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/compile-event-handler-settings-objects.html
@@ -0,0 +1,69 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Entry and incumbent settings objects when compiling an inline event handler</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="log"></div>
+
+<script>
+"use strict";
+window.name = "parent frame";
+
+async_test(t => {
+ const iframe = document.createElement("iframe");
+ iframe.src = "resources/compiled-event-handler-settings-objects-support.html";
+ iframe.onload = t.step_func(() => {
+ const button = iframe.contentDocument.querySelector("button");
+ const compiled = button.onclick;
+
+ assert_equals(compiled.constructor, iframe.contentWindow.Function, "The constructor must be from the iframe");
+ assert_not_equals(compiled.constructor, Function, "The constructor must not be from this page");
+
+ t.done();
+ });
+
+ document.body.appendChild(iframe);
+
+}, "The Function instance must be created in the Realm of the node document");
+
+async_test(t => {
+ const iframe = document.createElement("iframe");
+ iframe.src = "resources/compiled-event-handler-settings-objects-support.html";
+ iframe.onload = t.step_func(() => {
+ const button = iframe.contentDocument.querySelector("button");
+
+ window.onWindowLoaded = t.step_func_done(url => {
+ const pathname = new URL(url).pathname;
+ assert_equals(pathname,
+ "/html/webappapis/scripting/events/resources/open-window.html");
+ // This tests that the entry settings object used to resolve URLs in that window.open() was the same as that
+ // of the node document (i.e. the iframe document), not e.g. this window.
+ });
+
+ button.click();
+ });
+
+ document.body.appendChild(iframe);
+
+}, "The entry settings object while executing the compiled callback via Web IDL's invoke must be that " +
+ "of the node document");
+
+async_test(t => {
+ const iframe = document.createElement("iframe");
+ iframe.src = "resources/compiled-event-handler-settings-objects-support.html";
+ iframe.onload = t.step_func(() => {
+ window.onmessage = t.step_func_done(event => {
+ assert_equals(event.data, "PASS");
+ assert_equals(event.source.name, "iframe");
+ assert_equals(event.source, iframe.contentWindow, "The source must be the iframe");
+ });
+
+ iframe.src = "about:blank";
+ });
+
+ document.body.appendChild(iframe);
+
+}, "The incumbent settings object while executing the compiled callback via Web IDL's invoke must be that " +
+ "of the node document");
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/compile-event-handler-symbol-unscopables.html b/testing/web-platform/tests/html/webappapis/scripting/events/compile-event-handler-symbol-unscopables.html
new file mode 100644
index 0000000000..c840059e68
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/compile-event-handler-symbol-unscopables.html
@@ -0,0 +1,71 @@
+<!DOCTYPE html>
+<meta charset="UTF-8" />
+<title>Inline event handler scopes exclude unscopable properties</title>
+<link rel="author" title="ExE Boss" href="https://ExE-Boss.tech" />
+<link rel="help" href="https://html.spec.whatwg.org/multipage/webappapis.html#getting-the-current-value-of-the-event-handler" />
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+ "use strict";
+ window.testVariable = {};
+</script>
+
+<!-- test case 1: element, document, and window -->
+<div id="test1_target" onclick='
+ "use strict";
+
+ window.testResults.testVariable = testVariable;
+'></div>
+
+<script>
+ "use strict";
+
+ test(() => {
+ const results = window.testResults = {};
+
+ document[Symbol.unscopables].testVariable = true;
+ document.testVariable = "FAIL (document)";
+
+ document.getElementById("test1_target").click();
+ assert_equals(results.testVariable, window.testVariable);
+ }, "unscopable `document.testVariable` doesn't shadow `window.testVariable`");
+
+ test(() => {
+ const results = window.testResults = {};
+ const element = document.getElementById("test1_target");
+
+ element[Symbol.unscopables].testVariable = true;
+ element.testVariable = "FAIL (element)";
+
+ element.click();
+ assert_equals(results.testVariable, window.testVariable);
+ }, "unscopable `element.testVariable` doesn't shadow `window.testVariable`");
+</script>
+
+<!-- test case 2: element, form owner, document, and window -->
+<form id="test2_form_owner" onsubmit="return false;">
+ <!-- <button> is a form-associated element and has a form owner.
+ https://html.spec.whatwg.org/C/#form-associated-element -->
+ <button id="test2_target" onclick='
+ "use strict";
+
+ window.testResults.testVariable = testVariable;
+ '></button>
+</form>
+
+<script>
+ "use strict";
+
+ test(() => {
+ const results = window.testResults = {};
+ const element = document.getElementById("test2_target");
+ const formOwner = document.getElementById("test2_form_owner");
+
+ formOwner[Symbol.unscopables].testVariable = true;
+ formOwner.testVariable = "FAIL (formOwner)";
+
+ element.click();
+ assert_equals(results.testVariable, window.testVariable);
+ }, "unscopable `formOwner.testVariable` doesn't shadow `window.testVariable`")
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/contextmenu-event-manual.htm b/testing/web-platform/tests/html/webappapis/scripting/events/contextmenu-event-manual.htm
new file mode 100644
index 0000000000..2331fa17ee
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/contextmenu-event-manual.htm
@@ -0,0 +1,21 @@
+<!doctype html>
+<html>
+ <head>
+ <title>HTML contextmenu event is a MouseEvent</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <style>#contextmenutarget { width: 100px; height: 100px; background-color: red; }</style>
+ </head>
+ <body>
+ <div id='contextmenutarget'>Trigger context menu in this box.</div>
+ <div id="log"></div>
+ <script type="text/javascript">
+var t = async_test('contextmenu event generated from user action is MouseEvent');
+document.querySelector("#contextmenutarget").addEventListener('contextmenu', t.step_func(function (e) {
+ assert_equals(e.constructor, window.MouseEvent);
+ document.querySelector("#contextmenutarget").style.backgroundColor = "green";
+ t.done();
+}));
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-all-global-events.html b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-all-global-events.html
new file mode 100644
index 0000000000..ee8c34ced3
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-all-global-events.html
@@ -0,0 +1,88 @@
+<!DOCTYPE html>
+<title>GlobalEventHandlers</title>
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#globaleventhandlers">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#event-handler-idl-attributes">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#event-handler-content-attributes">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/WebIDLParser.js"></script>
+
+<script>
+"use strict";
+
+// The prefixed animation events are special; their event types are
+// camel-case.
+const prefixedAnimationAttributeToEventType = new Map([
+ ["webkitanimationend", "webkitAnimationEnd"],
+ ["webkitanimationiteration", "webkitAnimationIteration"],
+ ["webkitanimationstart", "webkitAnimationStart"],
+ ["webkittransitionend", "webkitTransitionEnd"],
+]);
+
+setup({ explicit_done: true });
+
+fetch("/interfaces/html.idl").then(res => res.text()).then(htmlIDL => {
+ const parsedHTMLIDL = WebIDL2.parse(htmlIDL);
+ const globalEventHandlers = parsedHTMLIDL.find(idl => idl.name === "GlobalEventHandlers");
+
+ // onerror is too special
+ const names = globalEventHandlers.members.map(member => member.name).filter(name => name !== "onerror");
+
+ for (const name of names) {
+ const withoutOn = name.substring(2);
+
+ test(() => {
+ for (const location of [window, HTMLElement.prototype, SVGElement.prototype, Document.prototype]) {
+ assert_true(location.hasOwnProperty(name),
+ `${location.constructor.name} has an own property named "${name}"`);
+ }
+ assert_false(name in Element.prototype, `Element.prototype must not contain a "${name}" property`);
+ }, `${name}: must be on the appropriate locations for GlobalEventHandlers`);
+
+ test(() => {
+ const htmlElement = document.createElement("span");
+ const svgElement = document.createElementNS("http://www.w3.org/2000/svg", "g");
+
+ for (var location of [window, htmlElement, svgElement, document]) {
+ assert_equals(location[name], null,
+ `The default value of the property is null for a ${location.constructor.name} instance`);
+ }
+ }, `${name}: the default value must be null`);
+
+ test(() => {
+ const el = document.createElement("div");
+ el.setAttribute(name, `window.${name}Happened = true;`);
+ const compiledHandler = el[name];
+
+ assert_equals(typeof compiledHandler, "function", `The ${name} property must be a function`);
+ compiledHandler();
+ assert_true(window[name + "Happened"], "Calling the handler must run the code");
+ }, `${name}: the content attribute must be compiled into a function as the corresponding property`);
+
+ test(() => {
+ const el = document.createElement("div");
+ el.setAttribute(name, `window.${name}Happened2 = true;`);
+
+ let eventType = withoutOn;
+ if (prefixedAnimationAttributeToEventType.has(eventType)) {
+ eventType = prefixedAnimationAttributeToEventType.get(eventType);
+ }
+ el.dispatchEvent(new Event(eventType));
+
+ assert_true(window[name + "Happened2"], "Dispatching an event must run the code");
+ }, `${name}: the content attribute must execute when an event is dispatched`);
+
+ test(() => {
+ const element = document.createElement("meta");
+ element[name] = e => {
+ assert_equals(e.currentTarget, element, "The event must be fired at the <meta> element");
+ };
+
+ element.dispatchEvent(new Event(withoutOn));
+ }, `${name}: dispatching an Event at a <meta> element must trigger element.${name}`);
+ }
+
+ done();
+});
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-attributes-body-window.html b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-attributes-body-window.html
new file mode 100644
index 0000000000..e8055d99f3
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-attributes-body-window.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<title>HTMLBodyElement event handlers</title>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/WebIDLParser.js"></script>
+<script src="resources/event-handler-body.js"></script>
+<div id="log"></div>
+<body>
+<script>
+setup({ explicit_done: true });
+
+handlersListPromise.then(({ shadowedHandlers, notShadowedHandlers }) => {
+ eventHandlerTest(shadowedHandlers, notShadowedHandlers, "body");
+
+ done();
+});
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-attributes-frameset-window.html b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-attributes-frameset-window.html
new file mode 100644
index 0000000000..b583eca52d
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-attributes-frameset-window.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>event handlers</title>
+<link rel="author" title="Intel" href="http://www.intel.com">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/WebIDLParser.js"></script>
+<script src="resources/event-handler-body.js"></script>
+<script>
+setup({ explicit_done: true });
+
+handlersListPromise.then(({ shadowedHandlers, notShadowedHandlers }) => {
+ eventHandlerTest(shadowedHandlers, notShadowedHandlers, "frameset");
+
+ // The testharness framework appends test results to document.body,
+ // show test results in frame after test done.
+ add_completion_callback(() => {
+ const log_elem = document.getElementById("log");
+ const frame_elem = document.querySelector("frame");
+ if (log_elem) {
+ frame_elem.contentDocument.body.innerHTML = log_elem.innerHTML;
+ }
+ });
+
+ done();
+});
+</script>
+<frameset>
+ <frame src="/common/blank.html" />
+</frameset>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-attributes-windowless-body.html b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-attributes-windowless-body.html
new file mode 100644
index 0000000000..9b81d42ff7
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-attributes-windowless-body.html
@@ -0,0 +1,71 @@
+<!doctype html>
+<meta charset="utf-8">
+<title></title>
+<body></body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/WebIDLParser.js"></script>
+<script src="resources/event-handler-body.js"></script>
+<script>
+setup({ explicit_done: true });
+const elements = ['body', 'frameset'];
+handlersListPromise.then(({ shadowedHandlers, notShadowedHandlers }) => {
+ elements.forEach(function (elementName) {
+ shadowedHandlers.forEach(function (eventName) {
+ var handlerName = "on" + eventName;
+
+ test(function() {
+ var windowHandler = function () { return "Handler attached to the window"; };
+ window[handlerName] = windowHandler;
+
+ var d = (new DOMParser).parseFromString('', 'text/html');
+ var b = d.createElement(elementName);
+
+ assert_equals(b[handlerName], null);
+
+ window[handlerName] = null;
+ }, "Return null when getting the " + eventName + " event handler of a windowless " + elementName);
+
+ test(function() {
+ var windowHandler = function () { return "Handler attached to the window"; };
+ window[handlerName] = windowHandler;
+
+ var d = (new DOMParser).parseFromString('', 'text/html');
+ var b = d.createElement(elementName);
+ b[handlerName] = function() { return "Handler attached to windowless element"; };
+
+ assert_equals(window[handlerName], windowHandler);
+ assert_equals(b[handlerName], null);
+
+ // Clean up window event handler
+ window[handlerName] = null;
+ }, "Ignore setting of " + eventName + " window event handlers on windowless " + elementName);
+ });
+
+ notShadowedHandlers.forEach(function (eventName) {
+ var handlerName = "on" + eventName;
+
+ test(function() {
+ var windowHandler = function () { return "Handler attached to the window"; };
+ window[handlerName] = windowHandler;
+
+ var d = (new DOMParser).parseFromString('', 'text/html');
+ var b = d.createElement(elementName);
+
+ assert_equals(b[handlerName], null);
+
+ var elementHandler = function () { return "Handler attached to the element"; };
+ b[handlerName] = elementHandler;
+
+ assert_equals(window[handlerName], windowHandler);
+ assert_equals(b[handlerName], elementHandler);
+
+ // Clean up window event handler
+ window[handlerName] = null;
+ }, eventName + " is unaffected on a windowless " + elementName);
+ });
+ });
+
+ done();
+});
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-handleEvent-ignored.html b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-handleEvent-ignored.html
new file mode 100644
index 0000000000..8039bac7ad
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-handleEvent-ignored.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>"handleEvent" property of EventHandler should be ignored</title>
+<link rel="help" href="https://html.spec.whatwg.org/#eventhandler">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+"use strict";
+
+test(t => {
+ const handler = Object.create(null, {
+ handleEvent: {
+ get: t.unreached_func('"handleEvent" property should not be looked up'),
+ },
+ });
+
+ const el = document.createElement("div");
+ el.onmouseenter = handler;
+ el.dispatchEvent(new MouseEvent("mouseenter"));
+}, 'plain object "mouseenter" handler');
+
+async_test(t => {
+ const handler = Object.create(Function.prototype, {
+ handleEvent: {
+ get: t.unreached_func('"handleEvent" property should not be looked up'),
+ },
+ });
+ assert_true(handler instanceof Function);
+
+ window.onmessage = handler;
+ window.postMessage({}, "*");
+
+ step_timeout(() => {
+ t.done();
+ }, 50);
+}, 'non-callable "message" handler that is instance of Function');
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-javascript.html b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-javascript.html
new file mode 100644
index 0000000000..657a37839d
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-javascript.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<title>Event handler with labels</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body onload="javascript:
+ for (var i = 0; i < 2; ++i) {
+ for (var j = 0; j < 2; ++j) {
+ t.step(function() {
+ assert_equals(i, 0);
+ assert_equals(j, 0);
+ });
+ break javascript;
+ }
+ }
+ t.done();
+">
+<div id="log"></div>
+<script>
+var t = async_test("Event handlers starting with 'javascript:' should treat that as a label.");
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-onmove-01.tentative.html b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-onmove-01.tentative.html
new file mode 100644
index 0000000000..e4454a20a1
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-onmove-01.tentative.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<title>Window Object 'onmove'</title>
+<link rel="author" title="Sonja Laurila" href="mailto:laurila@google.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#handler-window-onmove">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<script>
+promise_test(async t => {
+ var onMoveCalled = false;
+ window.onmove = (event) => { onMoveCalled = true; }
+ window.dispatchEvent(new Event('move'));
+ assert_true(onMoveCalled, "The onMove handler has been executed.");
+}, "Window move event");
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-onmove-02.tentative.html b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-onmove-02.tentative.html
new file mode 100644
index 0000000000..a751f39d30
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-onmove-02.tentative.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<title>Window Object 'onmove'</title>
+<link rel="author" title="Javier Fernandez" href="mailto:jfernandez@igalia.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#handler-window-onmove">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<script>
+promise_test((t) => {
+ var expectedRect = {'x': 100, 'y': 200};
+ return new Promise(resolve => {
+ window.addEventListener("move", resolve);
+ test_driver.set_window_rect(expectedRect);
+ }).then(event => {
+ return test_driver.get_window_rect()
+ .then(rect => {
+ assert_equals(rect.x, expectedRect.x, "The window rect X is correct.")
+ assert_equals(rect.y, expectedRect.y, "The window rect Y is correct.")
+ });
+ });
+}, "Window move event");
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-onresize.html b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-onresize.html
new file mode 100644
index 0000000000..0e44e7272f
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-onresize.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<title>HTMLBodyElement.onresize</title>
+<link rel="author" title="His-Name-Is-Joof" href="mailto:jeffrharrison@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#handler-window-onresize">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+var t = async_test("body.onresize should set the window.onresize handler")
+window.onresize = t.step_func(function() {
+ assert_unreached("This handler should be overwritten.")
+})
+
+var body = document.createElement("body")
+body.onresize = t.step_func(function(e) {
+ assert_equals(e.currentTarget, window,
+ "The event should be fired at the window.")
+ t.done()
+})
+window.dispatchEvent(new Event('resize'));
+
+t = async_test("document.onresize should set the document.onresize handler");
+document.onresize = t.step_func(function(e) {
+ assert_equals(e.currentTarget, document,
+ "The event should be fired at the document")
+ t.done()
+})
+document.dispatchEvent(new Event('resize'));
+
+t = async_test("meta.onresize should set the meta.onresize handler");
+var meta = document.createElement("meta")
+meta.onresize = t.step_func(function(e) {
+ assert_equals(e.currentTarget, meta,
+ "The event should be fired at the <meta> object")
+ t.done()
+})
+meta.dispatchEvent(new Event('resize'));
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/body-element-synthetic-errorevent.html b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/body-element-synthetic-errorevent.html
new file mode 100644
index 0000000000..9ab0020ec3
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/body-element-synthetic-errorevent.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Event handlers processing algorithm: error events</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-event-handler-processing-algorithm">
+
+<div id="log"></div>
+
+<script>
+"use strict";
+setup({ allow_uncaught_exception: true });
+
+promise_test(t => {
+ document.body.onerror = t.step_func((...args) => {
+ assert_greater_than(args.length, 1);
+ return true;
+ });
+
+ const eventWatcher = new EventWatcher(t, window, "error");
+ const promise = eventWatcher.wait_for("error").then(e => {
+ assert_equals(e.defaultPrevented, true);
+ });
+
+ document.body.dispatchEvent(new ErrorEvent("error", { bubbles: true, cancelable: true }));
+
+ return promise;
+}, "error event is weird (return true cancels; many args) on Window, with a synthetic ErrorEvent");
+
+promise_test(t => {
+ const theError = { the: "error object" };
+
+ document.body.onerror = t.step_func(function (message, filename, lineno, colno, error) {
+ assert_equals(arguments.length, 5, "There must be exactly 5 arguments");
+ assert_equals(message, "message");
+ assert_equals(filename, "filename");
+ assert_equals(lineno, 1);
+ assert_equals(colno, 2);
+ assert_equals(error, theError);
+ return true;
+ });
+
+ const eventWatcher = new EventWatcher(t, window, "error");
+ const promise = eventWatcher.wait_for("error");
+
+ document.body.dispatchEvent(new ErrorEvent("error", {
+ bubbles: true,
+ message: "message",
+ filename: "filename",
+ lineno: 1,
+ colno: 2,
+ error: theError
+ }));
+
+ return promise;
+}, "error event has the right 5 args on Window, with a synthetic ErrorEvent");
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/body-element-synthetic-event.html b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/body-element-synthetic-event.html
new file mode 100644
index 0000000000..9ed2638416
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/body-element-synthetic-event.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Event handlers processing algorithm: error events</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-event-handler-processing-algorithm">
+
+<div id="log"></div>
+
+<script>
+"use strict";
+setup({ allow_uncaught_exception: true });
+
+promise_test(t => {
+ document.body.onerror = t.step_func((...args) => {
+ assert_equals(args.length, 1);
+ return true;
+ });
+
+ const eventWatcher = new EventWatcher(t, window, "error");
+ const promise = eventWatcher.wait_for("error").then(e => {
+ assert_equals(e.defaultPrevented, false);
+ });
+
+ document.body.dispatchEvent(new Event("error", { bubbles: true, cancelable: true }));
+
+ return promise;
+}, "error event is normal (return true does not cancel; one arg) on Window, with a synthetic Event");
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/document-synthetic-errorevent.html b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/document-synthetic-errorevent.html
new file mode 100644
index 0000000000..4165beaf63
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/document-synthetic-errorevent.html
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Event handlers processing algorithm: error events</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-event-handler-processing-algorithm">
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+
+<div id="log"></div>
+
+<script>
+"use strict";
+setup({ allow_uncaught_exception: true });
+
+promise_test(t => {
+ document.onerror = t.step_func((...args) => {
+ assert_equals(args.length, 1);
+ return true;
+ });
+
+ const eventWatcher = new EventWatcher(t, document, "error");
+ const promise = eventWatcher.wait_for("error").then(e => {
+ assert_equals(e.defaultPrevented, false);
+ assert_equals(e.message, "");
+ assert_equals(e.filename, "");
+ assert_equals(e.lineno, 0);
+ assert_equals(e.colno, 0);
+ assert_equals(e.error, undefined);
+ });
+
+ document.dispatchEvent(new ErrorEvent("error", { cancelable: true }));
+
+ return promise;
+}, "error event is normal (return true does not cancel; one arg) on Document, with a synthetic ErrorEvent");
+
+test(() => {
+ const e = new ErrorEvent("error");
+ assert_equals(e.message, "");
+ assert_equals(e.filename, "");
+ assert_equals(e.lineno, 0);
+ assert_equals(e.colno, 0);
+ assert_equals(e.error, undefined);
+}, "Initial values of ErrorEvent members")
+
+test(() => {
+ const e = new ErrorEvent("error", {error : null});
+ assert_equals(e.error, null);
+}, "error member can be set to null")
+
+test(() => {
+ const e = new ErrorEvent("error", {error : undefined});
+ assert_equals(e.error, undefined);
+}, "error member can be set to undefined")
+
+test(() => {
+ const e = new ErrorEvent("error", {error : "foo"});
+ assert_equals(e.error, "foo");
+}, "error member can be set to arbitrary")
+
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/document-synthetic-event.html b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/document-synthetic-event.html
new file mode 100644
index 0000000000..6cf44e9d35
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/document-synthetic-event.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Event handlers processing algorithm: error events</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-event-handler-processing-algorithm">
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+
+<div id="log"></div>
+
+<script>
+"use strict";
+setup({ allow_uncaught_exception: true });
+
+promise_test(t => {
+ document.onerror = t.step_func((...args) => {
+ assert_equals(args.length, 1);
+ return true;
+ });
+
+ const eventWatcher = new EventWatcher(t, document, "error");
+ const promise = eventWatcher.wait_for("error").then(e => {
+ assert_equals(e.defaultPrevented, false);
+ });
+
+ document.dispatchEvent(new Event("error", { cancelable: true }));
+
+ return promise;
+}, "error event is normal (return true does not cancel; one arg) on Document, with a synthetic Event");
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/frameset-element-synthetic-errorevent.html b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/frameset-element-synthetic-errorevent.html
new file mode 100644
index 0000000000..20d87dbacf
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/frameset-element-synthetic-errorevent.html
@@ -0,0 +1,64 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Event handlers processing algorithm: error events</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-event-handler-processing-algorithm">
+
+<iframe name="framesetWindow" src="resources/frameset-frame.html"></iframe>
+<div id="log"></div>
+
+<script>
+"use strict";
+setup({ allow_uncaught_exception: true });
+
+window.onload = () => {
+
+const frameset = framesetWindow.document.querySelector("frameset");
+
+promise_test(t => {
+ frameset.onerror = t.step_func((...args) => {
+ assert_greater_than(args.length, 1);
+ return true;
+ });
+
+ const eventWatcher = new EventWatcher(t, framesetWindow, "error");
+ const promise = eventWatcher.wait_for("error").then(e => {
+ assert_equals(e.defaultPrevented, true);
+ });
+
+ frameset.dispatchEvent(new ErrorEvent("error", { bubbles: true, cancelable: true }));
+
+ return promise;
+}, "error event is weird (return true cancels; many args) on Window, with a synthetic ErrorEvent");
+
+promise_test(t => {
+ const theError = { the: "error object" };
+
+ frameset.onerror = t.step_func(function (message, filename, lineno, colno, error) {
+ assert_equals(arguments.length, 5, "There must be exactly 5 arguments");
+ assert_equals(message, "message");
+ assert_equals(filename, "filename");
+ assert_equals(lineno, 1);
+ assert_equals(colno, 2);
+ assert_equals(error, theError);
+ return true;
+ });
+
+ const eventWatcher = new EventWatcher(t, framesetWindow, "error");
+ const promise = eventWatcher.wait_for("error");
+
+ frameset.dispatchEvent(new ErrorEvent("error", {
+ bubbles: true,
+ message: "message",
+ filename: "filename",
+ lineno: 1,
+ colno: 2,
+ error: theError
+ }));
+
+ return promise;
+}, "error event has the right 5 args on Window, with a synthetic ErrorEvent");
+
+};
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/frameset-element-synthetic-event.html b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/frameset-element-synthetic-event.html
new file mode 100644
index 0000000000..2fdca3ad86
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/frameset-element-synthetic-event.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Event handlers processing algorithm: error events</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-event-handler-processing-algorithm">
+
+<iframe name="framesetWindow" src="resources/frameset-frame.html"></iframe>
+<div id="log"></div>
+
+<script>
+"use strict";
+setup({ allow_uncaught_exception: true });
+
+window.onload = () => {
+
+const frameset = framesetWindow.document.querySelector("frameset");
+
+promise_test(t => {
+ frameset.onerror = t.step_func((...args) => {
+ assert_equals(args.length, 1);
+ return true;
+ });
+
+ const eventWatcher = new EventWatcher(t, framesetWindow, "error");
+ const promise = eventWatcher.wait_for("error").then(e => {
+ assert_equals(e.defaultPrevented, false);
+ });
+
+ frameset.dispatchEvent(new Event("error", { bubbles: true, cancelable: true }));
+
+ return promise;
+}, "error event is normal (return true does not cancel; one arg) on Window, with a synthetic Event");
+
+};
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/resources/frameset-frame.html b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/resources/frameset-frame.html
new file mode 100644
index 0000000000..028be4919e
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/resources/frameset-frame.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+
+<frameset></frameset>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/resources/no-op-worker.js b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/resources/no-op-worker.js
new file mode 100644
index 0000000000..3918c74e44
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/resources/no-op-worker.js
@@ -0,0 +1 @@
+"use strict";
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/resources/worker-with-syntax-error.js b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/resources/worker-with-syntax-error.js
new file mode 100644
index 0000000000..dc9a0dbf4a
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/resources/worker-with-syntax-error.js
@@ -0,0 +1 @@
+< 3;
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/script-element.html b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/script-element.html
new file mode 100644
index 0000000000..f3ef1165e0
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/script-element.html
@@ -0,0 +1,67 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Event handlers processing algorithm: error events</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-event-handler-processing-algorithm">
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+
+<div id="log"></div>
+
+<script>
+"use strict";
+
+promise_test(t => {
+ const script = document.createElement("script");
+ script.onerror = t.step_func((...args) => {
+ assert_equals(args.length, 1);
+ return true;
+ });
+
+ const eventWatcher = new EventWatcher(t, script, "error");
+ const promise = eventWatcher.wait_for("error").then(e => {
+ assert_equals(e.constructor, Event); // not ErrorEvent
+ assert_equals(e.defaultPrevented, false);
+ });
+
+ script.src = "404.js";
+ document.body.appendChild(script);
+
+ return promise;
+}, "error event behaves normally (return true does not cancel; one arg) on a script element, with a 404 error");
+
+promise_test(t => {
+ const script = document.createElement("script");
+ script.onerror = t.step_func((...args) => {
+ assert_equals(args.length, 1);
+ return true;
+ });
+
+ const eventWatcher = new EventWatcher(t, script, "error");
+ const promise = eventWatcher.wait_for("error").then(e => {
+ assert_equals(e.defaultPrevented, false);
+ });
+
+ script.dispatchEvent(new Event("error", { cancelable: true }));
+
+ return promise;
+}, "error event behaves normally (return true does not cancel; one arg) on a script element, with a synthetic Event");
+
+promise_test(t => {
+ const script = document.createElement("script");
+ script.onerror = t.step_func((...args) => {
+ assert_equals(args.length, 1);
+ return true;
+ });
+
+ const eventWatcher = new EventWatcher(t, script, "error");
+ const promise = eventWatcher.wait_for("error").then(e => {
+ assert_equals(e.defaultPrevented, false);
+ });
+
+ script.dispatchEvent(new ErrorEvent("error", { cancelable: true }));
+
+ return promise;
+}, "error event behaves normally (return true does not cancel; one arg) on a script element, with a synthetic ErrorEvent");
+
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/synthetic-errorevent-click.html b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/synthetic-errorevent-click.html
new file mode 100644
index 0000000000..75a1772485
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/synthetic-errorevent-click.html
@@ -0,0 +1,78 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Event handlers processing algorithm: click events using ErrorEvent</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-event-handler-processing-algorithm">
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+
+<div id="log"></div>
+
+<script>
+"use strict";
+promise_test(t => {
+ document.onclick = t.step_func((...args) => {
+ assert_equals(args.length, 1);
+ return true;
+ });
+
+ const eventWatcher = new EventWatcher(t, document, "click");
+ const promise = eventWatcher.wait_for("click").then(e => {
+ assert_equals(e.defaultPrevented, false);
+ });
+
+ document.dispatchEvent(new ErrorEvent("click", { cancelable: true }));
+
+ return promise;
+}, "click event is normal (return true does not cancel; one arg) on Document, with a synthetic ErrorEvent");
+
+promise_test(t => {
+ window.onclick = t.step_func((...args) => {
+ assert_equals(args.length, 1);
+ return true;
+ });
+
+ const eventWatcher = new EventWatcher(t, window, "click");
+ const promise = eventWatcher.wait_for("click").then(e => {
+ assert_equals(e.defaultPrevented, false);
+ });
+
+ window.dispatchEvent(new ErrorEvent("click", { cancelable: true }));
+
+ return promise;
+}, "click event is normal (return true does not cancel; one arg) on Window, with a synthetic ErrorEvent");
+
+promise_test(t => {
+ const el = document.createElement("script");
+ el.onclick = t.step_func((...args) => {
+ assert_equals(args.length, 1);
+ return true;
+ });
+
+ const eventWatcher = new EventWatcher(t, el, "click");
+ const promise = eventWatcher.wait_for("click").then(e => {
+ assert_equals(e.defaultPrevented, false);
+ });
+
+ el.dispatchEvent(new ErrorEvent("click", { cancelable: true }));
+
+ return promise;
+}, "click event is normal (return true does not cancel; one arg) on a script element, with a synthetic ErrorEvent");
+
+promise_test(t => {
+ const worker = new Worker("resources/no-op-worker.js");
+ worker.onerror = t.step_func((...args) => {
+ assert_equals(args.length, 1);
+ return true;
+ });
+
+ const eventWatcher = new EventWatcher(t, worker, "click");
+ const promise = eventWatcher.wait_for("click").then(e => {
+ assert_equals(e.defaultPrevented, false);
+ });
+
+ worker.dispatchEvent(new ErrorEvent("click", { cancelable: true }));
+
+ return promise;
+}, "click event is normal (return true does not cancel; one arg) on Worker, with a synthetic ErrorEvent");
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/synthetic-errorevent-click.worker.js b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/synthetic-errorevent-click.worker.js
new file mode 100644
index 0000000000..177a99e2ce
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/synthetic-errorevent-click.worker.js
@@ -0,0 +1,22 @@
+"use strict";
+importScripts("/resources/testharness.js");
+
+setup({ allow_uncaught_exception: true });
+
+promise_test(t => {
+ self.onerror = t.step_func((...args) => {
+ assert_equals(args.length, 1);
+ return true;
+ });
+
+ const eventWatcher = new EventWatcher(t, self, "click");
+ const promise = eventWatcher.wait_for("click").then(e => {
+ assert_equals(e.defaultPrevented, false);
+ });
+
+ self.dispatchEvent(new ErrorEvent("click", { cancelable: true }));
+
+ return promise;
+}, "error event is normal (return true does not cancel; one arg) on WorkerGlobalScope, with a synthetic ErrorEvent");
+
+done();
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/window-runtime-error.html b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/window-runtime-error.html
new file mode 100644
index 0000000000..1b387ca81c
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/window-runtime-error.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Event handlers processing algorithm: error events</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-event-handler-processing-algorithm">
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+
+<div id="log"></div>
+
+<script>
+"use strict";
+setup({ allow_uncaught_exception: true });
+
+promise_test(t => {
+ window.onerror = t.step_func((...args) => {
+ assert_greater_than(args.length, 1);
+ return true;
+ });
+
+ const eventWatcher = new EventWatcher(t, window, "error");
+ const promise = eventWatcher.wait_for("error").then(e => {
+ assert_equals(e.defaultPrevented, true);
+ });
+
+ setTimeout(() => thisFunctionDoesNotExist(), 0);
+
+ return promise;
+}, "error event is weird (return true cancels; many args) on Window, with a runtime error");
+
+promise_test(t => {
+ window.onerror = t.step_func(function (message, filename, lineno, colno, error) {
+ assert_equals(arguments.length, 5, "There must be exactly 5 arguments");
+ assert_equals(typeof message, "string", "message argument must be a string");
+ assert_equals(typeof filename, "string", "filename argument must be a string");
+ assert_equals(typeof lineno, "number", "lineno argument must be a number");
+ assert_equals(typeof colno, "number", "colno argument must be a number");
+ assert_equals(typeof error, "object", "error argument must be an object");
+ assert_equals(error.constructor, ReferenceError, "error argument must be a ReferenceError");
+ return true;
+ });
+
+ setTimeout(() => thisFunctionDoesNotExist(), 0);
+
+ const eventWatcher = new EventWatcher(t, window, "error");
+ return eventWatcher.wait_for("error");
+}, "error event has the right 5 args on Window, with a runtime error");
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/window-synthetic-errorevent.html b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/window-synthetic-errorevent.html
new file mode 100644
index 0000000000..2d62d8a204
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/window-synthetic-errorevent.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Event handlers processing algorithm: error events</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-event-handler-processing-algorithm">
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+
+<div id="log"></div>
+
+<script>
+"use strict";
+setup({ allow_uncaught_exception: true });
+
+promise_test(t => {
+ window.onerror = t.step_func((...args) => {
+ assert_greater_than(args.length, 1);
+ return true;
+ });
+
+ const eventWatcher = new EventWatcher(t, window, "error");
+ const promise = eventWatcher.wait_for("error").then(e => {
+ assert_equals(e.defaultPrevented, true);
+ });
+
+ window.dispatchEvent(new ErrorEvent("error", { cancelable: true }));
+
+ return promise;
+}, "error event is weird (return true cancels; many args) on Window, with a synthetic ErrorEvent");
+
+promise_test(t => {
+ const theError = { the: "error object" };
+
+ window.onerror = t.step_func(function (message, filename, lineno, colno, error) {
+ assert_equals(arguments.length, 5, "There must be exactly 5 arguments");
+ assert_equals(message, "message");
+ assert_equals(filename, "filename");
+ assert_equals(lineno, 1);
+ assert_equals(colno, 2);
+ assert_equals(error, theError);
+ return true;
+ });
+
+ const eventWatcher = new EventWatcher(t, window, "error");
+ const promise = eventWatcher.wait_for("error");
+
+ window.dispatchEvent(new ErrorEvent("error", {
+ message: "message",
+ filename: "filename",
+ lineno: 1,
+ colno: 2,
+ error: theError
+ }));
+
+ return promise;
+}, "error event has the right 5 args on Window, with a synthetic ErrorEvent");
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/window-synthetic-event.html b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/window-synthetic-event.html
new file mode 100644
index 0000000000..0bcc7defb7
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/window-synthetic-event.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Event handlers processing algorithm: error events</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-event-handler-processing-algorithm">
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+
+<div id="log"></div>
+
+<script>
+"use strict";
+setup({ allow_uncaught_exception: true });
+
+promise_test(t => {
+ window.onerror = t.step_func((...args) => {
+ assert_equals(args.length, 1);
+ return true;
+ });
+
+ const eventWatcher = new EventWatcher(t, window, "error");
+ const promise = eventWatcher.wait_for("error").then(e => {
+ assert_equals(e.defaultPrevented, false);
+ });
+
+ window.dispatchEvent(new Event("error", { cancelable: true }));
+
+ return promise;
+}, "error event is normal (return true does not cancel; one arg) on Window, with a synthetic Event");
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/worker.html b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/worker.html
new file mode 100644
index 0000000000..a8c0d97ce2
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/worker.html
@@ -0,0 +1,63 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Event handlers processing algorithm: error events</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-event-handler-processing-algorithm">
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+
+<div id="log"></div>
+
+<script>
+"use strict";
+setup({ allow_uncaught_exception: true });
+
+promise_test(t => {
+ const worker = new Worker("resources/worker-with-syntax-error.js");
+ worker.onerror = t.step_func((...args) => {
+ assert_equals(args.length, 1);
+ return true;
+ });
+
+ const eventWatcher = new EventWatcher(t, worker, "error");
+ const promise = eventWatcher.wait_for("error").then(e => {
+ assert_equals(e.defaultPrevented, false);
+ });
+
+ return promise;
+}, "error event is normal (return true does not cancel; one arg) on Worker, with a syntax error in the worker code");
+
+promise_test(t => {
+ const worker = new Worker("resources/no-op-worker.js");
+ worker.onerror = t.step_func((...args) => {
+ assert_equals(args.length, 1);
+ return true;
+ });
+
+ const eventWatcher = new EventWatcher(t, worker, "error");
+ const promise = eventWatcher.wait_for("error").then(e => {
+ assert_equals(e.defaultPrevented, false);
+ });
+
+ worker.dispatchEvent(new Event("error", { cancelable: true }));
+
+ return promise;
+}, "error event is normal (return true does not cancel; one arg) on Worker, with a synthetic Event");
+
+promise_test(t => {
+ const worker = new Worker("resources/no-op-worker.js");
+ worker.onerror = t.step_func((...args) => {
+ assert_equals(args.length, 1);
+ return true;
+ });
+
+ const eventWatcher = new EventWatcher(t, worker, "error");
+ const promise = eventWatcher.wait_for("error").then(e => {
+ assert_equals(e.defaultPrevented, false);
+ });
+
+ worker.dispatchEvent(new ErrorEvent("error", { cancelable: true }));
+
+ return promise;
+}, "error event is normal (return true does not cancel; one arg) on Worker, with a synthetic ErrorEvent");
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/workerglobalscope-runtime-error.worker.js b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/workerglobalscope-runtime-error.worker.js
new file mode 100644
index 0000000000..264fef810d
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/workerglobalscope-runtime-error.worker.js
@@ -0,0 +1,40 @@
+"use strict";
+importScripts("/resources/testharness.js");
+
+setup({ allow_uncaught_exception: true });
+
+promise_test(t => {
+ self.onerror = t.step_func((...args) => {
+ assert_greater_than(args.length, 1);
+ return true;
+ });
+
+ const eventWatcher = new EventWatcher(t, self, "error");
+ const promise = eventWatcher.wait_for("error").then(e => {
+ assert_equals(e.defaultPrevented, true);
+ });
+
+ setTimeout(() => thisFunctionDoesNotExist(), 0);
+
+ return promise;
+}, "error event is weird (return true cancels; many args) on WorkerGlobalScope, with a runtime error");
+
+promise_test(t => {
+ self.onerror = t.step_func(function (message, filename, lineno, colno, error) {
+ assert_equals(arguments.length, 5, "There must be exactly 5 arguments");
+ assert_equals(typeof message, "string", "message argument must be a string");
+ assert_equals(typeof filename, "string", "filename argument must be a string");
+ assert_equals(typeof lineno, "number", "lineno argument must be a number");
+ assert_equals(typeof colno, "number", "colno argument must be a number");
+ assert_equals(typeof error, "object", "error argument must be an object");
+ assert_equals(error.constructor, ReferenceError, "error argument must be a ReferenceError");
+ return true;
+ });
+
+ setTimeout(() => thisFunctionDoesNotExist(), 0);
+
+ const eventWatcher = new EventWatcher(t, self, "error");
+ return eventWatcher.wait_for("error");
+}, "error event has the right 5 args on WorkerGlobalScope, with a runtime error");
+
+done();
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/workerglobalscope-synthetic-errorevent.worker.js b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/workerglobalscope-synthetic-errorevent.worker.js
new file mode 100644
index 0000000000..a14f6e01a9
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/workerglobalscope-synthetic-errorevent.worker.js
@@ -0,0 +1,49 @@
+"use strict";
+importScripts("/resources/testharness.js");
+
+setup({ allow_uncaught_exception: true });
+
+promise_test(t => {
+ self.onerror = t.step_func((...args) => {
+ assert_greater_than(args.length, 1);
+ return true;
+ });
+
+ const eventWatcher = new EventWatcher(t, self, "error");
+ const promise = eventWatcher.wait_for("error").then(e => {
+ assert_equals(e.defaultPrevented, true);
+ });
+
+ self.dispatchEvent(new ErrorEvent("error", { cancelable: true }));
+
+ return promise;
+}, "error event is weird (return true cancels; many args) on WorkerGlobalScope, with a synthetic ErrorEvent");
+
+promise_test(t => {
+ const theError = { the: "error object" };
+
+ self.onerror = t.step_func(function (message, filename, lineno, colno, error) {
+ assert_equals(arguments.length, 5, "There must be exactly 5 arguments");
+ assert_equals(message, "message");
+ assert_equals(filename, "filename");
+ assert_equals(lineno, 1);
+ assert_equals(colno, 2);
+ assert_equals(error, theError);
+ return true;
+ });
+
+ const eventWatcher = new EventWatcher(t, self, "error");
+ const promise = eventWatcher.wait_for("error");
+
+ self.dispatchEvent(new ErrorEvent("error", {
+ message: "message",
+ filename: "filename",
+ lineno: 1,
+ colno: 2,
+ error: theError
+ }));
+
+ return promise;
+}, "error event has the right 5 args on WorkerGlobalScope, with a synthetic ErrorEvent");
+
+done();
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/workerglobalscope-synthetic-event.worker.js b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/workerglobalscope-synthetic-event.worker.js
new file mode 100644
index 0000000000..a3e16ded88
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-error/workerglobalscope-synthetic-event.worker.js
@@ -0,0 +1,22 @@
+"use strict";
+importScripts("/resources/testharness.js");
+
+setup({ allow_uncaught_exception: true });
+
+promise_test(t => {
+ self.onerror = t.step_func((...args) => {
+ assert_equals(args.length, 1);
+ return true;
+ });
+
+ const eventWatcher = new EventWatcher(t, self, "error");
+ const promise = eventWatcher.wait_for("error").then(e => {
+ assert_equals(e.defaultPrevented, false);
+ });
+
+ self.dispatchEvent(new Event("error", { cancelable: true }));
+
+ return promise;
+}, "error event is normal (return true does not cancel; one arg) on WorkerGlobalScope, with a synthetic Event");
+
+done();
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-manual.html b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-manual.html
new file mode 100644
index 0000000000..205e876c1d
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm-manual.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Event handlers processing algorithm: manual tests</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-event-handler-processing-algorithm">
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+
+<style>
+ div[id^="d"] {
+ width: 100px;
+ height: 100px;
+ background-color: blue;
+ }
+</style>
+
+<div id="log"></div>
+
+<p>Mouseover these four divs</p>
+
+<div id="d1"></div>
+<div id="d2"></div>
+
+<div id="d3" onmouseover="return false"></div>
+<div id="d4" onmouseover="return true"></div>
+
+<script>
+"use strict";
+async_test(t => {
+ const div = document.querySelector("#d1");
+
+ div.onmouseover = t.step_func(() => false);
+ div.addEventListener("mouseover", t.step_func_done(e => {
+ assert_equals(e.defaultPrevented, true);
+ }));
+}, "Listener added via JavaScript, returns false: cancels the event");
+
+async_test(t => {
+ const div = document.querySelector("#d2");
+
+ div.onmouseover = t.step_func(() => true);
+ div.addEventListener("mouseover", t.step_func_done(e => {
+ assert_equals(e.defaultPrevented, false);
+ }));
+}, "Listener added via JavaScript, returns true: does not cancel the event");
+
+async_test(t => {
+ const div = document.querySelector("#d3");
+
+ div.addEventListener("mouseover", t.step_func_done(e => {
+ assert_equals(e.defaultPrevented, true);
+ }));
+}, "Listener added via markup, returns false: cancels the event");
+
+async_test(t => {
+ const div = document.querySelector("#d4");
+
+ div.addEventListener("mouseover", t.step_func_done(e => {
+ assert_equals(e.defaultPrevented, false);
+ }));
+}, "Listener added via markup, returns true: does not cancel the event");
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm.html b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm.html
new file mode 100644
index 0000000000..f5423d7ed4
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-processing-algorithm.html
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<title>Event handlers processing algorithm</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+ <body>
+ <div id="foo" style="width: 100px; height: 100px; background-color: black"></div>
+ <script>
+
+ // Historically mouseover was special in the spec, but now it is not. See https://github.com/whatwg/html/pull/2398.
+ test(function(t) {
+ var ev = new Event('mouseover', {cancelable: true});
+ document.getElementById("foo").onmouseover = t.step_func(function() { return false });
+ document.getElementById("foo").dispatchEvent(ev);
+ assert_equals(ev.defaultPrevented, true)
+ }, "mouseover listener returning false cancels event (using Event)");
+
+ test(function(t) {
+ var ev = new MouseEvent('mouseover', {cancelable: true});
+ document.getElementById("foo").onmouseover = t.step_func(function() { return false });
+ document.getElementById("foo").dispatchEvent(ev);
+ assert_equals(ev.defaultPrevented, true)
+ }, "mouseover listener returning false cancels event (using MouseEvent)");
+
+ test(function(t) {
+ var ev = new Event('mouseover', {cancelable: true});
+ document.getElementById("foo").onmouseover = t.step_func(function() { return true });
+ document.getElementById("foo").dispatchEvent(ev);
+ assert_equals(ev.defaultPrevented, false)
+ }, "mouseover listener returning true doesn't cancel event (using Event)");
+
+ test(function(t) {
+ var ev = new MouseEvent('mouseover', {cancelable: true});
+ document.getElementById("foo").onmouseover = t.step_func(function() { return true });
+ document.getElementById("foo").dispatchEvent(ev);
+ assert_equals(ev.defaultPrevented, false)
+ }, "mouseover listener returning true doesn't cancel event (using MouseEvent)");
+
+ // beforeunload is tested in html/browsers/browsing-the-web/unloading-documents/beforeunload-canceling.html
+
+ test(function(t) {
+ var ev = new Event("click", {cancelable: true});
+ document.getElementById("foo").onclick = t.step_func(function() { return false; });
+ document.getElementById("foo").dispatchEvent(ev);
+ assert_equals(ev.defaultPrevented, true);
+ }, "click listener returning false cancels event");
+
+ test(function(t) {
+ var ev = new Event("blur", {cancelable: true});
+ document.getElementById("foo").onblur = t.step_func(function() { return false; });
+ document.getElementById("foo").dispatchEvent(ev);
+ assert_equals(ev.defaultPrevented, true);
+ }, "blur listener returning false cancels event");
+
+ test(function(t) {
+ var ev = new Event("dblclick", {cancelable: true});
+ document.getElementById("foo").ondblclick = t.step_func(function() { return false; });
+ document.getElementById("foo").dispatchEvent(ev);
+ assert_equals(ev.defaultPrevented, true);
+ }, "dblclick listener returning false cancels event");
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-removal.window.js b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-removal.window.js
new file mode 100644
index 0000000000..a20e2ec1d2
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-removal.window.js
@@ -0,0 +1,76 @@
+let firstEventHandler;
+
+test(t => {
+ var i = 0;
+ firstEventHandler = t.unreached_func('First event handler.');
+ var uncalled = "firstEventHandler();";
+ var button = document.createElement('button');
+ button.addEventListener('click', t.step_func(() => { assert_equals(++i, 1) }), false);
+ button.setAttribute('onclick', uncalled); // event handler is activated here
+ button.addEventListener('click', t.step_func(() => { assert_equals(++i, 2) }), false);
+ button.onclick = null; // but de-activated here
+ button.addEventListener('click', t.step_func(() => { assert_equals(++i, 3) }), false);
+ button.onclick = t.step_func(() => { assert_equals(++i, 4); }); // and re-activated here
+ button.addEventListener('click', t.step_func(() => { assert_equals(++i, 5) }), false);
+ button.click()
+ assert_equals(button.getAttribute("onclick"), uncalled)
+ assert_equals(i, 5);
+}, "Event handler set through content attribute should be removed when they are set to null.");
+
+let happened = 0;
+test(() => {
+ var script = "happened++;";
+ var button = document.createElement('button');
+ button.setAttribute('onclick', script); // event handler is activated here
+ button.onclick = null; // but de-activated here
+ assert_equals(button.getAttribute("onclick"), script)
+ button.setAttribute('onclick', script); // and re-activated here
+ button.click()
+ assert_equals(happened, 1);
+}, "Event handler set through content attribute should be re-activated even if content is the same.");
+
+test(t => {
+ var i = 0;
+ firstEventHandler = t.unreached_func('First event handler.');
+ var uncalled = "firstEventHandler();";
+ var button = document.createElement('button');
+ button.addEventListener('click', t.step_func(() => { assert_equals(++i, 1) }), false);
+ button.setAttribute('onclick', uncalled); // event handler is activated here
+ button.addEventListener('click', t.step_func(() => { assert_equals(++i, 2) }), false);
+ button.removeAttribute('onclick'); // but de-activated here
+ button.addEventListener('click', t.step_func(() => { assert_equals(++i, 3) }), false);
+ button.onclick = t.step_func(() => { assert_equals(++i, 4); }); // and re-activated here
+ button.addEventListener('click', t.step_func(() => { assert_equals(++i, 5) }), false);
+ button.click()
+ assert_equals(i, 5);
+}, "Event handler set through content attribute should be deactivated when the content attribute is removed.");
+test(t => {
+ var i = 0;
+ firstEventHandler = t.unreached_func('First event handler.');
+ var uncalled = "firstEventHandler();";
+ var button = document.createElement('button');
+ button.addEventListener('click', t.step_func(() => { assert_equals(++i, 1) }), false);
+ button.onclick = t.unreached_func('First event handler.'); // event handler is activated here
+ button.addEventListener('click', t.step_func(() => { assert_equals(++i, 2) }), false);
+ button.onclick = null; // but de-activated here
+ button.addEventListener('click', t.step_func(() => { assert_equals(++i, 3) }), false);
+ button.onclick = t.step_func(() => { assert_equals(++i, 4); }); // and re-activated here
+ button.addEventListener('click', t.step_func(() => { assert_equals(++i, 5) }), false);
+ button.click()
+ assert_equals(i, 5);
+}, "Event handler set through IDL should be deactivated when the IDL attribute is set to null.");
+test(t => {
+ var i = 0;
+ firstEventHandler = t.unreached_func('First event handler.');
+ var uncalled = "firstEventHandler();";
+ var button = document.createElement('button');
+ button.addEventListener('click', t.step_func(() => { assert_equals(++i, 1) }), false);
+ button.onclick = t.unreached_func('First event handler.'); // event handler is activated here
+ button.addEventListener('click', t.step_func(() => { assert_equals(++i, 3) }), false);
+ button.removeAttribute('onclick'); // and NOT de-activated here
+ button.addEventListener('click', t.step_func(() => { assert_equals(++i, 4) }), false);
+ button.onclick = t.step_func(() => { assert_equals(++i, 2); });
+ button.addEventListener('click', t.step_func(() => { assert_equals(++i, 5) }), false);
+ button.click()
+ assert_equals(i, 5);
+}, "Event handler set through IDL should NOT be deactivated when the content attribute is removed.");
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-sourcetext.html b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-sourcetext.html
new file mode 100644
index 0000000000..57555faa7b
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-sourcetext.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Test the sourceText of event handlers</title>
+<link rel="help" href="https://github.com/whatwg/html/issues/5500">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<body>
+<script>
+"use strict";
+
+test(() => {
+ const el = document.createElement("div");
+ el.setAttribute("onclick", "foo");
+ assert_equals(el.onclick.toString(), "function onclick(event) {\nfoo\n}");
+}, "non-error event handler");
+
+test(() => {
+ const el = document.createElement("div");
+ el.setAttribute("onerror", "foo");
+ assert_equals(el.onerror.toString(), "function onerror(event) {\nfoo\n}");
+}, "error event handler not on body");
+
+test(() => {
+ const el = document.createElement("body");
+ el.setAttribute("onerror", "foo");
+ assert_equals(el.onerror.toString(), "function onerror(event, source, lineno, colno, error) {\nfoo\n}");
+}, "error event handler on disconnected body");
+
+test(() => {
+ const el = document.createElement("frameset");
+ el.setAttribute("onerror", "foo");
+ assert_equals(el.onerror.toString(), "function onerror(event, source, lineno, colno, error) {\nfoo\n}");
+}, "error event handler on disconnected frameset");
+
+test(() => {
+ document.body.setAttribute("onerror", "foo");
+ assert_equals(window.onerror.toString(), "function onerror(event, source, lineno, colno, error) {\nfoo\n}");
+}, "error event handler on connected body, reflected to Window");
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-spec-example.window.js b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-spec-example.window.js
new file mode 100644
index 0000000000..abf46882aa
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/event-handler-spec-example.window.js
@@ -0,0 +1,55 @@
+var objects = [{}, function() {}, new Number(42), new String()];
+var primitives = [42, null, undefined, ""];
+var firstEventHandler;
+objects.forEach(function(object) {
+ test(t => {
+ var i = 0;
+ firstEventHandler = t.unreached_func('First event handler.');
+ var uncalled = "firstEventHandler();";
+ var button = document.createElement('button');
+ button.onclick = object; // event handler listener is registered here
+ assert_equals(button.onclick, object);
+ button.addEventListener('click', t.step_func(() => { assert_equals(++i, 2) }), false);
+ button.setAttribute('onclick', uncalled);
+ button.addEventListener('click', t.step_func(() => { assert_equals(++i, 3) }), false);
+ button.onclick = t.step_func(() => { assert_equals(++i, 1); });
+ button.addEventListener('click', t.step_func(() => { assert_equals(++i, 4) }), false);
+ button.click()
+ assert_equals(button.getAttribute("onclick"), uncalled)
+ assert_equals(i, 4);
+ }, "Event handler listeners should be registered when they are first set to an object value " +
+ "(" + format_value(object) + ").");
+});
+primitives.forEach(function(primitive) {
+ test(t => {
+ var i = 0;
+ firstEventHandler = t.unreached_func('First event handler.');
+ var uncalled = "firstEventHandler();";
+ var button = document.createElement('button');
+ button.onclick = primitive;
+ assert_equals(button.onclick, null);
+ button.addEventListener('click', t.step_func(() => { assert_equals(++i, 1) }), false);
+ button.setAttribute('onclick', uncalled); // event handler listener is registered here
+ button.addEventListener('click', t.step_func(() => { assert_equals(++i, 3) }), false);
+ button.onclick = t.step_func(() => { assert_equals(++i, 2); });
+ button.addEventListener('click', t.step_func(() => { assert_equals(++i, 4) }), false);
+ button.click()
+ assert_equals(button.getAttribute("onclick"), uncalled)
+ assert_equals(i, 4);
+ }, "Event handler listeners should be registered when they are first set to an object value " +
+ "(" + format_value(primitive) + ").");
+});
+test(t => {
+ var i = 0;
+ firstEventHandler = t.unreached_func('First event handler.');
+ var uncalled = "firstEventHandler();";
+ var button = document.createElement('button');
+ button.addEventListener('click', t.step_func(() => { assert_equals(++i, 1) }), false);
+ button.setAttribute('onclick', uncalled); // event handler listener is registered here
+ button.addEventListener('click', t.step_func(() => { assert_equals(++i, 3) }), false);
+ button.onclick = t.step_func(() => { assert_equals(++i, 2); });
+ button.addEventListener('click', t.step_func(() => { assert_equals(++i, 4) }), false);
+ button.click()
+ assert_equals(button.getAttribute("onclick"), uncalled)
+ assert_equals(i, 4);
+}, "Event handler listeners should be registered when they are first set to an object value.");
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/eventhandler-cancellation.html b/testing/web-platform/tests/html/webappapis/scripting/events/eventhandler-cancellation.html
new file mode 100644
index 0000000000..6be581fa24
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/eventhandler-cancellation.html
@@ -0,0 +1,76 @@
+<!doctype html>
+<meta charset=utf-8>
+<title></title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<!-- A window to work with that won't trigger the harness exception detection
+ when we fire "error" events at it -->
+<iframe style="display: none"></iframe>
+<script>
+ test(function() {
+ var blob = new Blob([""]);
+ // Most targets disabled for now until
+ // https://github.com/whatwg/html/issues/2296 is sorted out.
+ var targets = [ frames[0] /*, document, document.documentElement,
+ new Worker(URL.createObjectURL(blob) */ ];
+ // Event constructors also mostly disabled until
+ // https://github.com/whatwg/html/issues/2296 is sorted out.
+ var eventCtors = [ /* Event, */ ErrorEvent /*, MouseEvent */ ];
+ var values = [true, false, "", "abc", {}, 0, 1, -1, null, undefined,
+ 2.5, NaN, Infinity, Symbol.toStringTag ];
+ // Event types also mostly disabled pending
+ // https://github.com/whatwg/html/issues/2296
+ var eventTypes = [ "error"/*, "click", "load"*/ ];
+
+ // Variables that keep track of which subtest we're running.
+ var curTarget;
+ var curValue;
+ var curCtor;
+ var curType;
+
+ function defaultPreventedTester(event) {
+ var expectedValue;
+ if (curTarget === frames[0] &&
+ curCtor === ErrorEvent &&
+ curValue === true &&
+ curType == "error") {
+ expectedValue = true;
+ } else {
+ // This will need adjusting once we allow more targets and event
+ // constructors above!
+ expectedValue = false;
+ }
+ var valueRepr;
+ if (typeof curValue == "string") {
+ valueRepr = '"' + curValue + '"';
+ } else {
+ valueRepr = String(curValue);
+ }
+ test(function() {
+ assert_equals(event.defaultPrevented, expectedValue);
+ }, "Returning " + valueRepr +
+ " from " + String(curTarget) + "'s on" + curType +
+ " event handler while " + curCtor.name +
+ " is firing should" +
+ (expectedValue ? "" : " not") +
+ " cancel the event");
+ }
+
+ for (curCtor of eventCtors) {
+ for (curTarget of targets) {
+ for (curType of eventTypes) {
+ for (curValue of values) {
+ // We have to make sure that defaultPreventedTester is added after
+ // our event handler.
+ curTarget["on" + curType] = function() { return curValue; }
+ curTarget.addEventListener(curType, defaultPreventedTester);
+ var e = new curCtor(curType, { cancelable: true });
+ curTarget.dispatchEvent(e);
+ curTarget["on" + curType] = null;
+ curTarget.removeEventListener(curType, defaultPreventedTester);
+ }
+ }
+ }
+ }
+ }, "event handler cancellation behavior");
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/inline-event-handler-ordering.html b/testing/web-platform/tests/html/webappapis/scripting/events/inline-event-handler-ordering.html
new file mode 100644
index 0000000000..aae0f1abf8
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/inline-event-handler-ordering.html
@@ -0,0 +1,53 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Inline event handlers retain their ordering even when invalid</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<script>
+setup({ allow_uncaught_exception: true });
+var events = [];
+
+test(function() {
+ events = [];
+ var e = document.createElement("div");
+ document.body.appendChild(e);
+ e.addEventListener("click", function() { events.push("ONE") });
+ e.setAttribute("onclick", "window.open(");
+ e.addEventListener("click", function() { events.push("THREE") });
+ // Try to compile the event handler.
+ e.onclick;
+ e.setAttribute("onclick", "events.push('TWO')");
+ e.dispatchEvent(new Event("click"));
+ var expected_events = ["ONE", "TWO", "THREE"];
+ assert_array_equals(events, expected_events);
+}, "Inline event handlers retain their ordering when invalid and force-compiled");
+
+test(function() {
+ events = [];
+ var e = document.createElement("div");
+ document.body.appendChild(e);
+ e.addEventListener("click", function() { events.push("ONE") });
+ e.setAttribute("onclick", "window.open(");
+ e.addEventListener("click", function() { events.push("THREE") });
+ e.dispatchEvent(new Event("click"));
+ e.setAttribute("onclick", "events.push('TWO')");
+ e.dispatchEvent(new Event("click"));
+ var expected_events = ["ONE", "THREE", "ONE", "TWO", "THREE"];
+ assert_array_equals(events, expected_events);
+}, "Inline event handlers retain their ordering when invalid and force-compiled via dispatch");
+
+test(function() {
+ events = [];
+ var e = document.createElement("div");
+ document.body.appendChild(e);
+ e.addEventListener("click", function() { events.push("ONE") });
+ e.setAttribute("onclick", "window.open(");
+ e.addEventListener("click", function() { events.push("THREE") });
+ e.setAttribute("onclick", "events.push('TWO')");
+ e.dispatchEvent(new Event("click"));
+ var expected_events = ["ONE", "TWO", "THREE"];
+ assert_array_equals(events, expected_events);
+}, "Inline event handlers retain their ordering when invalid and lazy-compiled");
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/invalid-uncompiled-raw-handler-compiled-late.window.js b/testing/web-platform/tests/html/webappapis/scripting/events/invalid-uncompiled-raw-handler-compiled-late.window.js
new file mode 100644
index 0000000000..2892a4c3ab
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/invalid-uncompiled-raw-handler-compiled-late.window.js
@@ -0,0 +1,16 @@
+setup({ allow_uncaught_exception: true });
+
+test(function() {
+ var events = [];
+ window.onerror = function() {
+ events.push("error");
+ };
+
+ var div = document.createElement("div");
+ div.addEventListener("click", function (e) { events.push("click 1") });
+ div.setAttribute("onclick", "}");
+ div.addEventListener("click", function (e) { events.push("click 2") });
+ div.dispatchEvent(new Event("click"));
+ assert_equals(div.onclick, null);
+ assert_array_equals(events, ["click 1", "error", "click 2"]);
+}, "Invalid uncompiled raw handlers should only be compiled when about to call them");
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/invalid-uncompiled-raw-handler-compiled-once.window.js b/testing/web-platform/tests/html/webappapis/scripting/events/invalid-uncompiled-raw-handler-compiled-once.window.js
new file mode 100644
index 0000000000..b39b54b0e9
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/invalid-uncompiled-raw-handler-compiled-once.window.js
@@ -0,0 +1,14 @@
+setup({ allow_uncaught_exception: true });
+
+var errors = 0;
+window.onerror = function() {
+ errors++;
+};
+
+test(function() {
+ var e = document.body;
+ e.setAttribute("onclick", "window.open(");
+ assert_equals(e.onclick, null);
+ assert_equals(e.onclick, null);
+ assert_equals(errors, 1);
+}, "Invalid uncompiled raw handlers should only be compiled once");
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/invalid-uncompiled-raw-handler-keeps-position.window.js b/testing/web-platform/tests/html/webappapis/scripting/events/invalid-uncompiled-raw-handler-keeps-position.window.js
new file mode 100644
index 0000000000..f9443bf99a
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/invalid-uncompiled-raw-handler-keeps-position.window.js
@@ -0,0 +1,20 @@
+setup({ allow_uncaught_exception: true });
+
+test(function() {
+ var events = [];
+ window.onerror = function() {
+ events.push("error");
+ };
+
+ var div = document.createElement("div");
+ div.addEventListener("click", function (e) { events.push("click 1"); });
+ div.setAttribute("onclick", "}");
+ div.addEventListener("click", function (e) { events.push("click 3"); });
+ assert_equals(div.onclick, null);
+ assert_array_equals(events, ["error"]);
+
+ events = [];
+ div.onclick = function (e) { events.push("click 2"); };
+ div.dispatchEvent(new Event("click"));
+ assert_array_equals(events, ["click 1", "click 2", "click 3"]);
+}, "Compiling invalid uncompiled raw handlers should keep the position in event listener list");
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/messageevent-constructor.https.html b/testing/web-platform/tests/html/webappapis/scripting/events/messageevent-constructor.https.html
new file mode 100644
index 0000000000..2d62019dbf
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/messageevent-constructor.https.html
@@ -0,0 +1,114 @@
+<!DOCTYPE html>
+<title>MessageEvent constructor</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/service-workers/service-worker/resources/test-helpers.sub.js"></script>
+<script>
+test(function() {
+ assert_throws_js(
+ TypeError,
+ () => MessageEvent(""),
+ "Calling MessageEvent constructor without 'new' must throw"
+ );
+}, "MessageEvent constructor called as normal function");
+
+test(function() {
+ var ev = new MessageEvent("test")
+ assert_equals(ev.type, "test", "type attribute")
+ assert_equals(ev.target, null, "target attribute")
+ assert_equals(ev.currentTarget, null, "currentTarget attribute")
+ assert_equals(ev.eventPhase, Event.NONE, "eventPhase attribute")
+ assert_equals(ev.bubbles, false, "bubbles attribute")
+ assert_equals(ev.cancelable, false, "cancelable attribute")
+ assert_equals(ev.defaultPrevented, false, "defaultPrevented attribute")
+ assert_equals(ev.isTrusted, false, "isTrusted attribute")
+ assert_true(ev.timeStamp > 0, "timeStamp attribute")
+ assert_true("initMessageEvent" in ev, "initMessageEvent operation")
+ assert_equals(ev.data, null, "data attribute")
+ assert_equals(ev.origin, "", "origin attribute")
+ assert_equals(ev.lastEventId, "", "lastEventId attribute")
+ assert_equals(ev.source, null, "source attribute")
+ assert_array_equals(ev.ports, [], "ports attribute")
+}, "Default event values")
+
+test(function() {
+ var channel = new MessageChannel()
+ var ev = new MessageEvent("test", { data: "testData", origin: "testOrigin", lastEventId: "testId", source: window, ports: [channel.port1] })
+ assert_equals(ev.type, "test", "type attribute")
+ assert_equals(ev.data, "testData", "data attribute")
+ assert_equals(ev.origin, "testOrigin", "origin attribute")
+ assert_equals(ev.lastEventId, "testId", "lastEventId attribute")
+ assert_equals(ev.source, window, "source attribute")
+ assert_array_equals(ev.ports, [channel.port1], "ports attribute")
+}, "MessageEventInit dictionary")
+
+test(function() {
+ assert_throws_js(TypeError, function() {
+ new MessageEvent("test", { ports: null })
+ })
+}, "Passing null for ports member")
+
+test(function() {
+ var ev = new MessageEvent("test", { ports: [] })
+ assert_true(Array.isArray(ev.ports), "Array.isArray() should return true")
+ assert_true(Object.isFrozen(ev.ports), "Object.isFrozen() should return true")
+ assert_equals(ev.ports, ev.ports, "ev.ports should return the same object")
+
+ const oldPorts = ev.ports;
+ ev.initMessageEvent("test", false, false, null, "", "", null, ev.ports);
+ assert_not_equals(oldPorts, ev.ports, "initMessageEvent() changes ev.ports");
+}, "ports attribute should be a FrozenArray")
+
+test(function() {
+ var ev = document.createEvent("messageevent");
+ var channel = new MessageChannel()
+ ev.initMessageEvent("test", true, false, "testData", "testOrigin", "testId", window, [channel.port1])
+ assert_equals(ev.type, "test", "type attribute")
+ assert_equals(ev.bubbles, true, "bubbles attribute")
+ assert_equals(ev.cancelable, false, "bubbles attribute")
+ assert_equals(ev.data, "testData", "data attribute")
+ assert_equals(ev.origin, "testOrigin", "origin attribute")
+ assert_equals(ev.lastEventId, "testId", "lastEventId attribute")
+ assert_equals(ev.source, window, "source attribute")
+ assert_array_equals(ev.ports, [channel.port1], "ports attribute")
+}, "initMessageEvent operation")
+
+test(function() {
+ var ev = document.createEvent("messageevent")
+ assert_throws_js(TypeError, function() {
+ ev.initMessageEvent("test", true, false, "testData", "testOrigin", "testId", window, null)
+ })
+}, "Passing null for ports parameter to initMessageEvent")
+
+test(function() {
+ var ev = document.createEvent("messageevent")
+ assert_equals(MessageEvent.prototype.initMessageEvent.length, 1, "MessageEvent.prototype.initMessageEvent.length should be 1")
+ ev.initMessageEvent("test")
+ assert_equals(ev.type, "test", "type attribute")
+ assert_equals(ev.bubbles, false, "bubbles attribute")
+ assert_equals(ev.cancelable, false, "bubbles attribute")
+ assert_equals(ev.data, null, "data attribute")
+ assert_equals(ev.origin, "", "origin attribute")
+ assert_equals(ev.lastEventId, "", "lastEventId attribute")
+ assert_equals(ev.source, null, "source attribute")
+ assert_array_equals(ev.ports, [], "ports attribute")
+}, "initMessageEvent operation default parameter values")
+
+promise_test(function(t) {
+ var worker_url = "/service-workers/service-worker/resources/empty-worker.js";
+ var scope = "/service-workers/service-worker/resources/";
+ var registration;
+
+ return service_worker_unregister_and_register(t, worker_url, scope)
+ .then(function(r) {
+ registration = r;
+ return wait_for_state(t, r.installing, "activated");
+ })
+ .then(function() {
+ var ev = new MessageEvent("test", { source: registration.active });
+ assert_equals(ev.source, registration.active, "source attribute should return the ServiceWorker");
+ service_worker_unregister(t, scope);
+ });
+ }, "Passing ServiceWorker for source member");
+
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/onerroreventhandler-frame.html b/testing/web-platform/tests/html/webappapis/scripting/events/onerroreventhandler-frame.html
new file mode 100644
index 0000000000..79e4af3020
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/onerroreventhandler-frame.html
@@ -0,0 +1,56 @@
+<body></body>
+<script>
+function check1(args, callee) {
+ parent.t.step(function() {
+ parent.assert_equals(callee.length, 5);
+ parent.assert_equals(args.length, 5);
+ parent.assert_equals(args[0], reference_error.message);
+ parent.assert_equals(args[1], reference_error.filename);
+ parent.assert_equals(args[2], reference_error.lineno);
+ parent.assert_equals(args[3], reference_error.colno);
+ parent.assert_equals(args[4], reference_error.error);
+ parent.t.done();
+ });
+}
+
+var reference_error = new ErrorEvent("error", {
+ filename: "error_file.js",
+ lineno: 333,
+ colno: 999,
+ message: "there was an error",
+ error: {nondefault: 'some unusual object'},
+});
+
+parent.t.step(function() {
+ document.body.outerHTML = "<body onerror='check1(arguments, arguments.callee)'></body>"
+ window.dispatchEvent(reference_error);
+});
+
+function check2(args, callee) {
+ parent.t2.step(function() {
+ parent.assert_equals(callee.length, 5);
+ parent.assert_equals(args.length, 1);
+ parent.assert_false(args[0] instanceof ErrorEvent);
+ parent.t2.done()
+ });
+}
+
+parent.t2.step(function() {
+ document.body.outerHTML = "<body onerror='check2(arguments, arguments.callee)'></body>"
+ window.dispatchEvent(new Event("error"));
+});
+
+function check3(args, callee) {
+ parent.t3.step(function() {
+ parent.assert_equals(args.length, 1);
+ parent.assert_equals(callee.length, 1);
+ });
+}
+
+parent.t3.step(function() {
+ document.body.outerHTML = "<body><span onerror='check3(arguments, arguments.callee)'></span></body>"
+ document.body.firstChild.dispatchEvent(reference_error);
+ document.body.firstChild.dispatchEvent(new Event("error"));
+ parent.t3.done();
+});
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/onerroreventhandler.html b/testing/web-platform/tests/html/webappapis/scripting/events/onerroreventhandler.html
new file mode 100644
index 0000000000..60fc674d57
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/onerroreventhandler.html
@@ -0,0 +1,11 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>OnErrorEventHandler + ErrorEvent is treated differently</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script>
+var t = async_test("onerror + ErrorEvent + Window");
+var t2 = async_test("onerror + !ErrorEvent + Window");
+var t3 = async_test("onerror + Document");
+</script>
+<iframe src="onerroreventhandler-frame.html"></iframe>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/resources/compiled-event-handler-settings-objects-support.html b/testing/web-platform/tests/html/webappapis/scripting/events/resources/compiled-event-handler-settings-objects-support.html
new file mode 100644
index 0000000000..d40c0b9cce
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/resources/compiled-event-handler-settings-objects-support.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>This will be in an iframe</title>
+
+<script>
+window.name = "iframe";
+</script>
+
+<body onbeforeunload="return { toString: parent.postMessage.bind(parent, 'PASS', '*') };">
+
+<!-- window.open() uses the entry settings object to determine how the URL will be parsed -->
+<button onclick="var w = window.open('open-window.html'); w.onload = () => { parent.onWindowLoaded(w.document.URL); };">This will be clicked</button>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/resources/event-handler-body.js b/testing/web-platform/tests/html/webappapis/scripting/events/resources/event-handler-body.js
new file mode 100644
index 0000000000..d7889e230e
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/resources/event-handler-body.js
@@ -0,0 +1,61 @@
+const windowReflectingBodyElementEventHandlerSet =
+ new Set(['blur', 'error', 'focus', 'load', 'resize', 'scroll']);
+
+function handlersInInterface(mainIDL, name) {
+ return mainIDL.find(idl => idl.name === name).members.map(member => member.name.slice(2));
+}
+
+const handlersListPromise = fetch("/interfaces/html.idl").then(res => res.text()).then(htmlIDL => {
+ const parsedHTMLIDL = WebIDL2.parse(htmlIDL);
+ const windowEventHandlers = handlersInInterface(parsedHTMLIDL, "WindowEventHandlers");
+ const globalEventHandlers = handlersInInterface(parsedHTMLIDL, "GlobalEventHandlers");
+
+ const shadowedHandlers = [
+ ...windowReflectingBodyElementEventHandlerSet,
+ ...windowEventHandlers
+ ];
+ const notShadowedHandlers = globalEventHandlers.filter(name => !windowReflectingBodyElementEventHandlerSet.has(name));
+ return {
+ shadowedHandlers,
+ notShadowedHandlers
+ };
+});
+
+function eventHandlerTest(shadowedHandlers, notShadowedHandlers, element) {
+ const altBody = document.createElement(element);
+ for (const [des, obj1, obj2, obj3, des1, des2, des3] of [
+ ["document.body", document.body, altBody, window, "body", "alternative body", "window"],
+ [`document.createElement("${element}")`, altBody, document.body, window, "alternative body", "body", "window"],
+ ["window", window, document.body, altBody, "window", "body", "alternative body"]
+ ]) {
+ const f = () => 0;
+
+ shadowedHandlers.forEach(handler => {
+ const eventHandler = obj1['on' + handler];
+ test(() => {
+ obj1['on' + handler] = f;
+ assert_equals(obj2['on' + handler], f, `${des2} should reflect`);
+ assert_equals(obj3['on' + handler], f, `${des3} should reflect`);
+ }, `shadowed ${handler} (${des})`);
+ obj1['on' + handler] = eventHandler;
+ });
+
+ notShadowedHandlers.forEach(handler => {
+ const eventHandler = obj1['on' + handler];
+ test(() => {
+ obj1['on' + handler] = f;
+ assert_equals(obj2['on' + handler], null, `${des2} should reflect`);
+ assert_equals(obj3['on' + handler], null, `${des3} should reflect`);
+ }, `not shadowed ${handler} (${des})`);
+ obj1['on' + handler] = eventHandler;
+ });
+
+ shadowedHandlers.forEach(handler => {
+ test(() => {
+ assert_equals(obj1['on' + handler], null, `${des1} should reflect changes to itself`);
+ assert_equals(obj2['on' + handler], null, `${des2} should reflect`);
+ assert_equals(obj3['on' + handler], null, `${des3} should reflect`);
+ }, `shadowed ${handler} removal (${des})`);
+ });
+ }
+}
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/resources/open-window.html b/testing/web-platform/tests/html/webappapis/scripting/events/resources/open-window.html
new file mode 100644
index 0000000000..1d23263570
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/resources/open-window.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>This window will open during the course of the test</title>
+<h1>Hello</h1>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/events/uncompiled_event_handler_with_scripting_disabled.html b/testing/web-platform/tests/html/webappapis/scripting/events/uncompiled_event_handler_with_scripting_disabled.html
new file mode 100644
index 0000000000..a912b32d7f
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/events/uncompiled_event_handler_with_scripting_disabled.html
@@ -0,0 +1,21 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Uncompiled event handler check that scripting is enabled</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ setup({ allow_uncaught_exception: true });
+ test(function() {
+ var invoked = false;
+ window.addEventListener("error", function() {
+ invoked = true;
+ });
+
+ // Make sure that `this_will_error` will in fact error when it's referenced
+ assert_equals(typeof this_will_error, "undefined");
+ var dom = (new DOMParser()).parseFromString("<div id=\"has-event-handler\" onclick=\"this_will_error;\"></div>", "text/html");
+ var click = new MouseEvent("click");
+ dom.getElementById("has-event-handler").dispatchEvent(click);
+ assert_equals(invoked, false);
+ }, "when scripting is disabled, the handler is never compiled");
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/addEventListener.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/addEventListener.html
new file mode 100644
index 0000000000..dbb1cdd5a9
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/addEventListener.html
@@ -0,0 +1,32 @@
+<!doctype html>
+<html>
+ <head>
+ <title>window.onerror - addEventListener</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id=log></div>
+ <script>
+ setup({allow_uncaught_exception:true});
+ var t = async_test();
+ var ran = false;
+ window.addEventListener('error', t.step_func(function(e){
+ ran = true;
+ assert_true(e.isTrusted, 'isTrusted');
+ }), false);
+ </script>
+ <script>
+ undefined_variable;
+ </script>
+ <script>
+ for (;) {}
+ </script>
+ <script>
+ t.step(function(){
+ assert_true(ran, 'ran');
+ t.done();
+ });
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/body-onerror-compile-error-data-url.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/body-onerror-compile-error-data-url.html
new file mode 100644
index 0000000000..66e1dfed4d
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/body-onerror-compile-error-data-url.html
@@ -0,0 +1,37 @@
+<!doctype html>
+<html>
+ <head>
+ <title>&lt;body onerror> - compile error in &lt;script src=data:...></title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <script>
+ setup({allow_uncaught_exception:true});
+ var t = async_test();
+ var t_col = async_test(document.title+' (column)');
+ var ran = false;
+ </script>
+ <body onerror="
+ t.step(function(){
+ ran = true;
+ assert_equals(typeof event, 'string', 'first arg');
+ assert_equals(source, 'data:text/javascript,for(;){}', 'second arg');
+ assert_equals(typeof lineno, 'number', 'third arg');
+ });
+ t_col.step(function() {
+ assert_equals(typeof colno, 'number', 'fourth arg');
+ });
+ ">
+ <div id=log></div>
+ <script src="data:text/javascript,for(;){}"></script>
+ <script>
+ t.step(function(){
+ assert_true(ran, 'ran');
+ t.done();
+ });
+ t_col.step(function(){
+ t_col.done();
+ });
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/body-onerror-compile-error.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/body-onerror-compile-error.html
new file mode 100644
index 0000000000..0f65f73999
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/body-onerror-compile-error.html
@@ -0,0 +1,39 @@
+<!doctype html>
+<html>
+ <head>
+ <title>&lt;body onerror> - compile error in &lt;script></title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <script>
+ setup({allow_uncaught_exception:true});
+ var t = async_test();
+ var t_col = async_test(document.title+' (column)');
+ var ran = false;
+ </script>
+ <body onerror="
+ t.step(function(){
+ ran = true;
+ assert_equals(typeof event, 'string', 'first arg');
+ assert_equals(source, location.href, 'second arg');
+ assert_equals(typeof lineno, 'number', 'third arg');
+ });
+ t_col.step(function() {
+ assert_equals(typeof colno, 'number', 'fourth arg');
+ });
+ ">
+ <div id=log></div>
+ <script>
+ for(;) {}
+ </script>
+ <script>
+ t.step(function(){
+ assert_true(ran, 'ran');
+ t.done();
+ });
+ t_col.step(function(){
+ t_col.done();
+ });
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/body-onerror-runtime-error.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/body-onerror-runtime-error.html
new file mode 100644
index 0000000000..faaddd9ed9
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/body-onerror-runtime-error.html
@@ -0,0 +1,39 @@
+<!doctype html>
+<html>
+ <head>
+ <title>&lt;body onerror> - runtime error in &lt;script></title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <script>
+ setup({allow_uncaught_exception:true});
+ var t = async_test();
+ var t_col = async_test(document.title+' (column)');
+ var ran = false;
+ </script>
+ <body onerror="
+ t.step(function(){
+ ran = true;
+ assert_equals(typeof event, 'string', 'first arg');
+ assert_equals(source, location.href, 'second arg');
+ assert_equals(typeof lineno, 'number', 'third arg');
+ });
+ t_col.step(function(){
+ assert_equals(typeof colno, 'number', 'fourth arg');
+ });
+ ">
+ <div id=log></div>
+ <script>
+ undefined_variable;
+ </script>
+ <script>
+ t.step(function(){
+ assert_true(ran, 'ran');
+ t.done();
+ });
+ t_col.step(function(){
+ t_col.done();
+ });
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error-cross-origin-setInterval.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error-cross-origin-setInterval.html
new file mode 100644
index 0000000000..c4028e650b
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error-cross-origin-setInterval.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<html>
+ <head>
+ <title>window.onerror - compile error in cross-origin setInterval</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id=log></div>
+ <script>
+ setup({allow_uncaught_exception:true});
+ var t = async_test();
+ var ran = false;
+ var interval;
+ window.addEventListener('error', t.step_func(e => {
+ clearInterval(interval);
+ ran = true;
+ assert_equals(e.error.constructor, SyntaxError);
+ }));
+ var script = document.createElement('script');
+ script.src = location.href.replace('://', '://www1.').replace(/\/[^\/]+$/, '/support/syntax-error-in-setInterval.js');
+ document.body.appendChild(script);
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error-cross-origin-setTimeout.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error-cross-origin-setTimeout.html
new file mode 100644
index 0000000000..1eebf82fbb
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error-cross-origin-setTimeout.html
@@ -0,0 +1,23 @@
+<!doctype html>
+<html>
+ <head>
+ <title>window.onerror - compile error in cross-origin setTimeout</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id=log></div>
+ <script>
+ setup({allow_uncaught_exception:true});
+ var t = async_test();
+ var ran = false;
+ window.addEventListener('error', t.step_func(e => {
+ ran = true;
+ assert_equals(e.error.constructor, SyntaxError);
+ }));
+ var script = document.createElement('script');
+ script.src = location.href.replace('://', '://www1.').replace(/\/[^\/]+$/, '/support/syntax-error-in-setTimeout.js');
+ document.body.appendChild(script);
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error-cross-origin.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error-cross-origin.html
new file mode 100644
index 0000000000..b7e989529f
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error-cross-origin.html
@@ -0,0 +1,38 @@
+<!doctype html>
+<html>
+ <head>
+ <title>window.onerror - compile error in &lt;script src=//www1...></title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id=log></div>
+ <script>
+ setup({allow_uncaught_exception:true});
+ var t = async_test();
+ var t_col = async_test(document.title+' (column)');
+ var ran = false;
+ var col_value;
+ window.onerror = t.step_func(function(a, b, c, d){
+ ran = true;
+ col_value = d;
+ assert_equals(a, 'Script error.', 'first arg');
+ assert_equals(b, '', 'second arg');
+ assert_equals(c, 0, 'third arg');
+ });
+ var script = document.createElement('script');
+ script.src = location.href.replace('://', '://www1.').replace(/\/[^\/]+$/, '/support/syntax-error.js');
+ document.body.appendChild(script);
+ onload = function(){
+ t.step(function(){
+ assert_true(ran, 'ran');
+ t.done();
+ });
+ t_col.step(function(){
+ assert_equals(col_value, 0, 'fourth arg');
+ t_col.done();
+ });
+ };
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error-data-url.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error-data-url.html
new file mode 100644
index 0000000000..08ce2f348f
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error-data-url.html
@@ -0,0 +1,36 @@
+<!doctype html>
+<html>
+ <head>
+ <title>window.onerror - compile error in &lt;script src=data:...></title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id=log></div>
+ <script>
+ setup({allow_uncaught_exception:true});
+ var t = async_test();
+ var t_col = async_test(document.title+' (column)');
+ var ran = false;
+ var col_value;
+ window.onerror = t.step_func(function(a, b, c, d){
+ ran = true;
+ col_value = d;
+ assert_equals(typeof a, 'string', 'first arg');
+ assert_equals(b, 'data:text/javascript,for(;){}', 'second arg');
+ assert_equals(typeof c, 'number', 'third arg');
+ });
+ </script>
+ <script src="data:text/javascript,for(;){}"></script>
+ <script>
+ t.step(function(){
+ assert_true(ran, 'ran');
+ t.done();
+ });
+ t_col.step(function(){
+ assert_equals(typeof col_value, 'number', 'fourth arg');
+ t_col.done();
+ });
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error-in-attribute.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error-in-attribute.html
new file mode 100644
index 0000000000..864d09fc1e
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error-in-attribute.html
@@ -0,0 +1,39 @@
+<!doctype html>
+<html>
+ <head>
+ <title>window.onerror - compile error in attribute</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id=log></div>
+ <script>
+ setup({allow_uncaught_exception:true});
+ var t = async_test();
+ var t_col = async_test(document.title+' (column)');
+ var ran = false;
+ var col_value;
+ window.onerror = t.step_func(function(a, b, c, d){
+ ran = true;
+ col_value = d;
+ assert_equals(typeof a, 'string', 'first arg');
+ assert_equals(b, location.href, 'second arg');
+ assert_equals(typeof c, 'number', 'third arg');
+ });
+ </script>
+ <p onclick="{"></p>
+ <script>
+ t.step(function(){
+ var ev = document.createEvent('Event');
+ ev.initEvent('click', false, false);
+ document.querySelector('p').dispatchEvent(ev);
+ assert_true(ran, 'ran');
+ t.done();
+ });
+ t_col.step(function(){
+ assert_equals(typeof col_value, 'number', 'fourth arg');
+ t_col.done();
+ });
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error-in-body-onerror.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error-in-body-onerror.html
new file mode 100644
index 0000000000..0b094e71c3
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error-in-body-onerror.html
@@ -0,0 +1,28 @@
+<!doctype html>
+<html>
+ <head>
+ <title>window.onerror - compile error in &lt;body onerror></title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ setup({allow_uncaught_exception:true});
+ var t = async_test();
+ var ran = false;
+ window.onerror = t.step_func(function(){
+ ran = true;
+ });
+ </script>
+ </head>
+ <body onerror="{"><!-- sets the event handler to null before compiling -->
+ <div id=log></div>
+ <script>
+ for(;) {}
+ </script>
+ <script>
+ t.step(function(){
+ assert_false(ran, 'ran');
+ t.done();
+ });
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error-in-setInterval.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error-in-setInterval.html
new file mode 100644
index 0000000000..79ca7d524a
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error-in-setInterval.html
@@ -0,0 +1,39 @@
+<!doctype html>
+<html>
+ <head>
+ <title>window.onerror - compile error in setInterval</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id=log></div>
+ <script>
+ setup({allow_uncaught_exception:true});
+ var t = async_test();
+ var t_col = async_test(document.title+' (column)');
+ var ran = false;
+ var col_value;
+ var interval;
+ window.onerror = t.step_func(function(a, b, c, d){
+ clearInterval(interval);
+ ran = true;
+ col_value = d;
+ assert_equals(typeof a, 'string', 'first arg');
+ assert_equals(b, location.href, 'second arg');
+ assert_equals(typeof c, 'number', 'third arg');
+ });
+ interval = setInterval("{", 10);
+ step_timeout(function(){
+ t.step(function(){
+ clearInterval(interval);
+ assert_true(ran, 'ran');
+ t.done();
+ });
+ t_col.step(function(){
+ assert_equals(typeof col_value, 'number', 'fourth arg');
+ t_col.done();
+ });
+ }, 20);
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error-in-setTimeout.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error-in-setTimeout.html
new file mode 100644
index 0000000000..1bb730e134
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error-in-setTimeout.html
@@ -0,0 +1,36 @@
+<!doctype html>
+<html>
+ <head>
+ <title>window.onerror - compile error in setTimeout</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id=log></div>
+ <script>
+ setup({allow_uncaught_exception:true});
+ var t = async_test();
+ var t_col = async_test(document.title+' (column)');
+ var ran = false;
+ var col_value;
+ window.onerror = t.step_func(function(a, b, c, d){
+ ran = true;
+ col_value = d;
+ assert_equals(typeof a, 'string', 'first arg');
+ assert_equals(b, location.href, 'second arg');
+ assert_equals(typeof c, 'number', 'third arg');
+ });
+ setTimeout("{", 10);
+ setTimeout(function(){
+ t.step(function(){
+ assert_true(ran, 'ran');
+ t.done();
+ });
+ t_col.step(function(){
+ assert_equals(typeof col_value, 'number', 'fourth arg');
+ t_col.done();
+ });
+ }, 20);
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error-same-origin-with-hash.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error-same-origin-with-hash.html
new file mode 100644
index 0000000000..c367e6cb2f
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error-same-origin-with-hash.html
@@ -0,0 +1,36 @@
+<!doctype html>
+<html>
+ <head>
+ <title>window.onerror - compile error in &lt;script src=...> with hash</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id=log></div>
+ <script>
+ setup({allow_uncaught_exception:true});
+ var t = async_test();
+ var t_col = async_test(document.title+' (column)');
+ var ran = false;
+ var col_value;
+ window.onerror = t.step_func(function(a, b, c, d){
+ ran = true;
+ col_value = d;
+ assert_equals(typeof a, 'string', 'first arg');
+ assert_equals(b, document.querySelector('script[src="support/syntax-error.js#"]').src, 'second arg');
+ assert_equals(typeof c, 'number', 'third arg');
+ });
+ </script>
+ <script src="support/syntax-error.js#"></script>
+ <script>
+ t.step(function(){
+ assert_true(ran, 'ran');
+ t.done();
+ });
+ t_col.step(function(){
+ assert_equals(typeof col_value, 'number', 'fourth arg');
+ t_col.done();
+ });
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error-same-origin.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error-same-origin.html
new file mode 100644
index 0000000000..71c28b584d
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error-same-origin.html
@@ -0,0 +1,36 @@
+<!doctype html>
+<html>
+ <head>
+ <title>window.onerror - compile error in &lt;script src=...></title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id=log></div>
+ <script>
+ setup({allow_uncaught_exception:true});
+ var t = async_test();
+ var t_col = async_test(document.title+' (column)');
+ var ran = false;
+ var col_value;
+ window.onerror = t.step_func(function(a, b, c, d){
+ ran = true;
+ col_value = d;
+ assert_equals(typeof a, 'string', 'first arg');
+ assert_equals(b, document.querySelector('script[src="support/syntax-error.js"]').src, 'second arg');
+ assert_equals(typeof c, 'number', 'third arg');
+ });
+ </script>
+ <script src="support/syntax-error.js"></script>
+ <script>
+ t.step(function(){
+ assert_true(ran, 'ran');
+ t.done();
+ });
+ t_col.step(function(){
+ assert_equals(typeof col_value, 'number', 'fourth arg');
+ t_col.done();
+ });
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error.html
new file mode 100644
index 0000000000..a4bdfd9c47
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/compile-error.html
@@ -0,0 +1,38 @@
+<!doctype html>
+<html>
+ <head>
+ <title>window.onerror - compile error in &lt;script></title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id=log></div>
+ <script>
+ setup({allow_uncaught_exception:true});
+ var t = async_test();
+ var t_col = async_test(document.title+' (column)');
+ var ran = false;
+ var col_value;
+ window.onerror = t.step_func(function(a, b, c, d){
+ ran = true;
+ col_value = d;
+ assert_equals(typeof a, 'string', 'first arg');
+ assert_equals(b, location.href, 'second arg');
+ assert_equals(typeof c, 'number', 'third arg');
+ });
+ </script>
+ <script>
+ for(;) {}
+ </script>
+ <script>
+ t.step(function(){
+ assert_true(ran, 'ran');
+ t.done();
+ });
+ t_col.step(function(){
+ assert_equals(typeof col_value, 'number', 'fourth arg');
+ t_col.done();
+ });
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-agent-formalism/atomics-wait-async.https.any.js b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-agent-formalism/atomics-wait-async.https.any.js
new file mode 100644
index 0000000000..3a3ea40d77
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-agent-formalism/atomics-wait-async.https.any.js
@@ -0,0 +1,25 @@
+// META: global=window,dedicatedworker
+
+promise_test(async () => {
+ const sab = new SharedArrayBuffer(64);
+ const ta = new Int32Array(sab);
+
+ const waitAsyncObj = Atomics.waitAsync(ta, 0, 0, 10);
+ assert_equals(waitAsyncObj.async, true);
+ const v = await waitAsyncObj.value;
+ assert_equals(v, "timed-out");
+}, `Atomics.waitAsync timeout in a ${self.constructor.name}`);
+
+promise_test(async () => {
+ const sab = new SharedArrayBuffer(64);
+ const ta = new Int32Array(sab);
+
+ const waitAsyncObj = Atomics.waitAsync(ta, 0, 0);
+ assert_equals(waitAsyncObj.async, true);
+
+ const worker = new Worker("resources/notify-worker.js");
+ worker.postMessage(sab);
+
+ const v = await waitAsyncObj.value;
+ assert_equals(v, "ok");
+}, `Atomics.waitAsync notification in a ${self.constructor.name}`);
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-agent-formalism/atomics-wait-async.https.any.js.headers b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-agent-formalism/atomics-wait-async.https.any.js.headers
new file mode 100644
index 0000000000..5f8621ef83
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-agent-formalism/atomics-wait-async.https.any.js.headers
@@ -0,0 +1,2 @@
+Cross-Origin-Embedder-Policy: require-corp
+Cross-Origin-Opener-Policy: same-origin
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-agent-formalism/requires-failure.https.any.js b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-agent-formalism/requires-failure.https.any.js
new file mode 100644
index 0000000000..fddf85dbed
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-agent-formalism/requires-failure.https.any.js
@@ -0,0 +1,11 @@
+// META: global=window,serviceworker
+
+test(() => {
+ // See https://github.com/whatwg/html/issues/5380 for why not `new SharedArrayBuffer()`
+ const sab = new WebAssembly.Memory({ shared:true, initial:1, maximum:1 }).buffer;
+ const ta = new Int32Array(sab);
+
+ assert_throws_js(TypeError, () => {
+ Atomics.wait(ta, 0, 0, 10);
+ });
+}, `[[CanBlock]] in a ${self.constructor.name}`);
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-agent-formalism/requires-success.any.js b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-agent-formalism/requires-success.any.js
new file mode 100644
index 0000000000..0da449a7cf
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-agent-formalism/requires-success.any.js
@@ -0,0 +1,9 @@
+// META: global=dedicatedworker,sharedworker
+
+test(() => {
+ // See https://github.com/whatwg/html/issues/5380 for why not `new SharedArrayBuffer()`
+ const sab = new WebAssembly.Memory({ shared:true, initial:1, maximum:1 }).buffer;
+ const ta = new Int32Array(sab);
+
+ assert_equals(Atomics.wait(ta, 0, 0, 10), "timed-out");
+}, `[[CanBlock]] in a ${self.constructor.name}`);
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-agent-formalism/resources/notify-worker.js b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-agent-formalism/resources/notify-worker.js
new file mode 100644
index 0000000000..2780e5bc34
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-agent-formalism/resources/notify-worker.js
@@ -0,0 +1,5 @@
+onmessage = (e) => {
+ const sab = e.data;
+ const ta = new Int32Array(sab);
+ Atomics.notify(ta, 0);
+};
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-agent-formalism/resources/notify-worker.js.headers b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-agent-formalism/resources/notify-worker.js.headers
new file mode 100644
index 0000000000..6604450991
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-agent-formalism/resources/notify-worker.js.headers
@@ -0,0 +1 @@
+Cross-Origin-Embedder-Policy: require-corp
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/promise-job-entry-different-function-realm.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/promise-job-entry-different-function-realm.html
new file mode 100644
index 0000000000..71f03a4dcf
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/promise-job-entry-different-function-realm.html
@@ -0,0 +1,112 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Entry settings object for promise jobs when the function realm is different from the test realm</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<!-- https://github.com/whatwg/html/pull/5212 -->
+<!-- https://github.com/whatwg/html/issues/1426 -->
+
+<!-- This is what would normally be considered the entry page. However, we use functions from the
+ resources/function/function.html realm. So window.open() should resolve relative to that realm
+ inside promise jobs. -->
+
+<iframe src="resources/promise-job-entry-incumbent.html"></iframe>
+<iframe src="resources/function/function.html" id="function-frame"></iframe>
+
+<script>
+setup({ explicit_done: true });
+
+const relativeURL = "resources/window-to-open.html";
+const expectedURL = (new URL(relativeURL, document.querySelector("#function-frame").src)).href;
+
+const incumbentWindow = frames[0];
+const functionWindow = frames[1];
+const FunctionFromAnotherWindow = frames[1].Function;
+
+window.onload = () => {
+ async_test(t => {
+ const func = FunctionFromAnotherWindow(`
+ const [incumbentWindow, relativeURL, t, assert_equals, expectedURL] = arguments[0];
+
+ const w = incumbentWindow.runWindowOpenVeryIndirectly(relativeURL);
+ w.onload = t.step_func_done(() => {
+ t.add_cleanup(() => w.close());
+ assert_equals(w.location.href, expectedURL);
+ });
+ `);
+
+ Promise.resolve([incumbentWindow, relativeURL, t, assert_equals, expectedURL]).then(func);
+ }, "Fulfillment handler on fulfilled promise");
+
+ async_test(t => {
+ const func = FunctionFromAnotherWindow(`
+ const [incumbentWindow, relativeURL, t, assert_equals, expectedURL] = arguments[0];
+
+ const w = incumbentWindow.runWindowOpenVeryIndirectly(relativeURL);
+ w.onload = t.step_func_done(() => {
+ t.add_cleanup(() => w.close());
+ assert_equals(w.location.href, expectedURL);
+ });
+ `);
+
+ Promise.reject([incumbentWindow, relativeURL, t, assert_equals, expectedURL]).catch(func);
+ }, "Rejection handler on rejected promise");
+
+ async_test(t => {
+ let resolve;
+ const p = new Promise(r => { resolve = r; });
+
+ const func = FunctionFromAnotherWindow(`
+ const [incumbentWindow, relativeURL, t, assert_equals, expectedURL] = arguments[0];
+
+ const w = incumbentWindow.runWindowOpenVeryIndirectly(relativeURL);
+ w.onload = t.step_func_done(() => {
+ t.add_cleanup(() => w.close());
+ assert_equals(w.location.href, expectedURL);
+ });
+ `);
+
+ p.then(func);
+ t.step_timeout(() => resolve([incumbentWindow, relativeURL, t, assert_equals, expectedURL]), 0);
+ }, "Fulfillment handler on pending-then-fulfilled promise");
+
+ async_test(t => {
+ let reject;
+ const p = new Promise((_, r) => { reject = r; });
+
+ const func = FunctionFromAnotherWindow(`
+ const [incumbentWindow, relativeURL, t, assert_equals, expectedURL] = arguments[0];
+
+ const w = incumbentWindow.runWindowOpenVeryIndirectly(relativeURL);
+ w.onload = t.step_func_done(() => {
+ t.add_cleanup(() => w.close());
+ assert_equals(w.location.href, expectedURL);
+ });
+ `);
+
+ p.catch(func);
+ t.step_timeout(() => reject([incumbentWindow, relativeURL, t, assert_equals, expectedURL]), 0);
+ }, "Rejection handler on pending-then-rejected promise");
+
+ async_test(t => {
+ t.add_cleanup(() => { delete frames[1].args; });
+ frames[1].args = [incumbentWindow, relativeURL, t, assert_equals, expectedURL];
+
+ const func = FunctionFromAnotherWindow(`
+ const [incumbentWindow, relativeURL, t, assert_equals, expectedURL] = window.args;
+
+ const w = incumbentWindow.runWindowOpenVeryIndirectly(relativeURL);
+ w.onload = t.step_func_done(() => {
+ t.add_cleanup(() => w.close());
+ assert_equals(w.location.href, expectedURL);
+ });
+ `);
+
+ const thenable = { then: func };
+
+ Promise.resolve(thenable);
+ }, "Thenable resolution");
+
+ done();
+};
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/promise-job-entry.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/promise-job-entry.html
new file mode 100644
index 0000000000..6d075d674c
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/promise-job-entry.html
@@ -0,0 +1,101 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Entry settings object for promise jobs</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<!-- https://github.com/whatwg/html/pull/5212 -->
+<!-- https://github.com/whatwg/html/issues/1426 -->
+
+<!-- This is the entry page, so window.open() should resolve relative to it, even inside promise jobs. -->
+
+<iframe src="resources/promise-job-entry-incumbent.html"></iframe>
+
+<script>
+setup({ explicit_done: true });
+
+const relativeURL = "resources/window-to-open.html";
+const expectedURL = (new URL(relativeURL, location.href)).href;
+
+const incumbentWindow = frames[0];
+
+window.onload = () => {
+ async_test(t => {
+ const w = incumbentWindow.runWindowOpenVeryIndirectly(relativeURL);
+ w.onload = t.step_func_done(() => {
+ t.add_cleanup(() => w.close());
+ assert_equals(w.location.href, expectedURL);
+ });
+ }, "Sanity check: this all works as expected with no promises involved");
+
+ async_test(t => {
+ // No t.step_func because that could change the realms
+ Promise.resolve().then(() => {
+ const w = incumbentWindow.runWindowOpenVeryIndirectly(relativeURL);
+ w.onload = t.step_func_done(() => {
+ t.add_cleanup(() => w.close());
+ assert_equals(w.location.href, expectedURL);
+ });
+ });
+ }, "Fulfillment handler on fulfilled promise");
+
+ async_test(t => {
+ // No t.step_func because that could change the realms
+ Promise.reject().catch(() => {
+ const w = incumbentWindow.runWindowOpenVeryIndirectly(relativeURL);
+ w.onload = t.step_func_done(() => {
+ t.add_cleanup(() => w.close());
+ assert_equals(w.location.href, expectedURL);
+ });
+ });
+ }, "Rejection handler on rejected promise");
+
+ async_test(t => {
+ let resolve;
+ const p = new Promise(r => { resolve = r; });
+
+ // No t.step_func because that could change the realms
+ p.then(() => {
+ const w = incumbentWindow.runWindowOpenVeryIndirectly(relativeURL);
+ w.onload = t.step_func_done(() => {
+ t.add_cleanup(() => w.close());
+ assert_equals(w.location.href, expectedURL);
+ });
+ });
+
+ t.step_timeout(resolve, 0);
+ }, "Fulfillment handler on pending-then-fulfilled promise");
+
+ async_test(t => {
+ let reject;
+ const p = new Promise((_, r) => { reject = r; });
+
+ // No t.step_func because that could change the realms
+ p.catch(() => {
+ const w = incumbentWindow.runWindowOpenVeryIndirectly(relativeURL);
+ w.onload = t.step_func_done(() => {
+ t.add_cleanup(() => w.close());
+ assert_equals(w.location.href, expectedURL);
+ });
+ });
+
+ t.step_timeout(reject, 0);
+ }, "Rejection handler on pending-then-rejected promise");
+
+ async_test(t => {
+ const thenable = {
+ // No t.step_func because that could change the realms
+ then(f) {
+ const w = incumbentWindow.runWindowOpenVeryIndirectly(relativeURL);
+ w.onload = t.step_func_done(() => {
+ t.add_cleanup(() => w.close());
+ assert_equals(w.location.href, expectedURL);
+ });
+ }
+ };
+
+ Promise.resolve(thenable);
+ }, "Thenable resolution");
+
+ done();
+};
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/promise-job-incumbent.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/promise-job-incumbent.html
new file mode 100644
index 0000000000..af00f834c1
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/promise-job-incumbent.html
@@ -0,0 +1,164 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Incumbent settings object for promise jobs</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<!-- This is the entry page. -->
+
+<iframe src="resources/promise-job-incumbent-incumbent.html"></iframe>
+<iframe src="resources/promise-job-incumbent-resolver.html"></iframe>
+
+<script>
+setup({ explicit_done: true });
+
+// postMessage should pick the incumbent page as its .source value to set on the MessageEvent, even
+// inside promise jobs.
+const expectedURL = (new URL("resources/promise-job-incumbent-incumbent.html", location.href)).href;
+
+let testId = 0;
+
+window.onload = () => {
+ const relevantWindow = frames[0].document.querySelector("#r").contentWindow;
+ const runInResolver = frames[1].runWhatYouGiveMe;
+
+ function setupTest(t) {
+ ++testId;
+ const thisTestId = testId;
+
+ relevantWindow.addEventListener("messagereceived", t.step_func(e => {
+ const [receivedTestId, receivedSourceURL] = e.detail;
+
+ if (receivedTestId !== thisTestId) {
+ return;
+ }
+
+ assert_equals(receivedSourceURL, expectedURL);
+ t.done();
+ }));
+
+ return thisTestId;
+ }
+
+ async_test(t => {
+ const thisTestId = setupTest(t);
+
+ frames[0].runWindowPostMessageVeryIndirectly(thisTestId, "*");
+ }, "Sanity check: this all works as expected with no promises involved");
+
+ async_test(t => {
+ const thisTestId = setupTest(t);
+
+ // No t.step_func because that could change the realms
+ Promise.resolve().then(() => {
+ frames[0].runWindowPostMessageVeryIndirectly(thisTestId, "*");
+ });
+ }, "Fulfillment handler on fulfilled promise");
+
+ async_test(t => {
+ const thisTestId = setupTest(t);
+
+ const p = Promise.resolve();
+ frames[0].runWindowPostMessageVeryIndirectlyWithNoUserCode(p, "then", thisTestId, "*");
+ }, "Fulfillment handler on fulfilled promise, using backup incumbent settings object stack");
+
+ async_test(t => {
+ const thisTestId = setupTest(t);
+
+ // No t.step_func because that could change the realms
+ Promise.reject().catch(() => {
+ frames[0].runWindowPostMessageVeryIndirectly(thisTestId, "*");
+ });
+ }, "Rejection handler on rejected promise");
+
+ async_test(t => {
+ const thisTestId = setupTest(t);
+
+ const p = Promise.reject();
+ frames[0].runWindowPostMessageVeryIndirectlyWithNoUserCode(p, "catch", thisTestId, "*");
+ }, "Rejection handler on rejected promise, using backup incumbent settings object stack");
+
+ // The following tests test that we derive the incumbent settings object at promise-job time from
+ // the incumbent realm at the time the handler was added, not at the time the resolve()/reject()
+ // was done. See https://github.com/whatwg/html/issues/5213 for the spec side of this issue.
+
+ async_test(t => {
+ const thisTestId = setupTest(t);
+
+ let resolve;
+ const p = new Promise(r => { resolve = r; });
+
+ // No t.step_func because that could change the realms
+ p.then(() => {
+ frames[0].runWindowPostMessageVeryIndirectly(thisTestId, "*");
+ });
+
+ t.step_timeout(() => {
+ runInResolver(resolve);
+ }, 0);
+ }, "Fulfillment handler on pending-then-fulfilled promise");
+
+ async_test(t => {
+ const thisTestId = setupTest(t);
+
+ let resolve;
+ const p = new Promise(r => { resolve = r; });
+
+ frames[0].runWindowPostMessageVeryIndirectlyWithNoUserCode(p, "then", thisTestId, "*");
+
+ t.step_timeout(() => {
+ runInResolver(resolve);
+ }, 0);
+ }, "Fulfillment handler on pending-then-fulfilled promise, using backup incumbent settings object stack");
+
+ async_test(t => {
+ const thisTestId = setupTest(t);
+
+ let reject;
+ const p = new Promise((_, r) => { reject = r; });
+
+ // No t.step_func because that could change the realms
+ p.catch(() => {
+ frames[0].runWindowPostMessageVeryIndirectly(thisTestId, "*");
+ });
+
+ t.step_timeout(() => {
+ runInResolver(reject);
+ }, 0);
+ }, "Rejection handler on pending-then-rejected promise");
+
+ async_test(t => {
+ const thisTestId = setupTest(t);
+
+ let reject;
+ const p = new Promise((_, r) => { reject = r; });
+
+ frames[0].runWindowPostMessageVeryIndirectlyWithNoUserCode(p, "catch", thisTestId, "*");
+
+ t.step_timeout(() => {
+ runInResolver(reject);
+ }, 0);
+ }, "Rejection handler on pending-then-rejected promise, using backup incumbent settings object stack");
+
+ async_test(t => {
+ const thisTestId = setupTest(t);
+
+ const thenable = {
+ // No t.step_func because that could change the realms
+ then(f) {
+ frames[0].runWindowPostMessageVeryIndirectly(thisTestId, "*");
+ }
+ };
+
+ Promise.resolve(thenable);
+ }, "Thenable resolution");
+
+ async_test(t => {
+ const thisTestId = setupTest(t);
+
+ frames[0].resolveThenableThatRunsWindowPostMessageVeryIndirectlyWithNoUserCode(testId, "*", []);
+ }, "Thenable resolution, using backup incumbent settings object stack");
+
+ done();
+};
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/README.md b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/README.md
new file mode 100644
index 0000000000..a89258a4e0
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/README.md
@@ -0,0 +1,5 @@
+A couple notes about the files scattered in this `resources/` directory:
+
+* The nested directory structure is necessary here so that relative URL resolution can be tested; we need different sub-paths for each document.
+
+* The semi-duplicate `window-to-open.html`s scattered throughout are present because Firefox, at least, does not fire `Window` `load` events for 404s, so we want to ensure that no matter which global is used, `window`'s `load` event is hit and our tests can proceed.
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/current/current.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/current/current.html
new file mode 100644
index 0000000000..63d9c437fc
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/current/current.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Current page used as a test helper</title>
+
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/current/resources/window-to-open.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/current/resources/window-to-open.html
new file mode 100644
index 0000000000..1bc4cca9a3
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/current/resources/window-to-open.html
@@ -0,0 +1,3 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>If the current settings object is used this page will be opened</title>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/function/function.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/function/function.html
new file mode 100644
index 0000000000..15841d387d
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/function/function.html
@@ -0,0 +1,3 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Realm for a "then" function used as a test helper</title>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/function/resources/window-to-open.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/function/resources/window-to-open.html
new file mode 100644
index 0000000000..3928c1f8aa
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/function/resources/window-to-open.html
@@ -0,0 +1,3 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>If the function's settings object is used this page will be opened</title>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/promise-job-entry-incumbent.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/promise-job-entry-incumbent.html
new file mode 100644
index 0000000000..3740c1467d
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/promise-job-entry-incumbent.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Incumbent page used as a test helper</title>
+
+<iframe src="relevant/relevant.html" id="r"></iframe>
+<iframe src="current/current.html" id="c"></iframe>
+
+<script>
+ const relevant = document.querySelector("#r");
+ const current = document.querySelector("#c");
+
+ window.runWindowOpenVeryIndirectly = (...args) => {
+ return current.contentWindow.open.call(relevant.contentWindow, ...args);
+ };
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/promise-job-incumbent-incumbent.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/promise-job-incumbent-incumbent.html
new file mode 100644
index 0000000000..57dd5dff10
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/promise-job-incumbent-incumbent.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Incumbent page used as a test helper</title>
+
+<iframe src="relevant/relevant.html" id="r"></iframe>
+<iframe src="current/current.html" id="c"></iframe>
+
+<script>
+ const relevant = document.querySelector("#r");
+ const current = document.querySelector("#c");
+
+ window.runWindowPostMessageVeryIndirectly = (...args) => {
+ return current.contentWindow.postMessage.call(relevant.contentWindow, ...args);
+ };
+
+ // This tests the backup incumbent settings object stack scenario, by avoiding putting user code on the stack.
+ window.runWindowPostMessageVeryIndirectlyWithNoUserCode = (promise, promiseMethod, ...args) => {
+ const runWindowPostMessage = current.contentWindow.postMessage.bind(relevant.contentWindow, ...args);
+ promise[promiseMethod](runWindowPostMessage);
+ };
+
+ window.resolveThenableThatRunsWindowPostMessageVeryIndirectlyWithNoUserCode = (...args) => {
+ Promise.resolve({
+ then: current.contentWindow.postMessage.bind(relevant.contentWindow, ...args)
+ });
+ };
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/promise-job-incumbent-resolver.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/promise-job-incumbent-resolver.html
new file mode 100644
index 0000000000..a730b9c3ce
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/promise-job-incumbent-resolver.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Incumbent page used as a test helper</title>
+
+<script>
+ window.runWhatYouGiveMe = (func) => {
+ func();
+ };
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/relevant/relevant.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/relevant/relevant.html
new file mode 100644
index 0000000000..f5965f2231
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/relevant/relevant.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Relevant page used as a test helper</title>
+
+<script>
+// promise-job-incumbent will end up posting a message to here. We need to signal back the "source".
+
+window.onmessage = e => {
+ const testId = e.data;
+ const sourceURL = e.source.document.URL;
+
+ window.dispatchEvent(new CustomEvent("messagereceived", { detail: [testId, sourceURL] }));
+};
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/relevant/resources/window-to-open.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/relevant/resources/window-to-open.html
new file mode 100644
index 0000000000..4138b5a084
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/relevant/resources/window-to-open.html
@@ -0,0 +1,3 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>If the relevant settings object is used this page will be opened</title>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/resources/window-to-open.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/resources/window-to-open.html
new file mode 100644
index 0000000000..7743b9b578
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/resources/window-to-open.html
@@ -0,0 +1,3 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>If the incumbent settings object is used this page will be opened</title>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/window-to-open.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/window-to-open.html
new file mode 100644
index 0000000000..ce357937f5
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/window-to-open.html
@@ -0,0 +1,3 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>If the entry settings object is used this page will be opened</title>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-cross-origin-setInterval.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-cross-origin-setInterval.html
new file mode 100644
index 0000000000..8b92f7d148
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-cross-origin-setInterval.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<html>
+ <head>
+ <title>window.onerror - runtime error in cross-origin setInterval</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id=log></div>
+ <script>
+ setup({allow_uncaught_exception:true});
+ var t = async_test();
+ var ran = false;
+ var interval;
+ window.addEventListener('error', t.step_func(e => {
+ clearInterval(interval);
+ ran = true;
+ assert_equals(e.error.constructor, ReferenceError);
+ }));
+ var script = document.createElement('script');
+ script.src = location.href.replace('://', '://www1.').replace(/\/[^\/]+$/, '/support/undefined-variable-in-setInterval.js');
+ document.body.appendChild(script);
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-cross-origin-setTimeout.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-cross-origin-setTimeout.html
new file mode 100644
index 0000000000..2e1a9d2315
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-cross-origin-setTimeout.html
@@ -0,0 +1,23 @@
+<!doctype html>
+<html>
+ <head>
+ <title>window.onerror - runtime error in cross-origin setTimeout</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id=log></div>
+ <script>
+ setup({allow_uncaught_exception:true});
+ var t = async_test();
+ var ran = false;
+ window.addEventListener('error', t.step_func(e => {
+ ran = true;
+ assert_equals(e.error.constructor, ReferenceError);
+ }));
+ var script = document.createElement('script');
+ script.src = location.href.replace('://', '://www1.').replace(/\/[^\/]+$/, '/support/undefined-variable-in-setTimeout.js');
+ document.body.appendChild(script);
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-cross-origin.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-cross-origin.html
new file mode 100644
index 0000000000..d63aaa6d3b
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-cross-origin.html
@@ -0,0 +1,38 @@
+<!doctype html>
+<html>
+ <head>
+ <title>window.onerror - runtime error in &lt;script src=//www1...></title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id=log></div>
+ <script>
+ setup({allow_uncaught_exception:true});
+ var t = async_test();
+ var t_col = async_test(document.title+' (column)');
+ var ran = false;
+ var col_value;
+ window.onerror = t.step_func(function(a, b, c, d){
+ ran = true;
+ col_value = d;
+ assert_equals(a, 'Script error.', 'first arg');
+ assert_equals(b, '', 'second arg');
+ assert_equals(c, 0, 'third arg');
+ });
+ var script = document.createElement('script');
+ script.src = location.href.replace('://', '://www1.').replace(/\/[^\/]+$/, '/support/undefined-variable.js');
+ document.body.appendChild(script);
+ onload = function(){
+ t.step(function(){
+ assert_true(ran, 'ran');
+ t.done();
+ });
+ t_col.step(function(){
+ assert_equals(col_value, 0, 'fourth arg');
+ t_col.done();
+ });
+ };
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-data-url.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-data-url.html
new file mode 100644
index 0000000000..485ce90aa6
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-data-url.html
@@ -0,0 +1,36 @@
+<!doctype html>
+<html>
+ <head>
+ <title>window.onerror - runtime error in &lt;script src=data:...></title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id=log></div>
+ <script>
+ setup({allow_uncaught_exception:true});
+ var t = async_test();
+ var t_col = async_test(document.title+' (column)');
+ var ran = false;
+ var col_value;
+ window.onerror = t.step_func(function(a, b, c, d){
+ ran = true;
+ col_value = d;
+ assert_equals(typeof a, 'string', 'first arg');
+ assert_equals(b, 'data:text/javascript,undefined_variable;', 'second arg');
+ assert_equals(typeof c, 'number', 'third arg');
+ });
+ </script>
+ <script src="data:text/javascript,undefined_variable;"></script>
+ <script>
+ t.step(function(){
+ assert_true(ran, 'ran');
+ t.done();
+ });
+ t_col.step(function(){
+ assert_equals(typeof col_value, 'number', 'fourth arg');
+ t_col.done();
+ });
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-in-attribute.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-in-attribute.html
new file mode 100644
index 0000000000..b4f69da7a2
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-in-attribute.html
@@ -0,0 +1,39 @@
+<!doctype html>
+<html>
+ <head>
+ <title>window.onerror - runtime error in attribute</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id=log></div>
+ <script>
+ setup({allow_uncaught_exception:true});
+ var t = async_test();
+ var t_col = async_test(document.title+' (column)');
+ var ran = false;
+ var col_value;
+ window.onerror = t.step_func(function(a, b, c, d){
+ ran = true;
+ col_value = d;
+ assert_equals(typeof a, 'string', 'first arg');
+ assert_equals(b, location.href, 'second arg');
+ assert_equals(typeof c, 'number', 'third arg');
+ });
+ </script>
+ <p onclick="undefined_variable;"></p>
+ <script>
+ t.step(function(){
+ var ev = document.createEvent('Event');
+ ev.initEvent('click', false, false);
+ document.querySelector('p').dispatchEvent(ev);
+ assert_true(ran, 'ran');
+ t.done();
+ });
+ t_col.step(function(){
+ assert_equals(typeof col_value, 'number', 'fourth arg');
+ t_col.done();
+ });
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-in-body-onerror.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-in-body-onerror.html
new file mode 100644
index 0000000000..e0fd1dcbd5
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-in-body-onerror.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<html>
+ <head>
+ <title>runtime error in &lt;body onerror></title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ setup({allow_uncaught_exception:true});
+ var t = async_test();
+ var ran = 0;
+ </script>
+ </head>
+ <body onerror="ran++; undefined_variable_in_onerror;">
+ <div id=log></div>
+ <script>
+ undefined_variable;
+ </script>
+ <script>
+ t.step(function(){
+ assert_equals(ran, 1, 'ran');
+ t.done();
+ });
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-in-setInterval.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-in-setInterval.html
new file mode 100644
index 0000000000..090e1dd78e
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-in-setInterval.html
@@ -0,0 +1,39 @@
+<!doctype html>
+<html>
+ <head>
+ <title>window.onerror - runtime error in setInterval</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id=log></div>
+ <script>
+ setup({allow_uncaught_exception:true});
+ var t = async_test();
+ var t_col = async_test(document.title+' (column)');
+ var ran = false;
+ var col_value;
+ var interval;
+ window.onerror = t.step_func(function(a, b, c, d){
+ clearInterval(interval);
+ ran = true;
+ col_value = d;
+ assert_equals(typeof a, 'string', 'first arg');
+ assert_equals(b, location.href, 'second arg');
+ assert_equals(typeof c, 'number', 'third arg');
+ });
+ interval = setInterval("undefined_variable;", 10);
+ step_timeout(function(){
+ clearInterval(interval);
+ t.step(function(){
+ assert_true(ran, 'ran');
+ t.done();
+ });
+ t_col.step(function(){
+ assert_equals(typeof col_value, 'number', 'fourth arg');
+ t_col.done();
+ });
+ }, 20);
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-in-setTimeout.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-in-setTimeout.html
new file mode 100644
index 0000000000..cebcd4346c
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-in-setTimeout.html
@@ -0,0 +1,36 @@
+<!doctype html>
+<html>
+ <head>
+ <title>window.onerror - runtime error in setTimeout</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id=log></div>
+ <script>
+ setup({allow_uncaught_exception:true});
+ var t = async_test();
+ var t_col = async_test(document.title+' (column)');
+ var ran = false;
+ var col_value;
+ window.onerror = t.step_func(function(a, b, c, d){
+ ran = true;
+ col_value = d;
+ assert_equals(typeof a, 'string', 'first arg');
+ assert_equals(b, location.href, 'second arg');
+ assert_equals(typeof c, 'number', 'third arg');
+ });
+ setTimeout("undefined_variable;", 10);
+ setTimeout(function(){
+ t.step(function(){
+ assert_true(ran, 'ran');
+ t.done();
+ });
+ t_col.step(function(){
+ assert_equals(typeof col_value, 'number', 'fourth arg');
+ t_col.done();
+ });
+ }, 20);
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-in-window-onerror.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-in-window-onerror.html
new file mode 100644
index 0000000000..150a793b79
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-in-window-onerror.html
@@ -0,0 +1,29 @@
+<!doctype html>
+<html>
+ <head>
+ <title>runtime error in window.onerror</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id=log></div>
+ <script>
+ setup({allow_uncaught_exception:true});
+ var t = async_test();
+ var ran = 0;
+ window.onerror = function(){
+ ran++;
+ undefined_variable_in_onerror;
+ };
+ </script>
+ <script>
+ undefined_variable;
+ </script>
+ <script>
+ t.step(function(){
+ assert_equals(ran, 1, 'ran');
+ t.done();
+ });
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-same-origin-with-hash.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-same-origin-with-hash.html
new file mode 100644
index 0000000000..dc6ec059a5
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-same-origin-with-hash.html
@@ -0,0 +1,36 @@
+<!doctype html>
+<html>
+ <head>
+ <title>window.onerror - runtime error in &lt;script src=...> with hash</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id=log></div>
+ <script>
+ setup({allow_uncaught_exception:true});
+ var t = async_test();
+ var t_col = async_test(document.title+' (column)');
+ var ran = false;
+ var col_value;
+ window.onerror = t.step_func(function(a, b, c, d){
+ ran = true;
+ col_value = d;
+ assert_equals(typeof a, 'string', 'first arg');
+ assert_equals(b, document.querySelector('script[src="support/undefined-variable.js#"]').src, 'second arg');
+ assert_equals(typeof c, 'number', 'third arg');
+ });
+ </script>
+ <script src="support/undefined-variable.js#"></script>
+ <script>
+ t.step(function(){
+ assert_true(ran, 'ran');
+ t.done();
+ });
+ t_col.step(function(){
+ assert_equals(typeof col_value, 'number', 'fourth arg');
+ t_col.done();
+ });
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-same-origin.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-same-origin.html
new file mode 100644
index 0000000000..8f3cfb70b2
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error-same-origin.html
@@ -0,0 +1,36 @@
+<!doctype html>
+<html>
+ <head>
+ <title>window.onerror - runtime error in &lt;script src=...></title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id=log></div>
+ <script>
+ setup({allow_uncaught_exception:true});
+ var t = async_test();
+ var t_col = async_test(document.title+' (column)');
+ var ran = false;
+ var col_value;
+ window.onerror = t.step_func(function(a, b, c, d){
+ ran = true;
+ col_value = d;
+ assert_equals(typeof a, 'string', 'first arg');
+ assert_equals(b, document.querySelector('script[src="support/undefined-variable.js"]').src, 'second arg');
+ assert_equals(typeof c, 'number', 'third arg');
+ });
+ </script>
+ <script src="support/undefined-variable.js"></script>
+ <script>
+ t.step(function(){
+ assert_true(ran, 'ran');
+ t.done();
+ });
+ t_col.step(function(){
+ assert_equals(typeof col_value, 'number', 'fourth arg');
+ t_col.done();
+ });
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error.html
new file mode 100644
index 0000000000..7907494aa6
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/runtime-error.html
@@ -0,0 +1,38 @@
+<!doctype html>
+<html>
+ <head>
+ <title>window.onerror - runtime error in &lt;script></title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id=log></div>
+ <script>
+ setup({allow_uncaught_exception:true});
+ var t = async_test();
+ var t_col = async_test(document.title+' (column)');
+ var ran = false;
+ var col_value;
+ window.onerror = t.step_func(function(a, b, c, d){
+ ran = true;
+ col_value = d;
+ assert_equals(typeof a, 'string', 'first arg');
+ assert_equals(b, location.href, 'second arg');
+ assert_equals(typeof c, 'number', 'third arg');
+ });
+ </script>
+ <script>
+ undefined_variable;
+ </script>
+ <script>
+ t.step(function(){
+ assert_true(ran, 'ran');
+ t.done();
+ });
+ t_col.step(function(){
+ assert_equals(typeof col_value, 'number', 'fourth arg');
+ t_col.done();
+ });
+ </script>
+ </body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/support/syntax-error-in-setInterval.js b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/support/syntax-error-in-setInterval.js
new file mode 100644
index 0000000000..afec114458
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/support/syntax-error-in-setInterval.js
@@ -0,0 +1,8 @@
+interval = setInterval('{', 10);
+step_timeout(function(){
+ clearInterval(interval);
+ t.step(function(){
+ assert_true(ran, 'ran');
+ t.done();
+ });
+}, 20); \ No newline at end of file
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/support/syntax-error-in-setTimeout.js b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/support/syntax-error-in-setTimeout.js
new file mode 100644
index 0000000000..427542b42e
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/support/syntax-error-in-setTimeout.js
@@ -0,0 +1,7 @@
+setTimeout('{', 10);
+setTimeout(function(){
+ t.step(function(){
+ assert_true(ran, 'ran');
+ t.done();
+ });
+}, 20);
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/support/syntax-error.js b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/support/syntax-error.js
new file mode 100644
index 0000000000..0f74a6fca6
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/support/syntax-error.js
@@ -0,0 +1 @@
+for (;) {} \ No newline at end of file
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/support/undefined-variable-in-setInterval.js b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/support/undefined-variable-in-setInterval.js
new file mode 100644
index 0000000000..c2a017a2ab
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/support/undefined-variable-in-setInterval.js
@@ -0,0 +1,8 @@
+interval = setInterval('undefined_variable;', 10);
+step_timeout(function(){
+ clearInterval(interval);
+ t.step(function(){
+ assert_true(ran, 'ran');
+ t.done();
+ });
+}, 20); \ No newline at end of file
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/support/undefined-variable-in-setTimeout.js b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/support/undefined-variable-in-setTimeout.js
new file mode 100644
index 0000000000..6fa54cda9f
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/support/undefined-variable-in-setTimeout.js
@@ -0,0 +1,7 @@
+setTimeout('undefined_variable;', 10);
+setTimeout(function(){
+ t.step(function(){
+ assert_true(ran, 'ran');
+ t.done();
+ });
+}, 20);
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/support/undefined-variable.js b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/support/undefined-variable.js
new file mode 100644
index 0000000000..e73a62ceda
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/support/undefined-variable.js
@@ -0,0 +1 @@
+undefined_variable; \ No newline at end of file
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/allow-crossorigin.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/allow-crossorigin.html
new file mode 100644
index 0000000000..7524604113
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/allow-crossorigin.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/cors/support.js?pipe=sub"></script>
+<link rel="help" href="https://html.spec.whatwg.org/#unhandled-promise-rejections">
+<link rel="help" href="https://html.spec.whatwg.org/#muted-errors">
+
+<body>
+<script>
+'use strict';
+setup({
+ allow_uncaught_exception: true
+});
+
+async_test(function(t) {
+ addEventListener('unhandledrejection', t.step_func(function(e) {
+ assert_equals(e.reason, 42, 'reason should be the one given by the script');
+ t.done();
+ }));
+}, 'Promise rejection event should be received for the cross-origin CORS script');
+
+(function() {
+ var scriptEl = document.createElement('script');
+ scriptEl.src = CROSSDOMAIN + 'support/promise-access-control.py?allow=true';
+ scriptEl.crossOrigin = 'anonymous';
+ document.body.appendChild(scriptEl);
+}());
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/disallow-crossorigin.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/disallow-crossorigin.html
new file mode 100644
index 0000000000..d61618a53e
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/disallow-crossorigin.html
@@ -0,0 +1,96 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/cors/support.js?pipe=sub"></script>
+<link rel="help" href="https://html.spec.whatwg.org/#unhandled-promise-rejections">
+<link rel="help" href="https://html.spec.whatwg.org/#muted-errors">
+
+<body>
+<script>
+'use strict';
+
+setup({
+ allow_uncaught_exception: true
+});
+
+(function() {
+ var resolveLoaded;
+ var loadedPromise = new Promise(function(resolve) { resolveLoaded = resolve; });
+
+ promise_test(function(t) {
+ var unreachedUnhandled = t.unreached_func('unhandledrejection event should never be triggered');
+ var unreachedHandled = t.unreached_func('rejectionhandled event should never be triggered');
+
+ addEventListener('unhandledrejection', unreachedUnhandled);
+ addEventListener('rejectionhandled', unreachedHandled);
+ ensureCleanup(t, unreachedUnhandled, unreachedHandled);
+
+ return loadedPromise.then(t.step_func(function() {
+ return new Promise(function(resolve) {
+ t.step_timeout(function() {
+ resolve();
+ }, 1000);
+ });
+ }));
+ }, 'Promise rejection event should be muted for cross-origin non-CORS script');
+
+ promise_test(function(t) {
+ var unreachedUnhandled = t.unreached_func('unhandledrejection event should never be triggered');
+ var unreachedHandled = t.unreached_func('rejectionhandled event should never be triggered');
+
+ addEventListener('unhandledrejection', unreachedUnhandled);
+ addEventListener('rejectionhandled', unreachedHandled);
+ ensureCleanup(t, unreachedUnhandled, unreachedHandled);
+
+ return new Promise(function(resolve) {
+ handleRejectedPromise(new Promise(function(resolve, reject) { reject(42); }));
+ t.step_timeout(function() {
+ resolve();
+ }, 1000);
+ });
+ }, 'Promise rejection should be muted if the rejected promise is handled in cross-origin non-CORS script');
+
+ promise_test(function(t) {
+ var promise = new Promise(function(resolve, reject) { reject(42); });
+ var resolveReceived;
+ var eventPromise = new Promise(function(resolve) { resolveReceived = resolve; });
+ var unhandled = t.step_func(function(e) {
+ if (e.promise === promise) {
+ handleRejectedPromise(promise);
+ resolveReceived();
+ }
+ });
+ var unreachedHandled = t.unreached_func('rejectionhandled event should never be triggered');
+
+ addEventListener('unhandledrejection', unhandled);
+ addEventListener('rejectionhandled', unreachedHandled);
+ ensureCleanup(t, unhandled, unreachedHandled);
+
+ return eventPromise.then(t.step_func(function() {
+ return new Promise(function(resolve) {
+ t.step_timeout(function() {
+ resolve();
+ }, 1000);
+ });
+ }));
+ }, 'Promise rejection should be muted if the rejected promise is handled in unhandledrejection event handler in cross-origin non-CORS script');
+
+ function ensureCleanup(t, unhandled, handled) {
+ t.add_cleanup(function() {
+ if (unhandled) {
+ removeEventListener('unhandledrejection', unhandled);
+ }
+ if (handled) {
+ removeEventListener('rejectionhandled', handled);
+ }
+ });
+ }
+
+ var scriptEl = document.createElement('script');
+ scriptEl.src = CROSSDOMAIN + 'support/promise-access-control.py?allow=false';
+ scriptEl.onload = resolveLoaded;
+ document.body.appendChild(scriptEl);
+}());
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-event-constructor.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-event-constructor.html
new file mode 100644
index 0000000000..4319deee7b
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-event-constructor.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/#the-promiserejectionevent-interface">
+<script>
+'use strict';
+
+test(function() {
+ var p = new Promise(function(resolve, reject) {});
+
+ assert_throws_js(TypeError,
+ function() {
+ PromiseRejectionEvent('', { promise: p });
+ },
+ "Calling PromiseRejectionEvent constructor without 'new' must throw");
+
+ // No custom options are passed (besides required promise).
+ assert_equals(new PromiseRejectionEvent('eventType', { promise: p }).bubbles, false);
+ assert_equals(new PromiseRejectionEvent('eventType', { promise: p }).cancelable, false);
+ assert_equals(new PromiseRejectionEvent('eventType', { promise: p }).promise, p);
+ assert_equals(new PromiseRejectionEvent('eventType', { promise: p }).reason, undefined);
+
+ // No promise is passed.
+ assert_throws_js(TypeError,
+ function() {
+ new PromiseRejectionEvent('eventType', { bubbles: false });
+ },
+ 'Cannot construct PromiseRejectionEventInit without promise');
+
+ // bubbles is passed.
+ assert_equals(new PromiseRejectionEvent('eventType', { bubbles: false, promise: p }).bubbles, false);
+ assert_equals(new PromiseRejectionEvent('eventType', { bubbles: true, promise: p }).bubbles, true);
+
+ // cancelable is passed.
+ assert_equals(new PromiseRejectionEvent('eventType', { cancelable: false, promise: p }).cancelable, false);
+ assert_equals(new PromiseRejectionEvent('eventType', { cancelable: true, promise: p }).cancelable, true);
+
+ // reason is passed.
+ var r = new Error();
+ assert_equals(new PromiseRejectionEvent('eventType', { promise: p, reason: r }).reason, r);
+ assert_equals(new PromiseRejectionEvent('eventType', { promise: p, reason: null }).reason, null);
+
+ // All initializers are passed.
+ assert_equals(new PromiseRejectionEvent('eventType', { bubbles: true, cancelable: true, promise: p, reason: r }).bubbles, true);
+ assert_equals(new PromiseRejectionEvent('eventType', { bubbles: true, cancelable: true, promise: p, reason: r }).cancelable, true);
+ assert_equals(new PromiseRejectionEvent('eventType', { bubbles: true, cancelable: true, promise: p, reason: r }).promise, p);
+ assert_equals(new PromiseRejectionEvent('eventType', { bubbles: true, cancelable: true, promise: p, reason: r }).reason, r);
+}, "This tests the constructor for the PromiseRejectionEvent DOM class.");
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-event-during-parse.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-event-during-parse.html
new file mode 100644
index 0000000000..160dad9b36
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-event-during-parse.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Promise rejection during initial parsing of document</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/#unhandled-promise-rejections">
+<body>
+<p>The script in this test is executed immediately while parsing is ongoing, and
+<a
+href="https://html.spec.whatwg.org/multipage/webappapis.html#clean-up-after-running-script">cleaning
+up after running script</a> involves queueing a task on the DOM manipulation
+task source to fire the <code>unhandledrejection</code> event. Parsing then
+completes, immediately transitioning the document's readiness state to
+"interactive," and queuing another task on the DOM manipulation task source to
+transition the state to "complete."
+</p>
+<script>
+'use strict';
+setup({ allow_uncaught_exception: true });
+
+async_test(function(t) {
+ const events = [];
+ document.addEventListener('readystatechange', t.step_func(function() {
+ events.push('readystatechange:' + document.readyState);
+ }));
+ addEventListener('unhandledrejection', t.step_func(function() {
+ events.push('unhandledrejection');
+ }));
+
+ Promise.reject(new Error('this error is intentional'));
+
+ addEventListener('load', t.step_func(function() {
+ assert_array_equals(
+ events,
+ [
+ 'readystatechange:interactive',
+ 'unhandledrejection',
+ 'readystatechange:complete'
+ ]
+ );
+ t.done();
+ }));
+});
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-events-attached-in-event.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-events-attached-in-event.html
new file mode 100644
index 0000000000..b151bd812f
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-events-attached-in-event.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/#unhandled-promise-rejections">
+<script>
+'use strict';
+setup({
+ allow_uncaught_exception: true
+});
+async_test(function(t) {
+ var e = new Error('e');
+ var p = Promise.reject(e);
+
+ window.onunhandledrejection = function(evt) {
+ t.step(function() {
+ assert_equals(evt.promise, p);
+ assert_equals(evt.reason, e);
+ });
+ var unreached = t.unreached_func('promise should not be fulfilled');
+ p.then(unreached, function(reason) {
+ t.step(function() {
+ assert_equals(reason, e);
+ });
+ t.step_timeout(function() { t.done(); }, 10);
+ });
+ };
+
+ window.onrejectionhandled = t.unreached_func('rejectionhandled event should not be invoked');
+}, 'Attaching a handler in unhandledrejection should not trigger rejectionhandled.');
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-events-iframe.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-events-iframe.html
new file mode 100644
index 0000000000..c749eadef4
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-events-iframe.html
@@ -0,0 +1,146 @@
+<!doctype html>
+<meta charset=utf-8>
+<title></title>
+<div id="log"></div><br>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script>
+'use strict';
+
+setup({
+ allow_uncaught_exception: true
+});
+
+async_test(function(t) {
+ createIframeAndStartTest(t, function(w) {
+ let e = new Error();
+ let promise = new w.Promise(function(_, reject) {
+ setTimeout(function() {
+ reject(e);
+ }, 1);
+ });
+
+ let unhandled = function(evt) {
+ if (evt.promise === promise) {
+ t.step(function() {
+ assert_equals(evt.reason, e);
+ assert_equals(evt.promise, promise);
+ });
+ t.done();
+ }
+ };
+ let handled = function(evt) {
+ if (evt.promise === promise) {
+ t.step(function() {
+ assert_unreached('rejectionhandled event is not supposed to be triggered');
+ });
+ }
+ };
+
+ w.addEventListener('unhandledrejection', unhandled);
+ w.addEventListener('rejectionhandled', handled);
+ ensureCleanup(t, w, unhandled, handled);
+ });
+}, "unhandledrejection: promise is created in iframe and being rejected elsewhere");
+
+async_test(function(t) {
+ createIframeAndStartTest(t, function(w) {
+ let e = new Error();
+ let promise = w.Promise.reject(e);
+
+ let unhandled = function(evt) {
+ if (evt.promise === promise) {
+ t.step(function() {
+ assert_unreached('unhandledrejection event is not supposed to be triggered');
+ });
+ }
+ };
+ let handled = function(evt) {
+ if (evt.promise === promise) {
+ t.step(function() {
+ assert_unreached('rejectionhandled event is not supposed to be triggered');
+ });
+ }
+ };
+
+ w.addEventListener('unhandledrejection', unhandled);
+ w.addEventListener('rejectionhandled', handled);
+ ensureCleanup(t, w, unhandled, handled);
+
+ promise.catch(function() {});
+ setTimeout(function() {
+ t.done();
+ }, 10);
+ });
+}, 'no unhandledrejection/rejectionhandled: promise is created in iframe and being rejected elsewhere');
+
+async_test(function(t) {
+ createIframeAndStartTest(t, function(w) {
+ let e = new Error();
+ let promise = w.Promise.reject(e);
+ var unhandledPromises = [];
+ var unhandledReasons = [];
+ var handledPromises = [];
+ var handledReasons = [];
+
+ let unhandled = function(evt) {
+ if (evt.promise === promise) {
+ t.step(function() {
+ unhandledPromises.push(evt.promise);
+ unhandledReasons.push(evt.reason);
+
+ setTimeout(function() {
+ var unreached = t.unreached_func('promise should not be fulfilled');
+ promise.then(unreached, function(reason) {
+ assert_equals(reason, e);
+ setTimeout(function() {
+ assert_array_equals(handledPromises, [promise]);
+ assert_array_equals(handledReasons, [e]);
+ t.done();
+ }, 10);
+ });
+ }, 10);
+ });
+ }
+ };
+ let handled = function(evt) {
+ if (evt.promise === promise) {
+ t.step(function() {
+ assert_array_equals(unhandledPromises, [promise]);
+ assert_array_equals(unhandledReasons, [e]);
+ handledPromises.push(evt.promise);
+ handledReasons.push(evt.reason);
+ });
+ }
+ };
+
+ w.addEventListener('unhandledrejection', unhandled);
+ w.addEventListener('rejectionhandled', handled);
+ ensureCleanup(t, w, unhandled, handled);
+ });
+}, 'delayed handling: promise is created in iframe and being rejected elsewhere');
+
+// Helpers
+
+function createIframeAndStartTest(t, runTest) {
+ var iframe = document.createElement("iframe");
+ iframe.onload = function() {
+ t.add_cleanup(() => iframe.remove());
+ runTest(iframe.contentWindow);
+ };
+ iframe.srcdoc = '';
+ document.documentElement.appendChild(iframe);
+}
+
+function ensureCleanup(t, win, unhandled, handled) {
+ t.add_cleanup(function() {
+ if (unhandled) {
+ win.removeEventListener('unhandledrejection', unhandled);
+ }
+ if (handled) {
+ win.removeEventListener('rejectionhandled', handled);
+ }
+ });
+}
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-events-onerror.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-events-onerror.html
new file mode 100644
index 0000000000..b6c02d27c9
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-events-onerror.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/#runtime-script-errors">
+<link rel="help" href="https://html.spec.whatwg.org/#unhandled-promise-rejections">
+<script>
+'use strict';
+setup({
+ allow_uncaught_exception: true
+});
+async_test(function(t) {
+ var e = new Error('e');
+ var e2 = new Error('e2');
+
+ window.onerror = function (msg, url, line, col, error) {
+ t.step(function() {
+ assert_true(msg.includes('e2'));
+ assert_equals(error, e2);
+ });
+ t.done();
+ };
+
+ window.onrejectionhandled = function() {
+ // This should cause onerror
+ throw e2;
+ };
+
+ var p = Promise.reject(e);
+ queueTask(function() {
+ queueTask(t.step_func(function() {
+ // This will cause onrejectionhandled
+ p.catch(function() {});
+ }));
+ });
+}, 'Throwing inside an unhandledrejection handler invokes the error handler.');
+
+// This function queues a task in "DOM manipulation task source"
+function queueTask(f) {
+ var d = document.createElement("details");
+ d.ontoggle = function() {
+ f();
+ };
+
+ d.setAttribute("open", "");
+}
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-events.dedicatedworker.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-events.dedicatedworker.html
new file mode 100644
index 0000000000..b6a4a9f3e6
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-events.dedicatedworker.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Promise rejection events tests: in a dedicated worker context</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/#unhandled-promise-rejections">
+
+<script>
+'use strict';
+fetch_tests_from_worker(new Worker('support/promise-rejection-events.js'));
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-events.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-events.html
new file mode 100644
index 0000000000..2fdfe26025
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-events.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Promise rejection events tests: in a Window context</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/#unhandled-promise-rejections">
+
+<script src="support/promise-rejection-events.js"></script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-events.serviceworker.https.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-events.serviceworker.https.html
new file mode 100644
index 0000000000..9d12125928
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-events.serviceworker.https.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Promise rejection events tests: in a service worker context</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/service-workers/service-worker/resources/test-helpers.sub.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/#unhandled-promise-rejections">
+
+<script>
+'use strict';
+service_worker_test('support/promise-rejection-events.js', 'Service worker setup');
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-events.sharedworker.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-events.sharedworker.html
new file mode 100644
index 0000000000..d832d1822f
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-rejection-events.sharedworker.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Promise rejection events tests: in a shared worker context</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/#unhandled-promise-rejections">
+
+<script>
+'use strict';
+fetch_tests_from_worker(new SharedWorker('support/promise-rejection-events.js'));
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-resolution-order.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-resolution-order.html
new file mode 100644
index 0000000000..314063025f
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/promise-resolution-order.html
@@ -0,0 +1,70 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Promise rejection ordering</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/webappapis.html#perform-a-microtask-checkpoint">
+<body>
+<p>A microtask checkpoint should notify about rejected promises. After
+<a
+href="https://html.spec.whatwg.org/multipage/webappapis.html#clean-up-after-running-script">cleaning
+up after running script</a> involves running a microtask checkpoint. So the order of unhandledrejection
+should occur before error2.
+</p>
+
+<script>
+'use strict';
+setup({ allow_uncaught_exception: true });
+
+async_test(function(t) {
+ const events = [];
+ addEventListener('unhandledrejection', t.step_func(() => {
+ events.push('unhandledrejection');
+ }));
+
+ function insertInvalidScript(id) {
+ // Inserting <script> with an empty source schedules dispatching an 'error' event on
+ // the dom manipulation task source.
+ let script = document.createElement('script');
+ script.setAttribute('src', ' ');
+ script.addEventListener('error', t.step_func(() => {
+ events.push(`error${id}`);
+
+ // This will be the end of the test. Verify the results are correct.
+ if (id == 2) {
+ assert_array_equals(
+ events,
+ [
+ 'raf1',
+ 'resolve1',
+ 'raf2',
+ 'resolve2',
+ 'error1',
+ 'unhandledrejection',
+ 'error2'
+ ]
+ );
+ t.done();
+ }
+ }));
+ document.body.append(script);
+ }
+
+ requestAnimationFrame(t.step_func(() => {
+ events.push('raf1');
+ Promise.reject();
+ Promise.resolve(0).then(t.step_func(() => {
+ events.push('resolve1');
+ }));
+ insertInvalidScript(1);
+ }));
+
+ requestAnimationFrame(t.step_func(() => {
+ events.push('raf2');
+ Promise.resolve(0).then(t.step_func(() => {
+ events.push('resolve2');
+ }));
+ insertInvalidScript(2);
+ }));
+});
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/support/promise-access-control.py b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/support/promise-access-control.py
new file mode 100644
index 0000000000..cf8ed5e492
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/support/promise-access-control.py
@@ -0,0 +1,18 @@
+def main(request, response):
+ allow = request.GET.first(b"allow", b"false")
+
+ headers = [(b"Content-Type", b"application/javascript")]
+ if allow != b"false":
+ headers.append((b"Access-Control-Allow-Origin", b"*"))
+
+ body = b"""
+ function handleRejectedPromise(promise) {
+ promise.catch(() => {});
+ }
+
+ (function() {
+ new Promise(function(resolve, reject) { reject(42); });
+ })();
+ """
+
+ return headers, body
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/support/promise-rejection-events.js b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/support/promise-rejection-events.js
new file mode 100644
index 0000000000..036e1784db
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/unhandled-promise-rejections/support/promise-rejection-events.js
@@ -0,0 +1,961 @@
+'use strict';
+
+if (self.importScripts) {
+ importScripts('/resources/testharness.js');
+}
+
+setup({
+ allow_uncaught_exception: true
+});
+
+//
+// Straightforward unhandledrejection tests
+//
+async_test(function(t) {
+ var e = new Error();
+ var p;
+
+ onUnhandledSucceed(t, e, function() { return p; });
+
+ p = Promise.reject(e);
+}, 'unhandledrejection: from Promise.reject');
+
+async_test(function(t) {
+ var e = new Error();
+ var p;
+
+ onUnhandledSucceed(t, e, function() { return p; });
+
+ p = new Promise(function(_, reject) {
+ reject(e);
+ });
+}, 'unhandledrejection: from a synchronous rejection in new Promise');
+
+async_test(function(t) {
+ var e = new Error();
+ var p;
+
+ onUnhandledSucceed(t, e, function() { return p; });
+
+ p = new Promise(function(_, reject) {
+ queueTask(function() {
+ reject(e);
+ });
+ });
+}, 'unhandledrejection: from a task-delayed rejection');
+
+async_test(function(t) {
+ var e = new Error();
+ var p;
+
+ onUnhandledSucceed(t, e, function() { return p; });
+
+ p = new Promise(function(_, reject) {
+ setTimeout(function() {
+ reject(e);
+ }, 1);
+ });
+}, 'unhandledrejection: from a setTimeout-delayed rejection');
+
+async_test(function(t) {
+ var e = new Error();
+ var e2 = new Error();
+ var promise2;
+
+ onUnhandledSucceed(t, e2, function() { return promise2; });
+
+ var unreached = t.unreached_func('promise should not be fulfilled');
+ promise2 = Promise.reject(e).then(unreached, function(reason) {
+ t.step(function() {
+ assert_equals(reason, e);
+ });
+ throw e2;
+ });
+}, 'unhandledrejection: from a throw in a rejection handler chained off of Promise.reject');
+
+async_test(function(t) {
+ var e = new Error();
+ var e2 = new Error();
+ var promise2;
+
+ onUnhandledSucceed(t, e2, function() { return promise2; });
+
+ var unreached = t.unreached_func('promise should not be fulfilled');
+ promise2 = new Promise(function(_, reject) {
+ setTimeout(function() {
+ reject(e);
+ }, 1);
+ }).then(unreached, function(reason) {
+ t.step(function() {
+ assert_equals(reason, e);
+ });
+ throw e2;
+ });
+}, 'unhandledrejection: from a throw in a rejection handler chained off of a setTimeout-delayed rejection');
+
+async_test(function(t) {
+ var e = new Error();
+ var e2 = new Error();
+ var promise2;
+
+ onUnhandledSucceed(t, e2, function() { return promise2; });
+
+ var promise = new Promise(function(_, reject) {
+ setTimeout(function() {
+ reject(e);
+ mutationObserverMicrotask(function() {
+ var unreached = t.unreached_func('promise should not be fulfilled');
+ promise2 = promise.then(unreached, function(reason) {
+ t.step(function() {
+ assert_equals(reason, e);
+ });
+ throw e2;
+ });
+ });
+ }, 1);
+ });
+}, 'unhandledrejection: from a throw in a rejection handler attached one microtask after a setTimeout-delayed rejection');
+
+async_test(function(t) {
+ var e = new Error();
+ var p;
+
+ onUnhandledSucceed(t, e, function() { return p; });
+
+ p = Promise.resolve().then(function() {
+ return Promise.reject(e);
+ });
+}, 'unhandledrejection: from returning a Promise.reject-created rejection in a fulfillment handler');
+
+async_test(function(t) {
+ var e = new Error();
+ var p;
+
+ onUnhandledSucceed(t, e, function() { return p; });
+
+ p = Promise.resolve().then(function() {
+ throw e;
+ });
+}, 'unhandledrejection: from a throw in a fulfillment handler');
+
+async_test(function(t) {
+ var e = new Error();
+ var p;
+
+ onUnhandledSucceed(t, e, function() { return p; });
+
+ p = Promise.resolve().then(function() {
+ return new Promise(function(_, reject) {
+ setTimeout(function() {
+ reject(e);
+ }, 1);
+ });
+ });
+}, 'unhandledrejection: from returning a setTimeout-delayed rejection in a fulfillment handler');
+
+async_test(function(t) {
+ var e = new Error();
+ var p;
+
+ onUnhandledSucceed(t, e, function() { return p; });
+
+ p = Promise.all([Promise.reject(e)]);
+}, 'unhandledrejection: from Promise.reject, indirected through Promise.all');
+
+async_test(function(t) {
+ var p;
+
+ var unhandled = function(ev) {
+ if (ev.promise === p) {
+ t.step(function() {
+ assert_equals(ev.reason.name, 'InvalidStateError');
+ assert_equals(ev.promise, p);
+ });
+ t.done();
+ }
+ };
+ addEventListener('unhandledrejection', unhandled);
+ ensureCleanup(t, unhandled);
+
+ p = createImageBitmap(new Blob());
+}, 'unhandledrejection: from createImageBitmap which is UA triggered');
+
+//
+// Negative unhandledrejection/rejectionhandled tests with immediate attachment
+//
+
+async_test(function(t) {
+ var e = new Error();
+ var p;
+
+ onUnhandledFail(t, function() { return p; });
+
+ var unreached = t.unreached_func('promise should not be fulfilled');
+ p = Promise.reject(e).then(unreached, function() {});
+}, 'no unhandledrejection/rejectionhandled: rejection handler attached synchronously to a promise from Promise.reject');
+
+async_test(function(t) {
+ var e = new Error();
+ var p;
+
+ onUnhandledFail(t, function() { return p; });
+
+ var unreached = t.unreached_func('promise should not be fulfilled');
+ p = Promise.all([Promise.reject(e)]).then(unreached, function() {});
+}, 'no unhandledrejection/rejectionhandled: rejection handler attached synchronously to a promise from ' +
+ 'Promise.reject, indirecting through Promise.all');
+
+async_test(function(t) {
+ var e = new Error();
+ var p;
+
+ onUnhandledFail(t, function() { return p; });
+
+ var unreached = t.unreached_func('promise should not be fulfilled');
+ p = new Promise(function(_, reject) {
+ reject(e);
+ }).then(unreached, function() {});
+}, 'no unhandledrejection/rejectionhandled: rejection handler attached synchronously to a synchronously-rejected ' +
+ 'promise created with new Promise');
+
+async_test(function(t) {
+ var e = new Error();
+ var p;
+
+ onUnhandledFail(t, function() { return p; });
+
+ var unreached = t.unreached_func('promise should not be fulfilled');
+ p = Promise.resolve().then(function() {
+ throw e;
+ }).then(unreached, function(reason) {
+ t.step(function() {
+ assert_equals(reason, e);
+ });
+ });
+}, 'no unhandledrejection/rejectionhandled: rejection handler attached synchronously to a promise created from ' +
+ 'throwing in a fulfillment handler');
+
+async_test(function(t) {
+ var e = new Error();
+ var p;
+
+ onUnhandledFail(t, function() { return p; });
+
+ var unreached = t.unreached_func('promise should not be fulfilled');
+ p = Promise.resolve().then(function() {
+ return Promise.reject(e);
+ }).then(unreached, function(reason) {
+ t.step(function() {
+ assert_equals(reason, e);
+ });
+ });
+}, 'no unhandledrejection/rejectionhandled: rejection handler attached synchronously to a promise created from ' +
+ 'returning a Promise.reject-created promise in a fulfillment handler');
+
+async_test(function(t) {
+ var e = new Error();
+ var p;
+
+ onUnhandledFail(t, function() { return p; });
+
+ var unreached = t.unreached_func('promise should not be fulfilled');
+ p = Promise.resolve().then(function() {
+ return new Promise(function(_, reject) {
+ setTimeout(function() {
+ reject(e);
+ }, 1);
+ });
+ }).then(unreached, function(reason) {
+ t.step(function() {
+ assert_equals(reason, e);
+ });
+ });
+}, 'no unhandledrejection/rejectionhandled: rejection handler attached synchronously to a promise created from ' +
+ 'returning a setTimeout-delayed rejection in a fulfillment handler');
+
+async_test(function(t) {
+ var e = new Error();
+ var p;
+
+ onUnhandledFail(t, function() { return p; });
+
+ queueTask(function() {
+ p = Promise.resolve().then(function() {
+ return Promise.reject(e);
+ })
+ .catch(function() {});
+ });
+}, 'no unhandledrejection/rejectionhandled: all inside a queued task, a rejection handler attached synchronously to ' +
+ 'a promise created from returning a Promise.reject-created promise in a fulfillment handler');
+
+async_test(function(t) {
+ var p;
+
+ onUnhandledFail(t, function() { return p; });
+
+ var unreached = t.unreached_func('promise should not be fulfilled');
+ p = createImageBitmap(new Blob()).then(unreached, function() {});
+}, 'no unhandledrejection/rejectionhandled: rejection handler attached synchronously to a promise created from ' +
+ 'createImageBitmap');
+
+//
+// Negative unhandledrejection/rejectionhandled tests with microtask-delayed attachment
+//
+
+async_test(function(t) {
+ var e = new Error();
+ var p;
+
+ onUnhandledFail(t, function() { return p; });
+
+ p = Promise.reject(e);
+ mutationObserverMicrotask(function() {
+ var unreached = t.unreached_func('promise should not be fulfilled');
+ p.then(unreached, function() {});
+ });
+}, 'delayed handling: a microtask delay before attaching a handler prevents both events (Promise.reject-created ' +
+ 'promise)');
+
+async_test(function(t) {
+ var e = new Error();
+ var p;
+
+ onUnhandledFail(t, function() { return p; });
+
+ p = new Promise(function(_, reject) {
+ reject(e);
+ });
+ mutationObserverMicrotask(function() {
+ var unreached = t.unreached_func('promise should not be fulfilled');
+ p.then(unreached, function() {});
+ });
+}, 'delayed handling: a microtask delay before attaching a handler prevents both events (immediately-rejected new ' +
+ 'Promise-created promise)');
+
+async_test(function(t) {
+ var e = new Error();
+ var p1;
+ var p2;
+
+ onUnhandledFail(t, function() { return p1; });
+ onUnhandledFail(t, function() { return p2; });
+
+ p1 = new Promise(function(_, reject) {
+ mutationObserverMicrotask(function() {
+ reject(e);
+ });
+ });
+ p2 = Promise.all([p1]);
+ mutationObserverMicrotask(function() {
+ var unreached = t.unreached_func('promise should not be fulfilled');
+ p2.then(unreached, function() {});
+ });
+}, 'delayed handling: a microtask delay before attaching the handler, and before rejecting the promise, indirected ' +
+ 'through Promise.all');
+
+//
+// Negative unhandledrejection/rejectionhandled tests with nested-microtask-delayed attachment
+//
+
+async_test(function(t) {
+ var e = new Error();
+ var p;
+
+ onUnhandledFail(t, function() { return p; });
+
+ p = Promise.reject(e);
+ mutationObserverMicrotask(function() {
+ Promise.resolve().then(function() {
+ mutationObserverMicrotask(function() {
+ Promise.resolve().then(function() {
+ p.catch(function() {});
+ });
+ });
+ });
+ });
+}, 'microtask nesting: attaching a handler inside a combination of mutationObserverMicrotask + promise microtasks');
+
+async_test(function(t) {
+ var e = new Error();
+ var p;
+
+ onUnhandledFail(t, function() { return p; });
+
+ queueTask(function() {
+ p = Promise.reject(e);
+ mutationObserverMicrotask(function() {
+ Promise.resolve().then(function() {
+ mutationObserverMicrotask(function() {
+ Promise.resolve().then(function() {
+ p.catch(function() {});
+ });
+ });
+ });
+ });
+ });
+}, 'microtask nesting: attaching a handler inside a combination of mutationObserverMicrotask + promise microtasks, ' +
+ 'all inside a queueTask');
+
+async_test(function(t) {
+ var e = new Error();
+ var p;
+
+ onUnhandledFail(t, function() { return p; });
+
+ setTimeout(function() {
+ p = Promise.reject(e);
+ mutationObserverMicrotask(function() {
+ Promise.resolve().then(function() {
+ mutationObserverMicrotask(function() {
+ Promise.resolve().then(function() {
+ p.catch(function() {});
+ });
+ });
+ });
+ });
+ }, 0);
+}, 'microtask nesting: attaching a handler inside a combination of mutationObserverMicrotask + promise microtasks, ' +
+ 'all inside a setTimeout');
+
+async_test(function(t) {
+ var e = new Error();
+ var p;
+
+ onUnhandledFail(t, function() { return p; });
+
+ p = Promise.reject(e);
+ Promise.resolve().then(function() {
+ mutationObserverMicrotask(function() {
+ Promise.resolve().then(function() {
+ mutationObserverMicrotask(function() {
+ p.catch(function() {});
+ });
+ });
+ });
+ });
+}, 'microtask nesting: attaching a handler inside a combination of promise microtasks + mutationObserverMicrotask');
+
+async_test(function(t) {
+ var e = new Error();
+ var p;
+
+ onUnhandledFail(t, function() { return p; });
+
+ queueTask(function() {
+ p = Promise.reject(e);
+ Promise.resolve().then(function() {
+ mutationObserverMicrotask(function() {
+ Promise.resolve().then(function() {
+ mutationObserverMicrotask(function() {
+ p.catch(function() {});
+ });
+ });
+ });
+ });
+ });
+}, 'microtask nesting: attaching a handler inside a combination of promise microtasks + mutationObserverMicrotask, ' +
+ 'all inside a queueTask');
+
+async_test(function(t) {
+ var e = new Error();
+ var p;
+
+ onUnhandledFail(t, function() { return p; });
+
+ setTimeout(function() {
+ p = Promise.reject(e);
+ Promise.resolve().then(function() {
+ mutationObserverMicrotask(function() {
+ Promise.resolve().then(function() {
+ mutationObserverMicrotask(function() {
+ p.catch(function() {});
+ });
+ });
+ });
+ });
+ }, 0);
+}, 'microtask nesting: attaching a handler inside a combination of promise microtasks + mutationObserverMicrotask, ' +
+ 'all inside a setTimeout');
+
+
+// For workers, queueTask() involves posting tasks to other threads, so
+// the following tests don't work there.
+
+if ('document' in self) {
+ //
+ // Negative unhandledrejection/rejectionhandled tests with task-delayed attachment
+ //
+
+ async_test(function(t) {
+ var e = new Error();
+ var p;
+
+ onUnhandledFail(t, function() { return p; });
+
+ var _reject;
+ p = new Promise(function(_, reject) {
+ _reject = reject;
+ });
+ _reject(e);
+ queueTask(function() {
+ var unreached = t.unreached_func('promise should not be fulfilled');
+ p.then(unreached, function() {});
+ });
+ }, 'delayed handling: a task delay before attaching a handler prevents unhandledrejection');
+
+ async_test(function(t) {
+ var e = new Error();
+ var p;
+
+ onUnhandledFail(t, function() { return p; });
+
+ p = Promise.reject(e);
+ queueTask(function() {
+ Promise.resolve().then(function() {
+ p.catch(function() {});
+ });
+ });
+ }, 'delayed handling: queueTask after promise creation/rejection, plus promise microtasks, is not too late to ' +
+ 'attach a rejection handler');
+
+ async_test(function(t) {
+ var e = new Error();
+ var p;
+
+ onUnhandledFail(t, function() { return p; });
+
+ queueTask(function() {
+ Promise.resolve().then(function() {
+ Promise.resolve().then(function() {
+ Promise.resolve().then(function() {
+ Promise.resolve().then(function() {
+ p.catch(function() {});
+ });
+ });
+ });
+ });
+ });
+ p = Promise.reject(e);
+ }, 'delayed handling: queueTask before promise creation/rejection, plus many promise microtasks, is not too ' +
+ 'late to attach a rejection handler');
+
+ async_test(function(t) {
+ var e = new Error();
+ var p;
+
+ onUnhandledFail(t, function() { return p; });
+
+ p = Promise.reject(e);
+ queueTask(function() {
+ Promise.resolve().then(function() {
+ Promise.resolve().then(function() {
+ Promise.resolve().then(function() {
+ Promise.resolve().then(function() {
+ p.catch(function() {});
+ });
+ });
+ });
+ });
+ });
+ }, 'delayed handling: queueTask after promise creation/rejection, plus many promise microtasks, is not too ' +
+ 'late to attach a rejection handler');
+}
+
+//
+// Positive unhandledrejection/rejectionhandled tests with delayed attachment
+//
+
+async_test(function(t) {
+ var e = new Error();
+ var p;
+
+ onUnhandledSucceed(t, e, function() { return p; });
+
+ var _reject;
+ p = new Promise(function(_, reject) {
+ _reject = reject;
+ });
+ _reject(e);
+ queueTask(function() {
+ queueTask(function() {
+ var unreached = t.unreached_func('promise should not be fulfilled');
+ p.then(unreached, function() {});
+ });
+ });
+}, 'delayed handling: a nested-task delay before attaching a handler causes unhandledrejection');
+
+async_test(function(t) {
+ var e = new Error();
+ var p;
+
+ onUnhandledSucceed(t, e, function() { return p; });
+
+ p = Promise.reject(e);
+ queueTask(function() {
+ queueTask(function() {
+ Promise.resolve().then(function() {
+ p.catch(function() {});
+ });
+ });
+ });
+}, 'delayed handling: a nested-queueTask after promise creation/rejection, plus promise microtasks, is too ' +
+ 'late to attach a rejection handler');
+
+async_test(function(t) {
+ var e = new Error();
+ var p;
+
+ onUnhandledSucceed(t, e, function() { return p; });
+
+ queueTask(function() {
+ queueTask(function() {
+ Promise.resolve().then(function() {
+ Promise.resolve().then(function() {
+ Promise.resolve().then(function() {
+ Promise.resolve().then(function() {
+ p.catch(function() {});
+ });
+ });
+ });
+ });
+ });
+ });
+ p = Promise.reject(e);
+}, 'delayed handling: a nested-queueTask before promise creation/rejection, plus many promise microtasks, is ' +
+ 'too late to attach a rejection handler');
+
+async_test(function(t) {
+ var e = new Error();
+ var p;
+
+ onUnhandledSucceed(t, e, function() { return p; });
+
+ p = Promise.reject(e);
+ queueTask(function() {
+ queueTask(function() {
+ Promise.resolve().then(function() {
+ Promise.resolve().then(function() {
+ Promise.resolve().then(function() {
+ Promise.resolve().then(function() {
+ p.catch(function() {});
+ });
+ });
+ });
+ });
+ });
+ });
+}, 'delayed handling: a nested-queueTask after promise creation/rejection, plus many promise microtasks, is ' +
+ 'too late to attach a rejection handler');
+
+async_test(function(t) {
+ var unhandledPromises = [];
+ var unhandledReasons = [];
+ var e = new Error();
+ var p;
+
+ var unhandled = function(ev) {
+ if (ev.promise === p) {
+ t.step(function() {
+ unhandledPromises.push(ev.promise);
+ unhandledReasons.push(ev.reason);
+ });
+ }
+ };
+ var handled = function(ev) {
+ if (ev.promise === p) {
+ t.step(function() {
+ assert_array_equals(unhandledPromises, [p]);
+ assert_array_equals(unhandledReasons, [e]);
+ assert_equals(ev.promise, p);
+ assert_equals(ev.reason, e);
+ });
+ }
+ };
+ addEventListener('unhandledrejection', unhandled);
+ addEventListener('rejectionhandled', handled);
+ ensureCleanup(t, unhandled, handled);
+
+ p = new Promise(function() {
+ throw e;
+ });
+ setTimeout(function() {
+ var unreached = t.unreached_func('promise should not be fulfilled');
+ p.then(unreached, function(reason) {
+ assert_equals(reason, e);
+ setTimeout(function() { t.done(); }, 10);
+ });
+ }, 10);
+}, 'delayed handling: delaying handling by setTimeout(,10) will cause both events to fire');
+
+async_test(function(t) {
+ var unhandledPromises = [];
+ var unhandledReasons = [];
+ var p;
+
+ var unhandled = function(ev) {
+ if (ev.promise === p) {
+ t.step(function() {
+ unhandledPromises.push(ev.promise);
+ unhandledReasons.push(ev.reason.name);
+ });
+ }
+ };
+ var handled = function(ev) {
+ if (ev.promise === p) {
+ t.step(function() {
+ assert_array_equals(unhandledPromises, [p]);
+ assert_array_equals(unhandledReasons, ['InvalidStateError']);
+ assert_equals(ev.promise, p);
+ assert_equals(ev.reason.name, 'InvalidStateError');
+ });
+ }
+ };
+ addEventListener('unhandledrejection', unhandled);
+ addEventListener('rejectionhandled', handled);
+ ensureCleanup(t, unhandled, handled);
+
+ p = createImageBitmap(new Blob());
+ setTimeout(function() {
+ var unreached = t.unreached_func('promise should not be fulfilled');
+ p.then(unreached, function(reason) {
+ assert_equals(reason.name, 'InvalidStateError');
+ setTimeout(function() { t.done(); }, 10);
+ });
+ }, 10);
+}, 'delayed handling: delaying handling rejected promise created from createImageBitmap will cause both events to fire');
+
+//
+// Miscellaneous tests about integration with the rest of the platform
+//
+
+async_test(function(t) {
+ var e = new Error();
+ var l = function(ev) {
+ var order = [];
+ mutationObserverMicrotask(function() {
+ order.push(1);
+ });
+ setTimeout(function() {
+ order.push(2);
+ t.step(function() {
+ assert_array_equals(order, [1, 2]);
+ });
+ t.done();
+ }, 1);
+ };
+ addEventListener('unhandledrejection', l);
+ ensureCleanup(t, l);
+ Promise.reject(e);
+}, 'mutationObserverMicrotask vs. queueTask ordering is not disturbed inside unhandledrejection events');
+
+// For workers, queueTask() involves posting tasks to other threads, so
+// the following tests don't work there.
+
+if ('document' in self) {
+
+ // For the next two see https://github.com/domenic/unhandled-rejections-browser-spec/issues/2#issuecomment-121121695
+ // and the following comments.
+
+ async_test(function(t) {
+ var sequenceOfEvents = [];
+
+ addEventListener('unhandledrejection', l);
+ ensureCleanup(t, l);
+
+ var p1 = Promise.reject();
+ var p2;
+ queueTask(function() {
+ p2 = Promise.reject();
+ queueTask(function() {
+ sequenceOfEvents.push('queueTask');
+ checkSequence();
+ });
+ });
+
+ function l(ev) {
+ if (ev.promise === p1 || ev.promise === p2) {
+ sequenceOfEvents.push(ev.promise);
+ checkSequence();
+ }
+ }
+
+ function checkSequence() {
+ if (sequenceOfEvents.length === 3) {
+ t.step(function() {
+ assert_array_equals(sequenceOfEvents, [p1, 'queueTask', p2]);
+ });
+ t.done();
+ }
+ }
+ }, 'queueTask ordering vs. the task queued for unhandled rejection notification (1)');
+
+ async_test(function(t) {
+ var sequenceOfEvents = [];
+
+ addEventListener('unhandledrejection', l);
+ ensureCleanup(t, l);
+
+ var p2;
+ queueTask(function() {
+ p2 = Promise.reject();
+ queueTask(function() {
+ sequenceOfEvents.push('queueTask');
+ checkSequence();
+ });
+ });
+
+ function l(ev) {
+ if (ev.promise == p2) {
+ sequenceOfEvents.push(ev.promise);
+ checkSequence();
+ }
+ }
+
+ function checkSequence() {
+ if (sequenceOfEvents.length === 2) {
+ t.step(function() {
+ assert_array_equals(sequenceOfEvents, ['queueTask', p2]);
+ });
+ t.done();
+ }
+ }
+ }, 'queueTask ordering vs. the task queued for unhandled rejection notification (2)');
+
+ async_test(function(t) {
+ var sequenceOfEvents = [];
+
+
+ addEventListener('unhandledrejection', unhandled);
+ addEventListener('rejectionhandled', handled);
+ ensureCleanup(t, unhandled, handled);
+
+ var p = Promise.reject();
+
+ function unhandled(ev) {
+ if (ev.promise === p) {
+ sequenceOfEvents.push('unhandled');
+ checkSequence();
+ setTimeout(function() {
+ queueTask(function() {
+ sequenceOfEvents.push('task before catch');
+ checkSequence();
+ });
+
+ p.catch(function() {
+ sequenceOfEvents.push('catch');
+ checkSequence();
+ });
+
+ queueTask(function() {
+ sequenceOfEvents.push('task after catch');
+ checkSequence();
+ });
+
+ sequenceOfEvents.push('after catch');
+ checkSequence();
+ }, 10);
+ }
+ }
+
+ function handled(ev) {
+ if (ev.promise === p) {
+ sequenceOfEvents.push('handled');
+ checkSequence();
+ }
+ }
+
+ function checkSequence() {
+ if (sequenceOfEvents.length === 6) {
+ t.step(function() {
+ assert_array_equals(sequenceOfEvents,
+ ['unhandled', 'after catch', 'catch', 'task before catch', 'handled', 'task after catch']);
+ });
+ t.done();
+ }
+ }
+ }, 'rejectionhandled is dispatched from a queued task, and not immediately');
+}
+
+//
+// HELPERS
+//
+
+// This function queues a task in "DOM manipulation task source" in window
+// context, but not in workers.
+function queueTask(f) {
+ if ('document' in self) {
+ var d = document.createElement("details");
+ d.ontoggle = function() {
+ f();
+ };
+ d.setAttribute("open", "");
+ } else {
+ // We need to fix this to use something that can queue tasks in
+ // "DOM manipulation task source" to ensure the order is correct
+ var channel = new MessageChannel();
+ channel.port1.onmessage = function() { channel.port1.close(); f(); };
+ channel.port2.postMessage('abusingpostmessageforfunandprofit');
+ channel.port2.close();
+ }
+}
+
+function mutationObserverMicrotask(f) {
+ if ('document' in self) {
+ var observer = new MutationObserver(function() { f(); });
+ var node = document.createTextNode('');
+ observer.observe(node, { characterData: true });
+ node.data = 'foo';
+ } else {
+ // We don't have mutation observers on workers, so just post a promise-based
+ // microtask.
+ Promise.resolve().then(function() { f(); });
+ }
+}
+
+function onUnhandledSucceed(t, expectedReason, expectedPromiseGetter) {
+ var l = function(ev) {
+ if (ev.promise === expectedPromiseGetter()) {
+ t.step(function() {
+ assert_equals(ev.reason, expectedReason);
+ assert_equals(ev.promise, expectedPromiseGetter());
+ });
+ t.done();
+ }
+ };
+ addEventListener('unhandledrejection', l);
+ ensureCleanup(t, l);
+}
+
+function onUnhandledFail(t, expectedPromiseGetter) {
+ var unhandled = function(evt) {
+ if (evt.promise === expectedPromiseGetter()) {
+ t.step(function() {
+ assert_unreached('unhandledrejection event is not supposed to be triggered');
+ });
+ }
+ };
+ var handled = function(evt) {
+ if (evt.promise === expectedPromiseGetter()) {
+ t.step(function() {
+ assert_unreached('rejectionhandled event is not supposed to be triggered');
+ });
+ }
+ };
+ addEventListener('unhandledrejection', unhandled);
+ addEventListener('rejectionhandled', handled);
+ ensureCleanup(t, unhandled, handled);
+ setTimeout(function() {
+ t.done();
+ }, 10);
+}
+
+function ensureCleanup(t, unhandled, handled) {
+ t.add_cleanup(function() {
+ if (unhandled)
+ removeEventListener('unhandledrejection', unhandled);
+ if (handled)
+ removeEventListener('rejectionhandled', handled);
+ });
+}
+
+done();
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/window-onerror-parse-error.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/window-onerror-parse-error.html
new file mode 100644
index 0000000000..3c21df49c9
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/window-onerror-parse-error.html
@@ -0,0 +1,40 @@
+<!doctype html>
+<html>
+ <head>
+ <title>window.onerror: parse errors</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <!--
+
+ In https://html.spec.whatwg.org/multipage/#creating-scripts ,
+ step 3 describes parsing the script, and step 5 says:
+ # Otherwise, report the error using the onerror event handler of
+ # the script's global object. If the error is still not handled
+ # after this, then the error may be reported to the user.
+ which links to
+ https://html.spec.whatwg.org/multipage/#report-the-error ,
+ which describes what to do when onerror is a Function.
+
+ -->
+ </head>
+ <body>
+
+ <div id="log"></div>
+ <script>
+ setup({allow_uncaught_exception:true});
+ var error_count = 0;
+ window.onerror = function(msg, url, lineno) {
+ ++error_count;
+ test(function() {assert_equals(url, window.location.href)},
+ "correct url passed to window.onerror");
+ test(function() {assert_equals(lineno, 34)},
+ "correct line number passed to window.onerror");
+ };
+ </script>
+ <script>This script does not parse correctly.</script>
+ <script>
+ test(function() {assert_equals(error_count, 1)},
+ "correct number of calls to window.onerror");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/window-onerror-runtime-error-throw.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/window-onerror-runtime-error-throw.html
new file mode 100644
index 0000000000..4b2bc1f22c
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/window-onerror-runtime-error-throw.html
@@ -0,0 +1,43 @@
+<!doctype html>
+<html>
+ <head>
+ <title>window.onerror: runtime scripterrors</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <!--
+
+ https://html.spec.whatwg.org/multipage/#runtime-script-errors
+ says what to do for uncaught runtime script errors, and just below
+ describes what to do when onerror is a Function.
+
+ -->
+ </head>
+ <body>
+
+ <div id="log"></div>
+ <script>
+ setup({allow_uncaught_exception:true});
+ var error_count = 0;
+ window.onerror = function(msg, url, lineno) {
+ ++error_count;
+ test(function() {assert_equals(url, window.location.href)},
+ "correct url passed to window.onerror");
+ test(function() {assert_equals(lineno, 36)},
+ "correct line number passed to window.onerror");
+ };
+ </script>
+ <script>
+ try {
+ // This error is caught, so it should NOT trigger onerror.
+ throw "foo";
+ } catch (ex) {
+ }
+ // This error is NOT caught, so it should trigger onerror.
+ throw "bar";
+ </script>
+ <script>
+ test(function() {assert_equals(error_count, 1)},
+ "correct number of calls to window.onerror");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/window-onerror-runtime-error.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/window-onerror-runtime-error.html
new file mode 100644
index 0000000000..1fdab521ae
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/window-onerror-runtime-error.html
@@ -0,0 +1,43 @@
+<!doctype html>
+<html>
+ <head>
+ <title>window.onerror: runtime scripterrors</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <!--
+
+ https://html.spec.whatwg.org/multipage/#runtime-script-errors
+ says what to do for uncaught runtime script errors, and just below
+ describes what to do when onerror is a Function.
+
+ -->
+ </head>
+ <body>
+
+ <div id="log"></div>
+ <script>
+ setup({allow_uncaught_exception:true});
+ var error_count = 0;
+ window.onerror = function(msg, url, lineno) {
+ ++error_count;
+ test(function() {assert_equals(url, window.location.href)},
+ "correct url passed to window.onerror");
+ test(function() {assert_equals(lineno, 36)},
+ "correct line number passed to window.onerror");
+ };
+ </script>
+ <script>
+ try {
+ // This error is caught, so it should NOT trigger onerror.
+ window.nonexistentproperty.oops();
+ } catch (ex) {
+ }
+ // This error is NOT caught, so it should trigger onerror.
+ window.nonexistentproperty.oops();
+ </script>
+ <script>
+ test(function() {assert_equals(error_count, 1)},
+ "correct number of calls to window.onerror");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/window-onerror-with-cross-frame-event-listeners-1.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/window-onerror-with-cross-frame-event-listeners-1.html
new file mode 100644
index 0000000000..65a1a02b11
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/window-onerror-with-cross-frame-event-listeners-1.html
@@ -0,0 +1,33 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>
+ When a listener from window A is added to an event target in window B via the
+ addEventListener function from window B, errors in that listener should be
+ reported to window A.
+</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<iframe></iframe>
+<iframe></iframe>
+<script>
+test(function() {
+ var f = new frames[0].Function("thereIsNoSuchCallable()");
+ frames[1].document.addEventListener("myevent", f);
+ var frame0ErrorFired = false;
+ var frame1ErrorFired = false;
+ var ourErrorFired = false;
+ frames[0].addEventListener("error", function() {
+ frame0ErrorFired = true;
+ });
+ frames[1].addEventListener("error", function() {
+ frame1ErrorFired = true;
+ });
+ addEventListener("error", function() {
+ ourErrorFired = true;
+ });
+ frames[1].document.dispatchEvent(new Event("myevent"));
+ assert_true(frame0ErrorFired);
+ assert_false(frame1ErrorFired);
+ assert_false(ourErrorFired);
+}, "The error event from an event listener should fire on that listener's global");
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/window-onerror-with-cross-frame-event-listeners-2.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/window-onerror-with-cross-frame-event-listeners-2.html
new file mode 100644
index 0000000000..6c5476542b
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/window-onerror-with-cross-frame-event-listeners-2.html
@@ -0,0 +1,33 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>
+ When a listener from window A is added to an event target in window B via the
+ addEventListener function from window A, errors in that listener should be
+ reported to window A.
+</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<iframe></iframe>
+<iframe></iframe>
+<script>
+test(function() {
+ var f = new frames[0].Function("thereIsNoSuchCallable()");
+ frames[0].document.addEventListener.call(frames[1].document, "myevent", f);
+ var frame0ErrorFired = false;
+ var frame1ErrorFired = false;
+ var ourErrorFired = false;
+ frames[0].addEventListener("error", function() {
+ frame0ErrorFired = true;
+ });
+ frames[1].addEventListener("error", function() {
+ frame1ErrorFired = true;
+ });
+ addEventListener("error", function() {
+ ourErrorFired = true;
+ });
+ frames[1].document.dispatchEvent(new Event("myevent"));
+ assert_true(frame0ErrorFired);
+ assert_false(frame1ErrorFired);
+ assert_false(ourErrorFired);
+}, "The error event from an event listener should fire on that listener's global");
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/window-onerror-with-cross-frame-event-listeners-3.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/window-onerror-with-cross-frame-event-listeners-3.html
new file mode 100644
index 0000000000..5e78baa8de
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/window-onerror-with-cross-frame-event-listeners-3.html
@@ -0,0 +1,33 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>
+ When a listener from window A is added to an event target in window A via the
+ addEventListener function from window A, errors in that listener should be
+ reported to window A.
+</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<iframe></iframe>
+<iframe></iframe>
+<script>
+test(function() {
+ var f = new frames[1].Function("thereIsNoSuchCallable()");
+ frames[1].document.addEventListener("myevent", f);
+ var frame0ErrorFired = false;
+ var frame1ErrorFired = false;
+ var ourErrorFired = false;
+ frames[0].addEventListener("error", function() {
+ frame0ErrorFired = true;
+ });
+ frames[1].addEventListener("error", function() {
+ frame1ErrorFired = true;
+ });
+ addEventListener("error", function() {
+ ourErrorFired = true;
+ });
+ frames[1].document.dispatchEvent(new Event("myevent"));
+ assert_false(frame0ErrorFired);
+ assert_true(frame1ErrorFired);
+ assert_false(ourErrorFired);
+}, "The error event from an event listener should fire on that listener's global");
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/window-onerror-with-cross-frame-event-listeners-4.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/window-onerror-with-cross-frame-event-listeners-4.html
new file mode 100644
index 0000000000..a5f35d613f
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/window-onerror-with-cross-frame-event-listeners-4.html
@@ -0,0 +1,33 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>
+ When a listener from window A is added to an event target in window A via the
+ addEventListener function from window B, errors in that listener should be
+ reported to window A.
+</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<iframe></iframe>
+<iframe></iframe>
+<script>
+test(function() {
+ var f = new frames[1].Function("thereIsNoSuchCallable()");
+ frames[0].document.addEventListener.call(frames[1].document, "myevent", f);
+ var frame0ErrorFired = false;
+ var frame1ErrorFired = false;
+ var ourErrorFired = false;
+ frames[0].addEventListener("error", function() {
+ frame0ErrorFired = true;
+ });
+ frames[1].addEventListener("error", function() {
+ frame1ErrorFired = true;
+ });
+ addEventListener("error", function() {
+ ourErrorFired = true;
+ });
+ frames[1].document.dispatchEvent(new Event("myevent"));
+ assert_false(frame0ErrorFired);
+ assert_true(frame1ErrorFired);
+ assert_false(ourErrorFired);
+}, "The error event from an event listener should fire on that listener's global");
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/window-onerror-with-cross-frame-event-listeners-5.html b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/window-onerror-with-cross-frame-event-listeners-5.html
new file mode 100644
index 0000000000..da93e782ca
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/processing-model-2/window-onerror-with-cross-frame-event-listeners-5.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>window.onerror listener reports the exception in global object of its callback</title>
+<link rel=help href="https://dom.spec.whatwg.org/#concept-event-listener-inner-invoke">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<iframe></iframe>
+<iframe></iframe>
+<iframe></iframe>
+<script>
+setup({ allow_uncaught_exception: true });
+
+window.onload = () => {
+ test(() => {
+ window.onerrorCalls = [];
+ window.onerror = () => { onerrorCalls.push("top"); };
+ frames[0].onerror = new frames[1].Function(`top.onerrorCalls.push("frame0"); throw new parent.frames[2].Error("PASS");`);
+ frames[1].onerror = () => { onerrorCalls.push("frame1"); };
+ frames[2].onerror = () => { onerrorCalls.push("frame2"); };
+
+ frames[0].dispatchEvent(new ErrorEvent("error", { error: new Error("foo") }));
+ assert_array_equals(onerrorCalls, ["frame0", "frame1"]);
+ });
+};
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/reporterror-cross-realm-method.html b/testing/web-platform/tests/html/webappapis/scripting/reporterror-cross-realm-method.html
new file mode 100644
index 0000000000..6e2c2aae8f
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/reporterror-cross-realm-method.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>self.reportError() dispatches an "error" event for this's relevant global object</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/webappapis.html#dom-reporterror">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<body>
+<script>
+setup({ allow_uncaught_exception: true });
+
+async_test(t => {
+ window.addEventListener("error", t.unreached_func("'error' event should not be dispatched for top window!"));
+
+ const iframe = document.createElement("iframe");
+ iframe.onload = t.step_func_done(() => {
+ let eventFired = false;
+ const error = new TypeError("foo");
+ const otherWindow = iframe.contentWindow;
+ otherWindow.addEventListener("error", t.step_func(event => {
+ assert_equals(event.error, error);
+ eventFired = true;
+ }));
+
+ window.reportError.call(otherWindow, error);
+ assert_true(eventFired);
+ });
+ document.body.append(iframe);
+});
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/reporterror-in-detached-window-crash.html b/testing/web-platform/tests/html/webappapis/scripting/reporterror-in-detached-window-crash.html
new file mode 100644
index 0000000000..33662caa4d
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/reporterror-in-detached-window-crash.html
@@ -0,0 +1,11 @@
+<!doctype html>
+<title>window.reportError() doesn't crash when window is detached</title>
+<link rel="help" href="https://crbug.com/1445375">
+<body>
+<script>
+let i = document.createElement("iframe");
+document.body.appendChild(i);
+let i_win = i.contentWindow;
+i.remove();
+i_win.reportError("an error");
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/scripting/reporterror.any.js b/testing/web-platform/tests/html/webappapis/scripting/reporterror.any.js
new file mode 100644
index 0000000000..b9e7ba25bc
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/scripting/reporterror.any.js
@@ -0,0 +1,49 @@
+setup({ allow_uncaught_exception:true });
+
+[
+ 1,
+ new TypeError(),
+ undefined
+].forEach(throwable => {
+ test(t => {
+ let happened = false;
+ self.addEventListener("error", t.step_func(e => {
+ assert_true(e.message !== "");
+ assert_equals(e.filename, new URL("reporterror.any.js", location.href).href);
+ assert_greater_than(e.lineno, 0);
+ assert_greater_than(e.colno, 0);
+ assert_equals(e.error, throwable);
+ happened = true;
+ }), { once:true });
+ self.reportError(throwable);
+ assert_true(happened);
+ }, `self.reportError(${throwable})`);
+});
+
+test(() => {
+ assert_throws_js(TypeError, () => self.reportError());
+}, `self.reportError() (without arguments) throws`);
+
+test(() => {
+ // Workaround for https://github.com/web-platform-tests/wpt/issues/32105
+ let invoked = false;
+ self.reportError({
+ get name() {
+ invoked = true;
+ assert_unreached('get name')
+ },
+ get message() {
+ invoked = true;
+ assert_unreached('get message');
+ },
+ get fileName() {
+ invoked = true;
+ assert_unreached('get fileName');
+ },
+ get lineNumber() {
+ invoked = true;
+ assert_unreached('get lineNumber');
+ }
+ });
+ assert_false(invoked);
+}, `self.reportError() doesn't invoke getters`);