Move LineWidth out of RenderBlockLineLayout
[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     ASSERT(block);
52 #if ENABLE(CSS_SHAPES)
53     if (ShapeInsideInfo* shapeInsideInfo = m_block.layoutShapeInsideInfo())
54         m_segment = shapeInsideInfo->currentSegment();
55 #endif
56     updateAvailableWidth();
57 }
58
59 bool LineWidth::fitsOnLine(bool ignoringTrailingSpace) const
60 {
61     return ignoringTrailingSpace ? fitsOnLineExcludingTrailingCollapsedWhitespace() : fitsOnLineIncludingExtraWidth(0);
62 }
63
64 bool LineWidth::fitsOnLineIncludingExtraWidth(float extra) const
65 {
66     return currentWidth() + extra <= m_availableWidth;
67 }
68
69 bool LineWidth::fitsOnLineExcludingTrailingWhitespace(float extra) const
70 {
71     return currentWidth() - m_trailingWhitespaceWidth + extra <= m_availableWidth;
72 }
73
74 void LineWidth::updateAvailableWidth(LayoutUnit replacedHeight)
75 {
76     LayoutUnit height = m_block.logicalHeight();
77     LayoutUnit logicalHeight = logicalHeightForLine(&m_block, m_isFirstLine, replacedHeight);
78     m_left = m_block.logicalLeftOffsetForLine(height, shouldIndentText(), logicalHeight);
79     m_right = m_block.logicalRightOffsetForLine(height, shouldIndentText(), logicalHeight);
80
81 #if ENABLE(CSS_SHAPES)
82     if (m_segment) {
83         m_left = std::max<float>(m_segment->logicalLeft, m_left);
84         m_right = std::min<float>(m_segment->logicalRight, m_right);
85     }
86 #endif
87
88     computeAvailableWidthFromLeftAndRight();
89 }
90
91 void LineWidth::shrinkAvailableWidthForNewFloatIfNeeded(FloatingObject* newFloat)
92 {
93     LayoutUnit height = m_block.logicalHeight();
94     if (height < newFloat->logicalTop(m_block.isHorizontalWritingMode()) || height >= newFloat->logicalBottom(m_block.isHorizontalWritingMode()))
95         return;
96
97 #if ENABLE(CSS_SHAPES)
98     // When floats with shape outside are stacked, the floats are positioned based on the margin box of the float,
99     // not the shape's contour. Since we computed the width based on the shape contour when we added the float,
100     // when we add a subsequent float on the same line, we need to undo the shape delta in order to position
101     // based on the margin box. In order to do this, we need to walk back through the floating object list to find
102     // the first previous float that is on the same side as our newFloat.
103     ShapeOutsideInfo* previousShapeOutsideInfo = 0;
104     const FloatingObjectSet& floatingObjectSet = m_block.m_floatingObjects->set();
105     FloatingObjectSetIterator it = floatingObjectSet.end();
106     FloatingObjectSetIterator begin = floatingObjectSet.begin();
107     for (--it; it != begin; --it) {
108         FloatingObject* previousFloat = *it;
109         if (previousFloat != newFloat && previousFloat->type() == newFloat->type()) {
110             previousShapeOutsideInfo = previousFloat->renderer()->shapeOutsideInfo();
111             if (previousShapeOutsideInfo)
112                 previousShapeOutsideInfo->computeSegmentsForContainingBlockLine(m_block.logicalHeight(), previousFloat->logicalTop(m_block.isHorizontalWritingMode()), logicalHeightForLine(&m_block, m_isFirstLine));
113             break;
114         }
115     }
116
117     ShapeOutsideInfo* shapeOutsideInfo = newFloat->renderer()->shapeOutsideInfo();
118     if (shapeOutsideInfo)
119         shapeOutsideInfo->computeSegmentsForContainingBlockLine(m_block.logicalHeight(), newFloat->logicalTop(m_block.isHorizontalWritingMode()), logicalHeightForLine(&m_block, m_isFirstLine));
120 #endif
121
122     if (newFloat->type() == FloatingObject::FloatLeft) {
123         float newLeft = newFloat->logicalRight(m_block.isHorizontalWritingMode());
124 #if ENABLE(CSS_SHAPES)
125         if (previousShapeOutsideInfo)
126             newLeft -= previousShapeOutsideInfo->rightSegmentMarginBoxDelta();
127         if (shapeOutsideInfo)
128             newLeft += shapeOutsideInfo->rightSegmentMarginBoxDelta();
129 #endif
130
131         if (shouldIndentText() && m_block.style()->isLeftToRightDirection())
132             newLeft += floorToInt(m_block.textIndentOffset());
133         m_left = std::max<float>(m_left, newLeft);
134     } else {
135         float newRight = newFloat->logicalLeft(m_block.isHorizontalWritingMode());
136 #if ENABLE(CSS_SHAPES)
137         if (previousShapeOutsideInfo)
138             newRight -= previousShapeOutsideInfo->leftSegmentMarginBoxDelta();
139         if (shapeOutsideInfo)
140             newRight += shapeOutsideInfo->leftSegmentMarginBoxDelta();
141 #endif
142
143         if (shouldIndentText() && !m_block.style()->isLeftToRightDirection())
144             newRight -= floorToInt(m_block.textIndentOffset());
145         m_right = std::min<float>(m_right, newRight);
146     }
147
148     computeAvailableWidthFromLeftAndRight();
149 }
150
151 void LineWidth::commit()
152 {
153     m_committedWidth += m_uncommittedWidth;
154     m_uncommittedWidth = 0;
155 }
156
157 void LineWidth::applyOverhang(RenderRubyRun* rubyRun, RenderObject* startRenderer, RenderObject* endRenderer)
158 {
159     int startOverhang;
160     int endOverhang;
161     rubyRun->getOverhang(m_isFirstLine, startRenderer, endRenderer, startOverhang, endOverhang);
162
163     startOverhang = std::min<int>(startOverhang, m_committedWidth);
164     m_availableWidth += startOverhang;
165
166     endOverhang = std::max(std::min<int>(endOverhang, m_availableWidth - currentWidth()), 0);
167     m_availableWidth += endOverhang;
168     m_overhangWidth += startOverhang + endOverhang;
169 }
170
171 void LineWidth::fitBelowFloats()
172 {
173     ASSERT(!m_committedWidth);
174     ASSERT(!fitsOnLine());
175
176     LayoutUnit floatLogicalBottom;
177     LayoutUnit lastFloatLogicalBottom = m_block.logicalHeight();
178     float newLineWidth = m_availableWidth;
179     float newLineLeft = m_left;
180     float newLineRight = m_right;
181     while (true) {
182         floatLogicalBottom = m_block.nextFloatLogicalBottomBelow(lastFloatLogicalBottom);
183         if (floatLogicalBottom <= lastFloatLogicalBottom)
184             break;
185
186         newLineLeft = m_block.logicalLeftOffsetForLine(floatLogicalBottom, shouldIndentText());
187         newLineRight = m_block.logicalRightOffsetForLine(floatLogicalBottom, shouldIndentText());
188         newLineWidth = max(0.0f, newLineRight - newLineLeft);
189         lastFloatLogicalBottom = floatLogicalBottom;
190         if (newLineWidth >= m_uncommittedWidth)
191             break;
192     }
193
194     if (newLineWidth > m_availableWidth) {
195         m_block.setLogicalHeight(lastFloatLogicalBottom);
196         m_availableWidth = newLineWidth + m_overhangWidth;
197         m_left = newLineLeft;
198         m_right = newLineRight;
199     }
200 }
201
202 void LineWidth::setTrailingWhitespaceWidth(float collapsedWhitespace, float borderPaddingMargin)
203 {
204     m_trailingCollapsedWhitespaceWidth = collapsedWhitespace;
205     m_trailingWhitespaceWidth = collapsedWhitespace + borderPaddingMargin;
206 }
207
208 void LineWidth::computeAvailableWidthFromLeftAndRight()
209 {
210     m_availableWidth = max(0.0f, m_right - m_left) + m_overhangWidth;
211 }
212
213 bool LineWidth::fitsOnLineExcludingTrailingCollapsedWhitespace() const
214 {
215     return currentWidth() - m_trailingCollapsedWhitespaceWidth <= m_availableWidth;
216 }
217
218 }