diff --git a/layout/forms/test/Makefile.in b/layout/forms/test/Makefile.in index c3109c7b780a..ac1672097138 100644 --- a/layout/forms/test/Makefile.in +++ b/layout/forms/test/Makefile.in @@ -57,6 +57,7 @@ _TEST_FILES = test_bug231389.html \ test_bug476308.html \ test_bug477531.html \ test_bug477700.html \ + test_textarea_resize.html \ test_bug478219.xhtml \ test_bug542914.html \ bug477700_subframe.html \ diff --git a/layout/forms/test/test_textarea_resize.html b/layout/forms/test/test_textarea_resize.html new file mode 100644 index 000000000000..cad0f717065d --- /dev/null +++ b/layout/forms/test/test_textarea_resize.html @@ -0,0 +1,77 @@ + + + + Test for Bug 477700 + + + + + + + + + + +
+
+
+ + diff --git a/layout/generic/nsGfxScrollFrame.cpp b/layout/generic/nsGfxScrollFrame.cpp index b4fd97b24f43..75a1c4e32974 100644 --- a/layout/generic/nsGfxScrollFrame.cpp +++ b/layout/generic/nsGfxScrollFrame.cpp @@ -1815,7 +1815,7 @@ nsGfxScrollFrameInner::BuildDisplayList(nsDisplayListBuilder* aBuilder, { nsresult rv = mOuter->DisplayBorderBackgroundOutline(aBuilder, aLists); NS_ENSURE_SUCCESS(rv, rv); - + if (aBuilder->GetIgnoreScrollFrame() == mOuter) { // Don't clip the scrolled child, and don't paint scrollbars/scrollcorner. // The scrolled frame shouldn't have its own background/border, so we @@ -1828,13 +1828,16 @@ nsGfxScrollFrameInner::BuildDisplayList(nsDisplayListBuilder* aBuilder, // in the border-background layer, on top of our own background and // borders and underneath borders and backgrounds of later elements // in the tree. - nsIFrame* kid = mOuter->GetFirstChild(nsnull); - while (kid) { + PRBool hasResizer = HasResizer(); + for (nsIFrame* kid = mOuter->GetFirstChild(nsnull); kid; kid = kid->GetNextSibling()) { if (kid != mScrolledFrame) { + if (kid == mScrollCornerBox && hasResizer) { + // skip the resizer as this will be drawn later on top of the scrolled content + continue; + } rv = mOuter->BuildDisplayListForChild(aBuilder, kid, aDirtyRect, aLists); NS_ENSURE_SUCCESS(rv, rv); } - kid = kid->GetNextSibling(); } // Overflow clipping can never clip frames outside our subtree, so there @@ -1860,6 +1863,15 @@ nsGfxScrollFrameInner::BuildDisplayList(nsDisplayListBuilder* aBuilder, rv = mOuter->OverflowClip(aBuilder, set, aLists, clip, PR_TRUE, mIsRoot); NS_ENSURE_SUCCESS(rv, rv); + // Place the resizer in the display list above the overflow clip. This + // ensures that the resizer appears above the content and the mouse can + // still target the resizer even when scrollbars are hidden. + if (hasResizer && mScrollCornerBox) { + rv = mOuter->BuildDisplayListForChild(aBuilder, mScrollCornerBox, aDirtyRect, aLists, + nsIFrame::DISPLAY_CHILD_FORCE_PSEUDO_STACKING_CONTEXT); + NS_ENSURE_SUCCESS(rv, rv); + } + return NS_OK; } @@ -2176,6 +2188,19 @@ nsGfxScrollFrameInner::CreateAnonymousContent(nsTArray& aElements) } } + // Check if the frame is resizable. + nsIFrame* resizableFrame = mOuter; + if (parent) { + // For textarea, mOuter is the frame for the anonymous div element, + // so get the resizability from the parent textarea instead. + nsCOMPtr textAreaElement(do_QueryInterface(parent->GetContent())); + if (textAreaElement) { + resizableFrame = parent; + } + } + + PRBool isResizable = resizableFrame->GetStyleDisplay()->mResize != NS_STYLE_RESIZE_NONE; + nsIScrollableFrame *scrollable = do_QueryFrame(mOuter); // At this stage in frame construction, the document element and/or @@ -2195,7 +2220,7 @@ nsGfxScrollFrameInner::CreateAnonymousContent(nsTArray& aElements) ScrollbarStyles styles = scrollable->GetScrollbarStyles(); PRBool canHaveHorizontal = styles.mHorizontal != NS_STYLE_OVERFLOW_HIDDEN; PRBool canHaveVertical = styles.mVertical != NS_STYLE_OVERFLOW_HIDDEN; - if (!canHaveHorizontal && !canHaveVertical) { + if (!canHaveHorizontal && !canHaveVertical && !isResizable) { // Nothing to do. return NS_OK; } @@ -2240,7 +2265,42 @@ nsGfxScrollFrameInner::CreateAnonymousContent(nsTArray& aElements) return NS_ERROR_OUT_OF_MEMORY; } - if (canHaveHorizontal && canHaveVertical) { + if (isResizable) { + nsCOMPtr nodeInfo; + nodeInfo = nodeInfoManager->GetNodeInfo(nsGkAtoms::resizer, nsnull, + kNameSpaceID_XUL); + NS_ENSURE_TRUE(nodeInfo, NS_ERROR_OUT_OF_MEMORY); + + rv = NS_NewXULElement(getter_AddRefs(mScrollCornerContent), nodeInfo); + NS_ENSURE_SUCCESS(rv, rv); + + nsAutoString dir; + switch (resizableFrame->GetStyleDisplay()->mResize) { + case NS_STYLE_RESIZE_HORIZONTAL: + if (IsScrollbarOnRight()) { + dir.AssignLiteral("right"); + } + else { + dir.AssignLiteral("left"); + } + break; + case NS_STYLE_RESIZE_VERTICAL: + dir.AssignLiteral("bottom"); + break; + case NS_STYLE_RESIZE_BOTH: + dir.AssignLiteral("bottomend"); + break; + default: + NS_WARNING("only resizable types should have resizers"); + } + mScrollCornerContent->SetAttr(kNameSpaceID_None, nsGkAtoms::dir, dir, PR_FALSE); + mScrollCornerContent->SetAttr(kNameSpaceID_None, nsGkAtoms::element, + NS_LITERAL_STRING("_parent"), PR_FALSE); + + if (!aElements.AppendElement(mScrollCornerContent)) + return NS_ERROR_OUT_OF_MEMORY; + } + else if (canHaveHorizontal && canHaveVertical) { nodeInfo = nodeInfoManager->GetNodeInfo(nsGkAtoms::scrollcorner, nsnull, kNameSpaceID_XUL); rv = NS_NewElement(getter_AddRefs(mScrollCornerContent), @@ -2972,26 +3032,36 @@ static void LayoutAndInvalidate(nsBoxLayoutState& aState, } } -static void AdjustScrollbarRect(nsIFrame* aFrame, nsPresContext* aPresContext, - nsRect& aRect, PRBool aVertical) +void +nsGfxScrollFrameInner::AdjustScrollbarRectForResizer( + nsIFrame* aFrame, nsPresContext* aPresContext, + nsRect& aRect, PRBool aHasResizer, PRBool aVertical) { if ((aVertical ? aRect.width : aRect.height) == 0) return; - nsPoint offsetToView; - nsPoint offsetToWidget; - nsIWidget* widget = - aFrame->GetClosestView(&offsetToView)->GetNearestWidget(&offsetToWidget); - nsPoint offset = offsetToView + offsetToWidget; - nsIntRect widgetRect; - if (!widget || !widget->ShowsResizeIndicator(&widgetRect)) - return; + // if a content resizer is present, use its size. Otherwise, check if the + // widget has a resizer. + nsRect resizerRect; + if (aHasResizer && mScrollCornerBox) { + resizerRect = mScrollCornerBox->GetRect(); + } + else { + nsPoint offsetToView; + nsPoint offsetToWidget; + nsIWidget* widget = + aFrame->GetClosestView(&offsetToView)->GetNearestWidget(&offsetToWidget); + nsPoint offset = offsetToView + offsetToWidget; + nsIntRect widgetRect; + if (!widget || !widget->ShowsResizeIndicator(&widgetRect)) + return; - nsRect resizerRect = - nsRect(aPresContext->DevPixelsToAppUnits(widgetRect.x) - offset.x, - aPresContext->DevPixelsToAppUnits(widgetRect.y) - offset.y, - aPresContext->DevPixelsToAppUnits(widgetRect.width), - aPresContext->DevPixelsToAppUnits(widgetRect.height)); + nsRect resizerRect = + nsRect(aPresContext->DevPixelsToAppUnits(widgetRect.x) - offset.x, + aPresContext->DevPixelsToAppUnits(widgetRect.y) - offset.y, + aPresContext->DevPixelsToAppUnits(widgetRect.width), + aPresContext->DevPixelsToAppUnits(widgetRect.height)); + } if (!resizerRect.Contains(aRect.BottomRight() - nsPoint(1, 1))) return; @@ -3010,18 +3080,62 @@ nsGfxScrollFrameInner::LayoutScrollbars(nsBoxLayoutState& aState, NS_ASSERTION(!mSupppressScrollbarUpdate, "This should have been suppressed"); + PRBool hasResizer = HasResizer(); + PRBool scrollbarOnLeft = !IsScrollbarOnRight(); + + // place the scrollcorner + if (mScrollCornerBox) { + NS_PRECONDITION(mScrollCornerBox->IsBoxFrame(), "Must be a box frame!"); + + // if a resizer is present, get its size + nsSize resizerSize; + if (HasResizer()) { + // just assume a default size of 15 pixels + nscoord defaultSize = nsPresContext::CSSPixelsToAppUnits(15); + resizerSize.width = + mVScrollbarBox ? mVScrollbarBox->GetMinSize(aState).width : defaultSize; + resizerSize.height = + mHScrollbarBox ? mHScrollbarBox->GetMinSize(aState).height : defaultSize; + } + else { + resizerSize = nsSize(0, 0); + } + + nsRect r(0, 0, 0, 0); + if (aContentArea.x != mScrollPort.x || scrollbarOnLeft) { + // scrollbar (if any) on left + r.x = aContentArea.x; + r.width = PR_MAX(resizerSize.width, mScrollPort.x - aContentArea.x); + NS_ASSERTION(r.width >= 0, "Scroll area should be inside client rect"); + } else { + // scrollbar (if any) on right + r.width = PR_MAX(resizerSize.width, aContentArea.XMost() - mScrollPort.XMost()); + r.x = aContentArea.XMost() - r.width; + NS_ASSERTION(r.width >= 0, "Scroll area should be inside client rect"); + } + if (aContentArea.y != mScrollPort.y) { + NS_ERROR("top scrollbars not supported"); + } else { + // scrollbar (if any) on bottom + r.height = PR_MAX(resizerSize.height, aContentArea.YMost() - mScrollPort.YMost()); + r.y = aContentArea.YMost() - r.height; + NS_ASSERTION(r.height >= 0, "Scroll area should be inside client rect"); + } + LayoutAndInvalidate(aState, mScrollCornerBox, r); + } + nsPresContext* presContext = mScrolledFrame->PresContext(); if (mVScrollbarBox) { NS_PRECONDITION(mVScrollbarBox->IsBoxFrame(), "Must be a box frame!"); nsRect vRect(mScrollPort); vRect.width = aContentArea.width - mScrollPort.width; - vRect.x = IsScrollbarOnRight() ? mScrollPort.XMost() : aContentArea.x; + vRect.x = scrollbarOnLeft ? aContentArea.x : mScrollPort.XMost(); #ifdef DEBUG nsMargin margin; mVScrollbarBox->GetMargin(margin); NS_ASSERTION(margin == nsMargin(0,0,0,0), "Scrollbar margin not supported"); #endif - AdjustScrollbarRect(mOuter, presContext, vRect, PR_TRUE); + AdjustScrollbarRectForResizer(mOuter, presContext, vRect, hasResizer, PR_TRUE); LayoutAndInvalidate(aState, mVScrollbarBox, vRect); } @@ -3035,39 +3149,10 @@ nsGfxScrollFrameInner::LayoutScrollbars(nsBoxLayoutState& aState, mHScrollbarBox->GetMargin(margin); NS_ASSERTION(margin == nsMargin(0,0,0,0), "Scrollbar margin not supported"); #endif - AdjustScrollbarRect(mOuter, presContext, hRect, PR_FALSE); + AdjustScrollbarRectForResizer(mOuter, presContext, hRect, hasResizer, PR_FALSE); LayoutAndInvalidate(aState, mHScrollbarBox, hRect); } - // place the scrollcorner - if (mScrollCornerBox) { - NS_PRECONDITION(mScrollCornerBox->IsBoxFrame(), "Must be a box frame!"); - nsRect r(0, 0, 0, 0); - if (aContentArea.x != mScrollPort.x) { - // scrollbar (if any) on left - r.x = aContentArea.x; - r.width = mScrollPort.x - aContentArea.x; - NS_ASSERTION(r.width >= 0, "Scroll area should be inside client rect"); - } else { - // scrollbar (if any) on right - r.x = mScrollPort.XMost(); - r.width = aContentArea.XMost() - mScrollPort.XMost(); - NS_ASSERTION(r.width >= 0, "Scroll area should be inside client rect"); - } - if (aContentArea.y != mScrollPort.y) { - // scrollbar (if any) on top - r.y = aContentArea.y; - r.height = mScrollPort.y - aContentArea.y; - NS_ASSERTION(r.height >= 0, "Scroll area should be inside client rect"); - } else { - // scrollbar (if any) on bottom - r.y = mScrollPort.YMost(); - r.height = aContentArea.YMost() - mScrollPort.YMost(); - NS_ASSERTION(r.height >= 0, "Scroll area should be inside client rect"); - } - LayoutAndInvalidate(aState, mScrollCornerBox, r); - } - // may need to update fixed position children of the viewport, // if the client area changed size because of an incremental // reflow of a descendant. (If the outer frame is dirty, the fixed diff --git a/layout/generic/nsGfxScrollFrame.h b/layout/generic/nsGfxScrollFrame.h index 20c0d9addee0..df8ad46de0c1 100644 --- a/layout/generic/nsGfxScrollFrame.h +++ b/layout/generic/nsGfxScrollFrame.h @@ -215,6 +215,15 @@ public: nsMargin GetDesiredScrollbarSizes(nsBoxLayoutState* aState); PRBool IsLTR() const; PRBool IsScrollbarOnRight() const; + // adjust the scrollbar rectangle aRect to account for any visible resizer. + // aHasResizer specifies if there is a content resizer, however this method + // will also check if a widget resizer is present as well. + void AdjustScrollbarRectForResizer(nsIFrame* aFrame, nsPresContext* aPresContext, + nsRect& aRect, PRBool aHasResizer, PRBool aVertical); + // returns true if a resizer should be visible + PRBool HasResizer() { + return mScrollCornerContent && mScrollCornerContent->Tag() == nsGkAtoms::resizer; + } void LayoutScrollbars(nsBoxLayoutState& aState, const nsRect& aContentArea, const nsRect& aOldScrollArea); diff --git a/layout/reftests/bugs/481024-1a.html b/layout/reftests/bugs/481024-1a.html index fb53f1e00801..6509f42ea3d8 100644 --- a/layout/reftests/bugs/481024-1a.html +++ b/layout/reftests/bugs/481024-1a.html @@ -1,6 +1,6 @@ - + diff --git a/layout/reftests/bugs/481024-1b.html b/layout/reftests/bugs/481024-1b.html index b71238a15270..1f4a0f32f1fa 100644 --- a/layout/reftests/bugs/481024-1b.html +++ b/layout/reftests/bugs/481024-1b.html @@ -1,6 +1,6 @@ - + diff --git a/layout/reftests/bugs/481024-1c.html b/layout/reftests/bugs/481024-1c.html index 71238e1bedaf..1ec19186ed64 100644 --- a/layout/reftests/bugs/481024-1c.html +++ b/layout/reftests/bugs/481024-1c.html @@ -1,6 +1,6 @@ - + diff --git a/layout/reftests/editor/xul/emptymultiline-ref.xul b/layout/reftests/editor/xul/emptymultiline-ref.xul index cb0f108a0b35..0adfc03c096b 100644 --- a/layout/reftests/editor/xul/emptymultiline-ref.xul +++ b/layout/reftests/editor/xul/emptymultiline-ref.xul @@ -8,6 +8,6 @@