summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/html/dom/render-blocking
diff options
context:
space:
mode:
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.tentative.html44
-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.tentative.html19
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/parser-inserted-async-script.tentative.html19
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/parser-inserted-defer-script.tentative.html19
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/parser-inserted-module-script.tentative.html19
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/parser-inserted-style-element.tentative.html20
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/parser-inserted-stylesheet-link.tentative.html18
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/remove-attr-script-keeps-blocking.tentative.html25
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/remove-attr-style-keeps-blocking.tentative.html28
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/remove-attr-stylesheet-link-keeps-blocking.tentative.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-module-script.tentative.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.tentative.html26
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/script-inserted-stylesheet-link.tentative.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
22 files changed, 706 insertions, 0 deletions
diff --git a/testing/web-platform/tests/html/dom/render-blocking/blocking-idl-attr.tentative.html b/testing/web-platform/tests/html/dom/render-blocking/blocking-idl-attr.tentative.html
new file mode 100644
index 0000000000..c33b411eb4
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/blocking-idl-attr.tentative.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/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.tentative.html b/testing/web-platform/tests/html/dom/render-blocking/parser-blocking-script.tentative.html
new file mode 100644
index 0000000000..8d391144b2
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/parser-blocking-script.tentative.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-script.tentative.html b/testing/web-platform/tests/html/dom/render-blocking/parser-inserted-async-script.tentative.html
new file mode 100644
index 0000000000..4b2216dfcb
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/parser-inserted-async-script.tentative.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.tentative.html b/testing/web-platform/tests/html/dom/render-blocking/parser-inserted-defer-script.tentative.html
new file mode 100644
index 0000000000..1ae8caf2ec
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/parser-inserted-defer-script.tentative.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-module-script.tentative.html b/testing/web-platform/tests/html/dom/render-blocking/parser-inserted-module-script.tentative.html
new file mode 100644
index 0000000000..2bca88e73c
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/parser-inserted-module-script.tentative.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.tentative.html b/testing/web-platform/tests/html/dom/render-blocking/parser-inserted-style-element.tentative.html
new file mode 100644
index 0000000000..9a358aa493
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/parser-inserted-style-element.tentative.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.tentative.html b/testing/web-platform/tests/html/dom/render-blocking/parser-inserted-stylesheet-link.tentative.html
new file mode 100644
index 0000000000..0a771448fd
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/parser-inserted-stylesheet-link.tentative.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.tentative.html b/testing/web-platform/tests/html/dom/render-blocking/remove-attr-script-keeps-blocking.tentative.html
new file mode 100644
index 0000000000..451d2f3695
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/remove-attr-script-keeps-blocking.tentative.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.tentative.html b/testing/web-platform/tests/html/dom/render-blocking/remove-attr-style-keeps-blocking.tentative.html
new file mode 100644
index 0000000000..31d4b56838
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/remove-attr-style-keeps-blocking.tentative.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.tentative.html b/testing/web-platform/tests/html/dom/render-blocking/remove-attr-stylesheet-link-keeps-blocking.tentative.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.tentative.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-module-script.tentative.html b/testing/web-platform/tests/html/dom/render-blocking/script-inserted-module-script.tentative.html
new file mode 100644
index 0000000000..73f0d3cdf4
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/script-inserted-module-script.tentative.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.tentative.html b/testing/web-platform/tests/html/dom/render-blocking/script-inserted-style-element.tentative.html
new file mode 100644
index 0000000000..683706af50
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/script-inserted-style-element.tentative.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.tentative.html b/testing/web-platform/tests/html/dom/render-blocking/script-inserted-stylesheet-link.tentative.html
new file mode 100644
index 0000000000..46755387d7
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/render-blocking/script-inserted-stylesheet-link.tentative.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);
+}