Reduce the border-radius of elements with overflow != visible so that any present scrollbars are not clipped. (Bug 459144, patch 8) r=roc a2.0=blocking:beta6+
authorL. David Baron <dbaron@dbaron.org>
Tue, 07 Sep 2010 15:20:35 -0700
changeset 52133 76358146c914e64a51c3862d5e8c0ac1d58713fd
parent 52132 a118b42abad133da59efb56a5b61b9b6237b2e44
child 52134 433efb14d970b425841ad356ab9da36c37bb7183
push idunknown
push userunknown
push dateunknown
reviewersroc
bugs459144
milestone2.0b6pre
Reduce the border-radius of elements with overflow != visible so that any present scrollbars are not clipped. (Bug 459144, patch 8) r=roc a2.0=blocking:beta6+
layout/generic/nsGfxScrollFrame.cpp
layout/generic/nsGfxScrollFrame.h
layout/reftests/border-radius/reftest.list
layout/reftests/border-radius/scrollbar-clamping-1-ref.html
layout/reftests/border-radius/scrollbar-clamping-1.html
layout/reftests/border-radius/scrollbar-clamping-2-ref.html
layout/reftests/border-radius/scrollbar-clamping-2.html
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -3136,16 +3136,79 @@ nsGfxScrollFrameInner::SetCoordAttribute
   newValue.AppendInt(aSize);
 
   if (aContent->AttrValueIs(kNameSpaceID_None, aAtom, newValue, eCaseMatters))
     return;
 
   aContent->SetAttr(kNameSpaceID_None, aAtom, newValue, PR_TRUE);
 }
 
