summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/html/dom/render-blocking/support/test-render-blocking.js
diff options
context:
space:
mode:
Diffstat (limited to 'testing/web-platform/tests/html/dom/render-blocking/support/test-render-blocking.js')
-rw-r--r--testing/web-platform/tests/html/dom/render-blocking/support/test-render-blocking.js118
1 files changed, 118 insertions, 0 deletions
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);
+}