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 "core/rendering/LineWidth.h"
32
33#include "core/rendering/RenderBlock.h"
34#include "core/rendering/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_left(0)
44    , m_right(0)
45    , m_availableWidth(0)
46    , m_segment(0)
47    , m_isFirstLine(isFirstLine)
48    , m_shouldIndentText(shouldIndentText)
49{
50    updateCurrentShapeSegment();
51    updateAvailableWidth();
52}
53
54void LineWidth::updateAvailableWidth(LayoutUnit replacedHeight)
55{
56    LayoutUnit height = m_block.logicalHeight();
57    LayoutUnit logicalHeight = m_block.minLineHeightForReplacedRenderer(m_isFirstLine, replacedHeight);
58    m_left = m_block.logicalLeftOffsetForLine(height, shouldIndentText(), logicalHeight);
59    m_right = m_block.logicalRightOffsetForLine(height, shouldIndentText(), logicalHeight);
60
61    if (m_segment) {
62        m_left = std::max<float>(m_segment->logicalLeft, m_left);
63        m_right = std::min<float>(m_segment->logicalRight, m_right);
64    }
65
66    computeAvailableWidthFromLeftAndRight();
67}
68
69void LineWidth::shrinkAvailableWidthForNewFloatIfNeeded(FloatingObject* newFloat)
70{
71    LayoutUnit height = m_block.logicalHeight();
72    if (height < m_block.logicalTopForFloat(newFloat) || height >= m_block.logicalBottomForFloat(newFloat))
73        return;
74
75    // When floats with shape outside are stacked, the floats are positioned based on the margin box of the float,
76    // not the shape's contour. Since we computed the width based on the shape contour when we added the float,
77    // when we add a subsequent float on the same line, we need to undo the shape delta in order to position
78    // based on the margin box. In order to do this, we need to walk back through the floating object list to find
79    // the first previous float that is on the same side as our newFloat.
80    ShapeOutsideInfo* previousShapeOutsideInfo = 0;
81    const FloatingObjectSet& floatingObjectSet = m_block.m_floatingObjects->set();
82    FloatingObjectSetIterator it = floatingObjectSet.end();
83    FloatingObjectSetIterator begin = floatingObjectSet.begin();
84    LayoutUnit lineHeight = m_block.lineHeight(m_isFirstLine, m_block.isHorizontalWritingMode() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes);
85    for (--it; it != begin; --it) {
86        FloatingObject* previousFloat = *it;
87        if (previousFloat != newFloat && previousFloat->type() == newFloat->type()) {
88            previousShapeOutsideInfo = previousFloat->renderer()->shapeOutsideInfo();
89            if (previousShapeOutsideInfo)
90                previousShapeOutsideInfo->updateDeltasForContainingBlockLine(&m_block, previousFloat, m_block.logicalHeight(), lineHeight);
91            break;
92        }
93    }
94
95    ShapeOutsideInfo* shapeOutsideInfo = newFloat->renderer()->shapeOutsideInfo();
96    if (shapeOutsideInfo)
97        shapeOutsideInfo->updateDeltasForContainingBlockLine(&m_block, newFloat, m_block.logicalHeight(), lineHeight);
98
99    if (newFloat->type() == FloatingObject::FloatLeft) {
100        float newLeft = m_block.logicalRightForFloat(newFloat);
101        if (previousShapeOutsideInfo)
102            newLeft -= previousShapeOutsideInfo->rightMarginBoxDelta();
103        if (shapeOutsideInfo)
104            newLeft += shapeOutsideInfo->rightMarginBoxDelta();
105
106        if (shouldIndentText() && m_block.style()->isLeftToRightDirection())
107            newLeft += floorToInt(m_block.textIndentOffset());
108        m_left = std::max<float>(m_left, newLeft);
109    } else {
110        float newRight = m_block.logicalLeftForFloat(newFloat);
111        if (previousShapeOutsideInfo)
112            newRight -= previousShapeOutsideInfo->leftMarginBoxDelta();
113        if (shapeOutsideInfo)
114            newRight += shapeOutsideInfo->leftMarginBoxDelta();
115
116        if (shouldIndentText() && !m_block.style()->isLeftToRightDirection())
117            newRight -= floorToInt(m_block.textIndentOffset());
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
144void LineWidth::fitBelowFloats()
145{
146    ASSERT(!m_committedWidth);
147    ASSERT(!fitsOnLine());
148
149    LayoutUnit floatLogicalBottom;
150    LayoutUnit lastFloatLogicalBottom = m_block.logicalHeight();
151    float newLineWidth = m_availableWidth;
152    float newLineLeft = m_left;
153    float newLineRight = m_right;
154    while (true) {
155        floatLogicalBottom = m_block.nextFloatLogicalBottomBelow(lastFloatLogicalBottom, ShapeOutsideFloatShapeOffset);
156        if (floatLogicalBottom <= lastFloatLogicalBottom)
157            break;
158
159        newLineLeft = m_block.logicalLeftOffsetForLine(floatLogicalBottom, shouldIndentText());
160        newLineRight = m_block.logicalRightOffsetForLine(floatLogicalBottom, shouldIndentText());
161        newLineWidth = max(0.0f, newLineRight - newLineLeft);
162        lastFloatLogicalBottom = floatLogicalBottom;
163
164        // FIXME: This code should be refactored to incorporate with the code above.
165        ShapeInsideInfo* shapeInsideInfo = m_block.layoutShapeInsideInfo();
166        if (shapeInsideInfo) {
167            LayoutUnit logicalOffsetFromShapeContainer = m_block.logicalOffsetFromShapeAncestorContainer(shapeInsideInfo->owner()).height();
168            LayoutUnit lineHeight = m_block.lineHeight(false, m_block.isHorizontalWritingMode() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes);
169            shapeInsideInfo->updateSegmentsForLine(lastFloatLogicalBottom + logicalOffsetFromShapeContainer, lineHeight);
170            updateCurrentShapeSegment();
171            updateAvailableWidth();
172        }
173
174        if (newLineWidth >= m_uncommittedWidth)
175            break;
176    }
177
178    if (newLineWidth > m_availableWidth) {
179        m_block.setLogicalHeight(lastFloatLogicalBottom);
180        m_availableWidth = newLineWidth + m_overhangWidth;
181        m_left = newLineLeft;
182        m_right = newLineRight;
183    }
184}
185
186void LineWidth::updateCurrentShapeSegment()
187{
188    if (ShapeInsideInfo* shapeInsideInfo = m_block.layoutShapeInsideInfo())
189        m_segment = shapeInsideInfo->currentSegment();
190}
191
192void LineWidth::computeAvailableWidthFromLeftAndRight()
193{
194    m_availableWidth = max(0.0f, m_right - m_left) + m_overhangWidth;
195}
196
197}
198