[CSS Exclusions] shape-inside does not properly handle padding or border
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 7 Feb 2013 20:50:21 +0000 (20:50 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 7 Feb 2013 20:50:21 +0000 (20:50 +0000)
https://bugs.webkit.org/show_bug.cgi?id=102715

Patch by Bear Travis <betravis@adobe.com> on 2013-02-07
Reviewed by David Hyatt.

Source/WebCore:

This patch positions the exclusion shape based on the value of the css box sizing
property. Geometry calculations happen in the shape coordinate space. For layout,
these coordinates are translated to the border-box coordinate system by adding
the appropriate offsets.

Test: fast/exclusions/shape-inside/shape-inside-box-sizing.html

* rendering/ExclusionShapeInfo.cpp:
(WebCore::::computedShape): Pass m_shapeLogicalWidth to the exclusion shape
geometry code.
* rendering/ExclusionShapeInfo.h:
(WebCore::ExclusionShapeInfo::setShapeSize): Adjust block layout dimensions to
shape dimensions when checking to see if the shape geometry must be recalculated.
(WebCore::ExclusionShapeInfo::shapeLogicalTop): Account for layout offsets.
(WebCore::ExclusionShapeInfo::shapeLogicalBottom): Ditto.
(WebCore::ExclusionShapeInfo::shapeLogicalLeft): Ditto.
(WebCore::ExclusionShapeInfo::shapeLogicalRight): Ditto.
(WebCore::ExclusionShapeInfo::logicalTopOffset): Return the offset from the logical
top of the border box to the logical top of the shape.
(WebCore::ExclusionShapeInfo::logicalLeftOffset): Return the offset from the logical
left of the border box to the logical left of the shape.
(ExclusionShapeInfo):
* rendering/ExclusionShapeInsideInfo.cpp:
(WebCore::ExclusionShapeInsideInfo::computeSegmentsForLine): Adjust line top to
be in shape coordinates.
(WebCore::ExclusionShapeInsideInfo::adjustLogicalLineTop): Ditto.
* rendering/ExclusionShapeInsideInfo.h:
(WebCore::ExclusionShapeInsideInfo::lineOverlapsShapeBounds): Use consistent
coordinate system (border box) to test for whether a line overlaps a shape.
(WebCore::ExclusionShapeInsideInfo::logicalLineTop): Include the logical offset
from the border box.
(WebCore::ExclusionShapeInsideInfo::logicalLineBottom): Ditto.

LayoutTests:

Test that borders and padding are properly accounted for when laying out text in
a shape inside.

* fast/exclusions/shape-inside/shape-inside-bottom-edge.html: Modified to no longer
use padding.
* fast/exclusions/shape-inside/shape-inside-box-sizing-expected.html: Added.
* fast/exclusions/shape-inside/shape-inside-box-sizing.html: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@142164 268f45cc-cd09-0410-ab3c-d52691b4dbfc

LayoutTests/ChangeLog
LayoutTests/fast/exclusions/shape-inside/shape-inside-bottom-edge.html
LayoutTests/fast/exclusions/shape-inside/shape-inside-box-sizing-expected.html [new file with mode: 0644]
LayoutTests/fast/exclusions/shape-inside/shape-inside-box-sizing.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/rendering/ExclusionShapeInfo.cpp
Source/WebCore/rendering/ExclusionShapeInfo.h
Source/WebCore/rendering/ExclusionShapeInsideInfo.cpp
Source/WebCore/rendering/ExclusionShapeInsideInfo.h

index a9fcde3b8a6b422811829070f80535fca7425332..193a1d5c6d2007fa5b53ce1950eba63473791617 100644 (file)
@@ -1,3 +1,18 @@
+2013-02-07  Bear Travis  <betravis@adobe.com>
+
+        [CSS Exclusions] shape-inside does not properly handle padding or border
+        https://bugs.webkit.org/show_bug.cgi?id=102715
+
+        Reviewed by David Hyatt.
+
+        Test that borders and padding are properly accounted for when laying out text in
+        a shape inside.
+
+        * fast/exclusions/shape-inside/shape-inside-bottom-edge.html: Modified to no longer
+        use padding.
+        * fast/exclusions/shape-inside/shape-inside-box-sizing-expected.html: Added.
+        * fast/exclusions/shape-inside/shape-inside-box-sizing.html: Added.
+
 2013-01-27  Robert Hogan  <robert@webkit.org>
 
         CSS 2.1 failure: floats-149 fails
index 9839d60a96d257b3753915fd2d432e5558d77411..5c80425ba22525cab95ef61411f9451325813166 100644 (file)
 
 #shape-inside-no-overlap {
     top: 0px;
-    padding-top: 150px;
 }
