diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:22:09 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:22:09 +0000 |
commit | 43a97878ce14b72f0981164f87f2e35e14151312 (patch) | |
tree | 620249daf56c0258faa40cbdcf9cfba06de2a846 /testing/web-platform/tests/html/dom/render-blocking | |
parent | Initial commit. (diff) | |
download | firefox-43a97878ce14b72f0981164f87f2e35e14151312.tar.xz firefox-43a97878ce14b72f0981164f87f2e35e14151312.zip |
Adding upstream version 110.0.1.upstream/110.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/web-platform/tests/html/dom/render-blocking')
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); +} |