mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-02 01:48:05 +00:00
Bug 1198135 - Part 2: Compute the scrolled rect stored by ScrollFrameHelper as what will actually be scrollable. r=dbaron
This commit is contained in:
parent
c38bfdabb3
commit
bdbef762ca
@ -727,13 +727,20 @@ nsHTMLScrollFrame::ReflowContents(ScrollReflowInput* aState,
|
||||
}
|
||||
|
||||
void
|
||||
nsHTMLScrollFrame::PlaceScrollArea(const ScrollReflowInput& aState,
|
||||
nsHTMLScrollFrame::PlaceScrollArea(ScrollReflowInput& aState,
|
||||
const nsPoint& aScrollPosition)
|
||||
{
|
||||
nsIFrame *scrolledFrame = mHelper.mScrolledFrame;
|
||||
// Set the x,y of the scrolled frame to the correct value
|
||||
scrolledFrame->SetPosition(mHelper.mScrollPort.TopLeft() - aScrollPosition);
|
||||
|
||||
// Recompute our scrollable overflow, taking perspective children into
|
||||
// account. Note that this only recomputes the overflow areas stored on the
|
||||
// helper (which are used to compute scrollable length and scrollbar thumb
|
||||
// sizes) but not the overflow areas stored on the frame. This seems to work
|
||||
// for now, but it's possible that we may need to update both in the future.
|
||||
AdjustForPerspective(aState.mContentsOverflowAreas.ScrollableOverflow());
|
||||
|
||||
nsRect scrolledArea;
|
||||
// Preserve the width or height of empty rects
|
||||
nsSize portSize = mHelper.mScrollPort.Size();
|
||||
@ -836,6 +843,140 @@ GetBrowserRoot(nsIContent* aContent)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// When we have perspective set on the outer scroll frame, and transformed
|
||||
// children (possibly with preserve-3d) then the effective transform on the
|
||||
// child depends on the offset to the scroll frame, which changes as we scroll.
|
||||
// This perspective transform can cause the element to move relative to the
|
||||
// scrolled inner frame, which would cause the scrollable length changes during
|
||||
// scrolling if we didn't account for it. Since we don't want scrollHeight/Width
|
||||
// and the size of scrollbar thumbs to change during scrolling, we compute the
|
||||
// scrollable overflow by determining the scroll position at which the child
|
||||
// becomes completely visible within the scrollport rather than using the union
|
||||
// of the overflow areas at their current position.
|
||||
void
|
||||
GetScrollableOverflowForPerspective(nsIFrame* aScrolledFrame,
|
||||
nsIFrame* aCurrentFrame,
|
||||
const nsRect aScrollPort,
|
||||
nsPoint aOffset,
|
||||
nsRect& aScrolledFrameOverflowArea)
|
||||
{
|
||||
// Iterate over all children except pop-ups.
|
||||
FrameChildListIDs skip = nsIFrame::kSelectPopupList | nsIFrame::kPopupList;
|
||||
for (nsIFrame::ChildListIterator childLists(aCurrentFrame);
|
||||
!childLists.IsDone(); childLists.Next()) {
|
||||
if (skip.Contains(childLists.CurrentID())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (nsIFrame* child : childLists.CurrentList()) {
|
||||
nsPoint offset = aOffset;
|
||||
|
||||
// When we reach a direct child of the scroll, then we record the offset
|
||||
// to convert from that frame's coordinate into the scroll frame's
|
||||
// coordinates. Preserve-3d descendant frames use the same offset as their
|
||||
// ancestors, since TransformRect already converts us into the coordinate
|
||||
// space of the preserve-3d root.
|
||||
if (aScrolledFrame == aCurrentFrame) {
|
||||
offset = child->GetPosition();
|
||||
}
|
||||
|
||||
if (child->Extend3DContext()) {
|
||||
// If we're a preserve-3d frame, then recurse and include our
|
||||
// descendants since overflow of preserve-3d frames is only included
|
||||
// in the post-transform overflow area of the preserve-3d root frame.
|
||||
GetScrollableOverflowForPerspective(aScrolledFrame, child, aScrollPort,
|
||||
offset, aScrolledFrameOverflowArea);
|
||||
}
|
||||
|
||||
// If we're transformed, then we want to consider the possibility that
|
||||
// this frame might move relative to the scrolled frame when scrolling.
|
||||
// For preserve-3d, leaf frames have correct overflow rects relative to
|
||||
// themselves. preserve-3d 'nodes' (intermediate frames and the root) have
|
||||
// only their untransformed children included in their overflow relative
|
||||
// to self, which is what we want to include here.
|
||||
if (child->IsTransformed()) {
|
||||
// Compute the overflow rect for this leaf transform frame in the
|
||||
// coordinate space of the scrolled frame.
|
||||
nsPoint scrollPos = aScrolledFrame->GetPosition();
|
||||
nsRect preScroll = nsDisplayTransform::TransformRect(
|
||||
child->GetScrollableOverflowRectRelativeToSelf(), child);
|
||||
|
||||
// Temporarily override the scroll position of the scrolled frame by
|
||||
// 10 CSS pixels, and then recompute what the overflow rect would be.
|
||||
// This scroll position may not be valid, but that shouldn't matter
|
||||
// for our calculations.
|
||||
aScrolledFrame->SetPosition(scrollPos + nsPoint(600, 600));
|
||||
nsRect postScroll = nsDisplayTransform::TransformRect(
|
||||
child->GetScrollableOverflowRectRelativeToSelf(), child);
|
||||
aScrolledFrame->SetPosition(scrollPos);
|
||||
|
||||
// Compute how many app units the overflow rects moves by when we adjust
|
||||
// the scroll position by 1 app unit.
|
||||
double rightDelta =
|
||||
(postScroll.XMost() - preScroll.XMost() + 600.0) / 600.0;
|
||||
double bottomDelta =
|
||||
(postScroll.YMost() - preScroll.YMost() + 600.0) / 600.0;
|
||||
|
||||
// We can't ever have negative scrolling.
|
||||
NS_ASSERTION(rightDelta > 0.0f && bottomDelta > 0.0f,
|
||||
"Scrolling can't be reversed!");
|
||||
|
||||
// Move preScroll into the coordinate space of the scrollport.
|
||||
preScroll += offset + scrollPos;
|
||||
|
||||
// For each of the four edges of preScroll, figure out how far they
|
||||
// extend beyond the scrollport. Ignore negative values since that means
|
||||
// that side is already scrolled in to view and we don't need to add
|
||||
// overflow to account for it.
|
||||
nsMargin overhang(std::max(0, aScrollPort.Y() - preScroll.Y()),
|
||||
std::max(0, preScroll.XMost() - aScrollPort.XMost()),
|
||||
std::max(0, preScroll.YMost() - aScrollPort.YMost()),
|
||||
std::max(0, aScrollPort.X() - preScroll.X()));
|
||||
|
||||
// Scale according to rightDelta/bottomDelta to adjust for the different
|
||||
// scroll rates.
|
||||
overhang.top /= bottomDelta;
|
||||
overhang.right /= rightDelta;
|
||||
overhang.bottom /= bottomDelta;
|
||||
overhang.left /= rightDelta;
|
||||
|
||||
// Take the minimum overflow rect that would allow the current scroll
|
||||
// position, using the size of the scroll port and offset by the
|
||||
// inverse of the scroll position.
|
||||
nsRect overflow(0, 0, aScrollPort.width, aScrollPort.height);
|
||||
|
||||
// Expand it by our margins to get an overflow rect that would allow all
|
||||
// edges of our transformed content to be scrolled into view.
|
||||
overflow.Inflate(overhang);
|
||||
|
||||
// Merge it with the combined overflow
|
||||
aScrolledFrameOverflowArea.UnionRect(aScrolledFrameOverflowArea,
|
||||
overflow);
|
||||
} else if (aCurrentFrame == aScrolledFrame) {
|
||||
aScrolledFrameOverflowArea.UnionRect(
|
||||
aScrolledFrameOverflowArea,
|
||||
child->GetScrollableOverflowRectRelativeToParent());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsHTMLScrollFrame::AdjustForPerspective(nsRect& aScrollableOverflow)
|
||||
{
|
||||
// If we have perspective that is being applied to our children, then
|
||||
// the effective transform on the child depends on the relative position
|
||||
// of the child to us and changes during scrolling.
|
||||
if (!ChildrenHavePerspective()) {
|
||||
return;
|
||||
}
|
||||
aScrollableOverflow.SetEmpty();
|
||||
GetScrollableOverflowForPerspective(mHelper.mScrolledFrame,
|
||||
mHelper.mScrolledFrame,
|
||||
mHelper.mScrollPort,
|
||||
nsPoint(), aScrollableOverflow);
|
||||
}
|
||||
|
||||
void
|
||||
nsHTMLScrollFrame::Reflow(nsPresContext* aPresContext,
|
||||
ReflowOutput& aDesiredSize,
|
||||
@ -4712,6 +4853,10 @@ nsXULScrollFrame::LayoutScrollArea(nsBoxLayoutState& aState,
|
||||
if (minSize.width > childRect.width)
|
||||
childRect.width = minSize.width;
|
||||
|
||||
// TODO: Handle transformed children that inherit perspective
|
||||
// from this frame. See AdjustForPerspective for how we handle
|
||||
// this for HTML scroll frames.
|
||||
|
||||
aState.SetLayoutFlags(flags);
|
||||
ClampAndSetBounds(aState, childRect, aScrollPosition);
|
||||
mHelper.mScrolledFrame->XULLayout(aState);
|
||||
|
@ -687,7 +687,7 @@ public:
|
||||
bool aFirstPass);
|
||||
void ReflowContents(ScrollReflowInput* aState,
|
||||
const ReflowOutput& aDesiredSize);
|
||||
void PlaceScrollArea(const ScrollReflowInput& aState,
|
||||
void PlaceScrollArea(ScrollReflowInput& aState,
|
||||
const nsPoint& aScrollPosition);
|
||||
nscoord GetIntrinsicVScrollbarWidth(nsRenderingContext *aRenderingContext);
|
||||
|
||||
@ -710,6 +710,11 @@ public:
|
||||
return mHelper.ComputeCustomOverflow(aOverflowAreas);
|
||||
}
|
||||
|
||||
// Recomputes the scrollable overflow area we store in the helper to take children
|
||||
// that are affected by perpsective set on the outer frame and scroll at different
|
||||
// rates.
|
||||
void AdjustForPerspective(nsRect& aScrollableOverflow);
|
||||
|
||||
// Called to set the child frames. We typically have three: the scroll area,
|
||||
// the vertical scrollbar, and the horizontal scrollbar.
|
||||
virtual void SetInitialChildList(ChildListID aListID,
|
||||
|
@ -101,6 +101,7 @@ skip-if = buildapp == 'b2g' # b2g(Target should not have scrolled - got 114.1000
|
||||
[test_bug970363.html]
|
||||
[test_bug1062406.html]
|
||||
[test_bug1174521.html]
|
||||
[test_bug1198135.html]
|
||||
[test_contained_plugin_transplant.html]
|
||||
skip-if = os=='win'
|
||||
[test_image_selection.html]
|
||||
|
84
layout/generic/test/test_bug1198135.html
Normal file
84
layout/generic/test/test_bug1198135.html
Normal file
@ -0,0 +1,84 @@
|
||||
<!doctype html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1198135
|
||||
-->
|
||||
<html><head>
|
||||
<title>Test for Bug 1198135</title>
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" href="/tests/SimpleTest/test.css">
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1198135">Mozilla Bug 1198135</a>
|
||||
<style>
|
||||
.example {
|
||||
perspective: 100px;
|
||||
height: 300px;
|
||||
width: 300px;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
border: 1px solid blue;
|
||||
}
|
||||
|
||||
.example__group {
|
||||
position: relative;
|
||||
transform-style: preserve-3d;
|
||||
height: 300px;
|
||||
top: 0;
|
||||
}
|
||||
.example__layer {
|
||||
position: absolute;
|
||||
top:0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
.layer--a {
|
||||
box-shadow: inset 0 0 0 1px red;
|
||||
}
|
||||
.layer--b {
|
||||
box-shadow: inset 0 0 0 1px blue;
|
||||
transform: translateZ(50px) scale(.45);
|
||||
}
|
||||
.layer--c {
|
||||
box-shadow: inset 0 0 0 1px green;
|
||||
}
|
||||
.layer--d {
|
||||
box-shadow: inset 00px 0 0px 1px orange;
|
||||
transform: translateZ(50px) scale(.45);
|
||||
}
|
||||
.layer--e {
|
||||
box-shadow: inset 00px 0 0px 1px orange;
|
||||
transform: translateZ(50px) scale(.45) translateY(300px);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div class="example" id="first">
|
||||
<div class="example__group">
|
||||
<div class="example__layer layer--a"></div>
|
||||
<div class="example__layer layer--b"></div>
|
||||
</div>
|
||||
<div class="example__group">
|
||||
<div class="example__layer layer--c"></div>
|
||||
<div class="example__layer layer--d"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="example" id="second">
|
||||
<div class="example__group">
|
||||
<div class="example__layer layer--a"></div>
|
||||
<div class="example__layer layer--b"></div>
|
||||
</div>
|
||||
<div class="example__group">
|
||||
<div class="example__layer layer--c"></div>
|
||||
<div class="example__layer layer--e"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
is(document.getElementById("first").scrollHeight, 600, "Scroll height should be computed correctly");
|
||||
// The true height is 727.5 and we don't always snap the same direction.
|
||||
isfuzzy(document.getElementById("second").scrollHeight, 728, 1, "Scroll height should be computed correctly");
|
||||
</script>
|
||||
|
||||
</body></html>
|
Loading…
Reference in New Issue
Block a user