-
+#shape-inside-no-overlap::before, #shape-inside-overlap::before {
+    display: block;
+    height: 150px;
+    content: ' ';
+}
+#shape-inside-overlap::before {
+    height: 149.9px;
+}
 #shape-background-overlap {
     top: 250px;
 }
 
 #shape-inside-overlap {
     top: 200px;
-    padding-top: 149.9px;
 }
 </style>
 </head>
diff --git a/LayoutTests/fast/exclusions/shape-inside/shape-inside-box-sizing-expected.html b/LayoutTests/fast/exclusions/shape-inside/shape-inside-box-sizing-expected.html
new file mode 100644 (file)
index 0000000..5f99d91
--- /dev/null
@@ -0,0 +1,88 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <style>
+    .border {
+        border-left: 5px solid blue;
+        border-top: 10px solid blue;
+        border-right: 15px solid blue;
+        border-bottom: 20px solid blue;
+        padding: 10px 15px 20px 5px;
+        width: 30px;
+        height: 40px;
+        border-style: solid;
+    }
+    .padding {
+        padding: 20px 30px 40px 10px;
+    }
+    .border-box {
+        -webkit-box-sizing: border-box;
+    }
+    .border-box.border {
+        width: 70px;
+        height: 100px;
+    }
+    .border-box.border.padding {
+        width: 90px;
+        height: 130px;
+    }
+    .shape-inside {
+        font-family: Ahem, sans-serif;
+        font-size: 10px;
+        color: green;
+    }
+    .vertical-lr {
+        -webkit-writing-mode: vertical-lr;
+    }
+    .vertical-rl {
+        -webkit-writing-mode: vertical-rl;
+    }
+    .horizontal-tb {
+        -webkit-writing-mode: horizontal-tb;
+    }
+    </style>
+</head>
+<body>
+    <p>These tests check that shape inside correctly offsets from the correct box-sizing box. They require the Ahem font.</p>
+    <p>The following tests check writing modes on a box with a 5 10 15 20px blue border, and 5 10 15 20px shape offsets.</p>
+    <div class='border shape-inside horizontal-tb'>
+    xxx xxx xxx xxx
+    </div>
+    <div class='border shape-inside vertical-lr'>
+        xxxx xxxx xxxx
+    </div>
+    <div class='border shape-inside vertical-rl'>
+        xxxx xxxx xxxx
+    </div>
+    <p>The following tests should look the same, but use box-sizing: border-box.</p>
+    <div class='border-box border shape-inside horizontal-tb'>
+    xxx xxx xxx xxx
+    </div>
+    <div class='border-box border shape-inside vertical-lr'>
+        xxxx xxxx xxxx
+    </div>
+    <div class='border-box border shape-inside vertical-rl'>
+        xxxx xxxx xxxx
+    </div>
+    <p>The following tests add 5 10 15 20px of padding.</p>
+    <div class='border padding shape-inside horizontal-tb'>
+    xxx xxx xxx xxx
+    </div>
+    <div class='border padding shape-inside vertical-lr'>
+        xxxx xxxx xxxx
+    </div>
+    <div class='border padding shape-inside vertical-rl'>
+        xxxx xxxx xxxx
+    </div>
+    <p>The following tests should look the same, but use box-sizing: border-box.</p>
+    <div class='border-box border padding shape-inside horizontal-tb'>
+    xxx xxx xxx xxx
+    </div>
+    <div class='border-box border padding shape-inside vertical-lr'>
+        xxxx xxxx xxxx
+    </div>
+    <div class='border-box border padding shape-inside vertical-rl'>
+        xxxx xxxx xxxx
+    </div>
+</body>
+</html>
\ No newline at end of file
diff --git a/LayoutTests/fast/exclusions/shape-inside/shape-inside-box-sizing.html b/LayoutTests/fast/exclusions/shape-inside/shape-inside-box-sizing.html
new file mode 100644 (file)
index 0000000..ad5452e
--- /dev/null
@@ -0,0 +1,98 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <script>
+    if (window.internals)
+        window.internals.settings.setCSSExclusionsEnabled(true);
+    </script>
+    <style>
+    .border {
+        border-left: 5px solid blue;
+        border-top: 10px solid blue;
+        border-right: 15px solid blue;
+        border-bottom: 20px solid blue;
+        width: 50px;
+        height: 70px;
+        border-style: solid;
+    }
+    .padding {
+        padding: 10px 15px 20px 5px;
+    }
+    .border-box {
+        -webkit-box-sizing: border-box;
+    }
+    .border-box.border {
+        width: 70px;
+        height: 100px;
+    }
+    .border-box.border.padding {
+        width: 90px;
+        height: 130px;
+    }
+    .shape-inside {
+        -webkit-shape-inside: rectangle(5px, 10px, 30px, 40px);
+        font-family: Ahem, sans-serif;
+        font-size: 10px;
+        color: green;
+    }
+    .border-box.shape-inside {
+        -webkit-shape-inside: rectangle(10px, 20px, 30px, 40px);
+    }
+    .border-box.border.padding.shape-inside {
+        -webkit-shape-inside: rectangle(15px, 30px, 30px, 40px);
+    }
+    .vertical-lr {
+        -webkit-writing-mode: vertical-lr;
+    }
+    .vertical-rl {
+        -webkit-writing-mode: vertical-rl;
+    }
+    .horizontal-tb {
+        -webkit-writing-mode: horizontal-tb;
+    }
+    </style>
+</head>
+<body>
+    <p>These tests check that shape inside correctly offsets from the correct box-sizing box. They require the Ahem font.</p>
+    <p>The following tests check writing modes on a box with a 5 10 15 20px blue border, and 5 10 15 20px shape offsets.</p>
+    <div class='border shape-inside horizontal-tb'>
+    xxx xxx xxx xxx
+    </div>
+    <div class='border shape-inside vertical-lr'>
+        xxxx xxxx xxxx
+    </div>
+    <div class='border shape-inside vertical-rl'>
+        xxxx xxxx xxxx
+    </div>
+    <p>The following tests should look the same, but use box-sizing: border-box.</p>
+    <div class='border-box border shape-inside horizontal-tb'>
+    xxx xxx xxx xxx
+    </div>
+    <div class='border-box border shape-inside vertical-lr'>
+        xxxx xxxx xxxx
+    </div>
+    <div class='border-box border shape-inside vertical-rl'>
+        xxxx xxxx xxxx
+    </div>
+    <p>The following tests add 5 10 15 20px of padding.</p>
+    <div class='border padding shape-inside horizontal-tb'>
+    xxx xxx xxx xxx
+    </div>
+    <div class='border padding shape-inside vertical-lr'>
+        xxxx xxxx xxxx
+    </div>
+    <div class='border padding shape-inside vertical-rl'>
+        xxxx xxxx xxxx
+    </div>
+    <p>The following tests should look the same, but use box-sizing: border-box.</p>
+    <div class='border-box border padding shape-inside horizontal-tb'>
+    xxx xxx xxx xxx
+    </div>
+    <div class='border-box border padding shape-inside vertical-lr'>
+        xxxx xxxx xxxx
+    </div>
+    <div class='border-box border padding shape-inside vertical-rl'>
+        xxxx xxxx xxxx
+    </div>
+</body>
+</html>
\ No newline at end of file
index 0fc4b2a0934743db56c0554e848574eb930873a7..7c5657135b26f1bf2b9500ae72719e18fab711e5 100644 (file)
@@ -1,3 +1,43 @@
+2013-02-07  Bear Travis  <betravis@adobe.com>
+
+        [CSS Exclusions] shape-inside does not properly handle padding or border
+        https://bugs.webkit.org/show_bug.cgi?id=102715
+
+        Reviewed by David Hyatt.
+
+        This patch positions the exclusion shape based on the value of the css box sizing
+        property. Geometry calculations happen in the shape coordinate space. For layout,
+        these coordinates are translated to the border-box coordinate system by adding
+        the appropriate offsets.
+
+        Test: fast/exclusions/shape-inside/shape-inside-box-sizing.html
+
+        * rendering/ExclusionShapeInfo.cpp:
+        (WebCore::::computedShape): Pass m_shapeLogicalWidth to the exclusion shape
+        geometry code.
+        * rendering/ExclusionShapeInfo.h:
+        (WebCore::ExclusionShapeInfo::setShapeSize): Adjust block layout dimensions to
+        shape dimensions when checking to see if the shape geometry must be recalculated.
+        (WebCore::ExclusionShapeInfo::shapeLogicalTop): Account for layout offsets.
+        (WebCore::ExclusionShapeInfo::shapeLogicalBottom): Ditto.
+        (WebCore::ExclusionShapeInfo::shapeLogicalLeft): Ditto.
+        (WebCore::ExclusionShapeInfo::shapeLogicalRight): Ditto.
+        (WebCore::ExclusionShapeInfo::logicalTopOffset): Return the offset from the logical
+        top of the border box to the logical top of the shape.
+        (WebCore::ExclusionShapeInfo::logicalLeftOffset): Return the offset from the logical
+        left of the border box to the logical left of the shape.
+        (ExclusionShapeInfo):
+        * rendering/ExclusionShapeInsideInfo.cpp:
+        (WebCore::ExclusionShapeInsideInfo::computeSegmentsForLine): Adjust line top to
+        be in shape coordinates.
+        (WebCore::ExclusionShapeInsideInfo::adjustLogicalLineTop): Ditto.
+        * rendering/ExclusionShapeInsideInfo.h:
+        (WebCore::ExclusionShapeInsideInfo::lineOverlapsShapeBounds): Use consistent
+        coordinate system (border box) to test for whether a line overlaps a shape.
+        (WebCore::ExclusionShapeInsideInfo::logicalLineTop): Include the logical offset
+        from the border box.
+        (WebCore::ExclusionShapeInsideInfo::logicalLineBottom): Ditto.
+
 2013-02-07  Benjamin Poulain  <bpoulain@apple.com>
 
         Upstream iOS isWebThread() and isUIThread()
