From 5ebd68ca4d500bf7319a4379900de0e10c594cba Mon Sep 17 00:00:00 2001
From: "roc+@cs.cmu.edu"
Date: Wed, 27 Feb 2008 01:46:22 -0800
Subject: [PATCH] Bug 416168. Ensure that the overflow rect for a frame always
includes (0,0) even if the frame rect is empty. Also ensures that if the
frame rect is empty but has non-zero dimension on one axis, the overflow rect
includes that size. A scrolled view for such a frame also includes that size
to ensure width:0,height:Npx and width:Npx,height:0 frames are scrollable by
that amount. r+sr=dbaron
---
gfx/public/nsRect.h | 20 +++++++++--
gfx/src/nsRect.cpp | 29 +++++++++-------
layout/base/nsPresShell.cpp | 6 +---
layout/generic/nsFrame.cpp | 6 +++-
layout/generic/nsGfxScrollFrame.cpp | 5 +--
layout/generic/test/Makefile.in | 1 +
layout/generic/test/test_bug416168.html | 45 +++++++++++++++++++++++++
view/src/nsViewManager.cpp | 2 +-
8 files changed, 91 insertions(+), 23 deletions(-)
create mode 100644 layout/generic/test/test_bug416168.html
diff --git a/gfx/public/nsRect.h b/gfx/public/nsRect.h
index b8067579818..469afa68da1 100644
--- a/gfx/public/nsRect.h
+++ b/gfx/public/nsRect.h
@@ -100,12 +100,21 @@ struct NS_GFX nsRect {
PRBool IntersectRect(const nsRect& aRect1, const nsRect& aRect2);
// Computes the smallest rectangle that contains both aRect1 and aRect2 and
- // fills 'this' with the result. Returns FALSE and sets 'this' rect to be an
- // empty rect if both aRect1 and aRect2 are empty
+ // fills 'this' with the result, ignoring empty input rectangles.
+ // Returns FALSE and sets 'this' rect to be an empty rect if both aRect1
+ // and aRect2 are empty.
//
// 'this' can be the same object as either aRect1 or aRect2
PRBool UnionRect(const nsRect& aRect1, const nsRect& aRect2);
+ // Computes the smallest rectangle that contains both aRect1 and aRect2,
+ // where empty input rectangles are allowed to affect the result; the
+ // top-left of an empty input rectangle will be inside or on the edge of
+ // the result.
+ //
+ // 'this' can be the same object as either aRect1 or aRect2
+ void UnionRectIncludeEmpty(const nsRect& aRect1, const nsRect& aRect2);
+
// Accessors
void SetRect(nscoord aX, nscoord aY, nscoord aWidth, nscoord aHeight) {
x = aX; y = aY; width = aWidth; height = aHeight;
@@ -143,6 +152,13 @@ struct NS_GFX nsRect {
return (PRBool) !operator==(aRect);
}
+ // Useful when we care about the exact x/y/width/height values being
+ // equal (i.e. we care about differences in empty rectangles)
+ PRBool IsExactEqual(const nsRect& aRect) const {
+ return x == aRect.x && y == aRect.y &&
+ width == aRect.width && height == aRect.height;
+ }
+
nsRect operator+(const nsPoint& aPoint) const {
return nsRect(x + aPoint.x, y + aPoint.y, width, height);
}
diff --git a/gfx/src/nsRect.cpp b/gfx/src/nsRect.cpp
index 3dce71e45b1..01e65062040 100644
--- a/gfx/src/nsRect.cpp
+++ b/gfx/src/nsRect.cpp
@@ -115,23 +115,28 @@ PRBool nsRect::UnionRect(const nsRect &aRect1, const nsRect &aRect2)
// aRect2 is empty so set the result to aRect1
*this = aRect1;
} else {
- nscoord xmost1 = aRect1.XMost();
- nscoord xmost2 = aRect2.XMost();
- nscoord ymost1 = aRect1.YMost();
- nscoord ymost2 = aRect2.YMost();
-
- // Compute the origin
- x = PR_MIN(aRect1.x, aRect2.x);
- y = PR_MIN(aRect1.y, aRect2.y);
-
- // Compute the size
- width = PR_MAX(xmost1, xmost2) - x;
- height = PR_MAX(ymost1, ymost2) - y;
+ UnionRectIncludeEmpty(aRect1, aRect2);
}
return result;
}
+void nsRect::UnionRectIncludeEmpty(const nsRect &aRect1, const nsRect &aRect2)
+{
+ nscoord xmost1 = aRect1.XMost();
+ nscoord xmost2 = aRect2.XMost();
+ nscoord ymost1 = aRect1.YMost();
+ nscoord ymost2 = aRect2.YMost();
+
+ // Compute the origin
+ x = PR_MIN(aRect1.x, aRect2.x);
+ y = PR_MIN(aRect1.y, aRect2.y);
+
+ // Compute the size
+ width = PR_MAX(xmost1, xmost2) - x;
+ height = PR_MAX(ymost1, ymost2) - y;
+}
+
// Inflate the rect by the specified width and height
void nsRect::Inflate(nscoord aDx, nscoord aDy)
{
diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp
index 09730b5f339..d2f47bf8655 100644
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -3803,11 +3803,7 @@ UnionRectForClosestScrolledView(nsIFrame* aFrame,
// We can't use nsRect::UnionRect since it drops empty rects on
// the floor, and we need to include them. (Thus we need
// aHaveRect to know when to drop the initial value on the floor.)
- nscoord x = PR_MIN(aRect.x, frameBounds.x),
- y = PR_MIN(aRect.y, frameBounds.y),
- xmost = PR_MAX(aRect.XMost(), frameBounds.XMost()),
- ymost = PR_MAX(aRect.YMost(), frameBounds.YMost());
- aRect.SetRect(x, y, xmost - x, ymost - y);
+ aRect.UnionRectIncludeEmpty(aRect, frameBounds);
} else {
aHaveRect = PR_TRUE;
aRect = frameBounds;
diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp
index 34812d15b73..0f99537467f 100644
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -5293,6 +5293,11 @@ nsIFrame::FinishAndStoreOverflow(nsRect* aOverflowArea, nsSize aNewSize)
aOverflowArea->UnionRect(*aOverflowArea, r);
}
}
+
+ // Overflow area must always include the frame's top-left and bottom-right,
+ // even if the frame rect is empty.
+ aOverflowArea->UnionRectIncludeEmpty(*aOverflowArea,
+ nsRect(nsPoint(0, 0), aNewSize));
PRBool geometricOverflow =
aOverflowArea->x < 0 || aOverflowArea->y < 0 ||
@@ -5318,7 +5323,6 @@ nsIFrame::FinishAndStoreOverflow(nsRect* aOverflowArea, nsSize aNewSize)
}
if (outlineRect != nsRect(nsPoint(0, 0), aNewSize)) {
- // Throw out any overflow if we're -moz-hidden-unscrollable
mState |= NS_FRAME_OUTSIDE_CHILDREN;
nsRect* overflowArea = GetOverflowAreaProperty(PR_TRUE);
NS_ASSERTION(overflowArea, "should have created rect");
diff --git a/layout/generic/nsGfxScrollFrame.cpp b/layout/generic/nsGfxScrollFrame.cpp
index ddd104a42d9..0713a9081c0 100644
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -673,8 +673,9 @@ nsHTMLScrollFrame::PlaceScrollArea(const ScrollReflowState& aState)
scrolledFrame->SetPosition(scrolledView->GetOffsetTo(GetView()));
nsRect scrolledArea;
- scrolledArea.UnionRect(mInner.GetScrolledRect(aState.mScrollPortRect.Size()),
- nsRect(nsPoint(0,0), aState.mScrollPortRect.Size()));
+ // Preserve the width or height of empty rects
+ scrolledArea.UnionRectIncludeEmpty(mInner.GetScrolledRect(aState.mScrollPortRect.Size()),
+ nsRect(nsPoint(0,0), aState.mScrollPortRect.Size()));
// Store the new overflow area. Note that this changes where an outline
// of the scrolled frame would be painted, but scrolled frames can't have
diff --git a/layout/generic/test/Makefile.in b/layout/generic/test/Makefile.in
index c705c6fe2bd..dc97f93b368 100644
--- a/layout/generic/test/Makefile.in
+++ b/layout/generic/test/Makefile.in
@@ -59,6 +59,7 @@ _TEST_FILES = test_bug288789.html \
test_bug402380.html \
test_bug404872.html \
test_bug405178.html \
+ test_bug416168.html \
test_character_movement.html \
test_word_movement.html \
test_backspace_delete.html \
diff --git a/layout/generic/test/test_bug416168.html b/layout/generic/test/test_bug416168.html
new file mode 100644
index 00000000000..7e124495e55
--- /dev/null
+++ b/layout/generic/test/test_bug416168.html
@@ -0,0 +1,45 @@
+
+
+
+
+ Test for Bug 416168
+
+
+
+
+
+Mozilla Bug 416168
+