summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/html/dom/render-blocking
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /testing/web-platform/tests/html/dom/render-blocking
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/web-platform/tests/html/dom/render-blocking')
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/blocking-idl-attr.html44
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-001.tentative.html28
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-002.tentative.html37
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-003.tentative.html33
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-004.tentative.html30
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-005.tentative.html30
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-006.tentative.html30
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-007.tentative.html28
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-008.tentative.html30
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-009.tentative.html30
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-010.tentative.html30
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-011.tentative.html30
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-012.tentative.html30
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-013.tentative.html30
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-014.tentative.html36
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-015.tentative.html32
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-016.tentative.html32
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-017.tentative.html31
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-018.tentative.html32
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-019.tentative.html32
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-020.tentative.html31
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-021.tentative.html35
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-022.tentative.html32
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-023.tentative.html31
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-024.tentative.html36
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-025.tentative.html38
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-026.tentative.html38
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-027.tentative.html38
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-028.tentative.html48
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/non-render-blocking-scripts.optional.html61
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/parser-blocking-script.html19
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/parser-inserted-async-inline-module-with-import.html21
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/parser-inserted-async-script.html19
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/parser-inserted-defer-script.html19
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/parser-inserted-inline-module-with-import.html21
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/parser-inserted-module-script.html19
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/parser-inserted-style-element.html20
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/parser-inserted-stylesheet-link.html18
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/remove-attr-script-keeps-blocking.html25
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/remove-attr-style-keeps-blocking.html28
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/remove-attr-stylesheet-link-keeps-blocking.html27
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/remove-attr-unblocks-rendering.optional.html86
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/remove-element-unblocks-rendering.optional.html83
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/remove-pending-async-render-blocking-script.html19
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/script-inserted-inline-module-with-import.html25
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/script-inserted-module-script.html22
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/script-inserted-script.html21
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/script-inserted-style-element.html26
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/script-inserted-stylesheet-link.html27
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/support/dummy-1.js1
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/support/dummy-1.mjs1
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/support/target-red.css3
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/support/test-render-blocking.js118
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/support/utils.js5
54 files changed, 1696 insertions, 0 deletions
diff --git a/testing/web-platform/tests/html/dom/render-blocking/blocking-idl-attr.html b/testing/web-platform/tests/html/dom/render-blocking/blocking-idl-attr.html
new file mode 100644
index 0000000000..c33b411eb4
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/blocking-idl-attr.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<title>Tests the 'blocking' IDL attribute on link, script and style elements</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+// Tests that the 'blocking' attribute follows the IDL:
+// [SameObject, PutForwards=value] readonly attribute DOMTokenList blocking;
+
+test(() => {
+ const link = document.createElement('link');
+ assert_true(link.blocking.supports('render'));
+ assert_false(link.blocking.supports('asdf'));
+}, "Supported tokens of the 'blocking' IDL attribute of the link element");
+
+test(() => {
+ const link = document.createElement('link');
+ link.blocking = 'asdf';
+ assert_equals(link.blocking.value, 'asdf');
+}, "Setting the 'blocking' IDL attribute of the link element");
+
+test(() => {
+ const script = document.createElement('script');
+ assert_true(script.blocking.supports('render'));
+ assert_false(script.blocking.supports('asdf'));
+}, "Supported tokens of the 'blocking' IDL attribute of the script element");
+
+test(() => {
+ const script = document.createElement('script');
+ script.blocking = 'asdf';
+ assert_equals(script.blocking.value, 'asdf');
+}, "Setting the 'blocking' IDL attribute of the script element");
+
+test(() => {
+ const style = document.createElement('style');
+ assert_true(style.blocking.supports('render'));
+ assert_false(style.blocking.supports('asdf'));
+}, "Supported tokens of the 'blocking' IDL attribute of the style element");
+
+test(() => {
+ const style = document.createElement('style');
+ style.blocking = 'asdf';
+ assert_equals(style.blocking.value, 'asdf');
+}, "Setting the 'blocking' IDL attribute of the style element");
+</script>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-001.tentative.html b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-001.tentative.html
new file mode 100644
index 0000000000..36567f9d54
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-001.tentative.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/utils.js"></script>
+<title>`link rel=expect` defers frames until href element is parsed</title>
+
+<link rel=expect href="#last" blocking="render">
+<script>
+async_test((t) => {
+ requestAnimationFrame(() => {
+ t.step(() => assert_true(!!document.getElementById("last")));
+ t.done();
+ });
+}, "blocking defers frames until full parsing");
+</script>
+</head>
+<body>
+ <div id="first"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="second"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="last"></div>
+</body>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-002.tentative.html b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-002.tentative.html
new file mode 100644
index 0000000000..3c907597f7
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-002.tentative.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/utils.js"></script>
+<title>Frames starts after href element is parsed before the end</title>
+
+<link rel=expect href="#third" blocking="render">
+<script>
+async_test((t) => {
+ requestAnimationFrame(() => {
+ t.step(() => assert_true(!!document.getElementById("third")));
+ t.step(() => assert_false(!!document.getElementById("last")));
+ t.done();
+ });
+}, "blocking defers until needed element is parsed");
+</script>
+</head>
+<body>
+ <div id="first"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="second"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="third"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="fourth"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="last"></div>
+</body>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-003.tentative.html b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-003.tentative.html
new file mode 100644
index 0000000000..2858798a35
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-003.tentative.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/utils.js"></script>
+<title>Adding link in the head has an effect</title>
+
+<script>
+let link = document.createElement("link");
+link.rel = "expect";
+link.href = "#last";
+link.blocking = "render";
+document.head.appendChild(link)
+
+async_test((t) => {
+ requestAnimationFrame(() => {
+ t.step(() => assert_true(!!document.getElementById("last")));
+ t.done();
+ });
+}, "adding link in the head defers frames");
+</script>
+</head>
+<body>
+ <div id="first"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="second"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="last"></div>
+</body>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-004.tentative.html b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-004.tentative.html
new file mode 100644
index 0000000000..f45f558720
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-004.tentative.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/utils.js"></script>
+<title>Removing link in the head has an effect</title>
+
+<link id=link rel=expect href="#last" blocking="render">
+<script>
+link.remove();
+
+async_test((t) => {
+ requestAnimationFrame(() => {
+ t.step(() => assert_false(!!document.getElementById("last")));
+ t.done();
+ });
+}, "removing link in the head makes it no longer blocking");
+</script>
+</head>
+<body>
+ <div id="first"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="second"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="last"></div>
+</body>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-005.tentative.html b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-005.tentative.html
new file mode 100644
index 0000000000..098a3c5767
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-005.tentative.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/utils.js"></script>
+<title>Removing blocking attr in the head has an effect</title>
+
+<link id=link rel=expect href="#last" blocking="render">
+<script>
+link.blocking = ""
+
+async_test((t) => {
+ requestAnimationFrame(() => {
+ t.step(() => assert_false(!!document.getElementById("last")));
+ t.done();
+ });
+}, "removing 'blocking' makes it no longer blocking");
+</script>
+</head>
+<body>
+ <div id="first"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="second"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="last"></div>
+</body>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-006.tentative.html b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-006.tentative.html
new file mode 100644
index 0000000000..223e42109e
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-006.tentative.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/utils.js"></script>
+<title>Adding blocking attr in the head has an effect</title>
+
+<link id=link rel=expect href="#last">
+<script>
+link.blocking = "render"
+
+async_test((t) => {
+ requestAnimationFrame(() => {
+ t.step(() => assert_true(!!document.getElementById("last")));
+ t.done();
+ });
+}, "adding 'blocking=render' in the head makes it blocking");
+</script>
+</head>
+<body>
+ <div id="first"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="second"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="last"></div>
+</body>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-007.tentative.html b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-007.tentative.html
new file mode 100644
index 0000000000..9aa0aeea79
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-007.tentative.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/utils.js"></script>
+<title>Media attribute that doesn't match makes the link not apply</title>
+
+<link rel=expect href="#last" blocking="render" media="(max-width: 10px)">
+<script>
+async_test((t) => {
+ requestAnimationFrame(() => {
+ t.step(() => assert_false(!!document.getElementById("last")));
+ t.done();
+ });
+}, "link with non-matching media has no effect");
+</script>
+</head>
+<body>
+ <div id="first"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="second"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="last"></div>
+</body>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-008.tentative.html b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-008.tentative.html
new file mode 100644
index 0000000000..e671dda19c
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-008.tentative.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/utils.js"></script>
+<title>Media attribute changes in the head to apply</title>
+
+<link id=link rel=expect href="#last" blocking="render" media="(max-width: 10px)">
+<script>
+link.media = "(min-width: 10px)";
+
+async_test((t) => {
+ requestAnimationFrame(() => {
+ t.step(() => assert_true(!!document.getElementById("last")));
+ t.done();
+ });
+}, "changing media to matching causes link to have an effect");
+</script>
+</head>
+<body>
+ <div id="first"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="second"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="last"></div>
+</body>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-009.tentative.html b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-009.tentative.html
new file mode 100644
index 0000000000..8498816ea5
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-009.tentative.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/utils.js"></script>
+<title>Media attribute changes in the head to not apply</title>
+
+<link id=link rel=expect href="#last" blocking="render" media="(min-width: 10px)">
+<script>
+link.media = "(max-width: 10px)";
+
+async_test((t) => {
+ requestAnimationFrame(() => {
+ t.step(() => assert_false(!!document.getElementById("last")));
+ t.done();
+ });
+}, "changing media to non-matching makes it non blocking");
+</script>
+</head>
+<body>
+ <div id="first"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="second"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="last"></div>
+</body>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-010.tentative.html b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-010.tentative.html
new file mode 100644
index 0000000000..ef6f709012
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-010.tentative.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/utils.js"></script>
+<title>Rel attribute changes in the head to not apply</title>
+
+<link id=link rel=expect href="#last" blocking="render">
+<script>
+link.rel = "stylesheet";
+
+async_test((t) => {
+ requestAnimationFrame(() => {
+ t.step(() => assert_false(!!document.getElementById("last")));
+ t.done();
+ });
+}, "changing rel to non-expect makes it non blocking");
+</script>
+</head>
+<body>
+ <div id="first"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="second"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="last"></div>
+</body>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-011.tentative.html b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-011.tentative.html
new file mode 100644
index 0000000000..dee82d8c59
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-011.tentative.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/utils.js"></script>
+<title>Rel attribute changes in the head to apply</title>
+
+<link id=link rel=stylesheet href="#last" blocking="render">
+<script>
+link.rel = "expect";
+
+async_test((t) => {
+ requestAnimationFrame(() => {
+ t.step(() => assert_true(!!document.getElementById("last")));
+ t.done();
+ });
+}, "changing rel to expect in the head causes it to be blocking");
+</script>
+</head>
+<body>
+ <div id="first"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="second"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="last"></div>
+</body>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-012.tentative.html b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-012.tentative.html
new file mode 100644
index 0000000000..4110e54c5f
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-012.tentative.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/utils.js"></script>
+<title>Href attribute changes in the head to apply</title>
+
+<link id=link rel=expect href="" blocking="render">
+<script>
+link.href = "#last";
+
+async_test((t) => {
+ requestAnimationFrame(() => {
+ t.step(() => assert_true(!!document.getElementById("last")));
+ t.done();
+ });
+}, "adding href in the head makes it blocking");
+</script>
+</head>
+<body>
+ <div id="first"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="second"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="last"></div>
+</body>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-013.tentative.html b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-013.tentative.html
new file mode 100644
index 0000000000..ecd97be86a
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-013.tentative.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/utils.js"></script>
+<title>Href attribute changes in the head to not apply</title>
+
+<link id=link rel=expect href="#last" blocking="render">
+<script>
+link.href = "";
+
+async_test((t) => {
+ requestAnimationFrame(() => {
+ t.step(() => assert_false(!!document.getElementById("last")));
+ t.done();
+ });
+}, "removing href makes it no longer blocking");
+</script>
+</head>
+<body>
+ <div id="first"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="second"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="last"></div>
+</body>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-014.tentative.html b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-014.tentative.html
new file mode 100644
index 0000000000..ea8948de42
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-014.tentative.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/utils.js"></script>
+<title>Blocking link added in the body has no effect</title>
+
+<script>
+async_test((t) => {
+ requestAnimationFrame(() => {
+ t.step(() => assert_false(!!document.getElementById("last")));
+ t.done();
+ });
+}, "link in the body has no effect");
+</script>
+</head>
+<body>
+<link rel=expect href="#last" blocking="render">
+<script>
+let link = document.createElement("link");
+link.rel = "rel";
+link.href = "#last";
+link.blocking = "render";
+document.head.appendChild(link);
+</script>
+
+ <div id="first"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="second"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="last"></div>
+</body>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-015.tentative.html b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-015.tentative.html
new file mode 100644
index 0000000000..a775ee4174
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-015.tentative.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/utils.js"></script>
+<title>Blocking link removed in the body has an effect</title>
+
+<link id=link rel=expect href="#last" blocking="render">
+<script>
+async_test((t) => {
+ requestAnimationFrame(() => {
+ t.step(() => assert_false(!!document.getElementById("last")));
+ t.done();
+ });
+}, "removing link the body makes it non blocking");
+</script>
+</head>
+<body>
+<script>
+link.remove();
+</script>
+
+ <div id="first"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="second"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="last"></div>
+</body>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-016.tentative.html b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-016.tentative.html
new file mode 100644
index 0000000000..8968c5dacd
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-016.tentative.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/utils.js"></script>
+<title>Removing blocking attr in the body has an effect</title>
+
+<link id=link rel=expect href="#last" blocking="render">
+<script>
+async_test((t) => {
+ requestAnimationFrame(() => {
+ t.step(() => assert_false(!!document.getElementById("last")));
+ t.done();
+ });
+}, "removing 'blocking' in the body makes it non blocking");
+</script>
+</head>
+<body>
+<script>
+link.blocking = "";
+</script>
+
+ <div id="first"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="second"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="last"></div>
+</body>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-017.tentative.html b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-017.tentative.html
new file mode 100644
index 0000000000..2d3b574721
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-017.tentative.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/utils.js"></script>
+<title>Adding blocking attr in the body has no effect</title>
+
+<link id=link rel=expect href="#last">
+<script>
+async_test((t) => {
+ requestAnimationFrame(() => {
+ t.step(() => assert_false(!!document.getElementById("last")));
+ t.done();
+ });
+}, "adding 'blocking=render' in the body has no effect");
+</script>
+</head>
+<body>
+<script>
+link.blocking = "render"
+</script>
+ <div id="first"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="second"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="last"></div>
+</body>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-018.tentative.html b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-018.tentative.html
new file mode 100644
index 0000000000..76e6394b5b
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-018.tentative.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/utils.js"></script>
+<title>Media attribute changes in the body to apply, but has no effect</title>
+
+<link id=link rel=expect href="#last" blocking="render" media="(max-width: 10px)">
+<script>
+async_test((t) => {
+ requestAnimationFrame(() => {
+ t.step(() => assert_false(!!document.getElementById("last")));
+ t.done();
+ });
+}, "changing media to matching in the body has no effect");
+</script>
+</head>
+<body>
+<script>
+link.media = "(min-width: 10px)";
+</script>
+
+ <div id="first"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="second"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="last"></div>
+</body>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-019.tentative.html b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-019.tentative.html
new file mode 100644
index 0000000000..80a7019edc
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-019.tentative.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/utils.js"></script>
+<title>Media attribute changes in the body to not apply</title>
+
+<link id=link rel=expect href="#last" blocking="render" media="(min-width: 10px)">
+<script>
+async_test((t) => {
+ requestAnimationFrame(() => {
+ t.step(() => assert_false(!!document.getElementById("last")));
+ t.done();
+ });
+}, "changing media to non-matching in the body makes it non blocking");
+</script>
+</head>
+<body>
+<script>
+link.media = "(max-width: 10px)";
+</script>
+
+ <div id="first"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="second"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="last"></div>
+</body>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-020.tentative.html b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-020.tentative.html
new file mode 100644
index 0000000000..10019c943f
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-020.tentative.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/utils.js"></script>
+<title>Rel attribute changes in the body to not apply</title>
+
+<link id=link rel=expect href="#last" blocking="render">
+<script>
+async_test((t) => {
+ requestAnimationFrame(() => {
+ t.step(() => assert_false(!!document.getElementById("last")));
+ t.done();
+ });
+}, "changing rel to non-expect in the body makes it non blocking");
+</script>
+</head>
+<body>
+<script>
+link.rel = "stylesheet";
+</script>
+ <div id="first"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="second"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="last"></div>
+</body>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-021.tentative.html b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-021.tentative.html
new file mode 100644
index 0000000000..1ca2114689
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-021.tentative.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/utils.js"></script>
+<title>Rel attribute changes in the body to apply, but has no effect</title>
+
+<link id=link rel=stylesheet href="#last" blocking="render">
+<script>
+async_test((t) => {
+ requestAnimationFrame(() => {
+ t.step(() => assert_false(!!document.getElementById("last")));
+ t.done();
+ });
+}, "changing rel to expect in the body has no effect");
+</script>
+</head>
+<body>
+<script>
+link.rel = "expect";
+</script>
+ <div id="first"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="second"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="third"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="last"></div>
+</body>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-022.tentative.html b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-022.tentative.html
new file mode 100644
index 0000000000..5dfbcac30a
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-022.tentative.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/utils.js"></script>
+<title>Href attribute changes in the body to apply, but has no effect</title>
+
+<link id=link rel=expect href="" blocking="render">
+<script>
+async_test((t) => {
+ requestAnimationFrame(() => {
+ t.step(() => assert_false(!!document.getElementById("last")));
+ t.done();
+ });
+}, "adding href in the body has no effect");
+</script>
+</head>
+<body>
+<script>
+link.href = "#last";
+</script>
+
+ <div id="first"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="second"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="last"></div>
+</body>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-023.tentative.html b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-023.tentative.html
new file mode 100644
index 0000000000..8fe8b6a8c8
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-023.tentative.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/utils.js"></script>
+<title>Href attribute changes in the body to not apply</title>
+
+<link id=link rel=expect href="#last" blocking="render">
+<script>
+async_test((t) => {
+ requestAnimationFrame(() => {
+ t.step(() => assert_false(!!document.getElementById("last")));
+ t.done();
+ });
+}, "removing href in the body makes it non blocking");
+</script>
+</head>
+<body>
+<script>
+link.href = "";
+</script>
+ <div id="first"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="second"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="last"></div>
+</body>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-024.tentative.html b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-024.tentative.html
new file mode 100644
index 0000000000..19e4020fb7
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-024.tentative.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/utils.js"></script>
+<title>Unknown href causes the whole document to be blocked</title>
+
+<link rel=expect href="#unknown" blocking="render">
+<script>
+async_test((t) => {
+ requestAnimationFrame(() => {
+ t.step(() => assert_true(!!document.getElementById("last")));
+ t.done();
+ });
+}, "unknown href causes the whole document to be blocked");
+</script>
+</head>
+<body>
+ <div id="first"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="second"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="third"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="fourth"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="last"></div>
+</body>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-025.tentative.html b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-025.tentative.html
new file mode 100644
index 0000000000..689ae69f45
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-025.tentative.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/utils.js"></script>
+<title>Adding an id to parsed element satisfies render block</title>
+
+<link rel=expect href="#first" blocking="render">
+<script>
+async_test((t) => {
+ requestAnimationFrame(() => {
+ t.step(() => assert_true(!!document.getElementById("first")));
+ t.step(() => assert_false(!!document.getElementById("last")));
+ t.done();
+ });
+}, "adding an id in the body satisfies render block");
+</script>
+</head>
+<body>
+ <div id="willbefirst"></div>
+ <script>
+ willbefirst.id = "first";
+ generateParserDelay();
+ </script>
+ <div id="second"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="third"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="fourth"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="last"></div>
+</body>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-026.tentative.html b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-026.tentative.html
new file mode 100644
index 0000000000..6abfc43b8b
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-026.tentative.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/utils.js"></script>
+<title>Removing id keeps render block satisfied</title>
+
+<link rel=expect href="#first" blocking="render">
+<script>
+async_test((t) => {
+ requestAnimationFrame(() => {
+ t.step(() => assert_true(!!document.getElementById("wasfirst")));
+ t.step(() => assert_false(!!document.getElementById("last")));
+ t.done();
+ });
+}, "removing id after it was renderer keeps render block satisfied");
+</script>
+</head>
+<body>
+ <div id="first"></div>
+ <script>
+ first.id = "wasfirst";
+ generateParserDelay();
+ </script>
+ <div id="second"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="third"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="fourth"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="last"></div>
+</body>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-027.tentative.html b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-027.tentative.html
new file mode 100644
index 0000000000..56f88e0fc2
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-027.tentative.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/utils.js"></script>
+<title>Unknown href causes the whole document to be blocked</title>
+
+<link id=link rel=expect href="#unknown" blocking="render">
+<script>
+link.href = "#stillunknown";
+
+async_test((t) => {
+ requestAnimationFrame(() => {
+ t.step(() => assert_true(!!document.getElementById("last")));
+ t.done();
+ });
+}, "unknown href causes the whole document to be blocked (with href changes!)");
+</script>
+</head>
+<body>
+ <div id="notfirst"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="second"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="third"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="fourth"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="last"></div>
+</body>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-028.tentative.html b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-028.tentative.html
new file mode 100644
index 0000000000..a64d542c4a
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/element-render-blocking-028.tentative.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/utils.js"></script>
+<title>Multiple links and all but one removed</title>
+
+<link rel=expect href="#third" blocking="render">
+<link id=one rel=expect href="#third" blocking="render">
+<link id=two rel=expect href="#third" blocking="render">
+<link id=three rel=expect href="#third" blocking="render">
+<link id=four rel=expect href="#third" blocking="render">
+<script>
+async_test((t) => {
+ requestAnimationFrame(() => {
+ t.step(() => assert_true(!!document.getElementById("third")));
+ t.step(() => assert_false(!!document.getElementById("last")));
+ t.done();
+ });
+}, "removing some links but not all keeps at least the matching link blocking");
+
+one.remove();
+two.remove();
+</script>
+</head>
+<body>
+<script>
+three.remove();
+four.remove();
+</script>
+ <div id="first"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="second"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="third"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="fourth"></div>
+ <script>
+ generateParserDelay();
+ </script>
+ <div id="last"></div>
+</body>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/non-render-blocking-scripts.optional.html b/testing/web-platform/tests/html/dom/render-blocking/non-render-blocking-scripts.optional.html
new file mode 100644
index 0000000000..a4c32ea037
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/non-render-blocking-scripts.optional.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<title>Tests when script is not implicitly potentially render-blocking</title>
+<link rel="help" href="https://github.com/whatwg/html/pull/7894">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/test-render-blocking.js"></script>
+
+<!--
+ The test is marked "optional" because even when the document is not
+ render-blocked, the user agent is still free to take other factors, which are
+ not limited by the spec, into consideration and therefore decide not to
+ render. However, it is still more desirable if rendering starts
+ immediately/soon.
+-->
+
+<script class="test" data="parser-inserted async script" async
+ src="support/dummy-1.js?pipe=trickle(d1)&async"></script>
+<script class="test" data="parser-inserted defer script" defer
+ src="support/dummy-1.js?pipe=trickle(d1)&defer"></script>
+<script class="test" data="parser-inserted module script" type="module"
+ src="support/dummy-1.mjs?pipe=trickle(d1)"></script>
+<script class="test" data="parser-inserted async module script" type="module"
+ async src="support/dummy-1.mjs?pipe=trickle(d1)&async"></script>
+
+<script>
+function addTestScriptElement(title, attributes) {
+ let element = document.createElement('script');
+ element.className = 'test';
+ element.setAttribute('data', title);
+ Object.assign(element, attributes);
+ document.head.appendChild(element);
+}
+
+addTestScriptElement('script-inserted script', {src: 'support/dummy-1.js?pipe=trickle(d1)&dynamic'});
+addTestScriptElement('script-inserted sync script', {async: false, src: 'support/dummy-1.js?pipe=trickle(d1)&dynamicSync'});
+addTestScriptElement('script-inserted module script', {type: 'module', src: 'support/dummy-1.mjs?pipe=trickle(d1)&dynamic'});
+</script>
+
+<div id="dummy">Some text</div>
+
+<script>
+const testElements = [...document.querySelectorAll('.test')];
+const loadObservers = testElements.map(element => new LoadObserver(element));
+
+promise_setup(async () => {
+ // Test cases are run after rendering is unblocked.
+ await new Promise(resolve => requestAnimationFrame(resolve));
+});
+
+for (let index = 0; index < testElements.length; ++index) {
+ promise_test(
+ async () => assert_false(loadObservers[index].finished),
+ testElements[index].getAttribute('data') + ' is not implicitly render-blocking');
+}
+
+for (let index = 0; index < testElements.length; ++index) {
+ promise_test(
+ () => loadObservers[index].load,
+ testElements[index].getAttribute('data') + ' should eventually be loaded and evaluated');
+}
+</script>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/parser-blocking-script.html b/testing/web-platform/tests/html/dom/render-blocking/parser-blocking-script.html
new file mode 100644
index 0000000000..8d391144b2
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/parser-blocking-script.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<title>Parser-blocking script elements are implicitly render-blocking</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/test-render-blocking.js"></script>
+
+<script>
+// Add some renderable content before parser inserts body
+document.documentElement.appendChild(document.createTextNode('text'));
+
+// Test must be setup before the parser-blocking script
+test_render_blocking(
+ () => assert_equals(window.dummy, 1),
+ 'Parser-blocking script is evaluated');
+</script>
+
+<script src="support/dummy-1.js?pipe=trickle(d1)"></script>
+
+<div>Some more text</div>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/parser-inserted-async-inline-module-with-import.html b/testing/web-platform/tests/html/dom/render-blocking/parser-inserted-async-inline-module-with-import.html
new file mode 100644
index 0000000000..50a2add277
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/parser-inserted-async-inline-module-with-import.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<head>
+<title>Parser-inserted async inline module script elements with "blocking=render" are render-blocking</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ window.did_execute_script = false;
+</script>
+<script type="module" blocking="render" async>
+ import "/loading/resources/dummy.js?pipe=trickle(d1)";
+ window.did_execute_script = true;
+</script>
+</head>
+<div id="dummy">some text</div>
+
+<script>
+ promise_test(async t => {
+ await new Promise(resolve => requestAnimationFrame(() => resolve()));
+ assert_true(window.did_execute_script, "Parser-inserted async render-blocking inline module script should execute before rAF callback");
+ });
+</script>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/parser-inserted-async-script.html b/testing/web-platform/tests/html/dom/render-blocking/parser-inserted-async-script.html
new file mode 100644
index 0000000000..4b2216dfcb
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/parser-inserted-async-script.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<title>Parser-inserted async script elements with "blocking=render" are render-blocking</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/test-render-blocking.js"></script>
+
+<script id="async-script" async blocking="render"
+ src="support/dummy-1.js?pipe=trickle(d1)">
+</script>
+
+<div>Some text</div>
+
+<script>
+const asyncScript = document.getElementById('async-script');
+test_render_blocking(
+ asyncScript,
+ () => assert_equals(window.dummy, 1),
+ 'Parser-inserted render-blocking async script is evaluated');
+</script>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/parser-inserted-defer-script.html b/testing/web-platform/tests/html/dom/render-blocking/parser-inserted-defer-script.html
new file mode 100644
index 0000000000..1ae8caf2ec
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/parser-inserted-defer-script.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<title>Parser-inserted defer script elements with "blocking=render" are render-blocking</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/test-render-blocking.js"></script>
+
+<script id="defer-script" defer blocking="render"
+ src="support/dummy-1.js?pipe=trickle(d1)">
+</script>
+
+<div>Some text</div>
+
+<script>
+const deferScript = document.getElementById('defer-script');
+test_render_blocking(
+ deferScript,
+ () => assert_equals(window.dummy, 1),
+ 'Parser-inserted render-blocking defer script is evaluated');
+</script>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/parser-inserted-inline-module-with-import.html b/testing/web-platform/tests/html/dom/render-blocking/parser-inserted-inline-module-with-import.html
new file mode 100644
index 0000000000..af2ac46aaf
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/parser-inserted-inline-module-with-import.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<head>
+<title>Parser-inserted module script elements with "blocking=render" are render-blocking</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ window.did_execute_script = false;
+</script>
+<script type="module" blocking="render">
+ import "/loading/resources/dummy.js?pipe=trickle(d1)";
+ window.did_execute_script = true;
+</script>
+</head>
+<div id="dummy">some text</div>
+
+<script>
+ promise_test(async t => {
+ await new Promise(resolve => requestAnimationFrame(() => resolve()));
+ assert_true(window.did_execute_script, "Parser-inserted render-blocking inline module script should execute before rAF callback");
+ });
+</script>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/parser-inserted-module-script.html b/testing/web-platform/tests/html/dom/render-blocking/parser-inserted-module-script.html
new file mode 100644
index 0000000000..2bca88e73c
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/parser-inserted-module-script.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<title>Parser-inserted module script elements with "blocking=render" are render-blocking</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/test-render-blocking.js"></script>
+
+<script id="module-script" type="module" blocking="render"
+ src="support/dummy-1.mjs?pipe=trickle(d1)">
+</script>
+
+<div id="dummy">some text</div>
+
+<script>
+const moduleScript = document.getElementById('module-script');
+test_render_blocking(
+ moduleScript,
+ () => assert_equals(document.getElementById('dummy').textContent, '1'),
+ 'Parser-inserted render-blocking module script is evaluated');
+</script>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/parser-inserted-style-element.html b/testing/web-platform/tests/html/dom/render-blocking/parser-inserted-style-element.html
new file mode 100644
index 0000000000..9a358aa493
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/parser-inserted-style-element.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<title>Parser-inserted style elements are implicitly render-blocking</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/test-render-blocking.js"></script>
+<script>
+// Test case must be set up before the stylesheet, because the stylesheet is
+// script-blocking, which means we can't set it up while the stylesheet is
+// loading.
+test_render_blocking(() => {
+ let color = getComputedStyle(document.querySelector('.target')).color;
+ assert_equals(color, 'rgb(255, 0, 0)');
+}, 'Render-blocking stylesheet is applied');
+</script>
+<style>
+@import url('support/target-red.css?pipe=trickle(d1)');
+</style>
+<div class="target">
+ This should be red
+</div>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/parser-inserted-stylesheet-link.html b/testing/web-platform/tests/html/dom/render-blocking/parser-inserted-stylesheet-link.html
new file mode 100644
index 0000000000..0a771448fd
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/parser-inserted-stylesheet-link.html
@@ -0,0 +1,18 @@
+<!doctype html>
+<title>Parser-inserted stylesheet links are implicitly render-blocking</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/test-render-blocking.js"></script>
+<script>
+// Test case must be set up before the stylesheet, because the stylesheet is
+// script-blocking, which means we can't set it up while the stylesheet is
+// loading.
+test_render_blocking(() => {
+ let color = getComputedStyle(document.querySelector('.target')).color;
+ assert_equals(color, 'rgb(255, 0, 0)');
+}, 'Render-blocking stylesheet is applied');
+</script>
+<link rel="stylesheet" href="support/target-red.css?pipe=trickle(d1)">
+<div class="target">
+ This should be red
+</div>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/remove-attr-script-keeps-blocking.html b/testing/web-platform/tests/html/dom/render-blocking/remove-attr-script-keeps-blocking.html
new file mode 100644
index 0000000000..451d2f3695
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/remove-attr-script-keeps-blocking.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<title>Synchronous script element still blocks rendering after removing `blocking=render`</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/test-render-blocking.js"></script>
+
+<script>
+// Test script must be added before the synchronous script because the
+// synchronous script is parser-blocking.
+
+promise_setup(async () => {
+ let script = await nodeInserted(document.head, node => node.id === 'script');
+ script.blocking = '';
+
+ // Also inserts some contents for non-compliant UA to render
+ document.body = document.createElement('body');
+ document.body.appendChild(document.createTextNode('Some text'));
+});
+
+test_render_blocking(
+ () => assert_equals(window.dummy, 1),
+ 'Render-blocking script is loaded and evaluated');
+</script>
+
+<script id="script" blocking="render" src="support/dummy-1.js?pipe=trickle(d1)"></script>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/remove-attr-style-keeps-blocking.html b/testing/web-platform/tests/html/dom/render-blocking/remove-attr-style-keeps-blocking.html
new file mode 100644
index 0000000000..31d4b56838
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/remove-attr-style-keeps-blocking.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<title>Parser-inserted style element still blocks rendering after removing `blocking=render`</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/test-render-blocking.js"></script>
+
+<script>
+// Test script must be added before the style element because the style
+// element is script-blocking.
+
+promise_setup(async () => {
+ let sheet = await nodeInserted(document.head, node => node.id === 'sheet');
+ sheet.blocking = '';
+});
+
+test_render_blocking(
+ () => {
+ let color = getComputedStyle(document.querySelector('.target')).color;
+ assert_equals(color, 'rgb(255, 0, 0)');
+ },
+ 'Render-blocking stylesheet is applied');
+</script>
+
+<style id="sheet" blocking="render">
+@import url("support/target-red.css?pipe=trickle(d1)");
+</style>
+
+<div class="target">Some text</div>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/remove-attr-stylesheet-link-keeps-blocking.html b/testing/web-platform/tests/html/dom/render-blocking/remove-attr-stylesheet-link-keeps-blocking.html
new file mode 100644
index 0000000000..1248b90b23
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/remove-attr-stylesheet-link-keeps-blocking.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<title>Parser-inserted stylesheet link still blocks rendering after removing `blocking=render`</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/test-render-blocking.js"></script>
+
+<script>
+// Test script must be added before the stylesheet link because the stylesheet
+// link is script-blocking.
+
+promise_setup(async () => {
+ let sheet = await nodeInserted(document.head, node => node.id === 'sheet');
+ sheet.blocking = '';
+});
+
+test_render_blocking(
+ () => {
+ let color = getComputedStyle(document.querySelector('.target')).color;
+ assert_equals(color, 'rgb(255, 0, 0)');
+ },
+ 'Render-blocking stylesheet is applied');
+</script>
+
+<link id="sheet" rel="stylesheet" blocking="render"
+ href="support/target-red.css?pipe=trickle(d1)">
+
+<div class="target">Some text</div>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/remove-attr-unblocks-rendering.optional.html b/testing/web-platform/tests/html/dom/render-blocking/remove-attr-unblocks-rendering.optional.html
new file mode 100644
index 0000000000..c73e3c6452
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/remove-attr-unblocks-rendering.optional.html
@@ -0,0 +1,86 @@
+<!DOCTYPE html>
+<title>Removing `blocking=render` should unblock rendering</title>
+<link rel="help" href="https://html.spec.whatwg.org/C/#blocking-attribute">
+<link rel="help" href="https://html.spec.whatwg.org/C/#rendering-opportunity">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/test-render-blocking.js"></script>
+
+<!--
+ The test is marked "optional" because even when the document is no longer
+ render-blocked, the user agent is still free to take other factors, which are
+ not limited by the spec, into consideration and therefore decide not to
+ render. However, it is still more desirable if rendering starts
+ immediately/soon.
+-->
+
+<script class="test" data="parser-inserted async script" async blocking="render"
+ src="support/dummy-1.js?pipe=trickle(d1)&async"></script>
+<script class="test" data="parser-inserted defer script" defer blocking="render"
+ src="support/dummy-1.js?pipe=trickle(d1)&defer"></script>
+<script class="test" data="parser-inserted module script" type="module"
+ blocking="render" src="support/dummy-1.mjs?pipe=trickle(d1)"></script>
+<script class="test" data="parser-inserted async module script" type="module"
+ async blocking="render" src="support/dummy-1.mjs?pipe=trickle(d1)&async"></script>
+
+<!--
+ No test for parser-inserted stylesheets and synchronous scripts because they
+ are render-blocking by default, so removing `blocking=render` does not unblock
+ rendering.
+-->
+
+<script>
+function addRenderBlockingElement(tag, title, attributes, optional_text) {
+ let element = document.createElement(tag);
+ element.className = 'test';
+ element.setAttribute('data', title);
+ element.blocking = 'render';
+ Object.assign(element, attributes);
+ if (optional_text)
+ element.textContent = optional_text;
+ document.head.appendChild(element);
+}
+
+addRenderBlockingElement(
+ 'link', 'script-inserted stylesheet link',
+ {rel: 'stylesheet', blocking: 'render', href: 'support/target-red.css?pipe=trickle(d1)&dynamic'});
+
+addRenderBlockingElement(
+ 'script', 'script-inserted script',
+ {src: 'support/dummy-1.js?pipe=trickle(d1)&dynamic'});
+addRenderBlockingElement(
+ 'script', 'script-inserted module script',
+ {type: 'module', src: 'support/dummy-1.mjs?pipe=trickle(d1)&dynamic'});
+
+addRenderBlockingElement(
+ 'style', 'script-inserted inline style', {},
+ '@import url("support/target-red.css?pipe=trickle(d1)&imported&dynamic")');
+</script>
+
+<div id="dummy">Some text</div>
+
+<script>
+const testElements = [...document.querySelectorAll('.test')];
+const loadObservers = testElements.map(element => new LoadObserver(element));
+
+promise_setup(async () => {
+ for (let element of testElements)
+ element.blocking = '';
+
+ // Test cases are run after rendering is unblocked.
+ await new Promise(resolve => requestAnimationFrame(resolve));
+});
+
+for (let index = 0; index < testElements.length; ++index) {
+ promise_test(
+ async () => assert_false(loadObservers[index].finished),
+ 'Render-blocking on ' + testElements[index].getAttribute('data') + ' is cancellable');
+}
+
+for (let index = 0; index < testElements.length; ++index) {
+ promise_test(
+ () => loadObservers[index].load,
+ 'Loading of ' + testElements[index].getAttribute('data') + ' should eventually succeed');
+}
+</script>
+
diff --git a/testing/web-platform/tests/html/dom/render-blocking/remove-element-unblocks-rendering.optional.html b/testing/web-platform/tests/html/dom/render-blocking/remove-element-unblocks-rendering.optional.html
new file mode 100644
index 0000000000..ad49c48c2e
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/remove-element-unblocks-rendering.optional.html
@@ -0,0 +1,83 @@
+<!DOCTYPE html>
+<title>Removing render-blocking element should unblock rendering</title>
+<link rel="help" href="https://html.spec.whatwg.org/C/#blocking-attribute">
+<link rel="help" href="https://html.spec.whatwg.org/C/#rendering-opportunity">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/test-render-blocking.js"></script>
+
+<!--
+ The test is marked "optional" because even when the document is no longer
+ render-blocked, the user agent is still free to take other factors, which are
+ not limited by the spec, into consideration and therefore decide not to
+ render. However, it is still more desirable if rendering starts
+ immediately/soon.
+-->
+
+<script class="test" data="parser-inserted async script" async blocking="render"
+ src="support/dummy-1.js?pipe=trickle(d1)&async"></script>
+<script class="test" data="parser-inserted defer script" defer blocking="render"
+ src="support/dummy-1.js?pipe=trickle(d1)&defer"></script>
+<script class="test" data="parser-inserted module script" type="module"
+ blocking="render" src="support/dummy-1.mjs?pipe=trickle(d1)"></script>
+<script class="test" data="parser-inserted async module script" type="module"
+ async blocking="render" src="support/dummy-1.mjs?pipe=trickle(d1)&async"></script>
+
+<!--
+ No test for parser-inserted stylesheets and synchronous scripts because
+ they are script-blocking or even parser-blocking, and they do not have new
+ behaviors to test about.
+-->
+
+<script>
+function addRenderBlockingElement(tag, title, attributes, optional_text) {
+ let element = document.createElement(tag);
+ element.className = 'test';
+ element.setAttribute('data', title);
+ element.blocking = 'render';
+ Object.assign(element, attributes);
+ if (optional_text)
+ element.textContent = optional_text;
+ document.head.appendChild(element);
+}
+
+addRenderBlockingElement(
+ 'link', 'script-inserted stylesheet link',
+ {rel: 'stylesheet', blocking: 'render', href: 'support/target-red.css?pipe=trickle(d1)&dynamic'});
+
+addRenderBlockingElement(
+ 'script', 'script-inserted script',
+ {src: 'support/dummy-1.js?pipe=trickle(d1)&dynamic'});
+addRenderBlockingElement(
+ 'script', 'script-inserted module script',
+ {type: 'module', src: 'support/dummy-1.mjs?pipe=trickle(d1)&dynamic'});
+
+addRenderBlockingElement(
+ 'style', 'script-inserted inline style', {},
+ '@import url("support/target-red.css?pipe=trickle(d1)&imported&dynamic")');
+</script>
+
+<div id="dummy">Some text</div>
+
+<script>
+const testElements = [...document.querySelectorAll('.test')];
+const loadObservers = testElements.map(element => new LoadObserver(element));
+
+promise_setup(async () => {
+ for (let element of testElements)
+ element.remove();
+
+ // Test cases are run after rendering is unblocked.
+ await new Promise(resolve => requestAnimationFrame(resolve));
+});
+
+for (let index = 0; index < testElements.length; ++index) {
+ promise_test(
+ async () => assert_false(loadObservers[index].finished),
+ 'Render-blocking on ' + testElements[index].getAttribute('data') + ' is cancellable');
+
+ // The loading can either continue or cancel. This test does not assert it.
+ loadObservers[index].load.catch(() => {});
+}
+</script>
+
diff --git a/testing/web-platform/tests/html/dom/render-blocking/remove-pending-async-render-blocking-script.html b/testing/web-platform/tests/html/dom/render-blocking/remove-pending-async-render-blocking-script.html
new file mode 100644
index 0000000000..5f6e8b34d1
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/remove-pending-async-render-blocking-script.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<title>Removed render-blocking script should not indefinitely block rendering</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script id="target" async blocking="render"
+ src="support/dummy-1.js?pipe=trickle(d1)"></script>
+<script>
+promise_test(async () => {
+ const target = document.getElementById('target');
+ const newDoc = document.implementation.createHTMLDocument('new document');
+ newDoc.documentElement.appendChild(target);
+
+ await new Promise(resolve => requestAnimationFrame(resolve));
+
+ // reqeustAnimationFrame() should be eventually run, but the script removed
+ // while pending should not be run.
+ assert_equals(window.dummy, undefined);
+});
+</script>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/script-inserted-inline-module-with-import.html b/testing/web-platform/tests/html/dom/render-blocking/script-inserted-inline-module-with-import.html
new file mode 100644
index 0000000000..576c0b321a
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/script-inserted-inline-module-with-import.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<head>
+<title>Script-inserted module script elements with "blocking=render" are render-blocking</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ window.did_execute_script = false;
+ const script = document.createElement("script");
+ script.type = "module";
+ script.blocking = "render";
+ script.textContent = `
+ import "/loading/resources/dummy.js?pipe=trickle(d1)";
+ window.did_execute_script = true;
+ `;
+ document.head.append(script);
+</script>
+</head>
+<div id="dummy">some text</div>
+
+<script>
+ promise_test(async t => {
+ await new Promise(resolve => requestAnimationFrame(() => resolve()));
+ assert_true(window.did_execute_script, "Script-inserted render-blocking inline module script should execute before rAF callback");
+ });
+</script>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/script-inserted-module-script.html b/testing/web-platform/tests/html/dom/render-blocking/script-inserted-module-script.html
new file mode 100644
index 0000000000..73f0d3cdf4
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/script-inserted-module-script.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<title>Script-inserted module script elements with "blocking=render" are render-blocking</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/test-render-blocking.js"></script>
+
+<script>
+const moduleScript = document.createElement('script');
+moduleScript.type = 'module';
+moduleScript.blocking = 'render';
+moduleScript.src = 'support/dummy-1.mjs?pipe=trickle(d1)';
+document.head.appendChild(moduleScript);
+</script>
+
+<div id="dummy">some text</div>
+
+<script>
+test_render_blocking(
+ moduleScript,
+ () => assert_equals(document.getElementById('dummy').textContent, '1'),
+ 'Script-inserted render-blocking module script is evaluated');
+</script>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/script-inserted-script.html b/testing/web-platform/tests/html/dom/render-blocking/script-inserted-script.html
new file mode 100644
index 0000000000..faf346b4dd
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/script-inserted-script.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<title>Script-inserted script elements with "blocking=render" are render-blocking</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/test-render-blocking.js"></script>
+
+<script>
+const script = document.createElement('script');
+script.src = 'support/dummy-1.js?pipe=trickle(d1)';
+script.blocking = 'render';
+document.head.appendChild(script);
+</script>
+
+<div>Some text</div>
+
+<script>
+test_render_blocking(
+ script,
+ () => assert_equals(window.dummy, 1),
+ 'Script-inserted render-blocking script is evaluated');
+</script>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/script-inserted-style-element.html b/testing/web-platform/tests/html/dom/render-blocking/script-inserted-style-element.html
new file mode 100644
index 0000000000..683706af50
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/script-inserted-style-element.html
@@ -0,0 +1,26 @@
+<!doctype html>
+<title>Script-inserted style elements with "blocking=render" are render-blocking</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/test-render-blocking.js"></script>
+
+<script>
+const style = document.createElement('style');
+style.blocking = 'render';
+style.textContent = "@import url('support/target-red.css?pipe=trickle(d1)');";
+document.head.appendChild(style);
+</script>
+
+<div class="target">
+ This should be red
+</div>
+
+<script>
+test_render_blocking(
+ style,
+ () => {
+ let color = getComputedStyle(document.querySelector('.target')).color;
+ assert_equals(color, 'rgb(255, 0, 0)');
+ },
+ 'Render-blocking stylesheet is applied');
+</script>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/script-inserted-stylesheet-link.html b/testing/web-platform/tests/html/dom/render-blocking/script-inserted-stylesheet-link.html
new file mode 100644
index 0000000000..46755387d7
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/script-inserted-stylesheet-link.html
@@ -0,0 +1,27 @@
+<!doctype html>
+<title>Script-inserted stylesheet links with "blocking=render" are render-blocking</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/test-render-blocking.js"></script>
+
+<script>
+const stylesheet = document.createElement('link');
+stylesheet.rel = 'stylesheet';
+stylesheet.href = 'support/target-red.css?pipe=trickle(d1)';
+stylesheet.blocking = 'render';
+document.head.appendChild(stylesheet);
+</script>
+
+<div class="target">
+ This should be red
+</div>
+
+<script>
+test_render_blocking(
+ stylesheet,
+ () => {
+ let color = getComputedStyle(document.querySelector('.target')).color;
+ assert_equals(color, 'rgb(255, 0, 0)');
+ },
+ 'Render-blocking stylesheet is applied');
+</script>
diff --git a/testing/web-platform/tests/html/dom/render-blocking/support/dummy-1.js b/testing/web-platform/tests/html/dom/render-blocking/support/dummy-1.js
new file mode 100644
index 0000000000..597772cf64
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/support/dummy-1.js
@@ -0,0 +1 @@
+window.dummy = 1;
diff --git a/testing/web-platform/tests/html/dom/render-blocking/support/dummy-1.mjs b/testing/web-platform/tests/html/dom/render-blocking/support/dummy-1.mjs
new file mode 100644
index 0000000000..9b85a21033
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/support/dummy-1.mjs
@@ -0,0 +1 @@
+document.getElementById('dummy').textContent = 1;
diff --git a/testing/web-platform/tests/html/dom/render-blocking/support/target-red.css b/testing/web-platform/tests/html/dom/render-blocking/support/target-red.css
new file mode 100644
index 0000000000..a387acd4ec
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/support/target-red.css
@@ -0,0 +1,3 @@
+.target {
+ color: red;
+}
diff --git a/testing/web-platform/tests/html/dom/render-blocking/support/test-render-blocking.js b/testing/web-platform/tests/html/dom/render-blocking/support/test-render-blocking.js
new file mode 100644
index 0000000000..71d0d68096
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/support/test-render-blocking.js
@@ -0,0 +1,118 @@
+// Observes the `load` event of an EventTarget, or the finishing of a resource
+// given its url. Requires `/preload/resources/preload_helper.js` for the latter
+// usage.
+class LoadObserver {
+ constructor(target) {
+ this.finishTime = null;
+ this.load = new Promise((resolve, reject) => {
+ if (target.addEventListener) {
+ target.addEventListener('load', ev => {
+ this.finishTime = ev.timeStamp;
+ resolve(ev);
+ });
+ target.addEventListener('error', reject);
+ } else if (typeof target === 'string') {
+ const observer = new PerformanceObserver(() => {
+ if (numberOfResourceTimingEntries(target)) {
+ this.finishTime = performance.now();
+ resolve();
+ }
+ });
+ observer.observe({type: 'resource', buffered: true});
+ } else {
+ reject('Unsupported target for LoadObserver');
+ }
+ });
+ }
+
+ get finished() {
+ return this.finishTime !== null;
+ }
+}
+
+// Observes the insertion of a script/parser-blocking element into DOM via
+// MutationObserver, so that we can access the element before it's loaded.
+function nodeInserted(parentNode, predicate) {
+ return new Promise(resolve => {
+ function callback(mutationList) {
+ for (let mutation of mutationList) {
+ for (let node of mutation.addedNodes) {
+ if (predicate(node))
+ resolve(node);
+ }
+ }
+ }
+ new MutationObserver(callback).observe(parentNode, {childList: true});
+ });
+}
+
+function createAutofocusTarget() {
+ const autofocusTarget = document.createElement('textarea');
+ autofocusTarget.setAttribute('autofocus', '');
+ // We may not have a body element at this point if we are testing a
+ // script-blocking stylesheet. Hence, the new element is added to
+ // documentElement.
+ document.documentElement.appendChild(autofocusTarget);
+ return autofocusTarget;
+}
+
+function createScrollTarget() {
+ const scrollTarget = document.createElement('div');
+ scrollTarget.style.overflow = 'scroll';
+ scrollTarget.style.height = '100px';
+ const scrollContent = document.createElement('div');
+ scrollContent.style.height = '200px';
+ scrollTarget.appendChild(scrollContent);
+ document.documentElement.appendChild(scrollTarget);
+ return scrollTarget;
+}
+
+function createAnimationTarget() {
+ const style = document.createElement('style');
+ style.textContent = `
+ @keyframes anim {
+ from { height: 100px; }
+ to { height: 200px; }
+ }
+ `;
+ const animationTarget = document.createElement('div');
+ animationTarget.style.backgroundColor = 'green';
+ animationTarget.style.height = '50px';
+ animationTarget.style.animation = 'anim 100ms';
+ document.documentElement.appendChild(style);
+ document.documentElement.appendChild(animationTarget);
+ return animationTarget;
+}
+
+// Error margin for comparing timestamps of paint and load events, in case they
+// are reported by different threads.
+const epsilon = 50;
+
+function test_render_blocking(optionalElementOrUrl, finalTest, finalTestTitle) {
+ // Ideally, we should observe the 'load' event on the specific render-blocking
+ // elements. However, this is not possible for script-blocking stylesheets, so
+ // we have to observe the 'load' event on 'window' instead.
+ if (!(optionalElementOrUrl instanceof HTMLElement) &&
+ typeof optionalElementOrUrl !== 'string') {
+ finalTestTitle = finalTest;
+ finalTest = optionalElementOrUrl;
+ optionalElementOrUrl = undefined;
+ }
+ const loadObserver = new LoadObserver(optionalElementOrUrl || window);
+
+ promise_test(async test => {
+ assert_implements(window.PerformancePaintTiming);
+
+ await test.step_wait(() => performance.getEntriesByType('paint').length);
+
+ assert_true(loadObserver.finished);
+ for (let entry of performance.getEntriesByType('paint')) {
+ assert_greater_than(entry.startTime, loadObserver.finishTime - epsilon,
+ `${entry.name} should occur after loading render-blocking resources`);
+ }
+ }, 'Rendering is blocked before render-blocking resources are loaded');
+
+ promise_test(test => {
+ return loadObserver.load.then(() => finalTest(test));
+ }, finalTestTitle);
+}
diff --git a/testing/web-platform/tests/html/dom/render-blocking/support/utils.js b/testing/web-platform/tests/html/dom/render-blocking/support/utils.js
new file mode 100644
index 0000000000..8a9a537e96
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/support/utils.js
@@ -0,0 +1,5 @@
+function generateParserDelay(seconds = 1) {
+ document.write(`
+ <script src="/loading/resources/dummy.js?pipe=trickle(d${seconds})"></script>
+ `);
+}