diff options
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.js | 118 |
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); +} |