+static void
+ReduceRadii(nscoord aXBorder, nscoord aYBorder,
+            nscoord& aXRadius, nscoord& aYRadius)
+{
+  // In order to ensure that the inside edge of the border has no
+  // curvature, we need at least one of its radii to be zero.
+  if (aXRadius <= aXBorder || aYRadius <= aYBorder)
+    return;
+
+  // For any corner where we reduce the radii, preserve the corner's shape.
+  double ratio = NS_MAX(double(aXBorder) / aXRadius,
+                        double(aYBorder) / aYRadius);
+  aXRadius *= ratio;
+  aYRadius *= ratio;
+}
+
+/**
+ * Implement an override for nsIFrame::GetBorderRadii to ensure that
+ * the clipping region for the border radius does not clip the scrollbars.
+ *
+ * In other words, we require that the border radius be reduced until the
+ * inner border radius at the inner edge of the border is 0 wherever we
+ * have scrollbars.
+ */
+PRBool
+nsGfxScrollFrameInner::GetBorderRadii(nscoord aRadii[8]) const
+{
+  if (!mOuter->nsContainerFrame::GetBorderRadii(aRadii))
+    return PR_FALSE;
+
+  // Since we can use GetActualScrollbarSizes (rather than
+  // GetDesiredScrollbarSizes) since this doesn't affect reflow, we
+  // probably should.
+  nsMargin sb = GetActualScrollbarSizes();
+  nsMargin border = mOuter->GetUsedBorder();
+
+  if (sb.left > 0 || sb.top > 0) {
+    ReduceRadii(border.left, border.top,
+                aRadii[NS_CORNER_TOP_LEFT_X],
+                aRadii[NS_CORNER_TOP_LEFT_Y]);
+  }
+
+  if (sb.top > 0 || sb.right > 0) {
+    ReduceRadii(border.right, border.top,
+                aRadii[NS_CORNER_TOP_RIGHT_X],
+                aRadii[NS_CORNER_TOP_RIGHT_Y]);
+  }
+
+  if (sb.right > 0 || sb.bottom > 0) {
+    ReduceRadii(border.right, border.bottom,
+                aRadii[NS_CORNER_BOTTOM_RIGHT_X],
+                aRadii[NS_CORNER_BOTTOM_RIGHT_Y]);
+  }
+
+  if (sb.bottom > 0 || sb.left > 0) {
+    ReduceRadii(border.left, border.bottom,
+                aRadii[NS_CORNER_BOTTOM_LEFT_X],
+                aRadii[NS_CORNER_BOTTOM_LEFT_Y]);
+  }
+
+  return PR_TRUE;
+}
+
 nsRect
 nsGfxScrollFrameInner::GetScrolledRect() const
 {
   nsRect result =
     GetScrolledRectInternal(mScrolledFrame->GetOverflowRect(),
                             mScrollPort.Size());
 
   NS_ASSERTION(result.width >= mScrollPort.width,
--- a/layout/generic/nsGfxScrollFrame.h
+++ b/layout/generic/nsGfxScrollFrame.h
@@ -86,16 +86,18 @@ public:
   nsresult FireScrollPortEvent();
   void PostOverflowEvent();
   void Destroy();
 
   nsresult BuildDisplayList(nsDisplayListBuilder*   aBuilder,
                             const nsRect&           aDirtyRect,
                             const nsDisplayListSet& aLists);
 
+  PRBool GetBorderRadii(nscoord aRadii[8]) const;
+
   // nsIReflowCallback
   virtual PRBool ReflowFinished();
   virtual void ReflowCallbackCanceled();
 
   // This gets called when the 'curpos' attribute on one of the scrollbars changes
   void CurPosAttributeChanged(nsIContent* aChild);
   void PostScrollEvent();
   void FireScrollEvent();
@@ -336,16 +338,20 @@ public:
                                nsHTMLReflowMetrics* aMetrics,
                                PRBool aFirstPass);
   nsresult ReflowContents(ScrollReflowState* aState,
                           const nsHTMLReflowMetrics& aDesiredSize);
   void PlaceScrollArea(const ScrollReflowState& aState,
                        const nsPoint& aScrollPosition);
   nscoord GetIntrinsicVScrollbarWidth(nsIRenderingContext *aRenderingContext);
 
+  virtual PRBool GetBorderRadii(nscoord aRadii[8]) const {
+    return mInner.GetBorderRadii(aRadii);
+  }
+
   virtual nscoord GetMinWidth(nsIRenderingContext *aRenderingContext);
   virtual nscoord GetPrefWidth(nsIRenderingContext *aRenderingContext);
   NS_IMETHOD GetPadding(nsMargin& aPadding);
   virtual PRBool IsCollapsed(nsBoxLayoutState& aBoxLayoutState);
   
   NS_IMETHOD Reflow(nsPresContext*          aPresContext,
                     nsHTMLReflowMetrics&     aDesiredSize,
                     const nsHTMLReflowState& aReflowState,
@@ -591,16 +597,20 @@ public:
   virtual nsSize GetMinSize(nsBoxLayoutState& aBoxLayoutState);
   virtual nsSize GetPrefSize(nsBoxLayoutState& aBoxLayoutState);
   virtual nsSize GetMaxSize(nsBoxLayoutState& aBoxLayoutState);
   virtual nscoord GetBoxAscent(nsBoxLayoutState& aBoxLayoutState);
 
   NS_IMETHOD DoLayout(nsBoxLayoutState& aBoxLayoutState);
   NS_IMETHOD GetPadding(nsMargin& aPadding);
 
+  virtual PRBool GetBorderRadii(nscoord aRadii[8]) const {
+    return mInner.GetBorderRadii(aRadii);
+  }
+
   nsresult Layout(nsBoxLayoutState& aState);
   void LayoutScrollArea(nsBoxLayoutState& aState, const nsPoint& aScrollPosition);
 
   static PRBool AddRemoveScrollbar(PRBool& aHasScrollbar, 
                                    nscoord& aXY, 
                                    nscoord& aSize, 
                                    nscoord aSbSize, 
                                    PRBool aOnRightOrBottom, 
--- a/layout/reftests/border-radius/reftest.list
+++ b/layout/reftests/border-radius/reftest.list
@@ -43,8 +43,12 @@ fails == clipping-1.html clipping-1-ref.
 == inherit-1.html inherit-1-ref.html # border-radius shouldn't inherit
 
 # Table elements
 == table-collapse-1.html table-collapse-1-ref.html # border-radius is ignored on internal table elements
 # when border-collapse: collapse
 
 == invalidate-1a.html invalidate-1-ref.html
 == invalidate-1b.html invalidate-1-ref.html
+
+# test that border-radius is reduced for scrollbars
+== scrollbar-clamping-1.html scrollbar-clamping-1-ref.html
+== scrollbar-clamping-2.html scrollbar-clamping-2-ref.html
new file mode 100644
--- /dev/null
+++ b/layout/reftests/border-radius/scrollbar-clamping-1-ref.html
@@ -0,0 +1,47 @@
+<!DOCTYPE HTML>
+<html dir="ltr">
+<title>Test of reduction of border-radius for scrollbars (border drawing)</title>
+<style>
+
+.contain { height: 130px; position: relative }
+
+.test {
+  position: absolute;
+  top: 0;
+  left: 0;
+  /* border-width: 2px 4px 8px 10px; */
+  height: 100px;
+  width: 200px;
+  /* -moz-border-radius: 12px / 15px; */
+  border: medium solid blue;
+  border-width: 2px 4px 8px 10px;
+}
+
+.cover {
+  position: absolute;
+  width: 200px;
+  height: 100px;
+  top: 2px;
+  left: 10px;
+  background: blue;
+}
+
+</style>
+
+<div class="contain">
+  <!-- scrollbar along bottom -->
+  <div class="test" id="x" style="-moz-border-radius: 12px 12px 6.4px 10px / 15px 15px 8px 12.5px"></div>
+  <div class="cover" style="-moz-border-radius-topright: 5px"></div>
+</div>
+
+<div class="contain">
+  <!-- scrollbar along right -->
+  <div class="test" id="y" style="-moz-border-radius: 12px 4px 6.4px 12px / 15px 5px 8px 15px"></div>
+  <div class="cover"></div>
+</div>
+
+<div class="contain">
+  <!-- scrollbar along bottom and right -->
+  <div class="test" id="xy" style="-moz-border-radius: 12px 4px 6.4px 10px / 15px 5px 8px 12.5px"></div>
+  <div class="cover"></div>
+</div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/border-radius/scrollbar-clamping-1.html
@@ -0,0 +1,45 @@
+<!DOCTYPE HTML>
+<title>Test of reduction of border-radius for scrollbars (border drawing)</title>
+<style>
+
+.contain { height: 130px; position: relative }
+
+.test {
+  position: absolute;
+  top: 0;
+  left: 0;
+  border: medium solid blue;
+  border-width: 2px 4px 8px 10px;
+  height: 100px;
+  width: 200px;
+  -moz-border-radius: 12px / 15px;
+}
+
+.cover {
+  position: absolute;
+  width: 200px;
+  height: 100px;
+  top: 2px;
+  left: 10px;
+  background: blue;
+}
+
+#x, #xy { overflow-x: scroll }
+#y, #xy { overflow-y: scroll }
+
+</style>
+
+<div class="contain">
+  <div class="test" id="x"></div>
+  <div class="cover" style="-moz-border-radius-topright: 5px"></div>
+</div>
+
+<div class="contain">
+  <div class="test" id="y"></div>
+  <div class="cover"></div>
+</div>
+
+<div class="contain">
+  <div class="test" id="xy"></div>
+  <div class="cover"></div>
+</div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/border-radius/scrollbar-clamping-2-ref.html
@@ -0,0 +1,34 @@
+<!DOCTYPE HTML>
+<html dir="ltr">
+<title>Test of reduction of border-radius for scrollbars (background drawing)</title>
+<style>
+
+.contain { height: 130px; position: relative }
+
+.test {
+  position: absolute;
+  top: 0;
+  left: 0;
+  /* border-width: 2px 4px 8px 10px; */
+  height: 110px;
+  width: 214px;
+  /* -moz-border-radius: 12px / 15px; */
+  background: blue;
+}
+
+</style>
+
+<div class="contain">
+  <!-- scrollbar along bottom -->
+  <div class="test" id="x" style="-moz-border-radius: 12px 12px 6.4px 10px / 15px 15px 8px 12.5px"></div>
+</div>
+
+<div class="contain">
+  <!-- scrollbar along right -->
+  <div class="test" id="y" style="-moz-border-radius: 12px 4px 6.4px 12px / 15px 5px 8px 15px"></div>
+</div>
+
+<div class="contain">
+  <!-- scrollbar along bottom and right -->
+  <div class="test" id="xy" style="-moz-border-radius: 12px 4px 6.4px 10px / 15px 5px 8px 12.5px"></div>
+</div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/border-radius/scrollbar-clamping-2.html
@@ -0,0 +1,46 @@
+<!DOCTYPE HTML>
+<title>Test of reduction of border-radius for scrollbars (background drawing)</title>
+<style>
+
+.contain { height: 130px; position: relative }
+
+.test {
+  position: absolute;
+  top: 0;
+  left: 0;
+  border: medium solid transparent;
+  background: blue;
+  border-width: 2px 4px 8px 10px;
+  height: 100px;
+  width: 200px;
+  -moz-border-radius: 12px / 15px;
+}
+
+.cover {
+  position: absolute;
+  width: 200px;
+  height: 100px;
+  top: 2px;
+  left: 10px;
+  background: blue;
+}
+
+#x, #xy { overflow-x: scroll }
+#y, #xy { overflow-y: scroll }
+
+</style>
+
+<div class="contain">
+  <div class="test" id="x"></div>
+  <div class="cover" style="margin-top: 50px; height: 50px"></div>
+</div>
+
+<div class="contain">
+  <div class="test" id="y"></div>
+  <div class="cover" style="margin-left: 100px; width: 100px"></div>
+</div>
+
+<div class="contain">
+  <div class="test" id="xy"></div>
+  <div class="cover"></div>
+</div>