mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-01 14:45:29 +00:00
ebb461c874
We want the maximum scroll position to be aligned with layer pixels. That way we don't have to re-rasterize the scrolled contents once scrolling hits the edge of the scrollable area. Here's how we determine the maximum scroll position: We get the scroll port rect, snapped to layer pixels. Then we get the scrolled rect and also snap that to layer pixels. The maximum scroll position is set to the difference between right/bottom edges of these rectangles. Now the scrollable area is computed by adding this maximum scroll position to the unsnapped scroll port size. The underlying idea here is: Pretend we have overflow:visible so that the scrolled contents start at (0, 0) relative to the scroll port and spill over the scroll port edges. When these contents are rendered, their rendering is snapped to layer pixels. We want those exact pixels to be accessible by scrolling. This way of computing the snapped scrollable area ensures that, if you scroll to the maximum scroll position, the right/bottom edges of the rendered scrolled contents line up exactly with the right/bottom edges of the scroll port. The scrolled contents are neither cut off nor are they moved too far. (This is something that no other browser engine gets completely right, see the testcase in bug 1012752.) There are also a few disadvantages to this solution. We snap to layer pixels, and the size of a layer pixel can depend on the zoom level, the document resolution, the current screen's scale factor, and CSS transforms. The snap origin is the position of the reference frame. So a change to any of these things can influence the scrollable area and the maximum scroll position. This patch does not make us adjust the current scroll position in the event that the maximum scroll position changes such that the current scroll position would be out of range, unless there's a reflow of the scrolled contents. This means that we can sometimes render a slightly inconsistent state where the current scroll position exceeds the maximum scroll position. We can fix this once it turns out to be a problem; I doubt that it will be a problem because none of the other browsers seems to prevent this problem either. The size of the scrollable area is exposed through the DOM properties scrollWidth and scrollHeight. At the moment, these are integer properties, so their value is rounded to the nearest CSS pixel. Before this patch, the returned value would always be within 0.5 CSS pixels of the value that layout computed for the content's scrollable overflow based on the CSS styles of the contents. Now that scrollWidth and scrollHeight also depend on pixel snapping, their values can deviate by up to one layer pixel from what the page might expect based on the styles of the contents. This change requires a few changes to existing tests. The fact that scrollWidth and scrollHeight can change based on the position of the scrollable element and the zoom level / resolution may surprise some web pages. However, this also seems to happen in Edge. Edge seems to always round scrollWidth and scrollHeight upwards, possibly to their equivalent of layout device pixels. MozReview-Commit-ID: 3LFV7Lio4tG --HG-- extra : rebase_source : 3e4e0b60493397e61283aa1d7fd93d7c197dec29 extra : source : d43c2d5e87f31ff47d7f3ada66c3f5f27cef84a9
70 lines
2.2 KiB
HTML
70 lines
2.2 KiB
HTML
<!DOCTYPE HTML>
|
|
<html>
|
|
<head>
|
|
<title>Test bug 784410</title>
|
|
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
|
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
|
|
<script src="/tests/SimpleTest/paint_listener.js"></script>
|
|
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
|
</head>
|
|
<body>
|
|
<p id="display"></p>
|
|
<div id="outer" style="overflow:auto; height:200px; border:2px dotted black; transform: translateY(1px)" onscroll="doneScroll()">
|
|
<div id="d" style="overflow:auto; height:102px;" onscroll="doneScroll()">
|
|
<div id="inner" style="height:100.1px; border:1px solid black; background:yellow;">Hello</div>
|
|
</div>
|
|
<div style="height:500px;"></div>
|
|
</div>
|
|
<pre id="test">
|
|
<script class="testbody" type="text/javascript;version=1.7">
|
|
var sel = window.getSelection();
|
|
var outer = document.getElementById("outer");
|
|
var d = document.getElementById("d");
|
|
var inner = document.getElementById("inner");
|
|
var smoothScrollPref = "general.smoothScroll";
|
|
|
|
function innerScrollOffset() {
|
|
return inner.getBoundingClientRect().top - d.getBoundingClientRect().top;
|
|
}
|
|
var innerStartScrollOffset = innerScrollOffset();
|
|
|
|
var step = 0;
|
|
function doneScroll() {
|
|
++step;
|
|
switch (step) {
|
|
case 1:
|
|
is(innerScrollOffset(), innerStartScrollOffset, "Inner element should not have scrolled down");
|
|
ok(outer.scrollTop > 0, "Outer element should have scrolled down");
|
|
|
|
outer.scrollTop = 0;
|
|
break;
|
|
case 2:
|
|
// Wait for paints to flush, so APZ is notified of the new scroll offset.
|
|
sendWheelAndPaint(inner, 4, 4,
|
|
{ deltaMode: WheelEvent.DOM_DELTA_LINE, deltaY: 1 },
|
|
function() {});
|
|
break;
|
|
case 3:
|
|
is(innerScrollOffset(), innerStartScrollOffset, "Inner element should not have scrolled down");
|
|
ok(outer.scrollTop > 0, "Outer element should have scrolled down");
|
|
|
|
SpecialPowers.DOMWindowUtils.restoreNormalRefresh();
|
|
SimpleTest.finish();
|
|
break;
|
|
}
|
|
}
|
|
|
|
function test() {
|
|
sel.collapse(inner.firstChild, 2);
|
|
synthesizeKey("VK_PAGE_DOWN", {});
|
|
}
|
|
|
|
SimpleTest.waitForExplicitFinish();
|
|
SimpleTest.waitForFocus(function() {
|
|
SpecialPowers.pushPrefEnv({"set":[[smoothScrollPref, false]]}, test);
|
|
});
|
|
</script>
|
|
</pre>
|
|
</body>
|
|
</html>
|