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 +

+ +

+
+
+ +
+
+ +
+
+ +

+ +
+
+
+ + diff --git a/view/src/nsViewManager.cpp b/view/src/nsViewManager.cpp index b1cf8a4cf80..c5263b5912b 100644 --- a/view/src/nsViewManager.cpp +++ b/view/src/nsViewManager.cpp @@ -1662,7 +1662,7 @@ NS_IMETHODIMP nsViewManager::ResizeView(nsIView *aView, const nsRect &aRect, PRB nsRect oldDimensions; view->GetDimensions(oldDimensions); - if (oldDimensions != aRect) { + if (!oldDimensions.IsExactEqual(aRect)) { nsView* parentView = view->GetParent(); if (parentView == nsnull) parentView = view;