Remove CSS_SHAPES feature definition. This should always be on.
[WebKit-https.git] / Source / WebCore / rendering / line / 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 #include "RenderBlockFlow.h"
34 #include "RenderRubyRun.h"
35
36 namespace WebCore {
37
38 LineWidth::LineWidth(RenderBlockFlow& block, bool isFirstLine, IndentTextOrNot shouldIndentText)
39     : m_block(block)
40     , m_isFirstLine(isFirstLine)
41     , m_shouldIndentText(shouldIndentText)
42 {
43     updateAvailableWidth();
44 }
45
46 bool LineWidth::fitsOnLine(bool ignoringTrailingSpace) const
47 {
48     return ignoringTrailingSpace ? fitsOnLineExcludingTrailingCollapsedWhitespace() : fitsOnLineIncludingExtraWidth(0);
49 }
50
51 bool LineWidth::fitsOnLineIncludingExtraWidth(float extra) const
52 {
53     return currentWidth() + extra <= m_availableWidth;
54 }
55
56 bool LineWidth::fitsOnLineExcludingTrailingWhitespace(float extra) const
57 {
58     return currentWidth() - m_trailingWhitespaceWidth + extra <= m_availableWidth;
59 }
60
61 void LineWidth::updateAvailableWidth(LayoutUnit replacedHeight)
62 {
63     LayoutUnit height = m_block.logicalHeight();
64     LayoutUnit logicalHeight = m_block.minLineHeightForReplacedRenderer(m_isFirstLine, replacedHeight);
65     m_left = m_block.logicalLeftOffsetForLine(height, shouldIndentText(), logicalHeight);
66     m_right = m_block.logicalRightOffsetForLine(height, shouldIndentText(), logicalHeight);
67
68     computeAvailableWidthFromLeftAndRight();
69 }
70
71 static bool newFloatShrinksLine(const FloatingObject& newFloat, const RenderBlockFlow& block, bool isFirstLine)
72 {
73     LayoutUnit blockOffset = block.logicalHeight();
74     if (blockOffset >= block.logicalTopForFloat(newFloat) && blockOffset < block.logicalBottomForFloat(newFloat))
75         return true;
76
77     // initial-letter float always shrinks the first line.
78     const auto& style = newFloat.renderer().style();
79     if (isFirstLine && style.styleType() == FIRST_LETTER && !style.initialLetter().isEmpty())
80         return true;
81     return false;
82 }
83
84 void LineWidth::shrinkAvailableWidthForNewFloatIfNeeded(const FloatingObject& newFloat)
85 {
86     if (!newFloatShrinksLine(newFloat, m_block, m_isFirstLine))
87         return;
88     ShapeOutsideDeltas shapeDeltas;
89     if (ShapeOutsideInfo* shapeOutsideInfo = newFloat.renderer().shapeOutsideInfo()) {
90         LayoutUnit lineHeight = m_block.lineHeight(m_isFirstLine, m_block.isHorizontalWritingMode() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes);
91         shapeDeltas = shapeOutsideInfo->computeDeltasForContainingBlockLine(m_block, newFloat, m_block.logicalHeight(), lineHeight);
92     }
93
94     if (newFloat.type() == FloatingObject::FloatLeft) {
95         float newLeft = m_block.logicalRightForFloat(newFloat);
96         if (shouldIndentText() == IndentText && m_block.style().isLeftToRightDirection())
97             newLeft += floorToInt(m_block.textIndentOffset());
98         if (shapeDeltas.isValid()) {
99             if (shapeDeltas.lineOverlapsShape())
100                 newLeft += shapeDeltas.rightMarginBoxDelta();
101             else // If the line doesn't overlap the shape, then we need to act as if this float didn't exist.
102                 newLeft = m_left;
103         }
104         m_left = std::max<float>(m_left, newLeft);
105     } else {
106         float newRight = m_block.logicalLeftForFloat(newFloat);
107         if (shouldIndentText() == IndentText && !m_block.style().isLeftToRightDirection())
108             newRight -= floorToInt(m_block.textIndentOffset());
109         if (shapeDeltas.isValid()) {
110             if (shapeDeltas.lineOverlapsShape())
111                 newRight += shapeDeltas.leftMarginBoxDelta();
112             else // If the line doesn't overlap the shape, then we need to act as if this float didn't exist.
113                 newRight = m_right;
114         }
115         m_right = std::min<float>(m_right, newRight);
116     }
117
118     computeAvailableWidthFromLeftAndRight();
119 }
120
121 void LineWidth::commit()
122 {
123     m_committedWidth += m_uncommittedWidth;
124     m_uncommittedWidth = 0;
125     m_hasCommitted = true;
126 }
127
128 void LineWidth::applyOverhang(RenderRubyRun* rubyRun, RenderObject* startRenderer, RenderObject* endRenderer)
129 {
130     float startOverhang;
131     float endOverhang;
132     rubyRun->getOverhang(m_isFirstLine, startRenderer, endRenderer, startOverhang, endOverhang);
133
134     startOverhang = std::min(startOverhang, m_committedWidth);
135     m_availableWidth += startOverhang;
136
137     endOverhang = std::max(std::min(endOverhang, m_availableWidth - currentWidth()), 0.0f);
138     m_availableWidth += endOverhang;
139     m_overhangWidth += startOverhang + endOverhang;
140 }
141
142 inline static float availableWidthAtOffset(const RenderBlockFlow& block, const LayoutUnit& offset, IndentTextOrNot shouldIndentText,
143     float& newLineLeft, float& newLineRight, const LayoutUnit& lineHeight = 0)
144 {
145     newLineLeft = block.logicalLeftOffsetForLine(offset, shouldIndentText, lineHeight);
146     newLineRight = block.logicalRightOffsetForLine(offset, shouldIndentText, lineHeight);
147     return std::max(0.0f, newLineRight - newLineLeft);
148 }
149
150 void LineWidth::updateLineDimension(LayoutUnit newLineTop, LayoutUnit newLineWidth, float newLineLeft, float newLineRight)
151 {
152     if (newLineWidth <= m_availableWidth)
153         return;
154
155     m_block.setLogicalHeight(newLineTop);
156     m_availableWidth = newLineWidth + m_overhangWidth;
157     m_left = newLineLeft;
158     m_right = newLineRight;
159 }
160
161 void LineWidth::wrapNextToShapeOutside(bool isFirstLine)
162 {
163     LayoutUnit lineHeight = m_block.lineHeight(isFirstLine, m_block.isHorizontalWritingMode() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes);
164     LayoutUnit lineLogicalTop = m_block.logicalHeight();
165     LayoutUnit newLineTop = lineLogicalTop;
166     LayoutUnit floatLogicalBottom = m_block.nextFloatLogicalBottomBelow(lineLogicalTop);
167
168     float newLineWidth;
169     float newLineLeft = m_left;
170     float newLineRight = m_right;
171     while (true) {
172         newLineWidth = availableWidthAtOffset(m_block, newLineTop, shouldIndentText(), newLineLeft, newLineRight, lineHeight);
173         if (newLineWidth >= m_uncommittedWidth)
174             break;
175
176         if (newLineTop >= floatLogicalBottom)
177             break;
178
179         ++newLineTop;
180     }
181     updateLineDimension(newLineTop, newLineWidth, newLineLeft, newLineRight);
182 }
183
184 void LineWidth::fitBelowFloats(bool isFirstLine)
185 {
186     ASSERT(!m_committedWidth);
187     ASSERT(!fitsOnLine());
188
189     LayoutUnit floatLogicalBottom;
190     LayoutUnit lastFloatLogicalBottom = m_block.logicalHeight();
191     float newLineWidth = m_availableWidth;
192     float newLineLeft = m_left;
193     float newLineRight = m_right;
194
195     FloatingObject* lastFloatFromPreviousLine = (m_block.containsFloats() ? m_block.m_floatingObjects->set().last().get() : 0);
196     if (lastFloatFromPreviousLine && lastFloatFromPreviousLine->renderer().shapeOutsideInfo())
197         return wrapNextToShapeOutside(isFirstLine);
198
199     while (true) {
200         floatLogicalBottom = m_block.nextFloatLogicalBottomBelow(lastFloatLogicalBottom);
201         if (floatLogicalBottom <= lastFloatLogicalBottom)
202             break;
203
204         newLineWidth = availableWidthAtOffset(m_block, floatLogicalBottom, shouldIndentText(), newLineLeft, newLineRight);
205         lastFloatLogicalBottom = floatLogicalBottom;
206
207         if (newLineWidth >= m_uncommittedWidth)
208             break;
209     }
210
211     updateLineDimension(lastFloatLogicalBottom, newLineWidth, newLineLeft, newLineRight);
212 }
213
214 void LineWidth::setTrailingWhitespaceWidth(float collapsedWhitespace, float borderPaddingMargin)
215 {
216     m_trailingCollapsedWhitespaceWidth = collapsedWhitespace;
217     m_trailingWhitespaceWidth = collapsedWhitespace + borderPaddingMargin;
218 }
219
220 void LineWidth::computeAvailableWidthFromLeftAndRight()
221 {
222     m_availableWidth = std::max<float>(0, m_right - m_left) + m_overhangWidth;
223 }
224
225 bool LineWidth::fitsOnLineExcludingTrailingCollapsedWhitespace() const
226 {
227     return currentWidth() - m_trailingCollapsedWhitespaceWidth <= m_availableWidth;
228 }
229
230 IndentTextOrNot requiresIndent(bool isFirstLine, bool isAfterHardLineBreak, const RenderStyle& style)
231 {
232     IndentTextOrNot shouldIndentText = DoNotIndentText;
233     if (isFirstLine)
234         shouldIndentText = IndentText;
235 #if ENABLE(CSS3_TEXT)
236     else if (isAfterHardLineBreak && style.textIndentLine() == TextIndentEachLine)
237         shouldIndentText = IndentText;
238
239     if (style.textIndentType() == TextIndentHanging)
240         shouldIndentText = shouldIndentText == IndentText ? DoNotIndentText : IndentText;
241 #else
242     UNUSED_PARAM(isAfterHardLineBreak);
243     UNUSED_PARAM(style);
244 #endif
245     return shouldIndentText;
246 }
247
248 }