1/* 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. 7 * 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. 12 * 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. 17 * 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. 22 * 23 */ 24 25#include "config.h" 26 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 "InlineTextBoxStyle.h" 35#include "LineLayoutState.h" 36#include "Logging.h" 37#include "RenderBlockFlow.h" 38#include "RenderFlowThread.h" 39#include "RenderLineBreak.h" 40#include "RenderRegion.h" 41#include "RenderView.h" 42#include "SVGRootInlineBox.h" 43#include "Settings.h" 44#include "SimpleLineLayoutFunctions.h" 45#include "TrailingFloatsRootInlineBox.h" 46#include "VerticalPositionCache.h" 47#include <wtf/RefCountedLeakCounter.h> 48#include <wtf/StdLibExtras.h> 49 50namespace WebCore { 51 52static void determineDirectionality(TextDirection& dir, InlineIterator iter) 53{ 54 while (!iter.atEnd()) { 55 if (iter.atParagraphSeparator()) 56 return; 57 if (UChar current = iter.current()) { 58 UCharDirection charDirection = u_charDirection(current); 59 if (charDirection == U_LEFT_TO_RIGHT) { 60 dir = LTR; 61 return; 62 } 63 if (charDirection == U_RIGHT_TO_LEFT || charDirection == U_RIGHT_TO_LEFT_ARABIC) { 64 dir = RTL; 65 return; 66 } 67 } 68 iter.increment(); 69 } 70} 71 72inline BidiRun* createRun(int start, int end, RenderObject* obj, InlineBidiResolver& resolver) 73{ 74 ASSERT(obj); 75 return new BidiRun(start, end, *obj, resolver.context(), resolver.dir()); 76} 77 78void RenderBlockFlow::appendRunsForObject(BidiRunList<BidiRun>& runs, int start, int end, RenderObject* obj, InlineBidiResolver& resolver) 79{ 80 if (start > end || shouldSkipCreatingRunsForObject(obj)) 81 return; 82 83 LineMidpointState& lineMidpointState = resolver.midpointState(); 84 bool haveNextMidpoint = (lineMidpointState.currentMidpoint() < lineMidpointState.numMidpoints()); 85 InlineIterator nextMidpoint; 86 if (haveNextMidpoint) 87 nextMidpoint = lineMidpointState.midpoints()[lineMidpointState.currentMidpoint()]; 88 if (lineMidpointState.betweenMidpoints()) { 89 if (!(haveNextMidpoint && nextMidpoint.renderer() == obj)) 90 return; 91 // This is a new start point. Stop ignoring objects and 92 // adjust our start. 93 lineMidpointState.setBetweenMidpoints(false); 94 start = nextMidpoint.offset(); 95 lineMidpointState.incrementCurrentMidpoint(); 96 if (start < end) 97 return appendRunsForObject(runs, start, end, obj, resolver); 98 } else { 99 if (!haveNextMidpoint || (obj != nextMidpoint.renderer())) { 100 runs.addRun(createRun(start, end, obj, resolver)); 101 return; 102 } 103 104 // An end midpoint has been encountered within our object. We 105 // need to go ahead and append a run with our endpoint. 106 if (static_cast<int>(nextMidpoint.offset() + 1) <= end) { 107 lineMidpointState.setBetweenMidpoints(true); 108 lineMidpointState.incrementCurrentMidpoint(); 109 // The end of the line is before the object we're inspecting. Skip everything and return 110 if (nextMidpoint.refersToEndOfPreviousNode()) 111 return; 112 if (static_cast<int>(nextMidpoint.offset() + 1) > start) 113 runs.addRun(createRun(start, nextMidpoint.offset() + 1, obj, resolver)); 114 appendRunsForObject(runs, nextMidpoint.offset() + 1, end, obj, resolver); 115 } else 116 runs.addRun(createRun(start, end, obj, resolver)); 117 } 118} 119 120std::unique_ptr<RootInlineBox> RenderBlockFlow::createRootInlineBox() 121{ 122 return std::make_unique<RootInlineBox>(*this); 123} 124 125RootInlineBox* RenderBlockFlow::createAndAppendRootInlineBox() 126{ 127 auto newRootBox = createRootInlineBox(); 128 RootInlineBox* rootBox = newRootBox.get(); 129 m_lineBoxes.appendLineBox(WTF::move(newRootBox)); 130 131 if (UNLIKELY(AXObjectCache::accessibilityEnabled()) && firstRootBox() == rootBox) { 132 if (AXObjectCache* cache = document().existingAXObjectCache()) 133 cache->recomputeIsIgnored(this); 134 } 135 136 return rootBox; 137} 138 139static inline InlineBox* createInlineBoxForRenderer(RenderObject* obj, bool isRootLineBox, bool isOnlyRun = false) 140{ 141 if (isRootLineBox) 142 return toRenderBlockFlow(obj)->createAndAppendRootInlineBox(); 143 144 if (obj->isText()) 145 return toRenderText(obj)->createInlineTextBox(); 146 147 if (obj->isBox()) { 148 // FIXME: This is terrible. This branch returns an *owned* pointer! 149 return toRenderBox(obj)->createInlineBox().release(); 150 } 151 152 if (obj->isLineBreak()) { 153 // FIXME: This is terrible. This branch returns an *owned* pointer! 154 auto inlineBox = toRenderLineBreak(obj)->createInlineBox().release(); 155 // We only treat a box as text for a <br> if we are on a line by ourself or in strict mode 156 // (Note the use of strict mode. In "almost strict" mode, we don't treat the box for <br> as text.) 157 inlineBox->setBehavesLikeText(isOnlyRun || obj->document().inNoQuirksMode() || obj->isLineBreakOpportunity()); 158 return inlineBox; 159 } 160 161 return toRenderInline(obj)->createAndAppendInlineFlowBox(); 162} 163 164static inline void dirtyLineBoxesForRenderer(RenderObject& renderer, bool fullLayout) 165{ 166 if (renderer.isText()) { 167 RenderText& renderText = toRenderText(renderer); 168 updateCounterIfNeeded(renderText); 169 renderText.dirtyLineBoxes(fullLayout); 170 } else if (renderer.isLineBreak()) 171 toRenderLineBreak(renderer).dirtyLineBoxes(fullLayout); 172 else 173 toRenderInline(renderer).dirtyLineBoxes(fullLayout); 174} 175 176static bool parentIsConstructedOrHaveNext(InlineFlowBox* parentBox) 177{ 178 do { 179 if (parentBox->isConstructed() || parentBox->nextOnLine()) 180 return true; 181 parentBox = parentBox->parent(); 182 } while (parentBox); 183 return false; 184} 185 186InlineFlowBox* RenderBlockFlow::createLineBoxes(RenderObject* obj, const LineInfo& lineInfo, InlineBox* childBox) 187{ 188 // See if we have an unconstructed line box for this object that is also 189 // the last item on the line. 190 unsigned lineDepth = 1; 191 InlineFlowBox* parentBox = 0; 192 InlineFlowBox* result = 0; 193 bool hasDefaultLineBoxContain = style().lineBoxContain() == RenderStyle::initialLineBoxContain(); 194 do { 195 ASSERT_WITH_SECURITY_IMPLICATION(obj->isRenderInline() || obj == this); 196 197 RenderInline* inlineFlow = (obj != this) ? toRenderInline(obj) : 0; 198 199 // Get the last box we made for this render object. 200 parentBox = inlineFlow ? inlineFlow->lastLineBox() : toRenderBlockFlow(obj)->lastRootBox(); 201 202 // If this box or its ancestor is constructed then it is from a previous line, and we need 203 // to make a new box for our line. If this box or its ancestor is unconstructed but it has 204 // something following it on the line, then we know we have to make a new box 205 // as well. In this situation our inline has actually been split in two on 206 // the same line (this can happen with very fancy language mixtures). 207 bool constructedNewBox = false; 208 bool allowedToConstructNewBox = !hasDefaultLineBoxContain || !inlineFlow || inlineFlow->alwaysCreateLineBoxes(); 209 bool canUseExistingParentBox = parentBox && !parentIsConstructedOrHaveNext(parentBox); 210 if (allowedToConstructNewBox && !canUseExistingParentBox) { 211 // We need to make a new box for this render object. Once 212 // made, we need to place it at the end of the current line. 213 InlineBox* newBox = createInlineBoxForRenderer(obj, obj == this); 214 ASSERT_WITH_SECURITY_IMPLICATION(newBox->isInlineFlowBox()); 215 parentBox = toInlineFlowBox(newBox); 216 parentBox->setIsFirstLine(lineInfo.isFirstLine()); 217 parentBox->setIsHorizontal(isHorizontalWritingMode()); 218 if (!hasDefaultLineBoxContain) 219 parentBox->clearDescendantsHaveSameLineHeightAndBaseline(); 220 constructedNewBox = true; 221 } 222 223 if (constructedNewBox || canUseExistingParentBox) { 224 if (!result) 225 result = parentBox; 226 227 // If we have hit the block itself, then |box| represents the root 228 // inline box for the line, and it doesn't have to be appended to any parent 229 // inline. 230 if (childBox) 231 parentBox->addToLine(childBox); 232 233 if (!constructedNewBox || obj == this) 234 break; 235 236 childBox = parentBox; 237 } 238 239 // If we've exceeded our line depth, then jump straight to the root and skip all the remaining 240 // intermediate inline flows. 241 obj = (++lineDepth >= cMaxLineDepth) ? this : obj->parent(); 242 243 } while (true); 244 245 return result; 246} 247 248template <typename CharacterType> 249static inline bool endsWithASCIISpaces(const CharacterType* characters, unsigned pos, unsigned end) 250{ 251 while (isASCIISpace(characters[pos])) { 252 pos++; 253 if (pos >= end) 254 return true; 255 } 256 return false; 257} 258 259static bool reachedEndOfTextRenderer(const BidiRunList<BidiRun>& bidiRuns) 260{ 261 BidiRun* run = bidiRuns.logicallyLastRun(); 262 if (!run) 263 return true; 264 unsigned pos = run->stop(); 265 const RenderObject& r = run->renderer(); 266 if (!r.isText()) 267 return false; 268 const RenderText& renderText = toRenderText(r); 269 unsigned length = renderText.textLength(); 270 if (pos >= length) 271 return true; 272 273 if (renderText.is8Bit()) 274 return endsWithASCIISpaces(renderText.characters8(), pos, length); 275 return endsWithASCIISpaces(renderText.characters16(), pos, length); 276} 277 278RootInlineBox* RenderBlockFlow::constructLine(BidiRunList<BidiRun>& bidiRuns, const LineInfo& lineInfo) 279{ 280 ASSERT(bidiRuns.firstRun()); 281 282 bool rootHasSelectedChildren = false; 283 InlineFlowBox* parentBox = 0; 284 int runCount = bidiRuns.runCount() - lineInfo.runsFromLeadingWhitespace(); 285 for (BidiRun* r = bidiRuns.firstRun(); r; r = r->next()) { 286 // Create a box for our object. 287 bool isOnlyRun = (runCount == 1); 288 if (runCount == 2 && !r->renderer().isListMarker()) 289 isOnlyRun = (!style().isLeftToRightDirection() ? bidiRuns.lastRun() : bidiRuns.firstRun())->renderer().isListMarker(); 290 291 if (lineInfo.isEmpty()) 292 continue; 293 294 InlineBox* box = createInlineBoxForRenderer(&r->renderer(), false, isOnlyRun); 295 r->setBox(*box); 296 297 if (!rootHasSelectedChildren && box->renderer().selectionState() != RenderObject::SelectionNone) 298 rootHasSelectedChildren = true; 299 300 // If we have no parent box yet, or if the run is not simply a sibling, 301 // then we need to construct inline boxes as necessary to properly enclose the 302 // run's inline box. Segments can only be siblings at the root level, as 303 // they are positioned separately. 304 if (!parentBox || &parentBox->renderer() != r->renderer().parent()) { 305 // Create new inline boxes all the way back to the appropriate insertion point. 306 parentBox = createLineBoxes(r->renderer().parent(), lineInfo, box); 307 } else { 308 // Append the inline box to this line. 309 parentBox->addToLine(box); 310 } 311 312 bool visuallyOrdered = r->renderer().style().rtlOrdering() == VisualOrder; 313 box->setBidiLevel(r->level()); 314 315 if (box->isInlineTextBox()) { 316 InlineTextBox* text = toInlineTextBox(box); 317 text->setStart(r->m_start); 318 text->setLen(r->m_stop - r->m_start); 319 text->setDirOverride(r->dirOverride(visuallyOrdered)); 320 if (r->m_hasHyphen) 321 text->setHasHyphen(true); 322 } 323 } 324 325 // We should have a root inline box. It should be unconstructed and 326 // be the last continuation of our line list. 327 ASSERT(lastRootBox() && !lastRootBox()->isConstructed()); 328 329 // Set the m_selectedChildren flag on the root inline box if one of the leaf inline box 330 // from the bidi runs walk above has a selection state. 331 if (rootHasSelectedChildren) 332 lastRootBox()->root().setHasSelectedChildren(true); 333 334 // Set bits on our inline flow boxes that indicate which sides should 335 // paint borders/margins/padding. This knowledge will ultimately be used when 336 // we determine the horizontal positions and widths of all the inline boxes on 337 // the line. 338 bool isLogicallyLastRunWrapped = bidiRuns.logicallyLastRun()->renderer().isText() ? !reachedEndOfTextRenderer(bidiRuns) : true; 339 lastRootBox()->determineSpacingForFlowBoxes(lineInfo.isLastLine(), isLogicallyLastRunWrapped, &bidiRuns.logicallyLastRun()->renderer()); 340 341 // Now mark the line boxes as being constructed. 342 lastRootBox()->setConstructed(); 343 344 // Return the last line. 345 return lastRootBox(); 346} 347 348ETextAlign RenderBlockFlow::textAlignmentForLine(bool endsWithSoftBreak) const 349{ 350 ETextAlign alignment = style().textAlign(); 351 if (endsWithSoftBreak) 352 return alignment; 353 354#if !ENABLE(CSS3_TEXT) 355 return (alignment == JUSTIFY) ? TASTART : alignment; 356#else 357 if (alignment != JUSTIFY) 358 return alignment; 359 360 TextAlignLast alignmentLast = style().textAlignLast(); 361 switch (alignmentLast) { 362 case TextAlignLastStart: 363 return TASTART; 364 case TextAlignLastEnd: 365 return TAEND; 366 case TextAlignLastLeft: 367 return LEFT; 368 case TextAlignLastRight: 369 return RIGHT; 370 case TextAlignLastCenter: 371 return CENTER; 372 case TextAlignLastJustify: 373 return JUSTIFY; 374 case TextAlignLastAuto: 375 if (style().textJustify() == TextJustifyDistribute) 376 return JUSTIFY; 377 return TASTART; 378 } 379 return alignment; 380#endif 381} 382 383static void updateLogicalWidthForLeftAlignedBlock(bool isLeftToRightDirection, BidiRun* trailingSpaceRun, float& logicalLeft, float& totalLogicalWidth, float availableLogicalWidth) 384{ 385 // The direction of the block should determine what happens with wide lines. 386 // In particular with RTL blocks, wide lines should still spill out to the left. 387 if (isLeftToRightDirection) { 388 if (totalLogicalWidth > availableLogicalWidth && trailingSpaceRun) 389 trailingSpaceRun->box()->setLogicalWidth(std::max<float>(0, trailingSpaceRun->box()->logicalWidth() - totalLogicalWidth + availableLogicalWidth)); 390 return; 391 } 392 393 if (trailingSpaceRun) 394 trailingSpaceRun->box()->setLogicalWidth(0); 395 else if (totalLogicalWidth > availableLogicalWidth) 396 logicalLeft -= (totalLogicalWidth - availableLogicalWidth); 397} 398 399static void updateLogicalWidthForRightAlignedBlock(bool isLeftToRightDirection, BidiRun* trailingSpaceRun, float& logicalLeft, float& totalLogicalWidth, float availableLogicalWidth) 400{ 401 // Wide lines spill out of the block based off direction. 402 // So even if text-align is right, if direction is LTR, wide lines should overflow out of the right 403 // side of the block. 404 if (isLeftToRightDirection) { 405 if (trailingSpaceRun) { 406 totalLogicalWidth -= trailingSpaceRun->box()->logicalWidth(); 407 trailingSpaceRun->box()->setLogicalWidth(0); 408 } 409 if (totalLogicalWidth < availableLogicalWidth) 410 logicalLeft += availableLogicalWidth - totalLogicalWidth; 411 return; 412 } 413 414 if (totalLogicalWidth > availableLogicalWidth && trailingSpaceRun) { 415 trailingSpaceRun->box()->setLogicalWidth(std::max<float>(0, trailingSpaceRun->box()->logicalWidth() - totalLogicalWidth + availableLogicalWidth)); 416 totalLogicalWidth -= trailingSpaceRun->box()->logicalWidth(); 417 } else 418 logicalLeft += availableLogicalWidth - totalLogicalWidth; 419} 420 421static void updateLogicalWidthForCenterAlignedBlock(bool isLeftToRightDirection, BidiRun* trailingSpaceRun, float& logicalLeft, float& totalLogicalWidth, float availableLogicalWidth) 422{ 423 float trailingSpaceWidth = 0; 424 if (trailingSpaceRun) { 425 totalLogicalWidth -= trailingSpaceRun->box()->logicalWidth(); 426 trailingSpaceWidth = std::min(trailingSpaceRun->box()->logicalWidth(), (availableLogicalWidth - totalLogicalWidth + 1) / 2); 427 trailingSpaceRun->box()->setLogicalWidth(std::max<float>(0, trailingSpaceWidth)); 428 } 429 if (isLeftToRightDirection) 430 logicalLeft += std::max<float>((availableLogicalWidth - totalLogicalWidth) / 2, 0); 431 else 432 logicalLeft += totalLogicalWidth > availableLogicalWidth ? (availableLogicalWidth - totalLogicalWidth) : (availableLogicalWidth - totalLogicalWidth) / 2 - trailingSpaceWidth; 433} 434 435void RenderBlockFlow::setMarginsForRubyRun(BidiRun* run, RenderRubyRun& renderer, RenderObject* previousObject, const LineInfo& lineInfo) 436{ 437 int startOverhang; 438 int endOverhang; 439 RenderObject* nextObject = 0; 440 for (BidiRun* runWithNextObject = run->next(); runWithNextObject; runWithNextObject = runWithNextObject->next()) { 441 if (!runWithNextObject->renderer().isOutOfFlowPositioned() && !runWithNextObject->box()->isLineBreak()) { 442 nextObject = &runWithNextObject->renderer(); 443 break; 444 } 445 } 446 renderer.getOverhang(lineInfo.isFirstLine(), renderer.style().isLeftToRightDirection() ? previousObject : nextObject, renderer.style().isLeftToRightDirection() ? nextObject : previousObject, startOverhang, endOverhang); 447 setMarginStartForChild(renderer, -startOverhang); 448 setMarginEndForChild(renderer, -endOverhang); 449} 450 451static inline void setLogicalWidthForTextRun(RootInlineBox* lineBox, BidiRun* run, RenderText* renderer, float xPos, const LineInfo& lineInfo, 452 GlyphOverflowAndFallbackFontsMap& textBoxDataMap, VerticalPositionCache& verticalPositionCache, WordMeasurements& wordMeasurements) 453{ 454 HashSet<const SimpleFontData*> fallbackFonts; 455 GlyphOverflow glyphOverflow; 456 457 const Font& font = lineStyle(*renderer->parent(), lineInfo).font(); 458 // Always compute glyph overflow if the block's line-box-contain value is "glyphs". 459 if (lineBox->fitsToGlyphs()) { 460 // If we don't stick out of the root line's font box, then don't bother computing our glyph overflow. This optimization 461 // will keep us from computing glyph bounds in nearly all cases. 462 bool includeRootLine = lineBox->includesRootLineBoxFontOrLeading(); 463 int baselineShift = lineBox->verticalPositionForBox(run->box(), verticalPositionCache); 464 int rootDescent = includeRootLine ? font.fontMetrics().descent() : 0; 465 int rootAscent = includeRootLine ? font.fontMetrics().ascent() : 0; 466 int boxAscent = font.fontMetrics().ascent() - baselineShift; 467 int boxDescent = font.fontMetrics().descent() + baselineShift; 468 if (boxAscent > rootDescent || boxDescent > rootAscent) 469 glyphOverflow.computeBounds = true; 470 } 471 472 LayoutUnit hyphenWidth = 0; 473 if (toInlineTextBox(run->box())->hasHyphen()) 474 hyphenWidth = measureHyphenWidth(renderer, font, &fallbackFonts); 475 476 float measuredWidth = 0; 477 478 bool kerningIsEnabled = font.typesettingFeatures() & Kerning; 479 bool canUseSimpleFontCodePath = renderer->canUseSimpleFontCodePath(); 480 481 // Since we don't cache glyph overflows, we need to re-measure the run if 482 // the style is linebox-contain: glyph. 483 484 if (!lineBox->fitsToGlyphs() && canUseSimpleFontCodePath) { 485 int lastEndOffset = run->m_start; 486 for (size_t i = 0, size = wordMeasurements.size(); i < size && lastEndOffset < run->m_stop; ++i) { 487 WordMeasurement& wordMeasurement = wordMeasurements[i]; 488 if (wordMeasurement.width <= 0 || wordMeasurement.startOffset == wordMeasurement.endOffset) 489 continue; 490 if (wordMeasurement.renderer != renderer || wordMeasurement.startOffset != lastEndOffset || wordMeasurement.endOffset > run->m_stop) 491 continue; 492 493 lastEndOffset = wordMeasurement.endOffset; 494 if (kerningIsEnabled && lastEndOffset == run->m_stop) { 495 int wordLength = lastEndOffset - wordMeasurement.startOffset; 496 GlyphOverflow overflow; 497 measuredWidth += renderer->width(wordMeasurement.startOffset, wordLength, xPos + measuredWidth, lineInfo.isFirstLine(), 498 &wordMeasurement.fallbackFonts, &overflow); 499 UChar c = renderer->characterAt(wordMeasurement.startOffset); 500 if (i > 0 && wordLength == 1 && (c == ' ' || c == '\t')) 501 measuredWidth += renderer->style().font().wordSpacing(); 502 } else 503 measuredWidth += wordMeasurement.width; 504 if (!wordMeasurement.fallbackFonts.isEmpty()) { 505 HashSet<const SimpleFontData*>::const_iterator end = wordMeasurement.fallbackFonts.end(); 506 for (HashSet<const SimpleFontData*>::const_iterator it = wordMeasurement.fallbackFonts.begin(); it != end; ++it) 507 fallbackFonts.add(*it); 508 } 509 } 510 if (measuredWidth && lastEndOffset != run->m_stop) { 511 // If we don't have enough cached data, we'll measure the run again. 512 measuredWidth = 0; 513 fallbackFonts.clear(); 514 } 515 } 516 517 if (!measuredWidth) 518 measuredWidth = renderer->width(run->m_start, run->m_stop - run->m_start, xPos, lineInfo.isFirstLine(), &fallbackFonts, &glyphOverflow); 519 520 run->box()->setLogicalWidth(measuredWidth + hyphenWidth); 521 if (!fallbackFonts.isEmpty()) { 522 ASSERT(run->box()->behavesLikeText()); 523 GlyphOverflowAndFallbackFontsMap::iterator it = textBoxDataMap.add(toInlineTextBox(run->box()), std::make_pair(Vector<const SimpleFontData*>(), GlyphOverflow())).iterator; 524 ASSERT(it->value.first.isEmpty()); 525 copyToVector(fallbackFonts, it->value.first); 526 run->box()->parent()->clearDescendantsHaveSameLineHeightAndBaseline(); 527 } 528 529 // Include text decoration visual overflow as part of the glyph overflow. 530 if (renderer->style().textDecorationsInEffect() != TextDecorationNone) 531 glyphOverflow.extendTo(visualOverflowForDecorations(run->box()->lineStyle(), toInlineTextBox(run->box()))); 532 533 if (!glyphOverflow.isEmpty()) { 534 ASSERT(run->box()->behavesLikeText()); 535 GlyphOverflowAndFallbackFontsMap::iterator it = textBoxDataMap.add(toInlineTextBox(run->box()), std::make_pair(Vector<const SimpleFontData*>(), GlyphOverflow())).iterator; 536 it->value.second = glyphOverflow; 537 run->box()->clearKnownToHaveNoOverflow(); 538 } 539} 540 541static inline void computeExpansionForJustifiedText(BidiRun* firstRun, BidiRun* trailingSpaceRun, Vector<unsigned, 16>& expansionOpportunities, unsigned expansionOpportunityCount, float& totalLogicalWidth, float availableLogicalWidth) 542{ 543 if (!expansionOpportunityCount || availableLogicalWidth <= totalLogicalWidth) 544 return; 545 546 size_t i = 0; 547 for (BidiRun* r = firstRun; r; r = r->next()) { 548 if (!r->box() || r == trailingSpaceRun) 549 continue; 550 551 if (r->renderer().isText()) { 552 unsigned opportunitiesInRun = expansionOpportunities[i++]; 553 554 ASSERT(opportunitiesInRun <= expansionOpportunityCount); 555 556 // Only justify text if whitespace is collapsed. 557 if (r->renderer().style().collapseWhiteSpace()) { 558 InlineTextBox* textBox = toInlineTextBox(r->box()); 559 int expansion = (availableLogicalWidth - totalLogicalWidth) * opportunitiesInRun / expansionOpportunityCount; 560 textBox->setExpansion(expansion); 561 totalLogicalWidth += expansion; 562 } 563 expansionOpportunityCount -= opportunitiesInRun; 564 if (!expansionOpportunityCount) 565 break; 566 } 567 } 568} 569 570void RenderBlockFlow::updateLogicalWidthForAlignment(const ETextAlign& textAlign, const RootInlineBox* rootInlineBox, BidiRun* trailingSpaceRun, float& logicalLeft, float& totalLogicalWidth, float& availableLogicalWidth, int expansionOpportunityCount) 571{ 572 TextDirection direction; 573 if (rootInlineBox && style().unicodeBidi() == Plaintext) 574 direction = rootInlineBox->direction(); 575 else 576 direction = style().direction(); 577 578 // Armed with the total width of the line (without justification), 579 // we now examine our text-align property in order to determine where to position the 580 // objects horizontally. The total width of the line can be increased if we end up 581 // justifying text. 582 switch (textAlign) { 583 case LEFT: 584 case WEBKIT_LEFT: 585 updateLogicalWidthForLeftAlignedBlock(style().isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth); 586 break; 587 case RIGHT: 588 case WEBKIT_RIGHT: 589 updateLogicalWidthForRightAlignedBlock(style().isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth); 590 break; 591 case CENTER: 592 case WEBKIT_CENTER: 593 updateLogicalWidthForCenterAlignedBlock(style().isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth); 594 break; 595 case JUSTIFY: 596 adjustInlineDirectionLineBounds(expansionOpportunityCount, logicalLeft, availableLogicalWidth); 597 if (expansionOpportunityCount) { 598 if (trailingSpaceRun) { 599 totalLogicalWidth -= trailingSpaceRun->box()->logicalWidth(); 600 trailingSpaceRun->box()->setLogicalWidth(0); 601 } 602 break; 603 } 604 FALLTHROUGH; 605 case TASTART: 606 if (direction == LTR) 607 updateLogicalWidthForLeftAlignedBlock(style().isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth); 608 else 609 updateLogicalWidthForRightAlignedBlock(style().isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth); 610 break; 611 case TAEND: 612 if (direction == LTR) 613 updateLogicalWidthForRightAlignedBlock(style().isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth); 614 else 615 updateLogicalWidthForLeftAlignedBlock(style().isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth); 616 break; 617 } 618} 619 620static void updateLogicalInlinePositions(RenderBlockFlow& block, float& lineLogicalLeft, float& lineLogicalRight, float& availableLogicalWidth, bool firstLine, IndentTextOrNot shouldIndentText, LayoutUnit boxLogicalHeight) 621{ 622 LayoutUnit lineLogicalHeight = block.minLineHeightForReplacedRenderer(firstLine, boxLogicalHeight); 623 lineLogicalLeft = block.logicalLeftOffsetForLine(block.logicalHeight(), shouldIndentText == IndentText, lineLogicalHeight); 624 lineLogicalRight = block.logicalRightOffsetForLine(block.logicalHeight(), shouldIndentText == IndentText, lineLogicalHeight); 625 availableLogicalWidth = lineLogicalRight - lineLogicalLeft; 626} 627 628void RenderBlockFlow::computeInlineDirectionPositionsForLine(RootInlineBox* lineBox, const LineInfo& lineInfo, BidiRun* firstRun, BidiRun* trailingSpaceRun, bool reachedEnd, GlyphOverflowAndFallbackFontsMap& textBoxDataMap, VerticalPositionCache& verticalPositionCache, WordMeasurements& wordMeasurements) 629{ 630 ETextAlign textAlign = textAlignmentForLine(!reachedEnd && !lineBox->endsWithBreak()); 631 632 // 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 633 // box is only affected if it is the first child of its parent element." 634 // CSS3 "text-indent", "-webkit-each-line" affects the first line of the block container as well as each line after a forced line break, 635 // but does not affect lines after a soft wrap break. 636 bool isFirstLine = lineInfo.isFirstLine() && !(isAnonymousBlock() && parent()->firstChild() != this); 637 bool isAfterHardLineBreak = lineBox->prevRootBox() && lineBox->prevRootBox()->endsWithBreak(); 638 IndentTextOrNot shouldIndentText = requiresIndent(isFirstLine, isAfterHardLineBreak, style()); 639 float lineLogicalLeft; 640 float lineLogicalRight; 641 float availableLogicalWidth; 642 updateLogicalInlinePositions(*this, lineLogicalLeft, lineLogicalRight, availableLogicalWidth, isFirstLine, shouldIndentText, 0); 643 bool needsWordSpacing; 644 645 if (firstRun && firstRun->renderer().isReplaced()) { 646 RenderBox& renderBox = toRenderBox(firstRun->renderer()); 647 updateLogicalInlinePositions(*this, lineLogicalLeft, lineLogicalRight, availableLogicalWidth, isFirstLine, shouldIndentText, renderBox.logicalHeight()); 648 } 649 650 computeInlineDirectionPositionsForSegment(lineBox, lineInfo, textAlign, lineLogicalLeft, availableLogicalWidth, firstRun, trailingSpaceRun, textBoxDataMap, verticalPositionCache, wordMeasurements); 651 // The widths of all runs are now known. We can now place every inline box (and 652 // compute accurate widths for the inline flow boxes). 653 needsWordSpacing = false; 654 lineBox->placeBoxesInInlineDirection(lineLogicalLeft, needsWordSpacing); 655} 656 657BidiRun* RenderBlockFlow::computeInlineDirectionPositionsForSegment(RootInlineBox* lineBox, const LineInfo& lineInfo, ETextAlign textAlign, float& logicalLeft, 658 float& availableLogicalWidth, BidiRun* firstRun, BidiRun* trailingSpaceRun, GlyphOverflowAndFallbackFontsMap& textBoxDataMap, VerticalPositionCache& verticalPositionCache, 659 WordMeasurements& wordMeasurements) 660{ 661 bool needsWordSpacing = false; 662 float totalLogicalWidth = lineBox->getFlowSpacingLogicalWidth(); 663 unsigned expansionOpportunityCount = 0; 664 bool isAfterExpansion = true; 665 Vector<unsigned, 16> expansionOpportunities; 666 RenderObject* previousObject = 0; 667 668 BidiRun* r = firstRun; 669 for (; r; r = r->next()) { 670 if (!r->box() || r->renderer().isOutOfFlowPositioned() || r->box()->isLineBreak()) 671 continue; // Positioned objects are only participating to figure out their 672 // correct static x position. They have no effect on the width. 673 // Similarly, line break boxes have no effect on the width. 674 if (r->renderer().isText()) { 675 RenderText& rt = toRenderText(r->renderer()); 676 if (textAlign == JUSTIFY && r != trailingSpaceRun) { 677 if (!isAfterExpansion) 678 toInlineTextBox(r->box())->setCanHaveLeadingExpansion(true); 679 unsigned opportunitiesInRun; 680 if (rt.is8Bit()) 681 opportunitiesInRun = Font::expansionOpportunityCount(rt.characters8() + r->m_start, r->m_stop - r->m_start, r->box()->direction(), isAfterExpansion); 682 else 683 opportunitiesInRun = Font::expansionOpportunityCount(rt.characters16() + r->m_start, r->m_stop - r->m_start, r->box()->direction(), isAfterExpansion); 684 expansionOpportunities.append(opportunitiesInRun); 685 expansionOpportunityCount += opportunitiesInRun; 686 } 687 688 if (int length = rt.textLength()) { 689 if (!r->m_start && needsWordSpacing && isSpaceOrNewline(rt.characterAt(r->m_start))) 690 totalLogicalWidth += lineStyle(*rt.parent(), lineInfo).font().wordSpacing(); 691 needsWordSpacing = !isSpaceOrNewline(rt.characterAt(r->m_stop - 1)) && r->m_stop == length; 692 } 693 694 setLogicalWidthForTextRun(lineBox, r, &rt, totalLogicalWidth, lineInfo, textBoxDataMap, verticalPositionCache, wordMeasurements); 695 } else { 696 isAfterExpansion = false; 697 if (!r->renderer().isRenderInline()) { 698 RenderBox& renderBox = toRenderBox(r->renderer()); 699 if (renderBox.isRubyRun()) 700 setMarginsForRubyRun(r, toRenderRubyRun(renderBox), previousObject, lineInfo); 701 r->box()->setLogicalWidth(logicalWidthForChild(renderBox)); 702 totalLogicalWidth += marginStartForChild(renderBox) + marginEndForChild(renderBox); 703 } 704 } 705 706 totalLogicalWidth += r->box()->logicalWidth(); 707 previousObject = &r->renderer(); 708 } 709 710 if (isAfterExpansion && !expansionOpportunities.isEmpty()) { 711 expansionOpportunities.last()--; 712 expansionOpportunityCount--; 713 } 714 715 updateLogicalWidthForAlignment(textAlign, lineBox, trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth, expansionOpportunityCount); 716 717 computeExpansionForJustifiedText(firstRun, trailingSpaceRun, expansionOpportunities, expansionOpportunityCount, totalLogicalWidth, availableLogicalWidth); 718 719 return r; 720} 721 722void RenderBlockFlow::computeBlockDirectionPositionsForLine(RootInlineBox* lineBox, BidiRun* firstRun, GlyphOverflowAndFallbackFontsMap& textBoxDataMap, 723 VerticalPositionCache& verticalPositionCache) 724{ 725 setLogicalHeight(lineBox->alignBoxesInBlockDirection(logicalHeight(), textBoxDataMap, verticalPositionCache)); 726 727 // Now make sure we place replaced render objects correctly. 728 for (BidiRun* r = firstRun; r; r = r->next()) { 729 ASSERT(r->box()); 730 if (!r->box()) 731 continue; // Skip runs with no line boxes. 732 733 InlineBox& box = *r->box(); 734 735 // Align positioned boxes with the top of the line box. This is 736 // a reasonable approximation of an appropriate y position. 737 if (r->renderer().isOutOfFlowPositioned()) 738 box.setLogicalTop(logicalHeight()); 739 740 // Position is used to properly position both replaced elements and 741 // to update the static normal flow x/y of positioned elements. 742 if (r->renderer().isText()) 743 toRenderText(r->renderer()).positionLineBox(toInlineTextBox(box)); 744 else if (r->renderer().isBox()) 745 toRenderBox(r->renderer()).positionLineBox(toInlineElementBox(box)); 746 else if (r->renderer().isLineBreak()) 747 toRenderLineBreak(r->renderer()).replaceInlineBoxWrapper(toInlineElementBox(box)); 748 } 749 // Positioned objects and zero-length text nodes destroy their boxes in 750 // position(), which unnecessarily dirties the line. 751 lineBox->markDirty(false); 752} 753 754static inline bool isCollapsibleSpace(UChar character, const RenderText& renderer) 755{ 756 if (character == ' ' || character == '\t' || character == softHyphen) 757 return true; 758 if (character == '\n') 759 return !renderer.style().preserveNewline(); 760 if (character == noBreakSpace) 761 return renderer.style().nbspMode() == SPACE; 762 return false; 763} 764 765template <typename CharacterType> 766static inline int findFirstTrailingSpace(const RenderText& lastText, const CharacterType* characters, int start, int stop) 767{ 768 int firstSpace = stop; 769 while (firstSpace > start) { 770 UChar current = characters[firstSpace - 1]; 771 if (!isCollapsibleSpace(current, lastText)) 772 break; 773 firstSpace--; 774 } 775 776 return firstSpace; 777} 778 779inline BidiRun* RenderBlockFlow::handleTrailingSpaces(BidiRunList<BidiRun>& bidiRuns, BidiContext* currentContext) 780{ 781 if (!bidiRuns.runCount() 782 || !bidiRuns.logicallyLastRun()->renderer().style().breakOnlyAfterWhiteSpace() 783 || !bidiRuns.logicallyLastRun()->renderer().style().autoWrap()) 784 return 0; 785 786 BidiRun* trailingSpaceRun = bidiRuns.logicallyLastRun(); 787 const RenderObject& lastObject = trailingSpaceRun->renderer(); 788 if (!lastObject.isText()) 789 return 0; 790 791 const RenderText& lastText = toRenderText(lastObject); 792 int firstSpace; 793 if (lastText.is8Bit()) 794 firstSpace = findFirstTrailingSpace(lastText, lastText.characters8(), trailingSpaceRun->start(), trailingSpaceRun->stop()); 795 else 796 firstSpace = findFirstTrailingSpace(lastText, lastText.characters16(), trailingSpaceRun->start(), trailingSpaceRun->stop()); 797 798 if (firstSpace == trailingSpaceRun->stop()) 799 return 0; 800 801 TextDirection direction = style().direction(); 802 bool shouldReorder = trailingSpaceRun != (direction == LTR ? bidiRuns.lastRun() : bidiRuns.firstRun()); 803 if (firstSpace != trailingSpaceRun->start()) { 804 BidiContext* baseContext = currentContext; 805 while (BidiContext* parent = baseContext->parent()) 806 baseContext = parent; 807 808 BidiRun* newTrailingRun = new BidiRun(firstSpace, trailingSpaceRun->m_stop, trailingSpaceRun->renderer(), baseContext, U_OTHER_NEUTRAL); 809 trailingSpaceRun->m_stop = firstSpace; 810 if (direction == LTR) 811 bidiRuns.addRun(newTrailingRun); 812 else 813 bidiRuns.prependRun(newTrailingRun); 814 trailingSpaceRun = newTrailingRun; 815 return trailingSpaceRun; 816 } 817 if (!shouldReorder) 818 return trailingSpaceRun; 819 820 if (direction == LTR) { 821 bidiRuns.moveRunToEnd(trailingSpaceRun); 822 trailingSpaceRun->m_level = 0; 823 } else { 824 bidiRuns.moveRunToBeginning(trailingSpaceRun); 825 trailingSpaceRun->m_level = 1; 826 } 827 return trailingSpaceRun; 828} 829 830void RenderBlockFlow::appendFloatingObjectToLastLine(FloatingObject* floatingObject) 831{ 832 ASSERT(!floatingObject->originatingLine()); 833 floatingObject->setOriginatingLine(lastRootBox()); 834 lastRootBox()->appendFloat(floatingObject->renderer()); 835} 836 837static inline void setUpResolverToResumeInIsolate(InlineBidiResolver& resolver, RenderObject* root, RenderObject* startObject) 838{ 839 if (root != startObject) { 840 RenderObject* parent = startObject->parent(); 841 setUpResolverToResumeInIsolate(resolver, root, parent); 842 notifyObserverEnteredObject(&resolver, startObject); 843 } 844} 845 846// FIXME: BidiResolver should have this logic. 847static inline void constructBidiRunsForSegment(InlineBidiResolver& topResolver, BidiRunList<BidiRun>& bidiRuns, const InlineIterator& endOfRuns, VisualDirectionOverride override, bool previousLineBrokeCleanly) 848{ 849 // FIXME: We should pass a BidiRunList into createBidiRunsForLine instead 850 // of the resolver owning the runs. 851 ASSERT(&topResolver.runs() == &bidiRuns); 852 ASSERT(topResolver.position() != endOfRuns); 853 RenderObject* currentRoot = topResolver.position().root(); 854 topResolver.createBidiRunsForLine(endOfRuns, override, previousLineBrokeCleanly); 855 856 while (!topResolver.isolatedRuns().isEmpty()) { 857 // It does not matter which order we resolve the runs as long as we resolve them all. 858 BidiRun* isolatedRun = topResolver.isolatedRuns().last(); 859 topResolver.isolatedRuns().removeLast(); 860 861 RenderObject& startObject = isolatedRun->renderer(); 862 863 // Only inlines make sense with unicode-bidi: isolate (blocks are already isolated). 864 // FIXME: Because enterIsolate is not passed a RenderObject, we have to crawl up the 865 // tree to see which parent inline is the isolate. We could change enterIsolate 866 // to take a RenderObject and do this logic there, but that would be a layering 867 // violation for BidiResolver (which knows nothing about RenderObject). 868 RenderInline* isolatedInline = toRenderInline(highestContainingIsolateWithinRoot(startObject, currentRoot)); 869 ASSERT(isolatedInline); 870 871 InlineBidiResolver isolatedResolver; 872 EUnicodeBidi unicodeBidi = isolatedInline->style().unicodeBidi(); 873 TextDirection direction; 874 if (unicodeBidi == Plaintext) 875 determineDirectionality(direction, InlineIterator(isolatedInline, &isolatedRun->renderer(), 0)); 876 else { 877 ASSERT(unicodeBidi == Isolate || unicodeBidi == IsolateOverride); 878 direction = isolatedInline->style().direction(); 879 } 880 isolatedResolver.setStatus(BidiStatus(direction, isOverride(unicodeBidi))); 881 882 setUpResolverToResumeInIsolate(isolatedResolver, isolatedInline, &startObject); 883 884 // The starting position is the beginning of the first run within the isolate that was identified 885 // during the earlier call to createBidiRunsForLine. This can be but is not necessarily the 886 // first run within the isolate. 887 InlineIterator iter = InlineIterator(isolatedInline, &startObject, isolatedRun->m_start); 888 isolatedResolver.setPositionIgnoringNestedIsolates(iter); 889 890 // We stop at the next end of line; we may re-enter this isolate in the next call to constructBidiRuns(). 891 // FIXME: What should end and previousLineBrokeCleanly be? 892 // rniwa says previousLineBrokeCleanly is just a WinIE hack and could always be false here? 893 isolatedResolver.createBidiRunsForLine(endOfRuns, NoVisualOverride, previousLineBrokeCleanly); 894 // Note that we do not delete the runs from the resolver. 895 // We're not guaranteed to get any BidiRuns in the previous step. If we don't, we allow the placeholder 896 // itself to be turned into an InlineBox. We can't remove it here without potentially losing track of 897 // the logically last run. 898 if (isolatedResolver.runs().runCount()) 899 bidiRuns.replaceRunWithRuns(isolatedRun, isolatedResolver.runs()); 900 901 // If we encountered any nested isolate runs, just move them 902 // to the top resolver's list for later processing. 903 if (!isolatedResolver.isolatedRuns().isEmpty()) { 904 topResolver.isolatedRuns().appendVector(isolatedResolver.isolatedRuns()); 905 isolatedResolver.isolatedRuns().clear(); 906 currentRoot = isolatedInline; 907 } 908 } 909} 910 911// This function constructs line boxes for all of the text runs in the resolver and computes their position. 912RootInlineBox* RenderBlockFlow::createLineBoxesFromBidiRuns(unsigned bidiLevel, BidiRunList<BidiRun>& bidiRuns, const InlineIterator& end, LineInfo& lineInfo, VerticalPositionCache& verticalPositionCache, BidiRun* trailingSpaceRun, WordMeasurements& wordMeasurements) 913{ 914 if (!bidiRuns.runCount()) 915 return 0; 916 917 // FIXME: Why is this only done when we had runs? 918 lineInfo.setLastLine(!end.renderer()); 919 920 RootInlineBox* lineBox = constructLine(bidiRuns, lineInfo); 921 if (!lineBox) 922 return 0; 923 924 lineBox->setBidiLevel(bidiLevel); 925 lineBox->setEndsWithBreak(lineInfo.previousLineBrokeCleanly()); 926 927 bool isSVGRootInlineBox = lineBox->isSVGRootInlineBox(); 928 929 GlyphOverflowAndFallbackFontsMap textBoxDataMap; 930 931 // Now we position all of our text runs horizontally. 932 if (!isSVGRootInlineBox) 933 computeInlineDirectionPositionsForLine(lineBox, lineInfo, bidiRuns.firstRun(), trailingSpaceRun, end.atEnd(), textBoxDataMap, verticalPositionCache, wordMeasurements); 934 935 // Now position our text runs vertically. 936 computeBlockDirectionPositionsForLine(lineBox, bidiRuns.firstRun(), textBoxDataMap, verticalPositionCache); 937 938 // SVG text layout code computes vertical & horizontal positions on its own. 939 // Note that we still need to execute computeVerticalPositionsForLine() as 940 // it calls InlineTextBox::positionLineBox(), which tracks whether the box 941 // contains reversed text or not. If we wouldn't do that editing and thus 942 // text selection in RTL boxes would not work as expected. 943 if (isSVGRootInlineBox) { 944 ASSERT_WITH_SECURITY_IMPLICATION(isSVGText()); 945 toSVGRootInlineBox(lineBox)->computePerCharacterLayoutInformation(); 946 } 947 948 // Compute our overflow now. 949 lineBox->computeOverflow(lineBox->lineTop(), lineBox->lineBottom(), textBoxDataMap); 950 951 return lineBox; 952} 953 954static void deleteLineRange(LineLayoutState& layoutState, RootInlineBox* startLine, RootInlineBox* stopLine = 0) 955{ 956 RootInlineBox* boxToDelete = startLine; 957 while (boxToDelete && boxToDelete != stopLine) { 958 layoutState.updateRepaintRangeFromBox(boxToDelete); 959 // Note: deleteLineRange(firstRootBox()) is not identical to deleteLineBoxTree(). 960 // deleteLineBoxTree uses nextLineBox() instead of nextRootBox() when traversing. 961 RootInlineBox* next = boxToDelete->nextRootBox(); 962 boxToDelete->deleteLine(); 963 boxToDelete = next; 964 } 965} 966 967void RenderBlockFlow::layoutRunsAndFloats(LineLayoutState& layoutState, bool hasInlineChild) 968{ 969 // We want to skip ahead to the first dirty line 970 InlineBidiResolver resolver; 971 RootInlineBox* startLine = determineStartPosition(layoutState, resolver); 972 973 unsigned consecutiveHyphenatedLines = 0; 974 if (startLine) { 975 for (RootInlineBox* line = startLine->prevRootBox(); line && line->isHyphenated(); line = line->prevRootBox()) 976 consecutiveHyphenatedLines++; 977 } 978 979 // FIXME: This would make more sense outside of this function, but since 980 // determineStartPosition can change the fullLayout flag we have to do this here. Failure to call 981 // determineStartPosition first will break fast/repaint/line-flow-with-floats-9.html. 982 if (layoutState.isFullLayout() && hasInlineChild && !selfNeedsLayout()) { 983 setNeedsLayout(MarkOnlyThis); // Mark as needing a full layout to force us to repaint. 984 if (!view().doingFullRepaint() && hasLayer()) { 985 // Because we waited until we were already inside layout to discover 986 // that the block really needed a full layout, we missed our chance to repaint the layer 987 // before layout started. Luckily the layer has cached the repaint rect for its original 988 // position and size, and so we can use that to make a repaint happen now. 989 repaintUsingContainer(containerForRepaint(), layer()->repaintRect()); 990 } 991 } 992 993 if (containsFloats()) 994 layoutState.setLastFloat(m_floatingObjects->set().last().get()); 995 996 // We also find the first clean line and extract these lines. We will add them back 997 // if we determine that we're able to synchronize after handling all our dirty lines. 998 InlineIterator cleanLineStart; 999 BidiStatus cleanLineBidiStatus; 1000 if (!layoutState.isFullLayout() && startLine) 1001 determineEndPosition(layoutState, startLine, cleanLineStart, cleanLineBidiStatus); 1002 1003 if (startLine) { 1004 if (!layoutState.usesRepaintBounds()) 1005 layoutState.setRepaintRange(logicalHeight()); 1006 deleteLineRange(layoutState, startLine); 1007 } 1008 1009 if (!layoutState.isFullLayout() && lastRootBox() && lastRootBox()->endsWithBreak()) { 1010 // If the last line before the start line ends with a line break that clear floats, 1011 // adjust the height accordingly. 1012 // A line break can be either the first or the last object on a line, depending on its direction. 1013 if (InlineBox* lastLeafChild = lastRootBox()->lastLeafChild()) { 1014 RenderObject* lastObject = &lastLeafChild->renderer(); 1015 if (!lastObject->isBR()) 1016 lastObject = &lastRootBox()->firstLeafChild()->renderer(); 1017 if (lastObject->isBR()) { 1018 EClear clear = lastObject->style().clear(); 1019 if (clear != CNONE) 1020 clearFloats(clear); 1021 } 1022 } 1023 } 1024 1025 layoutRunsAndFloatsInRange(layoutState, resolver, cleanLineStart, cleanLineBidiStatus, consecutiveHyphenatedLines); 1026 linkToEndLineIfNeeded(layoutState); 1027 repaintDirtyFloats(layoutState.floats()); 1028} 1029 1030RenderTextInfo::RenderTextInfo() 1031 : m_text(0) 1032 , m_font(0) 1033{ 1034} 1035 1036RenderTextInfo::~RenderTextInfo() 1037{ 1038} 1039 1040// Before restarting the layout loop with a new logicalHeight, remove all floats that were added and reset the resolver. 1041inline const InlineIterator& RenderBlockFlow::restartLayoutRunsAndFloatsInRange(LayoutUnit oldLogicalHeight, LayoutUnit newLogicalHeight, FloatingObject* lastFloatFromPreviousLine, InlineBidiResolver& resolver, const InlineIterator& oldEnd) 1042{ 1043 removeFloatingObjectsBelow(lastFloatFromPreviousLine, oldLogicalHeight); 1044 setLogicalHeight(newLogicalHeight); 1045 resolver.setPositionIgnoringNestedIsolates(oldEnd); 1046 return oldEnd; 1047} 1048 1049void RenderBlockFlow::layoutRunsAndFloatsInRange(LineLayoutState& layoutState, InlineBidiResolver& resolver, const InlineIterator& cleanLineStart, const BidiStatus& cleanLineBidiStatus, unsigned consecutiveHyphenatedLines) 1050{ 1051 const RenderStyle& styleToUse = style(); 1052 bool paginated = view().layoutState() && view().layoutState()->isPaginated(); 1053 LineMidpointState& lineMidpointState = resolver.midpointState(); 1054 InlineIterator end = resolver.position(); 1055 bool checkForEndLineMatch = layoutState.endLine(); 1056 RenderTextInfo renderTextInfo; 1057 VerticalPositionCache verticalPositionCache; 1058 1059 LineBreaker lineBreaker(*this); 1060 1061 while (!end.atEnd()) { 1062 // FIXME: Is this check necessary before the first iteration or can it be moved to the end? 1063 if (checkForEndLineMatch) { 1064 layoutState.setEndLineMatched(matchedEndLine(layoutState, resolver, cleanLineStart, cleanLineBidiStatus)); 1065 if (layoutState.endLineMatched()) { 1066 resolver.setPosition(InlineIterator(resolver.position().root(), 0, 0), 0); 1067 break; 1068 } 1069 } 1070 1071 lineMidpointState.reset(); 1072 1073 layoutState.lineInfo().setEmpty(true); 1074 layoutState.lineInfo().resetRunsFromLeadingWhitespace(); 1075 1076 const InlineIterator oldEnd = end; 1077 bool isNewUBAParagraph = layoutState.lineInfo().previousLineBrokeCleanly(); 1078 FloatingObject* lastFloatFromPreviousLine = (containsFloats()) ? m_floatingObjects->set().last().get() : 0; 1079 1080 WordMeasurements wordMeasurements; 1081 end = lineBreaker.nextLineBreak(resolver, layoutState.lineInfo(), renderTextInfo, lastFloatFromPreviousLine, consecutiveHyphenatedLines, wordMeasurements); 1082 renderTextInfo.m_lineBreakIterator.resetPriorContext(); 1083 if (resolver.position().atEnd()) { 1084 // FIXME: We shouldn't be creating any runs in nextLineBreak to begin with! 1085 // Once BidiRunList is separated from BidiResolver this will not be needed. 1086 resolver.runs().deleteRuns(); 1087 resolver.markCurrentRunEmpty(); // FIXME: This can probably be replaced by an ASSERT (or just removed). 1088 layoutState.setCheckForFloatsFromLastLine(true); 1089 resolver.setPosition(InlineIterator(resolver.position().root(), 0, 0), 0); 1090 break; 1091 } 1092 1093 ASSERT(end != resolver.position()); 1094 1095 // This is a short-cut for empty lines. 1096 if (layoutState.lineInfo().isEmpty()) { 1097 if (lastRootBox()) 1098 lastRootBox()->setLineBreakInfo(end.renderer(), end.offset(), resolver.status()); 1099 } else { 1100 VisualDirectionOverride override = (styleToUse.rtlOrdering() == VisualOrder ? (styleToUse.direction() == LTR ? VisualLeftToRightOverride : VisualRightToLeftOverride) : NoVisualOverride); 1101 1102 if (isNewUBAParagraph && styleToUse.unicodeBidi() == Plaintext && !resolver.context()->parent()) { 1103 TextDirection direction = styleToUse.direction(); 1104 determineDirectionality(direction, resolver.position()); 1105 resolver.setStatus(BidiStatus(direction, isOverride(styleToUse.unicodeBidi()))); 1106 } 1107 // FIXME: This ownership is reversed. We should own the BidiRunList and pass it to createBidiRunsForLine. 1108 BidiRunList<BidiRun>& bidiRuns = resolver.runs(); 1109 constructBidiRunsForSegment(resolver, bidiRuns, end, override, layoutState.lineInfo().previousLineBrokeCleanly()); 1110 ASSERT(resolver.position() == end); 1111 1112 BidiRun* trailingSpaceRun = !layoutState.lineInfo().previousLineBrokeCleanly() ? handleTrailingSpaces(bidiRuns, resolver.context()) : 0; 1113 1114 if (bidiRuns.runCount() && lineBreaker.lineWasHyphenated()) { 1115 bidiRuns.logicallyLastRun()->m_hasHyphen = true; 1116 consecutiveHyphenatedLines++; 1117 } else 1118 consecutiveHyphenatedLines = 0; 1119 1120 // Now that the runs have been ordered, we create the line boxes. 1121 // At the same time we figure out where border/padding/margin should be applied for 1122 // inline flow boxes. 1123 1124 LayoutUnit oldLogicalHeight = logicalHeight(); 1125 RootInlineBox* lineBox = createLineBoxesFromBidiRuns(resolver.status().context->level(), bidiRuns, end, layoutState.lineInfo(), verticalPositionCache, trailingSpaceRun, wordMeasurements); 1126 1127 bidiRuns.deleteRuns(); 1128 resolver.markCurrentRunEmpty(); // FIXME: This can probably be replaced by an ASSERT (or just removed). 1129 1130 if (lineBox) { 1131 lineBox->setLineBreakInfo(end.renderer(), end.offset(), resolver.status()); 1132 if (layoutState.usesRepaintBounds()) 1133 layoutState.updateRepaintRangeFromBox(lineBox); 1134 1135 if (paginated) { 1136 LayoutUnit adjustment = 0; 1137 bool overflowsRegion; 1138 adjustLinePositionForPagination(lineBox, adjustment, overflowsRegion, layoutState.flowThread()); 1139 if (adjustment) { 1140 LayoutUnit oldLineWidth = availableLogicalWidthForLine(oldLogicalHeight, layoutState.lineInfo().isFirstLine()); 1141 lineBox->adjustBlockDirectionPosition(adjustment); 1142 if (layoutState.usesRepaintBounds()) 1143 layoutState.updateRepaintRangeFromBox(lineBox); 1144 1145 if (availableLogicalWidthForLine(oldLogicalHeight + adjustment, layoutState.lineInfo().isFirstLine()) != oldLineWidth) { 1146 // We have to delete this line, remove all floats that got added, and let line layout re-run. 1147 lineBox->deleteLine(); 1148 end = restartLayoutRunsAndFloatsInRange(oldLogicalHeight, oldLogicalHeight + adjustment, lastFloatFromPreviousLine, resolver, oldEnd); 1149 continue; 1150 } 1151 1152 setLogicalHeight(lineBox->lineBottomWithLeading()); 1153 } 1154 1155 if (RenderFlowThread* flowThread = flowThreadContainingBlock()) { 1156 if (flowThread->isRenderNamedFlowThread() && overflowsRegion && hasNextPage(lineBox->lineTop())) { 1157 // Limit the height of this block to the end of the current region because 1158 // it is also fragmented into the next region. 1159 LayoutUnit remainingLogicalHeight = pageRemainingLogicalHeightForOffset(logicalTop(), ExcludePageBoundary); 1160 if (logicalHeight() > remainingLogicalHeight) 1161 setLogicalHeight(remainingLogicalHeight); 1162 } 1163 } 1164 1165 if (layoutState.flowThread()) 1166 updateRegionForLine(lineBox); 1167 } 1168 } 1169 } 1170 1171 for (size_t i = 0; i < lineBreaker.positionedObjects().size(); ++i) 1172 setStaticPositions(*this, *lineBreaker.positionedObjects()[i]); 1173 1174 if (!layoutState.lineInfo().isEmpty()) { 1175 layoutState.lineInfo().setFirstLine(false); 1176 clearFloats(lineBreaker.clear()); 1177 } 1178 1179 if (m_floatingObjects && lastRootBox()) { 1180 const FloatingObjectSet& floatingObjectSet = m_floatingObjects->set(); 1181 auto it = floatingObjectSet.begin(); 1182 auto end = floatingObjectSet.end(); 1183 if (layoutState.lastFloat()) { 1184 auto lastFloatIterator = floatingObjectSet.find<FloatingObject&, FloatingObjectHashTranslator>(*layoutState.lastFloat()); 1185 ASSERT(lastFloatIterator != end); 1186 ++lastFloatIterator; 1187 it = lastFloatIterator; 1188 } 1189 for (; it != end; ++it) { 1190 FloatingObject* f = it->get(); 1191 appendFloatingObjectToLastLine(f); 1192 ASSERT(&f->renderer() == &layoutState.floats()[layoutState.floatIndex()].object); 1193 // If a float's geometry has changed, give up on syncing with clean lines. 1194 if (layoutState.floats()[layoutState.floatIndex()].rect != f->frameRect()) 1195 checkForEndLineMatch = false; 1196 layoutState.setFloatIndex(layoutState.floatIndex() + 1); 1197 } 1198 layoutState.setLastFloat(!floatingObjectSet.isEmpty() ? floatingObjectSet.last().get() : nullptr); 1199 } 1200 1201 lineMidpointState.reset(); 1202 resolver.setPosition(end, numberOfIsolateAncestors(end)); 1203 } 1204 1205 // In case we already adjusted the line positions during this layout to avoid widows 1206 // then we need to ignore the possibility of having a new widows situation. 1207 // Otherwise, we risk leaving empty containers which is against the block fragmentation principles. 1208 if (paginated && !style().hasAutoWidows() && !didBreakAtLineToAvoidWidow()) { 1209 // Check the line boxes to make sure we didn't create unacceptable widows. 1210 // However, we'll prioritize orphans - so nothing we do here should create 1211 // a new orphan. 1212 1213 RootInlineBox* lineBox = lastRootBox(); 1214 1215 // Count from the end of the block backwards, to see how many hanging 1216 // lines we have. 1217 RootInlineBox* firstLineInBlock = firstRootBox(); 1218 int numLinesHanging = 1; 1219 while (lineBox && lineBox != firstLineInBlock && !lineBox->isFirstAfterPageBreak()) { 1220 ++numLinesHanging; 1221 lineBox = lineBox->prevRootBox(); 1222 } 1223 1224 // If there were no breaks in the block, we didn't create any widows. 1225 if (!lineBox || !lineBox->isFirstAfterPageBreak() || lineBox == firstLineInBlock) 1226 return; 1227 1228 if (numLinesHanging < style().widows()) { 1229 // We have detected a widow. Now we need to work out how many 1230 // lines there are on the previous page, and how many we need 1231 // to steal. 1232 int numLinesNeeded = style().widows() - numLinesHanging; 1233 RootInlineBox* currentFirstLineOfNewPage = lineBox; 1234 1235 // Count the number of lines in the previous page. 1236 lineBox = lineBox->prevRootBox(); 1237 int numLinesInPreviousPage = 1; 1238 while (lineBox && lineBox != firstLineInBlock && !lineBox->isFirstAfterPageBreak()) { 1239 ++numLinesInPreviousPage; 1240 lineBox = lineBox->prevRootBox(); 1241 } 1242 1243 // If there was an explicit value for orphans, respect that. If not, we still 1244 // shouldn't create a situation where we make an orphan bigger than the initial value. 1245 // This means that setting widows implies we also care about orphans, but given 1246 // the specification says the initial orphan value is non-zero, this is ok. The 1247 // author is always free to set orphans explicitly as well. 1248 int orphans = style().hasAutoOrphans() ? style().initialOrphans() : style().orphans(); 1249 int numLinesAvailable = numLinesInPreviousPage - orphans; 1250 if (numLinesAvailable <= 0) 1251 return; 1252 1253 int numLinesToTake = std::min(numLinesAvailable, numLinesNeeded); 1254 // Wind back from our first widowed line. 1255 lineBox = currentFirstLineOfNewPage; 1256 for (int i = 0; i < numLinesToTake; ++i) 1257 lineBox = lineBox->prevRootBox(); 1258 1259 // We now want to break at this line. Remember for next layout and trigger relayout. 1260 setBreakAtLineToAvoidWidow(lineCount(lineBox)); 1261 markLinesDirtyInBlockRange(lastRootBox()->lineBottomWithLeading(), lineBox->lineBottomWithLeading(), lineBox); 1262 } 1263 } 1264 1265 clearDidBreakAtLineToAvoidWidow(); 1266} 1267 1268void RenderBlockFlow::linkToEndLineIfNeeded(LineLayoutState& layoutState) 1269{ 1270 if (layoutState.endLine()) { 1271 if (layoutState.endLineMatched()) { 1272 bool paginated = view().layoutState() && view().layoutState()->isPaginated(); 1273 // Attach all the remaining lines, and then adjust their y-positions as needed. 1274 LayoutUnit delta = logicalHeight() - layoutState.endLineLogicalTop(); 1275 for (RootInlineBox* line = layoutState.endLine(); line; line = line->nextRootBox()) { 1276 line->attachLine(); 1277 if (paginated) { 1278 delta -= line->paginationStrut(); 1279 bool overflowsRegion; 1280 adjustLinePositionForPagination(line, delta, overflowsRegion, layoutState.flowThread()); 1281 } 1282 if (delta) { 1283 layoutState.updateRepaintRangeFromBox(line, delta); 1284 line->adjustBlockDirectionPosition(delta); 1285 } 1286 if (layoutState.flowThread()) 1287 updateRegionForLine(line); 1288 if (Vector<RenderBox*>* cleanLineFloats = line->floatsPtr()) { 1289 for (auto it = cleanLineFloats->begin(), end = cleanLineFloats->end(); it != end; ++it) { 1290 RenderBox* floatingBox = *it; 1291 FloatingObject* floatingObject = insertFloatingObject(*floatingBox); 1292 ASSERT(!floatingObject->originatingLine()); 1293 floatingObject->setOriginatingLine(line); 1294 setLogicalHeight(logicalTopForChild(*floatingBox) - marginBeforeForChild(*floatingBox) + delta); 1295 positionNewFloats(); 1296 } 1297 } 1298 } 1299 setLogicalHeight(lastRootBox()->lineBottomWithLeading()); 1300 } else { 1301 // Delete all the remaining lines. 1302 deleteLineRange(layoutState, layoutState.endLine()); 1303 } 1304 } 1305 1306 if (m_floatingObjects && (layoutState.checkForFloatsFromLastLine() || positionNewFloats()) && lastRootBox()) { 1307 // In case we have a float on the last line, it might not be positioned up to now. 1308 // This has to be done before adding in the bottom border/padding, or the float will 1309 // include the padding incorrectly. -dwh 1310 if (layoutState.checkForFloatsFromLastLine()) { 1311 LayoutUnit bottomVisualOverflow = lastRootBox()->logicalBottomVisualOverflow(); 1312 LayoutUnit bottomLayoutOverflow = lastRootBox()->logicalBottomLayoutOverflow(); 1313 auto newLineBox = std::make_unique<TrailingFloatsRootInlineBox>(*this); 1314 auto trailingFloatsLineBox = newLineBox.get(); 1315 m_lineBoxes.appendLineBox(WTF::move(newLineBox)); 1316 trailingFloatsLineBox->setConstructed(); 1317 GlyphOverflowAndFallbackFontsMap textBoxDataMap; 1318 VerticalPositionCache verticalPositionCache; 1319 LayoutUnit blockLogicalHeight = logicalHeight(); 1320 trailingFloatsLineBox->alignBoxesInBlockDirection(blockLogicalHeight, textBoxDataMap, verticalPositionCache); 1321 trailingFloatsLineBox->setLineTopBottomPositions(blockLogicalHeight, blockLogicalHeight, blockLogicalHeight, blockLogicalHeight); 1322 trailingFloatsLineBox->setPaginatedLineWidth(availableLogicalWidthForContent(blockLogicalHeight)); 1323 LayoutRect logicalLayoutOverflow(0, blockLogicalHeight, 1, bottomLayoutOverflow - blockLogicalHeight); 1324 LayoutRect logicalVisualOverflow(0, blockLogicalHeight, 1, bottomVisualOverflow - blockLogicalHeight); 1325 trailingFloatsLineBox->setOverflowFromLogicalRects(logicalLayoutOverflow, logicalVisualOverflow, trailingFloatsLineBox->lineTop(), trailingFloatsLineBox->lineBottom()); 1326 if (layoutState.flowThread()) 1327 updateRegionForLine(trailingFloatsLineBox); 1328 } 1329 1330 const FloatingObjectSet& floatingObjectSet = m_floatingObjects->set(); 1331 auto it = floatingObjectSet.begin(); 1332 auto end = floatingObjectSet.end(); 1333 if (layoutState.lastFloat()) { 1334 auto lastFloatIterator = floatingObjectSet.find<FloatingObject&, FloatingObjectHashTranslator>(*layoutState.lastFloat()); 1335 ASSERT(lastFloatIterator != end); 1336 ++lastFloatIterator; 1337 it = lastFloatIterator; 1338 } 1339 for (; it != end; ++it) 1340 appendFloatingObjectToLastLine(it->get()); 1341 layoutState.setLastFloat(!floatingObjectSet.isEmpty() ? floatingObjectSet.last().get() : nullptr); 1342 } 1343} 1344 1345void RenderBlockFlow::repaintDirtyFloats(Vector<FloatWithRect>& floats) 1346{ 1347 size_t floatCount = floats.size(); 1348 // Floats that did not have layout did not repaint when we laid them out. They would have 1349 // painted by now if they had moved, but if they stayed at (0, 0), they still need to be 1350 // painted. 1351 for (size_t i = 0; i < floatCount; ++i) { 1352 if (!floats[i].everHadLayout) { 1353 RenderBox& box = floats[i].object; 1354 if (!box.x() && !box.y() && box.checkForRepaintDuringLayout()) 1355 box.repaint(); 1356 } 1357 } 1358} 1359 1360void RenderBlockFlow::layoutLineBoxes(bool relayoutChildren, LayoutUnit& repaintLogicalTop, LayoutUnit& repaintLogicalBottom) 1361{ 1362 ASSERT(!m_simpleLineLayout); 1363 1364 setLogicalHeight(borderAndPaddingBefore()); 1365 1366 // Lay out our hypothetical grid line as though it occurs at the top of the block. 1367 if (view().layoutState() && view().layoutState()->lineGrid() == this) 1368 layoutLineGridBox(); 1369 1370 RenderFlowThread* flowThread = flowThreadContainingBlock(); 1371 bool clearLinesForPagination = firstRootBox() && flowThread && !flowThread->hasRegions(); 1372 1373 // Figure out if we should clear out our line boxes. 1374 // FIXME: Handle resize eventually! 1375 bool isFullLayout = !firstRootBox() || selfNeedsLayout() || relayoutChildren || clearLinesForPagination; 1376 LineLayoutState layoutState(isFullLayout, repaintLogicalTop, repaintLogicalBottom, flowThread); 1377 1378 if (isFullLayout) 1379 lineBoxes().deleteLineBoxes(); 1380 1381 // Text truncation kicks in in two cases: 1382 // 1) If your overflow isn't visible and your text-overflow-mode isn't clip. 1383 // 2) If you're an anonymous block with a block parent that satisfies #1. 1384 // FIXME: CSS3 says that descendants that are clipped must also know how to truncate. This is insanely 1385 // difficult to figure out in general (especially in the middle of doing layout), so we only handle the 1386 // simple case of an anonymous block truncating when it's parent is clipped. 1387 bool hasTextOverflow = (style().textOverflow() && hasOverflowClip()) 1388 || (isAnonymousBlock() && parent() && parent()->isRenderBlock() && parent()->style().textOverflow() && parent()->hasOverflowClip()); 1389 1390 // Walk all the lines and delete our ellipsis line boxes if they exist. 1391 if (hasTextOverflow) 1392 deleteEllipsisLineBoxes(); 1393 1394 if (firstChild()) { 1395 // In full layout mode, clear the line boxes of children upfront. Otherwise, 1396 // siblings can run into stale root lineboxes during layout. Then layout 1397 // the replaced elements later. In partial layout mode, line boxes are not 1398 // deleted and only dirtied. In that case, we can layout the replaced 1399 // elements at the same time. 1400 bool hasInlineChild = false; 1401 Vector<RenderBox*> replacedChildren; 1402 for (InlineWalker walker(*this); !walker.atEnd(); walker.advance()) { 1403 RenderObject& o = *walker.current(); 1404 1405 if (!hasInlineChild && o.isInline()) 1406 hasInlineChild = true; 1407 1408 if (o.isReplaced() || o.isFloating() || o.isOutOfFlowPositioned()) { 1409 RenderBox& box = toRenderBox(o); 1410 1411 if (relayoutChildren || box.hasRelativeDimensions()) 1412 box.setChildNeedsLayout(MarkOnlyThis); 1413 1414 // If relayoutChildren is set and the child has percentage padding or an embedded content box, we also need to invalidate the childs pref widths. 1415 if (relayoutChildren && box.needsPreferredWidthsRecalculation()) 1416 box.setPreferredLogicalWidthsDirty(true, MarkOnlyThis); 1417 1418 if (box.isOutOfFlowPositioned()) 1419 box.containingBlock()->insertPositionedObject(box); 1420 else if (box.isFloating()) 1421 layoutState.floats().append(FloatWithRect(box)); 1422 else if (isFullLayout || box.needsLayout()) { 1423 // Replaced element. 1424 box.dirtyLineBoxes(isFullLayout); 1425 if (isFullLayout) 1426 replacedChildren.append(&box); 1427 else 1428 box.layoutIfNeeded(); 1429 } 1430 } else if (o.isTextOrLineBreak() || (o.isRenderInline() && !walker.atEndOfInline())) { 1431 if (o.isRenderInline()) 1432 toRenderInline(o).updateAlwaysCreateLineBoxes(layoutState.isFullLayout()); 1433 if (layoutState.isFullLayout() || o.selfNeedsLayout()) 1434 dirtyLineBoxesForRenderer(o, layoutState.isFullLayout()); 1435 o.clearNeedsLayout(); 1436 } 1437 } 1438 1439 for (size_t i = 0; i < replacedChildren.size(); i++) 1440 replacedChildren[i]->layoutIfNeeded(); 1441 1442 layoutRunsAndFloats(layoutState, hasInlineChild); 1443 } 1444 1445 // Expand the last line to accommodate Ruby and emphasis marks. 1446 int lastLineAnnotationsAdjustment = 0; 1447 if (lastRootBox()) { 1448 LayoutUnit lowestAllowedPosition = std::max(lastRootBox()->lineBottom(), logicalHeight() + paddingAfter()); 1449 if (!style().isFlippedLinesWritingMode()) 1450 lastLineAnnotationsAdjustment = lastRootBox()->computeUnderAnnotationAdjustment(lowestAllowedPosition); 1451 else 1452 lastLineAnnotationsAdjustment = lastRootBox()->computeOverAnnotationAdjustment(lowestAllowedPosition); 1453 } 1454 1455 // Now add in the bottom border/padding. 1456 setLogicalHeight(logicalHeight() + lastLineAnnotationsAdjustment + borderAndPaddingAfter() + scrollbarLogicalHeight()); 1457 1458 if (!firstRootBox() && hasLineIfEmpty()) 1459 setLogicalHeight(logicalHeight() + lineHeight(true, isHorizontalWritingMode() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes)); 1460 1461 // See if we have any lines that spill out of our block. If we do, then we will possibly need to 1462 // truncate text. 1463 if (hasTextOverflow) 1464 checkLinesForTextOverflow(); 1465} 1466 1467void RenderBlockFlow::checkFloatsInCleanLine(RootInlineBox* line, Vector<FloatWithRect>& floats, size_t& floatIndex, bool& encounteredNewFloat, bool& dirtiedByFloat) 1468{ 1469 Vector<RenderBox*>* cleanLineFloats = line->floatsPtr(); 1470 if (!cleanLineFloats) 1471 return; 1472 1473 if (!floats.size()) { 1474 encounteredNewFloat = true; 1475 return; 1476 } 1477 1478 for (auto it = cleanLineFloats->begin(), end = cleanLineFloats->end(); it != end; ++it) { 1479 RenderBox* floatingBox = *it; 1480 floatingBox->layoutIfNeeded(); 1481 LayoutSize newSize(floatingBox->width() + floatingBox->horizontalMarginExtent(), floatingBox->height() + floatingBox->verticalMarginExtent()); 1482 ASSERT_WITH_SECURITY_IMPLICATION(floatIndex < floats.size()); 1483 if (&floats[floatIndex].object != floatingBox) { 1484 encounteredNewFloat = true; 1485 return; 1486 } 1487 1488 if (floats[floatIndex].rect.size() != newSize) { 1489 LayoutUnit floatTop = isHorizontalWritingMode() ? floats[floatIndex].rect.y() : floats[floatIndex].rect.x(); 1490 LayoutUnit floatHeight = isHorizontalWritingMode() ? std::max(floats[floatIndex].rect.height(), newSize.height()) : std::max(floats[floatIndex].rect.width(), newSize.width()); 1491 floatHeight = std::min(floatHeight, LayoutUnit::max() - floatTop); 1492 line->markDirty(); 1493 markLinesDirtyInBlockRange(line->lineBottomWithLeading(), floatTop + floatHeight, line); 1494 floats[floatIndex].rect.setSize(newSize); 1495 dirtiedByFloat = true; 1496 } 1497 floatIndex++; 1498 } 1499} 1500 1501RootInlineBox* RenderBlockFlow::determineStartPosition(LineLayoutState& layoutState, InlineBidiResolver& resolver) 1502{ 1503 RootInlineBox* curr = 0; 1504 RootInlineBox* last = 0; 1505 1506 // FIXME: This entire float-checking block needs to be broken into a new function. 1507 bool dirtiedByFloat = false; 1508 if (!layoutState.isFullLayout()) { 1509 // Paginate all of the clean lines. 1510 bool paginated = view().layoutState() && view().layoutState()->isPaginated(); 1511 LayoutUnit paginationDelta = 0; 1512 size_t floatIndex = 0; 1513 for (curr = firstRootBox(); curr && !curr->isDirty(); curr = curr->nextRootBox()) { 1514 if (paginated) { 1515 if (lineWidthForPaginatedLineChanged(curr, 0, layoutState.flowThread())) { 1516 curr->markDirty(); 1517 break; 1518 } 1519 paginationDelta -= curr->paginationStrut(); 1520 bool overflowsRegion; 1521 adjustLinePositionForPagination(curr, paginationDelta, overflowsRegion, layoutState.flowThread()); 1522 if (paginationDelta) { 1523 if (containsFloats() || !layoutState.floats().isEmpty()) { 1524 // FIXME: Do better eventually. For now if we ever shift because of pagination and floats are present just go to a full layout. 1525 layoutState.markForFullLayout(); 1526 break; 1527 } 1528 1529 layoutState.updateRepaintRangeFromBox(curr, paginationDelta); 1530 curr->adjustBlockDirectionPosition(paginationDelta); 1531 } 1532 if (layoutState.flowThread()) 1533 updateRegionForLine(curr); 1534 } 1535 1536 // If a new float has been inserted before this line or before its last known float, just do a full layout. 1537 bool encounteredNewFloat = false; 1538 checkFloatsInCleanLine(curr, layoutState.floats(), floatIndex, encounteredNewFloat, dirtiedByFloat); 1539 if (encounteredNewFloat) 1540 layoutState.markForFullLayout(); 1541 1542 if (dirtiedByFloat || layoutState.isFullLayout()) 1543 break; 1544 } 1545 // Check if a new float has been inserted after the last known float. 1546 if (!curr && floatIndex < layoutState.floats().size()) 1547 layoutState.markForFullLayout(); 1548 } 1549 1550 if (layoutState.isFullLayout()) { 1551 m_lineBoxes.deleteLineBoxTree(); 1552 curr = 0; 1553 1554 ASSERT(!firstRootBox() && !lastRootBox()); 1555 } else { 1556 if (curr) { 1557 // We have a dirty line. 1558 if (RootInlineBox* prevRootBox = curr->prevRootBox()) { 1559 // We have a previous line. 1560 if (!dirtiedByFloat && (!prevRootBox->endsWithBreak() || !prevRootBox->lineBreakObj() || (prevRootBox->lineBreakObj()->isText() && prevRootBox->lineBreakPos() >= toRenderText(prevRootBox->lineBreakObj())->textLength()))) 1561 // The previous line didn't break cleanly or broke at a newline 1562 // that has been deleted, so treat it as dirty too. 1563 curr = prevRootBox; 1564 } 1565 } else { 1566 // No dirty lines were found. 1567 // If the last line didn't break cleanly, treat it as dirty. 1568 if (lastRootBox() && !lastRootBox()->endsWithBreak()) 1569 curr = lastRootBox(); 1570 } 1571 1572 // If we have no dirty lines, then last is just the last root box. 1573 last = curr ? curr->prevRootBox() : lastRootBox(); 1574 } 1575 1576 unsigned numCleanFloats = 0; 1577 if (!layoutState.floats().isEmpty()) { 1578 LayoutUnit savedLogicalHeight = logicalHeight(); 1579 // Restore floats from clean lines. 1580 RootInlineBox* line = firstRootBox(); 1581 while (line != curr) { 1582 if (Vector<RenderBox*>* cleanLineFloats = line->floatsPtr()) { 1583 for (auto it = cleanLineFloats->begin(), end = cleanLineFloats->end(); it != end; ++it) { 1584 RenderBox* floatingBox = *it; 1585 FloatingObject* floatingObject = insertFloatingObject(*floatingBox); 1586 ASSERT(!floatingObject->originatingLine()); 1587 floatingObject->setOriginatingLine(line); 1588 setLogicalHeight(logicalTopForChild(*floatingBox) - marginBeforeForChild(*floatingBox)); 1589 positionNewFloats(); 1590 ASSERT(&layoutState.floats()[numCleanFloats].object == floatingBox); 1591 numCleanFloats++; 1592 } 1593 } 1594 line = line->nextRootBox(); 1595 } 1596 setLogicalHeight(savedLogicalHeight); 1597 } 1598 layoutState.setFloatIndex(numCleanFloats); 1599 1600 layoutState.lineInfo().setFirstLine(!last); 1601 layoutState.lineInfo().setPreviousLineBrokeCleanly(!last || last->endsWithBreak()); 1602 1603 if (last) { 1604 setLogicalHeight(last->lineBottomWithLeading()); 1605 InlineIterator iter = InlineIterator(this, last->lineBreakObj(), last->lineBreakPos()); 1606 resolver.setPosition(iter, numberOfIsolateAncestors(iter)); 1607 resolver.setStatus(last->lineBreakBidiStatus()); 1608 } else { 1609 TextDirection direction = style().direction(); 1610 if (style().unicodeBidi() == Plaintext) 1611 determineDirectionality(direction, InlineIterator(this, bidiFirstSkippingEmptyInlines(*this), 0)); 1612 resolver.setStatus(BidiStatus(direction, isOverride(style().unicodeBidi()))); 1613 InlineIterator iter = InlineIterator(this, bidiFirstSkippingEmptyInlines(*this, &resolver), 0); 1614 resolver.setPosition(iter, numberOfIsolateAncestors(iter)); 1615 } 1616 return curr; 1617} 1618 1619void RenderBlockFlow::determineEndPosition(LineLayoutState& layoutState, RootInlineBox* startLine, InlineIterator& cleanLineStart, BidiStatus& cleanLineBidiStatus) 1620{ 1621 ASSERT(!layoutState.endLine()); 1622 size_t floatIndex = layoutState.floatIndex(); 1623 RootInlineBox* last = 0; 1624 for (RootInlineBox* curr = startLine->nextRootBox(); curr; curr = curr->nextRootBox()) { 1625 if (!curr->isDirty()) { 1626 bool encounteredNewFloat = false; 1627 bool dirtiedByFloat = false; 1628 checkFloatsInCleanLine(curr, layoutState.floats(), floatIndex, encounteredNewFloat, dirtiedByFloat); 1629 if (encounteredNewFloat) 1630 return; 1631 } 1632 if (curr->isDirty()) 1633 last = 0; 1634 else if (!last) 1635 last = curr; 1636 } 1637 1638 if (!last) 1639 return; 1640 1641 // At this point, |last| is the first line in a run of clean lines that ends with the last line 1642 // in the block. 1643 1644 RootInlineBox* prev = last->prevRootBox(); 1645 cleanLineStart = InlineIterator(this, prev->lineBreakObj(), prev->lineBreakPos()); 1646 cleanLineBidiStatus = prev->lineBreakBidiStatus(); 1647 layoutState.setEndLineLogicalTop(prev->lineBottomWithLeading()); 1648 1649 for (RootInlineBox* line = last; line; line = line->nextRootBox()) 1650 line->extractLine(); // Disconnect all line boxes from their render objects while preserving 1651 // their connections to one another. 1652 1653 layoutState.setEndLine(last); 1654} 1655 1656bool RenderBlockFlow::checkPaginationAndFloatsAtEndLine(LineLayoutState& layoutState) 1657{ 1658 LayoutUnit lineDelta = logicalHeight() - layoutState.endLineLogicalTop(); 1659 1660 bool paginated = view().layoutState() && view().layoutState()->isPaginated(); 1661 if (paginated && layoutState.flowThread()) { 1662 // Check all lines from here to the end, and see if the hypothetical new position for the lines will result 1663 // in a different available line width. 1664 for (RootInlineBox* lineBox = layoutState.endLine(); lineBox; lineBox = lineBox->nextRootBox()) { 1665 if (paginated) { 1666 // This isn't the real move we're going to do, so don't update the line box's pagination 1667 // strut yet. 1668 LayoutUnit oldPaginationStrut = lineBox->paginationStrut(); 1669 bool overflowsRegion; 1670 lineDelta -= oldPaginationStrut; 1671 adjustLinePositionForPagination(lineBox, lineDelta, overflowsRegion, layoutState.flowThread()); 1672 lineBox->setPaginationStrut(oldPaginationStrut); 1673 } 1674 if (lineWidthForPaginatedLineChanged(lineBox, lineDelta, layoutState.flowThread())) 1675 return false; 1676 } 1677 } 1678 1679 if (!lineDelta || !m_floatingObjects) 1680 return true; 1681 1682 // See if any floats end in the range along which we want to shift the lines vertically. 1683 LayoutUnit logicalTop = std::min(logicalHeight(), layoutState.endLineLogicalTop()); 1684 1685 RootInlineBox* lastLine = layoutState.endLine(); 1686 while (RootInlineBox* nextLine = lastLine->nextRootBox()) 1687 lastLine = nextLine; 1688 1689 LayoutUnit logicalBottom = lastLine->lineBottomWithLeading() + absoluteValue(lineDelta); 1690 1691 const FloatingObjectSet& floatingObjectSet = m_floatingObjects->set(); 1692 auto end = floatingObjectSet.end(); 1693 for (auto it = floatingObjectSet.begin(); it != end; ++it) { 1694 FloatingObject* floatingObject = it->get(); 1695 if (logicalBottomForFloat(floatingObject) >= logicalTop && logicalBottomForFloat(floatingObject) < logicalBottom) 1696 return false; 1697 } 1698 1699 return true; 1700} 1701 1702bool RenderBlockFlow::lineWidthForPaginatedLineChanged(RootInlineBox* rootBox, LayoutUnit lineDelta, RenderFlowThread* flowThread) const 1703{ 1704 if (!flowThread) 1705 return false; 1706 1707 RenderRegion* currentRegion = regionAtBlockOffset(rootBox->lineTopWithLeading() + lineDelta); 1708 // Just bail if the region didn't change. 1709 if (rootBox->containingRegion() == currentRegion) 1710 return false; 1711 return rootBox->paginatedLineWidth() != availableLogicalWidthForContent(currentRegion); 1712} 1713 1714bool RenderBlockFlow::matchedEndLine(LineLayoutState& layoutState, const InlineBidiResolver& resolver, const InlineIterator& endLineStart, const BidiStatus& endLineStatus) 1715{ 1716 if (resolver.position() == endLineStart) { 1717 if (resolver.status() != endLineStatus) 1718 return false; 1719 return checkPaginationAndFloatsAtEndLine(layoutState); 1720 } 1721 1722 // The first clean line doesn't match, but we can check a handful of following lines to try 1723 // to match back up. 1724 static int numLines = 8; // The # of lines we're willing to match against. 1725 RootInlineBox* originalEndLine = layoutState.endLine(); 1726 RootInlineBox* line = originalEndLine; 1727 for (int i = 0; i < numLines && line; i++, line = line->nextRootBox()) { 1728 if (line->lineBreakObj() == resolver.position().renderer() && line->lineBreakPos() == resolver.position().offset()) { 1729 // We have a match. 1730 if (line->lineBreakBidiStatus() != resolver.status()) 1731 return false; // ...but the bidi state doesn't match. 1732 1733 bool matched = false; 1734 RootInlineBox* result = line->nextRootBox(); 1735 layoutState.setEndLine(result); 1736 if (result) { 1737 layoutState.setEndLineLogicalTop(line->lineBottomWithLeading()); 1738 matched = checkPaginationAndFloatsAtEndLine(layoutState); 1739 } 1740 1741 // Now delete the lines that we failed to sync. 1742 deleteLineRange(layoutState, originalEndLine, result); 1743 return matched; 1744 } 1745 } 1746 1747 return false; 1748} 1749 1750bool RenderBlock::generatesLineBoxesForInlineChild(RenderObject* inlineObj) 1751{ 1752 ASSERT(inlineObj->parent() == this); 1753 1754 InlineIterator it(this, inlineObj, 0); 1755 // FIXME: We should pass correct value for WhitespacePosition. 1756 while (!it.atEnd() && !requiresLineBox(it)) 1757 it.increment(); 1758 1759 return !it.atEnd(); 1760} 1761 1762void RenderBlockFlow::addOverflowFromInlineChildren() 1763{ 1764 if (auto layout = simpleLineLayout()) { 1765 ASSERT(!hasOverflowClip()); 1766 SimpleLineLayout::collectFlowOverflow(*this, *layout); 1767 return; 1768 } 1769 LayoutUnit endPadding = hasOverflowClip() ? paddingEnd() : LayoutUnit(); 1770 // FIXME: Need to find another way to do this, since scrollbars could show when we don't want them to. 1771 if (hasOverflowClip() && !endPadding && element() && element()->isRootEditableElement() && style().isLeftToRightDirection()) 1772 endPadding = 1; 1773 for (RootInlineBox* curr = firstRootBox(); curr; curr = curr->nextRootBox()) { 1774 addLayoutOverflow(curr->paddedLayoutOverflowRect(endPadding)); 1775 RenderRegion* region = flowThreadContainingBlock() ? curr->containingRegion() : nullptr; 1776 if (region) 1777 region->addLayoutOverflowForBox(this, curr->paddedLayoutOverflowRect(endPadding)); 1778 if (!hasOverflowClip()) { 1779 addVisualOverflow(curr->visualOverflowRect(curr->lineTop(), curr->lineBottom())); 1780 if (region) 1781 region->addVisualOverflowForBox(this, curr->visualOverflowRect(curr->lineTop(), curr->lineBottom())); 1782 } 1783 } 1784} 1785 1786void RenderBlockFlow::deleteEllipsisLineBoxes() 1787{ 1788 ETextAlign textAlign = style().textAlign(); 1789 bool ltr = style().isLeftToRightDirection(); 1790 bool firstLine = true; 1791 for (RootInlineBox* curr = firstRootBox(); curr; curr = curr->nextRootBox()) { 1792 if (curr->hasEllipsisBox()) { 1793 curr->clearTruncation(); 1794 1795 // Shift the line back where it belongs if we cannot accomodate an ellipsis. 1796 float logicalLeft = logicalLeftOffsetForLine(curr->lineTop(), firstLine); 1797 float availableLogicalWidth = logicalRightOffsetForLine(curr->lineTop(), false) - logicalLeft; 1798 float totalLogicalWidth = curr->logicalWidth(); 1799 updateLogicalWidthForAlignment(textAlign, curr, 0, logicalLeft, totalLogicalWidth, availableLogicalWidth, 0); 1800 1801 if (ltr) 1802 curr->adjustLogicalPosition((logicalLeft - curr->logicalLeft()), 0); 1803 else 1804 curr->adjustLogicalPosition(-(curr->logicalLeft() - logicalLeft), 0); 1805 } 1806 firstLine = false; 1807 } 1808} 1809 1810void RenderBlockFlow::checkLinesForTextOverflow() 1811{ 1812 // Determine the width of the ellipsis using the current font. 1813 // FIXME: CSS3 says this is configurable, also need to use 0x002E (FULL STOP) if horizontal ellipsis is "not renderable" 1814 const Font& font = style().font(); 1815 DEPRECATED_DEFINE_STATIC_LOCAL(AtomicString, ellipsisStr, (&horizontalEllipsis, 1)); 1816 const Font& firstLineFont = firstLineStyle().font(); 1817 float firstLineEllipsisWidth = firstLineFont.width(constructTextRun(this, firstLineFont, &horizontalEllipsis, 1, firstLineStyle())); 1818 float ellipsisWidth = (font == firstLineFont) ? firstLineEllipsisWidth : font.width(constructTextRun(this, font, &horizontalEllipsis, 1, style())); 1819 1820 // For LTR text truncation, we want to get the right edge of our padding box, and then we want to see 1821 // if the right edge of a line box exceeds that. For RTL, we use the left edge of the padding box and 1822 // check the left edge of the line box to see if it is less 1823 // Include the scrollbar for overflow blocks, which means we want to use "contentWidth()" 1824 bool ltr = style().isLeftToRightDirection(); 1825 ETextAlign textAlign = style().textAlign(); 1826 bool firstLine = true; 1827 for (RootInlineBox* curr = firstRootBox(); curr; curr = curr->nextRootBox()) { 1828 LayoutUnit blockRightEdge = logicalRightOffsetForLine(curr->lineTop(), firstLine); 1829 LayoutUnit blockLeftEdge = logicalLeftOffsetForLine(curr->lineTop(), firstLine); 1830 LayoutUnit lineBoxEdge = ltr ? curr->x() + curr->logicalWidth() : curr->x(); 1831 if ((ltr && lineBoxEdge > blockRightEdge) || (!ltr && lineBoxEdge < blockLeftEdge)) { 1832 // This line spills out of our box in the appropriate direction. Now we need to see if the line 1833 // can be truncated. In order for truncation to be possible, the line must have sufficient space to 1834 // accommodate our truncation string, and no replaced elements (images, tables) can overlap the ellipsis 1835 // space. 1836 1837 LayoutUnit width = firstLine ? firstLineEllipsisWidth : ellipsisWidth; 1838 LayoutUnit blockEdge = ltr ? blockRightEdge : blockLeftEdge; 1839 if (curr->lineCanAccommodateEllipsis(ltr, blockEdge, lineBoxEdge, width)) { 1840 float totalLogicalWidth = curr->placeEllipsis(ellipsisStr, ltr, blockLeftEdge, blockRightEdge, width); 1841 1842 float logicalLeft = 0; // We are only interested in the delta from the base position. 1843 float truncatedWidth = logicalRightOffsetForLine(curr->lineTop(), firstLine); 1844 updateLogicalWidthForAlignment(textAlign, curr, 0, logicalLeft, totalLogicalWidth, truncatedWidth, 0); 1845 if (ltr) 1846 curr->adjustLogicalPosition(logicalLeft, 0); 1847 else 1848 curr->adjustLogicalPosition(-(truncatedWidth - (logicalLeft + totalLogicalWidth)), 0); 1849 } 1850 } 1851 firstLine = false; 1852 } 1853} 1854 1855bool RenderBlockFlow::positionNewFloatOnLine(FloatingObject* newFloat, FloatingObject* lastFloatFromPreviousLine, LineInfo& lineInfo, LineWidth& width) 1856{ 1857 if (!positionNewFloats()) 1858 return false; 1859 1860 width.shrinkAvailableWidthForNewFloatIfNeeded(newFloat); 1861 1862 // We only connect floats to lines for pagination purposes if the floats occur at the start of 1863 // the line and the previous line had a hard break (so this line is either the first in the block 1864 // or follows a <br>). 1865 if (!newFloat->paginationStrut() || !lineInfo.previousLineBrokeCleanly() || !lineInfo.isEmpty()) 1866 return true; 1867 1868 const FloatingObjectSet& floatingObjectSet = m_floatingObjects->set(); 1869 ASSERT(floatingObjectSet.last().get() == newFloat); 1870 1871 LayoutUnit floatLogicalTop = logicalTopForFloat(newFloat); 1872 LayoutUnit paginationStrut = newFloat->paginationStrut(); 1873 1874 if (floatLogicalTop - paginationStrut != logicalHeight() + lineInfo.floatPaginationStrut()) 1875 return true; 1876 1877 auto it = floatingObjectSet.end(); 1878 --it; // Last float is newFloat, skip that one. 1879 auto begin = floatingObjectSet.begin(); 1880 while (it != begin) { 1881 --it; 1882 FloatingObject* floatingObject = it->get(); 1883 if (floatingObject == lastFloatFromPreviousLine) 1884 break; 1885 if (logicalTopForFloat(floatingObject) == logicalHeight() + lineInfo.floatPaginationStrut()) { 1886 floatingObject->setPaginationStrut(paginationStrut + floatingObject->paginationStrut()); 1887 RenderBox& floatBox = floatingObject->renderer(); 1888 setLogicalTopForChild(floatBox, logicalTopForChild(floatBox) + marginBeforeForChild(floatBox) + paginationStrut); 1889 1890 if (updateRegionRangeForBoxChild(floatingObject->renderer())) 1891 floatBox.setNeedsLayout(MarkOnlyThis); 1892 else if (floatBox.isRenderBlock()) 1893 toRenderBlock(floatBox).setChildNeedsLayout(MarkOnlyThis); 1894 floatBox.layoutIfNeeded(); 1895 1896 // Save the old logical top before calling removePlacedObject which will set 1897 // isPlaced to false. Otherwise it will trigger an assert in logicalTopForFloat. 1898 LayoutUnit oldLogicalTop = logicalTopForFloat(floatingObject); 1899 m_floatingObjects->removePlacedObject(floatingObject); 1900 setLogicalTopForFloat(floatingObject, oldLogicalTop + paginationStrut); 1901 m_floatingObjects->addPlacedObject(floatingObject); 1902 } 1903 } 1904 1905 // Just update the line info's pagination strut without altering our logical height yet. If the line ends up containing 1906 // no content, then we don't want to improperly grow the height of the block. 1907 lineInfo.setFloatPaginationStrut(lineInfo.floatPaginationStrut() + paginationStrut); 1908 return true; 1909} 1910 1911LayoutUnit RenderBlockFlow::startAlignedOffsetForLine(LayoutUnit position, bool firstLine) 1912{ 1913 ETextAlign textAlign = style().textAlign(); 1914 1915 // <rdar://problem/15427571> 1916 // https://bugs.webkit.org/show_bug.cgi?id=124522 1917 // This quirk is for legacy content that doesn't work properly with the center positioning scheme 1918 // being honored (e.g., epubs). 1919 if (textAlign == TASTART || document().settings()->useLegacyTextAlignPositionedElementBehavior()) // FIXME: Handle TAEND here 1920 return startOffsetForLine(position, firstLine); 1921 1922 // updateLogicalWidthForAlignment() handles the direction of the block so no need to consider it here 1923 float totalLogicalWidth = 0; 1924 float logicalLeft = logicalLeftOffsetForLine(logicalHeight(), false); 1925 float availableLogicalWidth = logicalRightOffsetForLine(logicalHeight(), false) - logicalLeft; 1926 1927 // FIXME: Bug 129311: We need to pass a valid RootInlineBox here, considering the bidi level used to construct the line. 1928 updateLogicalWidthForAlignment(textAlign, 0, 0, logicalLeft, totalLogicalWidth, availableLogicalWidth, 0); 1929 1930 if (!style().isLeftToRightDirection()) 1931 return logicalWidth() - logicalLeft; 1932 return logicalLeft; 1933} 1934 1935void RenderBlockFlow::updateRegionForLine(RootInlineBox* lineBox) const 1936{ 1937 ASSERT(lineBox); 1938 1939 if (!hasRegionRangeInFlowThread()) 1940 lineBox->clearContainingRegion(); 1941 else { 1942 if (auto containingRegion = regionAtBlockOffset(lineBox->lineTopWithLeading())) 1943 lineBox->setContainingRegion(*containingRegion); 1944 else 1945 lineBox->clearContainingRegion(); 1946 } 1947 1948 RootInlineBox* prevLineBox = lineBox->prevRootBox(); 1949 if (!prevLineBox) 1950 return; 1951 1952 // This check is more accurate than the one in |adjustLinePositionForPagination| because it takes into 1953 // account just the container changes between lines. The before mentioned function doesn't set the flag 1954 // correctly if the line is positioned at the top of the last fragment container. 1955 if (lineBox->containingRegion() != prevLineBox->containingRegion()) 1956 lineBox->setIsFirstAfterPageBreak(true); 1957} 1958 1959} 1960