Correct overflow propagation in BTT and RTL writing-modes

Overflow rectangles are not quite physical, not quite logical. This
means that we cannot use clientBoxRect() directly to represent a
rectangle that expresses exactly no overflow. This rectangle is the
padding box (relative to the border box) in vertical-lr and
horizontal-tb, but the block-direction borders need to be flipped in
vertical-rl and horizontal-bt.

fast/multicol/vertical-rl/rules-with-border-before.html now needs a
rebaseline, because it now renders differently, but correctly.

R=
BUG=236326

Review URL: https://chromiumcodereview.appspot.com/18720003

git-svn-id: svn://svn.chromium.org/blink/trunk@154945 bbb929c8-8fbe-4397-9dbb-9b2b20218538
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index 9591b6c..a936f92 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -507,6 +507,7 @@
 
 webkit.org/b/110296 [ Linux MountainLion ] fast/multicol/newmulticol/column-rules-fixed-height.html [ ImageOnlyFailure ]
 crbug.com/250935 [ Win ] fast/multicol/shrink-to-column-height-for-pagination.html [ ImageOnlyFailure Pass ]
+crbug.com/236326 fast/multicol/vertical-rl/rules-with-border-before.html [ NeedsRebaseline ]
 
 # There seems to be some endemic MountainLion reftest failure.
 # In theory, this shouldn't happen with reftests. Indicates a likely genuine
diff --git a/third_party/WebKit/LayoutTests/fast/css/overflow-btt-border-after-expected.txt b/third_party/WebKit/LayoutTests/fast/css/overflow-btt-border-after-expected.txt
new file mode 100644
index 0000000..24b6717
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/css/overflow-btt-border-after-expected.txt
@@ -0,0 +1,4 @@
+The word 'HEST' should be seen below, and there should be no red.
+
+HEST
+PASS
diff --git a/third_party/WebKit/LayoutTests/fast/css/overflow-btt-border-after.html b/third_party/WebKit/LayoutTests/fast/css/overflow-btt-border-after.html
new file mode 100644
index 0000000..c07c6256
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/css/overflow-btt-border-after.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>Border-after in horizontal-bt mode, non-visible overflow</title>
+    <style>
+      body { color:black; background:white; }
+      #container {
+        -webkit-writing-mode: horizontal-bt;
+        overflow:scroll;
+        height:100px;
+        border-top:150px solid white;
+        background:red;
+      }
+      #inner { height:200px; background:white; }
+    </style>
+    <script src="../../resources/check-layout.js"></script>
+  </head>
+  <body onload="checkLayout('#container'); inner.scrollIntoView();">
+    <!-- We have to set the scroll position, because of https://bugs.webkit.org/show_bug.cgi?id=76129
+         This is just to make it look good; checkLayout() doesn't care. -->
+    <p>The word 'HEST' should be seen below, and there should be no red.</p>
+    <div id="container" data-expected-scroll-height="200">
+      <div id="inner">HEST</div>
+    </div>
+  </body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/fast/css/overflow-rtl-border-after-expected.txt b/third_party/WebKit/LayoutTests/fast/css/overflow-rtl-border-after-expected.txt
new file mode 100644
index 0000000..24b6717
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/css/overflow-rtl-border-after-expected.txt
@@ -0,0 +1,4 @@
+The word 'HEST' should be seen below, and there should be no red.
+
+HEST
+PASS
diff --git a/third_party/WebKit/LayoutTests/fast/css/overflow-rtl-border-after.html b/third_party/WebKit/LayoutTests/fast/css/overflow-rtl-border-after.html
new file mode 100644
index 0000000..7f2066e2
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/css/overflow-rtl-border-after.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>Border-after in vertical-rl mode, non-visible overflow</title>
+    <style>
+      body { color:black; background:white; }
+      #container {
+        -webkit-writing-mode: vertical-rl;
+        writing-mode: vertical-rl;
+        overflow:scroll;
+        width:100px;
+        border-left:150px solid white;
+        background:red;
+      }
+      #inner { width:200px; background:white; }
+    </style>
+    <script src="../../resources/check-layout.js"></script>
+  </head>
+  <body onload="checkLayout('#container'); container.scrollLeft = 1000;">
+    <!-- We have to set the scroll position, because of https://bugs.webkit.org/show_bug.cgi?id=76129
+         Furthermore, inner.scrollIntoView() doesn't work, so just set some insane left value.
+         This is just to make it look good; checkLayout() doesn't care. -->
+    <p>The word 'HEST' should be seen below, and there should be no red.</p>
+    <div id="container" data-expected-scroll-width="200">
+      <div id="inner">HEST</div>
+    </div>
+  </body>
+</html>
diff --git a/third_party/WebKit/Source/core/rendering/RenderBlock.cpp b/third_party/WebKit/Source/core/rendering/RenderBlock.cpp
index 117dad0..c7edc42 100644
--- a/third_party/WebKit/Source/core/rendering/RenderBlock.cpp
+++ b/third_party/WebKit/Source/core/rendering/RenderBlock.cpp
@@ -1784,7 +1784,7 @@
         // When we have overflow clip, propagate the original spillout since it will include collapsed bottom margins
         // and bottom padding.  Set the axis we don't care about to be 1, since we want this overflow to always
         // be considered reachable.
-        LayoutRect clientRect(clientBoxRect());
+        LayoutRect clientRect(noOverflowRect());
         LayoutRect rectToApply;
         if (isHorizontalWritingMode())
             rectToApply = LayoutRect(clientRect.x(), clientRect.y(), 1, max<LayoutUnit>(0, oldClientAfterEdge - clientRect.y()));
