| <!doctype html> |
| <title>ResizeObserver notification event loop tests</title> |
| <script src="/resources/testharness.js"></script> |
| <script src="/resources/testharnessreport.js"></script> |
| <script src="./resources/resizeTestHelper.js"></script> |
| <div id="log"></div> |
| <script> |
| 'use strict'; |
| |
| // allow uncaught exception because ResizeObserver posts exceptions |
| // to window error handler when limit is exceeded. |
| // This codepath is tested in this file. |
| |
| setup({allow_uncaught_exception: true}); |
| |
| function template() { |
| let helper = new ResizeTestHelper( |
| "test0: title", |
| [ |
| { |
| setup: observer => { |
| }, |
| notify: (entries, observer) => { |
| return true; // Delay next step |
| } |
| } |
| ]); |
| return helper.start(); |
| } |
| |
| var onErrorCalled = false; |
| |
| window.onerror = err => { |
| onErrorCalled = true; |
| } |
| |
| function test0() { |
| let t1 = createAndAppendElement("div"); |
| let t2 = createAndAppendElement("div", t1); |
| let t3 = createAndAppendElement("div", t2); |
| |
| let divs = [t1, t2, t3]; |
| let rAF = 0; |
| let helper = new ResizeTestHelper( |
| "test0: multiple notifications inside same event loop", |
| [ |
| { |
| setup: observer => { |
| onErrorCalled = false; |
| observer.observe(t1); |
| observer.observe(t2); |
| observer.observe(t3); |
| }, |
| notify: (entries, observer) => { |
| assert_equals(entries.length, 3, "3 notifications"); |
| } |
| }, |
| { |
| setup: observer => { |
| helper.startCountingRaf(); |
| divs.forEach( el => { el.style.width = "101px";}); |
| }, |
| notify: (entries, observer) => { |
| // t1 is not delivered |
| assert_equals(entries.length, 2, "2 notifications"); |
| assert_equals(helper.rafCount, 0, "still in same loop"); |
| } |
| }, |
| { |
| setup: observer => { |
| divs.forEach( el => { el.style.width = "102px";}); |
| }, |
| notify: (entries, observer) => { |
| assert_equals(entries.length, 1, "1 notifications"); |
| assert_equals(helper.rafCount, 0, "same loop"); |
| } |
| }, |
| { // t1 and t2 get notified |
| setup: observer => { |
| }, |
| notify: (entries, observer) => { |
| assert_equals(entries.length, 2, "2 notifications"); |
| assert_equals(helper.rafCount, 1, "new loop"); |
| assert_equals(onErrorCalled, true, "error was fired"); |
| observer.disconnect(); |
| } |
| } |
| ]); |
| |
| return new Promise((resolve, reject) => { |
| // This test uses requestAnimationFrame() to check the count of event loop, |
| // but on some browsers, the FrameRequestCallback may be throttled (i.e. |
| // simply fired after some extra time) in cases where this test is running |
| // in an iframe that hasn't yet been painted (i.e. we're not visible). |
| // This may result in some intermittent failures if this test didn't get a |
| // first paint (and hence may not have started firing FrameRequestCallbacks) |
| // by the time the test starts expecting helper.rafCount to have changed. |
| // |
| // Therefore, we don't start the test logic until body.onload has fired. |
| // This increases the likelihood that this testcase will have gotten a |
| // chance to paint when we start invoking requestAnimationFrame, and that |
| // its rAF callbacks will fire when the test logic expects them to. |
| document.body.onload = () => resolve(); |
| }).then(() => { |
| return helper.start(() => t1.remove()); |
| }); |
| } |
| |
| function test1() { |
| let t1 = createAndAppendElement("div"); |
| t1.style.width = '100px'; |
| let t2 = createAndAppendElement("div", t1); |
| let t3 = createAndAppendElement("div", t2); |
| let shadow = t3.attachShadow({ mode: "open" }); |
| let t4 = createAndAppendElement("div", shadow); |
| let t5 = createAndAppendElement("div", t4); |
| |
| let resizers = [t1, t2, t3, t4, t5]; |
| |
| // Testing depths of shadow roots |
| // DOM: t1 <- t2 <- t3 <-shadow- t4 <- t5 |
| let helper = new ResizeTestHelper( |
| "test1: depths of shadow roots", |
| [ |
| { |
| setup: observer => { |
| onErrorCalled = false; |
| resizers.forEach( el => observer.observe(el) ); |
| }, |
| notify: (entries, observer) => { |
| assert_equals(entries.length, 5, "all entries resized"); |
| } |
| }, |
| { |
| setup: observer => { |
| resizers.forEach( el => el.style.width = "111px" ); |
| }, |
| notify: (entries, observer) => { |
| assert_equals(entries.length, 4, "depth limited"); |
| } |
| }, |
| { |
| setup: observer => { |
| resizers.forEach( el => el.style.width = "112px" ); |
| }, |
| notify: (entries, observer) => { |
| assert_equals(entries.length, 3, "depth limited"); |
| } |
| }, |
| { |
| setup: observer => { |
| resizers.forEach( el => el.style.width = "113px" ); |
| }, |
| notify: (entries, observer) => { |
| assert_equals(entries.length, 2, "depth limited"); |
| } |
| }, |
| { |
| setup: observer => { |
| resizers.forEach( el => el.style.width = "114px" ); |
| }, |
| notify: (entries, observer) => { |
| assert_equals(entries.length, 1, "depth limited"); |
| } |
| }, |
| { |
| setup: observer => { |
| }, |
| notify: (entries, observer) => { |
| assert_equals(entries.length, 4, "limit notifications"); |
| assert_equals(onErrorCalled, true, "breached limit"); |
| observer.disconnect(); |
| } |
| }, |
| ]); |
| return helper.start(() => t1.remove()); |
| } |
| |
| function test2() { |
| // <div id="container"> |
| // <div id="a1" style="width:100px;height:100px"> |
| // <div id="a2" style="width:100px;height:100px"></div> |
| // </div> |
| // <div id="b1" style="width:100px;height:100px"> |
| // <div id="b2" style="width:100px;height:100px"></div> |
| // </div> |
| // </div> |
| let container = createAndAppendElement("div"); |
| let a1 = createAndAppendElement("div", container); |
| let a2 = createAndAppendElement("div", a1); |
| let b1 = createAndAppendElement("div", container); |
| let b2 = createAndAppendElement("div", b1); |
| let targets = [a1, a2, b1, b2]; |
| |
| let helper = new ResizeTestHelper( |
| "test2: move target in dom while inside event loop", |
| [ |
| { |
| setup: observer => { |
| for (let t of targets) |
| observer.observe(t); |
| }, |
| notify: (entries, observer) => { |
| return true; // delay next observation |
| } |
| }, |
| { // resize them all |
| setup: observer => { |
| for (let t of targets) |
| t.style.width = "110px"; |
| }, |
| notify: (entries, observer) => { |
| assert_equals(entries.length, targets.length, "all targets observed"); |
| } |
| }, |
| { // resize all, move dom upwards |
| setup: observer => { |
| for (let t of targets) |
| t.style.width = "130px"; |
| container.appendChild(b2); |
| }, |
| notify: (entries, observer) => { |
| assert_equals(entries.length, 1, "b2 moved upwards"); |
| assert_equals(entries[0].target, a2); |
| } |
| }, |
| { // resize all, move dom downwards |
| setup: observer => { |
| for (let t of targets) |
| t.style.width = "130px"; |
| a2.appendChild(b2); |
| }, |
| notify: (entries, observer) => { |
| assert_equals(entries.length, 1, "b2 moved downwards"); |
| assert_equals(entries[0].target, b2); |
| } |
| }, |
| ]); |
| return helper.start(() => container.remove()); |
| } |
| |
| let guard; |
| test(_ => { |
| assert_own_property(window, "ResizeObserver"); |
| guard = async_test('guard'); |
| }, "ResizeObserver implemented") |
| |
| test0() |
| .then(() => test1()) |
| .then(() => test2()) |
| .then(() => guard.done()); |
| |
| </script> |