summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/html/webappapis/scripting/processing-model-2
diff options
context:
space:
mode:
Diffstat (limited to 'testing/web-platform/tests/html/webappapis/scripting/processing-model-2')
-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
76 files changed, 3293 insertions, 0 deletions
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>