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