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.
5 * Copyright (C) 2013 ChangSeok Oh <shivamidow@gmail.com>
6 * Copyright (C) 2013 Adobe Systems Inc. All right reserved.
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
18 * You should have received a copy of the GNU Library General Public License
19 * along with this library; see the file COPYING.LIB. If not, write to
20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
27 #include "AXObjectCache.h"
28 #include "BidiResolver.h"
29 #include "BreakingContextInlineHeaders.h"
30 #include "FloatingObjects.h"
31 #include "InlineElementBox.h"
32 #include "InlineIterator.h"
33 #include "InlineTextBox.h"
34 #include "LineLayoutState.h"
36 #include "RenderBlockFlow.h"
37 #include "RenderFlowThread.h"
38 #include "RenderLineBreak.h"
39 #include "RenderRegion.h"
40 #include "RenderView.h"
42 #include "SimpleLineLayoutFunctions.h"
43 #include "TrailingFloatsRootInlineBox.h"
44 #include "VerticalPositionCache.h"
45 #include <wtf/RefCountedLeakCounter.h>
46 #include <wtf/StdLibExtras.h>
49 #include "SVGRootInlineBox.h"
54 static void determineDirectionality(TextDirection& dir, InlineIterator iter)
56 while (!iter.atEnd()) {
57 if (iter.atParagraphSeparator())
59 if (UChar current = iter.current()) {
60 UCharDirection charDirection = u_charDirection(current);
61 if (charDirection == U_LEFT_TO_RIGHT) {
65 if (charDirection == U_RIGHT_TO_LEFT || charDirection == U_RIGHT_TO_LEFT_ARABIC) {
74 inline BidiRun* createRun(int start, int end, RenderObject* obj, InlineBidiResolver& resolver)
77 return new BidiRun(start, end, *obj, resolver.context(), resolver.dir());
80 void RenderBlockFlow::appendRunsForObject(BidiRunList<BidiRun>& runs, int start, int end, RenderObject* obj, InlineBidiResolver& resolver)
82 if (start > end || shouldSkipCreatingRunsForObject(obj))
85 LineMidpointState& lineMidpointState = resolver.midpointState();
86 bool haveNextMidpoint = (lineMidpointState.currentMidpoint() < lineMidpointState.numMidpoints());
87 InlineIterator nextMidpoint;
89 nextMidpoint = lineMidpointState.midpoints()[lineMidpointState.currentMidpoint()];
90 if (lineMidpointState.betweenMidpoints()) {
91 if (!(haveNextMidpoint && nextMidpoint.renderer() == obj))
93 // This is a new start point. Stop ignoring objects and
95 lineMidpointState.setBetweenMidpoints(false);
96 start = nextMidpoint.offset();
97 lineMidpointState.incrementCurrentMidpoint();
99 return appendRunsForObject(runs, start, end, obj, resolver);
101 if (!haveNextMidpoint || (obj != nextMidpoint.renderer())) {
102 runs.addRun(createRun(start, end, obj, resolver));
106 // An end midpoint has been encountered within our object. We
107 // need to go ahead and append a run with our endpoint.
108 if (static_cast<int>(nextMidpoint.offset() + 1) <= end) {
109 lineMidpointState.setBetweenMidpoints(true);
110 lineMidpointState.incrementCurrentMidpoint();
111 if (nextMidpoint.offset() != UINT_MAX) { // UINT_MAX means stop at the object and don't include any of it.
112 if (static_cast<int>(nextMidpoint.offset() + 1) > start)
113 runs.addRun(createRun(start, nextMidpoint.offset() + 1, obj, resolver));
114 return appendRunsForObject(runs, nextMidpoint.offset() + 1, end, obj, resolver);
117 runs.addRun(createRun(start, end, obj, resolver));
121 std::unique_ptr<RootInlineBox> RenderBlockFlow::createRootInlineBox()
123 return std::make_unique<RootInlineBox>(*this);
126 RootInlineBox* RenderBlockFlow::createAndAppendRootInlineBox()
128 auto newRootBox = createRootInlineBox();
129 RootInlineBox* rootBox = newRootBox.get();
130 m_lineBoxes.appendLineBox(std::move(newRootBox));
132 if (UNLIKELY(AXObjectCache::accessibilityEnabled()) && firstRootBox() == rootBox) {
133 if (AXObjectCache* cache = document().existingAXObjectCache())
134 cache->recomputeIsIgnored(this);
140 static inline InlineBox* createInlineBoxForRenderer(RenderObject* obj, bool isRootLineBox, bool isOnlyRun = false)
143 return toRenderBlockFlow(obj)->createAndAppendRootInlineBox();
146 return toRenderText(obj)->createInlineTextBox();
149 // FIXME: This is terrible. This branch returns an *owned* pointer!
150 return toRenderBox(obj)->createInlineBox().release();
153 if (obj->isLineBreak()) {
154 // FIXME: This is terrible. This branch returns an *owned* pointer!
155 auto inlineBox = toRenderLineBreak(obj)->createInlineBox().release();
156 // We only treat a box as text for a <br> if we are on a line by ourself or in strict mode
157 // (Note the use of strict mode. In "almost strict" mode, we don't treat the box for <br> as text.)
158 inlineBox->setBehavesLikeText(isOnlyRun || obj->document().inNoQuirksMode() || obj->isLineBreakOpportunity());
162 return toRenderInline(obj)->createAndAppendInlineFlowBox();
165 static inline void dirtyLineBoxesForRenderer(RenderObject& renderer, bool fullLayout)
167 if (renderer.isText()) {
168 RenderText& renderText = toRenderText(renderer);
169 updateCounterIfNeeded(renderText);
170 renderText.dirtyLineBoxes(fullLayout);
171 } else if (renderer.isLineBreak())
172 toRenderLineBreak(renderer).dirtyLineBoxes(fullLayout);
174 toRenderInline(renderer).dirtyLineBoxes(fullLayout);
177 static bool parentIsConstructedOrHaveNext(InlineFlowBox* parentBox)
180 if (parentBox->isConstructed() || parentBox->nextOnLine())
182 parentBox = parentBox->parent();
187 InlineFlowBox* RenderBlockFlow::createLineBoxes(RenderObject* obj, const LineInfo& lineInfo, InlineBox* childBox, bool startNewSegment)
189 // See if we have an unconstructed line box for this object that is also
190 // the last item on the line.
191 unsigned lineDepth = 1;
192 InlineFlowBox* parentBox = 0;
193 InlineFlowBox* result = 0;
194 bool hasDefaultLineBoxContain = style().lineBoxContain() == RenderStyle::initialLineBoxContain();
196 ASSERT_WITH_SECURITY_IMPLICATION(obj->isRenderInline() || obj == this);
198 RenderInline* inlineFlow = (obj != this) ? toRenderInline(obj) : 0;
200 // Get the last box we made for this render object.
201 parentBox = inlineFlow ? inlineFlow->lastLineBox() : toRenderBlockFlow(obj)->lastRootBox();
203 // If this box or its ancestor is constructed then it is from a previous line, and we need
204 // to make a new box for our line. If this box or its ancestor is unconstructed but it has
205 // something following it on the line, then we know we have to make a new box
206 // as well. In this situation our inline has actually been split in two on
207 // the same line (this can happen with very fancy language mixtures).
208 bool constructedNewBox = false;
209 bool allowedToConstructNewBox = !hasDefaultLineBoxContain || !inlineFlow || inlineFlow->alwaysCreateLineBoxes();
210 bool mustCreateBoxesToRoot = startNewSegment && !(parentBox && parentBox->isRootInlineBox());
211 bool canUseExistingParentBox = parentBox && !parentIsConstructedOrHaveNext(parentBox) && !mustCreateBoxesToRoot;
212 if (allowedToConstructNewBox && !canUseExistingParentBox) {
213 // We need to make a new box for this render object. Once
214 // made, we need to place it at the end of the current line.
215 InlineBox* newBox = createInlineBoxForRenderer(obj, obj == this);
216 ASSERT_WITH_SECURITY_IMPLICATION(newBox->isInlineFlowBox());
217 parentBox = toInlineFlowBox(newBox);
218 parentBox->setIsFirstLine(lineInfo.isFirstLine());
219 parentBox->setIsHorizontal(isHorizontalWritingMode());
220 if (!hasDefaultLineBoxContain)
221 parentBox->clearDescendantsHaveSameLineHeightAndBaseline();
222 constructedNewBox = true;
225 if (constructedNewBox || canUseExistingParentBox) {
229 // If we have hit the block itself, then |box| represents the root
230 // inline box for the line, and it doesn't have to be appended to any parent
233 parentBox->addToLine(childBox);
235 if (!constructedNewBox || obj == this)
238 childBox = parentBox;
241 // If we've exceeded our line depth, then jump straight to the root and skip all the remaining
242 // intermediate inline flows.
243 obj = (++lineDepth >= cMaxLineDepth) ? this : obj->parent();
250 template <typename CharacterType>
251 static inline bool endsWithASCIISpaces(const CharacterType* characters, unsigned pos, unsigned end)
253 while (isASCIISpace(characters[pos])) {
261 static bool reachedEndOfTextRenderer(const BidiRunList<BidiRun>& bidiRuns)
263 BidiRun* run = bidiRuns.logicallyLastRun();
266 unsigned pos = run->stop();
267 const RenderObject& r = run->renderer();
270 const RenderText& renderText = toRenderText(r);
271 unsigned length = renderText.textLength();
275 if (renderText.is8Bit())
276 return endsWithASCIISpaces(renderText.characters8(), pos, length);
277 return endsWithASCIISpaces(renderText.characters16(), pos, length);
280 RootInlineBox* RenderBlockFlow::constructLine(BidiRunList<BidiRun>& bidiRuns, const LineInfo& lineInfo)
282 ASSERT(bidiRuns.firstRun());
284 bool rootHasSelectedChildren = false;
285 InlineFlowBox* parentBox = 0;
286 int runCount = bidiRuns.runCount() - lineInfo.runsFromLeadingWhitespace();
287 for (BidiRun* r = bidiRuns.firstRun(); r; r = r->next()) {
288 // Create a box for our object.
289 bool isOnlyRun = (runCount == 1);
290 if (runCount == 2 && !r->renderer().isListMarker())
291 isOnlyRun = (!style().isLeftToRightDirection() ? bidiRuns.lastRun() : bidiRuns.firstRun())->renderer().isListMarker();
293 if (lineInfo.isEmpty())
296 InlineBox* box = createInlineBoxForRenderer(&r->renderer(), false, isOnlyRun);
299 if (!rootHasSelectedChildren && box->renderer().selectionState() != RenderObject::SelectionNone)
300 rootHasSelectedChildren = true;
302 // If we have no parent box yet, or if the run is not simply a sibling,
303 // then we need to construct inline boxes as necessary to properly enclose the
304 // run's inline box. Segments can only be siblings at the root level, as
305 // they are positioned separately.
306 #if ENABLE(CSS_SHAPES)
307 bool runStartsSegment = r->m_startsSegment;
309 bool runStartsSegment = false;
311 if (!parentBox || &parentBox->renderer() != r->renderer().parent() || runStartsSegment)
312 // Create new inline boxes all the way back to the appropriate insertion point.
313 parentBox = createLineBoxes(r->renderer().parent(), lineInfo, box, runStartsSegment);
315 // Append the inline box to this line.
316 parentBox->addToLine(box);
319 bool visuallyOrdered = r->renderer().style().rtlOrdering() == VisualOrder;
320 box->setBidiLevel(r->level());
322 if (box->isInlineTextBox()) {
323 InlineTextBox* text = toInlineTextBox(box);
324 text->setStart(r->m_start);
325 text->setLen(r->m_stop - r->m_start);
326 text->setDirOverride(r->dirOverride(visuallyOrdered));
328 text->setHasHyphen(true);
332 // We should have a root inline box. It should be unconstructed and
333 // be the last continuation of our line list.
334 ASSERT(lastRootBox() && !lastRootBox()->isConstructed());
336 // Set the m_selectedChildren flag on the root inline box if one of the leaf inline box
337 // from the bidi runs walk above has a selection state.
338 if (rootHasSelectedChildren)
339 lastRootBox()->root().setHasSelectedChildren(true);
341 // Set bits on our inline flow boxes that indicate which sides should
342 // paint borders/margins/padding. This knowledge will ultimately be used when
343 // we determine the horizontal positions and widths of all the inline boxes on
345 bool isLogicallyLastRunWrapped = bidiRuns.logicallyLastRun()->renderer().isText() ? !reachedEndOfTextRenderer(bidiRuns) : true;
346 lastRootBox()->determineSpacingForFlowBoxes(lineInfo.isLastLine(), isLogicallyLastRunWrapped, &bidiRuns.logicallyLastRun()->renderer());
348 // Now mark the line boxes as being constructed.
349 lastRootBox()->setConstructed();
351 // Return the last line.
352 return lastRootBox();
355 ETextAlign RenderBlockFlow::textAlignmentForLine(bool endsWithSoftBreak) const
357 ETextAlign alignment = style().textAlign();
358 if (endsWithSoftBreak)
361 #if !ENABLE(CSS3_TEXT)
362 return (alignment == JUSTIFY) ? TASTART : alignment;
364 if (alignment != JUSTIFY)
367 TextAlignLast alignmentLast = style().textAlignLast();
368 switch (alignmentLast) {
369 case TextAlignLastStart:
371 case TextAlignLastEnd:
373 case TextAlignLastLeft:
375 case TextAlignLastRight:
377 case TextAlignLastCenter:
379 case TextAlignLastJustify:
381 case TextAlignLastAuto:
382 if (style().textJustify() == TextJustifyDistribute)
390 static void updateLogicalWidthForLeftAlignedBlock(bool isLeftToRightDirection, BidiRun* trailingSpaceRun, float& logicalLeft, float& totalLogicalWidth, float availableLogicalWidth)
392 // The direction of the block should determine what happens with wide lines.
393 // In particular with RTL blocks, wide lines should still spill out to the left.
394 if (isLeftToRightDirection) {
395 if (totalLogicalWidth > availableLogicalWidth && trailingSpaceRun)
396 trailingSpaceRun->box()->setLogicalWidth(std::max<float>(0, trailingSpaceRun->box()->logicalWidth() - totalLogicalWidth + availableLogicalWidth));
400 if (trailingSpaceRun)
401 trailingSpaceRun->box()->setLogicalWidth(0);
402 else if (totalLogicalWidth > availableLogicalWidth)
403 logicalLeft -= (totalLogicalWidth - availableLogicalWidth);
406 static void updateLogicalWidthForRightAlignedBlock(bool isLeftToRightDirection, BidiRun* trailingSpaceRun, float& logicalLeft, float& totalLogicalWidth, float availableLogicalWidth)
408 // Wide lines spill out of the block based off direction.
409 // So even if text-align is right, if direction is LTR, wide lines should overflow out of the right
410 // side of the block.
411 if (isLeftToRightDirection) {
412 if (trailingSpaceRun) {
413 totalLogicalWidth -= trailingSpaceRun->box()->logicalWidth();
414 trailingSpaceRun->box()->setLogicalWidth(0);
416 if (totalLogicalWidth < availableLogicalWidth)
417 logicalLeft += availableLogicalWidth - totalLogicalWidth;
421 if (totalLogicalWidth > availableLogicalWidth && trailingSpaceRun) {
422 trailingSpaceRun->box()->setLogicalWidth(std::max<float>(0, trailingSpaceRun->box()->logicalWidth() - totalLogicalWidth + availableLogicalWidth));
423 totalLogicalWidth -= trailingSpaceRun->box()->logicalWidth();
425 logicalLeft += availableLogicalWidth - totalLogicalWidth;
428 static void updateLogicalWidthForCenterAlignedBlock(bool isLeftToRightDirection, BidiRun* trailingSpaceRun, float& logicalLeft, float& totalLogicalWidth, float availableLogicalWidth)
430 float trailingSpaceWidth = 0;
431 if (trailingSpaceRun) {
432 totalLogicalWidth -= trailingSpaceRun->box()->logicalWidth();
433 trailingSpaceWidth = std::min(trailingSpaceRun->box()->logicalWidth(), (availableLogicalWidth - totalLogicalWidth + 1) / 2);
434 trailingSpaceRun->box()->setLogicalWidth(std::max<float>(0, trailingSpaceWidth));
436 if (isLeftToRightDirection)
437 logicalLeft += std::max<float>((availableLogicalWidth - totalLogicalWidth) / 2, 0);
439 logicalLeft += totalLogicalWidth > availableLogicalWidth ? (availableLogicalWidth - totalLogicalWidth) : (availableLogicalWidth - totalLogicalWidth) / 2 - trailingSpaceWidth;
442 void RenderBlockFlow::setMarginsForRubyRun(BidiRun* run, RenderRubyRun& renderer, RenderObject* previousObject, const LineInfo& lineInfo)
446 RenderObject* nextObject = 0;
447 for (BidiRun* runWithNextObject = run->next(); runWithNextObject; runWithNextObject = runWithNextObject->next()) {
448 if (!runWithNextObject->renderer().isOutOfFlowPositioned() && !runWithNextObject->box()->isLineBreak()) {
449 nextObject = &runWithNextObject->renderer();
453 renderer.getOverhang(lineInfo.isFirstLine(), renderer.style().isLeftToRightDirection() ? previousObject : nextObject, renderer.style().isLeftToRightDirection() ? nextObject : previousObject, startOverhang, endOverhang);
454 setMarginStartForChild(renderer, -startOverhang);
455 setMarginEndForChild(renderer, -endOverhang);
458 static inline void setLogicalWidthForTextRun(RootInlineBox* lineBox, BidiRun* run, RenderText* renderer, float xPos, const LineInfo& lineInfo,
459 GlyphOverflowAndFallbackFontsMap& textBoxDataMap, VerticalPositionCache& verticalPositionCache, WordMeasurements& wordMeasurements)
461 HashSet<const SimpleFontData*> fallbackFonts;
462 GlyphOverflow glyphOverflow;
464 const Font& font = lineStyle(*renderer->parent(), lineInfo).font();
465 // Always compute glyph overflow if the block's line-box-contain value is "glyphs".
466 if (lineBox->fitsToGlyphs()) {
467 // If we don't stick out of the root line's font box, then don't bother computing our glyph overflow. This optimization
468 // will keep us from computing glyph bounds in nearly all cases.
469 bool includeRootLine = lineBox->includesRootLineBoxFontOrLeading();
470 int baselineShift = lineBox->verticalPositionForBox(run->box(), verticalPositionCache);
471 int rootDescent = includeRootLine ? font.fontMetrics().descent() : 0;
472 int rootAscent = includeRootLine ? font.fontMetrics().ascent() : 0;
473 int boxAscent = font.fontMetrics().ascent() - baselineShift;
474 int boxDescent = font.fontMetrics().descent() + baselineShift;
475 if (boxAscent > rootDescent || boxDescent > rootAscent)
476 glyphOverflow.computeBounds = true;
479 LayoutUnit hyphenWidth = 0;
480 if (toInlineTextBox(run->box())->hasHyphen())
481 hyphenWidth = measureHyphenWidth(renderer, font, &fallbackFonts);
483 float measuredWidth = 0;
485 bool kerningIsEnabled = font.typesettingFeatures() & Kerning;
486 bool canUseSimpleFontCodePath = renderer->canUseSimpleFontCodePath();
488 // Since we don't cache glyph overflows, we need to re-measure the run if
489 // the style is linebox-contain: glyph.
491 if (!lineBox->fitsToGlyphs() && canUseSimpleFontCodePath) {
492 int lastEndOffset = run->m_start;
493 for (size_t i = 0, size = wordMeasurements.size(); i < size && lastEndOffset < run->m_stop; ++i) {
494 WordMeasurement& wordMeasurement = wordMeasurements[i];
495 if (wordMeasurement.width <= 0 || wordMeasurement.startOffset == wordMeasurement.endOffset)
497 if (wordMeasurement.renderer != renderer || wordMeasurement.startOffset != lastEndOffset || wordMeasurement.endOffset > run->m_stop)
500 lastEndOffset = wordMeasurement.endOffset;
501 if (kerningIsEnabled && lastEndOffset == run->m_stop) {
502 int wordLength = lastEndOffset - wordMeasurement.startOffset;
503 GlyphOverflow overflow;
504 measuredWidth += renderer->width(wordMeasurement.startOffset, wordLength, xPos + measuredWidth, lineInfo.isFirstLine(),
505 &wordMeasurement.fallbackFonts, &overflow);
506 UChar c = renderer->characterAt(wordMeasurement.startOffset);
507 if (i > 0 && wordLength == 1 && (c == ' ' || c == '\t'))
508 measuredWidth += renderer->style().font().wordSpacing();
510 measuredWidth += wordMeasurement.width;
511 if (!wordMeasurement.fallbackFonts.isEmpty()) {
512 HashSet<const SimpleFontData*>::const_iterator end = wordMeasurement.fallbackFonts.end();
513 for (HashSet<const SimpleFontData*>::const_iterator it = wordMeasurement.fallbackFonts.begin(); it != end; ++it)
514 fallbackFonts.add(*it);
517 if (measuredWidth && lastEndOffset != run->m_stop) {
518 // If we don't have enough cached data, we'll measure the run again.
520 fallbackFonts.clear();
525 measuredWidth = renderer->width(run->m_start, run->m_stop - run->m_start, xPos, lineInfo.isFirstLine(), &fallbackFonts, &glyphOverflow);
527 run->box()->setLogicalWidth(measuredWidth + hyphenWidth);
528 if (!fallbackFonts.isEmpty()) {
529 ASSERT(run->box()->behavesLikeText());
530 GlyphOverflowAndFallbackFontsMap::iterator it = textBoxDataMap.add(toInlineTextBox(run->box()), std::make_pair(Vector<const SimpleFontData*>(), GlyphOverflow())).iterator;
531 ASSERT(it->value.first.isEmpty());
532 copyToVector(fallbackFonts, it->value.first);
533 run->box()->parent()->clearDescendantsHaveSameLineHeightAndBaseline();
535 if ((glyphOverflow.top || glyphOverflow.bottom || glyphOverflow.left || glyphOverflow.right)) {
536 ASSERT(run->box()->behavesLikeText());
537 GlyphOverflowAndFallbackFontsMap::iterator it = textBoxDataMap.add(toInlineTextBox(run->box()), std::make_pair(Vector<const SimpleFontData*>(), GlyphOverflow())).iterator;
538 it->value.second = glyphOverflow;
539 run->box()->clearKnownToHaveNoOverflow();
543 static inline void computeExpansionForJustifiedText(BidiRun* firstRun, BidiRun* trailingSpaceRun, Vector<unsigned, 16>& expansionOpportunities, unsigned expansionOpportunityCount, float& totalLogicalWidth, float availableLogicalWidth)
545 if (!expansionOpportunityCount || availableLogicalWidth <= totalLogicalWidth)
549 for (BidiRun* r = firstRun; r; r = r->next()) {
550 #if ENABLE(CSS_SHAPES)
551 // This method is called once per segment, do not move past the current segment.
552 if (r->m_startsSegment)
555 if (!r->box() || r == trailingSpaceRun)
558 if (r->renderer().isText()) {
559 unsigned opportunitiesInRun = expansionOpportunities[i++];
561 ASSERT(opportunitiesInRun <= expansionOpportunityCount);
563 // Only justify text if whitespace is collapsed.
564 if (r->renderer().style().collapseWhiteSpace()) {
565 InlineTextBox* textBox = toInlineTextBox(r->box());
566 int expansion = (availableLogicalWidth - totalLogicalWidth) * opportunitiesInRun / expansionOpportunityCount;
567 textBox->setExpansion(expansion);
568 totalLogicalWidth += expansion;
570 expansionOpportunityCount -= opportunitiesInRun;
571 if (!expansionOpportunityCount)
577 void RenderBlockFlow::updateLogicalWidthForAlignment(const ETextAlign& textAlign, BidiRun* trailingSpaceRun, float& logicalLeft, float& totalLogicalWidth, float& availableLogicalWidth, int expansionOpportunityCount)
579 // Armed with the total width of the line (without justification),
580 // we now examine our text-align property in order to determine where to position the
581 // objects horizontally. The total width of the line can be increased if we end up
586 updateLogicalWidthForLeftAlignedBlock(style().isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth);
590 updateLogicalWidthForRightAlignedBlock(style().isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth);
594 updateLogicalWidthForCenterAlignedBlock(style().isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth);
597 adjustInlineDirectionLineBounds(expansionOpportunityCount, logicalLeft, availableLogicalWidth);
598 if (expansionOpportunityCount) {
599 if (trailingSpaceRun) {
600 totalLogicalWidth -= trailingSpaceRun->box()->logicalWidth();
601 trailingSpaceRun->box()->setLogicalWidth(0);
607 if (style().isLeftToRightDirection())
608 updateLogicalWidthForLeftAlignedBlock(style().isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth);
610 updateLogicalWidthForRightAlignedBlock(style().isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth);
613 if (style().isLeftToRightDirection())
614 updateLogicalWidthForRightAlignedBlock(style().isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth);
616 updateLogicalWidthForLeftAlignedBlock(style().isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth);
621 static void updateLogicalInlinePositions(RenderBlockFlow& block, float& lineLogicalLeft, float& lineLogicalRight, float& availableLogicalWidth, bool firstLine, IndentTextOrNot shouldIndentText, LayoutUnit boxLogicalHeight)
623 LayoutUnit lineLogicalHeight = block.minLineHeightForReplacedRenderer(firstLine, boxLogicalHeight);
624 lineLogicalLeft = block.pixelSnappedLogicalLeftOffsetForLine(block.logicalHeight(), shouldIndentText == IndentText, lineLogicalHeight);
625 lineLogicalRight = block.pixelSnappedLogicalRightOffsetForLine(block.logicalHeight(), shouldIndentText == IndentText, lineLogicalHeight);
626 availableLogicalWidth = lineLogicalRight - lineLogicalLeft;
629 void RenderBlockFlow::computeInlineDirectionPositionsForLine(RootInlineBox* lineBox, const LineInfo& lineInfo, BidiRun* firstRun, BidiRun* trailingSpaceRun, bool reachedEnd, GlyphOverflowAndFallbackFontsMap& textBoxDataMap, VerticalPositionCache& verticalPositionCache, WordMeasurements& wordMeasurements)
631 ETextAlign textAlign = textAlignmentForLine(!reachedEnd && !lineBox->endsWithBreak());
633 // 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
634 // box is only affected if it is the first child of its parent element."
635 // CSS3 "text-indent", "-webkit-each-line" affects the first line of the block container as well as each line after a forced line break,
636 // but does not affect lines after a soft wrap break.
637 bool isFirstLine = lineInfo.isFirstLine() && !(isAnonymousBlock() && parent()->firstChild() != this);
638 bool isAfterHardLineBreak = lineBox->prevRootBox() && lineBox->prevRootBox()->endsWithBreak();
639 IndentTextOrNot shouldIndentText = requiresIndent(isFirstLine, isAfterHardLineBreak, style());
640 float lineLogicalLeft;
641 float lineLogicalRight;
642 float availableLogicalWidth;
643 updateLogicalInlinePositions(*this, lineLogicalLeft, lineLogicalRight, availableLogicalWidth, isFirstLine, shouldIndentText, 0);
644 bool needsWordSpacing;
645 #if ENABLE(CSS_SHAPES)
646 ShapeInsideInfo* shapeInsideInfo = layoutShapeInsideInfo();
647 if (shapeInsideInfo && shapeInsideInfo->hasSegments()) {
648 BidiRun* segmentStart = firstRun;
649 const SegmentList& segments = shapeInsideInfo->segments();
650 float logicalLeft = std::max<float>(roundToInt(segments[0].logicalLeft), lineLogicalLeft);
651 float logicalRight = std::min<float>(floorToInt(segments[0].logicalRight), lineLogicalRight);
652 float startLogicalLeft = logicalLeft;
653 float endLogicalRight = logicalLeft;
654 float minLogicalLeft = logicalLeft;
655 float maxLogicalRight = logicalLeft;
656 lineBox->beginPlacingBoxRangesInInlineDirection(logicalLeft);
657 for (size_t i = 0; i < segments.size(); i++) {
659 logicalLeft = std::max<float>(roundToInt(segments[i].logicalLeft), lineLogicalLeft);
660 logicalRight = std::min<float>(floorToInt(segments[i].logicalRight), lineLogicalRight);
662 availableLogicalWidth = logicalRight - logicalLeft;
663 BidiRun* newSegmentStart = computeInlineDirectionPositionsForSegment(lineBox, lineInfo, textAlign, logicalLeft, availableLogicalWidth, segmentStart, trailingSpaceRun, textBoxDataMap, verticalPositionCache, wordMeasurements);
664 needsWordSpacing = false;
665 endLogicalRight = lineBox->placeBoxRangeInInlineDirection(segmentStart->box(), newSegmentStart ? newSegmentStart->box() : 0, logicalLeft, minLogicalLeft, maxLogicalRight, needsWordSpacing, textBoxDataMap);
666 if (!newSegmentStart || !newSegmentStart->next())
668 ASSERT(newSegmentStart->m_startsSegment);
669 // Discard the empty segment start marker bidi runs
670 segmentStart = newSegmentStart->next();
672 lineBox->endPlacingBoxRangesInInlineDirection(startLogicalLeft, endLogicalRight, minLogicalLeft, maxLogicalRight);
677 if (firstRun && firstRun->renderer().isReplaced()) {
678 RenderBox& renderBox = toRenderBox(firstRun->renderer());
679 updateLogicalInlinePositions(*this, lineLogicalLeft, lineLogicalRight, availableLogicalWidth, isFirstLine, shouldIndentText, renderBox.logicalHeight());
682 computeInlineDirectionPositionsForSegment(lineBox, lineInfo, textAlign, lineLogicalLeft, availableLogicalWidth, firstRun, trailingSpaceRun, textBoxDataMap, verticalPositionCache, wordMeasurements);
683 // The widths of all runs are now known. We can now place every inline box (and
684 // compute accurate widths for the inline flow boxes).
685 needsWordSpacing = false;
686 lineBox->placeBoxesInInlineDirection(lineLogicalLeft, needsWordSpacing, textBoxDataMap);
689 BidiRun* RenderBlockFlow::computeInlineDirectionPositionsForSegment(RootInlineBox* lineBox, const LineInfo& lineInfo, ETextAlign textAlign, float& logicalLeft,
690 float& availableLogicalWidth, BidiRun* firstRun, BidiRun* trailingSpaceRun, GlyphOverflowAndFallbackFontsMap& textBoxDataMap, VerticalPositionCache& verticalPositionCache,
691 WordMeasurements& wordMeasurements)
693 bool needsWordSpacing = false;
694 float totalLogicalWidth = lineBox->getFlowSpacingLogicalWidth();
695 unsigned expansionOpportunityCount = 0;
696 bool isAfterExpansion = true;
697 Vector<unsigned, 16> expansionOpportunities;
698 RenderObject* previousObject = 0;
700 BidiRun* r = firstRun;
701 for (; r; r = r->next()) {
702 #if ENABLE(CSS_SHAPES)
703 // Once we have reached the start of the next segment, we have finished
704 // computing the positions for this segment's contents.
705 if (r->m_startsSegment)
708 if (!r->box() || r->renderer().isOutOfFlowPositioned() || r->box()->isLineBreak())
709 continue; // Positioned objects are only participating to figure out their
710 // correct static x position. They have no effect on the width.
711 // Similarly, line break boxes have no effect on the width.
712 if (r->renderer().isText()) {
713 RenderText& rt = toRenderText(r->renderer());
714 if (textAlign == JUSTIFY && r != trailingSpaceRun) {
715 if (!isAfterExpansion)
716 toInlineTextBox(r->box())->setCanHaveLeadingExpansion(true);
717 unsigned opportunitiesInRun;
719 opportunitiesInRun = Font::expansionOpportunityCount(rt.characters8() + r->m_start, r->m_stop - r->m_start, r->box()->direction(), isAfterExpansion);
721 opportunitiesInRun = Font::expansionOpportunityCount(rt.characters16() + r->m_start, r->m_stop - r->m_start, r->box()->direction(), isAfterExpansion);
722 expansionOpportunities.append(opportunitiesInRun);
723 expansionOpportunityCount += opportunitiesInRun;
726 if (int length = rt.textLength()) {
727 if (!r->m_start && needsWordSpacing && isSpaceOrNewline(rt.characterAt(r->m_start)))
728 totalLogicalWidth += lineStyle(*rt.parent(), lineInfo).font().wordSpacing();
729 needsWordSpacing = !isSpaceOrNewline(rt.characterAt(r->m_stop - 1)) && r->m_stop == length;
732 setLogicalWidthForTextRun(lineBox, r, &rt, totalLogicalWidth, lineInfo, textBoxDataMap, verticalPositionCache, wordMeasurements);
734 isAfterExpansion = false;
735 if (!r->renderer().isRenderInline()) {
736 RenderBox& renderBox = toRenderBox(r->renderer());
737 if (renderBox.isRubyRun())
738 setMarginsForRubyRun(r, toRenderRubyRun(renderBox), previousObject, lineInfo);
739 r->box()->setLogicalWidth(logicalWidthForChild(renderBox));
740 totalLogicalWidth += marginStartForChild(renderBox) + marginEndForChild(renderBox);
744 totalLogicalWidth += r->box()->logicalWidth();
745 previousObject = &r->renderer();
748 if (isAfterExpansion && !expansionOpportunities.isEmpty()) {
749 expansionOpportunities.last()--;
750 expansionOpportunityCount--;
753 updateLogicalWidthForAlignment(textAlign, trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth, expansionOpportunityCount);
755 computeExpansionForJustifiedText(firstRun, trailingSpaceRun, expansionOpportunities, expansionOpportunityCount, totalLogicalWidth, availableLogicalWidth);
760 void RenderBlockFlow::computeBlockDirectionPositionsForLine(RootInlineBox* lineBox, BidiRun* firstRun, GlyphOverflowAndFallbackFontsMap& textBoxDataMap,
761 VerticalPositionCache& verticalPositionCache)
763 setLogicalHeight(lineBox->alignBoxesInBlockDirection(logicalHeight(), textBoxDataMap, verticalPositionCache));
765 // Now make sure we place replaced render objects correctly.
766 for (BidiRun* r = firstRun; r; r = r->next()) {
769 continue; // Skip runs with no line boxes.
771 InlineBox& box = *r->box();
773 // Align positioned boxes with the top of the line box. This is
774 // a reasonable approximation of an appropriate y position.
775 if (r->renderer().isOutOfFlowPositioned())
776 box.setLogicalTop(logicalHeight());
778 // Position is used to properly position both replaced elements and
779 // to update the static normal flow x/y of positioned elements.
780 if (r->renderer().isText())
781 toRenderText(r->renderer()).positionLineBox(toInlineTextBox(box));
782 else if (r->renderer().isBox())
783 toRenderBox(r->renderer()).positionLineBox(toInlineElementBox(box));
784 else if (r->renderer().isLineBreak())
785 toRenderLineBreak(r->renderer()).replaceInlineBoxWrapper(toInlineElementBox(box));
787 // Positioned objects and zero-length text nodes destroy their boxes in
788 // position(), which unnecessarily dirties the line.
789 lineBox->markDirty(false);
792 static inline bool isCollapsibleSpace(UChar character, const RenderText& renderer)
794 if (character == ' ' || character == '\t' || character == softHyphen)
796 if (character == '\n')
797 return !renderer.style().preserveNewline();
798 if (character == noBreakSpace)
799 return renderer.style().nbspMode() == SPACE;
803 template <typename CharacterType>
804 static inline int findFirstTrailingSpace(const RenderText& lastText, const CharacterType* characters, int start, int stop)
806 int firstSpace = stop;
807 while (firstSpace > start) {
808 UChar current = characters[firstSpace - 1];
809 if (!isCollapsibleSpace(current, lastText))
817 inline BidiRun* RenderBlockFlow::handleTrailingSpaces(BidiRunList<BidiRun>& bidiRuns, BidiContext* currentContext)
819 if (!bidiRuns.runCount()
820 || !bidiRuns.logicallyLastRun()->renderer().style().breakOnlyAfterWhiteSpace()
821 || !bidiRuns.logicallyLastRun()->renderer().style().autoWrap())
824 BidiRun* trailingSpaceRun = bidiRuns.logicallyLastRun();
825 const RenderObject& lastObject = trailingSpaceRun->renderer();
826 if (!lastObject.isText())
829 const RenderText& lastText = toRenderText(lastObject);
831 if (lastText.is8Bit())
832 firstSpace = findFirstTrailingSpace(lastText, lastText.characters8(), trailingSpaceRun->start(), trailingSpaceRun->stop());
834 firstSpace = findFirstTrailingSpace(lastText, lastText.characters16(), trailingSpaceRun->start(), trailingSpaceRun->stop());
836 if (firstSpace == trailingSpaceRun->stop())
839 TextDirection direction = style().direction();
840 bool shouldReorder = trailingSpaceRun != (direction == LTR ? bidiRuns.lastRun() : bidiRuns.firstRun());
841 if (firstSpace != trailingSpaceRun->start()) {
842 BidiContext* baseContext = currentContext;
843 while (BidiContext* parent = baseContext->parent())
844 baseContext = parent;
846 BidiRun* newTrailingRun = new BidiRun(firstSpace, trailingSpaceRun->m_stop, trailingSpaceRun->renderer(), baseContext, U_OTHER_NEUTRAL);
847 trailingSpaceRun->m_stop = firstSpace;
848 if (direction == LTR)
849 bidiRuns.addRun(newTrailingRun);
851 bidiRuns.prependRun(newTrailingRun);
852 trailingSpaceRun = newTrailingRun;
853 return trailingSpaceRun;
856 return trailingSpaceRun;
858 if (direction == LTR) {
859 bidiRuns.moveRunToEnd(trailingSpaceRun);
860 trailingSpaceRun->m_level = 0;
862 bidiRuns.moveRunToBeginning(trailingSpaceRun);
863 trailingSpaceRun->m_level = 1;
865 return trailingSpaceRun;
868 void RenderBlockFlow::appendFloatingObjectToLastLine(FloatingObject* floatingObject)
870 ASSERT(!floatingObject->originatingLine());
871 floatingObject->setOriginatingLine(lastRootBox());
872 lastRootBox()->appendFloat(floatingObject->renderer());
875 // FIXME: BidiResolver should have this logic.
876 static inline void constructBidiRunsForSegment(InlineBidiResolver& topResolver, BidiRunList<BidiRun>& bidiRuns, const InlineIterator& endOfRuns, VisualDirectionOverride override, bool previousLineBrokeCleanly)
878 // FIXME: We should pass a BidiRunList into createBidiRunsForLine instead
879 // of the resolver owning the runs.
880 ASSERT(&topResolver.runs() == &bidiRuns);
881 ASSERT(topResolver.position() != endOfRuns);
882 RenderObject* currentRoot = topResolver.position().root();
883 topResolver.createBidiRunsForLine(endOfRuns, override, previousLineBrokeCleanly);
885 while (!topResolver.isolatedRuns().isEmpty()) {
886 // It does not matter which order we resolve the runs as long as we resolve them all.
887 BidiRun* isolatedRun = topResolver.isolatedRuns().last();
888 topResolver.isolatedRuns().removeLast();
890 RenderObject& startObj = isolatedRun->renderer();
892 // Only inlines make sense with unicode-bidi: isolate (blocks are already isolated).
893 // FIXME: Because enterIsolate is not passed a RenderObject, we have to crawl up the
894 // tree to see which parent inline is the isolate. We could change enterIsolate
895 // to take a RenderObject and do this logic there, but that would be a layering
896 // violation for BidiResolver (which knows nothing about RenderObject).
897 RenderInline* isolatedInline = toRenderInline(containingIsolate(&startObj, currentRoot));
898 InlineBidiResolver isolatedResolver;
899 EUnicodeBidi unicodeBidi = isolatedInline->style().unicodeBidi();
900 TextDirection direction;
901 if (unicodeBidi == Plaintext)
902 determineDirectionality(direction, InlineIterator(isolatedInline, &isolatedRun->renderer(), 0));
904 ASSERT(unicodeBidi == Isolate || unicodeBidi == IsolateOverride);
905 direction = isolatedInline->style().direction();
907 isolatedResolver.setStatus(BidiStatus(direction, isOverride(unicodeBidi)));
909 // FIXME: The fact that we have to construct an Iterator here
910 // currently prevents this code from moving into BidiResolver.
911 if (!bidiFirstSkippingEmptyInlines(*isolatedInline, &isolatedResolver))
914 // The starting position is the beginning of the first run within the isolate that was identified
915 // during the earlier call to createBidiRunsForLine. This can be but is not necessarily the
916 // first run within the isolate.
917 InlineIterator iter = InlineIterator(isolatedInline, &startObj, isolatedRun->m_start);
918 isolatedResolver.setPositionIgnoringNestedIsolates(iter);
920 // We stop at the next end of line; we may re-enter this isolate in the next call to constructBidiRuns().
921 // FIXME: What should end and previousLineBrokeCleanly be?
922 // rniwa says previousLineBrokeCleanly is just a WinIE hack and could always be false here?
923 isolatedResolver.createBidiRunsForLine(endOfRuns, NoVisualOverride, previousLineBrokeCleanly);
924 // Note that we do not delete the runs from the resolver.
925 // We're not guaranteed to get any BidiRuns in the previous step. If we don't, we allow the placeholder
926 // itself to be turned into an InlineBox. We can't remove it here without potentially losing track of
927 // the logically last run.
928 if (isolatedResolver.runs().runCount())
929 bidiRuns.replaceRunWithRuns(isolatedRun, isolatedResolver.runs());
931 // If we encountered any nested isolate runs, just move them
932 // to the top resolver's list for later processing.
933 if (!isolatedResolver.isolatedRuns().isEmpty()) {
934 topResolver.isolatedRuns().appendVector(isolatedResolver.isolatedRuns());
935 isolatedResolver.isolatedRuns().clear();
936 currentRoot = isolatedInline;
941 static inline void constructBidiRunsForLine(const RenderBlockFlow* block, InlineBidiResolver& topResolver, BidiRunList<BidiRun>& bidiRuns, const InlineIterator& endOfLine, VisualDirectionOverride override, bool previousLineBrokeCleanly)
943 #if !ENABLE(CSS_SHAPES)
945 constructBidiRunsForSegment(topResolver, bidiRuns, endOfLine, override, previousLineBrokeCleanly);
947 ShapeInsideInfo* shapeInsideInfo = block->layoutShapeInsideInfo();
948 if (!shapeInsideInfo || !shapeInsideInfo->hasSegments()) {
949 constructBidiRunsForSegment(topResolver, bidiRuns, endOfLine, override, previousLineBrokeCleanly);
953 const SegmentRangeList& segmentRanges = shapeInsideInfo->segmentRanges();
954 ASSERT(segmentRanges.size());
956 for (size_t i = 0; i < segmentRanges.size(); i++) {
957 LineSegmentIterator iterator = segmentRanges[i].start;
958 InlineIterator segmentStart(iterator.root, iterator.object, iterator.offset);
959 iterator = segmentRanges[i].end;
960 InlineIterator segmentEnd(iterator.root, iterator.object, iterator.offset);
962 ASSERT(segmentStart.renderer());
963 BidiRun* segmentMarker = createRun(segmentStart.offset(), segmentStart.offset(), segmentStart.renderer(), topResolver);
964 segmentMarker->m_startsSegment = true;
965 bidiRuns.addRun(segmentMarker);
966 // Do not collapse midpoints between segments
967 topResolver.midpointState().setBetweenMidpoints(false);
969 if (segmentStart == segmentEnd)
971 topResolver.setPosition(segmentStart, numberOfIsolateAncestors(segmentStart));
972 constructBidiRunsForSegment(topResolver, bidiRuns, segmentEnd, override, previousLineBrokeCleanly);
977 // This function constructs line boxes for all of the text runs in the resolver and computes their position.
978 RootInlineBox* RenderBlockFlow::createLineBoxesFromBidiRuns(BidiRunList<BidiRun>& bidiRuns, const InlineIterator& end, LineInfo& lineInfo, VerticalPositionCache& verticalPositionCache, BidiRun* trailingSpaceRun, WordMeasurements& wordMeasurements)
980 if (!bidiRuns.runCount())
983 // FIXME: Why is this only done when we had runs?
984 lineInfo.setLastLine(!end.renderer());
986 RootInlineBox* lineBox = constructLine(bidiRuns, lineInfo);
990 lineBox->setEndsWithBreak(lineInfo.previousLineBrokeCleanly());
993 bool isSVGRootInlineBox = lineBox->isSVGRootInlineBox();
995 bool isSVGRootInlineBox = false;
998 GlyphOverflowAndFallbackFontsMap textBoxDataMap;
1000 // Now we position all of our text runs horizontally.
1001 if (!isSVGRootInlineBox)
1002 computeInlineDirectionPositionsForLine(lineBox, lineInfo, bidiRuns.firstRun(), trailingSpaceRun, end.atEnd(), textBoxDataMap, verticalPositionCache, wordMeasurements);
1004 // Now position our text runs vertically.
1005 computeBlockDirectionPositionsForLine(lineBox, bidiRuns.firstRun(), textBoxDataMap, verticalPositionCache);
1008 // SVG text layout code computes vertical & horizontal positions on its own.
1009 // Note that we still need to execute computeVerticalPositionsForLine() as
1010 // it calls InlineTextBox::positionLineBox(), which tracks whether the box
1011 // contains reversed text or not. If we wouldn't do that editing and thus
1012 // text selection in RTL boxes would not work as expected.
1013 if (isSVGRootInlineBox) {
1014 ASSERT_WITH_SECURITY_IMPLICATION(isSVGText());
1015 toSVGRootInlineBox(lineBox)->computePerCharacterLayoutInformation();
1019 // Compute our overflow now.
1020 lineBox->computeOverflow(lineBox->lineTop(), lineBox->lineBottom(), textBoxDataMap);
1023 // Highlight acts as an overflow inflation.
1024 if (style().highlight() != nullAtom)
1025 lineBox->addHighlightOverflow();
1030 static void deleteLineRange(LineLayoutState& layoutState, RootInlineBox* startLine, RootInlineBox* stopLine = 0)
1032 RootInlineBox* boxToDelete = startLine;
1033 while (boxToDelete && boxToDelete != stopLine) {
1034 layoutState.updateRepaintRangeFromBox(boxToDelete);
1035 // Note: deleteLineRange(firstRootBox()) is not identical to deleteLineBoxTree().
1036 // deleteLineBoxTree uses nextLineBox() instead of nextRootBox() when traversing.
1037 RootInlineBox* next = boxToDelete->nextRootBox();
1038 boxToDelete->deleteLine();
1043 void RenderBlockFlow::layoutRunsAndFloats(LineLayoutState& layoutState, bool hasInlineChild)
1045 // We want to skip ahead to the first dirty line
1046 InlineBidiResolver resolver;
1047 RootInlineBox* startLine = determineStartPosition(layoutState, resolver);
1049 unsigned consecutiveHyphenatedLines = 0;
1051 for (RootInlineBox* line = startLine->prevRootBox(); line && line->isHyphenated(); line = line->prevRootBox())
1052 consecutiveHyphenatedLines++;
1055 // FIXME: This would make more sense outside of this function, but since
1056 // determineStartPosition can change the fullLayout flag we have to do this here. Failure to call
1057 // determineStartPosition first will break fast/repaint/line-flow-with-floats-9.html.
1058 if (layoutState.isFullLayout() && hasInlineChild && !selfNeedsLayout()) {
1059 setNeedsLayout(MarkOnlyThis); // Mark as needing a full layout to force us to repaint.
1060 if (!view().doingFullRepaint() && hasLayer()) {
1061 // Because we waited until we were already inside layout to discover
1062 // that the block really needed a full layout, we missed our chance to repaint the layer
1063 // before layout started. Luckily the layer has cached the repaint rect for its original
1064 // position and size, and so we can use that to make a repaint happen now.
1065 repaintUsingContainer(containerForRepaint(), pixelSnappedIntRect(layer()->repaintRect()));
1069 if (containsFloats())
1070 layoutState.setLastFloat(m_floatingObjects->set().last().get());
1072 // We also find the first clean line and extract these lines. We will add them back
1073 // if we determine that we're able to synchronize after handling all our dirty lines.
1074 InlineIterator cleanLineStart;
1075 BidiStatus cleanLineBidiStatus;
1076 if (!layoutState.isFullLayout() && startLine)
1077 determineEndPosition(layoutState, startLine, cleanLineStart, cleanLineBidiStatus);
1080 if (!layoutState.usesRepaintBounds())
1081 layoutState.setRepaintRange(logicalHeight());
1082 deleteLineRange(layoutState, startLine);
1085 if (!layoutState.isFullLayout() && lastRootBox() && lastRootBox()->endsWithBreak()) {
1086 // If the last line before the start line ends with a line break that clear floats,
1087 // adjust the height accordingly.
1088 // A line break can be either the first or the last object on a line, depending on its direction.
1089 if (InlineBox* lastLeafChild = lastRootBox()->lastLeafChild()) {
1090 RenderObject* lastObject = &lastLeafChild->renderer();
1091 if (!lastObject->isBR())
1092 lastObject = &lastRootBox()->firstLeafChild()->renderer();
1093 if (lastObject->isBR()) {
1094 EClear clear = lastObject->style().clear();
1101 layoutRunsAndFloatsInRange(layoutState, resolver, cleanLineStart, cleanLineBidiStatus, consecutiveHyphenatedLines);
1102 linkToEndLineIfNeeded(layoutState);
1103 repaintDirtyFloats(layoutState.floats());
1106 RenderTextInfo::RenderTextInfo()
1112 RenderTextInfo::~RenderTextInfo()
1116 // Before restarting the layout loop with a new logicalHeight, remove all floats that were added and reset the resolver.
1117 inline const InlineIterator& RenderBlockFlow::restartLayoutRunsAndFloatsInRange(LayoutUnit oldLogicalHeight, LayoutUnit newLogicalHeight, FloatingObject* lastFloatFromPreviousLine, InlineBidiResolver& resolver, const InlineIterator& oldEnd)
1119 removeFloatingObjectsBelow(lastFloatFromPreviousLine, oldLogicalHeight);
1120 setLogicalHeight(newLogicalHeight);
1121 resolver.setPositionIgnoringNestedIsolates(oldEnd);
1125 #if ENABLE(CSS_SHAPES)
1126 static inline void pushShapeContentOverflowBelowTheContentBox(RenderBlockFlow* block, ShapeInsideInfo* shapeInsideInfo, LayoutUnit lineTop, LayoutUnit lineHeight)
1128 ASSERT(shapeInsideInfo);
1130 LayoutUnit logicalLineBottom = lineTop + lineHeight;
1131 LayoutUnit shapeLogicalBottom = shapeInsideInfo->shapeLogicalBottom();
1132 LayoutUnit shapeContainingBlockLogicalHeight = shapeInsideInfo->shapeContainingBlockLogicalHeight();
1134 bool isOverflowPositionedAlready = (shapeContainingBlockLogicalHeight - shapeInsideInfo->owner().borderAndPaddingAfter() + lineHeight) <= lineTop;
1136 // If the last line overlaps with the shape, we don't need the segments anymore
1137 if (lineTop < shapeLogicalBottom && shapeLogicalBottom < logicalLineBottom)
1138 shapeInsideInfo->clearSegments();
1140 if (logicalLineBottom <= shapeLogicalBottom || !shapeContainingBlockLogicalHeight || isOverflowPositionedAlready)
1143 LayoutUnit newLogicalHeight = block->logicalHeight() + (shapeContainingBlockLogicalHeight - (lineTop + shapeInsideInfo->owner().borderAndPaddingAfter()));
1144 block->setLogicalHeight(newLogicalHeight);
1147 void RenderBlockFlow::updateShapeAndSegmentsForCurrentLine(ShapeInsideInfo*& shapeInsideInfo, const LayoutSize& logicalOffsetFromShapeContainer, LineLayoutState& layoutState)
1149 if (layoutState.flowThread())
1150 return updateShapeAndSegmentsForCurrentLineInFlowThread(shapeInsideInfo, layoutState);
1152 if (!shapeInsideInfo)
1155 LayoutUnit lineTop = logicalHeight() + logicalOffsetFromShapeContainer.height();
1156 LayoutUnit lineLeft = logicalOffsetFromShapeContainer.width();
1157 LayoutUnit lineHeight = this->lineHeight(layoutState.lineInfo().isFirstLine(), isHorizontalWritingMode() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes);
1159 // FIXME: Bug 95361: It is possible for a line to grow beyond lineHeight, in which case these segments may be incorrect.
1160 shapeInsideInfo->updateSegmentsForLine(LayoutSize(lineLeft, lineTop), lineHeight);
1162 pushShapeContentOverflowBelowTheContentBox(this, shapeInsideInfo, lineTop, lineHeight);
1165 void RenderBlockFlow::updateShapeAndSegmentsForCurrentLineInFlowThread(ShapeInsideInfo*& shapeInsideInfo, LineLayoutState& layoutState)
1167 ASSERT(layoutState.flowThread());
1169 RenderRegion* currentRegion = regionAtBlockOffset(logicalHeight());
1170 if (!currentRegion || !currentRegion->logicalHeight())
1173 shapeInsideInfo = currentRegion->shapeInsideInfo();
1175 RenderRegion* nextRegion = 0;
1176 if (!currentRegion->isLastRegion()) {
1177 RenderRegionList regionList = layoutState.flowThread()->renderRegionList();
1178 auto it = regionList.find(currentRegion);
1179 nextRegion = *(++it);
1182 // We only want to deal regions with shapes, so we check if the next region has a shape
1183 if (!shapeInsideInfo && nextRegion && !nextRegion->shapeInsideInfo())
1186 LayoutUnit lineHeight = this->lineHeight(layoutState.lineInfo().isFirstLine(), isHorizontalWritingMode() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes);
1187 LayoutUnit logicalLineTopInFlowThread = logicalHeight() + offsetFromLogicalTopOfFirstPage();
1188 LayoutUnit logicalLineBottomInFlowThread = logicalLineTopInFlowThread + lineHeight;
1189 LayoutUnit logicalRegionTopInFlowThread = currentRegion->logicalTopForFlowThreadContent();
1190 LayoutUnit logicalRegionBottomInFlowThread = logicalRegionTopInFlowThread + currentRegion->logicalHeight() - currentRegion->borderAndPaddingBefore() - currentRegion->borderAndPaddingAfter();
1192 LayoutUnit shapeBottomInFlowThread = LayoutUnit::max();
1193 if (shapeInsideInfo)
1194 shapeBottomInFlowThread = shapeInsideInfo->shapeLogicalBottom() + currentRegion->logicalTopForFlowThreadContent();
1196 bool lineOverLapsWithShapeBottom = shapeBottomInFlowThread < logicalLineBottomInFlowThread;
1197 bool lineTopAdjustedIntoNextRegion = layoutState.adjustedLogicalLineTop() >= currentRegion->logicalHeight();
1198 bool lineOverLapsWithRegionBottom = logicalLineBottomInFlowThread > logicalRegionBottomInFlowThread || lineTopAdjustedIntoNextRegion;
1199 bool overFlowsToNextRegion = nextRegion && (lineOverLapsWithShapeBottom || lineOverLapsWithRegionBottom);
1201 // If the line is between two shapes/regions we position the line to the top of the next shape/region
1202 if (overFlowsToNextRegion) {
1203 ASSERT(currentRegion != nextRegion);
1204 LayoutUnit deltaToNextRegion = logicalRegionBottomInFlowThread - logicalLineTopInFlowThread;
1205 setLogicalHeight(logicalHeight() + deltaToNextRegion);
1207 currentRegion = nextRegion;
1208 shapeInsideInfo = currentRegion->shapeInsideInfo();
1210 logicalLineTopInFlowThread = logicalHeight() + offsetFromLogicalTopOfFirstPage();
1211 logicalLineBottomInFlowThread = logicalLineTopInFlowThread + lineHeight;
1212 logicalRegionTopInFlowThread = currentRegion->logicalTopForFlowThreadContent();
1213 logicalRegionBottomInFlowThread = logicalRegionTopInFlowThread + currentRegion->logicalHeight() - currentRegion->borderAndPaddingBefore() - currentRegion->borderAndPaddingAfter();
1215 if (lineTopAdjustedIntoNextRegion)
1216 layoutState.setAdjustedLogicalLineTop(0);
1219 if (!shapeInsideInfo)
1222 bool isFirstLineInRegion = logicalLineBottomInFlowThread <= (logicalRegionTopInFlowThread + lineHeight);
1223 bool isFirstLineAdjusted = (logicalLineTopInFlowThread - logicalRegionTopInFlowThread) < (layoutState.adjustedLogicalLineTop() - currentRegion->borderAndPaddingBefore());
1224 // We position the first line to the top of the shape in the region or to the previously adjusted position in the shape
1225 if (isFirstLineInRegion || isFirstLineAdjusted) {
1226 LayoutUnit shapeTopOffset = layoutState.adjustedLogicalLineTop();
1228 if (!shapeTopOffset && (shapeInsideInfo->shapeLogicalTop() > 0))
1229 shapeTopOffset = shapeInsideInfo->shapeLogicalTop();
1231 LayoutUnit shapePositionInFlowThread = currentRegion->logicalTopForFlowThreadContent() + shapeTopOffset;
1232 LayoutUnit shapeTopLineTopDelta = shapePositionInFlowThread - logicalLineTopInFlowThread - currentRegion->borderAndPaddingBefore();
1234 setLogicalHeight(logicalHeight() + shapeTopLineTopDelta);
1235 logicalLineTopInFlowThread += shapeTopLineTopDelta;
1236 layoutState.setAdjustedLogicalLineTop(0);
1239 LayoutUnit lineTop = logicalLineTopInFlowThread - currentRegion->logicalTopForFlowThreadContent() + currentRegion->borderAndPaddingBefore();
1241 // FIXME: 118571 - Shape inside on a region does not yet take into account its padding for nested flow blocks
1242 shapeInsideInfo->updateSegmentsForLine(LayoutSize(0, lineTop), lineHeight);
1244 if (currentRegion->isLastRegion())
1245 pushShapeContentOverflowBelowTheContentBox(this, shapeInsideInfo, lineTop, lineHeight);
1248 static inline LayoutUnit adjustLogicalLineTop(ShapeInsideInfo* shapeInsideInfo, InlineIterator start, InlineIterator end, const WordMeasurements& wordMeasurements)
1250 if (!shapeInsideInfo || end != start)
1253 float minWidth = firstPositiveWidth(wordMeasurements);
1254 ASSERT(minWidth || wordMeasurements.isEmpty());
1255 if (minWidth > 0 && shapeInsideInfo->adjustLogicalLineTop(minWidth))
1256 return shapeInsideInfo->logicalLineTop();
1258 return shapeInsideInfo->shapeLogicalBottom();
1261 bool RenderBlockFlow::adjustLogicalLineTopAndLogicalHeightIfNeeded(ShapeInsideInfo* shapeInsideInfo, LayoutUnit absoluteLogicalTop, LineLayoutState& layoutState, InlineBidiResolver& resolver, FloatingObject* lastFloatFromPreviousLine, InlineIterator& end, WordMeasurements& wordMeasurements)
1263 LayoutUnit adjustedLogicalLineTop = adjustLogicalLineTop(shapeInsideInfo, resolver.position(), end, wordMeasurements);
1265 if (shapeInsideInfo && containsFloats()) {
1266 lastFloatFromPreviousLine = m_floatingObjects->set().last().get();
1267 if (!wordMeasurements.size()) {
1268 LayoutUnit floatLogicalTopOffset = shapeInsideInfo->computeFirstFitPositionForFloat(logicalSizeForFloat(lastFloatFromPreviousLine));
1269 if (logicalHeight() < floatLogicalTopOffset)
1270 adjustedLogicalLineTop = floatLogicalTopOffset;
1274 if (!adjustedLogicalLineTop)
1277 LayoutUnit newLogicalHeight = adjustedLogicalLineTop - absoluteLogicalTop;
1279 if (layoutState.flowThread()) {
1280 layoutState.setAdjustedLogicalLineTop(adjustedLogicalLineTop);
1281 newLogicalHeight = logicalHeight();
1284 end = restartLayoutRunsAndFloatsInRange(logicalHeight(), newLogicalHeight, lastFloatFromPreviousLine, resolver, end);
1289 void RenderBlockFlow::layoutRunsAndFloatsInRange(LineLayoutState& layoutState, InlineBidiResolver& resolver, const InlineIterator& cleanLineStart, const BidiStatus& cleanLineBidiStatus, unsigned consecutiveHyphenatedLines)
1291 const RenderStyle& styleToUse = style();
1292 bool paginated = view().layoutState() && view().layoutState()->isPaginated();
1293 LineMidpointState& lineMidpointState = resolver.midpointState();
1294 InlineIterator end = resolver.position();
1295 bool checkForEndLineMatch = layoutState.endLine();
1296 RenderTextInfo renderTextInfo;
1297 VerticalPositionCache verticalPositionCache;
1299 LineBreaker lineBreaker(*this);
1301 #if ENABLE(CSS_SHAPES)
1302 LayoutSize logicalOffsetFromShapeContainer;
1303 ShapeInsideInfo* shapeInsideInfo = layoutShapeInsideInfo();
1304 if (shapeInsideInfo) {
1305 ASSERT(&shapeInsideInfo->owner() == this || allowsShapeInsideInfoSharing());
1306 if (shapeInsideInfo != this->shapeInsideInfo()) {
1307 // FIXME Bug 100284: If subsequent LayoutStates are pushed, we will have to add
1308 // their offsets from the original shape-inside container.
1309 logicalOffsetFromShapeContainer = logicalOffsetFromShapeAncestorContainer(&shapeInsideInfo->owner());
1311 // Begin layout at the logical top of our shape inside.
1312 if (logicalHeight() + logicalOffsetFromShapeContainer.height() < shapeInsideInfo->shapeLogicalTop()) {
1313 LayoutUnit logicalHeight = shapeInsideInfo->shapeLogicalTop() - logicalOffsetFromShapeContainer.height();
1314 if (layoutState.flowThread())
1315 logicalHeight -= shapeInsideInfo->owner().borderAndPaddingBefore();
1316 setLogicalHeight(logicalHeight);
1321 while (!end.atEnd()) {
1322 // FIXME: Is this check necessary before the first iteration or can it be moved to the end?
1323 if (checkForEndLineMatch) {
1324 layoutState.setEndLineMatched(matchedEndLine(layoutState, resolver, cleanLineStart, cleanLineBidiStatus));
1325 if (layoutState.endLineMatched()) {
1326 resolver.setPosition(InlineIterator(resolver.position().root(), 0, 0), 0);
1331 lineMidpointState.reset();
1333 layoutState.lineInfo().setEmpty(true);
1334 layoutState.lineInfo().resetRunsFromLeadingWhitespace();
1336 const InlineIterator oldEnd = end;
1337 bool isNewUBAParagraph = layoutState.lineInfo().previousLineBrokeCleanly();
1338 FloatingObject* lastFloatFromPreviousLine = (containsFloats()) ? m_floatingObjects->set().last().get() : 0;
1340 #if ENABLE(CSS_SHAPES)
1341 updateShapeAndSegmentsForCurrentLine(shapeInsideInfo, logicalOffsetFromShapeContainer, layoutState);
1343 WordMeasurements wordMeasurements;
1344 end = lineBreaker.nextLineBreak(resolver, layoutState.lineInfo(), renderTextInfo, lastFloatFromPreviousLine, consecutiveHyphenatedLines, wordMeasurements);
1345 renderTextInfo.m_lineBreakIterator.resetPriorContext();
1346 if (resolver.position().atEnd()) {
1347 // FIXME: We shouldn't be creating any runs in nextLineBreak to begin with!
1348 // Once BidiRunList is separated from BidiResolver this will not be needed.
1349 resolver.runs().deleteRuns();
1350 resolver.markCurrentRunEmpty(); // FIXME: This can probably be replaced by an ASSERT (or just removed).
1351 layoutState.setCheckForFloatsFromLastLine(true);
1352 resolver.setPosition(InlineIterator(resolver.position().root(), 0, 0), 0);
1356 #if ENABLE(CSS_SHAPES)
1357 if (adjustLogicalLineTopAndLogicalHeightIfNeeded(shapeInsideInfo, logicalOffsetFromShapeContainer.height(), layoutState, resolver, lastFloatFromPreviousLine, end, wordMeasurements))
1360 ASSERT(end != resolver.position());
1362 // This is a short-cut for empty lines.
1363 if (layoutState.lineInfo().isEmpty()) {
1365 lastRootBox()->setLineBreakInfo(end.renderer(), end.offset(), resolver.status());
1367 VisualDirectionOverride override = (styleToUse.rtlOrdering() == VisualOrder ? (styleToUse.direction() == LTR ? VisualLeftToRightOverride : VisualRightToLeftOverride) : NoVisualOverride);
1369 if (isNewUBAParagraph && styleToUse.unicodeBidi() == Plaintext && !resolver.context()->parent()) {
1370 TextDirection direction = styleToUse.direction();
1371 determineDirectionality(direction, resolver.position());
1372 resolver.setStatus(BidiStatus(direction, isOverride(styleToUse.unicodeBidi())));
1374 // FIXME: This ownership is reversed. We should own the BidiRunList and pass it to createBidiRunsForLine.
1375 BidiRunList<BidiRun>& bidiRuns = resolver.runs();
1376 constructBidiRunsForLine(this, resolver, bidiRuns, end, override, layoutState.lineInfo().previousLineBrokeCleanly());
1377 ASSERT(resolver.position() == end);
1379 BidiRun* trailingSpaceRun = !layoutState.lineInfo().previousLineBrokeCleanly() ? handleTrailingSpaces(bidiRuns, resolver.context()) : 0;
1381 if (bidiRuns.runCount() && lineBreaker.lineWasHyphenated()) {
1382 bidiRuns.logicallyLastRun()->m_hasHyphen = true;
1383 consecutiveHyphenatedLines++;
1385 consecutiveHyphenatedLines = 0;
1387 // Now that the runs have been ordered, we create the line boxes.
1388 // At the same time we figure out where border/padding/margin should be applied for
1389 // inline flow boxes.
1391 LayoutUnit oldLogicalHeight = logicalHeight();
1392 RootInlineBox* lineBox = createLineBoxesFromBidiRuns(bidiRuns, end, layoutState.lineInfo(), verticalPositionCache, trailingSpaceRun, wordMeasurements);
1394 bidiRuns.deleteRuns();
1395 resolver.markCurrentRunEmpty(); // FIXME: This can probably be replaced by an ASSERT (or just removed).
1398 lineBox->setLineBreakInfo(end.renderer(), end.offset(), resolver.status());
1399 if (layoutState.usesRepaintBounds())
1400 layoutState.updateRepaintRangeFromBox(lineBox);
1403 LayoutUnit adjustment = 0;
1404 adjustLinePositionForPagination(lineBox, adjustment, layoutState.flowThread());
1406 LayoutUnit oldLineWidth = availableLogicalWidthForLine(oldLogicalHeight, layoutState.lineInfo().isFirstLine());
1407 lineBox->adjustBlockDirectionPosition(adjustment);
1408 if (layoutState.usesRepaintBounds())
1409 layoutState.updateRepaintRangeFromBox(lineBox);
1411 if (availableLogicalWidthForLine(oldLogicalHeight + adjustment, layoutState.lineInfo().isFirstLine()) != oldLineWidth) {
1412 // We have to delete this line, remove all floats that got added, and let line layout re-run.
1413 lineBox->deleteLine();
1414 end = restartLayoutRunsAndFloatsInRange(oldLogicalHeight, oldLogicalHeight + adjustment, lastFloatFromPreviousLine, resolver, oldEnd);
1418 setLogicalHeight(lineBox->lineBottomWithLeading());
1421 if (layoutState.flowThread())
1422 updateRegionForLine(lineBox);
1427 for (size_t i = 0; i < lineBreaker.positionedObjects().size(); ++i)
1428 setStaticPositions(*this, *lineBreaker.positionedObjects()[i]);
1430 if (!layoutState.lineInfo().isEmpty()) {
1431 layoutState.lineInfo().setFirstLine(false);
1432 newLine(lineBreaker.clear());
1435 if (m_floatingObjects && lastRootBox()) {
1436 const FloatingObjectSet& floatingObjectSet = m_floatingObjects->set();
1437 auto it = floatingObjectSet.begin();
1438 auto end = floatingObjectSet.end();
1439 if (layoutState.lastFloat()) {
1440 auto lastFloatIterator = floatingObjectSet.find<FloatingObject&, FloatingObjectHashTranslator>(*layoutState.lastFloat());
1441 ASSERT(lastFloatIterator != end);
1442 ++lastFloatIterator;
1443 it = lastFloatIterator;
1445 for (; it != end; ++it) {
1446 FloatingObject* f = it->get();
1447 appendFloatingObjectToLastLine(f);
1448 ASSERT(&f->renderer() == &layoutState.floats()[layoutState.floatIndex()].object);
1449 // If a float's geometry has changed, give up on syncing with clean lines.
1450 if (layoutState.floats()[layoutState.floatIndex()].rect != f->frameRect())
1451 checkForEndLineMatch = false;
1452 layoutState.setFloatIndex(layoutState.floatIndex() + 1);
1454 layoutState.setLastFloat(!floatingObjectSet.isEmpty() ? floatingObjectSet.last().get() : nullptr);
1457 lineMidpointState.reset();
1458 resolver.setPosition(end, numberOfIsolateAncestors(end));
1461 // In case we already adjusted the line positions during this layout to avoid widows
1462 // then we need to ignore the possibility of having a new widows situation.
1463 // Otherwise, we risk leaving empty containers which is against the block fragmentation principles.
1464 if (paginated && !style().hasAutoWidows() && !didBreakAtLineToAvoidWidow()) {
1465 // Check the line boxes to make sure we didn't create unacceptable widows.
1466 // However, we'll prioritize orphans - so nothing we do here should create
1469 RootInlineBox* lineBox = lastRootBox();
1471 // Count from the end of the block backwards, to see how many hanging
1473 RootInlineBox* firstLineInBlock = firstRootBox();
1474 int numLinesHanging = 1;
1475 while (lineBox && lineBox != firstLineInBlock && !lineBox->isFirstAfterPageBreak()) {
1477 lineBox = lineBox->prevRootBox();
1480 // If there were no breaks in the block, we didn't create any widows.
1481 if (!lineBox || !lineBox->isFirstAfterPageBreak() || lineBox == firstLineInBlock)
1484 if (numLinesHanging < style().widows()) {
1485 // We have detected a widow. Now we need to work out how many
1486 // lines there are on the previous page, and how many we need
1488 int numLinesNeeded = style().widows() - numLinesHanging;
1489 RootInlineBox* currentFirstLineOfNewPage = lineBox;
1491 // Count the number of lines in the previous page.
1492 lineBox = lineBox->prevRootBox();
1493 int numLinesInPreviousPage = 1;
1494 while (lineBox && lineBox != firstLineInBlock && !lineBox->isFirstAfterPageBreak()) {
1495 ++numLinesInPreviousPage;
1496 lineBox = lineBox->prevRootBox();
1499 // If there was an explicit value for orphans, respect that. If not, we still
1500 // shouldn't create a situation where we make an orphan bigger than the initial value.
1501 // This means that setting widows implies we also care about orphans, but given
1502 // the specification says the initial orphan value is non-zero, this is ok. The
1503 // author is always free to set orphans explicitly as well.
1504 int orphans = style().hasAutoOrphans() ? style().initialOrphans() : style().orphans();
1505 int numLinesAvailable = numLinesInPreviousPage - orphans;
1506 if (numLinesAvailable <= 0)
1509 int numLinesToTake = std::min(numLinesAvailable, numLinesNeeded);
1510 // Wind back from our first widowed line.
1511 lineBox = currentFirstLineOfNewPage;
1512 for (int i = 0; i < numLinesToTake; ++i)
1513 lineBox = lineBox->prevRootBox();
1515 // We now want to break at this line. Remember for next layout and trigger relayout.
1516 setBreakAtLineToAvoidWidow(lineCount(lineBox));
1517 markLinesDirtyInBlockRange(lastRootBox()->lineBottomWithLeading(), lineBox->lineBottomWithLeading(), lineBox);
1521 clearDidBreakAtLineToAvoidWidow();
1524 void RenderBlockFlow::linkToEndLineIfNeeded(LineLayoutState& layoutState)
1526 if (layoutState.endLine()) {
1527 if (layoutState.endLineMatched()) {
1528 bool paginated = view().layoutState() && view().layoutState()->isPaginated();
1529 // Attach all the remaining lines, and then adjust their y-positions as needed.
1530 LayoutUnit delta = logicalHeight() - layoutState.endLineLogicalTop();
1531 for (RootInlineBox* line = layoutState.endLine(); line; line = line->nextRootBox()) {
1534 delta -= line->paginationStrut();
1535 adjustLinePositionForPagination(line, delta, layoutState.flowThread());
1538 layoutState.updateRepaintRangeFromBox(line, delta);
1539 line->adjustBlockDirectionPosition(delta);
1541 if (layoutState.flowThread())
1542 updateRegionForLine(line);
1543 if (Vector<RenderBox*>* cleanLineFloats = line->floatsPtr()) {
1544 for (auto it = cleanLineFloats->begin(), end = cleanLineFloats->end(); it != end; ++it) {
1545 RenderBox* floatingBox = *it;
1546 FloatingObject* floatingObject = insertFloatingObject(*floatingBox);
1547 ASSERT(!floatingObject->originatingLine());
1548 floatingObject->setOriginatingLine(line);
1549 setLogicalHeight(logicalTopForChild(*floatingBox) - marginBeforeForChild(*floatingBox) + delta);
1550 positionNewFloats();
1554 setLogicalHeight(lastRootBox()->lineBottomWithLeading());
1556 // Delete all the remaining lines.
1557 deleteLineRange(layoutState, layoutState.endLine());
1561 if (m_floatingObjects && (layoutState.checkForFloatsFromLastLine() || positionNewFloats()) && lastRootBox()) {
1562 // In case we have a float on the last line, it might not be positioned up to now.
1563 // This has to be done before adding in the bottom border/padding, or the float will
1564 // include the padding incorrectly. -dwh
1565 if (layoutState.checkForFloatsFromLastLine()) {
1566 LayoutUnit bottomVisualOverflow = lastRootBox()->logicalBottomVisualOverflow();
1567 LayoutUnit bottomLayoutOverflow = lastRootBox()->logicalBottomLayoutOverflow();
1568 auto newLineBox = std::make_unique<TrailingFloatsRootInlineBox>(*this);
1569 auto trailingFloatsLineBox = newLineBox.get();
1570 m_lineBoxes.appendLineBox(std::move(newLineBox));
1571 trailingFloatsLineBox->setConstructed();
1572 GlyphOverflowAndFallbackFontsMap textBoxDataMap;
1573 VerticalPositionCache verticalPositionCache;
1574 LayoutUnit blockLogicalHeight = logicalHeight();
1575 trailingFloatsLineBox->alignBoxesInBlockDirection(blockLogicalHeight, textBoxDataMap, verticalPositionCache);
1576 trailingFloatsLineBox->setLineTopBottomPositions(blockLogicalHeight, blockLogicalHeight, blockLogicalHeight, blockLogicalHeight);
1577 trailingFloatsLineBox->setPaginatedLineWidth(availableLogicalWidthForContent(blockLogicalHeight));
1578 LayoutRect logicalLayoutOverflow(0, blockLogicalHeight, 1, bottomLayoutOverflow - blockLogicalHeight);
1579 LayoutRect logicalVisualOverflow(0, blockLogicalHeight, 1, bottomVisualOverflow - blockLogicalHeight);
1580 trailingFloatsLineBox->setOverflowFromLogicalRects(logicalLayoutOverflow, logicalVisualOverflow, trailingFloatsLineBox->lineTop(), trailingFloatsLineBox->lineBottom());
1581 if (layoutState.flowThread())
1582 updateRegionForLine(trailingFloatsLineBox);
1585 const FloatingObjectSet& floatingObjectSet = m_floatingObjects->set();
1586 auto it = floatingObjectSet.begin();
1587 auto end = floatingObjectSet.end();
1588 if (layoutState.lastFloat()) {
1589 auto lastFloatIterator = floatingObjectSet.find<FloatingObject&, FloatingObjectHashTranslator>(*layoutState.lastFloat());
1590 ASSERT(lastFloatIterator != end);
1591 ++lastFloatIterator;
1592 it = lastFloatIterator;
1594 for (; it != end; ++it)
1595 appendFloatingObjectToLastLine(it->get());
1596 layoutState.setLastFloat(!floatingObjectSet.isEmpty() ? floatingObjectSet.last().get() : nullptr);
1600 void RenderBlockFlow::repaintDirtyFloats(Vector<FloatWithRect>& floats)
1602 size_t floatCount = floats.size();
1603 // Floats that did not have layout did not repaint when we laid them out. They would have
1604 // painted by now if they had moved, but if they stayed at (0, 0), they still need to be
1606 for (size_t i = 0; i < floatCount; ++i) {
1607 if (!floats[i].everHadLayout) {
1608 RenderBox& box = floats[i].object;
1609 if (!box.x() && !box.y() && box.checkForRepaintDuringLayout())
1615 void RenderBlockFlow::layoutLineBoxes(bool relayoutChildren, LayoutUnit& repaintLogicalTop, LayoutUnit& repaintLogicalBottom)
1617 ASSERT(!m_simpleLineLayout);
1619 setLogicalHeight(borderAndPaddingBefore());
1621 // Lay out our hypothetical grid line as though it occurs at the top of the block.
1622 if (view().layoutState() && view().layoutState()->lineGrid() == this)
1623 layoutLineGridBox();
1625 RenderFlowThread* flowThread = flowThreadContainingBlock();
1626 bool clearLinesForPagination = firstRootBox() && flowThread && !flowThread->hasRegions();
1628 // Figure out if we should clear out our line boxes.
1629 // FIXME: Handle resize eventually!
1630 bool isFullLayout = !firstRootBox() || selfNeedsLayout() || relayoutChildren || clearLinesForPagination;
1631 LineLayoutState layoutState(isFullLayout, repaintLogicalTop, repaintLogicalBottom, flowThread);
1634 lineBoxes().deleteLineBoxes();
1636 // Text truncation kicks in in two cases:
1637 // 1) If your overflow isn't visible and your text-overflow-mode isn't clip.
1638 // 2) If you're an anonymous block with a block parent that satisfies #1.
1639 // FIXME: CSS3 says that descendants that are clipped must also know how to truncate. This is insanely
1640 // difficult to figure out in general (especially in the middle of doing layout), so we only handle the
1641 // simple case of an anonymous block truncating when it's parent is clipped.
1642 bool hasTextOverflow = (style().textOverflow() && hasOverflowClip())
1643 || (isAnonymousBlock() && parent() && parent()->isRenderBlock() && parent()->style().textOverflow() && parent()->hasOverflowClip());
1645 // Walk all the lines and delete our ellipsis line boxes if they exist.
1646 if (hasTextOverflow)
1647 deleteEllipsisLineBoxes();
1650 // In full layout mode, clear the line boxes of children upfront. Otherwise,
1651 // siblings can run into stale root lineboxes during layout. Then layout
1652 // the replaced elements later. In partial layout mode, line boxes are not
1653 // deleted and only dirtied. In that case, we can layout the replaced
1654 // elements at the same time.
1655 bool hasInlineChild = false;
1656 Vector<RenderBox*> replacedChildren;
1657 for (InlineWalker walker(*this); !walker.atEnd(); walker.advance()) {
1658 RenderObject& o = *walker.current();
1660 if (!hasInlineChild && o.isInline())
1661 hasInlineChild = true;
1663 if (o.isReplaced() || o.isFloating() || o.isOutOfFlowPositioned()) {
1664 RenderBox& box = toRenderBox(o);
1666 if (relayoutChildren || box.hasRelativeDimensions())
1667 box.setChildNeedsLayout(MarkOnlyThis);
1669 // If relayoutChildren is set and the child has percentage padding or an embedded content box, we also need to invalidate the childs pref widths.
1670 if (relayoutChildren && box.needsPreferredWidthsRecalculation())
1671 box.setPreferredLogicalWidthsDirty(true, MarkOnlyThis);
1673 if (box.isOutOfFlowPositioned())
1674 box.containingBlock()->insertPositionedObject(box);
1675 else if (box.isFloating())
1676 layoutState.floats().append(FloatWithRect(box));
1677 else if (isFullLayout || box.needsLayout()) {
1678 // Replaced element.
1679 box.dirtyLineBoxes(isFullLayout);
1681 replacedChildren.append(&box);
1683 box.layoutIfNeeded();
1685 } else if (o.isTextOrLineBreak() || (o.isRenderInline() && !walker.atEndOfInline())) {
1686 if (o.isRenderInline())
1687 toRenderInline(o).updateAlwaysCreateLineBoxes(layoutState.isFullLayout());
1688 if (layoutState.isFullLayout() || o.selfNeedsLayout())
1689 dirtyLineBoxesForRenderer(o, layoutState.isFullLayout());
1690 o.clearNeedsLayout();
1694 for (size_t i = 0; i < replacedChildren.size(); i++)
1695 replacedChildren[i]->layoutIfNeeded();
1697 layoutRunsAndFloats(layoutState, hasInlineChild);
1700 // Expand the last line to accommodate Ruby and emphasis marks.
1701 int lastLineAnnotationsAdjustment = 0;
1702 if (lastRootBox()) {
1703 LayoutUnit lowestAllowedPosition = std::max(lastRootBox()->lineBottom(), logicalHeight() + paddingAfter());
1704 if (!style().isFlippedLinesWritingMode())
1705 lastLineAnnotationsAdjustment = lastRootBox()->computeUnderAnnotationAdjustment(lowestAllowedPosition);
1707 lastLineAnnotationsAdjustment = lastRootBox()->computeOverAnnotationAdjustment(lowestAllowedPosition);
1710 // Now add in the bottom border/padding.
1711 setLogicalHeight(logicalHeight() + lastLineAnnotationsAdjustment + borderAndPaddingAfter() + scrollbarLogicalHeight());
1713 if (!firstRootBox() && hasLineIfEmpty())
1714 setLogicalHeight(logicalHeight() + lineHeight(true, isHorizontalWritingMode() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes));
1716 // See if we have any lines that spill out of our block. If we do, then we will possibly need to
1718 if (hasTextOverflow)
1719 checkLinesForTextOverflow();
1722 void RenderBlockFlow::checkFloatsInCleanLine(RootInlineBox* line, Vector<FloatWithRect>& floats, size_t& floatIndex, bool& encounteredNewFloat, bool& dirtiedByFloat)
1724 Vector<RenderBox*>* cleanLineFloats = line->floatsPtr();
1725 if (!cleanLineFloats)
1728 for (auto it = cleanLineFloats->begin(), end = cleanLineFloats->end(); it != end; ++it) {
1729 RenderBox* floatingBox = *it;
1730 floatingBox->layoutIfNeeded();
1731 LayoutSize newSize(floatingBox->width() + floatingBox->marginWidth(), floatingBox->height() + floatingBox->marginHeight());
1732 ASSERT_WITH_SECURITY_IMPLICATION(floatIndex < floats.size());
1733 if (&floats[floatIndex].object != floatingBox) {
1734 encounteredNewFloat = true;
1738 if (floats[floatIndex].rect.size() != newSize) {
1739 LayoutUnit floatTop = isHorizontalWritingMode() ? floats[floatIndex].rect.y() : floats[floatIndex].rect.x();
1740 LayoutUnit floatHeight = isHorizontalWritingMode() ? std::max(floats[floatIndex].rect.height(), newSize.height()) : std::max(floats[floatIndex].rect.width(), newSize.width());
1741 floatHeight = std::min(floatHeight, LayoutUnit::max() - floatTop);
1743 markLinesDirtyInBlockRange(line->lineBottomWithLeading(), floatTop + floatHeight, line);
1744 floats[floatIndex].rect.setSize(newSize);
1745 dirtiedByFloat = true;
1751 RootInlineBox* RenderBlockFlow::determineStartPosition(LineLayoutState& layoutState, InlineBidiResolver& resolver)
1753 RootInlineBox* curr = 0;
1754 RootInlineBox* last = 0;
1756 // FIXME: This entire float-checking block needs to be broken into a new function.
1757 bool dirtiedByFloat = false;
1758 if (!layoutState.isFullLayout()) {
1759 // Paginate all of the clean lines.
1760 bool paginated = view().layoutState() && view().layoutState()->isPaginated();
1761 LayoutUnit paginationDelta = 0;
1762 size_t floatIndex = 0;
1763 for (curr = firstRootBox(); curr && !curr->isDirty(); curr = curr->nextRootBox()) {
1765 if (lineWidthForPaginatedLineChanged(curr, 0, layoutState.flowThread())) {
1769 paginationDelta -= curr->paginationStrut();
1770 adjustLinePositionForPagination(curr, paginationDelta, layoutState.flowThread());
1771 if (paginationDelta) {
1772 if (containsFloats() || !layoutState.floats().isEmpty()) {
1773 // FIXME: Do better eventually. For now if we ever shift because of pagination and floats are present just go to a full layout.
1774 layoutState.markForFullLayout();
1778 layoutState.updateRepaintRangeFromBox(curr, paginationDelta);
1779 curr->adjustBlockDirectionPosition(paginationDelta);
1781 if (layoutState.flowThread())
1782 updateRegionForLine(curr);
1785 // If a new float has been inserted before this line or before its last known float, just do a full layout.
1786 bool encounteredNewFloat = false;
1787 checkFloatsInCleanLine(curr, layoutState.floats(), floatIndex, encounteredNewFloat, dirtiedByFloat);
1788 if (encounteredNewFloat)
1789 layoutState.markForFullLayout();
1791 if (dirtiedByFloat || layoutState.isFullLayout())
1794 // Check if a new float has been inserted after the last known float.
1795 if (!curr && floatIndex < layoutState.floats().size())
1796 layoutState.markForFullLayout();
1799 if (layoutState.isFullLayout()) {
1800 m_lineBoxes.deleteLineBoxTree();
1803 ASSERT(!firstRootBox() && !lastRootBox());
1806 // We have a dirty line.
1807 if (RootInlineBox* prevRootBox = curr->prevRootBox()) {
1808 // We have a previous line.
1809 if (!dirtiedByFloat && (!prevRootBox->endsWithBreak() || !prevRootBox->lineBreakObj() || (prevRootBox->lineBreakObj()->isText() && prevRootBox->lineBreakPos() >= toRenderText(prevRootBox->lineBreakObj())->textLength())))
1810 // The previous line didn't break cleanly or broke at a newline
1811 // that has been deleted, so treat it as dirty too.
1815 // No dirty lines were found.
1816 // If the last line didn't break cleanly, treat it as dirty.
1817 if (lastRootBox() && !lastRootBox()->endsWithBreak())
1818 curr = lastRootBox();
1821 // If we have no dirty lines, then last is just the last root box.
1822 last = curr ? curr->prevRootBox() : lastRootBox();
1825 unsigned numCleanFloats = 0;
1826 if (!layoutState.floats().isEmpty()) {
1827 LayoutUnit savedLogicalHeight = logicalHeight();
1828 // Restore floats from clean lines.
1829 RootInlineBox* line = firstRootBox();
1830 while (line != curr) {
1831 if (Vector<RenderBox*>* cleanLineFloats = line->floatsPtr()) {
1832 for (auto it = cleanLineFloats->begin(), end = cleanLineFloats->end(); it != end; ++it) {
1833 RenderBox* floatingBox = *it;
1834 FloatingObject* floatingObject = insertFloatingObject(*floatingBox);
1835 ASSERT(!floatingObject->originatingLine());
1836 floatingObject->setOriginatingLine(line);
1837 setLogicalHeight(logicalTopForChild(*floatingBox) - marginBeforeForChild(*floatingBox));
1838 positionNewFloats();
1839 ASSERT(&layoutState.floats()[numCleanFloats].object == floatingBox);
1843 line = line->nextRootBox();
1845 setLogicalHeight(savedLogicalHeight);
1847 layoutState.setFloatIndex(numCleanFloats);
1849 layoutState.lineInfo().setFirstLine(!last);
1850 layoutState.lineInfo().setPreviousLineBrokeCleanly(!last || last->endsWithBreak());
1853 setLogicalHeight(last->lineBottomWithLeading());
1854 InlineIterator iter = InlineIterator(this, last->lineBreakObj(), last->lineBreakPos());
1855 resolver.setPosition(iter, numberOfIsolateAncestors(iter));
1856 resolver.setStatus(last->lineBreakBidiStatus());
1858 TextDirection direction = style().direction();
1859 if (style().unicodeBidi() == Plaintext)
1860 determineDirectionality(direction, InlineIterator(this, bidiFirstSkippingEmptyInlines(*this), 0));
1861 resolver.setStatus(BidiStatus(direction, isOverride(style().unicodeBidi())));
1862 InlineIterator iter = InlineIterator(this, bidiFirstSkippingEmptyInlines(*this, &resolver), 0);
1863 resolver.setPosition(iter, numberOfIsolateAncestors(iter));
1868 void RenderBlockFlow::determineEndPosition(LineLayoutState& layoutState, RootInlineBox* startLine, InlineIterator& cleanLineStart, BidiStatus& cleanLineBidiStatus)
1870 ASSERT(!layoutState.endLine());
1871 size_t floatIndex = layoutState.floatIndex();
1872 RootInlineBox* last = 0;
1873 for (RootInlineBox* curr = startLine->nextRootBox(); curr; curr = curr->nextRootBox()) {
1874 if (!curr->isDirty()) {
1875 bool encounteredNewFloat = false;
1876 bool dirtiedByFloat = false;
1877 checkFloatsInCleanLine(curr, layoutState.floats(), floatIndex, encounteredNewFloat, dirtiedByFloat);
1878 if (encounteredNewFloat)
1881 if (curr->isDirty())
1890 // At this point, |last| is the first line in a run of clean lines that ends with the last line
1893 RootInlineBox* prev = last->prevRootBox();
1894 cleanLineStart = InlineIterator(this, prev->lineBreakObj(), prev->lineBreakPos());
1895 cleanLineBidiStatus = prev->lineBreakBidiStatus();
1896 layoutState.setEndLineLogicalTop(prev->lineBottomWithLeading());
1898 for (RootInlineBox* line = last; line; line = line->nextRootBox())
1899 line->extractLine(); // Disconnect all line boxes from their render objects while preserving
1900 // their connections to one another.
1902 layoutState.setEndLine(last);
1905 bool RenderBlockFlow::checkPaginationAndFloatsAtEndLine(LineLayoutState& layoutState)
1907 LayoutUnit lineDelta = logicalHeight() - layoutState.endLineLogicalTop();
1909 bool paginated = view().layoutState() && view().layoutState()->isPaginated();
1910 if (paginated && layoutState.flowThread()) {
1911 // Check all lines from here to the end, and see if the hypothetical new position for the lines will result
1912 // in a different available line width.
1913 for (RootInlineBox* lineBox = layoutState.endLine(); lineBox; lineBox = lineBox->nextRootBox()) {
1915 // This isn't the real move we're going to do, so don't update the line box's pagination
1917 LayoutUnit oldPaginationStrut = lineBox->paginationStrut();
1918 lineDelta -= oldPaginationStrut;
1919 adjustLinePositionForPagination(lineBox, lineDelta, layoutState.flowThread());
1920 lineBox->setPaginationStrut(oldPaginationStrut);
1922 if (lineWidthForPaginatedLineChanged(lineBox, lineDelta, layoutState.flowThread()))
1927 if (!lineDelta || !m_floatingObjects)
1930 // See if any floats end in the range along which we want to shift the lines vertically.
1931 LayoutUnit logicalTop = std::min(logicalHeight(), layoutState.endLineLogicalTop());
1933 RootInlineBox* lastLine = layoutState.endLine();
1934 while (RootInlineBox* nextLine = lastLine->nextRootBox())
1935 lastLine = nextLine;
1937 LayoutUnit logicalBottom = lastLine->lineBottomWithLeading() + absoluteValue(lineDelta);
1939 const FloatingObjectSet& floatingObjectSet = m_floatingObjects->set();
1940 auto end = floatingObjectSet.end();
1941 for (auto it = floatingObjectSet.begin(); it != end; ++it) {
1942 FloatingObject* floatingObject = it->get();
1943 if (logicalBottomForFloat(floatingObject) >= logicalTop && logicalBottomForFloat(floatingObject) < logicalBottom)
1950 bool RenderBlockFlow::lineWidthForPaginatedLineChanged(RootInlineBox* rootBox, LayoutUnit lineDelta, RenderFlowThread* flowThread) const
1955 RenderRegion* currentRegion = regionAtBlockOffset(rootBox->lineTopWithLeading() + lineDelta);
1956 // Just bail if the region didn't change.
1957 if (rootBox->containingRegion() == currentRegion)
1959 return rootBox->paginatedLineWidth() != availableLogicalWidthForContent(currentRegion);
1962 bool RenderBlockFlow::matchedEndLine(LineLayoutState& layoutState, const InlineBidiResolver& resolver, const InlineIterator& endLineStart, const BidiStatus& endLineStatus)
1964 if (resolver.position() == endLineStart) {
1965 if (resolver.status() != endLineStatus)
1967 return checkPaginationAndFloatsAtEndLine(layoutState);
1970 // The first clean line doesn't match, but we can check a handful of following lines to try
1971 // to match back up.
1972 static int numLines = 8; // The # of lines we're willing to match against.
1973 RootInlineBox* originalEndLine = layoutState.endLine();
1974 RootInlineBox* line = originalEndLine;
1975 for (int i = 0; i < numLines && line; i++, line = line->nextRootBox()) {
1976 if (line->lineBreakObj() == resolver.position().renderer() && line->lineBreakPos() == resolver.position().offset()) {
1978 if (line->lineBreakBidiStatus() != resolver.status())
1979 return false; // ...but the bidi state doesn't match.
1981 bool matched = false;
1982 RootInlineBox* result = line->nextRootBox();
1983 layoutState.setEndLine(result);
1985 layoutState.setEndLineLogicalTop(line->lineBottomWithLeading());
1986 matched = checkPaginationAndFloatsAtEndLine(layoutState);
1989 // Now delete the lines that we failed to sync.
1990 deleteLineRange(layoutState, originalEndLine, result);
1998 bool RenderBlock::generatesLineBoxesForInlineChild(RenderObject* inlineObj)
2000 ASSERT(inlineObj->parent() == this);
2002 InlineIterator it(this, inlineObj, 0);
2003 // FIXME: We should pass correct value for WhitespacePosition.
2004 while (!it.atEnd() && !requiresLineBox(it))
2010 void RenderBlockFlow::addOverflowFromInlineChildren()
2012 if (auto layout = simpleLineLayout()) {
2013 ASSERT(!hasOverflowClip());
2014 SimpleLineLayout::collectFlowOverflow(*this, *layout);
2017 LayoutUnit endPadding = hasOverflowClip() ? paddingEnd() : LayoutUnit();
2018 // FIXME: Need to find another way to do this, since scrollbars could show when we don't want them to.
2019 if (hasOverflowClip() && !endPadding && element() && element()->isRootEditableElement() && style().isLeftToRightDirection())
2021 for (RootInlineBox* curr = firstRootBox(); curr; curr = curr->nextRootBox()) {
2022 addLayoutOverflow(curr->paddedLayoutOverflowRect(endPadding));
2023 RenderRegion* region = curr->containingRegion();
2025 region->addLayoutOverflowForBox(this, curr->paddedLayoutOverflowRect(endPadding));
2026 if (!hasOverflowClip()) {
2027 addVisualOverflow(curr->visualOverflowRect(curr->lineTop(), curr->lineBottom()));
2029 region->addVisualOverflowForBox(this, curr->visualOverflowRect(curr->lineTop(), curr->lineBottom()));
2034 void RenderBlockFlow::deleteEllipsisLineBoxes()
2036 ETextAlign textAlign = style().textAlign();
2037 bool ltr = style().isLeftToRightDirection();
2038 bool firstLine = true;
2039 for (RootInlineBox* curr = firstRootBox(); curr; curr = curr->nextRootBox()) {
2040 if (curr->hasEllipsisBox()) {
2041 curr->clearTruncation();
2043 // Shift the line back where it belongs if we cannot accomodate an ellipsis.
2044 float logicalLeft = pixelSnappedLogicalLeftOffsetForLine(curr->lineTop(), firstLine);
2045 float availableLogicalWidth = logicalRightOffsetForLine(curr->lineTop(), false) - logicalLeft;
2046 float totalLogicalWidth = curr->logicalWidth();
2047 updateLogicalWidthForAlignment(textAlign, 0, logicalLeft, totalLogicalWidth, availableLogicalWidth, 0);
2050 curr->adjustLogicalPosition((logicalLeft - curr->logicalLeft()), 0);
2052 curr->adjustLogicalPosition(-(curr->logicalLeft() - logicalLeft), 0);
2058 void RenderBlockFlow::checkLinesForTextOverflow()
2060 // Determine the width of the ellipsis using the current font.
2061 // FIXME: CSS3 says this is configurable, also need to use 0x002E (FULL STOP) if horizontal ellipsis is "not renderable"
2062 const Font& font = style().font();
2063 DEFINE_STATIC_LOCAL(AtomicString, ellipsisStr, (&horizontalEllipsis, 1));
2064 const Font& firstLineFont = firstLineStyle().font();
2065 int firstLineEllipsisWidth = firstLineFont.width(constructTextRun(this, firstLineFont, &horizontalEllipsis, 1, firstLineStyle()));
2066 int ellipsisWidth = (font == firstLineFont) ? firstLineEllipsisWidth : font.width(constructTextRun(this, font, &horizontalEllipsis, 1, style()));
2068 // For LTR text truncation, we want to get the right edge of our padding box, and then we want to see
2069 // if the right edge of a line box exceeds that. For RTL, we use the left edge of the padding box and
2070 // check the left edge of the line box to see if it is less
2071 // Include the scrollbar for overflow blocks, which means we want to use "contentWidth()"
2072 bool ltr = style().isLeftToRightDirection();
2073 ETextAlign textAlign = style().textAlign();
2074 bool firstLine = true;
2075 for (RootInlineBox* curr = firstRootBox(); curr; curr = curr->nextRootBox()) {
2076 // FIXME: Use pixelSnappedLogicalRightOffsetForLine instead of snapping it ourselves once the column workaround in said method has been fixed.
2077 // https://bugs.webkit.org/show_bug.cgi?id=105461
2078 int blockRightEdge = snapSizeToPixel(logicalRightOffsetForLine(curr->lineTop(), firstLine), curr->x());
2079 int blockLeftEdge = pixelSnappedLogicalLeftOffsetForLine(curr->lineTop(), firstLine);
2080 int lineBoxEdge = ltr ? snapSizeToPixel(curr->x() + curr->logicalWidth(), curr->x()) : snapSizeToPixel(curr->x(), 0);
2081 if ((ltr && lineBoxEdge > blockRightEdge) || (!ltr && lineBoxEdge < blockLeftEdge)) {
2082 // This line spills out of our box in the appropriate direction. Now we need to see if the line
2083 // can be truncated. In order for truncation to be possible, the line must have sufficient space to
2084 // accommodate our truncation string, and no replaced elements (images, tables) can overlap the ellipsis
2087 LayoutUnit width = firstLine ? firstLineEllipsisWidth : ellipsisWidth;
2088 LayoutUnit blockEdge = ltr ? blockRightEdge : blockLeftEdge;
2089 if (curr->lineCanAccommodateEllipsis(ltr, blockEdge, lineBoxEdge, width)) {
2090 float totalLogicalWidth = curr->placeEllipsis(ellipsisStr, ltr, blockLeftEdge, blockRightEdge, width);
2092 float logicalLeft = 0; // We are only interested in the delta from the base position.
2093 float truncatedWidth = pixelSnappedLogicalRightOffsetForLine(curr->lineTop(), firstLine);
2094 updateLogicalWidthForAlignment(textAlign, 0, logicalLeft, totalLogicalWidth, truncatedWidth, 0);
2096 curr->adjustLogicalPosition(logicalLeft, 0);
2098 curr->adjustLogicalPosition(-(truncatedWidth - (logicalLeft + totalLogicalWidth)), 0);
2105 bool RenderBlockFlow::positionNewFloatOnLine(FloatingObject* newFloat, FloatingObject* lastFloatFromPreviousLine, LineInfo& lineInfo, LineWidth& width)
2107 if (!positionNewFloats())
2110 width.shrinkAvailableWidthForNewFloatIfNeeded(newFloat);
2112 // We only connect floats to lines for pagination purposes if the floats occur at the start of
2113 // the line and the previous line had a hard break (so this line is either the first in the block
2114 // or follows a <br>).
2115 if (!newFloat->paginationStrut() || !lineInfo.previousLineBrokeCleanly() || !lineInfo.isEmpty())
2118 const FloatingObjectSet& floatingObjectSet = m_floatingObjects->set();
2119 ASSERT(floatingObjectSet.last().get() == newFloat);
2121 LayoutUnit floatLogicalTop = logicalTopForFloat(newFloat);
2122 int paginationStrut = newFloat->paginationStrut();
2124 if (floatLogicalTop - paginationStrut != logicalHeight() + lineInfo.floatPaginationStrut())
2127 auto it = floatingObjectSet.end();
2128 --it; // Last float is newFloat, skip that one.
2129 auto begin = floatingObjectSet.begin();
2130 while (it != begin) {
2132 FloatingObject* floatingObject = it->get();
2133 if (floatingObject == lastFloatFromPreviousLine)
2135 if (logicalTopForFloat(floatingObject) == logicalHeight() + lineInfo.floatPaginationStrut()) {
2136 floatingObject->setPaginationStrut(paginationStrut + floatingObject->paginationStrut());
2137 RenderBox& floatBox = floatingObject->renderer();
2138 setLogicalTopForChild(floatBox, logicalTopForChild(floatBox) + marginBeforeForChild(floatBox) + paginationStrut);
2140 if (updateRegionRangeForBoxChild(floatingObject->renderer()))
2141 floatBox.setNeedsLayout(MarkOnlyThis);
2142 else if (floatBox.isRenderBlock())
2143 toRenderBlock(floatBox).setChildNeedsLayout(MarkOnlyThis);
2144 floatBox.layoutIfNeeded();
2146 // Save the old logical top before calling removePlacedObject which will set
2147 // isPlaced to false. Otherwise it will trigger an assert in logicalTopForFloat.
2148 LayoutUnit oldLogicalTop = logicalTopForFloat(floatingObject);
2149 m_floatingObjects->removePlacedObject(floatingObject);
2150 setLogicalTopForFloat(floatingObject, oldLogicalTop + paginationStrut);
2151 m_floatingObjects->addPlacedObject(floatingObject);
2155 // Just update the line info's pagination strut without altering our logical height yet. If the line ends up containing
2156 // no content, then we don't want to improperly grow the height of the block.
2157 lineInfo.setFloatPaginationStrut(lineInfo.floatPaginationStrut() + paginationStrut);
2161 LayoutUnit RenderBlockFlow::startAlignedOffsetForLine(LayoutUnit position, bool firstLine)
2163 ETextAlign textAlign = style().textAlign();
2165 // <rdar://problem/15427571>
2166 // https://bugs.webkit.org/show_bug.cgi?id=124522
2167 // This quirk is for legacy content that doesn't work properly with the center positioning scheme
2168 // being honored (e.g., epubs).
2169 if (textAlign == TASTART || document().settings()->useLegacyTextAlignPositionedElementBehavior()) // FIXME: Handle TAEND here
2170 return startOffsetForLine(position, firstLine);
2172 // updateLogicalWidthForAlignment() handles the direction of the block so no need to consider it here
2173 float totalLogicalWidth = 0;
2174 float logicalLeft = logicalLeftOffsetForLine(logicalHeight(), false);
2175 float availableLogicalWidth = logicalRightOffsetForLine(logicalHeight(), false) - logicalLeft;
2176 updateLogicalWidthForAlignment(textAlign, 0, logicalLeft, totalLogicalWidth, availableLogicalWidth, 0);
2178 if (!style().isLeftToRightDirection())
2179 return logicalWidth() - logicalLeft;
2183 void RenderBlockFlow::updateRegionForLine(RootInlineBox* lineBox) const
2187 if (auto containingRegion = regionAtBlockOffset(lineBox->lineTopWithLeading()))
2188 lineBox->setContainingRegion(*containingRegion);
2190 lineBox->clearContainingRegion();
2192 RootInlineBox* prevLineBox = lineBox->prevRootBox();
2196 // This check is more accurate than the one in |adjustLinePositionForPagination| because it takes into
2197 // account just the container changes between lines. The before mentioned function doesn't set the flag
2198 // correctly if the line is positioned at the top of the last fragment container.
2199 if (lineBox->containingRegion() != prevLineBox->containingRegion())
2200 lineBox->setIsFirstAfterPageBreak(true);