@@ -1798,7 +1798,7 @@
     // Allow our overflow to catch cases where the caret in an empty editable element with negative text indent needs to get painted.
     LayoutUnit textIndent = textIndentOffset();
     if (textIndent < 0) {
-        LayoutRect clientRect(clientBoxRect());
+        LayoutRect clientRect(noOverflowRect());
         LayoutRect rectToApply = LayoutRect(clientRect.x() + min<LayoutUnit>(0, textIndent), clientRect.y(), clientRect.width() - min<LayoutUnit>(0, textIndent), clientRect.height());
         addVisualOverflow(rectToApply);
     }
diff --git a/third_party/WebKit/Source/core/rendering/RenderBox.cpp b/third_party/WebKit/Source/core/rendering/RenderBox.cpp
index 12360edf..5a7811c 100644
--- a/third_party/WebKit/Source/core/rendering/RenderBox.cpp
+++ b/third_party/WebKit/Source/core/rendering/RenderBox.cpp
@@ -4221,7 +4221,7 @@
 
 void RenderBox::addLayoutOverflow(const LayoutRect& rect)
 {
-    LayoutRect clientBox = clientBoxRect();
+    LayoutRect clientBox = noOverflowRect();
     if (clientBox.contains(rect) || rect.isEmpty())
         return;
     
@@ -4276,7 +4276,7 @@
         return;
         
     if (!m_overflow)
-        m_overflow = adoptPtr(new RenderOverflow(clientBoxRect(), borderBox));
+        m_overflow = adoptPtr(new RenderOverflow(noOverflowRect(), borderBox));
     
     m_overflow->addVisualOverflow(rect);
 }
@@ -4285,13 +4285,14 @@
 {
     if (!m_overflow)
         return;
-    
-    if (visualOverflowRect() == borderBoxRect()) {
+
+    LayoutRect noOverflowRect = this->noOverflowRect();
+    if (visualOverflowRect() == noOverflowRect) {
         m_overflow.clear();
         return;
     }
-    
-    m_overflow->setLayoutOverflow(borderBoxRect());
+
+    m_overflow->setLayoutOverflow(noOverflowRect);
 }
 
 inline static bool percentageLogicalHeightIsResolvable(const RenderBox* box)
@@ -4470,6 +4471,34 @@
     return rect;
 }
 
+LayoutRect RenderBox::noOverflowRect() const
+{
+    // Because of the special coodinate system used for overflow rectangles and many other
+    // rectangles (not quite logical, not quite physical), we need to flip the block progression
+    // coordinate in vertical-rl and horizontal-bt writing modes. In other words, the rectangle
+    // returned is physical, except for the block direction progression coordinate (y in horizontal
+    // writing modes, x in vertical writing modes), which is always "logical top". Apart from the
+    // flipping, this method does the same as clientBoxRect().
+
+    LayoutUnit left = borderLeft();
+    LayoutUnit top = borderTop();
+    LayoutUnit right = borderRight();
+    LayoutUnit bottom = borderBottom();
+    LayoutRect rect(left, top, width() - left - right, height() - top - bottom);
+    flipForWritingMode(rect);
+    // Subtract space occupied by scrollbars. Order is important here: first flip, then subtract
+    // scrollbars. This may seem backwards and weird, since one would think that a horizontal
+    // scrollbar at the physical bottom in horizontal-bt ought to be at the logical top (physical
+    // bottom), between the logical top (physical bottom) border and the logical top (physical
+    // bottom) padding. But this is how the rest of the code expects us to behave. This is highly
+    // related to https://bugs.webkit.org/show_bug.cgi?id=76129
+    // FIXME: when the above mentioned bug is fixed, it should hopefully be possible to call
+    // clientBoxRect() or paddingBoxRect() in this method, rather than fiddling with the edges on
+    // our own.
+    rect.contract(verticalScrollbarWidth(), horizontalScrollbarHeight());
+    return rect;
+}
+
 LayoutRect RenderBox::overflowRectForPaintRejection() const
 {
     LayoutRect overflowRect = visualOverflowRect();
diff --git a/third_party/WebKit/Source/core/rendering/RenderBox.h b/third_party/WebKit/Source/core/rendering/RenderBox.h
index ad38279..ca05984 100644
--- a/third_party/WebKit/Source/core/rendering/RenderBox.h
+++ b/third_party/WebKit/Source/core/rendering/RenderBox.h
@@ -173,7 +173,8 @@
     // For horizontal-tb and vertical-lr they will match physical directions, but for horizontal-bt and vertical-rl, the top/bottom and left/right
     // respectively are flipped when compared to their physical counterparts.  For example minX is on the left in vertical-lr,
     // but it is on the right in vertical-rl.
-    LayoutRect layoutOverflowRect() const { return m_overflow ? m_overflow->layoutOverflowRect() : clientBoxRect(); }
+    LayoutRect noOverflowRect() const;
+    LayoutRect layoutOverflowRect() const { return m_overflow ? m_overflow->layoutOverflowRect() : noOverflowRect(); }
     IntRect pixelSnappedLayoutOverflowRect() const { return pixelSnappedIntRect(layoutOverflowRect()); }
     LayoutSize maxLayoutOverflow() const { return LayoutSize(layoutOverflowRect().maxX(), layoutOverflowRect().maxY()); }
     LayoutUnit logicalLeftLayoutOverflow() const { return style()->isHorizontalWritingMode() ? layoutOverflowRect().x() : layoutOverflowRect().y(); }