2 * Copyright (C) 2000 Lars Knoll (knoll@kde.org)
3 * Copyright (C) 2003, 2004, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All right reserved.
4 * Copyright (C) 2010 Google Inc. All rights reserved.
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
16 * You should have received a copy of the GNU Library General Public License
17 * along with this library; see the file COPYING.LIB. If not, write to
18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
25 #include "core/accessibility/AXObjectCache.h"
26 #include "core/rendering/RenderCounter.h"
27 #include "core/rendering/RenderFlowThread.h"
28 #include "core/rendering/RenderLayer.h"
29 #include "core/rendering/RenderListMarker.h"
30 #include "core/rendering/RenderRegion.h"
31 #include "core/rendering/RenderRubyRun.h"
32 #include "core/rendering/RenderView.h"
33 #include "core/rendering/TrailingFloatsRootInlineBox.h"
34 #include "core/rendering/VerticalPositionCache.h"
35 #include "core/rendering/line/BreakingContextInlineHeaders.h"
36 #include "core/rendering/line/LineLayoutState.h"
37 #include "core/rendering/line/LineWidth.h"
38 #include "core/rendering/svg/SVGRootInlineBox.h"
39 #include "platform/fonts/Character.h"
40 #include "platform/text/BidiResolver.h"
41 #include "wtf/RefCountedLeakCounter.h"
42 #include "wtf/StdLibExtras.h"
43 #include "wtf/Vector.h"
44 #include "wtf/unicode/CharacterNames.h"
49 using namespace WTF::Unicode;
51 static RenderObject* firstRenderObjectForDirectionalityDetermination(RenderObject* root, RenderObject* current = 0)
53 RenderObject* next = current;
55 if (isIsolated(current->style()->unicodeBidi())
56 && (current->isRenderInline() || current->isRenderBlock())) {
63 current = current->parent();
67 current = root->firstChild();
71 if (isIteratorTarget(current) && !(current->isText() && toRenderText(current)->isAllCollapsibleWhitespace()))
74 if (!isIteratorTarget(current) && !isIsolated(current->style()->unicodeBidi()))
75 next = current->firstChild();
78 while (current && current != root) {
79 next = current->nextSibling();
82 current = current->parent();
95 static TextDirection determinePlaintextDirectionality(RenderObject* root, RenderObject* current = 0, unsigned pos = 0)
97 InlineIterator iter(root, firstRenderObjectForDirectionalityDetermination(root, current), pos);
98 InlineBidiResolver observer;
99 observer.setStatus(BidiStatus(root->style()->direction(), isOverride(root->style()->unicodeBidi())));
100 observer.setPositionIgnoringNestedIsolates(iter);
101 return observer.determineParagraphDirectionality();
104 static inline InlineBox* createInlineBoxForRenderer(RenderObject* obj, bool isRootLineBox, bool isOnlyRun = false)
107 return toRenderBlockFlow(obj)->createAndAppendRootInlineBox();
110 InlineTextBox* textBox = toRenderText(obj)->createInlineTextBox();
111 // We only treat a box as text for a <br> if we are on a line by ourself or in strict mode
112 // (Note the use of strict mode. In "almost strict" mode, we don't treat the box for <br> as text.)
114 textBox->setIsText(isOnlyRun || obj->document().inNoQuirksMode());
119 return toRenderBox(obj)->createInlineBox();
121 return toRenderInline(obj)->createAndAppendInlineFlowBox();
124 static inline void dirtyLineBoxesForRenderer(RenderObject* o, bool fullLayout)
127 RenderText* renderText = toRenderText(o);
128 renderText->dirtyLineBoxes(fullLayout);
130 toRenderInline(o)->dirtyLineBoxes(fullLayout);
133 static bool parentIsConstructedOrHaveNext(InlineFlowBox* parentBox)
136 if (parentBox->isConstructed() || parentBox->nextOnLine())
138 parentBox = parentBox->parent();
143 InlineFlowBox* RenderBlockFlow::createLineBoxes(RenderObject* obj, const LineInfo& lineInfo, InlineBox* childBox)
145 // See if we have an unconstructed line box for this object that is also
146 // the last item on the line.
147 unsigned lineDepth = 1;
148 InlineFlowBox* parentBox = 0;
149 InlineFlowBox* result = 0;
150 bool hasDefaultLineBoxContain = style()->lineBoxContain() == RenderStyle::initialLineBoxContain();
152 ASSERT_WITH_SECURITY_IMPLICATION(obj->isRenderInline() || obj == this);
154 RenderInline* inlineFlow = (obj != this) ? toRenderInline(obj) : 0;
156 // Get the last box we made for this render object.
157 parentBox = inlineFlow ? inlineFlow->lastLineBox() : toRenderBlock(obj)->lastLineBox();
159 // If this box or its ancestor is constructed then it is from a previous line, and we need
160 // to make a new box for our line. If this box or its ancestor is unconstructed but it has
161 // something following it on the line, then we know we have to make a new box
162 // as well. In this situation our inline has actually been split in two on
163 // the same line (this can happen with very fancy language mixtures).
164 bool constructedNewBox = false;
165 bool allowedToConstructNewBox = !hasDefaultLineBoxContain || !inlineFlow || inlineFlow->alwaysCreateLineBoxes();
166 bool canUseExistingParentBox = parentBox && !parentIsConstructedOrHaveNext(parentBox);
167 if (allowedToConstructNewBox && !canUseExistingParentBox) {
168 // We need to make a new box for this render object. Once
169 // made, we need to place it at the end of the current line.
170 InlineBox* newBox = createInlineBoxForRenderer(obj, obj == this);
171 ASSERT_WITH_SECURITY_IMPLICATION(newBox->isInlineFlowBox());
172 parentBox = toInlineFlowBox(newBox);
173 parentBox->setFirstLineStyleBit(lineInfo.isFirstLine());
174 parentBox->setIsHorizontal(isHorizontalWritingMode());
175 if (!hasDefaultLineBoxContain)
176 parentBox->clearDescendantsHaveSameLineHeightAndBaseline();
177 constructedNewBox = true;
180 if (constructedNewBox || canUseExistingParentBox) {
184 // If we have hit the block itself, then |box| represents the root
185 // inline box for the line, and it doesn't have to be appended to any parent
188 parentBox->addToLine(childBox);
190 if (!constructedNewBox || obj == this)
193 childBox = parentBox;
196 // If we've exceeded our line depth, then jump straight to the root and skip all the remaining
197 // intermediate inline flows.
198 obj = (++lineDepth >= cMaxLineDepth) ? this : obj->parent();
205 template <typename CharacterType>
206 static inline bool endsWithASCIISpaces(const CharacterType* characters, unsigned pos, unsigned end)
208 while (isASCIISpace(characters[pos])) {
216 static bool reachedEndOfTextRenderer(const BidiRunList<BidiRun>& bidiRuns)
218 BidiRun* run = bidiRuns.logicallyLastRun();
221 unsigned pos = run->stop();
222 RenderObject* r = run->m_object;
223 if (!r->isText() || r->isBR())
225 RenderText* renderText = toRenderText(r);
226 unsigned length = renderText->textLength();
230 if (renderText->is8Bit())
231 return endsWithASCIISpaces(renderText->characters8(), pos, length);
232 return endsWithASCIISpaces(renderText->characters16(), pos, length);
235 RootInlineBox* RenderBlockFlow::constructLine(BidiRunList<BidiRun>& bidiRuns, const LineInfo& lineInfo)
237 ASSERT(bidiRuns.firstRun());
239 bool rootHasSelectedChildren = false;
240 InlineFlowBox* parentBox = 0;
241 int runCount = bidiRuns.runCount() - lineInfo.runsFromLeadingWhitespace();
242 for (BidiRun* r = bidiRuns.firstRun(); r; r = r->next()) {
243 // Create a box for our object.
244 bool isOnlyRun = (runCount == 1);
245 if (runCount == 2 && !r->m_object->isListMarker())
246 isOnlyRun = (!style()->isLeftToRightDirection() ? bidiRuns.lastRun() : bidiRuns.firstRun())->m_object->isListMarker();
248 if (lineInfo.isEmpty())
251 InlineBox* box = createInlineBoxForRenderer(r->m_object, false, isOnlyRun);
258 if (!rootHasSelectedChildren && box->renderer().selectionState() != RenderObject::SelectionNone)
259 rootHasSelectedChildren = true;
261 // If we have no parent box yet, or if the run is not simply a sibling,
262 // then we need to construct inline boxes as necessary to properly enclose the
263 // run's inline box. Segments can only be siblings at the root level, as
264 // they are positioned separately.
265 if (!parentBox || parentBox->renderer() != r->m_object->parent()) {
266 // Create new inline boxes all the way back to the appropriate insertion point.
267 parentBox = createLineBoxes(r->m_object->parent(), lineInfo, box);
269 // Append the inline box to this line.
270 parentBox->addToLine(box);
273 bool visuallyOrdered = r->m_object->style()->rtlOrdering() == VisualOrder;
274 box->setBidiLevel(r->level());
276 if (box->isInlineTextBox()) {
277 InlineTextBox* text = toInlineTextBox(box);
278 text->setStart(r->m_start);
279 text->setLen(r->m_stop - r->m_start);
280 text->setDirOverride(r->dirOverride(visuallyOrdered));
282 text->setHasHyphen(true);
284 if (AXObjectCache* cache = document().existingAXObjectCache())
285 cache->inlineTextBoxesUpdated(r->m_object);
289 // We should have a root inline box. It should be unconstructed and
290 // be the last continuation of our line list.
291 ASSERT(lastLineBox() && !lastLineBox()->isConstructed());
293 // Set the m_selectedChildren flag on the root inline box if one of the leaf inline box
294 // from the bidi runs walk above has a selection state.
295 if (rootHasSelectedChildren)
296 lastLineBox()->root().setHasSelectedChildren(true);
298 // Set bits on our inline flow boxes that indicate which sides should
299 // paint borders/margins/padding. This knowledge will ultimately be used when
300 // we determine the horizontal positions and widths of all the inline boxes on
302 bool isLogicallyLastRunWrapped = bidiRuns.logicallyLastRun()->m_object && bidiRuns.logicallyLastRun()->m_object->isText() ? !reachedEndOfTextRenderer(bidiRuns) : true;
303 lastLineBox()->determineSpacingForFlowBoxes(lineInfo.isLastLine(), isLogicallyLastRunWrapped, bidiRuns.logicallyLastRun()->m_object);
305 // Now mark the line boxes as being constructed.
306 lastLineBox()->setConstructed();
308 // Return the last line.
309 return lastRootBox();
312 ETextAlign RenderBlockFlow::textAlignmentForLine(bool endsWithSoftBreak) const
314 ETextAlign alignment = style()->textAlign();
315 if (endsWithSoftBreak)
318 if (!RuntimeEnabledFeatures::css3TextEnabled())
319 return (alignment == JUSTIFY) ? TASTART : alignment;
321 TextAlignLast alignmentLast = style()->textAlignLast();
322 switch (alignmentLast) {
323 case TextAlignLastStart:
325 case TextAlignLastEnd:
327 case TextAlignLastLeft:
329 case TextAlignLastRight:
331 case TextAlignLastCenter:
333 case TextAlignLastJustify:
335 case TextAlignLastAuto:
336 if (alignment != JUSTIFY)
338 if (style()->textJustify() == TextJustifyDistribute)
346 static void updateLogicalWidthForLeftAlignedBlock(bool isLeftToRightDirection, BidiRun* trailingSpaceRun, float& logicalLeft, float& totalLogicalWidth, float availableLogicalWidth)
348 // The direction of the block should determine what happens with wide lines.
349 // In particular with RTL blocks, wide lines should still spill out to the left.
350 if (isLeftToRightDirection) {
351 if (totalLogicalWidth > availableLogicalWidth && trailingSpaceRun)
352 trailingSpaceRun->m_box->setLogicalWidth(max<float>(0, trailingSpaceRun->m_box->logicalWidth() - totalLogicalWidth + availableLogicalWidth));
356 if (trailingSpaceRun)
357 trailingSpaceRun->m_box->setLogicalWidth(0);
358 else if (totalLogicalWidth > availableLogicalWidth)
359 logicalLeft -= (totalLogicalWidth - availableLogicalWidth);
362 static void updateLogicalWidthForRightAlignedBlock(bool isLeftToRightDirection, BidiRun* trailingSpaceRun, float& logicalLeft, float& totalLogicalWidth, float availableLogicalWidth)
364 // Wide lines spill out of the block based off direction.
365 // So even if text-align is right, if direction is LTR, wide lines should overflow out of the right
366 // side of the block.
367 if (isLeftToRightDirection) {
368 if (trailingSpaceRun) {
369 totalLogicalWidth -= trailingSpaceRun->m_box->logicalWidth();
370 trailingSpaceRun->m_box->setLogicalWidth(0);
372 if (totalLogicalWidth < availableLogicalWidth)
373 logicalLeft += availableLogicalWidth - totalLogicalWidth;
377 if (totalLogicalWidth > availableLogicalWidth && trailingSpaceRun) {
378 trailingSpaceRun->m_box->setLogicalWidth(max<float>(0, trailingSpaceRun->m_box->logicalWidth() - totalLogicalWidth + availableLogicalWidth));
379 totalLogicalWidth -= trailingSpaceRun->m_box->logicalWidth();
381 logicalLeft += availableLogicalWidth - totalLogicalWidth;
384 static void updateLogicalWidthForCenterAlignedBlock(bool isLeftToRightDirection, BidiRun* trailingSpaceRun, float& logicalLeft, float& totalLogicalWidth, float availableLogicalWidth)
386 float trailingSpaceWidth = 0;
387 if (trailingSpaceRun) {
388 totalLogicalWidth -= trailingSpaceRun->m_box->logicalWidth();
389 trailingSpaceWidth = min(trailingSpaceRun->m_box->logicalWidth(), (availableLogicalWidth - totalLogicalWidth + 1) / 2);
390 trailingSpaceRun->m_box->setLogicalWidth(max<float>(0, trailingSpaceWidth));
392 if (isLeftToRightDirection)
393 logicalLeft += max<float>((availableLogicalWidth - totalLogicalWidth) / 2, 0);
395 logicalLeft += totalLogicalWidth > availableLogicalWidth ? (availableLogicalWidth - totalLogicalWidth) : (availableLogicalWidth - totalLogicalWidth) / 2 - trailingSpaceWidth;
398 void RenderBlockFlow::setMarginsForRubyRun(BidiRun* run, RenderRubyRun* renderer, RenderObject* previousObject, const LineInfo& lineInfo)
402 RenderObject* nextObject = 0;
403 for (BidiRun* runWithNextObject = run->next(); runWithNextObject; runWithNextObject = runWithNextObject->next()) {
404 if (!runWithNextObject->m_object->isOutOfFlowPositioned() && !runWithNextObject->m_box->isLineBreak()) {
405 nextObject = runWithNextObject->m_object;
409 renderer->getOverhang(lineInfo.isFirstLine(), renderer->style()->isLeftToRightDirection() ? previousObject : nextObject, renderer->style()->isLeftToRightDirection() ? nextObject : previousObject, startOverhang, endOverhang);
410 setMarginStartForChild(renderer, -startOverhang);
411 setMarginEndForChild(renderer, -endOverhang);
414 static inline void setLogicalWidthForTextRun(RootInlineBox* lineBox, BidiRun* run, RenderText* renderer, float xPos, const LineInfo& lineInfo,
415 GlyphOverflowAndFallbackFontsMap& textBoxDataMap, VerticalPositionCache& verticalPositionCache, WordMeasurements& wordMeasurements)
417 HashSet<const SimpleFontData*> fallbackFonts;
418 GlyphOverflow glyphOverflow;
420 const Font& font = renderer->style(lineInfo.isFirstLine())->font();
421 // Always compute glyph overflow if the block's line-box-contain value is "glyphs".
422 if (lineBox->fitsToGlyphs()) {
423 // If we don't stick out of the root line's font box, then don't bother computing our glyph overflow. This optimization
424 // will keep us from computing glyph bounds in nearly all cases.
425 bool includeRootLine = lineBox->includesRootLineBoxFontOrLeading();
426 int baselineShift = lineBox->verticalPositionForBox(run->m_box, verticalPositionCache);
427 int rootDescent = includeRootLine ? font.fontMetrics().descent() : 0;
428 int rootAscent = includeRootLine ? font.fontMetrics().ascent() : 0;
429 int boxAscent = font.fontMetrics().ascent() - baselineShift;
430 int boxDescent = font.fontMetrics().descent() + baselineShift;
431 if (boxAscent > rootDescent || boxDescent > rootAscent)
432 glyphOverflow.computeBounds = true;
435 LayoutUnit hyphenWidth = 0;
436 if (toInlineTextBox(run->m_box)->hasHyphen()) {
437 const Font& font = renderer->style(lineInfo.isFirstLine())->font();
438 hyphenWidth = measureHyphenWidth(renderer, font, run->direction());
440 float measuredWidth = 0;
442 bool kerningIsEnabled = font.fontDescription().typesettingFeatures() & Kerning;
445 // FIXME: Having any font feature settings enabled can lead to selection gaps on
446 // Chromium-mac. https://bugs.webkit.org/show_bug.cgi?id=113418
447 bool canUseSimpleFontCodePath = renderer->canUseSimpleFontCodePath() && !font.fontDescription().featureSettings();
449 bool canUseSimpleFontCodePath = renderer->canUseSimpleFontCodePath();
452 // Since we don't cache glyph overflows, we need to re-measure the run if
453 // the style is linebox-contain: glyph.
455 if (!lineBox->fitsToGlyphs() && canUseSimpleFontCodePath) {
456 int lastEndOffset = run->m_start;
457 for (size_t i = 0, size = wordMeasurements.size(); i < size && lastEndOffset < run->m_stop; ++i) {
458 const WordMeasurement& wordMeasurement = wordMeasurements[i];
459 if (wordMeasurement.width <=0 || wordMeasurement.startOffset == wordMeasurement.endOffset)
461 if (wordMeasurement.renderer != renderer || wordMeasurement.startOffset != lastEndOffset || wordMeasurement.endOffset > run->m_stop)
464 lastEndOffset = wordMeasurement.endOffset;
465 if (kerningIsEnabled && lastEndOffset == run->m_stop) {
466 int wordLength = lastEndOffset - wordMeasurement.startOffset;
467 measuredWidth += renderer->width(wordMeasurement.startOffset, wordLength, xPos, run->direction(), lineInfo.isFirstLine());
468 if (i > 0 && wordLength == 1 && renderer->characterAt(wordMeasurement.startOffset) == ' ')
469 measuredWidth += renderer->style()->wordSpacing();
471 measuredWidth += wordMeasurement.width;
472 if (!wordMeasurement.fallbackFonts.isEmpty()) {
473 HashSet<const SimpleFontData*>::const_iterator end = wordMeasurement.fallbackFonts.end();
474 for (HashSet<const SimpleFontData*>::const_iterator it = wordMeasurement.fallbackFonts.begin(); it != end; ++it)
475 fallbackFonts.add(*it);
478 if (measuredWidth && lastEndOffset != run->m_stop) {
479 // If we don't have enough cached data, we'll measure the run again.
481 fallbackFonts.clear();
486 measuredWidth = renderer->width(run->m_start, run->m_stop - run->m_start, xPos, run->direction(), lineInfo.isFirstLine(), &fallbackFonts, &glyphOverflow);
488 run->m_box->setLogicalWidth(measuredWidth + hyphenWidth);
489 if (!fallbackFonts.isEmpty()) {
490 ASSERT(run->m_box->isText());
491 GlyphOverflowAndFallbackFontsMap::ValueType* it = textBoxDataMap.add(toInlineTextBox(run->m_box), make_pair(Vector<const SimpleFontData*>(), GlyphOverflow())).storedValue;
492 ASSERT(it->value.first.isEmpty());
493 copyToVector(fallbackFonts, it->value.first);
494 run->m_box->parent()->clearDescendantsHaveSameLineHeightAndBaseline();
496 if ((glyphOverflow.top || glyphOverflow.bottom || glyphOverflow.left || glyphOverflow.right)) {
497 ASSERT(run->m_box->isText());
498 GlyphOverflowAndFallbackFontsMap::ValueType* it = textBoxDataMap.add(toInlineTextBox(run->m_box), make_pair(Vector<const SimpleFontData*>(), GlyphOverflow())).storedValue;
499 it->value.second = glyphOverflow;
500 run->m_box->clearKnownToHaveNoOverflow();
504 static inline void computeExpansionForJustifiedText(BidiRun* firstRun, BidiRun* trailingSpaceRun, Vector<unsigned, 16>& expansionOpportunities, unsigned expansionOpportunityCount, float& totalLogicalWidth, float availableLogicalWidth)
506 if (!expansionOpportunityCount || availableLogicalWidth <= totalLogicalWidth)
510 for (BidiRun* r = firstRun; r; r = r->next()) {
511 if (!r->m_box || r == trailingSpaceRun)
514 if (r->m_object->isText()) {
515 unsigned opportunitiesInRun = expansionOpportunities[i++];
517 ASSERT(opportunitiesInRun <= expansionOpportunityCount);
519 // Only justify text if whitespace is collapsed.
520 if (r->m_object->style()->collapseWhiteSpace()) {
521 InlineTextBox* textBox = toInlineTextBox(r->m_box);
522 int expansion = (availableLogicalWidth - totalLogicalWidth) * opportunitiesInRun / expansionOpportunityCount;
523 textBox->setExpansion(expansion);
524 totalLogicalWidth += expansion;
526 expansionOpportunityCount -= opportunitiesInRun;
527 if (!expansionOpportunityCount)
533 void RenderBlockFlow::updateLogicalWidthForAlignment(const ETextAlign& textAlign, const RootInlineBox* rootInlineBox, BidiRun* trailingSpaceRun, float& logicalLeft, float& totalLogicalWidth, float& availableLogicalWidth, unsigned expansionOpportunityCount)
535 TextDirection direction;
536 if (rootInlineBox && rootInlineBox->renderer().style()->unicodeBidi() == Plaintext)
537 direction = rootInlineBox->direction();
539 direction = style()->direction();
541 // Armed with the total width of the line (without justification),
542 // we now examine our text-align property in order to determine where to position the
543 // objects horizontally. The total width of the line can be increased if we end up
548 updateLogicalWidthForLeftAlignedBlock(style()->isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth);
552 updateLogicalWidthForRightAlignedBlock(style()->isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth);
556 updateLogicalWidthForCenterAlignedBlock(style()->isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth);
559 adjustInlineDirectionLineBounds(expansionOpportunityCount, logicalLeft, availableLogicalWidth);
560 if (expansionOpportunityCount) {
561 if (trailingSpaceRun) {
562 totalLogicalWidth -= trailingSpaceRun->m_box->logicalWidth();
563 trailingSpaceRun->m_box->setLogicalWidth(0);
569 if (direction == LTR)
570 updateLogicalWidthForLeftAlignedBlock(style()->isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth);
572 updateLogicalWidthForRightAlignedBlock(style()->isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth);
575 if (direction == LTR)
576 updateLogicalWidthForRightAlignedBlock(style()->isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth);
578 updateLogicalWidthForLeftAlignedBlock(style()->isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth);
583 static void updateLogicalInlinePositions(RenderBlockFlow* block, float& lineLogicalLeft, float& lineLogicalRight, float& availableLogicalWidth, bool firstLine, IndentTextOrNot shouldIndentText, LayoutUnit boxLogicalHeight)
585 LayoutUnit lineLogicalHeight = block->minLineHeightForReplacedRenderer(firstLine, boxLogicalHeight);
586 lineLogicalLeft = block->logicalLeftOffsetForLine(block->logicalHeight(), shouldIndentText == IndentText, lineLogicalHeight).toFloat();
587 // FIXME: This shouldn't be pixel snapped once multicolumn layout has been updated to correctly carry over subpixel values.
588 // https://bugs.webkit.org/show_bug.cgi?id=105461
589 lineLogicalRight = block->pixelSnappedLogicalRightOffsetForLine(block->logicalHeight(), shouldIndentText == IndentText, lineLogicalHeight).toFloat();
590 availableLogicalWidth = lineLogicalRight - lineLogicalLeft;
593 void RenderBlockFlow::computeInlineDirectionPositionsForLine(RootInlineBox* lineBox, const LineInfo& lineInfo, BidiRun* firstRun, BidiRun* trailingSpaceRun, bool reachedEnd,
594 GlyphOverflowAndFallbackFontsMap& textBoxDataMap, VerticalPositionCache& verticalPositionCache, WordMeasurements& wordMeasurements)
596 ETextAlign textAlign = textAlignmentForLine(!reachedEnd && !lineBox->endsWithBreak());
598 // CSS 2.1: "'Text-indent' only affects a line if it is the first formatted line of an element. For example, the first line of an anonymous block
599 // box is only affected if it is the first child of its parent element."
600 // CSS3 "text-indent", "each-line" affects the first line of the block container as well as each line after a forced line break,
601 // but does not affect lines after a soft wrap break.
602 bool isFirstLine = lineInfo.isFirstLine() && !(isAnonymousBlock() && parent()->firstChild() != this);
603 bool isAfterHardLineBreak = lineBox->prevRootBox() && lineBox->prevRootBox()->endsWithBreak();
604 IndentTextOrNot shouldIndentText = requiresIndent(isFirstLine, isAfterHardLineBreak, style());
605 float lineLogicalLeft;
606 float lineLogicalRight;
607 float availableLogicalWidth;
608 updateLogicalInlinePositions(this, lineLogicalLeft, lineLogicalRight, availableLogicalWidth, isFirstLine, shouldIndentText, 0);
609 bool needsWordSpacing;
611 if (firstRun && firstRun->m_object->isReplaced()) {
612 RenderBox* renderBox = toRenderBox(firstRun->m_object);
613 updateLogicalInlinePositions(this, lineLogicalLeft, lineLogicalRight, availableLogicalWidth, isFirstLine, shouldIndentText, renderBox->logicalHeight());
616 computeInlineDirectionPositionsForSegment(lineBox, lineInfo, textAlign, lineLogicalLeft, availableLogicalWidth, firstRun, trailingSpaceRun, textBoxDataMap, verticalPositionCache, wordMeasurements);
617 // The widths of all runs are now known. We can now place every inline box (and
618 // compute accurate widths for the inline flow boxes).
619 needsWordSpacing = false;
620 lineBox->placeBoxesInInlineDirection(lineLogicalLeft, needsWordSpacing, textBoxDataMap);
623 BidiRun* RenderBlockFlow::computeInlineDirectionPositionsForSegment(RootInlineBox* lineBox, const LineInfo& lineInfo, ETextAlign textAlign, float& logicalLeft,
624 float& availableLogicalWidth, BidiRun* firstRun, BidiRun* trailingSpaceRun, GlyphOverflowAndFallbackFontsMap& textBoxDataMap, VerticalPositionCache& verticalPositionCache,
625 WordMeasurements& wordMeasurements)
627 bool needsWordSpacing = false;
628 float totalLogicalWidth = lineBox->getFlowSpacingLogicalWidth().toFloat();
629 unsigned expansionOpportunityCount = 0;
630 bool isAfterExpansion = true;
631 Vector<unsigned, 16> expansionOpportunities;
632 RenderObject* previousObject = 0;
633 TextJustify textJustify = style()->textJustify();
635 BidiRun* r = firstRun;
636 for (; r; r = r->next()) {
637 if (!r->m_box || r->m_object->isOutOfFlowPositioned() || r->m_box->isLineBreak())
638 continue; // Positioned objects are only participating to figure out their
639 // correct static x position. They have no effect on the width.
640 // Similarly, line break boxes have no effect on the width.
641 if (r->m_object->isText()) {
642 RenderText* rt = toRenderText(r->m_object);
643 if (textAlign == JUSTIFY && r != trailingSpaceRun && textJustify != TextJustifyNone) {
644 if (!isAfterExpansion)
645 toInlineTextBox(r->m_box)->setCanHaveLeadingExpansion(true);
646 unsigned opportunitiesInRun;
648 opportunitiesInRun = Character::expansionOpportunityCount(rt->characters8() + r->m_start, r->m_stop - r->m_start, r->m_box->direction(), isAfterExpansion);
650 opportunitiesInRun = Character::expansionOpportunityCount(rt->characters16() + r->m_start, r->m_stop - r->m_start, r->m_box->direction(), isAfterExpansion);
651 expansionOpportunities.append(opportunitiesInRun);
652 expansionOpportunityCount += opportunitiesInRun;
655 if (int length = rt->textLength()) {
656 if (!r->m_start && needsWordSpacing && isSpaceOrNewline(rt->characterAt(r->m_start)))
657 totalLogicalWidth += rt->style(lineInfo.isFirstLine())->font().fontDescription().wordSpacing();
658 needsWordSpacing = !isSpaceOrNewline(rt->characterAt(r->m_stop - 1)) && r->m_stop == length;
661 setLogicalWidthForTextRun(lineBox, r, rt, totalLogicalWidth, lineInfo, textBoxDataMap, verticalPositionCache, wordMeasurements);
663 isAfterExpansion = false;
664 if (!r->m_object->isRenderInline()) {
665 RenderBox* renderBox = toRenderBox(r->m_object);
666 if (renderBox->isRubyRun())
667 setMarginsForRubyRun(r, toRenderRubyRun(renderBox), previousObject, lineInfo);
668 r->m_box->setLogicalWidth(logicalWidthForChild(renderBox).toFloat());
669 totalLogicalWidth += marginStartForChild(renderBox) + marginEndForChild(renderBox);
673 totalLogicalWidth += r->m_box->logicalWidth();
674 previousObject = r->m_object;
677 if (isAfterExpansion && !expansionOpportunities.isEmpty()) {
678 expansionOpportunities.last()--;
679 expansionOpportunityCount--;
682 updateLogicalWidthForAlignment(textAlign, lineBox, trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth, expansionOpportunityCount);
684 computeExpansionForJustifiedText(firstRun, trailingSpaceRun, expansionOpportunities, expansionOpportunityCount, totalLogicalWidth, availableLogicalWidth);
689 void RenderBlockFlow::computeBlockDirectionPositionsForLine(RootInlineBox* lineBox, BidiRun* firstRun, GlyphOverflowAndFallbackFontsMap& textBoxDataMap,
690 VerticalPositionCache& verticalPositionCache)
692 setLogicalHeight(lineBox->alignBoxesInBlockDirection(logicalHeight(), textBoxDataMap, verticalPositionCache));
694 // Now make sure we place replaced render objects correctly.
695 for (BidiRun* r = firstRun; r; r = r->next()) {
698 continue; // Skip runs with no line boxes.
700 // Align positioned boxes with the top of the line box. This is
701 // a reasonable approximation of an appropriate y position.
702 if (r->m_object->isOutOfFlowPositioned())
703 r->m_box->setLogicalTop(logicalHeight().toFloat());
705 // Position is used to properly position both replaced elements and
706 // to update the static normal flow x/y of positioned elements.
707 if (r->m_object->isText())
708 toRenderText(r->m_object)->positionLineBox(r->m_box);
709 else if (r->m_object->isBox())
710 toRenderBox(r->m_object)->positionLineBox(r->m_box);
714 void RenderBlockFlow::appendFloatingObjectToLastLine(FloatingObject* floatingObject)
716 ASSERT(!floatingObject->originatingLine());
717 floatingObject->setOriginatingLine(lastRootBox());
718 lastRootBox()->appendFloat(floatingObject->renderer());
721 // FIXME: This should be a BidiStatus constructor or create method.
722 static inline BidiStatus statusWithDirection(TextDirection textDirection, bool isOverride)
724 WTF::Unicode::Direction direction = textDirection == LTR ? LeftToRight : RightToLeft;
725 RefPtr<BidiContext> context = BidiContext::create(textDirection == LTR ? 0 : 1, direction, isOverride, FromStyleOrDOM);
727 // This copies BidiStatus and may churn the ref on BidiContext I doubt it matters.
728 return BidiStatus(direction, direction, direction, context.release());
731 static inline void setupResolverToResumeInIsolate(InlineBidiResolver& resolver, RenderObject* root, RenderObject* startObject)
733 if (root != startObject) {
734 RenderObject* parent = startObject->parent();
735 setupResolverToResumeInIsolate(resolver, root, parent);
736 notifyObserverEnteredObject(&resolver, startObject);
740 static void restoreIsolatedMidpointStates(InlineBidiResolver& topResolver, InlineBidiResolver& isolatedResolver)
742 while (!isolatedResolver.isolatedRuns().isEmpty()) {
743 BidiRun* run = isolatedResolver.isolatedRuns().last();
744 isolatedResolver.isolatedRuns().removeLast();
745 topResolver.setMidpointStateForIsolatedRun(run, isolatedResolver.midpointStateForIsolatedRun(run));
749 // FIXME: BidiResolver should have this logic.
750 static inline void constructBidiRunsForLine(const RenderBlockFlow* block, InlineBidiResolver& topResolver, BidiRunList<BidiRun>& bidiRuns, const InlineIterator& endOfLine, VisualDirectionOverride override, bool previousLineBrokeCleanly, bool isNewUBAParagraph)
752 // FIXME: We should pass a BidiRunList into createBidiRunsForLine instead
753 // of the resolver owning the runs.
754 ASSERT(&topResolver.runs() == &bidiRuns);
755 ASSERT(topResolver.position() != endOfLine);
756 RenderObject* currentRoot = topResolver.position().root();
757 topResolver.createBidiRunsForLine(endOfLine, override, previousLineBrokeCleanly);
759 while (!topResolver.isolatedRuns().isEmpty()) {
760 // It does not matter which order we resolve the runs as long as we resolve them all.
761 BidiRun* isolatedRun = topResolver.isolatedRuns().last();
762 topResolver.isolatedRuns().removeLast();
764 RenderObject* startObj = isolatedRun->object();
766 // Only inlines make sense with unicode-bidi: isolate (blocks are already isolated).
767 // FIXME: Because enterIsolate is not passed a RenderObject, we have to crawl up the
768 // tree to see which parent inline is the isolate. We could change enterIsolate
769 // to take a RenderObject and do this logic there, but that would be a layering
770 // violation for BidiResolver (which knows nothing about RenderObject).
771 RenderInline* isolatedInline = toRenderInline(highestContainingIsolateWithinRoot(startObj, currentRoot));
772 ASSERT(isolatedInline);
774 InlineBidiResolver isolatedResolver;
775 LineMidpointState& isolatedLineMidpointState = isolatedResolver.midpointState();
776 isolatedLineMidpointState = topResolver.midpointStateForIsolatedRun(isolatedRun);
777 EUnicodeBidi unicodeBidi = isolatedInline->style()->unicodeBidi();
778 TextDirection direction;
779 if (unicodeBidi == Plaintext) {
780 if (isNewUBAParagraph)
781 direction = determinePlaintextDirectionality(isolatedInline, startObj);
783 direction = determinePlaintextDirectionality(isolatedInline);
785 ASSERT(unicodeBidi == Isolate || unicodeBidi == IsolateOverride);
786 direction = isolatedInline->style()->direction();
788 isolatedResolver.setStatus(statusWithDirection(direction, isOverride(unicodeBidi)));
790 setupResolverToResumeInIsolate(isolatedResolver, isolatedInline, startObj);
792 // The starting position is the beginning of the first run within the isolate that was identified
793 // during the earlier call to createBidiRunsForLine. This can be but is not necessarily the
794 // first run within the isolate.
795 InlineIterator iter = InlineIterator(isolatedInline, startObj, isolatedRun->m_start);
796 isolatedResolver.setPositionIgnoringNestedIsolates(iter);
797 // We stop at the next end of line; we may re-enter this isolate in the next call to constructBidiRuns().
798 // FIXME: What should end and previousLineBrokeCleanly be?
799 // rniwa says previousLineBrokeCleanly is just a WinIE hack and could always be false here?
800 isolatedResolver.createBidiRunsForLine(endOfLine, NoVisualOverride, previousLineBrokeCleanly);
802 ASSERT(isolatedResolver.runs().runCount());
803 if (isolatedResolver.runs().runCount())
804 bidiRuns.replaceRunWithRuns(isolatedRun, isolatedResolver.runs());
806 // If we encountered any nested isolate runs, just move them
807 // to the top resolver's list for later processing.
808 if (!isolatedResolver.isolatedRuns().isEmpty()) {
809 topResolver.isolatedRuns().appendVector(isolatedResolver.isolatedRuns());
810 currentRoot = isolatedInline;
811 restoreIsolatedMidpointStates(topResolver, isolatedResolver);
816 // This function constructs line boxes for all of the text runs in the resolver and computes their position.
817 RootInlineBox* RenderBlockFlow::createLineBoxesFromBidiRuns(unsigned bidiLevel, BidiRunList<BidiRun>& bidiRuns, const InlineIterator& end, LineInfo& lineInfo, VerticalPositionCache& verticalPositionCache, BidiRun* trailingSpaceRun, WordMeasurements& wordMeasurements)
819 if (!bidiRuns.runCount())
822 // FIXME: Why is this only done when we had runs?
823 lineInfo.setLastLine(!end.object());
825 RootInlineBox* lineBox = constructLine(bidiRuns, lineInfo);
829 lineBox->setBidiLevel(bidiLevel);
830 lineBox->setEndsWithBreak(lineInfo.previousLineBrokeCleanly());
832 bool isSVGRootInlineBox = lineBox->isSVGRootInlineBox();
834 GlyphOverflowAndFallbackFontsMap textBoxDataMap;
836 // Now we position all of our text runs horizontally.
837 if (!isSVGRootInlineBox)
838 computeInlineDirectionPositionsForLine(lineBox, lineInfo, bidiRuns.firstRun(), trailingSpaceRun, end.atEnd(), textBoxDataMap, verticalPositionCache, wordMeasurements);
840 // Now position our text runs vertically.
841 computeBlockDirectionPositionsForLine(lineBox, bidiRuns.firstRun(), textBoxDataMap, verticalPositionCache);
843 // SVG text layout code computes vertical & horizontal positions on its own.
844 // Note that we still need to execute computeVerticalPositionsForLine() as
845 // it calls InlineTextBox::positionLineBox(), which tracks whether the box
846 // contains reversed text or not. If we wouldn't do that editing and thus
847 // text selection in RTL boxes would not work as expected.
848 if (isSVGRootInlineBox) {
850 toSVGRootInlineBox(lineBox)->computePerCharacterLayoutInformation();
853 // Compute our overflow now.
854 lineBox->computeOverflow(lineBox->lineTop(), lineBox->lineBottom(), textBoxDataMap);
859 static void deleteLineRange(LineLayoutState& layoutState, RootInlineBox* startLine, RootInlineBox* stopLine = 0)
861 RootInlineBox* boxToDelete = startLine;
862 while (boxToDelete && boxToDelete != stopLine) {
863 layoutState.updateRepaintRangeFromBox(boxToDelete);
864 // Note: deleteLineRange(firstRootBox()) is not identical to deleteLineBoxTree().
865 // deleteLineBoxTree uses nextLineBox() instead of nextRootBox() when traversing.
866 RootInlineBox* next = boxToDelete->nextRootBox();
867 boxToDelete->deleteLine();
872 void RenderBlockFlow::layoutRunsAndFloats(LineLayoutState& layoutState)
874 // We want to skip ahead to the first dirty line
875 InlineBidiResolver resolver;
876 RootInlineBox* startLine = determineStartPosition(layoutState, resolver);
878 unsigned consecutiveHyphenatedLines = 0;
880 for (RootInlineBox* line = startLine->prevRootBox(); line && line->isHyphenated(); line = line->prevRootBox())
881 consecutiveHyphenatedLines++;
884 if (containsFloats())
885 layoutState.setLastFloat(m_floatingObjects->set().last());
887 // We also find the first clean line and extract these lines. We will add them back
888 // if we determine that we're able to synchronize after handling all our dirty lines.
889 InlineIterator cleanLineStart;
890 BidiStatus cleanLineBidiStatus;
891 if (!layoutState.isFullLayout() && startLine)
892 determineEndPosition(layoutState, startLine, cleanLineStart, cleanLineBidiStatus);
895 if (!layoutState.usesRepaintBounds())
896 layoutState.setRepaintRange(logicalHeight());
897 deleteLineRange(layoutState, startLine);
900 if (!layoutState.isFullLayout() && lastRootBox() && lastRootBox()->endsWithBreak()) {
901 // If the last line before the start line ends with a line break that clear floats,
902 // adjust the height accordingly.
903 // A line break can be either the first or the last object on a line, depending on its direction.
904 if (InlineBox* lastLeafChild = lastRootBox()->lastLeafChild()) {
905 RenderObject* lastObject = &lastLeafChild->renderer();
906 if (!lastObject->isBR())
907 lastObject = &lastRootBox()->firstLeafChild()->renderer();
908 if (lastObject->isBR()) {
909 EClear clear = lastObject->style()->clear();
916 layoutRunsAndFloatsInRange(layoutState, resolver, cleanLineStart, cleanLineBidiStatus, consecutiveHyphenatedLines);
917 linkToEndLineIfNeeded(layoutState);
918 repaintDirtyFloats(layoutState.floats());
921 RenderTextInfo::RenderTextInfo()
927 RenderTextInfo::~RenderTextInfo()
931 // Before restarting the layout loop with a new logicalHeight, remove all floats that were added and reset the resolver.
932 inline const InlineIterator& RenderBlockFlow::restartLayoutRunsAndFloatsInRange(LayoutUnit oldLogicalHeight, LayoutUnit newLogicalHeight, FloatingObject* lastFloatFromPreviousLine, InlineBidiResolver& resolver, const InlineIterator& oldEnd)
934 removeFloatingObjectsBelow(lastFloatFromPreviousLine, oldLogicalHeight);
935 setLogicalHeight(newLogicalHeight);
936 resolver.setPositionIgnoringNestedIsolates(oldEnd);
940 void RenderBlockFlow::layoutRunsAndFloatsInRange(LineLayoutState& layoutState, InlineBidiResolver& resolver, const InlineIterator& cleanLineStart, const BidiStatus& cleanLineBidiStatus, unsigned consecutiveHyphenatedLines)
942 RenderStyle* styleToUse = style();
943 bool paginated = view()->layoutState() && view()->layoutState()->isPaginated();
944 LineMidpointState& lineMidpointState = resolver.midpointState();
945 InlineIterator endOfLine = resolver.position();
946 bool checkForEndLineMatch = layoutState.endLine();
947 RenderTextInfo renderTextInfo;
948 VerticalPositionCache verticalPositionCache;
950 LineBreaker lineBreaker(this);
952 LayoutSize logicalOffsetFromShapeContainer;
954 while (!endOfLine.atEnd()) {
955 // FIXME: Is this check necessary before the first iteration or can it be moved to the end?
956 if (checkForEndLineMatch) {
957 layoutState.setEndLineMatched(matchedEndLine(layoutState, resolver, cleanLineStart, cleanLineBidiStatus));
958 if (layoutState.endLineMatched()) {
959 resolver.setPosition(InlineIterator(resolver.position().root(), 0, 0), 0);
964 lineMidpointState.reset();
966 layoutState.lineInfo().setEmpty(true);
967 layoutState.lineInfo().resetRunsFromLeadingWhitespace();
969 const InlineIterator previousEndofLine = endOfLine;
970 bool isNewUBAParagraph = layoutState.lineInfo().previousLineBrokeCleanly();
971 FloatingObject* lastFloatFromPreviousLine = (containsFloats()) ? m_floatingObjects->set().last() : 0;
973 WordMeasurements wordMeasurements;
974 endOfLine = lineBreaker.nextLineBreak(resolver, layoutState.lineInfo(), renderTextInfo, lastFloatFromPreviousLine, consecutiveHyphenatedLines, wordMeasurements);
975 renderTextInfo.m_lineBreakIterator.resetPriorContext();
976 if (resolver.position().atEnd()) {
977 // FIXME: We shouldn't be creating any runs in nextLineBreak to begin with!
978 // Once BidiRunList is separated from BidiResolver this will not be needed.
979 resolver.runs().deleteRuns();
980 resolver.markCurrentRunEmpty(); // FIXME: This can probably be replaced by an ASSERT (or just removed).
981 layoutState.setCheckForFloatsFromLastLine(true);
982 resolver.setPosition(InlineIterator(resolver.position().root(), 0, 0), 0);
986 ASSERT(endOfLine != resolver.position());
988 // This is a short-cut for empty lines.
989 if (layoutState.lineInfo().isEmpty()) {
991 lastRootBox()->setLineBreakInfo(endOfLine.object(), endOfLine.offset(), resolver.status());
993 VisualDirectionOverride override = (styleToUse->rtlOrdering() == VisualOrder ? (styleToUse->direction() == LTR ? VisualLeftToRightOverride : VisualRightToLeftOverride) : NoVisualOverride);
994 if (isNewUBAParagraph && styleToUse->unicodeBidi() == Plaintext && !resolver.context()->parent()) {
995 TextDirection direction = determinePlaintextDirectionality(resolver.position().root(), resolver.position().object(), resolver.position().offset());
996 resolver.setStatus(BidiStatus(direction, isOverride(styleToUse->unicodeBidi())));
998 // FIXME: This ownership is reversed. We should own the BidiRunList and pass it to createBidiRunsForLine.
999 BidiRunList<BidiRun>& bidiRuns = resolver.runs();
1000 constructBidiRunsForLine(this, resolver, bidiRuns, endOfLine, override, layoutState.lineInfo().previousLineBrokeCleanly(), isNewUBAParagraph);
1001 ASSERT(resolver.position() == endOfLine);
1003 BidiRun* trailingSpaceRun = resolver.trailingSpaceRun();
1005 if (bidiRuns.runCount() && lineBreaker.lineWasHyphenated()) {
1006 bidiRuns.logicallyLastRun()->m_hasHyphen = true;
1007 consecutiveHyphenatedLines++;
1009 consecutiveHyphenatedLines = 0;
1011 // Now that the runs have been ordered, we create the line boxes.
1012 // At the same time we figure out where border/padding/margin should be applied for
1013 // inline flow boxes.
1015 LayoutUnit oldLogicalHeight = logicalHeight();
1016 RootInlineBox* lineBox = createLineBoxesFromBidiRuns(resolver.status().context->level(), bidiRuns, endOfLine, layoutState.lineInfo(), verticalPositionCache, trailingSpaceRun, wordMeasurements);
1018 bidiRuns.deleteRuns();
1019 resolver.markCurrentRunEmpty(); // FIXME: This can probably be replaced by an ASSERT (or just removed).
1022 lineBox->setLineBreakInfo(endOfLine.object(), endOfLine.offset(), resolver.status());
1023 if (layoutState.usesRepaintBounds())
1024 layoutState.updateRepaintRangeFromBox(lineBox);
1027 LayoutUnit adjustment = 0;
1028 adjustLinePositionForPagination(lineBox, adjustment, layoutState.flowThread());
1030 LayoutUnit oldLineWidth = availableLogicalWidthForLine(oldLogicalHeight, layoutState.lineInfo().isFirstLine());
1031 lineBox->adjustBlockDirectionPosition(adjustment.toFloat());
1032 if (layoutState.usesRepaintBounds())
1033 layoutState.updateRepaintRangeFromBox(lineBox);
1035 if (availableLogicalWidthForLine(oldLogicalHeight + adjustment, layoutState.lineInfo().isFirstLine()) != oldLineWidth) {
1036 // We have to delete this line, remove all floats that got added, and let line layout re-run.
1037 lineBox->deleteLine();
1038 endOfLine = restartLayoutRunsAndFloatsInRange(oldLogicalHeight, oldLogicalHeight + adjustment, lastFloatFromPreviousLine, resolver, previousEndofLine);
1042 setLogicalHeight(lineBox->lineBottomWithLeading());
1048 for (size_t i = 0; i < lineBreaker.positionedObjects().size(); ++i)
1049 setStaticPositions(this, lineBreaker.positionedObjects()[i]);
1051 if (!layoutState.lineInfo().isEmpty()) {
1052 layoutState.lineInfo().setFirstLine(false);
1053 clearFloats(lineBreaker.clear());
1056 if (m_floatingObjects && lastRootBox()) {
1057 const FloatingObjectSet& floatingObjectSet = m_floatingObjects->set();
1058 FloatingObjectSetIterator it = floatingObjectSet.begin();
1059 FloatingObjectSetIterator end = floatingObjectSet.end();
1060 if (layoutState.lastFloat()) {
1061 FloatingObjectSetIterator lastFloatIterator = floatingObjectSet.find(layoutState.lastFloat());
1062 ASSERT(lastFloatIterator != end);
1063 ++lastFloatIterator;
1064 it = lastFloatIterator;
1066 for (; it != end; ++it) {
1067 FloatingObject* f = *it;
1068 appendFloatingObjectToLastLine(f);
1069 ASSERT(f->renderer() == layoutState.floats()[layoutState.floatIndex()].object);
1070 // If a float's geometry has changed, give up on syncing with clean lines.
1071 if (layoutState.floats()[layoutState.floatIndex()].rect != f->frameRect())
1072 checkForEndLineMatch = false;
1073 layoutState.setFloatIndex(layoutState.floatIndex() + 1);
1075 layoutState.setLastFloat(!floatingObjectSet.isEmpty() ? floatingObjectSet.last() : 0);
1078 lineMidpointState.reset();
1079 resolver.setPosition(endOfLine, numberOfIsolateAncestors(endOfLine));
1082 // In case we already adjusted the line positions during this layout to avoid widows
1083 // then we need to ignore the possibility of having a new widows situation.
1084 // Otherwise, we risk leaving empty containers which is against the block fragmentation principles.
1085 if (paginated && !style()->hasAutoWidows() && !didBreakAtLineToAvoidWidow()) {
1086 // Check the line boxes to make sure we didn't create unacceptable widows.
1087 // However, we'll prioritize orphans - so nothing we do here should create
1090 RootInlineBox* lineBox = lastRootBox();
1092 // Count from the end of the block backwards, to see how many hanging
1094 RootInlineBox* firstLineInBlock = firstRootBox();
1095 int numLinesHanging = 1;
1096 while (lineBox && lineBox != firstLineInBlock && !lineBox->isFirstAfterPageBreak()) {
1098 lineBox = lineBox->prevRootBox();
1101 // If there were no breaks in the block, we didn't create any widows.
1102 if (!lineBox || !lineBox->isFirstAfterPageBreak() || lineBox == firstLineInBlock)
1105 if (numLinesHanging < style()->widows()) {
1106 // We have detected a widow. Now we need to work out how many
1107 // lines there are on the previous page, and how many we need
1109 int numLinesNeeded = style()->widows() - numLinesHanging;
1110 RootInlineBox* currentFirstLineOfNewPage = lineBox;
1112 // Count the number of lines in the previous page.
1113 lineBox = lineBox->prevRootBox();
1114 int numLinesInPreviousPage = 1;
1115 while (lineBox && lineBox != firstLineInBlock && !lineBox->isFirstAfterPageBreak()) {
1116 ++numLinesInPreviousPage;
1117 lineBox = lineBox->prevRootBox();
1120 // If there was an explicit value for orphans, respect that. If not, we still
1121 // shouldn't create a situation where we make an orphan bigger than the initial value.
1122 // This means that setting widows implies we also care about orphans, but given
1123 // the specification says the initial orphan value is non-zero, this is ok. The
1124 // author is always free to set orphans explicitly as well.
1125 int orphans = style()->hasAutoOrphans() ? style()->initialOrphans() : style()->orphans();
1126 int numLinesAvailable = numLinesInPreviousPage - orphans;
1127 if (numLinesAvailable <= 0)
1130 int numLinesToTake = min(numLinesAvailable, numLinesNeeded);
1131 // Wind back from our first widowed line.
1132 lineBox = currentFirstLineOfNewPage;
1133 for (int i = 0; i < numLinesToTake; ++i)
1134 lineBox = lineBox->prevRootBox();
1136 // We now want to break at this line. Remember for next layout and trigger relayout.
1137 setBreakAtLineToAvoidWidow(lineCount(lineBox));
1138 markLinesDirtyInBlockRange(lastRootBox()->lineBottomWithLeading(), lineBox->lineBottomWithLeading(), lineBox);
1142 clearDidBreakAtLineToAvoidWidow();
1145 void RenderBlockFlow::linkToEndLineIfNeeded(LineLayoutState& layoutState)
1147 if (layoutState.endLine()) {
1148 if (layoutState.endLineMatched()) {
1149 bool paginated = view()->layoutState() && view()->layoutState()->isPaginated();
1150 // Attach all the remaining lines, and then adjust their y-positions as needed.
1151 LayoutUnit delta = logicalHeight() - layoutState.endLineLogicalTop();
1152 for (RootInlineBox* line = layoutState.endLine(); line; line = line->nextRootBox()) {
1155 delta -= line->paginationStrut();
1156 adjustLinePositionForPagination(line, delta, layoutState.flowThread());
1159 layoutState.updateRepaintRangeFromBox(line, delta);
1160 line->adjustBlockDirectionPosition(delta.toFloat());
1162 if (Vector<RenderBox*>* cleanLineFloats = line->floatsPtr()) {
1163 Vector<RenderBox*>::iterator end = cleanLineFloats->end();
1164 for (Vector<RenderBox*>::iterator f = cleanLineFloats->begin(); f != end; ++f) {
1165 FloatingObject* floatingObject = insertFloatingObject(*f);
1166 ASSERT(!floatingObject->originatingLine());
1167 floatingObject->setOriginatingLine(line);
1168 setLogicalHeight(logicalTopForChild(*f) - marginBeforeForChild(*f) + delta);
1169 positionNewFloats();
1173 setLogicalHeight(lastRootBox()->lineBottomWithLeading());
1175 // Delete all the remaining lines.
1176 deleteLineRange(layoutState, layoutState.endLine());
1180 if (m_floatingObjects && (layoutState.checkForFloatsFromLastLine() || positionNewFloats()) && lastRootBox()) {
1181 // In case we have a float on the last line, it might not be positioned up to now.
1182 // This has to be done before adding in the bottom border/padding, or the float will
1183 // include the padding incorrectly. -dwh
1184 if (layoutState.checkForFloatsFromLastLine()) {
1185 LayoutUnit bottomVisualOverflow = lastRootBox()->logicalBottomVisualOverflow();
1186 LayoutUnit bottomLayoutOverflow = lastRootBox()->logicalBottomLayoutOverflow();
1187 TrailingFloatsRootInlineBox* trailingFloatsLineBox = new TrailingFloatsRootInlineBox(*this);
1188 m_lineBoxes.appendLineBox(trailingFloatsLineBox);
1189 trailingFloatsLineBox->setConstructed();
1190 GlyphOverflowAndFallbackFontsMap textBoxDataMap;
1191 VerticalPositionCache verticalPositionCache;
1192 LayoutUnit blockLogicalHeight = logicalHeight();
1193 trailingFloatsLineBox->alignBoxesInBlockDirection(blockLogicalHeight, textBoxDataMap, verticalPositionCache);
1194 trailingFloatsLineBox->setLineTopBottomPositions(blockLogicalHeight, blockLogicalHeight, blockLogicalHeight, blockLogicalHeight);
1195 trailingFloatsLineBox->setPaginatedLineWidth(availableLogicalWidthForContent());
1196 LayoutRect logicalLayoutOverflow(0, blockLogicalHeight, 1, bottomLayoutOverflow - blockLogicalHeight);
1197 LayoutRect logicalVisualOverflow(0, blockLogicalHeight, 1, bottomVisualOverflow - blockLogicalHeight);
1198 trailingFloatsLineBox->setOverflowFromLogicalRects(logicalLayoutOverflow, logicalVisualOverflow, trailingFloatsLineBox->lineTop(), trailingFloatsLineBox->lineBottom());
1201 const FloatingObjectSet& floatingObjectSet = m_floatingObjects->set();
1202 FloatingObjectSetIterator it = floatingObjectSet.begin();
1203 FloatingObjectSetIterator end = floatingObjectSet.end();
1204 if (layoutState.lastFloat()) {
1205 FloatingObjectSetIterator lastFloatIterator = floatingObjectSet.find(layoutState.lastFloat());
1206 ASSERT(lastFloatIterator != end);
1207 ++lastFloatIterator;
1208 it = lastFloatIterator;
1210 for (; it != end; ++it)
1211 appendFloatingObjectToLastLine(*it);
1212 layoutState.setLastFloat(!floatingObjectSet.isEmpty() ? floatingObjectSet.last() : 0);
1216 void RenderBlockFlow::repaintDirtyFloats(Vector<FloatWithRect>& floats)
1218 size_t floatCount = floats.size();
1219 // Floats that did not have layout did not repaint when we laid them out. They would have
1220 // painted by now if they had moved, but if they stayed at (0, 0), they still need to be
1222 for (size_t i = 0; i < floatCount; ++i) {
1223 if (!floats[i].everHadLayout) {
1224 RenderBox* f = floats[i].object;
1225 if (!f->x() && !f->y() && f->checkForRepaint()) {
1226 if (RuntimeEnabledFeatures::repaintAfterLayoutEnabled())
1227 f->setShouldDoFullRepaintAfterLayout(true);
1235 struct InlineMinMaxIterator {
1236 /* InlineMinMaxIterator is a class that will iterate over all render objects that contribute to
1237 inline min/max width calculations. Note the following about the way it walks:
1238 (1) Positioned content is skipped (since it does not contribute to min/max width of a block)
1239 (2) We do not drill into the children of floats or replaced elements, since you can't break
1240 in the middle of such an element.
1241 (3) Inline flows (e.g., <a>, <span>, <i>) are walked twice, since each side can have
1242 distinct borders/margin/padding that contribute to the min/max width.
1244 RenderObject* parent;
1245 RenderObject* current;
1248 InlineMinMaxIterator(RenderObject* p, bool end = false)
1249 : parent(p), current(p), endOfInline(end)
1254 RenderObject* next();
1257 RenderObject* InlineMinMaxIterator::next()
1259 RenderObject* result = 0;
1260 bool oldEndOfInline = endOfInline;
1261 endOfInline = false;
1262 while (current || current == parent) {
1263 if (!oldEndOfInline && (current == parent || (!current->isFloating() && !current->isReplaced() && !current->isOutOfFlowPositioned())))
1264 result = current->firstChild();
1267 // We hit the end of our inline. (It was empty, e.g., <span></span>.)
1268 if (!oldEndOfInline && current->isRenderInline()) {
1274 while (current && current != parent) {
1275 result = current->nextSibling();
1278 current = current->parent();
1279 if (current && current != parent && current->isRenderInline()) {
1290 if (!result->isOutOfFlowPositioned() && (result->isText() || result->isFloating() || result->isReplaced() || result->isRenderInline()))
1297 // Update our position.
1302 static LayoutUnit getBPMWidth(LayoutUnit childValue, Length cssUnit)
1304 if (cssUnit.type() != Auto)
1305 return (cssUnit.isFixed() ? static_cast<LayoutUnit>(cssUnit.value()) : childValue);
1309 static LayoutUnit getBorderPaddingMargin(RenderBoxModelObject* child, bool endOfInline)
1311 RenderStyle* childStyle = child->style();
1313 return getBPMWidth(child->marginEnd(), childStyle->marginEnd()) +
1314 getBPMWidth(child->paddingEnd(), childStyle->paddingEnd()) +
1317 return getBPMWidth(child->marginStart(), childStyle->marginStart()) +
1318 getBPMWidth(child->paddingStart(), childStyle->paddingStart()) +
1319 child->borderStart();
1322 static inline void stripTrailingSpace(float& inlineMax, float& inlineMin, RenderObject* trailingSpaceChild)
1324 if (trailingSpaceChild && trailingSpaceChild->isText()) {
1325 // Collapse away the trailing space at the end of a block.
1326 RenderText* t = toRenderText(trailingSpaceChild);
1327 const UChar space = ' ';
1328 const Font& font = t->style()->font(); // FIXME: This ignores first-line.
1329 float spaceWidth = font.width(RenderBlockFlow::constructTextRun(t, font, &space, 1, t->style(), LTR));
1330 inlineMax -= spaceWidth + font.fontDescription().wordSpacing();
1331 if (inlineMin > inlineMax)
1332 inlineMin = inlineMax;
1336 static inline void updatePreferredWidth(LayoutUnit& preferredWidth, float& result)
1338 LayoutUnit snappedResult = LayoutUnit::fromFloatCeil(result);
1339 preferredWidth = max(snappedResult, preferredWidth);
1342 // When converting between floating point and LayoutUnits we risk losing precision
1343 // with each conversion. When this occurs while accumulating our preferred widths,
1344 // we can wind up with a line width that's larger than our maxPreferredWidth due to
1345 // pure float accumulation.
1346 static inline LayoutUnit adjustFloatForSubPixelLayout(float value)
1348 return LayoutUnit::fromFloatCeil(value);
1351 // FIXME: This function should be broken into something less monolithic.
1352 // FIXME: The main loop here is very similar to LineBreaker::nextSegmentBreak. They can probably reuse code.
1353 void RenderBlockFlow::computeInlinePreferredLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth)
1355 float inlineMax = 0;
1356 float inlineMin = 0;
1358 RenderStyle* styleToUse = style();
1359 RenderBlock* containingBlock = this->containingBlock();
1360 LayoutUnit cw = containingBlock ? containingBlock->contentLogicalWidth() : LayoutUnit();
1362 // If we are at the start of a line, we want to ignore all white-space.
1363 // Also strip spaces if we previously had text that ended in a trailing space.
1364 bool stripFrontSpaces = true;
1365 RenderObject* trailingSpaceChild = 0;
1367 // Firefox and Opera will allow a table cell to grow to fit an image inside it under
1368 // very specific cirucumstances (in order to match common WinIE renderings).
1369 // Not supporting the quirk has caused us to mis-render some real sites. (See Bugzilla 10517.)
1370 bool allowImagesToBreak = !document().inQuirksMode() || !isTableCell() || !styleToUse->logicalWidth().isIntrinsicOrAuto();
1372 bool autoWrap, oldAutoWrap;
1373 autoWrap = oldAutoWrap = styleToUse->autoWrap();
1375 InlineMinMaxIterator childIterator(this);
1377 // Only gets added to the max preffered width once.
1378 bool addedTextIndent = false;
1379 // Signals the text indent was more negative than the min preferred width
1380 bool hasRemainingNegativeTextIndent = false;
1382 LayoutUnit textIndent = minimumValueForLength(styleToUse->textIndent(), cw);
1383 RenderObject* prevFloat = 0;
1384 bool isPrevChildInlineFlow = false;
1385 bool shouldBreakLineAfterText = false;
1386 while (RenderObject* child = childIterator.next()) {
1387 autoWrap = child->isReplaced() ? child->parent()->style()->autoWrap() :
1388 child->style()->autoWrap();
1390 if (!child->isBR()) {
1391 // Step One: determine whether or not we need to go ahead and
1392 // terminate our current line. Each discrete chunk can become
1393 // the new min-width, if it is the widest chunk seen so far, and
1394 // it can also become the max-width.
1396 // Children fall into three categories:
1397 // (1) An inline flow object. These objects always have a min/max of 0,
1398 // and are included in the iteration solely so that their margins can
1401 // (2) An inline non-text non-flow object, e.g., an inline replaced element.
1402 // These objects can always be on a line by themselves, so in this situation
1403 // we need to go ahead and break the current line, and then add in our own
1404 // margins and min/max width on its own line, and then terminate the line.
1406 // (3) A text object. Text runs can have breakable characters at the start,
1407 // the middle or the end. They may also lose whitespace off the front if
1408 // we're already ignoring whitespace. In order to compute accurate min-width
1409 // information, we need three pieces of information.
1410 // (a) the min-width of the first non-breakable run. Should be 0 if the text string
1411 // starts with whitespace.
1412 // (b) the min-width of the last non-breakable run. Should be 0 if the text string
1413 // ends with whitespace.
1414 // (c) the min/max width of the string (trimmed for whitespace).
1416 // If the text string starts with whitespace, then we need to go ahead and
1417 // terminate our current line (unless we're already in a whitespace stripping
1420 // If the text string has a breakable character in the middle, but didn't start
1421 // with whitespace, then we add the width of the first non-breakable run and
1422 // then end the current line. We then need to use the intermediate min/max width
1423 // values (if any of them are larger than our current min/max). We then look at
1424 // the width of the last non-breakable run and use that to start a new line
1425 // (unless we end in whitespace).
1426 RenderStyle* childStyle = child->style();
1430 if (!child->isText()) {
1431 // Case (1) and (2). Inline replaced and inline flow elements.
1432 if (child->isRenderInline()) {
1433 // Add in padding/border/margin from the appropriate side of
1435 float bpm = getBorderPaddingMargin(toRenderInline(child), childIterator.endOfInline).toFloat();
1439 inlineMin += childMin;
1440 inlineMax += childMax;
1442 child->clearPreferredLogicalWidthsDirty();
1444 // Inline replaced elts add in their margins to their min/max values.
1445 LayoutUnit margins = 0;
1446 Length startMargin = childStyle->marginStart();
1447 Length endMargin = childStyle->marginEnd();
1448 if (startMargin.isFixed())
1449 margins += adjustFloatForSubPixelLayout(startMargin.value());
1450 if (endMargin.isFixed())
1451 margins += adjustFloatForSubPixelLayout(endMargin.value());
1452 childMin += margins.ceilToFloat();
1453 childMax += margins.ceilToFloat();
1457 if (!child->isRenderInline() && !child->isText()) {
1458 // Case (2). Inline replaced elements and floats.
1459 // Go ahead and terminate the current line as far as
1460 // minwidth is concerned.
1461 LayoutUnit childMinPreferredLogicalWidth, childMaxPreferredLogicalWidth;
1462 if (child->isBox() && child->isHorizontalWritingMode() != isHorizontalWritingMode()) {
1463 RenderBox* childBox = toRenderBox(child);
1464 LogicalExtentComputedValues computedValues;
1465 childBox->computeLogicalHeight(childBox->borderAndPaddingLogicalHeight(), 0, computedValues);
1466 childMinPreferredLogicalWidth = childMaxPreferredLogicalWidth = computedValues.m_extent;
1468 childMinPreferredLogicalWidth = child->minPreferredLogicalWidth();
1469 childMaxPreferredLogicalWidth = child->maxPreferredLogicalWidth();
1471 childMin += childMinPreferredLogicalWidth.ceilToFloat();
1472 childMax += childMaxPreferredLogicalWidth.ceilToFloat();
1474 bool clearPreviousFloat;
1475 if (child->isFloating()) {
1476 clearPreviousFloat = (prevFloat
1477 && ((prevFloat->style()->floating() == LeftFloat && (childStyle->clear() & CLEFT))
1478 || (prevFloat->style()->floating() == RightFloat && (childStyle->clear() & CRIGHT))));
1481 clearPreviousFloat = false;
1484 bool canBreakReplacedElement = !child->isImage() || allowImagesToBreak;
1485 if ((canBreakReplacedElement && (autoWrap || oldAutoWrap) && (!isPrevChildInlineFlow || shouldBreakLineAfterText)) || clearPreviousFloat) {
1486 updatePreferredWidth(minLogicalWidth, inlineMin);
1490 // If we're supposed to clear the previous float, then terminate maxwidth as well.
1491 if (clearPreviousFloat) {
1492 updatePreferredWidth(maxLogicalWidth, inlineMax);
1496 // Add in text-indent. This is added in only once.
1497 if (!addedTextIndent && !child->isFloating()) {
1498 float ceiledTextIndent = textIndent.ceilToFloat();
1499 childMin += ceiledTextIndent;
1500 childMax += ceiledTextIndent;
1503 textIndent = adjustFloatForSubPixelLayout(childMin);
1505 addedTextIndent = true;
1508 // Add our width to the max.
1509 inlineMax += max<float>(0, childMax);
1511 if (!autoWrap || !canBreakReplacedElement || (isPrevChildInlineFlow && !shouldBreakLineAfterText)) {
1512 if (child->isFloating())
1513 updatePreferredWidth(minLogicalWidth, childMin);
1515 inlineMin += childMin;
1517 // Now check our line.
1518 updatePreferredWidth(minLogicalWidth, childMin);
1520 // Now start a new line.
1524 if (autoWrap && canBreakReplacedElement && isPrevChildInlineFlow) {
1525 updatePreferredWidth(minLogicalWidth, inlineMin);
1529 // We are no longer stripping whitespace at the start of
1531 if (!child->isFloating()) {
1532 stripFrontSpaces = false;
1533 trailingSpaceChild = 0;
1535 } else if (child->isText()) {
1537 RenderText* t = toRenderText(child);
1539 if (t->isWordBreak()) {
1540 updatePreferredWidth(minLogicalWidth, inlineMin);
1545 if (t->style()->hasTextCombine() && t->isCombineText())
1546 toRenderCombineText(t)->combineText();
1548 // Determine if we have a breakable character. Pass in
1549 // whether or not we should ignore any spaces at the front
1550 // of the string. If those are going to be stripped out,
1551 // then they shouldn't be considered in the breakable char
1553 bool hasBreakableChar, hasBreak;
1554 float firstLineMinWidth, lastLineMinWidth;
1555 bool hasBreakableStart, hasBreakableEnd;
1556 float firstLineMaxWidth, lastLineMaxWidth;
1557 t->trimmedPrefWidths(inlineMax,
1558 firstLineMinWidth, hasBreakableStart, lastLineMinWidth, hasBreakableEnd,
1559 hasBreakableChar, hasBreak, firstLineMaxWidth, lastLineMaxWidth,
1560 childMin, childMax, stripFrontSpaces, styleToUse->direction());
1562 // This text object will not be rendered, but it may still provide a breaking opportunity.
1563 if (!hasBreak && !childMax) {
1564 if (autoWrap && (hasBreakableStart || hasBreakableEnd)) {
1565 updatePreferredWidth(minLogicalWidth, inlineMin);
1571 if (stripFrontSpaces)
1572 trailingSpaceChild = child;
1574 trailingSpaceChild = 0;
1576 // Add in text-indent. This is added in only once.
1578 if (!addedTextIndent || hasRemainingNegativeTextIndent) {
1579 ti = textIndent.ceilToFloat();
1581 firstLineMinWidth += ti;
1583 // It the text indent negative and larger than the child minimum, we re-use the remainder
1584 // in future minimum calculations, but using the negative value again on the maximum
1585 // will lead to under-counting the max pref width.
1586 if (!addedTextIndent) {
1588 firstLineMaxWidth += ti;
1589 addedTextIndent = true;
1593 textIndent = childMin;
1594 hasRemainingNegativeTextIndent = true;
1598 // If we have no breakable characters at all,
1599 // then this is the easy case. We add ourselves to the current
1600 // min and max and continue.
1601 if (!hasBreakableChar) {
1602 inlineMin += childMin;
1604 if (hasBreakableStart) {
1605 updatePreferredWidth(minLogicalWidth, inlineMin);
1607 inlineMin += firstLineMinWidth;
1608 updatePreferredWidth(minLogicalWidth, inlineMin);
1612 inlineMin = childMin;
1614 if (hasBreakableEnd) {
1615 updatePreferredWidth(minLogicalWidth, inlineMin);
1617 shouldBreakLineAfterText = false;
1619 updatePreferredWidth(minLogicalWidth, inlineMin);
1620 inlineMin = lastLineMinWidth;
1621 shouldBreakLineAfterText = true;
1626 inlineMax += firstLineMaxWidth;
1627 updatePreferredWidth(maxLogicalWidth, inlineMax);
1628 updatePreferredWidth(maxLogicalWidth, childMax);
1629 inlineMax = lastLineMaxWidth;
1630 addedTextIndent = true;
1632 inlineMax += max<float>(0, childMax);
1636 // Ignore spaces after a list marker.
1637 if (child->isListMarker())
1638 stripFrontSpaces = true;
1640 updatePreferredWidth(minLogicalWidth, inlineMin);
1641 updatePreferredWidth(maxLogicalWidth, inlineMax);
1642 inlineMin = inlineMax = 0;
1643 stripFrontSpaces = true;
1644 trailingSpaceChild = 0;
1645 addedTextIndent = true;
1648 if (!child->isText() && child->isRenderInline())
1649 isPrevChildInlineFlow = true;
1651 isPrevChildInlineFlow = false;
1653 oldAutoWrap = autoWrap;
1656 if (styleToUse->collapseWhiteSpace())
1657 stripTrailingSpace(inlineMax, inlineMin, trailingSpaceChild);
1659 updatePreferredWidth(minLogicalWidth, inlineMin);
1660 updatePreferredWidth(maxLogicalWidth, inlineMax);
1663 void RenderBlockFlow::layoutInlineChildren(bool relayoutChildren, LayoutUnit& repaintLogicalTop, LayoutUnit& repaintLogicalBottom, LayoutUnit afterEdge)
1665 RenderFlowThread* flowThread = flowThreadContainingBlock();
1666 bool clearLinesForPagination = firstLineBox() && flowThread && !flowThread->hasRegions();
1668 // Figure out if we should clear out our line boxes.
1669 // FIXME: Handle resize eventually!
1670 bool isFullLayout = !firstLineBox() || selfNeedsLayout() || relayoutChildren || clearLinesForPagination;
1671 LineLayoutState layoutState(isFullLayout, repaintLogicalTop, repaintLogicalBottom, flowThread);
1674 lineBoxes()->deleteLineBoxes();
1676 // Text truncation kicks in in two cases:
1677 // 1) If your overflow isn't visible and your text-overflow-mode isn't clip.
1678 // 2) If you're an anonymous block with a block parent that satisfies #1 that was created
1679 // to accomodate a block that has inline and block children. This excludes parents where
1680 // canCollapseAnonymousBlockChild is false, notabley flex items and grid items.
1681 // FIXME: CSS3 says that descendants that are clipped must also know how to truncate. This is insanely
1682 // difficult to figure out in general (especially in the middle of doing layout), so we only handle the
1683 // simple case of an anonymous block truncating when it's parent is clipped.
1684 bool hasTextOverflow = (style()->textOverflow() && hasOverflowClip())
1685 || (isAnonymousBlock() && parent() && parent()->isRenderBlock() && toRenderBlock(parent())->canCollapseAnonymousBlockChild()
1686 && parent()->style()->textOverflow() && parent()->hasOverflowClip());
1688 // Walk all the lines and delete our ellipsis line boxes if they exist.
1689 if (hasTextOverflow)
1690 deleteEllipsisLineBoxes();
1693 // In full layout mode, clear the line boxes of children upfront. Otherwise,
1694 // siblings can run into stale root lineboxes during layout. Then layout
1695 // the replaced elements later. In partial layout mode, line boxes are not
1696 // deleted and only dirtied. In that case, we can layout the replaced
1697 // elements at the same time.
1698 Vector<RenderBox*> replacedChildren;
1699 for (InlineWalker walker(this); !walker.atEnd(); walker.advance()) {
1700 RenderObject* o = walker.current();
1702 if (!layoutState.hasInlineChild() && o->isInline())
1703 layoutState.setHasInlineChild(true);
1705 if (o->isReplaced() || o->isFloating() || o->isOutOfFlowPositioned()) {
1706 RenderBox* box = toRenderBox(o);
1708 updateBlockChildDirtyBitsBeforeLayout(relayoutChildren, box);
1710 if (o->isOutOfFlowPositioned())
1711 o->containingBlock()->insertPositionedObject(box);
1712 else if (o->isFloating())
1713 layoutState.floats().append(FloatWithRect(box));
1714 else if (isFullLayout || o->needsLayout()) {
1715 // Replaced element.
1716 box->dirtyLineBoxes(isFullLayout);
1718 replacedChildren.append(box);
1720 o->layoutIfNeeded();
1722 } else if (o->isText() || (o->isRenderInline() && !walker.atEndOfInline())) {
1724 toRenderInline(o)->updateAlwaysCreateLineBoxes(layoutState.isFullLayout());
1725 if (layoutState.isFullLayout() || o->selfNeedsLayout())
1726 dirtyLineBoxesForRenderer(o, layoutState.isFullLayout());
1727 o->clearNeedsLayout();
1731 for (size_t i = 0; i < replacedChildren.size(); i++)
1732 replacedChildren[i]->layoutIfNeeded();
1734 layoutRunsAndFloats(layoutState);
1737 // Expand the last line to accommodate Ruby and emphasis marks.
1738 int lastLineAnnotationsAdjustment = 0;
1739 if (lastRootBox()) {
1740 LayoutUnit lowestAllowedPosition = max(lastRootBox()->lineBottom(), logicalHeight() + paddingAfter());
1741 if (!style()->isFlippedLinesWritingMode())
1742 lastLineAnnotationsAdjustment = lastRootBox()->computeUnderAnnotationAdjustment(lowestAllowedPosition);
1744 lastLineAnnotationsAdjustment = lastRootBox()->computeOverAnnotationAdjustment(lowestAllowedPosition);
1747 // Now add in the bottom border/padding.
1748 setLogicalHeight(logicalHeight() + lastLineAnnotationsAdjustment + afterEdge);
1750 if (!firstLineBox() && hasLineIfEmpty())
1751 setLogicalHeight(logicalHeight() + lineHeight(true, isHorizontalWritingMode() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes));
1753 // See if we have any lines that spill out of our block. If we do, then we will possibly need to
1755 if (hasTextOverflow)
1756 checkLinesForTextOverflow();
1759 void RenderBlockFlow::checkFloatsInCleanLine(RootInlineBox* line, Vector<FloatWithRect>& floats, size_t& floatIndex, bool& encounteredNewFloat, bool& dirtiedByFloat)
1761 Vector<RenderBox*>* cleanLineFloats = line->floatsPtr();
1762 if (!cleanLineFloats)
1765 Vector<RenderBox*>::iterator end = cleanLineFloats->end();
1766 for (Vector<RenderBox*>::iterator it = cleanLineFloats->begin(); it != end; ++it) {
1767 RenderBox* floatingBox = *it;
1768 floatingBox->layoutIfNeeded();
1769 LayoutSize newSize(floatingBox->width() + floatingBox->marginWidth(), floatingBox->height() + floatingBox->marginHeight());
1770 if (floats[floatIndex].object != floatingBox) {
1771 encounteredNewFloat = true;
1775 if (floats[floatIndex].rect.size() != newSize) {
1776 LayoutUnit floatTop = isHorizontalWritingMode() ? floats[floatIndex].rect.y() : floats[floatIndex].rect.x();
1777 LayoutUnit floatHeight = isHorizontalWritingMode() ? max(floats[floatIndex].rect.height(), newSize.height())
1778 : max(floats[floatIndex].rect.width(), newSize.width());
1779 floatHeight = min(floatHeight, LayoutUnit::max() - floatTop);
1781 markLinesDirtyInBlockRange(line->lineBottomWithLeading(), floatTop + floatHeight, line);
1782 floats[floatIndex].rect.setSize(newSize);
1783 dirtiedByFloat = true;
1789 RootInlineBox* RenderBlockFlow::determineStartPosition(LineLayoutState& layoutState, InlineBidiResolver& resolver)
1791 RootInlineBox* curr = 0;
1792 RootInlineBox* last = 0;
1794 // FIXME: This entire float-checking block needs to be broken into a new function.
1795 bool dirtiedByFloat = false;
1796 if (!layoutState.isFullLayout()) {
1797 // Paginate all of the clean lines.
1798 bool paginated = view()->layoutState() && view()->layoutState()->isPaginated();
1799 LayoutUnit paginationDelta = 0;
1800 size_t floatIndex = 0;
1801 for (curr = firstRootBox(); curr && !curr->isDirty(); curr = curr->nextRootBox()) {
1803 paginationDelta -= curr->paginationStrut();
1804 adjustLinePositionForPagination(curr, paginationDelta, layoutState.flowThread());
1805 if (paginationDelta) {
1806 if (containsFloats() || !layoutState.floats().isEmpty()) {
1807 // FIXME: Do better eventually. For now if we ever shift because of pagination and floats are present just go to a full layout.
1808 layoutState.markForFullLayout();
1812 layoutState.updateRepaintRangeFromBox(curr, paginationDelta);
1813 curr->adjustBlockDirectionPosition(paginationDelta.toFloat());
1817 // If a new float has been inserted before this line or before its last known float, just do a full layout.
1818 bool encounteredNewFloat = false;
1819 checkFloatsInCleanLine(curr, layoutState.floats(), floatIndex, encounteredNewFloat, dirtiedByFloat);
1820 if (encounteredNewFloat)
1821 layoutState.markForFullLayout();
1823 if (dirtiedByFloat || layoutState.isFullLayout())
1826 // Check if a new float has been inserted after the last known float.
1827 if (!curr && floatIndex < layoutState.floats().size())
1828 layoutState.markForFullLayout();
1831 if (layoutState.isFullLayout()) {
1832 // If we encountered a new float and have inline children, mark ourself to force us to repaint.
1833 if (layoutState.hasInlineChild() && !selfNeedsLayout()) {
1834 setNeedsLayout(MarkOnlyThis);
1835 if (RuntimeEnabledFeatures::repaintAfterLayoutEnabled())
1836 setShouldDoFullRepaintAfterLayout(true);
1839 // FIXME: This should just call deleteLineBoxTree, but that causes
1840 // crashes for fast/repaint tests.
1841 curr = firstRootBox();
1843 // Note: This uses nextRootBox() insted of nextLineBox() like deleteLineBoxTree does.
1844 RootInlineBox* next = curr->nextRootBox();
1848 ASSERT(!firstLineBox() && !lastLineBox());
1851 // We have a dirty line.
1852 if (RootInlineBox* prevRootBox = curr->prevRootBox()) {
1853 // We have a previous line.
1854 if (!dirtiedByFloat && (!prevRootBox->endsWithBreak() || !prevRootBox->lineBreakObj() || (prevRootBox->lineBreakObj()->isText() && prevRootBox->lineBreakPos() >= toRenderText(prevRootBox->lineBreakObj())->textLength())))
1855 // The previous line didn't break cleanly or broke at a newline
1856 // that has been deleted, so treat it as dirty too.
1860 // No dirty lines were found.
1861 // If the last line didn't break cleanly, treat it as dirty.
1862 if (lastRootBox() && !lastRootBox()->endsWithBreak())
1863 curr = lastRootBox();
1866 // If we have no dirty lines, then last is just the last root box.
1867 last = curr ? curr->prevRootBox() : lastRootBox();
1870 unsigned numCleanFloats = 0;
1871 if (!layoutState.floats().isEmpty()) {
1872 LayoutUnit savedLogicalHeight = logicalHeight();
1873 // Restore floats from clean lines.
1874 RootInlineBox* line = firstRootBox();
1875 while (line != curr) {
1876 if (Vector<RenderBox*>* cleanLineFloats = line->floatsPtr()) {
1877 Vector<RenderBox*>::iterator end = cleanLineFloats->end();
1878 for (Vector<RenderBox*>::iterator f = cleanLineFloats->begin(); f != end; ++f) {
1879 FloatingObject* floatingObject = insertFloatingObject(*f);
1880 ASSERT(!floatingObject->originatingLine());
1881 floatingObject->setOriginatingLine(line);
1882 setLogicalHeight(logicalTopForChild(*f) - marginBeforeForChild(*f));
1883 positionNewFloats();
1884 ASSERT(layoutState.floats()[numCleanFloats].object == *f);
1888 line = line->nextRootBox();
1890 setLogicalHeight(savedLogicalHeight);
1892 layoutState.setFloatIndex(numCleanFloats);
1894 layoutState.lineInfo().setFirstLine(!last);
1895 layoutState.lineInfo().setPreviousLineBrokeCleanly(!last || last->endsWithBreak());
1898 setLogicalHeight(last->lineBottomWithLeading());
1899 InlineIterator iter = InlineIterator(this, last->lineBreakObj(), last->lineBreakPos());
1900 resolver.setPosition(iter, numberOfIsolateAncestors(iter));
1901 resolver.setStatus(last->lineBreakBidiStatus());
1903 TextDirection direction = style()->direction();
1904 if (style()->unicodeBidi() == Plaintext)
1905 direction = determinePlaintextDirectionality(this);
1906 resolver.setStatus(BidiStatus(direction, isOverride(style()->unicodeBidi())));
1907 InlineIterator iter = InlineIterator(this, bidiFirstSkippingEmptyInlines(this, &resolver), 0);
1908 resolver.setPosition(iter, numberOfIsolateAncestors(iter));
1913 void RenderBlockFlow::determineEndPosition(LineLayoutState& layoutState, RootInlineBox* startLine, InlineIterator& cleanLineStart, BidiStatus& cleanLineBidiStatus)
1915 ASSERT(!layoutState.endLine());
1916 size_t floatIndex = layoutState.floatIndex();
1917 RootInlineBox* last = 0;
1918 for (RootInlineBox* curr = startLine->nextRootBox(); curr; curr = curr->nextRootBox()) {
1919 if (!curr->isDirty()) {
1920 bool encounteredNewFloat = false;
1921 bool dirtiedByFloat = false;
1922 checkFloatsInCleanLine(curr, layoutState.floats(), floatIndex, encounteredNewFloat, dirtiedByFloat);
1923 if (encounteredNewFloat)
1926 if (curr->isDirty())
1935 // At this point, |last| is the first line in a run of clean lines that ends with the last line
1938 RootInlineBox* prev = last->prevRootBox();
1939 cleanLineStart = InlineIterator(this, prev->lineBreakObj(), prev->lineBreakPos());
1940 cleanLineBidiStatus = prev->lineBreakBidiStatus();
1941 layoutState.setEndLineLogicalTop(prev->lineBottomWithLeading());
1943 for (RootInlineBox* line = last; line; line = line->nextRootBox())
1944 line->extractLine(); // Disconnect all line boxes from their render objects while preserving
1945 // their connections to one another.
1947 layoutState.setEndLine(last);
1950 bool RenderBlockFlow::checkPaginationAndFloatsAtEndLine(LineLayoutState& layoutState)
1952 LayoutUnit lineDelta = logicalHeight() - layoutState.endLineLogicalTop();
1954 bool paginated = view()->layoutState() && view()->layoutState()->isPaginated();
1955 if (paginated && layoutState.flowThread()) {
1956 // Check all lines from here to the end, and see if the hypothetical new position for the lines will result
1957 // in a different available line width.
1958 for (RootInlineBox* lineBox = layoutState.endLine(); lineBox; lineBox = lineBox->nextRootBox()) {
1960 // This isn't the real move we're going to do, so don't update the line box's pagination
1962 LayoutUnit oldPaginationStrut = lineBox->paginationStrut();
1963 lineDelta -= oldPaginationStrut;
1964 adjustLinePositionForPagination(lineBox, lineDelta, layoutState.flowThread());
1965 lineBox->setPaginationStrut(oldPaginationStrut);
1970 if (!lineDelta || !m_floatingObjects)
1973 // See if any floats end in the range along which we want to shift the lines vertically.
1974 LayoutUnit logicalTop = min(logicalHeight(), layoutState.endLineLogicalTop());
1976 RootInlineBox* lastLine = layoutState.endLine();
1977 while (RootInlineBox* nextLine = lastLine->nextRootBox())
1978 lastLine = nextLine;
1980 LayoutUnit logicalBottom = lastLine->lineBottomWithLeading() + absoluteValue(lineDelta);
1982 const FloatingObjectSet& floatingObjectSet = m_floatingObjects->set();
1983 FloatingObjectSetIterator end = floatingObjectSet.end();
1984 for (FloatingObjectSetIterator it = floatingObjectSet.begin(); it != end; ++it) {
1985 FloatingObject* floatingObject = *it;
1986 if (logicalBottomForFloat(floatingObject) >= logicalTop && logicalBottomForFloat(floatingObject) < logicalBottom)
1993 bool RenderBlockFlow::matchedEndLine(LineLayoutState& layoutState, const InlineBidiResolver& resolver, const InlineIterator& endLineStart, const BidiStatus& endLineStatus)
1995 if (resolver.position() == endLineStart) {
1996 if (resolver.status() != endLineStatus)
1998 return checkPaginationAndFloatsAtEndLine(layoutState);
2001 // The first clean line doesn't match, but we can check a handful of following lines to try
2002 // to match back up.
2003 static int numLines = 8; // The # of lines we're willing to match against.
2004 RootInlineBox* originalEndLine = layoutState.endLine();
2005 RootInlineBox* line = originalEndLine;
2006 for (int i = 0; i < numLines && line; i++, line = line->nextRootBox()) {
2007 if (line->lineBreakObj() == resolver.position().object() && line->lineBreakPos() == resolver.position().offset()) {
2009 if (line->lineBreakBidiStatus() != resolver.status())
2010 return false; // ...but the bidi state doesn't match.
2012 bool matched = false;
2013 RootInlineBox* result = line->nextRootBox();
2014 layoutState.setEndLine(result);
2016 layoutState.setEndLineLogicalTop(line->lineBottomWithLeading());
2017 matched = checkPaginationAndFloatsAtEndLine(layoutState);
2020 // Now delete the lines that we failed to sync.
2021 deleteLineRange(layoutState, originalEndLine, result);
2029 bool RenderBlockFlow::generatesLineBoxesForInlineChild(RenderObject* inlineObj)
2032 ASSERT(inlineObj->parent() == this);
2034 InlineIterator it(this, inlineObj, 0);
2035 // FIXME: We should pass correct value for WhitespacePosition.
2036 while (!it.atEnd() && !requiresLineBox(it))
2043 void RenderBlockFlow::addOverflowFromInlineChildren()
2045 LayoutUnit endPadding = hasOverflowClip() ? paddingEnd() : LayoutUnit();
2046 // FIXME: Need to find another way to do this, since scrollbars could show when we don't want them to.
2047 if (hasOverflowClip() && !endPadding && node() && node()->isRootEditableElement() && style()->isLeftToRightDirection())
2049 for (RootInlineBox* curr = firstRootBox(); curr; curr = curr->nextRootBox()) {
2050 addLayoutOverflow(curr->paddedLayoutOverflowRect(endPadding));
2051 LayoutRect visualOverflow = curr->visualOverflowRect(curr->lineTop(), curr->lineBottom());
2052 addContentsVisualOverflow(visualOverflow);
2056 void RenderBlockFlow::deleteEllipsisLineBoxes()
2058 ETextAlign textAlign = style()->textAlign();
2059 bool ltr = style()->isLeftToRightDirection();
2060 bool firstLine = true;
2061 for (RootInlineBox* curr = firstRootBox(); curr; curr = curr->nextRootBox()) {
2062 if (curr->hasEllipsisBox()) {
2063 curr->clearTruncation();
2065 // Shift the line back where it belongs if we cannot accomodate an ellipsis.
2066 float logicalLeft = pixelSnappedLogicalLeftOffsetForLine(curr->lineTop(), firstLine).toFloat();
2067 float availableLogicalWidth = logicalRightOffsetForLine(curr->lineTop(), false) - logicalLeft;
2068 float totalLogicalWidth = curr->logicalWidth();
2069 updateLogicalWidthForAlignment(textAlign, curr, 0, logicalLeft, totalLogicalWidth, availableLogicalWidth, 0);
2072 curr->adjustLogicalPosition((logicalLeft - curr->logicalLeft()), 0);
2074 curr->adjustLogicalPosition(-(curr->logicalLeft() - logicalLeft), 0);
2080 void RenderBlockFlow::checkLinesForTextOverflow()
2082 // Determine the width of the ellipsis using the current font.
2083 // FIXME: CSS3 says this is configurable, also need to use 0x002E (FULL STOP) if horizontal ellipsis is "not renderable"
2084 const Font& font = style()->font();
2085 DEFINE_STATIC_LOCAL(AtomicString, ellipsisStr, (&horizontalEllipsis, 1));
2086 const Font& firstLineFont = firstLineStyle()->font();
2087 // FIXME: We should probably not hard-code the direction here. https://crbug.com/333004
2088 TextDirection ellipsisDirection = LTR;
2089 int firstLineEllipsisWidth = firstLineFont.width(constructTextRun(this, firstLineFont, &horizontalEllipsis, 1, firstLineStyle(), ellipsisDirection));
2090 int ellipsisWidth = (font == firstLineFont) ? firstLineEllipsisWidth : font.width(constructTextRun(this, font, &horizontalEllipsis, 1, style(), ellipsisDirection));
2092 // For LTR text truncation, we want to get the right edge of our padding box, and then we want to see
2093 // if the right edge of a line box exceeds that. For RTL, we use the left edge of the padding box and
2094 // check the left edge of the line box to see if it is less
2095 // Include the scrollbar for overflow blocks, which means we want to use "contentWidth()"
2096 bool ltr = style()->isLeftToRightDirection();
2097 ETextAlign textAlign = style()->textAlign();
2098 bool firstLine = true;
2099 for (RootInlineBox* curr = firstRootBox(); curr; curr = curr->nextRootBox()) {
2100 // FIXME: Use pixelSnappedLogicalRightOffsetForLine instead of snapping it ourselves once the column workaround in said method has been fixed.
2101 // https://bugs.webkit.org/show_bug.cgi?id=105461
2102 float currLogicalLeft = curr->logicalLeft();
2103 int blockRightEdge = snapSizeToPixel(logicalRightOffsetForLine(curr->lineTop(), firstLine), currLogicalLeft);
2104 int blockLeftEdge = pixelSnappedLogicalLeftOffsetForLine(curr->lineTop(), firstLine);
2105 int lineBoxEdge = ltr ? snapSizeToPixel(currLogicalLeft + curr->logicalWidth(), currLogicalLeft) : snapSizeToPixel(currLogicalLeft, 0);
2106 if ((ltr && lineBoxEdge > blockRightEdge) || (!ltr && lineBoxEdge < blockLeftEdge)) {
2107 // This line spills out of our box in the appropriate direction. Now we need to see if the line
2108 // can be truncated. In order for truncation to be possible, the line must have sufficient space to
2109 // accommodate our truncation string, and no replaced elements (images, tables) can overlap the ellipsis
2112 LayoutUnit width = firstLine ? firstLineEllipsisWidth : ellipsisWidth;
2113 LayoutUnit blockEdge = ltr ? blockRightEdge : blockLeftEdge;
2114 if (curr->lineCanAccommodateEllipsis(ltr, blockEdge, lineBoxEdge, width)) {
2115 float totalLogicalWidth = curr->placeEllipsis(ellipsisStr, ltr, blockLeftEdge, blockRightEdge, width.toFloat());
2117 float logicalLeft = 0; // We are only intersted in the delta from the base position.
2118 float snappedLogicalLeft = pixelSnappedLogicalLeftOffsetForLine(curr->lineTop(), firstLine).toFloat();
2119 float availableLogicalWidth = pixelSnappedLogicalRightOffsetForLine(curr->lineTop(), firstLine) - snappedLogicalLeft;
2120 updateLogicalWidthForAlignment(textAlign, curr, 0, logicalLeft, totalLogicalWidth, availableLogicalWidth, 0);
2122 curr->adjustLogicalPosition(logicalLeft, 0);
2124 curr->adjustLogicalPosition(logicalLeft - (availableLogicalWidth - totalLogicalWidth), 0);
2131 bool RenderBlockFlow::positionNewFloatOnLine(FloatingObject* newFloat, FloatingObject* lastFloatFromPreviousLine, LineInfo& lineInfo, LineWidth& width)
2133 if (!positionNewFloats())
2136 width.shrinkAvailableWidthForNewFloatIfNeeded(newFloat);
2138 // We only connect floats to lines for pagination purposes if the floats occur at the start of
2139 // the line and the previous line had a hard break (so this line is either the first in the block
2140 // or follows a <br>).
2141 if (!newFloat->paginationStrut() || !lineInfo.previousLineBrokeCleanly() || !lineInfo.isEmpty())
2144 const FloatingObjectSet& floatingObjectSet = m_floatingObjects->set();
2145 ASSERT(floatingObjectSet.last() == newFloat);
2147 LayoutUnit floatLogicalTop = logicalTopForFloat(newFloat);
2148 int paginationStrut = newFloat->paginationStrut();
2150 if (floatLogicalTop - paginationStrut != logicalHeight() + lineInfo.floatPaginationStrut())
2153 FloatingObjectSetIterator it = floatingObjectSet.end();
2154 --it; // Last float is newFloat, skip that one.
2155 FloatingObjectSetIterator begin = floatingObjectSet.begin();
2156 while (it != begin) {
2158 FloatingObject* floatingObject = *it;
2159 if (floatingObject == lastFloatFromPreviousLine)
2161 if (logicalTopForFloat(floatingObject) == logicalHeight() + lineInfo.floatPaginationStrut()) {
2162 floatingObject->setPaginationStrut(paginationStrut + floatingObject->paginationStrut());
2163 RenderBox* floatBox = floatingObject->renderer();
2164 setLogicalTopForChild(floatBox, logicalTopForChild(floatBox) + marginBeforeForChild(floatBox) + paginationStrut);
2165 if (floatBox->isRenderBlock())
2166 floatBox->forceChildLayout();
2168 floatBox->layoutIfNeeded();
2169 // Save the old logical top before calling removePlacedObject which will set
2170 // isPlaced to false. Otherwise it will trigger an assert in logicalTopForFloat.
2171 LayoutUnit oldLogicalTop = logicalTopForFloat(floatingObject);
2172 m_floatingObjects->removePlacedObject(floatingObject);
2173 setLogicalTopForFloat(floatingObject, oldLogicalTop + paginationStrut);
2174 m_floatingObjects->addPlacedObject(floatingObject);
2178 // Just update the line info's pagination strut without altering our logical height yet. If the line ends up containing
2179 // no content, then we don't want to improperly grow the height of the block.
2180 lineInfo.setFloatPaginationStrut(lineInfo.floatPaginationStrut() + paginationStrut);
2184 LayoutUnit RenderBlockFlow::startAlignedOffsetForLine(LayoutUnit position, bool firstLine)
2186 ETextAlign textAlign = style()->textAlign();
2188 if (textAlign == TASTART) // FIXME: Handle TAEND here
2189 return startOffsetForLine(position, firstLine);
2191 // updateLogicalWidthForAlignment() handles the direction of the block so no need to consider it here
2192 float totalLogicalWidth = 0;
2193 float logicalLeft = logicalLeftOffsetForLine(logicalHeight(), false).toFloat();
2194 float availableLogicalWidth = logicalRightOffsetForLine(logicalHeight(), false) - logicalLeft;
2195 updateLogicalWidthForAlignment(textAlign, 0, 0, logicalLeft, totalLogicalWidth, availableLogicalWidth, 0);
2197 if (!style()->isLeftToRightDirection())
2198 return logicalWidth() - logicalLeft;