2 * Copyright (C) 2013 Adobe Systems Incorporated. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above
9 * copyright notice, this list of conditions and the following
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.
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.
31 #include "LineWidth.h"
33 #include "RenderBlockFlow.h"
34 #include "RenderRubyRun.h"
38 LineWidth::LineWidth(RenderBlockFlow& block, bool isFirstLine, IndentTextOrNot shouldIndentText)
40 , m_uncommittedWidth(0)
43 , m_trailingWhitespaceWidth(0)
44 , m_trailingCollapsedWhitespaceWidth(0)
48 #if ENABLE(CSS_SHAPES)
51 , m_isFirstLine(isFirstLine)
52 , m_shouldIndentText(shouldIndentText)
54 #if ENABLE(CSS_SHAPES)
55 updateCurrentShapeSegment();
57 updateAvailableWidth();
60 bool LineWidth::fitsOnLine(bool ignoringTrailingSpace) const
62 return ignoringTrailingSpace ? fitsOnLineExcludingTrailingCollapsedWhitespace() : fitsOnLineIncludingExtraWidth(0);
65 bool LineWidth::fitsOnLineIncludingExtraWidth(float extra) const
67 return currentWidth() + extra <= m_availableWidth;
70 bool LineWidth::fitsOnLineExcludingTrailingWhitespace(float extra) const
72 return currentWidth() - m_trailingWhitespaceWidth + extra <= m_availableWidth;
75 void LineWidth::updateAvailableWidth(LayoutUnit replacedHeight)
77 LayoutUnit height = m_block.logicalHeight();
78 LayoutUnit logicalHeight = m_block.minLineHeightForReplacedRenderer(m_isFirstLine, replacedHeight);
79 m_left = m_block.logicalLeftOffsetForLine(height, shouldIndentText(), logicalHeight);
80 m_right = m_block.logicalRightOffsetForLine(height, shouldIndentText(), logicalHeight);
82 #if ENABLE(CSS_SHAPES)
84 m_left = std::max<float>(m_segment->logicalLeft, m_left);
85 m_right = std::min<float>(m_segment->logicalRight, m_right);
89 computeAvailableWidthFromLeftAndRight();
92 void LineWidth::shrinkAvailableWidthForNewFloatIfNeeded(FloatingObject* newFloat)
94 LayoutUnit height = m_block.logicalHeight();
95 if (height < newFloat->logicalTop(m_block.isHorizontalWritingMode()) || height >= newFloat->logicalBottom(m_block.isHorizontalWritingMode()))
98 #if ENABLE(CSS_SHAPES)
99 // When floats with shape outside are stacked, the floats are positioned based on the margin box of the float,
100 // not the shape's contour. Since we computed the width based on the shape contour when we added the float,
101 // when we add a subsequent float on the same line, we need to undo the shape delta in order to position
102 // based on the margin box. In order to do this, we need to walk back through the floating object list to find
103 // the first previous float that is on the same side as our newFloat.
104 ShapeOutsideInfo* previousShapeOutsideInfo = 0;
105 const FloatingObjectSet& floatingObjectSet = m_block.m_floatingObjects->set();
106 auto it = floatingObjectSet.end();
107 auto begin = floatingObjectSet.begin();
108 LayoutUnit lineHeight = m_block.lineHeight(m_isFirstLine, m_block.isHorizontalWritingMode() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes);
109 for (--it; it != begin; --it) {
110 FloatingObject* previousFloat = it->get();
111 if (previousFloat != newFloat && previousFloat->type() == newFloat->type()) {
112 previousShapeOutsideInfo = previousFloat->renderer().shapeOutsideInfo();
113 if (previousShapeOutsideInfo)
114 previousShapeOutsideInfo->updateDeltasForContainingBlockLine(&m_block, previousFloat, m_block.logicalHeight(), lineHeight);
119 ShapeOutsideInfo* shapeOutsideInfo = newFloat->renderer().shapeOutsideInfo();
120 if (shapeOutsideInfo)
121 shapeOutsideInfo->updateDeltasForContainingBlockLine(&m_block, newFloat, m_block.logicalHeight(), lineHeight);
124 if (newFloat->type() == FloatingObject::FloatLeft) {
125 float newLeft = newFloat->logicalRight(m_block.isHorizontalWritingMode());
126 #if ENABLE(CSS_SHAPES)
127 if (previousShapeOutsideInfo)
128 newLeft -= previousShapeOutsideInfo->rightMarginBoxDelta();
129 if (shapeOutsideInfo)
130 newLeft += shapeOutsideInfo->rightMarginBoxDelta();
133 if (shouldIndentText() && m_block.style()->isLeftToRightDirection())
134 newLeft += floorToInt(m_block.textIndentOffset());
135 m_left = std::max<float>(m_left, newLeft);
137 float newRight = newFloat->logicalLeft(m_block.isHorizontalWritingMode());
138 #if ENABLE(CSS_SHAPES)
139 if (previousShapeOutsideInfo)
140 newRight -= previousShapeOutsideInfo->leftMarginBoxDelta();
141 if (shapeOutsideInfo)
142 newRight += shapeOutsideInfo->leftMarginBoxDelta();
145 if (shouldIndentText() && !m_block.style()->isLeftToRightDirection())
146 newRight -= floorToInt(m_block.textIndentOffset());
147 m_right = std::min<float>(m_right, newRight);
150 computeAvailableWidthFromLeftAndRight();
153 void LineWidth::commit()
155 m_committedWidth += m_uncommittedWidth;
156 m_uncommittedWidth = 0;
159 void LineWidth::applyOverhang(RenderRubyRun* rubyRun, RenderObject* startRenderer, RenderObject* endRenderer)
163 rubyRun->getOverhang(m_isFirstLine, startRenderer, endRenderer, startOverhang, endOverhang);
165 startOverhang = std::min<int>(startOverhang, m_committedWidth);
166 m_availableWidth += startOverhang;
168 endOverhang = std::max(std::min<int>(endOverhang, m_availableWidth - currentWidth()), 0);
169 m_availableWidth += endOverhang;
170 m_overhangWidth += startOverhang + endOverhang;
173 void LineWidth::fitBelowFloats()
175 ASSERT(!m_committedWidth);
176 ASSERT(!fitsOnLine());
178 LayoutUnit floatLogicalBottom;
179 LayoutUnit lastFloatLogicalBottom = m_block.logicalHeight();
180 float newLineWidth = m_availableWidth;
181 float newLineLeft = m_left;
182 float newLineRight = m_right;
184 floatLogicalBottom = m_block.nextFloatLogicalBottomBelow(lastFloatLogicalBottom, ShapeOutsideFloatShapeOffset);
185 if (floatLogicalBottom <= lastFloatLogicalBottom)
188 newLineLeft = m_block.logicalLeftOffsetForLine(floatLogicalBottom, shouldIndentText());
189 newLineRight = m_block.logicalRightOffsetForLine(floatLogicalBottom, shouldIndentText());
190 newLineWidth = max(0.0f, newLineRight - newLineLeft);
191 lastFloatLogicalBottom = floatLogicalBottom;
193 #if ENABLE(CSS_SHAPES)
194 // FIXME: This code should be refactored to incorporate with the code above.
195 ShapeInsideInfo* shapeInsideInfo = m_block.layoutShapeInsideInfo();
196 if (shapeInsideInfo) {
197 LayoutUnit logicalOffsetFromShapeContainer = m_block.logicalOffsetFromShapeAncestorContainer(shapeInsideInfo->owner()).height();
198 LayoutUnit lineHeight = m_block.lineHeight(false, m_block.isHorizontalWritingMode() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes);
199 shapeInsideInfo->updateSegmentsForLine(lastFloatLogicalBottom + logicalOffsetFromShapeContainer, lineHeight);
200 updateCurrentShapeSegment();
201 updateAvailableWidth();
204 if (newLineWidth >= m_uncommittedWidth)
208 if (newLineWidth > m_availableWidth) {
209 m_block.setLogicalHeight(lastFloatLogicalBottom);
210 m_availableWidth = newLineWidth + m_overhangWidth;
211 m_left = newLineLeft;
212 m_right = newLineRight;
216 void LineWidth::setTrailingWhitespaceWidth(float collapsedWhitespace, float borderPaddingMargin)
218 m_trailingCollapsedWhitespaceWidth = collapsedWhitespace;
219 m_trailingWhitespaceWidth = collapsedWhitespace + borderPaddingMargin;
222 #if ENABLE(CSS_SHAPES)
223 void LineWidth::updateCurrentShapeSegment()
225 if (ShapeInsideInfo* shapeInsideInfo = m_block.layoutShapeInsideInfo())
226 m_segment = shapeInsideInfo->currentSegment();
230 void LineWidth::computeAvailableWidthFromLeftAndRight()
232 m_availableWidth = max(0.0f, m_right - m_left) + m_overhangWidth;
235 bool LineWidth::fitsOnLineExcludingTrailingCollapsedWhitespace() const
237 return currentWidth() - m_trailingCollapsedWhitespaceWidth <= m_availableWidth;