Bug 1617427 - Add a test for the scenario being fixed. r=botond

Differential Revision: https://phabricator.services.mozilla.com/D66428

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Kartikaya Gupta 2020-03-13 19:59:41 +00:00
Родитель c4913fea1d
Коммит 67bc5d7367
4 изменённых файлов: 163 добавлений и 0 удалений

Просмотреть файл

@ -198,6 +198,30 @@ function isLayerized(elementId) {
return false;
}
// Return a rect (or null) that holds the last known content-side displayport
// for a given element. (The element selection works the same way, and with
// the same assumptions as the isLayerized function above).
function getLastContentDisplayportFor(elementId) {
var contentTestData = SpecialPowers.getDOMWindowUtils(
window
).getContentAPZTestData();
var nonEmptyBucket = getLastNonemptyBucket(contentTestData.paints);
ok(nonEmptyBucket != null, "expected at least one nonempty paint");
var seqno = nonEmptyBucket.sequenceNumber;
contentTestData = convertTestData(contentTestData);
var paint = contentTestData.paints[seqno];
for (var scrollId in paint) {
if ("contentDescription" in paint[scrollId]) {
if (paint[scrollId].contentDescription.includes(elementId)) {
if ("displayport" in paint[scrollId]) {
return parseRect(paint[scrollId].displayport);
}
}
}
}
return null;
}
// Return a promise that is resolved on the next rAF callback
function waitForFrame() {
return new Promise(resolve => {

Просмотреть файл

@ -0,0 +1,104 @@
<!DOCTYPE HTML>
<html id="root-element">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width; initial-scale=1.0">
<title>Checkerboarding while root scrollframe async-scrolls and a
subframe has APZ force disabled</title>
<script type="application/javascript" src="apz_test_utils.js"></script>
<script type="application/javascript" src="apz_test_native_event_utils.js"></script>
<script src="/tests/SimpleTest/paint_listener.js"></script>
<script type="application/javascript">
function* test(testDriver) {
var utils = SpecialPowers.getDOMWindowUtils(window);
var subframe = document.getElementById('subframe');
// layerize subframe
yield synthesizeNativeClick(subframe, 10, 10, testDriver);
// verify layerization
yield waitForAllPaints(() => setTimeout(testDriver, 0));
ok(isLayerized("subframe"), "subframe should be layerized at this point");
var subframeScrollId = utils.getViewId(subframe);
ok(subframeScrollId > 0, "subframe should have a scroll id");
// then disable APZ for it
utils.disableApzForElement(subframe);
// wait for the dust to settle
yield waitForAllPaints(() => setTimeout(testDriver, 0));
// Check that the root element's displayport has at least 500px of vertical
// displayport margin on either side. This will ensure that we can scroll
// by 500px without causing the displayport to move, which in turn means that
// the scroll will not trigger repaints (due to paint-skipping).
var rootElem = document.documentElement;
var rootDisplayport = getLastContentDisplayportFor(rootElem.id);
ok(rootDisplayport != null, "root element should have a displayport");
dump("root dp: " + JSON.stringify(rootDisplayport) +
", height: " + rootElem.clientHeight);
var rootDpVerticalMargin = (rootDisplayport.h - rootElem.clientHeight) / 2;
ok(rootDpVerticalMargin > 500,
"root element should have at least 500px of vertical displayport margin");
// Scroll enough that we reveal new parts of the subframe, but not so much
// that the root displayport starts moving. If the root displayport moves,
// the main-thread will trigger a repaint of the subframe, but if the root
// displayport doesn't move, we get a paint-skipped scroll which is where the
// bug manifests. (The bug being that the subframe ends in a visual perma-
// checkerboarding state). Note that we do an 'auto' behavior scroll so
// that it's "instant" rather than an animation. Animations would demonstrate
// the bug too but are more complicated to wait for.
window.scrollBy({top: 500, left: 0, behavior: 'auto'});
is(window.scrollY, 500, "document got scrolled instantly");
// Note that at this point we must NOT call flushApzRepaints, because
// otherwise APZCCallbackHelper::NotifyFlushComplete will trigger a repaint
// (for unrelated reasons), and the repaint will clear the checkerboard
// state. We do, however, want to wait for a "steady state" here that
// includes all pending paints from the main thread and a composite that
// samples the APZ state. (The latter is not required if the APZ frame delay
// is disabled, but it's enabled by default so it's desirable to handle that
// situation as well.) In order to accomplish this we wait for all the main
// thread paints, and then force a composite via advanceTimeAndRefresh. The
// advanceTimeAndRefresh has the additional advantage of freezing the refresh
// driver which avoids any additional externally-triggered repaints from
// erasing the symptoms of the bug.
yield waitForAllPaints(() => setTimeout(testDriver, 0));
utils.advanceTimeAndRefresh(0);
var data = utils.getCompositorAPZTestData();
//dump(JSON.stringify(data, null, 4));
var found = false;
for (apzcData of data.additionalData) {
if (apzcData.key == subframeScrollId) {
var checkerboarding = apzcData.value.split(',').includes("checkerboarding");
ok(!checkerboarding, "subframe is not checkerboarding");
found = true;
}
}
ok(found, "Found the checkerboarding subframe");
utils.restoreNormalRefresh();
subtestDone();
}
waitUntilApzStable().then(runContinuation(test));
</script>
<style>
#subframe {
overflow-x: auto;
margin-left: 100px; /* makes APZ minimap easier to see */
}
</style>
</head>
<body>
<div id="subframe">
<div style="width: 10000px; height: 10000px; background-image: linear-gradient(green, red)">
</div>
</div>
</body>
</html>

Просмотреть файл

@ -68,3 +68,4 @@
[test_group_zoomToFocusedInput.html]
[test_group_scroll_snap.html]
skip-if = (os == 'android') # wheel events not supported on mobile
[test_group_checkerboarding.html]

Просмотреть файл

@ -0,0 +1,34 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="apz_test_utils.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="application/javascript">
if (isApzEnabled()) {
SimpleTest.waitForExplicitFinish();
var prefs = [
["apz.test.logging_enabled", true],
["apz.paint_skipping.enabled", true],
["apz.displayport_expiry_ms", 0],
["general.smoothScroll", false],
["apz.minimap.enabled", true], // helps to debug these tests
];
const subtests = [
{ file: "helper_checkerboard_apzforcedisabled.html", prefs },
];
// Run the actual test in its own window, because it requires that the
// root APZC be scrollable. Mochitest pages themselves often run
// inside an iframe which means we have no control over the root APZC.
window.onload = () => {
runSubtestsSeriallyInFreshWindows(subtests)
.then(SimpleTest.finish, SimpleTest.finish);
};
}
</script>
</head>
<body>
</body>
</html>