index fe1a782a1e6b7b6660ed0fc38c9ce6f71eae8233..35ad41c11b71c2b54459e1e93519302bdbeb5b60 100644 (file)
@@ -49,7 +49,7 @@ const ExclusionShape* ExclusionShapeInfo<RenderType, shapeGetter>::computedShape
 
     ASSERT(shape);
 
-    m_shape = ExclusionShape::createExclusionShape(shape, m_logicalWidth, m_logicalHeight, m_renderer->style()->writingMode());
+    m_shape = ExclusionShape::createExclusionShape(shape, m_shapeLogicalWidth, m_shapeLogicalHeight, m_renderer->style()->writingMode());
     ASSERT(m_shape);
     return m_shape.get();
 }
index bd0a66aee4ffdac366cd27432d433dd07540f4c9..ad92c620a027370812c5c4a6c2a4e7121c140e60 100644 (file)
@@ -72,17 +72,22 @@ public:
 
     void setShapeSize(LayoutUnit logicalWidth, LayoutUnit logicalHeight)
     {
-        if (m_logicalWidth == logicalWidth && m_logicalHeight == logicalHeight)
+        if (m_renderer->style()->boxSizing() == CONTENT_BOX) {
+            logicalWidth -= m_renderer->borderAndPaddingLogicalWidth();
+            logicalHeight -= m_renderer->borderAndPaddingLogicalHeight();
+        }
+
+        if (m_shapeLogicalWidth == logicalWidth && m_shapeLogicalHeight == logicalHeight)
             return;
         dirtyShapeSize();
-        m_logicalWidth = logicalWidth;
-        m_logicalHeight = logicalHeight;
+        m_shapeLogicalWidth = logicalWidth;
+        m_shapeLogicalHeight = logicalHeight;
     }
 
