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 id15552
push userdbaron@mozilla.com
push dateTue, 07 Sep 2010 22:22:17 +0000
treeherdermozilla-central@ebabfab36a6f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs459144
milestone2.0b6pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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>