diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /gfx/layers/apz/test/mochitest/test_layerization.html | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'gfx/layers/apz/test/mochitest/test_layerization.html')
-rw-r--r-- | gfx/layers/apz/test/mochitest/test_layerization.html | 312 |
1 files changed, 312 insertions, 0 deletions
diff --git a/gfx/layers/apz/test/mochitest/test_layerization.html b/gfx/layers/apz/test/mochitest/test_layerization.html new file mode 100644 index 0000000000..0ff76de317 --- /dev/null +++ b/gfx/layers/apz/test/mochitest/test_layerization.html @@ -0,0 +1,312 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1173580 +--> +<head> + <title>Test for layerization</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <script src="/tests/SimpleTest/EventUtils.js"></script> + <script src="/tests/SimpleTest/paint_listener.js"></script> + <script type="application/javascript" src="apz_test_native_event_utils.js"></script> + <script type="application/javascript" src="apz_test_utils.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <link rel="stylesheet" type="text/css" href="helper_subframe_style.css"/> + <style> + #container { + display: flex; + overflow: scroll; + height: 500px; + } + .outer-frame { + height: 500px; + overflow: scroll; + flex-basis: 100%; + background: repeating-linear-gradient(#CCC, #CCC 100px, #BBB 100px, #BBB 200px); + } + #container-content { + height: 200%; + } + </style> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1173580">APZ layerization tests</a> +<p id="display"></p> +<div id="container"> + <div id="outer1" class="outer-frame"> + <div id="inner1" class="inner-frame"> + <div class="inner-content"></div> + </div> + </div> + <div id="outer2" class="outer-frame"> + <div id="inner2" class="inner-frame"> + <div class="inner-content"></div> + </div> + </div> + <iframe id="outer3" class="outer-frame" src="helper_iframe1.html"></iframe> + <iframe id="outer4" class="outer-frame" src="helper_iframe2.html"></iframe> +<!-- The container-content div ensures 'container' is scrollable, so the + optimization that layerizes the primary async-scrollable frame on page + load layerizes it rather than its child subframes. --> + <div id="container-content"></div> +</div> +<pre id="test"> +<script type="application/javascript"> + +// Scroll the mouse wheel over |element|. +async function scrollWheelOver(element) { + await promiseMoveMouseAndScrollWheelOver(element, 10, 10, /* waitForScroll = */ false); +} + +const DISPLAYPORT_EXPIRY = 100; + +let config = getHitTestConfig(); +let activateAllScrollFrames = config.activateAllScrollFrames; + +let heightMultiplier = SpecialPowers.getCharPref("apz.y_stationary_size_multiplier"); +// With WebRender, the effective height multiplier can be reduced +// for alignment reasons. The reduction should be no more than a +// factor of two. +heightMultiplier /= 2; +info("effective displayport height multipler is " + heightMultiplier); + +function hasNonZeroMarginDisplayPort(elementId, containingDoc = null) { + let dp = getLastContentDisplayportFor(elementId); + if (dp == null) { + return false; + } + let elt = (containingDoc != null ? containingDoc : document).getElementById(elementId); + info(elementId); + info("window size " + window.innerWidth + " " + window.innerHeight); + info("dp " + dp.x + " " + dp.y + " " + dp.width + " " + dp.height); + info("eltsize " + elt.clientWidth + " " + elt.clientHeight); + return dp.height >= heightMultiplier * Math.min(elt.clientHeight, window.innerHeight); +} + +function hasMinimalDisplayPort(elementId, containingDoc = null) { + let dp = getLastContentDisplayportFor(elementId); + if (dp == null) { + return false; + } + let elt = (containingDoc != null ? containingDoc : document).getElementById(elementId); + info(elementId); + info("dp " + dp.x + " " + dp.y + " " + dp.width + " " + dp.height); + info("eltsize " + elt.clientWidth + " " + elt.clientHeight); + return dp.width <= (elt.clientWidth + 2) && dp.height <= (elt.clientHeight + 2); +} + +function checkDirectActivation(elementId, containingDoc = null) { + if (activateAllScrollFrames) { + return hasNonZeroMarginDisplayPort(elementId, containingDoc); + } + return isLayerized(elementId); + +} + +function checkAncestorActivation(elementId, containingDoc = null) { + if (activateAllScrollFrames) { + return hasMinimalDisplayPort(elementId, containingDoc); + } + return isLayerized(elementId); + +} + +function checkInactive(elementId, containingDoc = null) { + if (activateAllScrollFrames) { + return hasMinimalDisplayPort(elementId, containingDoc); + } + return !isLayerized(elementId); + +} + +async function test() { + await SpecialPowers.pushPrefEnv({ + "set": [ + // Causes the test to intermittently fail on ASAN opt linux. + ["mousewheel.system_scroll_override.enabled", false], + ] + }); + + let outer3Doc = document.getElementById("outer3").contentDocument; + let outer4Doc = document.getElementById("outer4").contentDocument; + + // Initially, everything should be inactive. + ok(checkInactive("outer1"), "initially 'outer1' should not be active"); + ok(checkInactive("inner1"), "initially 'inner1' should not be active"); + ok(checkInactive("outer2"), "initially 'outer2' should not be active"); + ok(checkInactive("inner2"), "initially 'inner2' should not be active"); + ok(checkInactive("outer3"), "initially 'outer3' should not be active"); + ok(checkInactive("inner3", outer3Doc), + "initially 'inner3' should not be active"); + ok(checkInactive("outer4"), "initially 'outer4' should not be active"); + ok(checkInactive("inner4", outer4Doc), + "initially 'inner4' should not be active"); + + // Scrolling over outer1 should activate outer1 directly, but not inner1. + await scrollWheelOver(document.getElementById("outer1")); + await promiseAllPaintsDone(); + await promiseOnlyApzControllerFlushed(); + ok(checkDirectActivation("outer1"), + "scrolling 'outer1' should activate it directly"); + ok(checkInactive("inner1"), + "scrolling 'outer1' should not cause 'inner1' to get activated"); + + // Scrolling over inner2 should activate inner2 directly, but outer2 only ancestrally. + await scrollWheelOver(document.getElementById("inner2")); + await promiseAllPaintsDone(); + await promiseOnlyApzControllerFlushed(); + ok(checkDirectActivation("inner2"), + "scrolling 'inner2' should cause it to be directly activated"); + ok(checkAncestorActivation("outer2"), + "scrolling 'inner2' should cause 'outer2' to be activated as an ancestor"); + + // The second half of the test repeats the same checks as the first half, + // but with an iframe as the outer scrollable frame. + + // Scrolling over outer3 should activate outer3 directly, but not inner3. + await scrollWheelOver(outer3Doc.documentElement); + await promiseAllPaintsDone(); + await promiseOnlyApzControllerFlushed(); + ok(checkDirectActivation("outer3"), "scrolling 'outer3' should cause it to be directly activated"); + ok(checkInactive("inner3", outer3Doc), + "scrolling 'outer3' should not cause 'inner3' to be activated"); + + // Scrolling over inner4 should activate inner4 directly, but outer4 only ancestrally. + await scrollWheelOver(outer4Doc.getElementById("inner4")); + await promiseAllPaintsDone(); + await promiseOnlyApzControllerFlushed(); + ok(checkDirectActivation("inner4", outer4Doc), + "scrolling 'inner4' should cause it to be directly activated"); + ok(checkAncestorActivation("outer4"), + "scrolling 'inner4' should cause 'outer4' to be activated"); + + // Now we enable displayport expiry, and verify that things are still + // activated as they were before. + await SpecialPowers.pushPrefEnv({"set": [["apz.displayport_expiry_ms", DISPLAYPORT_EXPIRY]]}); + ok(checkDirectActivation("outer1"), "outer1 still has non zero display port after enabling expiry"); + ok(checkInactive("inner1"), "inner1 is still has zero margin display port after enabling expiry"); + ok(checkAncestorActivation("outer2"), "outer2 still has zero margin display port after enabling expiry"); + ok(checkDirectActivation("inner2"), "inner2 still has non zero display port after enabling expiry"); + ok(checkDirectActivation("outer3"), "outer3 still has non zero display port after enabling expiry"); + ok(checkInactive("inner3", outer3Doc), + "inner3 still has zero margin display port after enabling expiry"); + ok(checkDirectActivation("inner4", outer4Doc), + "inner4 still has non zero display port after enabling expiry"); + ok(checkAncestorActivation("outer4"), "outer4 still has zero margin display port after enabling expiry"); + + // Now we trigger a scroll on some of the things still layerized, so that + // the displayport expiry gets triggered. + + // Expire displayport with scrolling on outer1 + await scrollWheelOver(document.getElementById("outer1")); + await promiseAllPaintsDone(); + await promiseOnlyApzControllerFlushed(); + await SpecialPowers.promiseTimeout(DISPLAYPORT_EXPIRY); + await promiseAllPaintsDone(); + ok(checkInactive("outer1"), "outer1 is inactive after displayport expiry"); + ok(checkInactive("inner1"), "inner1 is inactive after displayport expiry"); + + // Expire displayport with scrolling on inner2 + await scrollWheelOver(document.getElementById("inner2")); + await promiseAllPaintsDone(); + await promiseOnlyApzControllerFlushed(); + // Once the expiry elapses, it will trigger expiry on outer2, so we check + // both, one at a time. + await SpecialPowers.promiseTimeout(DISPLAYPORT_EXPIRY); + await promiseAllPaintsDone(); + ok(checkInactive("inner2"), "inner2 is inactive after displayport expiry"); + await SpecialPowers.promiseTimeout(DISPLAYPORT_EXPIRY); + await promiseAllPaintsDone(); + ok(checkInactive("outer2"), "outer2 is inactive with inner2"); + + // We need to wrap the next bit in a loop and keep retrying until it + // succeeds. Let me explain why this is the best option at this time. Below + // we scroll over inner3, this triggers a 100 ms timer to expire it's display + // port. Then when it expires it schedules a paint and triggers another + // 100 ms timer on it's parent, outer3, to expire. The paint needs to happen + // before the timer fires because the paint is what updates + // mIsParentToActiveScrollFrames on outer3, and mIsParentToActiveScrollFrames + // being true blocks a display port from expiring. It was true because it + // contained inner3, but no longer. In real life the timer is 15000 ms so a + // paint will happen, but here in a test the timer is 100 ms so that paint + // can not happen in time. We could add some more complication to this code + // just for this test, or we could just loop here. + let itWorked = false; + while (!itWorked) { + // Scroll on inner3. inner3 isn't layerized, and this will cause it to + // get layerized, but it will also trigger displayport expiration for inner3 + // which will eventually trigger displayport expiration on inner3 and outer3. + // Note that the displayport expiration might actually happen before the wheel + // input is processed in the compositor (see bug 1246480 comment 3), and so + // we make sure not to wait for a scroll event here, since it may never fire. + // However, if we do get a scroll event while waiting for the expiry, we need + // to restart the expiry timer because the displayport expiry got reset. There's + // no good way that I can think of to deterministically avoid doing this. + let inner3 = outer3Doc.getElementById("inner3"); + await scrollWheelOver(inner3); + await promiseAllPaintsDone(); + await promiseOnlyApzControllerFlushed(); + let timerPromise = new Promise(resolve => { + var timeoutTarget = function() { + inner3.removeEventListener("scroll", timeoutResetter); + resolve(); + }; + var timerId = setTimeout(timeoutTarget, DISPLAYPORT_EXPIRY); + var timeoutResetter = function() { + ok(true, "Got a scroll event; resetting timer..."); + clearTimeout(timerId); + setTimeout(timeoutTarget, DISPLAYPORT_EXPIRY); + // by not updating timerId we ensure that this listener resets the timeout + // at most once. + }; + inner3.addEventListener("scroll", timeoutResetter); + }); + await timerPromise; // wait for the setTimeout to elapse + + await promiseAllPaintsDone(); + ok(checkInactive("inner3", outer3Doc), + "inner3 is inactive after expiry"); + await SpecialPowers.promiseTimeout(DISPLAYPORT_EXPIRY); + await promiseAllPaintsDone(); + if (checkInactive("outer3")) { + ok(true, "outer3 is inactive after inner3 triggered expiry"); + itWorked = true; + } + } + + // Scroll outer4 and wait for the expiry. It should NOT get expired because + // inner4 is still layerized + await scrollWheelOver(outer4Doc.documentElement); + await promiseAllPaintsDone(); + await promiseOnlyApzControllerFlushed(); + // Wait for the expiry to elapse + await SpecialPowers.promiseTimeout(DISPLAYPORT_EXPIRY); + await promiseAllPaintsDone(); + ok(checkDirectActivation("inner4", outer4Doc), + "inner4 still is directly activated because it never expired"); + ok(checkDirectActivation("outer4"), + "outer4 still still is directly activated because inner4 is still layerized"); +} + +if (isApzEnabled()) { + SimpleTest.waitForExplicitFinish(); + SimpleTest.requestFlakyTimeout("we are testing code that measures an actual timeout"); + SimpleTest.expectAssertions(0, 8); // we get a bunch of "ASSERTION: Bounds computation mismatch" sometimes (bug 1232856) + + // Disable smooth scrolling, because it results in long-running scroll + // animations that can result in a 'scroll' event triggered by an earlier + // wheel event as corresponding to a later wheel event. + // Also enable APZ test logging, since we use that data to determine whether + // a scroll frame was layerized. + pushPrefs([["general.smoothScroll", false], + ["apz.displayport_expiry_ms", 0], + ["apz.test.logging_enabled", true]]) + .then(waitUntilApzStable) + .then(test) + .then(SimpleTest.finish, SimpleTest.finishWithFailure); +} + +</script> +</pre> +</body> +</html> |