-    LayoutUnit shapeLogicalTop() const { return floatLogicalTopToLayoutUnit(computedShape()->shapeLogicalBoundingBox().y()); }
-    LayoutUnit shapeLogicalBottom() const { return floatLogicalBottomToLayoutUnit(computedShape()->shapeLogicalBoundingBox().maxY()); }
-    LayoutUnit shapeLogicalLeft() const { return computedShape()->shapeLogicalBoundingBox().x(); }
-    LayoutUnit shapeLogicalRight() const { return computedShape()->shapeLogicalBoundingBox().y(); }
+    LayoutUnit shapeLogicalTop() const { return floatLogicalTopToLayoutUnit(computedShape()->shapeLogicalBoundingBox().y()) + logicalTopOffset(); }
+    LayoutUnit shapeLogicalBottom() const { return floatLogicalBottomToLayoutUnit(computedShape()->shapeLogicalBoundingBox().maxY()) + logicalTopOffset(); }
+    LayoutUnit shapeLogicalLeft() const { return computedShape()->shapeLogicalBoundingBox().x() + logicalLeftOffset(); }
+    LayoutUnit shapeLogicalRight() const { return computedShape()->shapeLogicalBoundingBox().y() + logicalLeftOffset(); }
     LayoutUnit shapeLogicalWidth() const { return computedShape()->shapeLogicalBoundingBox().width(); }
     LayoutUnit shapeLogicalHeight() const { return computedShape()->shapeLogicalBoundingBox().height(); }
 
