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 @@
-
+
diff --git a/layout/reftests/forms/textbox-setsize.xul b/layout/reftests/forms/textbox-setsize.xul
index b3757f4b0204..f4ec5b2fea13 100644
--- a/layout/reftests/forms/textbox-setsize.xul
+++ b/layout/reftests/forms/textbox-setsize.xul
@@ -3,6 +3,6 @@
-
+
diff --git a/layout/style/forms.css b/layout/style/forms.css
index 48cb4b0ff106..ad49bae367ee 100644
--- a/layout/style/forms.css
+++ b/layout/style/forms.css
@@ -127,6 +127,7 @@ textarea {
letter-spacing: normal;
vertical-align: text-bottom;
cursor: text;
+ -moz-resize: both;
-moz-binding: url("chrome://global/content/platformHTMLBindings.xml#textAreas");
-moz-appearance: textfield-multiline;
text-indent: 0;
diff --git a/layout/xul/base/reftest/reftest.list b/layout/xul/base/reftest/reftest.list
new file mode 100644
index 000000000000..ced7f2f4d383
--- /dev/null
+++ b/layout/xul/base/reftest/reftest.list
@@ -0,0 +1,2 @@
+== textbox-multiline-noresize.xul textbox-multiline-ref.xul
+!= textbox-multiline-resize.xul textbox-multiline-ref.xul
diff --git a/layout/xul/base/reftest/textbox-multiline-noresize.xul b/layout/xul/base/reftest/textbox-multiline-noresize.xul
new file mode 100644
index 000000000000..27945ae9a5e4
--- /dev/null
+++ b/layout/xul/base/reftest/textbox-multiline-noresize.xul
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/layout/xul/base/reftest/textbox-multiline-ref.xul b/layout/xul/base/reftest/textbox-multiline-ref.xul
new file mode 100644
index 000000000000..23d279a75bf3
--- /dev/null
+++ b/layout/xul/base/reftest/textbox-multiline-ref.xul
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/layout/xul/base/reftest/textbox-multiline-resize.xul b/layout/xul/base/reftest/textbox-multiline-resize.xul
new file mode 100644
index 000000000000..d7a25a669931
--- /dev/null
+++ b/layout/xul/base/reftest/textbox-multiline-resize.xul
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/toolkit/content/xul.css b/toolkit/content/xul.css
index 72f802df7f63..a529653d285d 100644
--- a/toolkit/content/xul.css
+++ b/toolkit/content/xul.css
@@ -694,6 +694,14 @@ textbox[multiline="true"] {
-moz-binding: url("chrome://global/content/bindings/textbox.xml#input-box");
}
+html|textarea.textbox-textarea {
+ -moz-resize: none;
+}
+
+textbox[resizable="true"] > .textbox-input-box > html|textarea.textbox-textarea {
+ -moz-resize: both;
+}
+
.textbox-input-box[spellcheck="true"] {
-moz-binding: url("chrome://global/content/bindings/textbox.xml#input-box-spell");
}