mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-28 15:23:51 +00:00
Bug 1012752 - Snap scrolled area to layer pixels. r=tnikkel
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
This commit is contained in:
parent
840bf3bfdb
commit
ebb461c874
@ -37,9 +37,13 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=332246
|
||||
|
||||
/** Test for Bug 332246 **/
|
||||
|
||||
function isWithFuzz(itIs, itShouldBe, fuzz, description) {
|
||||
ok(Math.abs(itIs - itShouldBe) <= fuzz, `${description} - expected a value between ${itShouldBe - fuzz} and ${itShouldBe + fuzz}, got ${itIs}`);
|
||||
}
|
||||
|
||||
var a1 = document.getElementById('a1');
|
||||
var a2 = document.getElementById('a2');
|
||||
is(a1.scrollHeight, 400, "Wrong a1.scrollHeight");
|
||||
isWithFuzz(a1.scrollHeight, 400, 1, "Wrong a1.scrollHeight");
|
||||
is(a1.offsetHeight, 100, "Wrong a1.offsetHeight");
|
||||
a2.scrollIntoView(true);
|
||||
is(a1.scrollTop, 100, "Wrong scrollTop value after a2.scrollIntoView(true)");
|
||||
@ -48,7 +52,7 @@ is(a1.scrollTop, 200, "Wrong scrollTop value after a2.scrollIntoView(false)");
|
||||
|
||||
var b1 = document.getElementById('b1');
|
||||
var b2 = document.getElementById('b2');
|
||||
is(b1.scrollHeight, 420, "Wrong b1.scrollHeight");
|
||||
isWithFuzz(b1.scrollHeight, 420, 1, "Wrong b1.scrollHeight");
|
||||
is(b1.offsetHeight, 100, "Wrong b1.offsetHeight");
|
||||
b2.scrollIntoView(true);
|
||||
is(b1.scrollTop, 100, "Wrong scrollTop value after b2.scrollIntoView(true)");
|
||||
@ -57,12 +61,12 @@ is(b1.scrollTop, 220, "Wrong scrollTop value after b2.scrollIntoView(false)");
|
||||
|
||||
var c1 = document.getElementById('c1');
|
||||
var c2 = document.getElementById('c2');
|
||||
is(c1.scrollHeight, 320, "Wrong c1.scrollHeight");
|
||||
isWithFuzz(c1.scrollHeight, 320, 1, "Wrong c1.scrollHeight");
|
||||
is(c1.offsetHeight, 100, "Wrong c1.offsetHeight");
|
||||
c2.scrollIntoView(true);
|
||||
is(c1.scrollTop, 100, "Wrong scrollTop value after c2.scrollIntoView(true)");
|
||||
c2.scrollIntoView(false);
|
||||
is(c1.scrollTop, 220, "Wrong scrollTop value after c2.scrollIntoView(false)");
|
||||
isWithFuzz(c1.scrollTop, 220, 1, "Wrong scrollTop value after c2.scrollIntoView(false)");
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
|
@ -12,8 +12,12 @@
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<!-- We set a transform on the body element so that it creates a reference frame.
|
||||
This makes sure that snapping of scrolled areas for the contained elements
|
||||
is not influenced by offsets outside of this document. -->
|
||||
<body id="body" onload="setTimeout(testElements, 0, 'testelements', SimpleTest.finish);"
|
||||
style="margin: 1px; border: 2px solid black; padding: 4px;">
|
||||
style="margin: 1px; border: 2px solid black; padding: 4px; transform: translateY(1px);">
|
||||
|
||||
<div id="testelements" style="margin: 0; border: 0; padding: 0;">
|
||||
<div id="div1" style="margin: 0; margin-left: 6px; margin-top: 2px; border: 1px solid green; padding: 6px; width: 50px; height: 20px"
|
||||
@ -55,7 +59,7 @@
|
||||
</div>
|
||||
|
||||
<div id="scrollbox"
|
||||
style="overflow: scroll; padding-left: 0px; margin: 3px; border: 4px solid green; max-width: 80px; max-height: 70px;"
|
||||
style="overflow: scroll; padding-left: 0px; margin: 3px; border: 4px solid green; max-width: 80px; max-height: 70px"
|
||||
_scrollWidth="62" _scrollHeight="32"
|
||||
_clientLeft="1" _clientTop="1" _clientWidth="62" _clientHeight="32"><p id="p1" style="margin: 0; padding: 0;">One</p>
|
||||
<p id="p2">Two</p>
|
||||
|
@ -181,12 +181,25 @@ function checkCoord(element, type, val, testname)
|
||||
is(element[type], Math.round(val), testname + " " + type);
|
||||
}
|
||||
|
||||
function checkCoordFuzzy(element, type, val, fuzz, testname)
|
||||
{
|
||||
if (val != -10000)
|
||||
ok(Math.abs(element[type] - Math.round(val)) <= fuzz, testname + " " + type);
|
||||
}
|
||||
|
||||
function checkCoords(element, type, left, top, width, height, testname)
|
||||
{
|
||||
checkCoord(element, type + "Left", left, testname);
|
||||
checkCoord(element, type + "Top", top, testname);
|
||||
checkCoord(element, type + "Width", width, testname);
|
||||
checkCoord(element, type + "Height", height, testname);
|
||||
|
||||
if (type == "scroll") {
|
||||
// scrollWidth and scrollHeight can deviate by 1 pixel due to snapping.
|
||||
checkCoordFuzzy(element, type + "Width", width, 1, testname);
|
||||
checkCoordFuzzy(element, type + "Height", height, 1, testname);
|
||||
} else {
|
||||
checkCoord(element, type + "Width", width, testname);
|
||||
checkCoord(element, type + "Height", height, testname);
|
||||
}
|
||||
|
||||
if (element instanceof SVGElement)
|
||||
return;
|
||||
|
@ -5,6 +5,7 @@
|
||||
|
||||
#include "nsRect.h"
|
||||
#include "mozilla/gfx/Types.h" // for NS_SIDE_BOTTOM, etc
|
||||
#include "mozilla/CheckedInt.h" // for CheckedInt
|
||||
#include "nsDeviceContext.h" // for nsDeviceContext
|
||||
#include "nsString.h" // for nsAutoString, etc
|
||||
#include "nsMargin.h" // for nsMargin
|
||||
@ -20,6 +21,19 @@ const mozilla::gfx::IntRect& GetMaxSizedIntRect() {
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
bool nsRect::Overflows() const {
|
||||
#ifdef NS_COORD_IS_FLOAT
|
||||
return false;
|
||||
#else
|
||||
mozilla::CheckedInt<int32_t> xMost = this->x;
|
||||
xMost += this->width;
|
||||
mozilla::CheckedInt<int32_t> yMost = this->y;
|
||||
yMost += this->height;
|
||||
return !xMost.isValid() || !yMost.isValid();
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
// Diagnostics
|
||||
|
||||
|
@ -129,6 +129,9 @@ struct nsRect :
|
||||
*this = aRect1.SaturatingUnionEdges(aRect2);
|
||||
}
|
||||
|
||||
// Return whether this rect's right or bottom edge overflow int32.
|
||||
bool Overflows() const;
|
||||
|
||||
/**
|
||||
* Return this rect scaled to a different appunits per pixel (APP) ratio.
|
||||
* In the RoundOut version we make the rect the smallest rect containing the
|
||||
|
@ -3701,7 +3701,7 @@ ContainerState::ComputeOpaqueRect(nsDisplayItem* aItem,
|
||||
}
|
||||
nsIFrame* scrollFrame = do_QueryFrame(sf);
|
||||
displayport += scrollFrame->GetOffsetToCrossDoc(mContainerReferenceFrame);
|
||||
if (opaque.Contains(displayport)) {
|
||||
if (opaquePixels.Contains(ScaleRegionToNearestPixels(displayport))) {
|
||||
*aOpaqueForAnimatedGeometryRootParent = true;
|
||||
}
|
||||
}
|
||||
|
@ -390,8 +390,8 @@ nsHTMLScrollFrame::TryLayout(ScrollReflowInput* aState,
|
||||
|
||||
if (!aForce) {
|
||||
nsRect scrolledRect =
|
||||
mHelper.GetScrolledRectInternal(aState->mContentsOverflowAreas.ScrollableOverflow(),
|
||||
scrollPortSize);
|
||||
mHelper.GetUnsnappedScrolledRectInternal(aState->mContentsOverflowAreas.ScrollableOverflow(),
|
||||
scrollPortSize);
|
||||
nscoord oneDevPixel = aState->mBoxState.PresContext()->DevPixelsToAppUnits(1);
|
||||
|
||||
// If the style is HIDDEN then we already know that aAssumeHScroll is false
|
||||
@ -686,8 +686,8 @@ nsHTMLScrollFrame::ReflowContents(ScrollReflowInput* aState,
|
||||
ComputeInsideBorderSize(aState,
|
||||
nsSize(kidDesiredSize.Width(), kidDesiredSize.Height()));
|
||||
nsRect scrolledRect =
|
||||
mHelper.GetScrolledRectInternal(kidDesiredSize.ScrollableOverflow(),
|
||||
insideBorderSize);
|
||||
mHelper.GetUnsnappedScrolledRectInternal(kidDesiredSize.ScrollableOverflow(),
|
||||
insideBorderSize);
|
||||
if (nsRect(nsPoint(0, 0), insideBorderSize).Contains(scrolledRect)) {
|
||||
// Let's pretend we had no scrollbars coming in here
|
||||
ReflowScrolledFrame(aState, false, false, &kidDesiredSize, false);
|
||||
@ -738,8 +738,8 @@ nsHTMLScrollFrame::PlaceScrollArea(const ScrollReflowInput& aState,
|
||||
// Preserve the width or height of empty rects
|
||||
nsSize portSize = mHelper.mScrollPort.Size();
|
||||
nsRect scrolledRect =
|
||||
mHelper.GetScrolledRectInternal(aState.mContentsOverflowAreas.ScrollableOverflow(),
|
||||
portSize);
|
||||
mHelper.GetUnsnappedScrolledRectInternal(aState.mContentsOverflowAreas.ScrollableOverflow(),
|
||||
portSize);
|
||||
scrolledArea.UnionRectEdges(scrolledRect,
|
||||
nsRect(nsPoint(0,0), portSize));
|
||||
|
||||
@ -3306,7 +3306,13 @@ ScrollFrameHelper::BuildDisplayList(nsDisplayListBuilder* aBuilder,
|
||||
MOZ_ASSERT(!inactiveScrollClip->mIsAsyncScrollable);
|
||||
}
|
||||
|
||||
DisplayListClipState::AutoSaveRestore displayPortClipState(aBuilder);
|
||||
// Clip our contents to the unsnapped scrolled rect. This makes sure that
|
||||
// we don't have display items over the subpixel seam at the edge of the
|
||||
// scrolled area.
|
||||
DisplayListClipState::AutoSaveRestore scrolledRectClipState(aBuilder);
|
||||
nsRect scrolledRectClip =
|
||||
GetUnsnappedScrolledRectInternal(mScrolledFrame->GetScrollableOverflowRect(),
|
||||
mScrollPort.Size()) + mScrolledFrame->GetPosition();
|
||||
if (usingDisplayPort) {
|
||||
// Clip the contents to the display port.
|
||||
// The dirty rect already acts kind of like a clip, in that
|
||||
@ -3324,8 +3330,10 @@ ScrollFrameHelper::BuildDisplayList(nsDisplayListBuilder* aBuilder,
|
||||
// pixels.
|
||||
// If there is no display port, we don't need this because the clip
|
||||
// from the scroll port is still applied.
|
||||
displayPortClipState.ClipContainingBlockDescendants(dirtyRect + aBuilder->ToReferenceFrame(mOuter));
|
||||
scrolledRectClip = scrolledRectClip.Intersect(dirtyRect);
|
||||
}
|
||||
scrolledRectClipState.ClipContainingBlockDescendants(
|
||||
scrolledRectClip + aBuilder->ToReferenceFrame(mOuter));
|
||||
|
||||
mOuter->BuildDisplayListForChild(aBuilder, mScrolledFrame, dirtyRect, scrolledContent);
|
||||
}
|
||||
@ -5566,12 +5574,19 @@ ScrollFrameHelper::GetBorderRadii(const nsSize& aFrameSize,
|
||||
return true;
|
||||
}
|
||||
|
||||
static nscoord
|
||||
SnapCoord(nscoord aCoord, double aRes, nscoord aAppUnitsPerPixel)
|
||||
{
|
||||
double snappedToLayerPixels = NS_round((aRes*aCoord)/aAppUnitsPerPixel);
|
||||
return NSToCoordRoundWithClamp(snappedToLayerPixels*aAppUnitsPerPixel/aRes);
|
||||
}
|
||||
|
||||
nsRect
|
||||
ScrollFrameHelper::GetScrolledRect() const
|
||||
{
|
||||
nsRect result =
|
||||
GetScrolledRectInternal(mScrolledFrame->GetScrollableOverflowRect(),
|
||||
mScrollPort.Size());
|
||||
GetUnsnappedScrolledRectInternal(mScrolledFrame->GetScrollableOverflowRect(),
|
||||
mScrollPort.Size());
|
||||
|
||||
if (result.width < mScrollPort.width) {
|
||||
NS_WARNING("Scrolled rect smaller than scrollport?");
|
||||
@ -5579,30 +5594,82 @@ ScrollFrameHelper::GetScrolledRect() const
|
||||
if (result.height < mScrollPort.height) {
|
||||
NS_WARNING("Scrolled rect smaller than scrollport?");
|
||||
}
|
||||
|
||||
// Expand / contract the result by up to half a layer pixel so that scrolling
|
||||
// to the right / bottom edge does not change the layer pixel alignment of
|
||||
// the scrolled contents.
|
||||
// For that, we first convert the scroll port and the scrolled rect to rects
|
||||
// relative to the reference frame, since that's the space where painting does
|
||||
// snapping.
|
||||
nsSize scrollPortSize = GetScrollPositionClampingScrollPortSize();
|
||||
nsIFrame* referenceFrame = nsLayoutUtils::GetReferenceFrame(mOuter);
|
||||
nsPoint toReferenceFrame = mOuter->GetOffsetToCrossDoc(referenceFrame);
|
||||
nsRect scrollPort(mScrollPort.TopLeft() + toReferenceFrame, scrollPortSize);
|
||||
nsRect scrolledRect = result + scrollPort.TopLeft();
|
||||
|
||||
if (scrollPort.Overflows() || scrolledRect.Overflows()) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// Now, snap the bottom right corner of both of these rects.
|
||||
// We snap to layer pixels, so we need to respect the layer's scale.
|
||||
nscoord appUnitsPerDevPixel = mScrolledFrame->PresContext()->AppUnitsPerDevPixel();
|
||||
gfxSize scale = FrameLayerBuilder::GetPaintedLayerScaleForFrame(mScrolledFrame);
|
||||
if (scale.IsEmpty()) {
|
||||
scale = gfxSize(1.0f, 1.0f);
|
||||
}
|
||||
|
||||
// Compute bounds for the scroll position, and computed the snapped scrolled
|
||||
// rect from the scroll position bounds.
|
||||
nscoord snappedScrolledAreaBottom = SnapCoord(scrolledRect.YMost(), scale.height, appUnitsPerDevPixel);
|
||||
nscoord snappedScrollPortBottom = SnapCoord(scrollPort.YMost(), scale.height, appUnitsPerDevPixel);
|
||||
nscoord maximumScrollOffsetY = snappedScrolledAreaBottom - snappedScrollPortBottom;
|
||||
result.SetBottomEdge(scrollPort.height + maximumScrollOffsetY);
|
||||
|
||||
if (GetScrolledFrameDir() == NS_STYLE_DIRECTION_LTR) {
|
||||
nscoord snappedScrolledAreaRight = SnapCoord(scrolledRect.XMost(), scale.width, appUnitsPerDevPixel);
|
||||
nscoord snappedScrollPortRight = SnapCoord(scrollPort.XMost(), scale.width, appUnitsPerDevPixel);
|
||||
nscoord maximumScrollOffsetX = snappedScrolledAreaRight - snappedScrollPortRight;
|
||||
result.SetRightEdge(scrollPort.width + maximumScrollOffsetX);
|
||||
} else {
|
||||
// In RTL, the scrolled area's right edge is at scrollPort.XMost(),
|
||||
// and the scrolled area's x position is zero or negative. We want
|
||||
// the right edge to stay flush with the scroll port, so we snap the
|
||||
// left edge.
|
||||
nscoord snappedScrolledAreaLeft = SnapCoord(scrolledRect.x, scale.width, appUnitsPerDevPixel);
|
||||
nscoord snappedScrollPortLeft = SnapCoord(scrollPort.x, scale.width, appUnitsPerDevPixel);
|
||||
nscoord minimumScrollOffsetX = snappedScrolledAreaLeft - snappedScrollPortLeft;
|
||||
result.SetLeftEdge(minimumScrollOffsetX);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
nsRect
|
||||
ScrollFrameHelper::GetScrolledRectInternal(const nsRect& aScrolledFrameOverflowArea,
|
||||
const nsSize& aScrollPortSize) const
|
||||
{
|
||||
uint8_t frameDir = IsLTR() ? NS_STYLE_DIRECTION_LTR : NS_STYLE_DIRECTION_RTL;
|
||||
|
||||
uint8_t
|
||||
ScrollFrameHelper::GetScrolledFrameDir() const
|
||||
{
|
||||
// If the scrolled frame has unicode-bidi: plaintext, the paragraph
|
||||
// direction set by the text content overrides the direction of the frame
|
||||
if (mScrolledFrame->StyleTextReset()->mUnicodeBidi &
|
||||
NS_STYLE_UNICODE_BIDI_PLAINTEXT) {
|
||||
nsIFrame* childFrame = mScrolledFrame->PrincipalChildList().FirstChild();
|
||||
if (childFrame) {
|
||||
frameDir =
|
||||
(nsBidiPresUtils::ParagraphDirection(childFrame) == NSBIDI_LTR)
|
||||
return (nsBidiPresUtils::ParagraphDirection(childFrame) == NSBIDI_LTR)
|
||||
? NS_STYLE_DIRECTION_LTR : NS_STYLE_DIRECTION_RTL;
|
||||
}
|
||||
}
|
||||
|
||||
return IsLTR() ? NS_STYLE_DIRECTION_LTR : NS_STYLE_DIRECTION_RTL;
|
||||
}
|
||||
|
||||
nsRect
|
||||
ScrollFrameHelper::GetUnsnappedScrolledRectInternal(const nsRect& aScrolledFrameOverflowArea,
|
||||
const nsSize& aScrollPortSize) const
|
||||
{
|
||||
return nsLayoutUtils::GetScrolledRect(mScrolledFrame,
|
||||
aScrolledFrameOverflowArea,
|
||||
aScrollPortSize, frameDir);
|
||||
aScrollPortSize, GetScrolledFrameDir());
|
||||
}
|
||||
|
||||
nsMargin
|
||||
|
@ -308,7 +308,7 @@ public:
|
||||
nsRect GetScrolledRect() const;
|
||||
|
||||
/**
|
||||
* GetScrolledRectInternal is designed to encapsulate deciding which
|
||||
* GetUnsnappedScrolledRectInternal is designed to encapsulate deciding which
|
||||
* directions of overflow should be reachable by scrolling and which
|
||||
* should not. Callers should NOT depend on it having any particular
|
||||
* behavior (although nsXULScrollFrame currently does).
|
||||
@ -318,8 +318,8 @@ public:
|
||||
* nsXULScrollFrames, and allows scrolling down and to the left for
|
||||
* nsHTMLScrollFrames with RTL directionality.
|
||||
*/
|
||||
nsRect GetScrolledRectInternal(const nsRect& aScrolledOverflowArea,
|
||||
const nsSize& aScrollPortSize) const;
|
||||
nsRect GetUnsnappedScrolledRectInternal(const nsRect& aScrolledOverflowArea,
|
||||
const nsSize& aScrollPortSize) const;
|
||||
|
||||
uint32_t GetScrollbarVisibility() const {
|
||||
return (mHasVerticalScrollbar ? nsIScrollableFrame::VERTICAL : 0) |
|
||||
@ -622,6 +622,7 @@ protected:
|
||||
bool HasPluginFrames();
|
||||
bool HasPerspective() const;
|
||||
bool HasBgAttachmentLocal() const;
|
||||
uint8_t GetScrolledFrameDir() const;
|
||||
|
||||
static void EnsureFrameVisPrefsCached();
|
||||
static bool sFrameVisPrefsCached;
|
||||
|
@ -9,7 +9,7 @@
|
||||
</head>
|
||||
<body>
|
||||
<p id="display"></p>
|
||||
<div id="outer" style="overflow:auto; height:200px; border:2px dotted black;" onscroll="doneScroll()">
|
||||
<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>
|
||||
|
@ -11,7 +11,7 @@
|
||||
position: absolute;
|
||||
left: 200px;
|
||||
top: 100px;
|
||||
font: 14px/1.1em "Consolas","Bitstream Vera Sans Mono","Courier New",Courier,monospace;
|
||||
font: 14px/1.3em "Consolas","Bitstream Vera Sans Mono","Courier New",Courier,monospace;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
@ -0,0 +1,59 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" reftest-async-scroll>
|
||||
<meta charset="utf-8">
|
||||
<title>Make sure the scrolled layer is not invalidated when you scroll all the way to the bottom</title>
|
||||
|
||||
<style>
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.scrollbox {
|
||||
margin: 50px;
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.scrolled-contents {
|
||||
height: 150.2px;
|
||||
padding-top: 150px;
|
||||
}
|
||||
|
||||
.reftest-no-paint {
|
||||
margin: 0 20px;
|
||||
border: 1px solid blue;
|
||||
height: 25px;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<body>
|
||||
|
||||
<div class="scrollbox"
|
||||
reftest-displayport-x="0" reftest-displayport-y="0"
|
||||
reftest-displayport-w="200" reftest-displayport-h="200"
|
||||
reftest-async-scroll-x="0" reftest-async-scroll-y="0">
|
||||
<div class="scrolled-contents">
|
||||
<div class="reftest-no-paint">
|
||||
<!-- This element has the magic "reftest-no-paint" class which
|
||||
constitutes the actual test here. -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
|
||||
var scrollbox = document.querySelector(".scrollbox");
|
||||
scrollbox.scrollTop = 2;
|
||||
scrollbox.scrollTop = 1;
|
||||
scrollbox.scrollTop = 0;
|
||||
|
||||
function doTest() {
|
||||
scrollbox.scrollTop = 999;
|
||||
document.documentElement.removeAttribute("class");
|
||||
}
|
||||
document.addEventListener("MozReftestInvalidate", doTest);
|
||||
|
||||
</script>
|
74
layout/reftests/scrolling/fractional-scroll-area.html
Normal file
74
layout/reftests/scrolling/fractional-scroll-area.html
Normal file
@ -0,0 +1,74 @@
|
||||
<!DOCTYPE html>
|
||||
<html class="reftest-wait">
|
||||
<meta charset="utf-8">
|
||||
<title>Fractional scroll area position / size</title>
|
||||
|
||||
<style>
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#scrollbox {
|
||||
width: 200px;
|
||||
overflow: hidden;
|
||||
background: red;
|
||||
}
|
||||
|
||||
#scrolled-content {
|
||||
background: lime;
|
||||
box-sizing: border-box;
|
||||
border: solid black;
|
||||
border-width: 1px 0;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<div id="scrollbox">
|
||||
<div id="scrolled-content"></div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
|
||||
function getFloatQueryParams(defaultValues) {
|
||||
let result = Object.assign({}, defaultValues);
|
||||
for (let chunk of location.search.substr(1).split("&")) {
|
||||
let parts = chunk.split("=");
|
||||
result[parts[0]] = parseFloat(parts[1]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
let params = getFloatQueryParams({
|
||||
top: 0,
|
||||
outerBottom: 100,
|
||||
innerBottom: 100,
|
||||
borderTop: 0,
|
||||
borderBottom: 0,
|
||||
scrollBefore: 0,
|
||||
scrollAfter: undefined,
|
||||
offsetAfter: undefined,
|
||||
});
|
||||
|
||||
let scrollArea = document.getElementById("scrollbox");
|
||||
let scrolledContent = document.getElementById("scrolled-content");
|
||||
|
||||
scrollArea.style.marginTop = params.top + "px";
|
||||
scrollArea.style.height = (params.outerBottom - params.top) + "px";
|
||||
scrolledContent.style.height = (params.innerBottom - params.top) + "px";
|
||||
|
||||
scrollArea.scrollTop = 1;
|
||||
scrollArea.scrollTop = 2;
|
||||
scrollArea.scrollTop = params.scrollBefore;
|
||||
|
||||
window.addEventListener("MozReftestInvalidate", function () {
|
||||
if (params.scrollAfter !== undefined) {
|
||||
scrollArea.scrollTop = params.scrollAfter;
|
||||
}
|
||||
if (params.offsetAfter !== undefined) {
|
||||
document.body.style.marginTop = params.offsetAfter + "px";
|
||||
}
|
||||
document.documentElement.className = "";
|
||||
});
|
||||
|
||||
</script>
|
@ -36,3 +36,52 @@ skip-if((B2G&&browserIsRemote)||Mulet) fuzzy-if(asyncPan&&!layersGPUAccelerated,
|
||||
== frame-scrolling-attr-1.html frame-scrolling-attr-ref.html
|
||||
fuzzy-if(asyncPan&&!layersGPUAccelerated,102,2420) == frame-scrolling-attr-2.html frame-scrolling-attr-ref.html
|
||||
== move-item.html move-item-ref.html # bug 1125750
|
||||
== fractional-scroll-area.html?top=-0.4&outerBottom=100&innerBottom=200 fractional-scroll-area.html?top=0&outerBottom=100&innerBottom=200
|
||||
== fractional-scroll-area.html?top=0.4&outerBottom=100&innerBottom=200 fractional-scroll-area.html?top=0&outerBottom=100&innerBottom=200
|
||||
== fractional-scroll-area.html?top=0&outerBottom=99.6&innerBottom=200 fractional-scroll-area.html?top=0&outerBottom=100&innerBottom=200
|
||||
== fractional-scroll-area.html?top=0&outerBottom=100.4&innerBottom=200 fractional-scroll-area.html?top=0&outerBottom=100&innerBottom=200
|
||||
== fractional-scroll-area.html?top=-0.4&outerBottom=99.6&innerBottom=200 fractional-scroll-area.html?top=0&outerBottom=100&innerBottom=200
|
||||
== fractional-scroll-area.html?top=-0.4&outerBottom=100.4&innerBottom=200 fractional-scroll-area.html?top=0&outerBottom=100&innerBottom=200
|
||||
== fractional-scroll-area.html?top=0.4&outerBottom=99.6&innerBottom=200 fractional-scroll-area.html?top=0&outerBottom=100&innerBottom=200
|
||||
== fractional-scroll-area.html?top=0.4&outerBottom=100.4&innerBottom=200 fractional-scroll-area.html?top=0&outerBottom=100&innerBottom=200
|
||||
== fractional-scroll-area.html?top=-0.4&outerBottom=100&innerBottom=199.6 fractional-scroll-area.html?top=0&outerBottom=100&innerBottom=200
|
||||
== fractional-scroll-area.html?top=0.4&outerBottom=100&innerBottom=199.6 fractional-scroll-area.html?top=0&outerBottom=100&innerBottom=200
|
||||
== fractional-scroll-area.html?top=0&outerBottom=99.6&innerBottom=199.6 fractional-scroll-area.html?top=0&outerBottom=100&innerBottom=200
|
||||
== fractional-scroll-area.html?top=0&outerBottom=100.4&innerBottom=199.6 fractional-scroll-area.html?top=0&outerBottom=100&innerBottom=200
|
||||
== fractional-scroll-area.html?top=-0.4&outerBottom=99.6&innerBottom=199.6 fractional-scroll-area.html?top=0&outerBottom=100&innerBottom=200
|
||||
== fractional-scroll-area.html?top=-0.4&outerBottom=100.4&innerBottom=199.6 fractional-scroll-area.html?top=0&outerBottom=100&innerBottom=200
|
||||
== fractional-scroll-area.html?top=0.4&outerBottom=99.6&innerBottom=199.6 fractional-scroll-area.html?top=0&outerBottom=100&innerBottom=200
|
||||
== fractional-scroll-area.html?top=0.4&outerBottom=100.4&innerBottom=199.6 fractional-scroll-area.html?top=0&outerBottom=100&innerBottom=200
|
||||
== fractional-scroll-area.html?top=-0.4&outerBottom=100&innerBottom=200.4 fractional-scroll-area.html?top=0&outerBottom=100&innerBottom=200
|
||||
== fractional-scroll-area.html?top=0.4&outerBottom=100&innerBottom=200.4 fractional-scroll-area.html?top=0&outerBottom=100&innerBottom=200
|
||||
== fractional-scroll-area.html?top=0&outerBottom=99.6&innerBottom=200.4 fractional-scroll-area.html?top=0&outerBottom=100&innerBottom=200
|
||||
== fractional-scroll-area.html?top=0&outerBottom=100.4&innerBottom=200.4 fractional-scroll-area.html?top=0&outerBottom=100&innerBottom=200
|
||||
== fractional-scroll-area.html?top=-0.4&outerBottom=99.6&innerBottom=200.4 fractional-scroll-area.html?top=0&outerBottom=100&innerBottom=200
|
||||
== fractional-scroll-area.html?top=-0.4&outerBottom=100.4&innerBottom=200.4 fractional-scroll-area.html?top=0&outerBottom=100&innerBottom=200
|
||||
== fractional-scroll-area.html?top=0.4&outerBottom=99.6&innerBottom=200.4 fractional-scroll-area.html?top=0&outerBottom=100&innerBottom=200
|
||||
== fractional-scroll-area.html?top=0.4&outerBottom=100.4&innerBottom=200.4 fractional-scroll-area.html?top=0&outerBottom=100&innerBottom=200
|
||||
== fractional-scroll-area.html?top=-0.4&outerBottom=100&innerBottom=200&scrollBefore=999 fractional-scroll-area.html?top=0&outerBottom=100&innerBottom=200&scrollBefore=999
|
||||
== fractional-scroll-area.html?top=0.4&outerBottom=100&innerBottom=200&scrollBefore=999 fractional-scroll-area.html?top=0&outerBottom=100&innerBottom=200&scrollBefore=999
|
||||
== fractional-scroll-area.html?top=0&outerBottom=99.6&innerBottom=200&scrollBefore=999 fractional-scroll-area.html?top=0&outerBottom=100&innerBottom=200&scrollBefore=999
|
||||
== fractional-scroll-area.html?top=0&outerBottom=100.4&innerBottom=200&scrollBefore=999 fractional-scroll-area.html?top=0&outerBottom=100&innerBottom=200&scrollBefore=999
|
||||
== fractional-scroll-area.html?top=-0.4&outerBottom=99.6&innerBottom=200&scrollBefore=999 fractional-scroll-area.html?top=0&outerBottom=100&innerBottom=200&scrollBefore=999
|
||||
== fractional-scroll-area.html?top=-0.4&outerBottom=100.4&innerBottom=200&scrollBefore=999 fractional-scroll-area.html?top=0&outerBottom=100&innerBottom=200&scrollBefore=999
|
||||
== fractional-scroll-area.html?top=0.4&outerBottom=99.6&innerBottom=200&scrollBefore=999 fractional-scroll-area.html?top=0&outerBottom=100&innerBottom=200&scrollBefore=999
|
||||
== fractional-scroll-area.html?top=0.4&outerBottom=100.4&innerBottom=200&scrollBefore=999 fractional-scroll-area.html?top=0&outerBottom=100&innerBottom=200&scrollBefore=999
|
||||
== fractional-scroll-area.html?top=-0.4&outerBottom=100&innerBottom=199.6&scrollBefore=999 fractional-scroll-area.html?top=0&outerBottom=100&innerBottom=200&scrollBefore=999
|
||||
== fractional-scroll-area.html?top=0.4&outerBottom=100&innerBottom=199.6&scrollBefore=999 fractional-scroll-area.html?top=0&outerBottom=100&innerBottom=200&scrollBefore=999
|
||||
== fractional-scroll-area.html?top=0&outerBottom=99.6&innerBottom=199.6&scrollBefore=999 fractional-scroll-area.html?top=0&outerBottom=100&innerBottom=200&scrollBefore=999
|
||||
== fractional-scroll-area.html?top=0&outerBottom=100.4&innerBottom=199.6&scrollBefore=999 fractional-scroll-area.html?top=0&outerBottom=100&innerBottom=200&scrollBefore=999
|
||||
== fractional-scroll-area.html?top=-0.4&outerBottom=99.6&innerBottom=199.6&scrollBefore=999 fractional-scroll-area.html?top=0&outerBottom=100&innerBottom=200&scrollBefore=999
|
||||
== fractional-scroll-area.html?top=-0.4&outerBottom=100.4&innerBottom=199.6&scrollBefore=999 fractional-scroll-area.html?top=0&outerBottom=100&innerBottom=200&scrollBefore=999
|
||||
== fractional-scroll-area.html?top=0.4&outerBottom=99.6&innerBottom=199.6&scrollBefore=999 fractional-scroll-area.html?top=0&outerBottom=100&innerBottom=200&scrollBefore=999
|
||||
== fractional-scroll-area.html?top=0.4&outerBottom=100.4&innerBottom=199.6&scrollBefore=999 fractional-scroll-area.html?top=0&outerBottom=100&innerBottom=200&scrollBefore=999
|
||||
== fractional-scroll-area.html?top=-0.4&outerBottom=100&innerBottom=200.4&scrollBefore=999 fractional-scroll-area.html?top=0&outerBottom=100&innerBottom=200&scrollBefore=999
|
||||
== fractional-scroll-area.html?top=0.4&outerBottom=100&innerBottom=200.4&scrollBefore=999 fractional-scroll-area.html?top=0&outerBottom=100&innerBottom=200&scrollBefore=999
|
||||
== fractional-scroll-area.html?top=0&outerBottom=99.6&innerBottom=200.4&scrollBefore=999 fractional-scroll-area.html?top=0&outerBottom=100&innerBottom=200&scrollBefore=999
|
||||
== fractional-scroll-area.html?top=0&outerBottom=100.4&innerBottom=200.4&scrollBefore=999 fractional-scroll-area.html?top=0&outerBottom=100&innerBottom=200&scrollBefore=999
|
||||
== fractional-scroll-area.html?top=-0.4&outerBottom=99.6&innerBottom=200.4&scrollBefore=999 fractional-scroll-area.html?top=0&outerBottom=100&innerBottom=200&scrollBefore=999
|
||||
== fractional-scroll-area.html?top=-0.4&outerBottom=100.4&innerBottom=200.4&scrollBefore=999 fractional-scroll-area.html?top=0&outerBottom=100&innerBottom=200&scrollBefore=999
|
||||
== fractional-scroll-area.html?top=0.4&outerBottom=99.6&innerBottom=200.4&scrollBefore=999 fractional-scroll-area.html?top=0&outerBottom=100&innerBottom=200&scrollBefore=999
|
||||
== fractional-scroll-area.html?top=0.4&outerBottom=100.4&innerBottom=200.4&scrollBefore=999 fractional-scroll-area.html?top=0&outerBottom=100&innerBottom=200&scrollBefore=999
|
||||
!= fractional-scroll-area-invalidation.html about:blank
|
||||
|
@ -22,7 +22,7 @@ skip-if(B2G||Mulet) HTTP(..) == table-cell.html table-cell-ref.html # Initial mu
|
||||
skip-if(Mulet) fuzzy-if(gtkWidget,10,32) HTTP(..) == two-value-syntax.html two-value-syntax-ref.html # MULET: Bug 1144079: Re-enable Mulet mochitests and reftests taskcluster-specific disables
|
||||
skip-if(B2G||Mulet) HTTP(..) == single-value.html single-value-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
skip-if(B2G||Mulet) fuzzy-if(gtkWidget,10,2) HTTP(..) == atomic-under-marker.html atomic-under-marker-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
fuzzy(1,702) skip-if(Android||B2G||Mulet) fuzzy-if(asyncPan&&!layersGPUAccelerated,102,12352) HTTP(..) == xulscroll.html xulscroll-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
fuzzy(1,2616) skip-if(Android||B2G||Mulet) fuzzy-if(asyncPan&&!layersGPUAccelerated,102,12352) HTTP(..) == xulscroll.html xulscroll-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
HTTP(..) == combobox-zoom.html combobox-zoom-ref.html
|
||||
|
||||
# The vertical-text pref setting can be removed after bug 1138384 lands
|
||||
|
Loading…
Reference in New Issue
Block a user