@@ -98,11 +103,14 @@ protected:
     LayoutUnit floatLogicalTopToLayoutUnit(float logicalTop) const { return LayoutUnit::fromFloatCeil(logicalTop); }
     LayoutUnit floatLogicalBottomToLayoutUnit(float logicalBottom) const { return LayoutUnit::fromFloatFloor(logicalBottom); }
 
+    LayoutUnit logicalTopOffset() const { return m_renderer->style()->boxSizing() == CONTENT_BOX ? m_renderer->borderBefore() + m_renderer->paddingBefore() : LayoutUnit(); }
+    LayoutUnit logicalLeftOffset() const { return m_renderer->style()->boxSizing() == CONTENT_BOX ? m_renderer->borderStart() + m_renderer->paddingStart() : LayoutUnit(); }
+
 private:
     mutable OwnPtr<ExclusionShape> m_shape;
 
-    LayoutUnit m_logicalWidth;
-    LayoutUnit m_logicalHeight;
+    LayoutUnit m_shapeLogicalWidth;
+    LayoutUnit m_shapeLogicalHeight;
     const RenderType* m_renderer;
 };
 }
index 467154cf697f5f937541a5f77fca84982551ffd6..3480196b556a87d2389368038fdd804b77990510 100644 (file)
@@ -38,13 +38,18 @@ namespace WebCore {
 bool ExclusionShapeInsideInfo::computeSegmentsForLine(LayoutUnit lineTop, LayoutUnit lineHeight)
 {
     ASSERT(lineHeight >= 0);
-    m_lineTop = lineTop;
+    m_shapeLineTop = lineTop - logicalTopOffset();
     m_lineHeight = lineHeight;
     m_segments.clear();
     m_segmentRanges.clear();
 
-    if (lineOverlapsShapeBounds()) {
-        computedShape()->getIncludedIntervals(lineTop, std::min(lineHeight, shapeLogicalBottom() - lineTop), m_segments);
+    if (lineOverlapsShapeBounds())
+        computedShape()->getIncludedIntervals(m_shapeLineTop, std::min(m_lineHeight, shapeLogicalBottom() - lineTop), m_segments);
+
+    LayoutUnit logicalLeftOffset = this->logicalLeftOffset();
+    for (size_t i = 0; i < m_segments.size(); i++) {
+        m_segments[i].logicalLeft += logicalLeftOffset;
+        m_segments[i].logicalRight += logicalLeftOffset;
     }
     return m_segments.size();
 }
@@ -52,14 +57,14 @@ bool ExclusionShapeInsideInfo::computeSegmentsForLine(LayoutUnit lineTop, Layout
 bool ExclusionShapeInsideInfo::adjustLogicalLineTop(float minSegmentWidth)
 {
     const ExclusionShape* shape = computedShape();
-    if (!shape || m_lineHeight <= 0 || m_lineTop > shapeLogicalBottom())
+    if (!shape || m_lineHeight <= 0 || logicalLineTop() > shapeLogicalBottom())
         return false;
 
     float floatNewLineTop;
-    if (shape->firstIncludedIntervalLogicalTop(m_lineTop, FloatSize(minSegmentWidth, m_lineHeight), floatNewLineTop)) {
+    if (shape->firstIncludedIntervalLogicalTop(m_shapeLineTop, FloatSize(minSegmentWidth, m_lineHeight), floatNewLineTop)) {
         LayoutUnit newLineTop = floatLogicalTopToLayoutUnit(floatNewLineTop);
-        if (newLineTop > m_lineTop) {
-            m_lineTop = newLineTop;
+        if (newLineTop > m_shapeLineTop) {
+            m_shapeLineTop = newLineTop;
             return true;
         }
     }
index 2b1877257992a9b856478bac406484f0943f49d7..7b71b7396c3c707c70c711ec6acf051dbd757229 100644 (file)
@@ -63,7 +63,7 @@ public:
         BasicShape* shape = (shapeValue && shapeValue->type() == ExclusionShapeValue::SHAPE) ? shapeValue->shape() : 0;
         return shape && (shape->type() == BasicShape::BASIC_SHAPE_RECTANGLE || shape->type() == BasicShape::BASIC_SHAPE_POLYGON);
     }
-    bool lineOverlapsShapeBounds() const { return m_lineTop < shapeLogicalBottom() && m_lineTop + m_lineHeight >= shapeLogicalTop(); }
+    bool lineOverlapsShapeBounds() const { return logicalLineTop() < shapeLogicalBottom() && logicalLineBottom() >= shapeLogicalTop(); }
 
     bool hasSegments() const
     {
@@ -85,12 +85,13 @@ public:
     }
     bool computeSegmentsForLine(LayoutUnit lineTop, LayoutUnit lineHeight);
     bool adjustLogicalLineTop(float minSegmentWidth);
-    LayoutUnit logicalLineTop() const { return m_lineTop; }
+    LayoutUnit logicalLineTop() const { return m_shapeLineTop + logicalTopOffset(); }
+    LayoutUnit logicalLineBottom() const { return m_shapeLineTop + m_lineHeight + logicalTopOffset(); }
 
 private:
     ExclusionShapeInsideInfo(const RenderBlock* renderer) : ExclusionShapeInfo<RenderBlock, &RenderStyle::shapeInside>(renderer) { }
 
-    LayoutUnit m_lineTop;
+    LayoutUnit m_shapeLineTop;
     LayoutUnit m_lineHeight;
 
     SegmentList m_segments;