Simplify the ShapeOutsideInfo and ShapeInfo interfaces
[WebKit-https.git] / Source / WebCore / rendering / LineWidth.cpp
1 /*
2  * Copyright (C) 2013 Adobe Systems Incorporated. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1. Redistributions of source code must retain the above
9  *    copyright notice, this list of conditions and the following
10  *    disclaimer.
11  * 2. Redistributions in binary form must reproduce the above
12  *    copyright notice, this list of conditions and the following
13  *    disclaimer in the documentation and/or other materials
14  *    provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
19  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
20  * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
21  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
25  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
27  * OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30 #include "config.h"
31 #include "LineWidth.h"
32
33 namespace WebCore {
34
35 LineWidth::LineWidth(RenderBlock& block, bool isFirstLine, IndentTextOrNot shouldIndentText)
36     : m_block(block)
37     , m_uncommittedWidth(0)
38     , m_committedWidth(0)
39     , m_overhangWidth(0)
40     , m_trailingWhitespaceWidth(0)
41     , m_trailingCollapsedWhitespaceWidth(0)
42     , m_left(0)
43     , m_right(0)
44     , m_availableWidth(0)
45 #if ENABLE(CSS_SHAPES)
46     , m_segment(0)
47 #endif
48     , m_isFirstLine(isFirstLine)
49     , m_shouldIndentText(shouldIndentText)
50 {
51 #if ENABLE(CSS_SHAPES)
52     updateCurrentShapeSegment();
53 #endif
54     updateAvailableWidth();
55 }
56
57 bool LineWidth::fitsOnLine(bool ignoringTrailingSpace) const
58 {
59     return ignoringTrailingSpace ? fitsOnLineExcludingTrailingCollapsedWhitespace() : fitsOnLineIncludingExtraWidth(0);
60 }
61
62 bool LineWidth::fitsOnLineIncludingExtraWidth(float extra) const
63 {
64     return currentWidth() + extra <= m_availableWidth;
65 }
66
67 bool LineWidth::fitsOnLineExcludingTrailingWhitespace(float extra) const
68 {
69     return currentWidth() - m_trailingWhitespaceWidth + extra <= m_availableWidth;
70 }
71
72 void LineWidth::updateAvailableWidth(LayoutUnit replacedHeight)
73 {
74     LayoutUnit height = m_block.logicalHeight();
75     LayoutUnit logicalHeight = logicalHeightForLine(&m_block, m_isFirstLine, replacedHeight);
76     m_left = m_block.logicalLeftOffsetForLine(height, shouldIndentText(), logicalHeight);
77     m_right = m_block.logicalRightOffsetForLine(height, shouldIndentText(), logicalHeight);
78
79 #if ENABLE(CSS_SHAPES)
80     if (m_segment) {
81         m_left = std::max<float>(m_segment->logicalLeft, m_left);
82         m_right = std::min<float>(m_segment->logicalRight, m_right);
83     }
84 #endif
85
86     computeAvailableWidthFromLeftAndRight();
87 }
88
89 void LineWidth::shrinkAvailableWidthForNewFloatIfNeeded(FloatingObject* newFloat)
90 {
91     LayoutUnit height = m_block.logicalHeight();
92     if (height < newFloat->logicalTop(m_block.isHorizontalWritingMode()) || height >= newFloat->logicalBottom(m_block.isHorizontalWritingMode()))
93         return;
94
95 #if ENABLE(CSS_SHAPES)
96     // When floats with shape outside are stacked, the floats are positioned based on the margin box of the float,
97     // not the shape's contour. Since we computed the width based on the shape contour when we added the float,
98     // when we add a subsequent float on the same line, we need to undo the shape delta in order to position
99     // based on the margin box. In order to do this, we need to walk back through the floating object list to find
100     // the first previous float that is on the same side as our newFloat.
101     ShapeOutsideInfo* previousShapeOutsideInfo = 0;
102     const FloatingObjectSet& floatingObjectSet = m_block.m_floatingObjects->set();
103     FloatingObjectSetIterator it = floatingObjectSet.end();
104     FloatingObjectSetIterator begin = floatingObjectSet.begin();
105     for (--it; it != begin; --it) {
106         FloatingObject* previousFloat = *it;
107         if (previousFloat != newFloat && previousFloat->type() == newFloat->type()) {
108             previousShapeOutsideInfo = previousFloat->renderer()->shapeOutsideInfo();
109             if (previousShapeOutsideInfo)
110                 previousShapeOutsideInfo->updateDeltasForContainingBlockLine(&m_block, previousFloat, m_block.logicalHeight(), logicalHeightForLine(&m_block, m_isFirstLine));
111             break;
112         }
113     }
114
115     ShapeOutsideInfo* shapeOutsideInfo = newFloat->renderer()->shapeOutsideInfo();
116     if (shapeOutsideInfo)
117         shapeOutsideInfo->updateDeltasForContainingBlockLine(&m_block, newFloat, m_block.logicalHeight(), logicalHeightForLine(&m_block, m_isFirstLine));
118 #endif
119
120     if (newFloat->type() == FloatingObject::FloatLeft) {
121         float newLeft = newFloat->logicalRight(m_block.isHorizontalWritingMode());
122 #if ENABLE(CSS_SHAPES)
123         if (previousShapeOutsideInfo)
124             newLeft -= previousShapeOutsideInfo->rightMarginBoxDelta();
125         if (shapeOutsideInfo)
126             newLeft += shapeOutsideInfo->rightMarginBoxDelta();
127 #endif
128
129         if (shouldIndentText() && m_block.style()->isLeftToRightDirection())
130             newLeft += floorToInt(m_block.textIndentOffset());
131         m_left = std::max<float>(m_left, newLeft);
132     } else {
133         float newRight = newFloat->logicalLeft(m_block.isHorizontalWritingMode());
134 #if ENABLE(CSS_SHAPES)
135         if (previousShapeOutsideInfo)
136             newRight -= previousShapeOutsideInfo->leftMarginBoxDelta();
137         if (shapeOutsideInfo)
138             newRight += shapeOutsideInfo->leftMarginBoxDelta();
139 #endif
140
141         if (shouldIndentText() && !m_block.style()->isLeftToRightDirection())
142             newRight -= floorToInt(m_block.textIndentOffset());
143         m_right = std::min<float>(m_right, newRight);
144     }
145
146     computeAvailableWidthFromLeftAndRight();
147 }
148
149 void LineWidth::commit()
150 {
151     m_committedWidth += m_uncommittedWidth;
152     m_uncommittedWidth = 0;
153 }
154
155 void LineWidth::applyOverhang(RenderRubyRun* rubyRun, RenderObject* startRenderer, RenderObject* endRenderer)
156 {
157     int startOverhang;
158     int endOverhang;
159     rubyRun->getOverhang(m_isFirstLine, startRenderer, endRenderer, startOverhang, endOverhang);
160
161     startOverhang = std::min<int>(startOverhang, m_committedWidth);
162     m_availableWidth += startOverhang;
163
164     endOverhang = std::max(std::min<int>(endOverhang, m_availableWidth - currentWidth()), 0);
165     m_availableWidth += endOverhang;
166     m_overhangWidth += startOverhang + endOverhang;
167 }
168
169 void LineWidth::fitBelowFloats()
170 {
171     ASSERT(!m_committedWidth);
172     ASSERT(!fitsOnLine());
173
174     LayoutUnit floatLogicalBottom;
175     LayoutUnit lastFloatLogicalBottom = m_block.logicalHeight();
176     float newLineWidth = m_availableWidth;
177     float newLineLeft = m_left;
178     float newLineRight = m_right;
179     while (true) {
180         floatLogicalBottom = m_block.nextFloatLogicalBottomBelow(lastFloatLogicalBottom);
181         if (floatLogicalBottom <= lastFloatLogicalBottom)
182             break;
183
184         newLineLeft = m_block.logicalLeftOffsetForLine(floatLogicalBottom, shouldIndentText());
185         newLineRight = m_block.logicalRightOffsetForLine(floatLogicalBottom, shouldIndentText());
186         newLineWidth = max(0.0f, newLineRight - newLineLeft);
187         lastFloatLogicalBottom = floatLogicalBottom;
188         if (newLineWidth >= m_uncommittedWidth)
189             break;
190     }
191
192     if (newLineWidth > m_availableWidth) {
193         m_block.setLogicalHeight(lastFloatLogicalBottom);
194         m_availableWidth = newLineWidth + m_overhangWidth;
195         m_left = newLineLeft;
196         m_right = newLineRight;
197     }
198 }
199
200 void LineWidth::setTrailingWhitespaceWidth(float collapsedWhitespace, float borderPaddingMargin)
201 {
202     m_trailingCollapsedWhitespaceWidth = collapsedWhitespace;
203     m_trailingWhitespaceWidth = collapsedWhitespace + borderPaddingMargin;
204 }
205
206 #if ENABLE(CSS_SHAPES)
207 void LineWidth::updateCurrentShapeSegment()
208 {
209     if (ShapeInsideInfo* shapeInsideInfo = m_block.layoutShapeInsideInfo())
210         m_segment = shapeInsideInfo->currentSegment();
211 }
212 #endif
213
214 void LineWidth::computeAvailableWidthFromLeftAndRight()
215 {
216     m_availableWidth = max(0.0f, m_right - m_left) + m_overhangWidth;
217 }
218
219 bool LineWidth::fitsOnLineExcludingTrailingCollapsedWhitespace() const
220 {
221     return currentWidth() - m_trailingCollapsedWhitespaceWidth <= m_availableWidth;
222 }
223
224 }