71e05cb23cee182068231b13a87f1d15c7b90c3e
[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 #if ENABLE(CSS_SHAPES)
89     ShapeOutsideDeltas shapeDeltas;
90     if (ShapeOutsideInfo* shapeOutsideInfo = newFloat.renderer().shapeOutsideInfo()) {
91         LayoutUnit lineHeight = m_block.lineHeight(m_isFirstLine, m_block.isHorizontalWritingMode() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes);
92         shapeDeltas = shapeOutsideInfo->computeDeltasForContainingBlockLine(m_block, newFloat, m_block.logicalHeight(), lineHeight);
93     }
94 #endif
95
96     if (newFloat.type() == FloatingObject::FloatLeft) {
97         float newLeft = m_block.logicalRightForFloat(newFloat);
98         if (shouldIndentText() == IndentText && m_block.style().isLeftToRightDirection())
99             newLeft += floorToInt(m_block.textIndentOffset());
100 #if ENABLE(CSS_SHAPES)
101         if (shapeDeltas.isValid()) {
102             if (shapeDeltas.lineOverlapsShape())
103                 newLeft += shapeDeltas.rightMarginBoxDelta();
104             else // If the line doesn't overlap the shape, then we need to act as if this float didn't exist.
105                 newLeft = m_left;
106         }
107 #endif
108         m_left = std::max<float>(m_left, newLeft);
109     } else {
110         float newRight = m_block.logicalLeftForFloat(newFloat);
111         if (shouldIndentText() == IndentText && !m_block.style().isLeftToRightDirection())
112             newRight -= floorToInt(m_block.textIndentOffset());
113 #if ENABLE(CSS_SHAPES)
114         if (shapeDeltas.isValid()) {
115             if (shapeDeltas.lineOverlapsShape())
116                 newRight += shapeDeltas.leftMarginBoxDelta();
117             else // If the line doesn't overlap the shape, then we need to act as if this float didn't exist.
118                 newRight = m_right;
119         }
120 #endif
121         m_right = std::min<float>(m_right, newRight);
122     }
123
124     computeAvailableWidthFromLeftAndRight();
125 }
126
127 void LineWidth::commit()
128 {
129     m_committedWidth += m_uncommittedWidth;
130     m_uncommittedWidth = 0;
131     m_hasCommitted = true;
132 }
133
134 void LineWidth::applyOverhang(RenderRubyRun* rubyRun, RenderObject* startRenderer, RenderObject* endRenderer)
135 {
136     float startOverhang;
137     float endOverhang;
138     rubyRun->getOverhang(m_isFirstLine, startRenderer, endRenderer, startOverhang, endOverhang);
139
140     startOverhang = std::min(startOverhang, m_committedWidth);
141     m_availableWidth += startOverhang;
142
143     endOverhang = std::max(std::min(endOverhang, m_availableWidth - currentWidth()), 0.0f);
144     m_availableWidth += endOverhang;
145     m_overhangWidth += startOverhang + endOverhang;
146 }
147
148 inline static float availableWidthAtOffset(const RenderBlockFlow& block, const LayoutUnit& offset, IndentTextOrNot shouldIndentText,
149     float& newLineLeft, float& newLineRight, const LayoutUnit& lineHeight = 0)
150 {
151     newLineLeft = block.logicalLeftOffsetForLine(offset, shouldIndentText, lineHeight);
152     newLineRight = block.logicalRightOffsetForLine(offset, shouldIndentText, lineHeight);
153     return std::max(0.0f, newLineRight - newLineLeft);
154 }
155
156 void LineWidth::updateLineDimension(LayoutUnit newLineTop, LayoutUnit newLineWidth, float newLineLeft, float newLineRight)
157 {
158     if (newLineWidth <= m_availableWidth)
159         return;
160
161     m_block.setLogicalHeight(newLineTop);
162     m_availableWidth = newLineWidth + m_overhangWidth;
163     m_left = newLineLeft;
164     m_right = newLineRight;
165 }
166
167 #if ENABLE(CSS_SHAPES)
168 void LineWidth::wrapNextToShapeOutside(bool isFirstLine)
169 {
170     LayoutUnit lineHeight = m_block.lineHeight(isFirstLine, m_block.isHorizontalWritingMode() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes);
171     LayoutUnit lineLogicalTop = m_block.logicalHeight();
172     LayoutUnit newLineTop = lineLogicalTop;
173     LayoutUnit floatLogicalBottom = m_block.nextFloatLogicalBottomBelow(lineLogicalTop);
174
175     float newLineWidth;
176     float newLineLeft = m_left;
177     float newLineRight = m_right;
178     while (true) {
179         newLineWidth = availableWidthAtOffset(m_block, newLineTop, shouldIndentText(), newLineLeft, newLineRight, lineHeight);
180         if (newLineWidth >= m_uncommittedWidth)
181             break;
182
183         if (newLineTop >= floatLogicalBottom)
184             break;
185
186         ++newLineTop;
187     }
188     updateLineDimension(newLineTop, newLineWidth, newLineLeft, newLineRight);
189 }
190 #endif
191
192 void LineWidth::fitBelowFloats(bool isFirstLine)
193 {
194 #if !ENABLE(CSS_SHAPES)
195     UNUSED_PARAM(isFirstLine);
196 #endif
197
198     ASSERT(!m_committedWidth);
199     ASSERT(!fitsOnLine());
200
201     LayoutUnit floatLogicalBottom;
202     LayoutUnit lastFloatLogicalBottom = m_block.logicalHeight();
203     float newLineWidth = m_availableWidth;
204     float newLineLeft = m_left;
205     float newLineRight = m_right;
206
207 #if ENABLE(CSS_SHAPES)
208     FloatingObject* lastFloatFromPreviousLine = (m_block.containsFloats() ? m_block.m_floatingObjects->set().last().get() : 0);
209     if (lastFloatFromPreviousLine && lastFloatFromPreviousLine->renderer().shapeOutsideInfo())
210         return wrapNextToShapeOutside(isFirstLine);
211 #endif
212
213     while (true) {
214         floatLogicalBottom = m_block.nextFloatLogicalBottomBelow(lastFloatLogicalBottom);
215         if (floatLogicalBottom <= lastFloatLogicalBottom)
216             break;
217
218         newLineWidth = availableWidthAtOffset(m_block, floatLogicalBottom, shouldIndentText(), newLineLeft, newLineRight);
219         lastFloatLogicalBottom = floatLogicalBottom;
220
221         if (newLineWidth >= m_uncommittedWidth)
222             break;
223     }
224
225     updateLineDimension(lastFloatLogicalBottom, newLineWidth, newLineLeft, newLineRight);
226 }
227
228 void LineWidth::setTrailingWhitespaceWidth(float collapsedWhitespace, float borderPaddingMargin)
229 {
230     m_trailingCollapsedWhitespaceWidth = collapsedWhitespace;
231     m_trailingWhitespaceWidth = collapsedWhitespace + borderPaddingMargin;
232 }
233
234 void LineWidth::computeAvailableWidthFromLeftAndRight()
235 {
236     m_availableWidth = std::max<float>(0, m_right - m_left) + m_overhangWidth;
237 }
238
239 bool LineWidth::fitsOnLineExcludingTrailingCollapsedWhitespace() const
240 {
241     return currentWidth() - m_trailingCollapsedWhitespaceWidth <= m_availableWidth;
242 }
243
244 IndentTextOrNot requiresIndent(bool isFirstLine, bool isAfterHardLineBreak, const RenderStyle& style)
245 {
246     IndentTextOrNot shouldIndentText = DoNotIndentText;
247     if (isFirstLine)
248         shouldIndentText = IndentText;
249 #if ENABLE(CSS3_TEXT)
250     else if (isAfterHardLineBreak && style.textIndentLine() == TextIndentEachLine)
251         shouldIndentText = IndentText;
252
253     if (style.textIndentType() == TextIndentHanging)
254         shouldIndentText = shouldIndentText == IndentText ? DoNotIndentText : IndentText;
255 #else
256     UNUSED_PARAM(isAfterHardLineBreak);
257     UNUSED_PARAM(style);
258 #endif
259     return shouldIndentText;
260 }
261
262 }