1 /* 2 * Copyright (C) 2000 Lars Knoll (knoll (at) 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 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Library General Public 8 * License as published by the Free Software Foundation; either 9 * version 2 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Library General Public License for more details. 15 * 16 * You should have received a copy of the GNU Library General Public License 17 * along with this library; see the file COPYING.LIB. If not, write to 18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 19 * Boston, MA 02110-1301, USA. 20 * 21 */ 22 23 #include "config.h" 24 25 #include "core/platform/text/BidiResolver.h" 26 #include "core/rendering/InlineIterator.h" 27 #include "core/rendering/InlineTextBox.h" 28 #include "core/rendering/RenderCombineText.h" 29 #include "core/rendering/RenderCounter.h" 30 #include "core/rendering/RenderFlowThread.h" 31 #include "core/rendering/RenderInline.h" 32 #include "core/rendering/RenderLayer.h" 33 #include "core/rendering/RenderListMarker.h" 34 #include "core/rendering/RenderRegion.h" 35 #include "core/rendering/RenderRubyRun.h" 36 #include "core/rendering/RenderView.h" 37 #include "core/rendering/TrailingFloatsRootInlineBox.h" 38 #include "core/rendering/VerticalPositionCache.h" 39 #include "core/rendering/break_lines.h" 40 #include "core/rendering/shapes/ShapeInsideInfo.h" 41 #include "core/rendering/svg/RenderSVGInlineText.h" 42 #include "core/rendering/svg/SVGRootInlineBox.h" 43 #include "wtf/RefCountedLeakCounter.h" 44 #include "wtf/StdLibExtras.h" 45 #include "wtf/Vector.h" 46 #include "wtf/unicode/CharacterNames.h" 47 48 using namespace std; 49 using namespace WTF; 50 using namespace Unicode; 51 52 namespace WebCore { 53 54 // We don't let our line box tree for a single line get any deeper than this. 55 const unsigned cMaxLineDepth = 200; 56 57 static LayoutUnit logicalHeightForLine(const RenderBlock* block, bool isFirstLine, LayoutUnit replacedHeight = 0) 58 { 59 if (!block->document()->inNoQuirksMode() && replacedHeight) 60 return replacedHeight; 61 62 if (!(block->style(isFirstLine)->lineBoxContain() & LineBoxContainBlock)) 63 return 0; 64 65 return max<LayoutUnit>(replacedHeight, block->lineHeight(isFirstLine, block->isHorizontalWritingMode() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes)); 66 } 67 68 ShapeInsideInfo* RenderBlock::layoutShapeInsideInfo() const 69 { 70 ShapeInsideInfo* shapeInsideInfo = view()->layoutState()->shapeInsideInfo(); 71 72 if (!shapeInsideInfo && flowThreadContainingBlock() && allowsShapeInsideInfoSharing()) { 73 // regionAtBlockOffset returns regions like an array first={0,N-1}, second={N,M-1}, ... 74 LayoutUnit offset = logicalHeight() + logicalHeightForLine(this, false) - LayoutUnit(1); 75 RenderRegion* region = regionAtBlockOffset(offset); 76 if (region) 77 shapeInsideInfo = region->shapeInsideInfo(); 78 } 79 80 return shapeInsideInfo; 81 } 82 83 enum IndentTextOrNot { DoNotIndentText, IndentText }; 84 85 class LineWidth { 86 public: 87 LineWidth(RenderBlock* block, bool isFirstLine, IndentTextOrNot shouldIndentText) 88 : m_block(block) 89 , m_uncommittedWidth(0) 90 , m_committedWidth(0) 91 , m_overhangWidth(0) 92 , m_left(0) 93 , m_right(0) 94 , m_availableWidth(0) 95 , m_segment(0) 96 , m_isFirstLine(isFirstLine) 97 , m_shouldIndentText(shouldIndentText) 98 { 99 ASSERT(block); 100 ShapeInsideInfo* shapeInsideInfo = m_block->layoutShapeInsideInfo(); 101 if (shapeInsideInfo) 102 m_segment = shapeInsideInfo->currentSegment(); 103 updateAvailableWidth(); 104 } 105 bool fitsOnLine() const { return currentWidth() <= m_availableWidth; } 106 bool fitsOnLine(float extra) const { return currentWidth() + extra <= m_availableWidth; } 107 float currentWidth() const { return m_committedWidth + m_uncommittedWidth; } 108 109 // FIXME: We should eventually replace these three functions by ones that work on a higher abstraction. 110 float uncommittedWidth() const { return m_uncommittedWidth; } 111 float committedWidth() const { return m_committedWidth; } 112 float availableWidth() const { return m_availableWidth; } 113 114 void updateAvailableWidth(LayoutUnit minimumHeight = 0); 115 void shrinkAvailableWidthForNewFloatIfNeeded(RenderBlock::FloatingObject*); 116 void addUncommittedWidth(float delta) { m_uncommittedWidth += delta; } 117 void commit() 118 { 119 m_committedWidth += m_uncommittedWidth; 120 m_uncommittedWidth = 0; 121 } 122 void applyOverhang(RenderRubyRun*, RenderObject* startRenderer, RenderObject* endRenderer); 123 void fitBelowFloats(); 124 125 bool shouldIndentText() const { return m_shouldIndentText == IndentText; } 126 127 private: 128 void computeAvailableWidthFromLeftAndRight() 129 { 130 m_availableWidth = max(0.0f, m_right - m_left) + m_overhangWidth; 131 } 132 133 private: 134 RenderBlock* m_block; 135 float m_uncommittedWidth; 136 float m_committedWidth; 137 float m_overhangWidth; // The amount by which |m_availableWidth| has been inflated to account for possible contraction due to ruby overhang. 138 float m_left; 139 float m_right; 140 float m_availableWidth; 141 const LineSegment* m_segment; 142 bool m_isFirstLine; 143 IndentTextOrNot m_shouldIndentText; 144 }; 145 146 inline void LineWidth::updateAvailableWidth(LayoutUnit replacedHeight) 147 { 148 LayoutUnit height = m_block->logicalHeight(); 149 LayoutUnit logicalHeight = logicalHeightForLine(m_block, m_isFirstLine, replacedHeight); 150 m_left = m_block->logicalLeftOffsetForLine(height, shouldIndentText(), logicalHeight); 151 m_right = m_block->logicalRightOffsetForLine(height, shouldIndentText(), logicalHeight); 152 153 if (m_segment) { 154 m_left = max<float>(m_segment->logicalLeft, m_left); 155 m_right = min<float>(m_segment->logicalRight, m_right); 156 } 157 158 computeAvailableWidthFromLeftAndRight(); 159 } 160 161 inline void LineWidth::shrinkAvailableWidthForNewFloatIfNeeded(RenderBlock::FloatingObject* newFloat) 162 { 163 LayoutUnit height = m_block->logicalHeight(); 164 if (height < m_block->logicalTopForFloat(newFloat) || height >= m_block->logicalBottomForFloat(newFloat)) 165 return; 166 167 // When floats with shape outside are stacked, the floats are positioned based on the margin box of the float, 168 // not the shape's contour. Since we computed the width based on the shape contour when we added the float, 169 // when we add a subsequent float on the same line, we need to undo the shape delta in order to position 170 // based on the margin box. In order to do this, we need to walk back through the floating object list to find 171 // the first previous float that is on the same side as our newFloat. 172 ShapeOutsideInfo* previousShapeOutsideInfo = 0; 173 const RenderBlock::FloatingObjectSet& floatingObjectSet = m_block->m_floatingObjects->set(); 174 RenderBlock::FloatingObjectSetIterator it = floatingObjectSet.end(); 175 RenderBlock::FloatingObjectSetIterator begin = floatingObjectSet.begin(); 176 while (it != begin) { 177 --it; 178 RenderBlock::FloatingObject* previousFloat = *it; 179 if (previousFloat != newFloat && previousFloat->type() == newFloat->type()) { 180 previousShapeOutsideInfo = previousFloat->renderer()->shapeOutsideInfo(); 181 if (previousShapeOutsideInfo) { 182 previousShapeOutsideInfo->computeSegmentsForContainingBlockLine(m_block->logicalHeight(), m_block->logicalTopForFloat(previousFloat), logicalHeightForLine(m_block, m_isFirstLine)); 183 } 184 break; 185 } 186 } 187 188 ShapeOutsideInfo* shapeOutsideInfo = newFloat->renderer()->shapeOutsideInfo(); 189 if (shapeOutsideInfo) 190 shapeOutsideInfo->computeSegmentsForContainingBlockLine(m_block->logicalHeight(), m_block->logicalTopForFloat(newFloat), logicalHeightForLine(m_block, m_isFirstLine)); 191 192 if (newFloat->type() == RenderBlock::FloatingObject::FloatLeft) { 193 float newLeft = m_block->logicalRightForFloat(newFloat); 194 if (previousShapeOutsideInfo) 195 newLeft -= previousShapeOutsideInfo->rightSegmentMarginBoxDelta(); 196 if (shapeOutsideInfo) 197 newLeft += shapeOutsideInfo->rightSegmentMarginBoxDelta(); 198 199 if (shouldIndentText() && m_block->style()->isLeftToRightDirection()) 200 newLeft += floorToInt(m_block->textIndentOffset()); 201 m_left = max<float>(m_left, newLeft); 202 } else { 203 float newRight = m_block->logicalLeftForFloat(newFloat); 204 if (previousShapeOutsideInfo) 205 newRight -= previousShapeOutsideInfo->leftSegmentMarginBoxDelta(); 206 if (shapeOutsideInfo) 207 newRight += shapeOutsideInfo->leftSegmentMarginBoxDelta(); 208 209 if (shouldIndentText() && !m_block->style()->isLeftToRightDirection()) 210 newRight -= floorToInt(m_block->textIndentOffset()); 211 m_right = min<float>(m_right, newRight); 212 } 213 214 computeAvailableWidthFromLeftAndRight(); 215 } 216 217 void LineWidth::applyOverhang(RenderRubyRun* rubyRun, RenderObject* startRenderer, RenderObject* endRenderer) 218 { 219 int startOverhang; 220 int endOverhang; 221 rubyRun->getOverhang(m_isFirstLine, startRenderer, endRenderer, startOverhang, endOverhang); 222 223 startOverhang = min<int>(startOverhang, m_committedWidth); 224 m_availableWidth += startOverhang; 225 226 endOverhang = max(min<int>(endOverhang, m_availableWidth - currentWidth()), 0); 227 m_availableWidth += endOverhang; 228 m_overhangWidth += startOverhang + endOverhang; 229 } 230 231 void LineWidth::fitBelowFloats() 232 { 233 ASSERT(!m_committedWidth); 234 ASSERT(!fitsOnLine()); 235 236 LayoutUnit floatLogicalBottom; 237 LayoutUnit lastFloatLogicalBottom = m_block->logicalHeight(); 238 float newLineWidth = m_availableWidth; 239 float newLineLeft = m_left; 240 float newLineRight = m_right; 241 while (true) { 242 floatLogicalBottom = m_block->nextFloatLogicalBottomBelow(lastFloatLogicalBottom); 243 if (floatLogicalBottom <= lastFloatLogicalBottom) 244 break; 245 246 newLineLeft = m_block->logicalLeftOffsetForLine(floatLogicalBottom, shouldIndentText()); 247 newLineRight = m_block->logicalRightOffsetForLine(floatLogicalBottom, shouldIndentText()); 248 newLineWidth = max(0.0f, newLineRight - newLineLeft); 249 lastFloatLogicalBottom = floatLogicalBottom; 250 if (newLineWidth >= m_uncommittedWidth) 251 break; 252 } 253 254 if (newLineWidth > m_availableWidth) { 255 m_block->setLogicalHeight(lastFloatLogicalBottom); 256 m_availableWidth = newLineWidth + m_overhangWidth; 257 m_left = newLineLeft; 258 m_right = newLineRight; 259 } 260 } 261 262 class LineInfo { 263 public: 264 LineInfo() 265 : m_isFirstLine(true) 266 , m_isLastLine(false) 267 , m_isEmpty(true) 268 , m_previousLineBrokeCleanly(true) 269 , m_floatPaginationStrut(0) 270 , m_runsFromLeadingWhitespace(0) 271 { } 272 273 bool isFirstLine() const { return m_isFirstLine; } 274 bool isLastLine() const { return m_isLastLine; } 275 bool isEmpty() const { return m_isEmpty; } 276 bool previousLineBrokeCleanly() const { return m_previousLineBrokeCleanly; } 277 LayoutUnit floatPaginationStrut() const { return m_floatPaginationStrut; } 278 unsigned runsFromLeadingWhitespace() const { return m_runsFromLeadingWhitespace; } 279 void resetRunsFromLeadingWhitespace() { m_runsFromLeadingWhitespace = 0; } 280 void incrementRunsFromLeadingWhitespace() { m_runsFromLeadingWhitespace++; } 281 282 void setFirstLine(bool firstLine) { m_isFirstLine = firstLine; } 283 void setLastLine(bool lastLine) { m_isLastLine = lastLine; } 284 void setEmpty(bool empty, RenderBlock* block = 0, LineWidth* lineWidth = 0) 285 { 286 if (m_isEmpty == empty) 287 return; 288 m_isEmpty = empty; 289 if (!empty && block && floatPaginationStrut()) { 290 block->setLogicalHeight(block->logicalHeight() + floatPaginationStrut()); 291 setFloatPaginationStrut(0); 292 lineWidth->updateAvailableWidth(); 293 } 294 } 295 296 void setPreviousLineBrokeCleanly(bool previousLineBrokeCleanly) { m_previousLineBrokeCleanly = previousLineBrokeCleanly; } 297 void setFloatPaginationStrut(LayoutUnit strut) { m_floatPaginationStrut = strut; } 298 299 private: 300 bool m_isFirstLine; 301 bool m_isLastLine; 302 bool m_isEmpty; 303 bool m_previousLineBrokeCleanly; 304 LayoutUnit m_floatPaginationStrut; 305 unsigned m_runsFromLeadingWhitespace; 306 }; 307 308 static inline LayoutUnit borderPaddingMarginStart(RenderInline* child) 309 { 310 return child->marginStart() + child->paddingStart() + child->borderStart(); 311 } 312 313 static inline LayoutUnit borderPaddingMarginEnd(RenderInline* child) 314 { 315 return child->marginEnd() + child->paddingEnd() + child->borderEnd(); 316 } 317 318 static bool shouldAddBorderPaddingMargin(RenderObject* child, bool &checkSide) 319 { 320 if (!child || (child->isText() && !toRenderText(child)->textLength())) 321 return true; 322 checkSide = false; 323 return checkSide; 324 } 325 326 static LayoutUnit inlineLogicalWidth(RenderObject* child, bool start = true, bool end = true) 327 { 328 unsigned lineDepth = 1; 329 LayoutUnit extraWidth = 0; 330 RenderObject* parent = child->parent(); 331 while (parent->isRenderInline() && lineDepth++ < cMaxLineDepth) { 332 RenderInline* parentAsRenderInline = toRenderInline(parent); 333 if (!isEmptyInline(parentAsRenderInline)) { 334 if (start && shouldAddBorderPaddingMargin(child->previousSibling(), start)) 335 extraWidth += borderPaddingMarginStart(parentAsRenderInline); 336 if (end && shouldAddBorderPaddingMargin(child->nextSibling(), end)) 337 extraWidth += borderPaddingMarginEnd(parentAsRenderInline); 338 if (!start && !end) 339 return extraWidth; 340 } 341 child = parent; 342 parent = child->parent(); 343 } 344 return extraWidth; 345 } 346 347 static void determineDirectionality(TextDirection& dir, InlineIterator iter) 348 { 349 while (!iter.atEnd()) { 350 if (iter.atParagraphSeparator()) 351 return; 352 if (UChar current = iter.current()) { 353 Direction charDirection = direction(current); 354 if (charDirection == LeftToRight) { 355 dir = LTR; 356 return; 357 } 358 if (charDirection == RightToLeft || charDirection == RightToLeftArabic) { 359 dir = RTL; 360 return; 361 } 362 } 363 iter.increment(); 364 } 365 } 366 367 static void checkMidpoints(LineMidpointState& lineMidpointState, InlineIterator& lBreak) 368 { 369 // Check to see if our last midpoint is a start point beyond the line break. If so, 370 // shave it off the list, and shave off a trailing space if the previous end point doesn't 371 // preserve whitespace. 372 if (lBreak.m_obj && lineMidpointState.numMidpoints && !(lineMidpointState.numMidpoints % 2)) { 373 InlineIterator* midpoints = lineMidpointState.midpoints.data(); 374 InlineIterator& endpoint = midpoints[lineMidpointState.numMidpoints - 2]; 375 const InlineIterator& startpoint = midpoints[lineMidpointState.numMidpoints - 1]; 376 InlineIterator currpoint = endpoint; 377 while (!currpoint.atEnd() && currpoint != startpoint && currpoint != lBreak) 378 currpoint.increment(); 379 if (currpoint == lBreak) { 380 // We hit the line break before the start point. Shave off the start point. 381 lineMidpointState.numMidpoints--; 382 if (endpoint.m_obj->style()->collapseWhiteSpace()) 383 endpoint.m_pos--; 384 } 385 } 386 } 387 388 // Don't call this directly. Use one of the descriptive helper functions below. 389 static void deprecatedAddMidpoint(LineMidpointState& lineMidpointState, const InlineIterator& midpoint) 390 { 391 if (lineMidpointState.midpoints.size() <= lineMidpointState.numMidpoints) 392 lineMidpointState.midpoints.grow(lineMidpointState.numMidpoints + 10); 393 394 InlineIterator* midpoints = lineMidpointState.midpoints.data(); 395 midpoints[lineMidpointState.numMidpoints++] = midpoint; 396 } 397 398 static inline void startIgnoringSpaces(LineMidpointState& lineMidpointState, const InlineIterator& midpoint) 399 { 400 ASSERT(!(lineMidpointState.numMidpoints % 2)); 401 deprecatedAddMidpoint(lineMidpointState, midpoint); 402 } 403 404 static inline void stopIgnoringSpaces(LineMidpointState& lineMidpointState, const InlineIterator& midpoint) 405 { 406 ASSERT(lineMidpointState.numMidpoints % 2); 407 deprecatedAddMidpoint(lineMidpointState, midpoint); 408 } 409 410 // When ignoring spaces, this needs to be called for objects that need line boxes such as RenderInlines or 411 // hard line breaks to ensure that they're not ignored. 412 static inline void ensureLineBoxInsideIgnoredSpaces(LineMidpointState& lineMidpointState, RenderObject* renderer) 413 { 414 InlineIterator midpoint(0, renderer, 0); 415 stopIgnoringSpaces(lineMidpointState, midpoint); 416 startIgnoringSpaces(lineMidpointState, midpoint); 417 } 418 419 // Adding a pair of midpoints before a character will split it out into a new line box. 420 static inline void ensureCharacterGetsLineBox(LineMidpointState& lineMidpointState, InlineIterator& textParagraphSeparator) 421 { 422 InlineIterator midpoint(0, textParagraphSeparator.m_obj, textParagraphSeparator.m_pos); 423 startIgnoringSpaces(lineMidpointState, InlineIterator(0, textParagraphSeparator.m_obj, textParagraphSeparator.m_pos - 1)); 424 stopIgnoringSpaces(lineMidpointState, InlineIterator(0, textParagraphSeparator.m_obj, textParagraphSeparator.m_pos)); 425 } 426 427 static inline BidiRun* createRun(int start, int end, RenderObject* obj, InlineBidiResolver& resolver) 428 { 429 return new BidiRun(start, end, obj, resolver.context(), resolver.dir()); 430 } 431 432 void RenderBlock::appendRunsForObject(BidiRunList<BidiRun>& runs, int start, int end, RenderObject* obj, InlineBidiResolver& resolver) 433 { 434 if (start > end || shouldSkipCreatingRunsForObject(obj)) 435 return; 436 437 LineMidpointState& lineMidpointState = resolver.midpointState(); 438 bool haveNextMidpoint = (lineMidpointState.currentMidpoint < lineMidpointState.numMidpoints); 439 InlineIterator nextMidpoint; 440 if (haveNextMidpoint) 441 nextMidpoint = lineMidpointState.midpoints[lineMidpointState.currentMidpoint]; 442 if (lineMidpointState.betweenMidpoints) { 443 if (!(haveNextMidpoint && nextMidpoint.m_obj == obj)) 444 return; 445 // This is a new start point. Stop ignoring objects and 446 // adjust our start. 447 lineMidpointState.betweenMidpoints = false; 448 start = nextMidpoint.m_pos; 449 lineMidpointState.currentMidpoint++; 450 if (start < end) 451 return appendRunsForObject(runs, start, end, obj, resolver); 452 } else { 453 if (!haveNextMidpoint || (obj != nextMidpoint.m_obj)) { 454 runs.addRun(createRun(start, end, obj, resolver)); 455 return; 456 } 457 458 // An end midpoint has been encountered within our object. We 459 // need to go ahead and append a run with our endpoint. 460 if (static_cast<int>(nextMidpoint.m_pos + 1) <= end) { 461 lineMidpointState.betweenMidpoints = true; 462 lineMidpointState.currentMidpoint++; 463 if (nextMidpoint.m_pos != INT_MAX) { // INT_MAX means stop at the object and don't include any of it. 464 if (static_cast<int>(nextMidpoint.m_pos + 1) > start) 465 runs.addRun(createRun(start, nextMidpoint.m_pos + 1, obj, resolver)); 466 return appendRunsForObject(runs, nextMidpoint.m_pos + 1, end, obj, resolver); 467 } 468 } else 469 runs.addRun(createRun(start, end, obj, resolver)); 470 } 471 } 472 473 static inline InlineBox* createInlineBoxForRenderer(RenderObject* obj, bool isRootLineBox, bool isOnlyRun = false) 474 { 475 if (isRootLineBox) 476 return toRenderBlock(obj)->createAndAppendRootInlineBox(); 477 478 if (obj->isText()) { 479 InlineTextBox* textBox = toRenderText(obj)->createInlineTextBox(); 480 // We only treat a box as text for a <br> if we are on a line by ourself or in strict mode 481 // (Note the use of strict mode. In "almost strict" mode, we don't treat the box for <br> as text.) 482 if (obj->isBR()) 483 textBox->setIsText(isOnlyRun || obj->document()->inNoQuirksMode()); 484 return textBox; 485 } 486 487 if (obj->isBox()) 488 return toRenderBox(obj)->createInlineBox(); 489 490 return toRenderInline(obj)->createAndAppendInlineFlowBox(); 491 } 492 493 // FIXME: Don't let counters mark themselves as needing pref width recalcs during layout 494 // so we don't need this hack. 495 static inline void updateCounterIfNeeded(RenderText* o) 496 { 497 if (!o->preferredLogicalWidthsDirty() || !o->isCounter()) 498 return; 499 toRenderCounter(o)->updateCounter(); 500 } 501 502 static inline void dirtyLineBoxesForRenderer(RenderObject* o, bool fullLayout) 503 { 504 if (o->isText()) { 505 RenderText* renderText = toRenderText(o); 506 updateCounterIfNeeded(renderText); 507 renderText->dirtyLineBoxes(fullLayout); 508 } else 509 toRenderInline(o)->dirtyLineBoxes(fullLayout); 510 } 511 512 static bool parentIsConstructedOrHaveNext(InlineFlowBox* parentBox) 513 { 514 do { 515 if (parentBox->isConstructed() || parentBox->nextOnLine()) 516 return true; 517 parentBox = parentBox->parent(); 518 } while (parentBox); 519 return false; 520 } 521 522 InlineFlowBox* RenderBlock::createLineBoxes(RenderObject* obj, const LineInfo& lineInfo, InlineBox* childBox, bool startNewSegment) 523 { 524 // See if we have an unconstructed line box for this object that is also 525 // the last item on the line. 526 unsigned lineDepth = 1; 527 InlineFlowBox* parentBox = 0; 528 InlineFlowBox* result = 0; 529 bool hasDefaultLineBoxContain = style()->lineBoxContain() == RenderStyle::initialLineBoxContain(); 530 do { 531 ASSERT_WITH_SECURITY_IMPLICATION(obj->isRenderInline() || obj == this); 532 533 RenderInline* inlineFlow = (obj != this) ? toRenderInline(obj) : 0; 534 535 // Get the last box we made for this render object. 536 parentBox = inlineFlow ? inlineFlow->lastLineBox() : toRenderBlock(obj)->lastLineBox(); 537 538 // If this box or its ancestor is constructed then it is from a previous line, and we need 539 // to make a new box for our line. If this box or its ancestor is unconstructed but it has 540 // something following it on the line, then we know we have to make a new box 541 // as well. In this situation our inline has actually been split in two on 542 // the same line (this can happen with very fancy language mixtures). 543 bool constructedNewBox = false; 544 bool allowedToConstructNewBox = !hasDefaultLineBoxContain || !inlineFlow || inlineFlow->alwaysCreateLineBoxes(); 545 bool mustCreateBoxesToRoot = startNewSegment && !(parentBox && parentBox->isRootInlineBox()); 546 bool canUseExistingParentBox = parentBox && !parentIsConstructedOrHaveNext(parentBox) && !mustCreateBoxesToRoot; 547 if (allowedToConstructNewBox && !canUseExistingParentBox) { 548 // We need to make a new box for this render object. Once 549 // made, we need to place it at the end of the current line. 550 InlineBox* newBox = createInlineBoxForRenderer(obj, obj == this); 551 ASSERT_WITH_SECURITY_IMPLICATION(newBox->isInlineFlowBox()); 552 parentBox = toInlineFlowBox(newBox); 553 parentBox->setFirstLineStyleBit(lineInfo.isFirstLine()); 554 parentBox->setIsHorizontal(isHorizontalWritingMode()); 555 if (!hasDefaultLineBoxContain) 556 parentBox->clearDescendantsHaveSameLineHeightAndBaseline(); 557 constructedNewBox = true; 558 } 559 560 if (constructedNewBox || canUseExistingParentBox) { 561 if (!result) 562 result = parentBox; 563 564 // If we have hit the block itself, then |box| represents the root 565 // inline box for the line, and it doesn't have to be appended to any parent 566 // inline. 567 if (childBox) 568 parentBox->addToLine(childBox); 569 570 if (!constructedNewBox || obj == this) 571 break; 572 573 childBox = parentBox; 574 } 575 576 // If we've exceeded our line depth, then jump straight to the root and skip all the remaining 577 // intermediate inline flows. 578 obj = (++lineDepth >= cMaxLineDepth) ? this : obj->parent(); 579 580 } while (true); 581 582 return result; 583 } 584 585 template <typename CharacterType> 586 static inline bool endsWithASCIISpaces(const CharacterType* characters, unsigned pos, unsigned end) 587 { 588 while (isASCIISpace(characters[pos])) { 589 pos++; 590 if (pos >= end) 591 return true; 592 } 593 return false; 594 } 595 596 static bool reachedEndOfTextRenderer(const BidiRunList<BidiRun>& bidiRuns) 597 { 598 BidiRun* run = bidiRuns.logicallyLastRun(); 599 if (!run) 600 return true; 601 unsigned pos = run->stop(); 602 RenderObject* r = run->m_object; 603 if (!r->isText() || r->isBR()) 604 return false; 605 RenderText* renderText = toRenderText(r); 606 unsigned length = renderText->textLength(); 607 if (pos >= length) 608 return true; 609 610 if (renderText->is8Bit()) 611 return endsWithASCIISpaces(renderText->characters8(), pos, length); 612 return endsWithASCIISpaces(renderText->characters16(), pos, length); 613 } 614 615 RootInlineBox* RenderBlock::constructLine(BidiRunList<BidiRun>& bidiRuns, const LineInfo& lineInfo) 616 { 617 ASSERT(bidiRuns.firstRun()); 618 619 bool rootHasSelectedChildren = false; 620 InlineFlowBox* parentBox = 0; 621 int runCount = bidiRuns.runCount() - lineInfo.runsFromLeadingWhitespace(); 622 for (BidiRun* r = bidiRuns.firstRun(); r; r = r->next()) { 623 // Create a box for our object. 624 bool isOnlyRun = (runCount == 1); 625 if (runCount == 2 && !r->m_object->isListMarker()) 626 isOnlyRun = (!style()->isLeftToRightDirection() ? bidiRuns.lastRun() : bidiRuns.firstRun())->m_object->isListMarker(); 627 628 if (lineInfo.isEmpty()) 629 continue; 630 631 InlineBox* box = createInlineBoxForRenderer(r->m_object, false, isOnlyRun); 632 r->m_box = box; 633 634 ASSERT(box); 635 if (!box) 636 continue; 637 638 if (!rootHasSelectedChildren && box->renderer()->selectionState() != RenderObject::SelectionNone) 639 rootHasSelectedChildren = true; 640 641 // If we have no parent box yet, or if the run is not simply a sibling, 642 // then we need to construct inline boxes as necessary to properly enclose the 643 // run's inline box. Segments can only be siblings at the root level, as 644 // they are positioned separately. 645 bool runStartsSegment = r->m_startsSegment; 646 647 if (!parentBox || parentBox->renderer() != r->m_object->parent() || runStartsSegment) 648 // Create new inline boxes all the way back to the appropriate insertion point. 649 parentBox = createLineBoxes(r->m_object->parent(), lineInfo, box, runStartsSegment); 650 else { 651 // Append the inline box to this line. 652 parentBox->addToLine(box); 653 } 654 655 bool visuallyOrdered = r->m_object->style()->rtlOrdering() == VisualOrder; 656 box->setBidiLevel(r->level()); 657 658 if (box->isInlineTextBox()) { 659 InlineTextBox* text = toInlineTextBox(box); 660 text->setStart(r->m_start); 661 text->setLen(r->m_stop - r->m_start); 662 text->setDirOverride(r->dirOverride(visuallyOrdered)); 663 if (r->m_hasHyphen) 664 text->setHasHyphen(true); 665 } 666 } 667 668 // We should have a root inline box. It should be unconstructed and 669 // be the last continuation of our line list. 670 ASSERT(lastLineBox() && !lastLineBox()->isConstructed()); 671 672 // Set the m_selectedChildren flag on the root inline box if one of the leaf inline box 673 // from the bidi runs walk above has a selection state. 674 if (rootHasSelectedChildren) 675 lastLineBox()->root()->setHasSelectedChildren(true); 676 677 // Set bits on our inline flow boxes that indicate which sides should 678 // paint borders/margins/padding. This knowledge will ultimately be used when 679 // we determine the horizontal positions and widths of all the inline boxes on 680 // the line. 681 bool isLogicallyLastRunWrapped = bidiRuns.logicallyLastRun()->m_object && bidiRuns.logicallyLastRun()->m_object->isText() ? !reachedEndOfTextRenderer(bidiRuns) : true; 682 lastLineBox()->determineSpacingForFlowBoxes(lineInfo.isLastLine(), isLogicallyLastRunWrapped, bidiRuns.logicallyLastRun()->m_object); 683 684 // Now mark the line boxes as being constructed. 685 lastLineBox()->setConstructed(); 686 687 // Return the last line. 688 return lastRootBox(); 689 } 690 691 ETextAlign RenderBlock::textAlignmentForLine(bool endsWithSoftBreak) const 692 { 693 ETextAlign alignment = style()->textAlign(); 694 if (!endsWithSoftBreak && alignment == JUSTIFY) 695 alignment = TASTART; 696 697 return alignment; 698 } 699 700 static void updateLogicalWidthForLeftAlignedBlock(bool isLeftToRightDirection, BidiRun* trailingSpaceRun, float& logicalLeft, float& totalLogicalWidth, float availableLogicalWidth) 701 { 702 // The direction of the block should determine what happens with wide lines. 703 // In particular with RTL blocks, wide lines should still spill out to the left. 704 if (isLeftToRightDirection) { 705 if (totalLogicalWidth > availableLogicalWidth && trailingSpaceRun) 706 trailingSpaceRun->m_box->setLogicalWidth(max<float>(0, trailingSpaceRun->m_box->logicalWidth() - totalLogicalWidth + availableLogicalWidth)); 707 return; 708 } 709 710 if (trailingSpaceRun) 711 trailingSpaceRun->m_box->setLogicalWidth(0); 712 else if (totalLogicalWidth > availableLogicalWidth) 713 logicalLeft -= (totalLogicalWidth - availableLogicalWidth); 714 } 715 716 static void updateLogicalWidthForRightAlignedBlock(bool isLeftToRightDirection, BidiRun* trailingSpaceRun, float& logicalLeft, float& totalLogicalWidth, float availableLogicalWidth) 717 { 718 // Wide lines spill out of the block based off direction. 719 // So even if text-align is right, if direction is LTR, wide lines should overflow out of the right 720 // side of the block. 721 if (isLeftToRightDirection) { 722 if (trailingSpaceRun) { 723 totalLogicalWidth -= trailingSpaceRun->m_box->logicalWidth(); 724 trailingSpaceRun->m_box->setLogicalWidth(0); 725 } 726 if (totalLogicalWidth < availableLogicalWidth) 727 logicalLeft += availableLogicalWidth - totalLogicalWidth; 728 return; 729 } 730 731 if (totalLogicalWidth > availableLogicalWidth && trailingSpaceRun) { 732 trailingSpaceRun->m_box->setLogicalWidth(max<float>(0, trailingSpaceRun->m_box->logicalWidth() - totalLogicalWidth + availableLogicalWidth)); 733 totalLogicalWidth -= trailingSpaceRun->m_box->logicalWidth(); 734 } else 735 logicalLeft += availableLogicalWidth - totalLogicalWidth; 736 } 737 738 static void updateLogicalWidthForCenterAlignedBlock(bool isLeftToRightDirection, BidiRun* trailingSpaceRun, float& logicalLeft, float& totalLogicalWidth, float availableLogicalWidth) 739 { 740 float trailingSpaceWidth = 0; 741 if (trailingSpaceRun) { 742 totalLogicalWidth -= trailingSpaceRun->m_box->logicalWidth(); 743 trailingSpaceWidth = min(trailingSpaceRun->m_box->logicalWidth(), (availableLogicalWidth - totalLogicalWidth + 1) / 2); 744 trailingSpaceRun->m_box->setLogicalWidth(max<float>(0, trailingSpaceWidth)); 745 } 746 if (isLeftToRightDirection) 747 logicalLeft += max<float>((availableLogicalWidth - totalLogicalWidth) / 2, 0); 748 else 749 logicalLeft += totalLogicalWidth > availableLogicalWidth ? (availableLogicalWidth - totalLogicalWidth) : (availableLogicalWidth - totalLogicalWidth) / 2 - trailingSpaceWidth; 750 } 751 752 void RenderBlock::setMarginsForRubyRun(BidiRun* run, RenderRubyRun* renderer, RenderObject* previousObject, const LineInfo& lineInfo) 753 { 754 int startOverhang; 755 int endOverhang; 756 RenderObject* nextObject = 0; 757 for (BidiRun* runWithNextObject = run->next(); runWithNextObject; runWithNextObject = runWithNextObject->next()) { 758 if (!runWithNextObject->m_object->isOutOfFlowPositioned() && !runWithNextObject->m_box->isLineBreak()) { 759 nextObject = runWithNextObject->m_object; 760 break; 761 } 762 } 763 renderer->getOverhang(lineInfo.isFirstLine(), renderer->style()->isLeftToRightDirection() ? previousObject : nextObject, renderer->style()->isLeftToRightDirection() ? nextObject : previousObject, startOverhang, endOverhang); 764 setMarginStartForChild(renderer, -startOverhang); 765 setMarginEndForChild(renderer, -endOverhang); 766 } 767 768 static inline float measureHyphenWidth(RenderText* renderer, const Font& font) 769 { 770 RenderStyle* style = renderer->style(); 771 return font.width(RenderBlock::constructTextRun(renderer, font, style->hyphenString().string(), style)); 772 } 773 774 class WordMeasurement { 775 public: 776 WordMeasurement() 777 : renderer(0) 778 , width(0) 779 , startOffset(0) 780 , endOffset(0) 781 { 782 } 783 784 RenderText* renderer; 785 float width; 786 int startOffset; 787 int endOffset; 788 HashSet<const SimpleFontData*> fallbackFonts; 789 }; 790 791 static inline void setLogicalWidthForTextRun(RootInlineBox* lineBox, BidiRun* run, RenderText* renderer, float xPos, const LineInfo& lineInfo, 792 GlyphOverflowAndFallbackFontsMap& textBoxDataMap, VerticalPositionCache& verticalPositionCache, WordMeasurements& wordMeasurements) 793 { 794 HashSet<const SimpleFontData*> fallbackFonts; 795 GlyphOverflow glyphOverflow; 796 797 const Font& font = renderer->style(lineInfo.isFirstLine())->font(); 798 // Always compute glyph overflow if the block's line-box-contain value is "glyphs". 799 if (lineBox->fitsToGlyphs()) { 800 // If we don't stick out of the root line's font box, then don't bother computing our glyph overflow. This optimization 801 // will keep us from computing glyph bounds in nearly all cases. 802 bool includeRootLine = lineBox->includesRootLineBoxFontOrLeading(); 803 int baselineShift = lineBox->verticalPositionForBox(run->m_box, verticalPositionCache); 804 int rootDescent = includeRootLine ? font.fontMetrics().descent() : 0; 805 int rootAscent = includeRootLine ? font.fontMetrics().ascent() : 0; 806 int boxAscent = font.fontMetrics().ascent() - baselineShift; 807 int boxDescent = font.fontMetrics().descent() + baselineShift; 808 if (boxAscent > rootDescent || boxDescent > rootAscent) 809 glyphOverflow.computeBounds = true; 810 } 811 812 LayoutUnit hyphenWidth = 0; 813 if (toInlineTextBox(run->m_box)->hasHyphen()) { 814 const Font& font = renderer->style(lineInfo.isFirstLine())->font(); 815 hyphenWidth = measureHyphenWidth(renderer, font); 816 } 817 float measuredWidth = 0; 818 819 bool kerningIsEnabled = font.typesettingFeatures() & Kerning; 820 821 #if OS(DARWIN) 822 // FIXME: Having any font feature settings enabled can lead to selection gaps on 823 // Chromium-mac. https://bugs.webkit.org/show_bug.cgi?id=113418 824 bool canUseSimpleFontCodePath = renderer->canUseSimpleFontCodePath() && !font.fontDescription().featureSettings(); 825 #else 826 bool canUseSimpleFontCodePath = renderer->canUseSimpleFontCodePath(); 827 #endif 828 829 // Since we don't cache glyph overflows, we need to re-measure the run if 830 // the style is linebox-contain: glyph. 831 832 if (!lineBox->fitsToGlyphs() && canUseSimpleFontCodePath) { 833 int lastEndOffset = run->m_start; 834 for (size_t i = 0, size = wordMeasurements.size(); i < size && lastEndOffset < run->m_stop; ++i) { 835 const WordMeasurement& wordMeasurement = wordMeasurements[i]; 836 if (wordMeasurement.width <=0 || wordMeasurement.startOffset == wordMeasurement.endOffset) 837 continue; 838 if (wordMeasurement.renderer != renderer || wordMeasurement.startOffset != lastEndOffset || wordMeasurement.endOffset > run->m_stop) 839 continue; 840 841 lastEndOffset = wordMeasurement.endOffset; 842 if (kerningIsEnabled && lastEndOffset == run->m_stop) { 843 int wordLength = lastEndOffset - wordMeasurement.startOffset; 844 measuredWidth += renderer->width(wordMeasurement.startOffset, wordLength, xPos, lineInfo.isFirstLine()); 845 if (i > 0 && wordLength == 1 && renderer->characterAt(wordMeasurement.startOffset) == ' ') 846 measuredWidth += renderer->style()->wordSpacing(); 847 } else 848 measuredWidth += wordMeasurement.width; 849 if (!wordMeasurement.fallbackFonts.isEmpty()) { 850 HashSet<const SimpleFontData*>::const_iterator end = wordMeasurement.fallbackFonts.end(); 851 for (HashSet<const SimpleFontData*>::const_iterator it = wordMeasurement.fallbackFonts.begin(); it != end; ++it) 852 fallbackFonts.add(*it); 853 } 854 } 855 if (measuredWidth && lastEndOffset != run->m_stop) { 856 // If we don't have enough cached data, we'll measure the run again. 857 measuredWidth = 0; 858 fallbackFonts.clear(); 859 } 860 } 861 862 if (!measuredWidth) 863 measuredWidth = renderer->width(run->m_start, run->m_stop - run->m_start, xPos, lineInfo.isFirstLine(), &fallbackFonts, &glyphOverflow); 864 865 run->m_box->setLogicalWidth(measuredWidth + hyphenWidth); 866 if (!fallbackFonts.isEmpty()) { 867 ASSERT(run->m_box->isText()); 868 GlyphOverflowAndFallbackFontsMap::iterator it = textBoxDataMap.add(toInlineTextBox(run->m_box), make_pair(Vector<const SimpleFontData*>(), GlyphOverflow())).iterator; 869 ASSERT(it->value.first.isEmpty()); 870 copyToVector(fallbackFonts, it->value.first); 871 run->m_box->parent()->clearDescendantsHaveSameLineHeightAndBaseline(); 872 } 873 if ((glyphOverflow.top || glyphOverflow.bottom || glyphOverflow.left || glyphOverflow.right)) { 874 ASSERT(run->m_box->isText()); 875 GlyphOverflowAndFallbackFontsMap::iterator it = textBoxDataMap.add(toInlineTextBox(run->m_box), make_pair(Vector<const SimpleFontData*>(), GlyphOverflow())).iterator; 876 it->value.second = glyphOverflow; 877 run->m_box->clearKnownToHaveNoOverflow(); 878 } 879 } 880 881 static inline void computeExpansionForJustifiedText(BidiRun* firstRun, BidiRun* trailingSpaceRun, Vector<unsigned, 16>& expansionOpportunities, unsigned expansionOpportunityCount, float& totalLogicalWidth, float availableLogicalWidth) 882 { 883 if (!expansionOpportunityCount || availableLogicalWidth <= totalLogicalWidth) 884 return; 885 886 size_t i = 0; 887 for (BidiRun* r = firstRun; r; r = r->next()) { 888 // This method is called once per segment, do not move past the current segment. 889 if (r->m_startsSegment) 890 break; 891 if (!r->m_box || r == trailingSpaceRun) 892 continue; 893 894 if (r->m_object->isText()) { 895 unsigned opportunitiesInRun = expansionOpportunities[i++]; 896 897 ASSERT(opportunitiesInRun <= expansionOpportunityCount); 898 899 // Only justify text if whitespace is collapsed. 900 if (r->m_object->style()->collapseWhiteSpace()) { 901 InlineTextBox* textBox = toInlineTextBox(r->m_box); 902 int expansion = (availableLogicalWidth - totalLogicalWidth) * opportunitiesInRun / expansionOpportunityCount; 903 textBox->setExpansion(expansion); 904 totalLogicalWidth += expansion; 905 } 906 expansionOpportunityCount -= opportunitiesInRun; 907 if (!expansionOpportunityCount) 908 break; 909 } 910 } 911 } 912 913 void RenderBlock::updateLogicalWidthForAlignment(const ETextAlign& textAlign, BidiRun* trailingSpaceRun, float& logicalLeft, float& totalLogicalWidth, float& availableLogicalWidth, int expansionOpportunityCount) 914 { 915 // Armed with the total width of the line (without justification), 916 // we now examine our text-align property in order to determine where to position the 917 // objects horizontally. The total width of the line can be increased if we end up 918 // justifying text. 919 switch (textAlign) { 920 case LEFT: 921 case WEBKIT_LEFT: 922 updateLogicalWidthForLeftAlignedBlock(style()->isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth); 923 break; 924 case RIGHT: 925 case WEBKIT_RIGHT: 926 updateLogicalWidthForRightAlignedBlock(style()->isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth); 927 break; 928 case CENTER: 929 case WEBKIT_CENTER: 930 updateLogicalWidthForCenterAlignedBlock(style()->isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth); 931 break; 932 case JUSTIFY: 933 adjustInlineDirectionLineBounds(expansionOpportunityCount, logicalLeft, availableLogicalWidth); 934 if (expansionOpportunityCount) { 935 if (trailingSpaceRun) { 936 totalLogicalWidth -= trailingSpaceRun->m_box->logicalWidth(); 937 trailingSpaceRun->m_box->setLogicalWidth(0); 938 } 939 break; 940 } 941 // Fall through 942 case TASTART: 943 if (style()->isLeftToRightDirection()) 944 updateLogicalWidthForLeftAlignedBlock(style()->isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth); 945 else 946 updateLogicalWidthForRightAlignedBlock(style()->isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth); 947 break; 948 case TAEND: 949 if (style()->isLeftToRightDirection()) 950 updateLogicalWidthForRightAlignedBlock(style()->isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth); 951 else 952 updateLogicalWidthForLeftAlignedBlock(style()->isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth); 953 break; 954 } 955 } 956 957 static IndentTextOrNot requiresIndent(bool isFirstLine, bool isAfterHardLineBreak, RenderStyle* style) 958 { 959 if (isFirstLine) 960 return IndentText; 961 #if ENABLE(CSS3_TEXT) 962 if (isAfterHardLineBreak && style->textIndentLine() == TextIndentEachLine) 963 return IndentText; 964 #else 965 UNUSED_PARAM(isAfterHardLineBreak); 966 UNUSED_PARAM(style); 967 #endif 968 return DoNotIndentText; 969 } 970 971 static void updateLogicalInlinePositions(RenderBlock* block, float& lineLogicalLeft, float& lineLogicalRight, float& availableLogicalWidth, bool firstLine, IndentTextOrNot shouldIndentText, LayoutUnit boxLogicalHeight) 972 { 973 LayoutUnit lineLogicalHeight = logicalHeightForLine(block, firstLine, boxLogicalHeight); 974 lineLogicalLeft = block->pixelSnappedLogicalLeftOffsetForLine(block->logicalHeight(), shouldIndentText == IndentText, lineLogicalHeight); 975 lineLogicalRight = block->pixelSnappedLogicalRightOffsetForLine(block->logicalHeight(), shouldIndentText == IndentText, lineLogicalHeight); 976 availableLogicalWidth = lineLogicalRight - lineLogicalLeft; 977 } 978 979 void RenderBlock::computeInlineDirectionPositionsForLine(RootInlineBox* lineBox, const LineInfo& lineInfo, BidiRun* firstRun, BidiRun* trailingSpaceRun, bool reachedEnd, 980 GlyphOverflowAndFallbackFontsMap& textBoxDataMap, VerticalPositionCache& verticalPositionCache, WordMeasurements& wordMeasurements) 981 { 982 ETextAlign textAlign = textAlignmentForLine(!reachedEnd && !lineBox->endsWithBreak()); 983 984 // 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 985 // box is only affected if it is the first child of its parent element." 986 // CSS3 "text-indent", "-webkit-each-line" affects the first line of the block container as well as each line after a forced line break, 987 // but does not affect lines after a soft wrap break. 988 bool isFirstLine = lineInfo.isFirstLine() && !(isAnonymousBlock() && parent()->firstChild() != this); 989 bool isAfterHardLineBreak = lineBox->prevRootBox() && lineBox->prevRootBox()->endsWithBreak(); 990 IndentTextOrNot shouldIndentText = requiresIndent(isFirstLine, isAfterHardLineBreak, style()); 991 float lineLogicalLeft; 992 float lineLogicalRight; 993 float availableLogicalWidth; 994 updateLogicalInlinePositions(this, lineLogicalLeft, lineLogicalRight, availableLogicalWidth, isFirstLine, shouldIndentText, 0); 995 bool needsWordSpacing; 996 ShapeInsideInfo* shapeInsideInfo = layoutShapeInsideInfo(); 997 if (shapeInsideInfo && shapeInsideInfo->hasSegments()) { 998 BidiRun* segmentStart = firstRun; 999 const SegmentList& segments = shapeInsideInfo->segments(); 1000 float logicalLeft = max<float>(roundToInt(segments[0].logicalLeft), lineLogicalLeft); 1001 float logicalRight = min<float>(floorToInt(segments[0].logicalRight), lineLogicalRight); 1002 float startLogicalLeft = logicalLeft; 1003 float endLogicalRight = logicalLeft; 1004 float minLogicalLeft = logicalLeft; 1005 float maxLogicalRight = logicalLeft; 1006 lineBox->beginPlacingBoxRangesInInlineDirection(logicalLeft); 1007 for (size_t i = 0; i < segments.size(); i++) { 1008 if (i) { 1009 logicalLeft = max<float>(roundToInt(segments[i].logicalLeft), lineLogicalLeft); 1010 logicalRight = min<float>(floorToInt(segments[i].logicalRight), lineLogicalRight); 1011 } 1012 availableLogicalWidth = logicalRight - logicalLeft; 1013 BidiRun* newSegmentStart = computeInlineDirectionPositionsForSegment(lineBox, lineInfo, textAlign, logicalLeft, availableLogicalWidth, segmentStart, trailingSpaceRun, textBoxDataMap, verticalPositionCache, wordMeasurements); 1014 needsWordSpacing = false; 1015 endLogicalRight = lineBox->placeBoxRangeInInlineDirection(segmentStart->m_box, newSegmentStart ? newSegmentStart->m_box : 0, logicalLeft, minLogicalLeft, maxLogicalRight, needsWordSpacing, textBoxDataMap); 1016 if (!newSegmentStart || !newSegmentStart->next()) 1017 break; 1018 ASSERT(newSegmentStart->m_startsSegment); 1019 // Discard the empty segment start marker bidi runs 1020 segmentStart = newSegmentStart->next(); 1021 } 1022 lineBox->endPlacingBoxRangesInInlineDirection(startLogicalLeft, endLogicalRight, minLogicalLeft, maxLogicalRight); 1023 return; 1024 } 1025 1026 if (firstRun && firstRun->m_object->isReplaced()) { 1027 RenderBox* renderBox = toRenderBox(firstRun->m_object); 1028 updateLogicalInlinePositions(this, lineLogicalLeft, lineLogicalRight, availableLogicalWidth, isFirstLine, shouldIndentText, renderBox->logicalHeight()); 1029 } 1030 1031 computeInlineDirectionPositionsForSegment(lineBox, lineInfo, textAlign, lineLogicalLeft, availableLogicalWidth, firstRun, trailingSpaceRun, textBoxDataMap, verticalPositionCache, wordMeasurements); 1032 // The widths of all runs are now known. We can now place every inline box (and 1033 // compute accurate widths for the inline flow boxes). 1034 needsWordSpacing = false; 1035 lineBox->placeBoxesInInlineDirection(lineLogicalLeft, needsWordSpacing, textBoxDataMap); 1036 } 1037 1038 BidiRun* RenderBlock::computeInlineDirectionPositionsForSegment(RootInlineBox* lineBox, const LineInfo& lineInfo, ETextAlign textAlign, float& logicalLeft, 1039 float& availableLogicalWidth, BidiRun* firstRun, BidiRun* trailingSpaceRun, GlyphOverflowAndFallbackFontsMap& textBoxDataMap, VerticalPositionCache& verticalPositionCache, 1040 WordMeasurements& wordMeasurements) 1041 { 1042 bool needsWordSpacing = false; 1043 float totalLogicalWidth = lineBox->getFlowSpacingLogicalWidth(); 1044 unsigned expansionOpportunityCount = 0; 1045 bool isAfterExpansion = true; 1046 Vector<unsigned, 16> expansionOpportunities; 1047 RenderObject* previousObject = 0; 1048 1049 BidiRun* r = firstRun; 1050 for (; r; r = r->next()) { 1051 // Once we have reached the start of the next segment, we have finished 1052 // computing the positions for this segment's contents. 1053 if (r->m_startsSegment) 1054 break; 1055 if (!r->m_box || r->m_object->isOutOfFlowPositioned() || r->m_box->isLineBreak()) 1056 continue; // Positioned objects are only participating to figure out their 1057 // correct static x position. They have no effect on the width. 1058 // Similarly, line break boxes have no effect on the width. 1059 if (r->m_object->isText()) { 1060 RenderText* rt = toRenderText(r->m_object); 1061 if (textAlign == JUSTIFY && r != trailingSpaceRun) { 1062 if (!isAfterExpansion) 1063 toInlineTextBox(r->m_box)->setCanHaveLeadingExpansion(true); 1064 unsigned opportunitiesInRun; 1065 if (rt->is8Bit()) 1066 opportunitiesInRun = Font::expansionOpportunityCount(rt->characters8() + r->m_start, r->m_stop - r->m_start, r->m_box->direction(), isAfterExpansion); 1067 else 1068 opportunitiesInRun = Font::expansionOpportunityCount(rt->characters16() + r->m_start, r->m_stop - r->m_start, r->m_box->direction(), isAfterExpansion); 1069 expansionOpportunities.append(opportunitiesInRun); 1070 expansionOpportunityCount += opportunitiesInRun; 1071 } 1072 1073 if (int length = rt->textLength()) { 1074 if (!r->m_start && needsWordSpacing && isSpaceOrNewline(rt->characterAt(r->m_start))) 1075 totalLogicalWidth += rt->style(lineInfo.isFirstLine())->font().wordSpacing(); 1076 needsWordSpacing = !isSpaceOrNewline(rt->characterAt(r->m_stop - 1)) && r->m_stop == length; 1077 } 1078 1079 setLogicalWidthForTextRun(lineBox, r, rt, totalLogicalWidth, lineInfo, textBoxDataMap, verticalPositionCache, wordMeasurements); 1080 } else { 1081 isAfterExpansion = false; 1082 if (!r->m_object->isRenderInline()) { 1083 RenderBox* renderBox = toRenderBox(r->m_object); 1084 if (renderBox->isRubyRun()) 1085 setMarginsForRubyRun(r, toRenderRubyRun(renderBox), previousObject, lineInfo); 1086 r->m_box->setLogicalWidth(logicalWidthForChild(renderBox)); 1087 totalLogicalWidth += marginStartForChild(renderBox) + marginEndForChild(renderBox); 1088 } 1089 } 1090 1091 totalLogicalWidth += r->m_box->logicalWidth(); 1092 previousObject = r->m_object; 1093 } 1094 1095 if (isAfterExpansion && !expansionOpportunities.isEmpty()) { 1096 expansionOpportunities.last()--; 1097 expansionOpportunityCount--; 1098 } 1099 1100 updateLogicalWidthForAlignment(textAlign, trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth, expansionOpportunityCount); 1101 1102 computeExpansionForJustifiedText(firstRun, trailingSpaceRun, expansionOpportunities, expansionOpportunityCount, totalLogicalWidth, availableLogicalWidth); 1103 1104 return r; 1105 } 1106 1107 void RenderBlock::computeBlockDirectionPositionsForLine(RootInlineBox* lineBox, BidiRun* firstRun, GlyphOverflowAndFallbackFontsMap& textBoxDataMap, 1108 VerticalPositionCache& verticalPositionCache) 1109 { 1110 setLogicalHeight(lineBox->alignBoxesInBlockDirection(logicalHeight(), textBoxDataMap, verticalPositionCache)); 1111 1112 // Now make sure we place replaced render objects correctly. 1113 for (BidiRun* r = firstRun; r; r = r->next()) { 1114 ASSERT(r->m_box); 1115 if (!r->m_box) 1116 continue; // Skip runs with no line boxes. 1117 1118 // Align positioned boxes with the top of the line box. This is 1119 // a reasonable approximation of an appropriate y position. 1120 if (r->m_object->isOutOfFlowPositioned()) 1121 r->m_box->setLogicalTop(logicalHeight()); 1122 1123 // Position is used to properly position both replaced elements and 1124 // to update the static normal flow x/y of positioned elements. 1125 if (r->m_object->isText()) 1126 toRenderText(r->m_object)->positionLineBox(r->m_box); 1127 else if (r->m_object->isBox()) 1128 toRenderBox(r->m_object)->positionLineBox(r->m_box); 1129 } 1130 // Positioned objects and zero-length text nodes destroy their boxes in 1131 // position(), which unnecessarily dirties the line. 1132 lineBox->markDirty(false); 1133 } 1134 1135 static inline bool isCollapsibleSpace(UChar character, RenderText* renderer) 1136 { 1137 if (character == ' ' || character == '\t' || character == softHyphen) 1138 return true; 1139 if (character == '\n') 1140 return !renderer->style()->preserveNewline(); 1141 return false; 1142 } 1143 1144 1145 static void setStaticPositions(RenderBlock* block, RenderBox* child) 1146 { 1147 // FIXME: The math here is actually not really right. It's a best-guess approximation that 1148 // will work for the common cases 1149 RenderObject* containerBlock = child->container(); 1150 LayoutUnit blockHeight = block->logicalHeight(); 1151 if (containerBlock->isRenderInline()) { 1152 // A relative positioned inline encloses us. In this case, we also have to determine our 1153 // position as though we were an inline. Set |staticInlinePosition| and |staticBlockPosition| on the relative positioned 1154 // inline so that we can obtain the value later. 1155 toRenderInline(containerBlock)->layer()->setStaticInlinePosition(block->startAlignedOffsetForLine(blockHeight, false)); 1156 toRenderInline(containerBlock)->layer()->setStaticBlockPosition(blockHeight); 1157 } 1158 block->updateStaticInlinePositionForChild(child, blockHeight); 1159 child->layer()->setStaticBlockPosition(blockHeight); 1160 } 1161 1162 template <typename CharacterType> 1163 static inline int findFirstTrailingSpace(RenderText* lastText, const CharacterType* characters, int start, int stop) 1164 { 1165 int firstSpace = stop; 1166 while (firstSpace > start) { 1167 UChar current = characters[firstSpace - 1]; 1168 if (!isCollapsibleSpace(current, lastText)) 1169 break; 1170 firstSpace--; 1171 } 1172 1173 return firstSpace; 1174 } 1175 1176 inline BidiRun* RenderBlock::handleTrailingSpaces(BidiRunList<BidiRun>& bidiRuns, BidiContext* currentContext) 1177 { 1178 if (!bidiRuns.runCount() 1179 || !bidiRuns.logicallyLastRun()->m_object->style()->breakOnlyAfterWhiteSpace() 1180 || !bidiRuns.logicallyLastRun()->m_object->style()->autoWrap()) 1181 return 0; 1182 1183 BidiRun* trailingSpaceRun = bidiRuns.logicallyLastRun(); 1184 RenderObject* lastObject = trailingSpaceRun->m_object; 1185 if (!lastObject->isText()) 1186 return 0; 1187 1188 RenderText* lastText = toRenderText(lastObject); 1189 int firstSpace; 1190 if (lastText->is8Bit()) 1191 firstSpace = findFirstTrailingSpace(lastText, lastText->characters8(), trailingSpaceRun->start(), trailingSpaceRun->stop()); 1192 else 1193 firstSpace = findFirstTrailingSpace(lastText, lastText->characters16(), trailingSpaceRun->start(), trailingSpaceRun->stop()); 1194 1195 if (firstSpace == trailingSpaceRun->stop()) 1196 return 0; 1197 1198 TextDirection direction = style()->direction(); 1199 bool shouldReorder = trailingSpaceRun != (direction == LTR ? bidiRuns.lastRun() : bidiRuns.firstRun()); 1200 if (firstSpace != trailingSpaceRun->start()) { 1201 BidiContext* baseContext = currentContext; 1202 while (BidiContext* parent = baseContext->parent()) 1203 baseContext = parent; 1204 1205 BidiRun* newTrailingRun = new BidiRun(firstSpace, trailingSpaceRun->m_stop, trailingSpaceRun->m_object, baseContext, OtherNeutral); 1206 trailingSpaceRun->m_stop = firstSpace; 1207 if (direction == LTR) 1208 bidiRuns.addRun(newTrailingRun); 1209 else 1210 bidiRuns.prependRun(newTrailingRun); 1211 trailingSpaceRun = newTrailingRun; 1212 return trailingSpaceRun; 1213 } 1214 if (!shouldReorder) 1215 return trailingSpaceRun; 1216 1217 if (direction == LTR) { 1218 bidiRuns.moveRunToEnd(trailingSpaceRun); 1219 trailingSpaceRun->m_level = 0; 1220 } else { 1221 bidiRuns.moveRunToBeginning(trailingSpaceRun); 1222 trailingSpaceRun->m_level = 1; 1223 } 1224 return trailingSpaceRun; 1225 } 1226 1227 void RenderBlock::appendFloatingObjectToLastLine(FloatingObject* floatingObject) 1228 { 1229 ASSERT(!floatingObject->m_originatingLine); 1230 floatingObject->m_originatingLine = lastRootBox(); 1231 lastRootBox()->appendFloat(floatingObject->renderer()); 1232 } 1233 1234 // FIXME: This should be a BidiStatus constructor or create method. 1235 static inline BidiStatus statusWithDirection(TextDirection textDirection, bool isOverride) 1236 { 1237 WTF::Unicode::Direction direction = textDirection == LTR ? LeftToRight : RightToLeft; 1238 RefPtr<BidiContext> context = BidiContext::create(textDirection == LTR ? 0 : 1, direction, isOverride, FromStyleOrDOM); 1239 1240 // This copies BidiStatus and may churn the ref on BidiContext. I doubt it matters. 1241 return BidiStatus(direction, direction, direction, context.release()); 1242 } 1243 1244 static inline void setupResolverToResumeInIsolate(InlineBidiResolver& resolver, RenderObject* root, RenderObject* startObject) 1245 { 1246 if (root != startObject) { 1247 RenderObject* parent = startObject->parent(); 1248 setupResolverToResumeInIsolate(resolver, root, parent); 1249 notifyObserverEnteredObject(&resolver, startObject); 1250 } 1251 } 1252 1253 // FIXME: BidiResolver should have this logic. 1254 static inline void constructBidiRunsForSegment(InlineBidiResolver& topResolver, BidiRunList<BidiRun>& bidiRuns, const InlineIterator& endOfRuns, VisualDirectionOverride override, bool previousLineBrokeCleanly) 1255 { 1256 // FIXME: We should pass a BidiRunList into createBidiRunsForLine instead 1257 // of the resolver owning the runs. 1258 ASSERT(&topResolver.runs() == &bidiRuns); 1259 ASSERT(topResolver.position() != endOfRuns); 1260 RenderObject* currentRoot = topResolver.position().root(); 1261 topResolver.createBidiRunsForLine(endOfRuns, override, previousLineBrokeCleanly); 1262 1263 while (!topResolver.isolatedRuns().isEmpty()) { 1264 // It does not matter which order we resolve the runs as long as we resolve them all. 1265 BidiRun* isolatedRun = topResolver.isolatedRuns().last(); 1266 topResolver.isolatedRuns().removeLast(); 1267 1268 RenderObject* startObj = isolatedRun->object(); 1269 1270 // Only inlines make sense with unicode-bidi: isolate (blocks are already isolated). 1271 // FIXME: Because enterIsolate is not passed a RenderObject, we have to crawl up the 1272 // tree to see which parent inline is the isolate. We could change enterIsolate 1273 // to take a RenderObject and do this logic there, but that would be a layering 1274 // violation for BidiResolver (which knows nothing about RenderObject). 1275 RenderInline* isolatedInline = toRenderInline(highestContainingIsolateWithinRoot(startObj, currentRoot)); 1276 ASSERT(isolatedInline); 1277 1278 InlineBidiResolver isolatedResolver; 1279 EUnicodeBidi unicodeBidi = isolatedInline->style()->unicodeBidi(); 1280 TextDirection direction; 1281 if (unicodeBidi == Plaintext) 1282 determineDirectionality(direction, InlineIterator(isolatedInline, isolatedRun->object(), 0)); 1283 else { 1284 ASSERT(unicodeBidi == Isolate || unicodeBidi == IsolateOverride); 1285 direction = isolatedInline->style()->direction(); 1286 } 1287 isolatedResolver.setStatus(statusWithDirection(direction, isOverride(unicodeBidi))); 1288 1289 setupResolverToResumeInIsolate(isolatedResolver, isolatedInline, startObj); 1290 1291 // The starting position is the beginning of the first run within the isolate that was identified 1292 // during the earlier call to createBidiRunsForLine. This can be but is not necessarily the 1293 // first run within the isolate. 1294 InlineIterator iter = InlineIterator(isolatedInline, startObj, isolatedRun->m_start); 1295 isolatedResolver.setPositionIgnoringNestedIsolates(iter); 1296 1297 // We stop at the next end of line; we may re-enter this isolate in the next call to constructBidiRuns(). 1298 // FIXME: What should end and previousLineBrokeCleanly be? 1299 // rniwa says previousLineBrokeCleanly is just a WinIE hack and could always be false here? 1300 isolatedResolver.createBidiRunsForLine(endOfRuns, NoVisualOverride, previousLineBrokeCleanly); 1301 // Note that we do not delete the runs from the resolver. 1302 // We're not guaranteed to get any BidiRuns in the previous step. If we don't, we allow the placeholder 1303 // itself to be turned into an InlineBox. We can't remove it here without potentially losing track of 1304 // the logically last run. 1305 if (isolatedResolver.runs().runCount()) 1306 bidiRuns.replaceRunWithRuns(isolatedRun, isolatedResolver.runs()); 1307 1308 // If we encountered any nested isolate runs, just move them 1309 // to the top resolver's list for later processing. 1310 if (!isolatedResolver.isolatedRuns().isEmpty()) { 1311 topResolver.isolatedRuns().append(isolatedResolver.isolatedRuns()); 1312 isolatedResolver.isolatedRuns().clear(); 1313 currentRoot = isolatedInline; 1314 } 1315 } 1316 } 1317 1318 static inline bool segmentIsEmpty(const InlineIterator& segmentStart, const InlineIterator& segmentEnd) 1319 { 1320 return segmentStart == segmentEnd; 1321 } 1322 1323 static inline void constructBidiRunsForLine(const RenderBlock* block, InlineBidiResolver& topResolver, BidiRunList<BidiRun>& bidiRuns, const InlineIterator& endOfLine, VisualDirectionOverride override, bool previousLineBrokeCleanly) 1324 { 1325 ShapeInsideInfo* shapeInsideInfo = block->layoutShapeInsideInfo(); 1326 if (!shapeInsideInfo || !shapeInsideInfo->hasSegments()) { 1327 constructBidiRunsForSegment(topResolver, bidiRuns, endOfLine, override, previousLineBrokeCleanly); 1328 return; 1329 } 1330 1331 const SegmentRangeList& segmentRanges = shapeInsideInfo->segmentRanges(); 1332 ASSERT(segmentRanges.size()); 1333 1334 for (size_t i = 0; i < segmentRanges.size(); i++) { 1335 LineSegmentIterator iterator = segmentRanges[i].start; 1336 InlineIterator segmentStart(iterator.root, iterator.object, iterator.offset); 1337 iterator = segmentRanges[i].end; 1338 InlineIterator segmentEnd(iterator.root, iterator.object, iterator.offset); 1339 if (i) { 1340 ASSERT(segmentStart.m_obj); 1341 BidiRun* segmentMarker = createRun(segmentStart.m_pos, segmentStart.m_pos, segmentStart.m_obj, topResolver); 1342 segmentMarker->m_startsSegment = true; 1343 bidiRuns.addRun(segmentMarker); 1344 // Do not collapse midpoints between segments 1345 topResolver.midpointState().betweenMidpoints = false; 1346 } 1347 if (!segmentIsEmpty(segmentStart, segmentEnd)) { 1348 topResolver.setPosition(segmentStart, numberOfIsolateAncestors(segmentStart)); 1349 constructBidiRunsForSegment(topResolver, bidiRuns, segmentEnd, override, previousLineBrokeCleanly); 1350 } 1351 } 1352 } 1353 1354 // This function constructs line boxes for all of the text runs in the resolver and computes their position. 1355 RootInlineBox* RenderBlock::createLineBoxesFromBidiRuns(BidiRunList<BidiRun>& bidiRuns, const InlineIterator& end, LineInfo& lineInfo, VerticalPositionCache& verticalPositionCache, BidiRun* trailingSpaceRun, WordMeasurements& wordMeasurements) 1356 { 1357 if (!bidiRuns.runCount()) 1358 return 0; 1359 1360 // FIXME: Why is this only done when we had runs? 1361 lineInfo.setLastLine(!end.m_obj); 1362 1363 RootInlineBox* lineBox = constructLine(bidiRuns, lineInfo); 1364 if (!lineBox) 1365 return 0; 1366 1367 lineBox->setEndsWithBreak(lineInfo.previousLineBrokeCleanly()); 1368 1369 bool isSVGRootInlineBox = lineBox->isSVGRootInlineBox(); 1370 1371 GlyphOverflowAndFallbackFontsMap textBoxDataMap; 1372 1373 // Now we position all of our text runs horizontally. 1374 if (!isSVGRootInlineBox) 1375 computeInlineDirectionPositionsForLine(lineBox, lineInfo, bidiRuns.firstRun(), trailingSpaceRun, end.atEnd(), textBoxDataMap, verticalPositionCache, wordMeasurements); 1376 1377 // Now position our text runs vertically. 1378 computeBlockDirectionPositionsForLine(lineBox, bidiRuns.firstRun(), textBoxDataMap, verticalPositionCache); 1379 1380 // SVG text layout code computes vertical & horizontal positions on its own. 1381 // Note that we still need to execute computeVerticalPositionsForLine() as 1382 // it calls InlineTextBox::positionLineBox(), which tracks whether the box 1383 // contains reversed text or not. If we wouldn't do that editing and thus 1384 // text selection in RTL boxes would not work as expected. 1385 if (isSVGRootInlineBox) { 1386 ASSERT(isSVGText()); 1387 static_cast<SVGRootInlineBox*>(lineBox)->computePerCharacterLayoutInformation(); 1388 } 1389 1390 // Compute our overflow now. 1391 lineBox->computeOverflow(lineBox->lineTop(), lineBox->lineBottom(), textBoxDataMap); 1392 1393 return lineBox; 1394 } 1395 1396 // Like LayoutState for layout(), LineLayoutState keeps track of global information 1397 // during an entire linebox tree layout pass (aka layoutInlineChildren). 1398 class LineLayoutState { 1399 public: 1400 LineLayoutState(bool fullLayout, LayoutUnit& repaintLogicalTop, LayoutUnit& repaintLogicalBottom, RenderFlowThread* flowThread) 1401 : m_lastFloat(0) 1402 , m_endLine(0) 1403 , m_floatIndex(0) 1404 , m_endLineLogicalTop(0) 1405 , m_endLineMatched(false) 1406 , m_checkForFloatsFromLastLine(false) 1407 , m_isFullLayout(fullLayout) 1408 , m_repaintLogicalTop(repaintLogicalTop) 1409 , m_repaintLogicalBottom(repaintLogicalBottom) 1410 , m_adjustedLogicalLineTop(0) 1411 , m_usesRepaintBounds(false) 1412 , m_flowThread(flowThread) 1413 { } 1414 1415 void markForFullLayout() { m_isFullLayout = true; } 1416 bool isFullLayout() const { return m_isFullLayout; } 1417 1418 bool usesRepaintBounds() const { return m_usesRepaintBounds; } 1419 1420 void setRepaintRange(LayoutUnit logicalHeight) 1421 { 1422 m_usesRepaintBounds = true; 1423 m_repaintLogicalTop = m_repaintLogicalBottom = logicalHeight; 1424 } 1425 1426 void updateRepaintRangeFromBox(RootInlineBox* box, LayoutUnit paginationDelta = 0) 1427 { 1428 m_usesRepaintBounds = true; 1429 m_repaintLogicalTop = min(m_repaintLogicalTop, box->logicalTopVisualOverflow() + min<LayoutUnit>(paginationDelta, 0)); 1430 m_repaintLogicalBottom = max(m_repaintLogicalBottom, box->logicalBottomVisualOverflow() + max<LayoutUnit>(paginationDelta, 0)); 1431 } 1432 1433 bool endLineMatched() const { return m_endLineMatched; } 1434 void setEndLineMatched(bool endLineMatched) { m_endLineMatched = endLineMatched; } 1435 1436 bool checkForFloatsFromLastLine() const { return m_checkForFloatsFromLastLine; } 1437 void setCheckForFloatsFromLastLine(bool check) { m_checkForFloatsFromLastLine = check; } 1438 1439 LineInfo& lineInfo() { return m_lineInfo; } 1440 const LineInfo& lineInfo() const { return m_lineInfo; } 1441 1442 LayoutUnit endLineLogicalTop() const { return m_endLineLogicalTop; } 1443 void setEndLineLogicalTop(LayoutUnit logicalTop) { m_endLineLogicalTop = logicalTop; } 1444 1445 RootInlineBox* endLine() const { return m_endLine; } 1446 void setEndLine(RootInlineBox* line) { m_endLine = line; } 1447 1448 RenderBlock::FloatingObject* lastFloat() const { return m_lastFloat; } 1449 void setLastFloat(RenderBlock::FloatingObject* lastFloat) { m_lastFloat = lastFloat; } 1450 1451 Vector<RenderBlock::FloatWithRect>& floats() { return m_floats; } 1452 1453 unsigned floatIndex() const { return m_floatIndex; } 1454 void setFloatIndex(unsigned floatIndex) { m_floatIndex = floatIndex; } 1455 1456 LayoutUnit adjustedLogicalLineTop() const { return m_adjustedLogicalLineTop; } 1457 void setAdjustedLogicalLineTop(LayoutUnit value) { m_adjustedLogicalLineTop = value; } 1458 1459 RenderFlowThread* flowThread() const { return m_flowThread; } 1460 void setFlowThread(RenderFlowThread* thread) { m_flowThread = thread; } 1461 1462 private: 1463 Vector<RenderBlock::FloatWithRect> m_floats; 1464 RenderBlock::FloatingObject* m_lastFloat; 1465 RootInlineBox* m_endLine; 1466 LineInfo m_lineInfo; 1467 unsigned m_floatIndex; 1468 LayoutUnit m_endLineLogicalTop; 1469 bool m_endLineMatched; 1470 bool m_checkForFloatsFromLastLine; 1471 1472 bool m_isFullLayout; 1473 1474 // FIXME: Should this be a range object instead of two ints? 1475 LayoutUnit& m_repaintLogicalTop; 1476 LayoutUnit& m_repaintLogicalBottom; 1477 1478 LayoutUnit m_adjustedLogicalLineTop; 1479 1480 bool m_usesRepaintBounds; 1481 1482 RenderFlowThread* m_flowThread; 1483 }; 1484 1485 static void deleteLineRange(LineLayoutState& layoutState, RootInlineBox* startLine, RootInlineBox* stopLine = 0) 1486 { 1487 RootInlineBox* boxToDelete = startLine; 1488 while (boxToDelete && boxToDelete != stopLine) { 1489 layoutState.updateRepaintRangeFromBox(boxToDelete); 1490 // Note: deleteLineRange(firstRootBox()) is not identical to deleteLineBoxTree(). 1491 // deleteLineBoxTree uses nextLineBox() instead of nextRootBox() when traversing. 1492 RootInlineBox* next = boxToDelete->nextRootBox(); 1493 boxToDelete->deleteLine(); 1494 boxToDelete = next; 1495 } 1496 } 1497 1498 void RenderBlock::layoutRunsAndFloats(LineLayoutState& layoutState, bool hasInlineChild) 1499 { 1500 // We want to skip ahead to the first dirty line 1501 InlineBidiResolver resolver; 1502 RootInlineBox* startLine = determineStartPosition(layoutState, resolver); 1503 1504 unsigned consecutiveHyphenatedLines = 0; 1505 if (startLine) { 1506 for (RootInlineBox* line = startLine->prevRootBox(); line && line->isHyphenated(); line = line->prevRootBox()) 1507 consecutiveHyphenatedLines++; 1508 } 1509 1510 // FIXME: This would make more sense outside of this function, but since 1511 // determineStartPosition can change the fullLayout flag we have to do this here. Failure to call 1512 // determineStartPosition first will break fast/repaint/line-flow-with-floats-9.html. 1513 if (layoutState.isFullLayout() && hasInlineChild && !selfNeedsLayout()) { 1514 setNeedsLayout(MarkOnlyThis); // Mark as needing a full layout to force us to repaint. 1515 RenderView* v = view(); 1516 if (v && !v->doingFullRepaint() && hasLayer()) { 1517 // Because we waited until we were already inside layout to discover 1518 // that the block really needed a full layout, we missed our chance to repaint the layer 1519 // before layout started. Luckily the layer has cached the repaint rect for its original 1520 // position and size, and so we can use that to make a repaint happen now. 1521 repaintUsingContainer(containerForRepaint(), pixelSnappedIntRect(layer()->repaintRect())); 1522 } 1523 } 1524 1525 if (containsFloats()) 1526 layoutState.setLastFloat(m_floatingObjects->set().last()); 1527 1528 // We also find the first clean line and extract these lines. We will add them back 1529 // if we determine that we're able to synchronize after handling all our dirty lines. 1530 InlineIterator cleanLineStart; 1531 BidiStatus cleanLineBidiStatus; 1532 if (!layoutState.isFullLayout() && startLine) 1533 determineEndPosition(layoutState, startLine, cleanLineStart, cleanLineBidiStatus); 1534 1535 if (startLine) { 1536 if (!layoutState.usesRepaintBounds()) 1537 layoutState.setRepaintRange(logicalHeight()); 1538 deleteLineRange(layoutState, startLine); 1539 } 1540 1541 if (!layoutState.isFullLayout() && lastRootBox() && lastRootBox()->endsWithBreak()) { 1542 // If the last line before the start line ends with a line break that clear floats, 1543 // adjust the height accordingly. 1544 // A line break can be either the first or the last object on a line, depending on its direction. 1545 if (InlineBox* lastLeafChild = lastRootBox()->lastLeafChild()) { 1546 RenderObject* lastObject = lastLeafChild->renderer(); 1547 if (!lastObject->isBR()) 1548 lastObject = lastRootBox()->firstLeafChild()->renderer(); 1549 if (lastObject->isBR()) { 1550 EClear clear = lastObject->style()->clear(); 1551 if (clear != CNONE) 1552 newLine(clear); 1553 } 1554 } 1555 } 1556 1557 layoutRunsAndFloatsInRange(layoutState, resolver, cleanLineStart, cleanLineBidiStatus, consecutiveHyphenatedLines); 1558 linkToEndLineIfNeeded(layoutState); 1559 repaintDirtyFloats(layoutState.floats()); 1560 } 1561 1562 RenderBlock::RenderTextInfo::RenderTextInfo() 1563 : m_text(0) 1564 , m_font(0) 1565 { 1566 } 1567 1568 RenderBlock::RenderTextInfo::~RenderTextInfo() 1569 { 1570 } 1571 1572 // Before restarting the layout loop with a new logicalHeight, remove all floats that were added and reset the resolver. 1573 inline const InlineIterator& RenderBlock::restartLayoutRunsAndFloatsInRange(LayoutUnit oldLogicalHeight, LayoutUnit newLogicalHeight, FloatingObject* lastFloatFromPreviousLine, InlineBidiResolver& resolver, const InlineIterator& oldEnd) 1574 { 1575 removeFloatingObjectsBelow(lastFloatFromPreviousLine, oldLogicalHeight); 1576 setLogicalHeight(newLogicalHeight); 1577 resolver.setPositionIgnoringNestedIsolates(oldEnd); 1578 return oldEnd; 1579 } 1580 1581 static inline float firstPositiveWidth(const WordMeasurements& wordMeasurements) 1582 { 1583 for (size_t i = 0; i < wordMeasurements.size(); ++i) { 1584 if (wordMeasurements[i].width > 0) 1585 return wordMeasurements[i].width; 1586 } 1587 return 0; 1588 } 1589 1590 1591 static inline LayoutUnit adjustLogicalLineTop(ShapeInsideInfo* shapeInsideInfo, InlineIterator start, InlineIterator end, const WordMeasurements& wordMeasurements) 1592 { 1593 if (!shapeInsideInfo || end != start) 1594 return 0; 1595 1596 float minWidth = firstPositiveWidth(wordMeasurements); 1597 ASSERT(minWidth || wordMeasurements.isEmpty()); 1598 if (minWidth > 0 && shapeInsideInfo->adjustLogicalLineTop(minWidth)) 1599 return shapeInsideInfo->logicalLineTop(); 1600 1601 return shapeInsideInfo->shapeLogicalBottom(); 1602 } 1603 1604 static inline void pushShapeContentOverflowBelowTheContentBox(RenderBlock* block, ShapeInsideInfo* shapeInsideInfo, LayoutUnit lineTop, LayoutUnit lineHeight) 1605 { 1606 ASSERT(shapeInsideInfo); 1607 1608 LayoutUnit logicalLineBottom = lineTop + lineHeight; 1609 LayoutUnit shapeLogicalBottom = shapeInsideInfo->shapeLogicalBottom(); 1610 LayoutUnit shapeContainingBlockHeight = shapeInsideInfo->shapeContainingBlockHeight(); 1611 1612 bool isOverflowPositionedAlready = (shapeContainingBlockHeight - shapeInsideInfo->owner()->borderAndPaddingAfter() + lineHeight) <= lineTop; 1613 1614 // If the last line overlaps with the shape, we don't need the segments anymore 1615 if (lineTop < shapeLogicalBottom && shapeLogicalBottom < logicalLineBottom) 1616 shapeInsideInfo->clearSegments(); 1617 if (logicalLineBottom <= shapeLogicalBottom || !shapeContainingBlockHeight || isOverflowPositionedAlready) 1618 return; 1619 1620 LayoutUnit newLogicalHeight = block->logicalHeight() + (shapeContainingBlockHeight - (lineTop + shapeInsideInfo->owner()->borderAndPaddingAfter())); 1621 block->setLogicalHeight(newLogicalHeight); 1622 } 1623 1624 void RenderBlock::updateShapeAndSegmentsForCurrentLine(ShapeInsideInfo*& shapeInsideInfo, LayoutUnit& absoluteLogicalTop, LineLayoutState& layoutState) 1625 { 1626 if (layoutState.flowThread()) 1627 return updateShapeAndSegmentsForCurrentLineInFlowThread(shapeInsideInfo, layoutState); 1628 1629 if (!shapeInsideInfo) 1630 return; 1631 1632 LayoutUnit lineTop = logicalHeight() + absoluteLogicalTop; 1633 LayoutUnit lineHeight = this->lineHeight(layoutState.lineInfo().isFirstLine(), isHorizontalWritingMode() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes); 1634 1635 // FIXME: Bug 95361: It is possible for a line to grow beyond lineHeight, in which case these segments may be incorrect. 1636 shapeInsideInfo->computeSegmentsForLine(lineTop, lineHeight); 1637 1638 pushShapeContentOverflowBelowTheContentBox(this, shapeInsideInfo, lineTop, lineHeight); 1639 } 1640 1641 void RenderBlock::updateShapeAndSegmentsForCurrentLineInFlowThread(ShapeInsideInfo*& shapeInsideInfo, LineLayoutState& layoutState) 1642 { 1643 ASSERT(layoutState.flowThread()); 1644 1645 LayoutUnit lineHeight = this->lineHeight(layoutState.lineInfo().isFirstLine(), isHorizontalWritingMode() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes); 1646 1647 RenderRegion* currentRegion = regionAtBlockOffset(logicalHeight()); 1648 if (!currentRegion) 1649 return; 1650 1651 shapeInsideInfo = currentRegion->shapeInsideInfo(); 1652 1653 LayoutUnit logicalLineTopInFlowThread = logicalHeight() + offsetFromLogicalTopOfFirstPage(); 1654 LayoutUnit logicalLineBottomInFlowThread = logicalLineTopInFlowThread + lineHeight; 1655 LayoutUnit logicalRegionTopInFlowThread = currentRegion->logicalTopForFlowThreadContent(); 1656 LayoutUnit logicalRegionBottomInFlowThread = logicalRegionTopInFlowThread + currentRegion->logicalHeight() - currentRegion->borderAndPaddingBefore() - currentRegion->borderAndPaddingAfter(); 1657 1658 // We only want to deal regions with shapes, so we look up for the next region whether it has a shape 1659 if (!shapeInsideInfo && !currentRegion->isLastRegion()) { 1660 LayoutUnit deltaToNextRegion = logicalHeight() + logicalRegionBottomInFlowThread - logicalLineTopInFlowThread; 1661 RenderRegion* lookupForNextRegion = regionAtBlockOffset(logicalHeight() + deltaToNextRegion); 1662 if (!lookupForNextRegion->shapeInsideInfo()) 1663 return; 1664 } 1665 1666 LayoutUnit shapeBottomInFlowThread = LayoutUnit::max(); 1667 if (shapeInsideInfo) 1668 shapeBottomInFlowThread = shapeInsideInfo->shapeLogicalBottom() + currentRegion->logicalTopForFlowThreadContent(); 1669 1670 // If the line is between two shapes/regions we position the line to the top of the next shape/region 1671 RenderRegion* nextRegion = regionAtBlockOffset(logicalHeight() + lineHeight); 1672 if ((currentRegion != nextRegion && (logicalLineBottomInFlowThread > logicalRegionBottomInFlowThread)) || (!currentRegion->isLastRegion() && shapeBottomInFlowThread < logicalLineBottomInFlowThread)) { 1673 LayoutUnit deltaToNextRegion = logicalRegionBottomInFlowThread - logicalLineTopInFlowThread; 1674 nextRegion = regionAtBlockOffset(logicalHeight() + deltaToNextRegion); 1675 1676 ASSERT(currentRegion != nextRegion); 1677 1678 shapeInsideInfo = nextRegion->shapeInsideInfo(); 1679 setLogicalHeight(logicalHeight() + deltaToNextRegion); 1680 1681 currentRegion = nextRegion; 1682 1683 logicalLineTopInFlowThread = logicalHeight() + offsetFromLogicalTopOfFirstPage(); 1684 logicalLineBottomInFlowThread = logicalLineTopInFlowThread + lineHeight; 1685 logicalRegionTopInFlowThread = currentRegion->logicalTopForFlowThreadContent(); 1686 logicalRegionBottomInFlowThread = logicalRegionTopInFlowThread + currentRegion->logicalHeight() - currentRegion->borderAndPaddingBefore() - currentRegion->borderAndPaddingAfter(); 1687 } 1688 1689 if (!shapeInsideInfo) 1690 return; 1691 1692 // We position the first line to the top of the shape in the region or to the previously adjusted position in the shape 1693 if (logicalLineBottomInFlowThread <= (logicalRegionTopInFlowThread + lineHeight) || (logicalLineTopInFlowThread - logicalRegionTopInFlowThread) < (layoutState.adjustedLogicalLineTop() - currentRegion->borderAndPaddingBefore())) { 1694 LayoutUnit shapeTopOffset = layoutState.adjustedLogicalLineTop(); 1695 if (!shapeTopOffset) 1696 shapeTopOffset = shapeInsideInfo->shapeLogicalTop(); 1697 1698 LayoutUnit shapePositionInFlowThread = currentRegion->logicalTopForFlowThreadContent() + shapeTopOffset; 1699 LayoutUnit shapeTopLineTopDelta = shapePositionInFlowThread - logicalLineTopInFlowThread - currentRegion->borderAndPaddingBefore(); 1700 1701 setLogicalHeight(logicalHeight() + shapeTopLineTopDelta); 1702 logicalLineTopInFlowThread += shapeTopLineTopDelta; 1703 layoutState.setAdjustedLogicalLineTop(0); 1704 } 1705 1706 LayoutUnit lineTop = logicalLineTopInFlowThread - currentRegion->logicalTopForFlowThreadContent() + currentRegion->borderAndPaddingBefore(); 1707 shapeInsideInfo->computeSegmentsForLine(lineTop, lineHeight); 1708 1709 if (currentRegion->isLastRegion()) 1710 pushShapeContentOverflowBelowTheContentBox(this, shapeInsideInfo, lineTop, lineHeight); 1711 } 1712 1713 bool RenderBlock::adjustLogicalLineTopAndLogicalHeightIfNeeded(ShapeInsideInfo* shapeInsideInfo, LayoutUnit absoluteLogicalTop, LineLayoutState& layoutState, InlineBidiResolver& resolver, FloatingObject* lastFloatFromPreviousLine, InlineIterator& end, WordMeasurements& wordMeasurements) 1714 { 1715 LayoutUnit adjustedLogicalLineTop = adjustLogicalLineTop(shapeInsideInfo, resolver.position(), end, wordMeasurements); 1716 if (!adjustedLogicalLineTop) 1717 return false; 1718 1719 LayoutUnit newLogicalHeight = adjustedLogicalLineTop - absoluteLogicalTop; 1720 1721 if (layoutState.flowThread()) { 1722 layoutState.setAdjustedLogicalLineTop(adjustedLogicalLineTop); 1723 newLogicalHeight = logicalHeight(); 1724 } 1725 1726 1727 end = restartLayoutRunsAndFloatsInRange(logicalHeight(), newLogicalHeight, lastFloatFromPreviousLine, resolver, end); 1728 return true; 1729 } 1730 1731 void RenderBlock::layoutRunsAndFloatsInRange(LineLayoutState& layoutState, InlineBidiResolver& resolver, const InlineIterator& cleanLineStart, const BidiStatus& cleanLineBidiStatus, unsigned consecutiveHyphenatedLines) 1732 { 1733 RenderStyle* styleToUse = style(); 1734 bool paginated = view()->layoutState() && view()->layoutState()->isPaginated(); 1735 LineMidpointState& lineMidpointState = resolver.midpointState(); 1736 InlineIterator end = resolver.position(); 1737 bool checkForEndLineMatch = layoutState.endLine(); 1738 RenderTextInfo renderTextInfo; 1739 VerticalPositionCache verticalPositionCache; 1740 1741 LineBreaker lineBreaker(this); 1742 1743 LayoutUnit absoluteLogicalTop; 1744 ShapeInsideInfo* shapeInsideInfo = layoutShapeInsideInfo(); 1745 if (shapeInsideInfo) { 1746 ASSERT(shapeInsideInfo->owner() == this || allowsShapeInsideInfoSharing()); 1747 if (shapeInsideInfo != this->shapeInsideInfo()) { 1748 // FIXME Bug 100284: If subsequent LayoutStates are pushed, we will have to add 1749 // their offsets from the original shape-inside container. 1750 absoluteLogicalTop = logicalTop(); 1751 } 1752 // Begin layout at the logical top of our shape inside. 1753 if (logicalHeight() + absoluteLogicalTop < shapeInsideInfo->shapeLogicalTop()) { 1754 LayoutUnit logicalHeight = shapeInsideInfo->shapeLogicalTop() - absoluteLogicalTop; 1755 if (layoutState.flowThread()) 1756 logicalHeight -= shapeInsideInfo->owner()->borderAndPaddingBefore(); 1757 setLogicalHeight(logicalHeight); 1758 } 1759 } 1760 1761 while (!end.atEnd()) { 1762 // FIXME: Is this check necessary before the first iteration or can it be moved to the end? 1763 if (checkForEndLineMatch) { 1764 layoutState.setEndLineMatched(matchedEndLine(layoutState, resolver, cleanLineStart, cleanLineBidiStatus)); 1765 if (layoutState.endLineMatched()) { 1766 resolver.setPosition(InlineIterator(resolver.position().root(), 0, 0), 0); 1767 break; 1768 } 1769 } 1770 1771 lineMidpointState.reset(); 1772 1773 layoutState.lineInfo().setEmpty(true); 1774 layoutState.lineInfo().resetRunsFromLeadingWhitespace(); 1775 1776 const InlineIterator oldEnd = end; 1777 bool isNewUBAParagraph = layoutState.lineInfo().previousLineBrokeCleanly(); 1778 FloatingObject* lastFloatFromPreviousLine = (containsFloats()) ? m_floatingObjects->set().last() : 0; 1779 1780 updateShapeAndSegmentsForCurrentLine(shapeInsideInfo, absoluteLogicalTop, layoutState); 1781 1782 WordMeasurements wordMeasurements; 1783 end = lineBreaker.nextLineBreak(resolver, layoutState.lineInfo(), renderTextInfo, lastFloatFromPreviousLine, consecutiveHyphenatedLines, wordMeasurements); 1784 renderTextInfo.m_lineBreakIterator.resetPriorContext(); 1785 if (resolver.position().atEnd()) { 1786 // FIXME: We shouldn't be creating any runs in nextLineBreak to begin with! 1787 // Once BidiRunList is separated from BidiResolver this will not be needed. 1788 resolver.runs().deleteRuns(); 1789 resolver.markCurrentRunEmpty(); // FIXME: This can probably be replaced by an ASSERT (or just removed). 1790 layoutState.setCheckForFloatsFromLastLine(true); 1791 resolver.setPosition(InlineIterator(resolver.position().root(), 0, 0), 0); 1792 break; 1793 } 1794 1795 if (adjustLogicalLineTopAndLogicalHeightIfNeeded(shapeInsideInfo, absoluteLogicalTop, layoutState, resolver, lastFloatFromPreviousLine, end, wordMeasurements)) 1796 continue; 1797 1798 ASSERT(end != resolver.position()); 1799 1800 // This is a short-cut for empty lines. 1801 if (layoutState.lineInfo().isEmpty()) { 1802 if (lastRootBox()) 1803 lastRootBox()->setLineBreakInfo(end.m_obj, end.m_pos, resolver.status()); 1804 } else { 1805 VisualDirectionOverride override = (styleToUse->rtlOrdering() == VisualOrder ? (styleToUse->direction() == LTR ? VisualLeftToRightOverride : VisualRightToLeftOverride) : NoVisualOverride); 1806 1807 if (isNewUBAParagraph && styleToUse->unicodeBidi() == Plaintext && !resolver.context()->parent()) { 1808 TextDirection direction = styleToUse->direction(); 1809 determineDirectionality(direction, resolver.position()); 1810 resolver.setStatus(BidiStatus(direction, isOverride(styleToUse->unicodeBidi()))); 1811 } 1812 // FIXME: This ownership is reversed. We should own the BidiRunList and pass it to createBidiRunsForLine. 1813 BidiRunList<BidiRun>& bidiRuns = resolver.runs(); 1814 constructBidiRunsForLine(this, resolver, bidiRuns, end, override, layoutState.lineInfo().previousLineBrokeCleanly()); 1815 ASSERT(resolver.position() == end); 1816 1817 BidiRun* trailingSpaceRun = !layoutState.lineInfo().previousLineBrokeCleanly() ? handleTrailingSpaces(bidiRuns, resolver.context()) : 0; 1818 1819 if (bidiRuns.runCount() && lineBreaker.lineWasHyphenated()) { 1820 bidiRuns.logicallyLastRun()->m_hasHyphen = true; 1821 consecutiveHyphenatedLines++; 1822 } else 1823 consecutiveHyphenatedLines = 0; 1824 1825 // Now that the runs have been ordered, we create the line boxes. 1826 // At the same time we figure out where border/padding/margin should be applied for 1827 // inline flow boxes. 1828 1829 LayoutUnit oldLogicalHeight = logicalHeight(); 1830 RootInlineBox* lineBox = createLineBoxesFromBidiRuns(bidiRuns, end, layoutState.lineInfo(), verticalPositionCache, trailingSpaceRun, wordMeasurements); 1831 1832 bidiRuns.deleteRuns(); 1833 resolver.markCurrentRunEmpty(); // FIXME: This can probably be replaced by an ASSERT (or just removed). 1834 1835 if (lineBox) { 1836 lineBox->setLineBreakInfo(end.m_obj, end.m_pos, resolver.status()); 1837 if (layoutState.usesRepaintBounds()) 1838 layoutState.updateRepaintRangeFromBox(lineBox); 1839 1840 if (paginated) { 1841 LayoutUnit adjustment = 0; 1842 adjustLinePositionForPagination(lineBox, adjustment, layoutState.flowThread()); 1843 if (adjustment) { 1844 LayoutUnit oldLineWidth = availableLogicalWidthForLine(oldLogicalHeight, layoutState.lineInfo().isFirstLine()); 1845 lineBox->adjustBlockDirectionPosition(adjustment); 1846 if (layoutState.usesRepaintBounds()) 1847 layoutState.updateRepaintRangeFromBox(lineBox); 1848 1849 if (availableLogicalWidthForLine(oldLogicalHeight + adjustment, layoutState.lineInfo().isFirstLine()) != oldLineWidth) { 1850 // We have to delete this line, remove all floats that got added, and let line layout re-run. 1851 lineBox->deleteLine(); 1852 end = restartLayoutRunsAndFloatsInRange(oldLogicalHeight, oldLogicalHeight + adjustment, lastFloatFromPreviousLine, resolver, oldEnd); 1853 continue; 1854 } 1855 1856 setLogicalHeight(lineBox->lineBottomWithLeading()); 1857 } 1858 1859 if (layoutState.flowThread()) 1860 lineBox->setContainingRegion(regionAtBlockOffset(lineBox->lineTopWithLeading())); 1861 } 1862 } 1863 } 1864 1865 for (size_t i = 0; i < lineBreaker.positionedObjects().size(); ++i) 1866 setStaticPositions(this, lineBreaker.positionedObjects()[i]); 1867 1868 if (!layoutState.lineInfo().isEmpty()) { 1869 layoutState.lineInfo().setFirstLine(false); 1870 newLine(lineBreaker.clear()); 1871 } 1872 1873 if (m_floatingObjects && lastRootBox()) { 1874 const FloatingObjectSet& floatingObjectSet = m_floatingObjects->set(); 1875 FloatingObjectSetIterator it = floatingObjectSet.begin(); 1876 FloatingObjectSetIterator end = floatingObjectSet.end(); 1877 if (layoutState.lastFloat()) { 1878 FloatingObjectSetIterator lastFloatIterator = floatingObjectSet.find(layoutState.lastFloat()); 1879 ASSERT(lastFloatIterator != end); 1880 ++lastFloatIterator; 1881 it = lastFloatIterator; 1882 } 1883 for (; it != end; ++it) { 1884 FloatingObject* f = *it; 1885 appendFloatingObjectToLastLine(f); 1886 ASSERT(f->m_renderer == layoutState.floats()[layoutState.floatIndex()].object); 1887 // If a float's geometry has changed, give up on syncing with clean lines. 1888 if (layoutState.floats()[layoutState.floatIndex()].rect != f->frameRect()) 1889 checkForEndLineMatch = false; 1890 layoutState.setFloatIndex(layoutState.floatIndex() + 1); 1891 } 1892 layoutState.setLastFloat(!floatingObjectSet.isEmpty() ? floatingObjectSet.last() : 0); 1893 } 1894 1895 lineMidpointState.reset(); 1896 resolver.setPosition(end, numberOfIsolateAncestors(end)); 1897 } 1898 1899 if (paginated && !style()->hasAutoWidows()) { 1900 // Check the line boxes to make sure we didn't create unacceptable widows. 1901 // However, we'll prioritize orphans - so nothing we do here should create 1902 // a new orphan. 1903 1904 RootInlineBox* lineBox = lastRootBox(); 1905 1906 // Count from the end of the block backwards, to see how many hanging 1907 // lines we have. 1908 RootInlineBox* firstLineInBlock = firstRootBox(); 1909 int numLinesHanging = 1; 1910 while (lineBox && lineBox != firstLineInBlock && !lineBox->isFirstAfterPageBreak()) { 1911 ++numLinesHanging; 1912 lineBox = lineBox->prevRootBox(); 1913 } 1914 1915 // If there were no breaks in the block, we didn't create any widows. 1916 if (!lineBox || !lineBox->isFirstAfterPageBreak() || lineBox == firstLineInBlock) 1917 return; 1918 1919 if (numLinesHanging < style()->widows()) { 1920 // We have detected a widow. Now we need to work out how many 1921 // lines there are on the previous page, and how many we need 1922 // to steal. 1923 int numLinesNeeded = style()->widows() - numLinesHanging; 1924 RootInlineBox* currentFirstLineOfNewPage = lineBox; 1925 1926 // Count the number of lines in the previous page. 1927 lineBox = lineBox->prevRootBox(); 1928 int numLinesInPreviousPage = 1; 1929 while (lineBox && lineBox != firstLineInBlock && !lineBox->isFirstAfterPageBreak()) { 1930 ++numLinesInPreviousPage; 1931 lineBox = lineBox->prevRootBox(); 1932 } 1933 1934 // If there was an explicit value for orphans, respect that. If not, we still 1935 // shouldn't create a situation where we make an orphan bigger than the initial value. 1936 // This means that setting widows implies we also care about orphans, but given 1937 // the specification says the initial orphan value is non-zero, this is ok. The 1938 // author is always free to set orphans explicitly as well. 1939 int orphans = style()->hasAutoOrphans() ? style()->initialOrphans() : style()->orphans(); 1940 int numLinesAvailable = numLinesInPreviousPage - orphans; 1941 if (numLinesAvailable <= 0) 1942 return; 1943 1944 int numLinesToTake = min(numLinesAvailable, numLinesNeeded); 1945 // Wind back from our first widowed line. 1946 lineBox = currentFirstLineOfNewPage; 1947 for (int i = 0; i < numLinesToTake; ++i) 1948 lineBox = lineBox->prevRootBox(); 1949 1950 // We now want to break at this line. Remember for next layout and trigger relayout. 1951 setBreakAtLineToAvoidWidow(lineBox); 1952 markLinesDirtyInBlockRange(lastRootBox()->lineBottomWithLeading(), lineBox->lineBottomWithLeading(), lineBox); 1953 } 1954 } 1955 } 1956 1957 void RenderBlock::linkToEndLineIfNeeded(LineLayoutState& layoutState) 1958 { 1959 if (layoutState.endLine()) { 1960 if (layoutState.endLineMatched()) { 1961 bool paginated = view()->layoutState() && view()->layoutState()->isPaginated(); 1962 // Attach all the remaining lines, and then adjust their y-positions as needed. 1963 LayoutUnit delta = logicalHeight() - layoutState.endLineLogicalTop(); 1964 for (RootInlineBox* line = layoutState.endLine(); line; line = line->nextRootBox()) { 1965 line->attachLine(); 1966 if (paginated) { 1967 delta -= line->paginationStrut(); 1968 adjustLinePositionForPagination(line, delta, layoutState.flowThread()); 1969 } 1970 if (delta) { 1971 layoutState.updateRepaintRangeFromBox(line, delta); 1972 line->adjustBlockDirectionPosition(delta); 1973 } 1974 if (layoutState.flowThread()) 1975 line->setContainingRegion(regionAtBlockOffset(line->lineTopWithLeading())); 1976 if (Vector<RenderBox*>* cleanLineFloats = line->floatsPtr()) { 1977 Vector<RenderBox*>::iterator end = cleanLineFloats->end(); 1978 for (Vector<RenderBox*>::iterator f = cleanLineFloats->begin(); f != end; ++f) { 1979 FloatingObject* floatingObject = insertFloatingObject(*f); 1980 ASSERT(!floatingObject->m_originatingLine); 1981 floatingObject->m_originatingLine = line; 1982 setLogicalHeight(logicalTopForChild(*f) - marginBeforeForChild(*f) + delta); 1983 positionNewFloats(); 1984 } 1985 } 1986 } 1987 setLogicalHeight(lastRootBox()->lineBottomWithLeading()); 1988 } else { 1989 // Delete all the remaining lines. 1990 deleteLineRange(layoutState, layoutState.endLine()); 1991 } 1992 } 1993 1994 if (m_floatingObjects && (layoutState.checkForFloatsFromLastLine() || positionNewFloats()) && lastRootBox()) { 1995 // In case we have a float on the last line, it might not be positioned up to now. 1996 // This has to be done before adding in the bottom border/padding, or the float will 1997 // include the padding incorrectly. -dwh 1998 if (layoutState.checkForFloatsFromLastLine()) { 1999 LayoutUnit bottomVisualOverflow = lastRootBox()->logicalBottomVisualOverflow(); 2000 LayoutUnit bottomLayoutOverflow = lastRootBox()->logicalBottomLayoutOverflow(); 2001 TrailingFloatsRootInlineBox* trailingFloatsLineBox = new TrailingFloatsRootInlineBox(this); 2002 m_lineBoxes.appendLineBox(trailingFloatsLineBox); 2003 trailingFloatsLineBox->setConstructed(); 2004 GlyphOverflowAndFallbackFontsMap textBoxDataMap; 2005 VerticalPositionCache verticalPositionCache; 2006 LayoutUnit blockLogicalHeight = logicalHeight(); 2007 trailingFloatsLineBox->alignBoxesInBlockDirection(blockLogicalHeight, textBoxDataMap, verticalPositionCache); 2008 trailingFloatsLineBox->setLineTopBottomPositions(blockLogicalHeight, blockLogicalHeight, blockLogicalHeight, blockLogicalHeight); 2009 trailingFloatsLineBox->setPaginatedLineWidth(availableLogicalWidthForContent(blockLogicalHeight)); 2010 LayoutRect logicalLayoutOverflow(0, blockLogicalHeight, 1, bottomLayoutOverflow - blockLogicalHeight); 2011 LayoutRect logicalVisualOverflow(0, blockLogicalHeight, 1, bottomVisualOverflow - blockLogicalHeight); 2012 trailingFloatsLineBox->setOverflowFromLogicalRects(logicalLayoutOverflow, logicalVisualOverflow, trailingFloatsLineBox->lineTop(), trailingFloatsLineBox->lineBottom()); 2013 if (layoutState.flowThread()) 2014 trailingFloatsLineBox->setContainingRegion(regionAtBlockOffset(trailingFloatsLineBox->lineTopWithLeading())); 2015 } 2016 2017 const FloatingObjectSet& floatingObjectSet = m_floatingObjects->set(); 2018 FloatingObjectSetIterator it = floatingObjectSet.begin(); 2019 FloatingObjectSetIterator end = floatingObjectSet.end(); 2020 if (layoutState.lastFloat()) { 2021 FloatingObjectSetIterator lastFloatIterator = floatingObjectSet.find(layoutState.lastFloat()); 2022 ASSERT(lastFloatIterator != end); 2023 ++lastFloatIterator; 2024 it = lastFloatIterator; 2025 } 2026 for (; it != end; ++it) 2027 appendFloatingObjectToLastLine(*it); 2028 layoutState.setLastFloat(!floatingObjectSet.isEmpty() ? floatingObjectSet.last() : 0); 2029 } 2030 } 2031 2032 void RenderBlock::repaintDirtyFloats(Vector<FloatWithRect>& floats) 2033 { 2034 size_t floatCount = floats.size(); 2035 // Floats that did not have layout did not repaint when we laid them out. They would have 2036 // painted by now if they had moved, but if they stayed at (0, 0), they still need to be 2037 // painted. 2038 for (size_t i = 0; i < floatCount; ++i) { 2039 if (!floats[i].everHadLayout) { 2040 RenderBox* f = floats[i].object; 2041 if (!f->x() && !f->y() && f->checkForRepaintDuringLayout()) 2042 f->repaint(); 2043 } 2044 } 2045 } 2046 2047 void RenderBlock::layoutInlineChildren(bool relayoutChildren, LayoutUnit& repaintLogicalTop, LayoutUnit& repaintLogicalBottom) 2048 { 2049 setLogicalHeight(borderBefore() + paddingBefore()); 2050 2051 // Lay out our hypothetical grid line as though it occurs at the top of the block. 2052 if (view()->layoutState() && view()->layoutState()->lineGrid() == this) 2053 layoutLineGridBox(); 2054 2055 RenderFlowThread* flowThread = flowThreadContainingBlock(); 2056 bool clearLinesForPagination = firstLineBox() && flowThread && !flowThread->hasRegions(); 2057 2058 // Figure out if we should clear out our line boxes. 2059 // FIXME: Handle resize eventually! 2060 bool isFullLayout = !firstLineBox() || selfNeedsLayout() || relayoutChildren || clearLinesForPagination; 2061 LineLayoutState layoutState(isFullLayout, repaintLogicalTop, repaintLogicalBottom, flowThread); 2062 2063 if (isFullLayout) 2064 lineBoxes()->deleteLineBoxes(); 2065 2066 // Text truncation kicks in in two cases: 2067 // 1) If your overflow isn't visible and your text-overflow-mode isn't clip. 2068 // 2) If you're an anonymous block with a block parent that satisfies #1. 2069 // FIXME: CSS3 says that descendants that are clipped must also know how to truncate. This is insanely 2070 // difficult to figure out in general (especially in the middle of doing layout), so we only handle the 2071 // simple case of an anonymous block truncating when it's parent is clipped. 2072 bool hasTextOverflow = (style()->textOverflow() && hasOverflowClip()) 2073 || (isAnonymousBlock() && parent() && parent()->isRenderBlock() && parent()->style()->textOverflow() && parent()->hasOverflowClip()); 2074 2075 // Walk all the lines and delete our ellipsis line boxes if they exist. 2076 if (hasTextOverflow) 2077 deleteEllipsisLineBoxes(); 2078 2079 if (firstChild()) { 2080 // In full layout mode, clear the line boxes of children upfront. Otherwise, 2081 // siblings can run into stale root lineboxes during layout. Then layout 2082 // the replaced elements later. In partial layout mode, line boxes are not 2083 // deleted and only dirtied. In that case, we can layout the replaced 2084 // elements at the same time. 2085 bool hasInlineChild = false; 2086 Vector<RenderBox*> replacedChildren; 2087 for (InlineWalker walker(this); !walker.atEnd(); walker.advance()) { 2088 RenderObject* o = walker.current(); 2089 if (!hasInlineChild && o->isInline()) 2090 hasInlineChild = true; 2091 2092 if (o->isReplaced() || o->isFloating() || o->isOutOfFlowPositioned()) { 2093 RenderBox* box = toRenderBox(o); 2094 2095 if (relayoutChildren || box->hasRelativeDimensions()) 2096 o->setChildNeedsLayout(MarkOnlyThis); 2097 2098 // If relayoutChildren is set and the child has percentage padding or an embedded content box, we also need to invalidate the childs pref widths. 2099 if (relayoutChildren && box->needsPreferredWidthsRecalculation()) 2100 o->setPreferredLogicalWidthsDirty(true, MarkOnlyThis); 2101 2102 if (o->isOutOfFlowPositioned()) 2103 o->containingBlock()->insertPositionedObject(box); 2104 else if (o->isFloating()) 2105 layoutState.floats().append(FloatWithRect(box)); 2106 else if (isFullLayout || o->needsLayout()) { 2107 // Replaced element. 2108 box->dirtyLineBoxes(isFullLayout); 2109 if (isFullLayout) 2110 replacedChildren.append(box); 2111 else 2112 o->layoutIfNeeded(); 2113 } 2114 } else if (o->isText() || (o->isRenderInline() && !walker.atEndOfInline())) { 2115 if (!o->isText()) 2116 toRenderInline(o)->updateAlwaysCreateLineBoxes(layoutState.isFullLayout()); 2117 if (layoutState.isFullLayout() || o->selfNeedsLayout()) 2118 dirtyLineBoxesForRenderer(o, layoutState.isFullLayout()); 2119 o->clearNeedsLayout(); 2120 } 2121 } 2122 2123 for (size_t i = 0; i < replacedChildren.size(); i++) 2124 replacedChildren[i]->layoutIfNeeded(); 2125 2126 layoutRunsAndFloats(layoutState, hasInlineChild); 2127 } 2128 2129 // Expand the last line to accommodate Ruby and emphasis marks. 2130 int lastLineAnnotationsAdjustment = 0; 2131 if (lastRootBox()) { 2132 LayoutUnit lowestAllowedPosition = max(lastRootBox()->lineBottom(), logicalHeight() + paddingAfter()); 2133 if (!style()->isFlippedLinesWritingMode()) 2134 lastLineAnnotationsAdjustment = lastRootBox()->computeUnderAnnotationAdjustment(lowestAllowedPosition); 2135 else 2136 lastLineAnnotationsAdjustment = lastRootBox()->computeOverAnnotationAdjustment(lowestAllowedPosition); 2137 } 2138 2139 // Now add in the bottom border/padding. 2140 setLogicalHeight(logicalHeight() + lastLineAnnotationsAdjustment + borderAfter() + paddingAfter() + scrollbarLogicalHeight()); 2141 2142 if (!firstLineBox() && hasLineIfEmpty()) 2143 setLogicalHeight(logicalHeight() + lineHeight(true, isHorizontalWritingMode() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes)); 2144 2145 // See if we have any lines that spill out of our block. If we do, then we will possibly need to 2146 // truncate text. 2147 if (hasTextOverflow) 2148 checkLinesForTextOverflow(); 2149 } 2150 2151 void RenderBlock::checkFloatsInCleanLine(RootInlineBox* line, Vector<FloatWithRect>& floats, size_t& floatIndex, bool& encounteredNewFloat, bool& dirtiedByFloat) 2152 { 2153 Vector<RenderBox*>* cleanLineFloats = line->floatsPtr(); 2154 if (!cleanLineFloats) 2155 return; 2156 2157 Vector<RenderBox*>::iterator end = cleanLineFloats->end(); 2158 for (Vector<RenderBox*>::iterator it = cleanLineFloats->begin(); it != end; ++it) { 2159 RenderBox* floatingBox = *it; 2160 floatingBox->layoutIfNeeded(); 2161 LayoutSize newSize(floatingBox->width() + floatingBox->marginWidth(), floatingBox->height() + floatingBox->marginHeight()); 2162 if (floats[floatIndex].object != floatingBox) { 2163 encounteredNewFloat = true; 2164 return; 2165 } 2166 2167 if (floats[floatIndex].rect.size() != newSize) { 2168 LayoutUnit floatTop = isHorizontalWritingMode() ? floats[floatIndex].rect.y() : floats[floatIndex].rect.x(); 2169 LayoutUnit floatHeight = isHorizontalWritingMode() ? max(floats[floatIndex].rect.height(), newSize.height()) 2170 : max(floats[floatIndex].rect.width(), newSize.width()); 2171 floatHeight = min(floatHeight, LayoutUnit::max() - floatTop); 2172 line->markDirty(); 2173 markLinesDirtyInBlockRange(line->lineBottomWithLeading(), floatTop + floatHeight, line); 2174 floats[floatIndex].rect.setSize(newSize); 2175 dirtiedByFloat = true; 2176 } 2177 floatIndex++; 2178 } 2179 } 2180 2181 RootInlineBox* RenderBlock::determineStartPosition(LineLayoutState& layoutState, InlineBidiResolver& resolver) 2182 { 2183 RootInlineBox* curr = 0; 2184 RootInlineBox* last = 0; 2185 2186 // FIXME: This entire float-checking block needs to be broken into a new function. 2187 bool dirtiedByFloat = false; 2188 if (!layoutState.isFullLayout()) { 2189 // Paginate all of the clean lines. 2190 bool paginated = view()->layoutState() && view()->layoutState()->isPaginated(); 2191 LayoutUnit paginationDelta = 0; 2192 size_t floatIndex = 0; 2193 for (curr = firstRootBox(); curr && !curr->isDirty(); curr = curr->nextRootBox()) { 2194 if (paginated) { 2195 if (lineWidthForPaginatedLineChanged(curr, 0, layoutState.flowThread())) { 2196 curr->markDirty(); 2197 break; 2198 } 2199 paginationDelta -= curr->paginationStrut(); 2200 adjustLinePositionForPagination(curr, paginationDelta, layoutState.flowThread()); 2201 if (paginationDelta) { 2202 if (containsFloats() || !layoutState.floats().isEmpty()) { 2203 // FIXME: Do better eventually. For now if we ever shift because of pagination and floats are present just go to a full layout. 2204 layoutState.markForFullLayout(); 2205 break; 2206 } 2207 2208 layoutState.updateRepaintRangeFromBox(curr, paginationDelta); 2209 curr->adjustBlockDirectionPosition(paginationDelta); 2210 } 2211 if (layoutState.flowThread()) 2212 curr->setContainingRegion(regionAtBlockOffset(curr->lineTopWithLeading())); 2213 } 2214 2215 // If a new float has been inserted before this line or before its last known float, just do a full layout. 2216 bool encounteredNewFloat = false; 2217 checkFloatsInCleanLine(curr, layoutState.floats(), floatIndex, encounteredNewFloat, dirtiedByFloat); 2218 if (encounteredNewFloat) 2219 layoutState.markForFullLayout(); 2220 2221 if (dirtiedByFloat || layoutState.isFullLayout()) 2222 break; 2223 } 2224 // Check if a new float has been inserted after the last known float. 2225 if (!curr && floatIndex < layoutState.floats().size()) 2226 layoutState.markForFullLayout(); 2227 } 2228 2229 if (layoutState.isFullLayout()) { 2230 // FIXME: This should just call deleteLineBoxTree, but that causes 2231 // crashes for fast/repaint tests. 2232 curr = firstRootBox(); 2233 while (curr) { 2234 // Note: This uses nextRootBox() insted of nextLineBox() like deleteLineBoxTree does. 2235 RootInlineBox* next = curr->nextRootBox(); 2236 curr->deleteLine(); 2237 curr = next; 2238 } 2239 ASSERT(!firstLineBox() && !lastLineBox()); 2240 } else { 2241 if (curr) { 2242 // We have a dirty line. 2243 if (RootInlineBox* prevRootBox = curr->prevRootBox()) { 2244 // We have a previous line. 2245 if (!dirtiedByFloat && (!prevRootBox->endsWithBreak() || !prevRootBox->lineBreakObj() || (prevRootBox->lineBreakObj()->isText() && prevRootBox->lineBreakPos() >= toRenderText(prevRootBox->lineBreakObj())->textLength()))) 2246 // The previous line didn't break cleanly or broke at a newline 2247 // that has been deleted, so treat it as dirty too. 2248 curr = prevRootBox; 2249 } 2250 } else { 2251 // No dirty lines were found. 2252 // If the last line didn't break cleanly, treat it as dirty. 2253 if (lastRootBox() && !lastRootBox()->endsWithBreak()) 2254 curr = lastRootBox(); 2255 } 2256 2257 // If we have no dirty lines, then last is just the last root box. 2258 last = curr ? curr->prevRootBox() : lastRootBox(); 2259 } 2260 2261 unsigned numCleanFloats = 0; 2262 if (!layoutState.floats().isEmpty()) { 2263 LayoutUnit savedLogicalHeight = logicalHeight(); 2264 // Restore floats from clean lines. 2265 RootInlineBox* line = firstRootBox(); 2266 while (line != curr) { 2267 if (Vector<RenderBox*>* cleanLineFloats = line->floatsPtr()) { 2268 Vector<RenderBox*>::iterator end = cleanLineFloats->end(); 2269 for (Vector<RenderBox*>::iterator f = cleanLineFloats->begin(); f != end; ++f) { 2270 FloatingObject* floatingObject = insertFloatingObject(*f); 2271 ASSERT(!floatingObject->m_originatingLine); 2272 floatingObject->m_originatingLine = line; 2273 setLogicalHeight(logicalTopForChild(*f) - marginBeforeForChild(*f)); 2274 positionNewFloats(); 2275 ASSERT(layoutState.floats()[numCleanFloats].object == *f); 2276 numCleanFloats++; 2277 } 2278 } 2279 line = line->nextRootBox(); 2280 } 2281 setLogicalHeight(savedLogicalHeight); 2282 } 2283 layoutState.setFloatIndex(numCleanFloats); 2284 2285 layoutState.lineInfo().setFirstLine(!last); 2286 layoutState.lineInfo().setPreviousLineBrokeCleanly(!last || last->endsWithBreak()); 2287 2288 if (last) { 2289 setLogicalHeight(last->lineBottomWithLeading()); 2290 InlineIterator iter = InlineIterator(this, last->lineBreakObj(), last->lineBreakPos()); 2291 resolver.setPosition(iter, numberOfIsolateAncestors(iter)); 2292 resolver.setStatus(last->lineBreakBidiStatus()); 2293 } else { 2294 TextDirection direction = style()->direction(); 2295 if (style()->unicodeBidi() == Plaintext) 2296 determineDirectionality(direction, InlineIterator(this, bidiFirstSkippingEmptyInlines(this), 0)); 2297 resolver.setStatus(BidiStatus(direction, isOverride(style()->unicodeBidi()))); 2298 InlineIterator iter = InlineIterator(this, bidiFirstSkippingEmptyInlines(this, &resolver), 0); 2299 resolver.setPosition(iter, numberOfIsolateAncestors(iter)); 2300 } 2301 return curr; 2302 } 2303 2304 void RenderBlock::determineEndPosition(LineLayoutState& layoutState, RootInlineBox* startLine, InlineIterator& cleanLineStart, BidiStatus& cleanLineBidiStatus) 2305 { 2306 ASSERT(!layoutState.endLine()); 2307 size_t floatIndex = layoutState.floatIndex(); 2308 RootInlineBox* last = 0; 2309 for (RootInlineBox* curr = startLine->nextRootBox(); curr; curr = curr->nextRootBox()) { 2310 if (!curr->isDirty()) { 2311 bool encounteredNewFloat = false; 2312 bool dirtiedByFloat = false; 2313 checkFloatsInCleanLine(curr, layoutState.floats(), floatIndex, encounteredNewFloat, dirtiedByFloat); 2314 if (encounteredNewFloat) 2315 return; 2316 } 2317 if (curr->isDirty()) 2318 last = 0; 2319 else if (!last) 2320 last = curr; 2321 } 2322 2323 if (!last) 2324 return; 2325 2326 // At this point, |last| is the first line in a run of clean lines that ends with the last line 2327 // in the block. 2328 2329 RootInlineBox* prev = last->prevRootBox(); 2330 cleanLineStart = InlineIterator(this, prev->lineBreakObj(), prev->lineBreakPos()); 2331 cleanLineBidiStatus = prev->lineBreakBidiStatus(); 2332 layoutState.setEndLineLogicalTop(prev->lineBottomWithLeading()); 2333 2334 for (RootInlineBox* line = last; line; line = line->nextRootBox()) 2335 line->extractLine(); // Disconnect all line boxes from their render objects while preserving 2336 // their connections to one another. 2337 2338 layoutState.setEndLine(last); 2339 } 2340 2341 bool RenderBlock::checkPaginationAndFloatsAtEndLine(LineLayoutState& layoutState) 2342 { 2343 LayoutUnit lineDelta = logicalHeight() - layoutState.endLineLogicalTop(); 2344 2345 bool paginated = view()->layoutState() && view()->layoutState()->isPaginated(); 2346 if (paginated && layoutState.flowThread()) { 2347 // Check all lines from here to the end, and see if the hypothetical new position for the lines will result 2348 // in a different available line width. 2349 for (RootInlineBox* lineBox = layoutState.endLine(); lineBox; lineBox = lineBox->nextRootBox()) { 2350 if (paginated) { 2351 // This isn't the real move we're going to do, so don't update the line box's pagination 2352 // strut yet. 2353 LayoutUnit oldPaginationStrut = lineBox->paginationStrut(); 2354 lineDelta -= oldPaginationStrut; 2355 adjustLinePositionForPagination(lineBox, lineDelta, layoutState.flowThread()); 2356 lineBox->setPaginationStrut(oldPaginationStrut); 2357 } 2358 if (lineWidthForPaginatedLineChanged(lineBox, lineDelta, layoutState.flowThread())) 2359 return false; 2360 } 2361 } 2362 2363 if (!lineDelta || !m_floatingObjects) 2364 return true; 2365 2366 // See if any floats end in the range along which we want to shift the lines vertically. 2367 LayoutUnit logicalTop = min(logicalHeight(), layoutState.endLineLogicalTop()); 2368 2369 RootInlineBox* lastLine = layoutState.endLine(); 2370 while (RootInlineBox* nextLine = lastLine->nextRootBox()) 2371 lastLine = nextLine; 2372 2373 LayoutUnit logicalBottom = lastLine->lineBottomWithLeading() + absoluteValue(lineDelta); 2374 2375 const FloatingObjectSet& floatingObjectSet = m_floatingObjects->set(); 2376 FloatingObjectSetIterator end = floatingObjectSet.end(); 2377 for (FloatingObjectSetIterator it = floatingObjectSet.begin(); it != end; ++it) { 2378 FloatingObject* f = *it; 2379 if (logicalBottomForFloat(f) >= logicalTop && logicalBottomForFloat(f) < logicalBottom) 2380 return false; 2381 } 2382 2383 return true; 2384 } 2385 2386 bool RenderBlock::matchedEndLine(LineLayoutState& layoutState, const InlineBidiResolver& resolver, const InlineIterator& endLineStart, const BidiStatus& endLineStatus) 2387 { 2388 if (resolver.position() == endLineStart) { 2389 if (resolver.status() != endLineStatus) 2390 return false; 2391 return checkPaginationAndFloatsAtEndLine(layoutState); 2392 } 2393 2394 // The first clean line doesn't match, but we can check a handful of following lines to try 2395 // to match back up. 2396 static int numLines = 8; // The # of lines we're willing to match against. 2397 RootInlineBox* originalEndLine = layoutState.endLine(); 2398 RootInlineBox* line = originalEndLine; 2399 for (int i = 0; i < numLines && line; i++, line = line->nextRootBox()) { 2400 if (line->lineBreakObj() == resolver.position().m_obj && line->lineBreakPos() == resolver.position().m_pos) { 2401 // We have a match. 2402 if (line->lineBreakBidiStatus() != resolver.status()) 2403 return false; // ...but the bidi state doesn't match. 2404 2405 bool matched = false; 2406 RootInlineBox* result = line->nextRootBox(); 2407 layoutState.setEndLine(result); 2408 if (result) { 2409 layoutState.setEndLineLogicalTop(line->lineBottomWithLeading()); 2410 matched = checkPaginationAndFloatsAtEndLine(layoutState); 2411 } 2412 2413 // Now delete the lines that we failed to sync. 2414 deleteLineRange(layoutState, originalEndLine, result); 2415 return matched; 2416 } 2417 } 2418 2419 return false; 2420 } 2421 2422 enum WhitespacePosition { LeadingWhitespace, TrailingWhitespace }; 2423 static inline bool shouldCollapseWhiteSpace(const RenderStyle* style, const LineInfo& lineInfo, WhitespacePosition whitespacePosition) 2424 { 2425 // CSS2 16.6.1 2426 // If a space (U+0020) at the beginning of a line has 'white-space' set to 'normal', 'nowrap', or 'pre-line', it is removed. 2427 // If a space (U+0020) at the end of a line has 'white-space' set to 'normal', 'nowrap', or 'pre-line', it is also removed. 2428 // If spaces (U+0020) or tabs (U+0009) at the end of a line have 'white-space' set to 'pre-wrap', UAs may visually collapse them. 2429 return style->collapseWhiteSpace() 2430 || (whitespacePosition == TrailingWhitespace && style->whiteSpace() == PRE_WRAP && (!lineInfo.isEmpty() || !lineInfo.previousLineBrokeCleanly())); 2431 } 2432 2433 static bool requiresLineBoxForContent(RenderInline* flow, const LineInfo& lineInfo) 2434 { 2435 RenderObject* parent = flow->parent(); 2436 if (flow->document()->inNoQuirksMode() 2437 && (flow->style(lineInfo.isFirstLine())->lineHeight() != parent->style(lineInfo.isFirstLine())->lineHeight() 2438 || flow->style()->verticalAlign() != parent->style()->verticalAlign() 2439 || !parent->style()->font().fontMetrics().hasIdenticalAscentDescentAndLineGap(flow->style()->font().fontMetrics()))) 2440 return true; 2441 return false; 2442 } 2443 2444 static bool alwaysRequiresLineBox(RenderObject* flow) 2445 { 2446 // FIXME: Right now, we only allow line boxes for inlines that are truly empty. 2447 // We need to fix this, though, because at the very least, inlines containing only 2448 // ignorable whitespace should should also have line boxes. 2449 return isEmptyInline(flow) && toRenderInline(flow)->hasInlineDirectionBordersPaddingOrMargin(); 2450 } 2451 2452 static bool requiresLineBox(const InlineIterator& it, const LineInfo& lineInfo = LineInfo(), WhitespacePosition whitespacePosition = LeadingWhitespace) 2453 { 2454 if (it.m_obj->isFloatingOrOutOfFlowPositioned()) 2455 return false; 2456 2457 if (it.m_obj->isRenderInline() && !alwaysRequiresLineBox(it.m_obj) && !requiresLineBoxForContent(toRenderInline(it.m_obj), lineInfo)) 2458 return false; 2459 2460 if (!shouldCollapseWhiteSpace(it.m_obj->style(), lineInfo, whitespacePosition) || it.m_obj->isBR()) 2461 return true; 2462 2463 UChar current = it.current(); 2464 bool notJustWhitespace = current != ' ' && current != '\t' && current != softHyphen && (current != '\n' || it.m_obj->preservesNewline()); 2465 return notJustWhitespace || isEmptyInline(it.m_obj); 2466 } 2467 2468 bool RenderBlock::generatesLineBoxesForInlineChild(RenderObject* inlineObj) 2469 { 2470 ASSERT(inlineObj->parent() == this); 2471 2472 InlineIterator it(this, inlineObj, 0); 2473 // FIXME: We should pass correct value for WhitespacePosition. 2474 while (!it.atEnd() && !requiresLineBox(it)) 2475 it.increment(); 2476 2477 return !it.atEnd(); 2478 } 2479 2480 // FIXME: The entire concept of the skipTrailingWhitespace function is flawed, since we really need to be building 2481 // line boxes even for containers that may ultimately collapse away. Otherwise we'll never get positioned 2482 // elements quite right. In other words, we need to build this function's work into the normal line 2483 // object iteration process. 2484 // NB. this function will insert any floating elements that would otherwise 2485 // be skipped but it will not position them. 2486 void RenderBlock::LineBreaker::skipTrailingWhitespace(InlineIterator& iterator, const LineInfo& lineInfo) 2487 { 2488 while (!iterator.atEnd() && !requiresLineBox(iterator, lineInfo, TrailingWhitespace)) { 2489 RenderObject* object = iterator.m_obj; 2490 if (object->isOutOfFlowPositioned()) 2491 setStaticPositions(m_block, toRenderBox(object)); 2492 else if (object->isFloating()) 2493 m_block->insertFloatingObject(toRenderBox(object)); 2494 iterator.increment(); 2495 } 2496 } 2497 2498 void RenderBlock::LineBreaker::skipLeadingWhitespace(InlineBidiResolver& resolver, LineInfo& lineInfo, 2499 FloatingObject* lastFloatFromPreviousLine, LineWidth& width) 2500 { 2501 while (!resolver.position().atEnd() && !requiresLineBox(resolver.position(), lineInfo, LeadingWhitespace)) { 2502 RenderObject* object = resolver.position().m_obj; 2503 if (object->isOutOfFlowPositioned()) { 2504 setStaticPositions(m_block, toRenderBox(object)); 2505 if (object->style()->isOriginalDisplayInlineType()) { 2506 resolver.runs().addRun(createRun(0, 1, object, resolver)); 2507 lineInfo.incrementRunsFromLeadingWhitespace(); 2508 } 2509 } else if (object->isFloating()) { 2510 // The top margin edge of a self-collapsing block that clears a float intrudes up into it by the height of the margin, 2511 // so in order to place this first child float at the top content edge of the self-collapsing block add the margin back in before placement. 2512 LayoutUnit marginOffset = (!object->previousSibling() && m_block->isSelfCollapsingBlock() && m_block->style()->clear() && m_block->getClearDelta(m_block, LayoutUnit())) ? m_block->collapsedMarginBeforeForChild(m_block) : LayoutUnit(); 2513 LayoutUnit oldLogicalHeight = m_block->logicalHeight(); 2514 m_block->setLogicalHeight(oldLogicalHeight + marginOffset); 2515 m_block->positionNewFloatOnLine(m_block->insertFloatingObject(toRenderBox(object)), lastFloatFromPreviousLine, lineInfo, width); 2516 m_block->setLogicalHeight(oldLogicalHeight); 2517 } else if (object->isText() && object->style()->hasTextCombine() && object->isCombineText() && !toRenderCombineText(object)->isCombined()) { 2518 toRenderCombineText(object)->combineText(); 2519 if (toRenderCombineText(object)->isCombined()) 2520 continue; 2521 } 2522 resolver.increment(); 2523 } 2524 resolver.commitExplicitEmbedding(); 2525 } 2526 2527 // This is currently just used for list markers and inline flows that have line boxes. Neither should 2528 // have an effect on whitespace at the start of the line. 2529 static bool shouldSkipWhitespaceAfterStartObject(RenderBlock* block, RenderObject* o, LineMidpointState& lineMidpointState) 2530 { 2531 RenderObject* next = bidiNextSkippingEmptyInlines(block, o); 2532 while (next && next->isFloatingOrOutOfFlowPositioned()) 2533 next = bidiNextSkippingEmptyInlines(block, next); 2534 2535 if (next && !next->isBR() && next->isText() && toRenderText(next)->textLength() > 0) { 2536 RenderText* nextText = toRenderText(next); 2537 UChar nextChar = nextText->characterAt(0); 2538 if (nextText->style()->isCollapsibleWhiteSpace(nextChar)) { 2539 startIgnoringSpaces(lineMidpointState, InlineIterator(0, o, 0)); 2540 return true; 2541 } 2542 } 2543 2544 return false; 2545 } 2546 2547 static ALWAYS_INLINE float textWidth(RenderText* text, unsigned from, unsigned len, const Font& font, float xPos, bool isFixedPitch, bool collapseWhiteSpace, HashSet<const SimpleFontData*>* fallbackFonts = 0, TextLayout* layout = 0) 2548 { 2549 GlyphOverflow glyphOverflow; 2550 if (isFixedPitch || (!from && len == text->textLength()) || text->style()->hasTextCombine()) 2551 return text->width(from, len, font, xPos, fallbackFonts, &glyphOverflow); 2552 2553 if (layout) 2554 return Font::width(*layout, from, len, fallbackFonts); 2555 2556 TextRun run = RenderBlock::constructTextRun(text, font, text, from, len, text->style()); 2557 run.setCharactersLength(text->textLength() - from); 2558 ASSERT(run.charactersLength() >= run.length()); 2559 2560 run.setCharacterScanForCodePath(!text->canUseSimpleFontCodePath()); 2561 run.setTabSize(!collapseWhiteSpace, text->style()->tabSize()); 2562 run.setXPos(xPos); 2563 return font.width(run, fallbackFonts, &glyphOverflow); 2564 } 2565 2566 class TrailingObjects { 2567 public: 2568 TrailingObjects(); 2569 void setTrailingWhitespace(RenderText*); 2570 void clear(); 2571 void appendBoxIfNeeded(RenderBox*); 2572 2573 enum CollapseFirstSpaceOrNot { DoNotCollapseFirstSpace, CollapseFirstSpace }; 2574 2575 void updateMidpointsForTrailingBoxes(LineMidpointState&, const InlineIterator& lBreak, CollapseFirstSpaceOrNot); 2576 2577 private: 2578 RenderText* m_whitespace; 2579 Vector<RenderBox*, 4> m_boxes; 2580 }; 2581 2582 TrailingObjects::TrailingObjects() 2583 : m_whitespace(0) 2584 { 2585 } 2586 2587 inline void TrailingObjects::setTrailingWhitespace(RenderText* whitespace) 2588 { 2589 ASSERT(whitespace); 2590 m_whitespace = whitespace; 2591 } 2592 2593 inline void TrailingObjects::clear() 2594 { 2595 m_whitespace = 0; 2596 // Using resize(0) rather than clear() here saves 2% on 2597 // PerformanceTests/Layout/line-layout.html because we avoid freeing and 2598 // re-allocating the underlying buffer repeatedly. 2599 m_boxes.resize(0); 2600 } 2601 2602 inline void TrailingObjects::appendBoxIfNeeded(RenderBox* box) 2603 { 2604 if (m_whitespace) 2605 m_boxes.append(box); 2606 } 2607 2608 void TrailingObjects::updateMidpointsForTrailingBoxes(LineMidpointState& lineMidpointState, const InlineIterator& lBreak, CollapseFirstSpaceOrNot collapseFirstSpace) 2609 { 2610 if (!m_whitespace) 2611 return; 2612 2613 // This object is either going to be part of the last midpoint, or it is going to be the actual endpoint. 2614 // In both cases we just decrease our pos by 1 level to exclude the space, allowing it to - in effect - collapse into the newline. 2615 if (lineMidpointState.numMidpoints % 2) { 2616 // Find the trailing space object's midpoint. 2617 int trailingSpaceMidpoint = lineMidpointState.numMidpoints - 1; 2618 for ( ; trailingSpaceMidpoint > 0 && lineMidpointState.midpoints[trailingSpaceMidpoint].m_obj != m_whitespace; --trailingSpaceMidpoint) { } 2619 ASSERT(trailingSpaceMidpoint >= 0); 2620 if (collapseFirstSpace == CollapseFirstSpace) 2621 lineMidpointState.midpoints[trailingSpaceMidpoint].m_pos--; 2622 2623 // Now make sure every single trailingPositionedBox following the trailingSpaceMidpoint properly stops and starts 2624 // ignoring spaces. 2625 size_t currentMidpoint = trailingSpaceMidpoint + 1; 2626 for (size_t i = 0; i < m_boxes.size(); ++i) { 2627 if (currentMidpoint >= lineMidpointState.numMidpoints) { 2628 // We don't have a midpoint for this box yet. 2629 ensureLineBoxInsideIgnoredSpaces(lineMidpointState, m_boxes[i]); 2630 } else { 2631 ASSERT(lineMidpointState.midpoints[currentMidpoint].m_obj == m_boxes[i]); 2632 ASSERT(lineMidpointState.midpoints[currentMidpoint + 1].m_obj == m_boxes[i]); 2633 } 2634 currentMidpoint += 2; 2635 } 2636 } else if (!lBreak.m_obj) { 2637 ASSERT(m_whitespace->isText()); 2638 ASSERT(collapseFirstSpace == CollapseFirstSpace); 2639 // Add a new end midpoint that stops right at the very end. 2640 unsigned length = m_whitespace->textLength(); 2641 unsigned pos = length >= 2 ? length - 2 : INT_MAX; 2642 InlineIterator endMid(0, m_whitespace, pos); 2643 startIgnoringSpaces(lineMidpointState, endMid); 2644 for (size_t i = 0; i < m_boxes.size(); ++i) { 2645 ensureLineBoxInsideIgnoredSpaces(lineMidpointState, m_boxes[i]); 2646 } 2647 } 2648 } 2649 2650 void RenderBlock::LineBreaker::reset() 2651 { 2652 m_positionedObjects.clear(); 2653 m_hyphenated = false; 2654 m_clear = CNONE; 2655 } 2656 2657 InlineIterator RenderBlock::LineBreaker::nextLineBreak(InlineBidiResolver& resolver, LineInfo& lineInfo, RenderTextInfo& renderTextInfo, FloatingObject* lastFloatFromPreviousLine, unsigned consecutiveHyphenatedLines, WordMeasurements& wordMeasurements) 2658 { 2659 ShapeInsideInfo* shapeInsideInfo = m_block->layoutShapeInsideInfo(); 2660 2661 if (!shapeInsideInfo || !shapeInsideInfo->lineOverlapsShapeBounds()) 2662 return nextSegmentBreak(resolver, lineInfo, renderTextInfo, lastFloatFromPreviousLine, consecutiveHyphenatedLines, wordMeasurements); 2663 2664 InlineIterator end = resolver.position(); 2665 InlineIterator oldEnd = end; 2666 2667 if (!shapeInsideInfo->hasSegments()) { 2668 end = nextSegmentBreak(resolver, lineInfo, renderTextInfo, lastFloatFromPreviousLine, consecutiveHyphenatedLines, wordMeasurements); 2669 resolver.setPositionIgnoringNestedIsolates(oldEnd); 2670 return oldEnd; 2671 } 2672 2673 const SegmentList& segments = shapeInsideInfo->segments(); 2674 SegmentRangeList& segmentRanges = shapeInsideInfo->segmentRanges(); 2675 2676 for (unsigned i = 0; i < segments.size() && !end.atEnd(); i++) { 2677 InlineIterator segmentStart = resolver.position(); 2678 end = nextSegmentBreak(resolver, lineInfo, renderTextInfo, lastFloatFromPreviousLine, consecutiveHyphenatedLines, wordMeasurements); 2679 2680 ASSERT(segmentRanges.size() == i); 2681 if (resolver.position().atEnd()) { 2682 segmentRanges.append(LineSegmentRange(segmentStart, end)); 2683 break; 2684 } 2685 if (resolver.position() == end) { 2686 // Nothing fit this segment 2687 end = segmentStart; 2688 segmentRanges.append(LineSegmentRange(segmentStart, segmentStart)); 2689 resolver.setPositionIgnoringNestedIsolates(segmentStart); 2690 } else { 2691 // Note that resolver.position is already skipping some of the white space at the beginning of the line, 2692 // so that's why segmentStart might be different than resolver.position(). 2693 LineSegmentRange range(resolver.position(), end); 2694 segmentRanges.append(range); 2695 resolver.setPosition(end, numberOfIsolateAncestors(end)); 2696 2697 if (lineInfo.previousLineBrokeCleanly()) { 2698 // If we hit a new line break, just stop adding anything to this line. 2699 break; 2700 } 2701 } 2702 } 2703 resolver.setPositionIgnoringNestedIsolates(oldEnd); 2704 return end; 2705 } 2706 2707 static inline bool iteratorIsBeyondEndOfRenderCombineText(const InlineIterator& iter, RenderCombineText* renderer) 2708 { 2709 return iter.m_obj == renderer && iter.m_pos >= renderer->textLength(); 2710 } 2711 2712 InlineIterator RenderBlock::LineBreaker::nextSegmentBreak(InlineBidiResolver& resolver, LineInfo& lineInfo, RenderTextInfo& renderTextInfo, FloatingObject* lastFloatFromPreviousLine, unsigned consecutiveHyphenatedLines, WordMeasurements& wordMeasurements) 2713 { 2714 reset(); 2715 2716 ASSERT(resolver.position().root() == m_block); 2717 2718 bool appliedStartWidth = resolver.position().m_pos > 0; 2719 bool includeEndWidth = true; 2720 LineMidpointState& lineMidpointState = resolver.midpointState(); 2721 2722 LineWidth width(m_block, lineInfo.isFirstLine(), requiresIndent(lineInfo.isFirstLine(), lineInfo.previousLineBrokeCleanly(), m_block->style())); 2723 2724 skipLeadingWhitespace(resolver, lineInfo, lastFloatFromPreviousLine, width); 2725 2726 if (resolver.position().atEnd()) 2727 return resolver.position(); 2728 2729 // This variable is used only if whitespace isn't set to PRE, and it tells us whether 2730 // or not we are currently ignoring whitespace. 2731 bool ignoringSpaces = false; 2732 InlineIterator ignoreStart; 2733 2734 // This variable tracks whether the very last character we saw was a space. We use 2735 // this to detect when we encounter a second space so we know we have to terminate 2736 // a run. 2737 bool currentCharacterIsSpace = false; 2738 bool currentCharacterShouldCollapseIfPreWap = false; 2739 TrailingObjects trailingObjects; 2740 2741 InlineIterator lBreak = resolver.position(); 2742 2743 // FIXME: It is error-prone to split the position object out like this. 2744 // Teach this code to work with objects instead of this split tuple. 2745 InlineIterator current = resolver.position(); 2746 RenderObject* last = current.m_obj; 2747 bool atStart = true; 2748 2749 bool startingNewParagraph = lineInfo.previousLineBrokeCleanly(); 2750 lineInfo.setPreviousLineBrokeCleanly(false); 2751 2752 bool autoWrapWasEverTrueOnLine = false; 2753 bool floatsFitOnLine = true; 2754 2755 // Firefox and Opera will allow a table cell to grow to fit an image inside it under 2756 // very specific circumstances (in order to match common WinIE renderings). 2757 // Not supporting the quirk has caused us to mis-render some real sites. (See Bugzilla 10517.) 2758 RenderStyle* blockStyle = m_block->style(); 2759 bool allowImagesToBreak = !m_block->document()->inQuirksMode() || !m_block->isTableCell() || !blockStyle->logicalWidth().isIntrinsicOrAuto(); 2760 2761 EWhiteSpace currWS = blockStyle->whiteSpace(); 2762 EWhiteSpace lastWS = currWS; 2763 while (current.m_obj) { 2764 RenderStyle* currentStyle = current.m_obj->style(); 2765 RenderObject* next = bidiNextSkippingEmptyInlines(m_block, current.m_obj); 2766 if (next && next->parent() && !next->parent()->isDescendantOf(current.m_obj->parent())) 2767 includeEndWidth = true; 2768 2769 currWS = current.m_obj->isReplaced() ? current.m_obj->parent()->style()->whiteSpace() : currentStyle->whiteSpace(); 2770 lastWS = last->isReplaced() ? last->parent()->style()->whiteSpace() : last->style()->whiteSpace(); 2771 2772 bool autoWrap = RenderStyle::autoWrap(currWS); 2773 autoWrapWasEverTrueOnLine = autoWrapWasEverTrueOnLine || autoWrap; 2774 2775 bool preserveNewline = current.m_obj->isSVGInlineText() ? false : RenderStyle::preserveNewline(currWS); 2776 2777 bool collapseWhiteSpace = RenderStyle::collapseWhiteSpace(currWS); 2778 2779 if (current.m_obj->isBR()) { 2780 if (width.fitsOnLine()) { 2781 lBreak.moveToStartOf(current.m_obj); 2782 lBreak.increment(); 2783 2784 // A <br> always breaks a line, so don't let the line be collapsed 2785 // away. Also, the space at the end of a line with a <br> does not 2786 // get collapsed away. It only does this if the previous line broke 2787 // cleanly. Otherwise the <br> has no effect on whether the line is 2788 // empty or not. 2789 if (startingNewParagraph) 2790 lineInfo.setEmpty(false, m_block, &width); 2791 trailingObjects.clear(); 2792 lineInfo.setPreviousLineBrokeCleanly(true); 2793 2794 // A <br> with clearance always needs a linebox in case the lines below it get dirtied later and 2795 // need to check for floats to clear - so if we're ignoring spaces, stop ignoring them and add a 2796 // run for this object. 2797 if (ignoringSpaces && currentStyle->clear() != CNONE) 2798 ensureLineBoxInsideIgnoredSpaces(lineMidpointState, current.m_obj); 2799 2800 if (!lineInfo.isEmpty()) 2801 m_clear = currentStyle->clear(); 2802 } 2803 goto end; 2804 } 2805 2806 if (current.m_obj->isOutOfFlowPositioned()) { 2807 // If our original display wasn't an inline type, then we can 2808 // go ahead and determine our static inline position now. 2809 RenderBox* box = toRenderBox(current.m_obj); 2810 bool isInlineType = box->style()->isOriginalDisplayInlineType(); 2811 if (!isInlineType) 2812 m_block->setStaticInlinePositionForChild(box, m_block->logicalHeight(), m_block->startOffsetForContent(m_block->logicalHeight())); 2813 else { 2814 // If our original display was an INLINE type, then we can go ahead 2815 // and determine our static y position now. 2816 box->layer()->setStaticBlockPosition(m_block->logicalHeight()); 2817 } 2818 2819 // If we're ignoring spaces, we have to stop and include this object and 2820 // then start ignoring spaces again. 2821 if (isInlineType || current.m_obj->container()->isRenderInline()) { 2822 if (ignoringSpaces) 2823 ensureLineBoxInsideIgnoredSpaces(lineMidpointState, current.m_obj); 2824 trailingObjects.appendBoxIfNeeded(box); 2825 } else 2826 m_positionedObjects.append(box); 2827 width.addUncommittedWidth(inlineLogicalWidth(current.m_obj)); 2828 // Reset prior line break context characters. 2829 renderTextInfo.m_lineBreakIterator.resetPriorContext(); 2830 } else if (current.m_obj->isFloating()) { 2831 RenderBox* floatBox = toRenderBox(current.m_obj); 2832 FloatingObject* f = m_block->insertFloatingObject(floatBox); 2833 // check if it fits in the current line. 2834 // If it does, position it now, otherwise, position 2835 // it after moving to next line (in newLine() func) 2836 // FIXME: Bug 110372: Properly position multiple stacked floats with non-rectangular shape outside. 2837 if (floatsFitOnLine && width.fitsOnLine(m_block->logicalWidthForFloat(f))) { 2838 m_block->positionNewFloatOnLine(f, lastFloatFromPreviousLine, lineInfo, width); 2839 if (lBreak.m_obj == current.m_obj) { 2840 ASSERT(!lBreak.m_pos); 2841 lBreak.increment(); 2842 } 2843 } else 2844 floatsFitOnLine = false; 2845 // Update prior line break context characters, using U+FFFD (OBJECT REPLACEMENT CHARACTER) for floating element. 2846 renderTextInfo.m_lineBreakIterator.updatePriorContext(replacementCharacter); 2847 } else if (current.m_obj->isRenderInline()) { 2848 // Right now, we should only encounter empty inlines here. 2849 ASSERT(isEmptyInline(current.m_obj)); 2850 2851 RenderInline* flowBox = toRenderInline(current.m_obj); 2852 2853 // Now that some inline flows have line boxes, if we are already ignoring spaces, we need 2854 // to make sure that we stop to include this object and then start ignoring spaces again. 2855 // If this object is at the start of the line, we need to behave like list markers and 2856 // start ignoring spaces. 2857 bool requiresLineBox = alwaysRequiresLineBox(current.m_obj); 2858 if (requiresLineBox || requiresLineBoxForContent(flowBox, lineInfo)) { 2859 // An empty inline that only has line-height, vertical-align or font-metrics will only get a 2860 // line box to affect the height of the line if the rest of the line is not empty. 2861 if (requiresLineBox) 2862 lineInfo.setEmpty(false, m_block, &width); 2863 if (ignoringSpaces) { 2864 trailingObjects.clear(); 2865 ensureLineBoxInsideIgnoredSpaces(lineMidpointState, current.m_obj); 2866 } else if (blockStyle->collapseWhiteSpace() && resolver.position().m_obj == current.m_obj 2867 && shouldSkipWhitespaceAfterStartObject(m_block, current.m_obj, lineMidpointState)) { 2868 // Like with list markers, we start ignoring spaces to make sure that any 2869 // additional spaces we see will be discarded. 2870 currentCharacterShouldCollapseIfPreWap = currentCharacterIsSpace = true; 2871 ignoringSpaces = true; 2872 } 2873 } 2874 2875 width.addUncommittedWidth(inlineLogicalWidth(current.m_obj) + borderPaddingMarginStart(flowBox) + borderPaddingMarginEnd(flowBox)); 2876 } else if (current.m_obj->isReplaced()) { 2877 RenderBox* replacedBox = toRenderBox(current.m_obj); 2878 2879 if (atStart) 2880 width.updateAvailableWidth(replacedBox->logicalHeight()); 2881 2882 // Break on replaced elements if either has normal white-space. 2883 if ((autoWrap || RenderStyle::autoWrap(lastWS)) && (!current.m_obj->isImage() || allowImagesToBreak)) { 2884 width.commit(); 2885 lBreak.moveToStartOf(current.m_obj); 2886 } 2887 2888 if (ignoringSpaces) 2889 stopIgnoringSpaces(lineMidpointState, InlineIterator(0, current.m_obj, 0)); 2890 2891 lineInfo.setEmpty(false, m_block, &width); 2892 ignoringSpaces = false; 2893 currentCharacterShouldCollapseIfPreWap = currentCharacterIsSpace = false; 2894 trailingObjects.clear(); 2895 2896 // Optimize for a common case. If we can't find whitespace after the list 2897 // item, then this is all moot. 2898 LayoutUnit replacedLogicalWidth = m_block->logicalWidthForChild(replacedBox) + m_block->marginStartForChild(replacedBox) + m_block->marginEndForChild(replacedBox) + inlineLogicalWidth(current.m_obj); 2899 if (current.m_obj->isListMarker()) { 2900 if (blockStyle->collapseWhiteSpace() && shouldSkipWhitespaceAfterStartObject(m_block, current.m_obj, lineMidpointState)) { 2901 // Like with inline flows, we start ignoring spaces to make sure that any 2902 // additional spaces we see will be discarded. 2903 currentCharacterShouldCollapseIfPreWap = currentCharacterIsSpace = true; 2904 ignoringSpaces = true; 2905 } 2906 if (toRenderListMarker(current.m_obj)->isInside()) 2907 width.addUncommittedWidth(replacedLogicalWidth); 2908 } else 2909 width.addUncommittedWidth(replacedLogicalWidth); 2910 if (current.m_obj->isRubyRun()) 2911 width.applyOverhang(toRenderRubyRun(current.m_obj), last, next); 2912 // Update prior line break context characters, using U+FFFD (OBJECT REPLACEMENT CHARACTER) for replaced element. 2913 renderTextInfo.m_lineBreakIterator.updatePriorContext(replacementCharacter); 2914 } else if (current.m_obj->isText()) { 2915 if (!current.m_pos) 2916 appliedStartWidth = false; 2917 2918 RenderText* t = toRenderText(current.m_obj); 2919 2920 bool isSVGText = t->isSVGInlineText(); 2921 2922 if (t->style()->hasTextCombine() && current.m_obj->isCombineText() && !toRenderCombineText(current.m_obj)->isCombined()) { 2923 RenderCombineText* combineRenderer = toRenderCombineText(current.m_obj); 2924 combineRenderer->combineText(); 2925 // The length of the renderer's text may have changed. Increment stale iterator positions 2926 if (iteratorIsBeyondEndOfRenderCombineText(lBreak, combineRenderer)) { 2927 ASSERT(iteratorIsBeyondEndOfRenderCombineText(resolver.position(), combineRenderer)); 2928 lBreak.increment(); 2929 resolver.increment(); 2930 } 2931 } 2932 2933 RenderStyle* style = t->style(lineInfo.isFirstLine()); 2934 const Font& f = style->font(); 2935 bool isFixedPitch = f.isFixedPitch(); 2936 2937 unsigned lastSpace = current.m_pos; 2938 float wordSpacing = currentStyle->wordSpacing(); 2939 float lastSpaceWordSpacing = 0; 2940 float wordSpacingForWordMeasurement = 0; 2941 2942 float wrapW = width.uncommittedWidth() + inlineLogicalWidth(current.m_obj, !appliedStartWidth, true); 2943 float charWidth = 0; 2944 // Auto-wrapping text should wrap in the middle of a word only if it could not wrap before the word, 2945 // which is only possible if the word is the first thing on the line, that is, if |w| is zero. 2946 bool breakWords = currentStyle->breakWords() && ((autoWrap && !width.committedWidth()) || currWS == PRE); 2947 bool midWordBreak = false; 2948 bool breakAll = currentStyle->wordBreak() == BreakAllWordBreak && autoWrap; 2949 float hyphenWidth = 0; 2950 2951 if (isSVGText) { 2952 breakWords = false; 2953 breakAll = false; 2954 } 2955 2956 if (t->isWordBreak()) { 2957 width.commit(); 2958 lBreak.moveToStartOf(current.m_obj); 2959 ASSERT(current.m_pos == t->textLength()); 2960 } 2961 2962 if (renderTextInfo.m_text != t) { 2963 updateCounterIfNeeded(t); 2964 renderTextInfo.m_text = t; 2965 renderTextInfo.m_font = &f; 2966 renderTextInfo.m_layout = f.createLayout(t, width.currentWidth(), collapseWhiteSpace); 2967 renderTextInfo.m_lineBreakIterator.resetStringAndReleaseIterator(t->text(), style->locale()); 2968 } else if (renderTextInfo.m_layout && renderTextInfo.m_font != &f) { 2969 renderTextInfo.m_font = &f; 2970 renderTextInfo.m_layout = f.createLayout(t, width.currentWidth(), collapseWhiteSpace); 2971 } 2972 2973 TextLayout* textLayout = renderTextInfo.m_layout.get(); 2974 2975 // Non-zero only when kerning is enabled and TextLayout isn't used, in which case we measure 2976 // words with their trailing space, then subtract its width. 2977 float wordTrailingSpaceWidth = (f.typesettingFeatures() & Kerning) && !textLayout ? f.width(constructTextRun(t, f, &space, 1, style)) + wordSpacing : 0; 2978 2979 UChar lastCharacter = renderTextInfo.m_lineBreakIterator.lastCharacter(); 2980 UChar secondToLastCharacter = renderTextInfo.m_lineBreakIterator.secondToLastCharacter(); 2981 for (; current.m_pos < t->textLength(); current.fastIncrementInTextNode()) { 2982 bool previousCharacterIsSpace = currentCharacterIsSpace; 2983 bool previousCharacterShouldCollapseIfPreWap = currentCharacterShouldCollapseIfPreWap; 2984 UChar c = current.current(); 2985 currentCharacterShouldCollapseIfPreWap = currentCharacterIsSpace = c == ' ' || c == '\t' || (!preserveNewline && (c == '\n')); 2986 2987 if (!collapseWhiteSpace || !currentCharacterIsSpace) 2988 lineInfo.setEmpty(false, m_block, &width); 2989 2990 if (c == softHyphen && autoWrap && !hyphenWidth) { 2991 hyphenWidth = measureHyphenWidth(t, f); 2992 width.addUncommittedWidth(hyphenWidth); 2993 } 2994 2995 bool applyWordSpacing = false; 2996 2997 if ((breakAll || breakWords) && !midWordBreak) { 2998 wrapW += charWidth; 2999 bool midWordBreakIsBeforeSurrogatePair = U16_IS_LEAD(c) && current.m_pos + 1 < t->textLength() && U16_IS_TRAIL((*t)[current.m_pos + 1]); 3000 charWidth = textWidth(t, current.m_pos, midWordBreakIsBeforeSurrogatePair ? 2 : 1, f, width.committedWidth() + wrapW, isFixedPitch, collapseWhiteSpace, 0, textLayout); 3001 midWordBreak = width.committedWidth() + wrapW + charWidth > width.availableWidth(); 3002 } 3003 3004 bool betweenWords = c == '\n' || (currWS != PRE && !atStart && isBreakable(renderTextInfo.m_lineBreakIterator, current.m_pos, current.m_nextBreakablePosition)); 3005 3006 if (betweenWords || midWordBreak) { 3007 bool stoppedIgnoringSpaces = false; 3008 if (ignoringSpaces) { 3009 lastSpaceWordSpacing = 0; 3010 if (!currentCharacterIsSpace) { 3011 // Stop ignoring spaces and begin at this 3012 // new point. 3013 ignoringSpaces = false; 3014 wordSpacingForWordMeasurement = 0; 3015 lastSpace = current.m_pos; // e.g., "Foo goo", don't add in any of the ignored spaces. 3016 stopIgnoringSpaces(lineMidpointState, InlineIterator(0, current.m_obj, current.m_pos)); 3017 stoppedIgnoringSpaces = true; 3018 } else { 3019 // Just keep ignoring these spaces. 3020 goto nextCharacter; 3021 } 3022 } 3023 3024 wordMeasurements.grow(wordMeasurements.size() + 1); 3025 WordMeasurement& wordMeasurement = wordMeasurements.last(); 3026 3027 wordMeasurement.renderer = t; 3028 wordMeasurement.endOffset = current.m_pos; 3029 wordMeasurement.startOffset = lastSpace; 3030 3031 float additionalTmpW; 3032 if (wordTrailingSpaceWidth && c == ' ') 3033 additionalTmpW = textWidth(t, lastSpace, current.m_pos + 1 - lastSpace, f, width.currentWidth(), isFixedPitch, collapseWhiteSpace, &wordMeasurement.fallbackFonts, textLayout) - wordTrailingSpaceWidth; 3034 else 3035 additionalTmpW = textWidth(t, lastSpace, current.m_pos - lastSpace, f, width.currentWidth(), isFixedPitch, collapseWhiteSpace, &wordMeasurement.fallbackFonts, textLayout); 3036 3037 wordMeasurement.width = additionalTmpW + wordSpacingForWordMeasurement; 3038 additionalTmpW += lastSpaceWordSpacing; 3039 width.addUncommittedWidth(additionalTmpW); 3040 if (!appliedStartWidth) { 3041 width.addUncommittedWidth(inlineLogicalWidth(current.m_obj, true, false)); 3042 appliedStartWidth = true; 3043 } 3044 3045 applyWordSpacing = wordSpacing && currentCharacterIsSpace; 3046 3047 if (!width.committedWidth() && autoWrap && !width.fitsOnLine()) 3048 width.fitBelowFloats(); 3049 3050 if (autoWrap || breakWords) { 3051 // If we break only after white-space, consider the current character 3052 // as candidate width for this line. 3053 bool lineWasTooWide = false; 3054 if (width.fitsOnLine() && currentCharacterIsSpace && currentStyle->breakOnlyAfterWhiteSpace() && !midWordBreak) { 3055 float charWidth = textWidth(t, current.m_pos, 1, f, width.currentWidth(), isFixedPitch, collapseWhiteSpace, &wordMeasurement.fallbackFonts, textLayout) + (applyWordSpacing ? wordSpacing : 0); 3056 // Check if line is too big even without the extra space 3057 // at the end of the line. If it is not, do nothing. 3058 // If the line needs the extra whitespace to be too long, 3059 // then move the line break to the space and skip all 3060 // additional whitespace. 3061 if (!width.fitsOnLine(charWidth)) { 3062 lineWasTooWide = true; 3063 lBreak.moveTo(current.m_obj, current.m_pos, current.m_nextBreakablePosition); 3064 skipTrailingWhitespace(lBreak, lineInfo); 3065 } 3066 } 3067 if (lineWasTooWide || !width.fitsOnLine()) { 3068 if (lBreak.atTextParagraphSeparator()) { 3069 if (!stoppedIgnoringSpaces && current.m_pos > 0) 3070 ensureCharacterGetsLineBox(lineMidpointState, current); 3071 lBreak.increment(); 3072 lineInfo.setPreviousLineBrokeCleanly(true); 3073 wordMeasurement.endOffset = lBreak.m_pos; 3074 } 3075 if (lBreak.m_obj && lBreak.m_pos && lBreak.m_obj->isText() && toRenderText(lBreak.m_obj)->textLength() && toRenderText(lBreak.m_obj)->characterAt(lBreak.m_pos - 1) == softHyphen) 3076 m_hyphenated = true; 3077 if (lBreak.m_pos && lBreak.m_pos != (unsigned)wordMeasurement.endOffset && !wordMeasurement.width) { 3078 if (charWidth) { 3079 wordMeasurement.endOffset = lBreak.m_pos; 3080 wordMeasurement.width = charWidth; 3081 } 3082 } 3083 // Didn't fit. Jump to the end unless there's still an opportunity to collapse whitespace. 3084 if (ignoringSpaces || !collapseWhiteSpace || !currentCharacterIsSpace || !previousCharacterIsSpace) 3085 goto end; 3086 } else { 3087 if (!betweenWords || (midWordBreak && !autoWrap)) 3088 width.addUncommittedWidth(-additionalTmpW); 3089 if (hyphenWidth) { 3090 // Subtract the width of the soft hyphen out since we fit on a line. 3091 width.addUncommittedWidth(-hyphenWidth); 3092 hyphenWidth = 0; 3093 } 3094 } 3095 } 3096 3097 if (c == '\n' && preserveNewline) { 3098 if (!stoppedIgnoringSpaces && current.m_pos > 0) 3099 ensureCharacterGetsLineBox(lineMidpointState, current); 3100 lBreak.moveTo(current.m_obj, current.m_pos, current.m_nextBreakablePosition); 3101 lBreak.increment(); 3102 lineInfo.setPreviousLineBrokeCleanly(true); 3103 return lBreak; 3104 } 3105 3106 if (autoWrap && betweenWords) { 3107 width.commit(); 3108 wrapW = 0; 3109 lBreak.moveTo(current.m_obj, current.m_pos, current.m_nextBreakablePosition); 3110 // Auto-wrapping text should not wrap in the middle of a word once it has had an 3111 // opportunity to break after a word. 3112 breakWords = false; 3113 } 3114 3115 if (midWordBreak && !U16_IS_TRAIL(c) && !(category(c) & (Mark_NonSpacing | Mark_Enclosing | Mark_SpacingCombining))) { 3116 // Remember this as a breakable position in case 3117 // adding the end width forces a break. 3118 lBreak.moveTo(current.m_obj, current.m_pos, current.m_nextBreakablePosition); 3119 midWordBreak &= (breakWords || breakAll); 3120 } 3121 3122 if (betweenWords) { 3123 lastSpaceWordSpacing = applyWordSpacing ? wordSpacing : 0; 3124 wordSpacingForWordMeasurement = (applyWordSpacing && wordMeasurement.width) ? wordSpacing : 0; 3125 lastSpace = current.m_pos; 3126 } 3127 3128 if (!ignoringSpaces && currentStyle->collapseWhiteSpace()) { 3129 // If we encounter a newline, or if we encounter a 3130 // second space, we need to go ahead and break up this 3131 // run and enter a mode where we start collapsing spaces. 3132 if (currentCharacterIsSpace && previousCharacterIsSpace) { 3133 ignoringSpaces = true; 3134 3135 // We just entered a mode where we are ignoring 3136 // spaces. Create a midpoint to terminate the run 3137 // before the second space. 3138 startIgnoringSpaces(lineMidpointState, ignoreStart); 3139 trailingObjects.updateMidpointsForTrailingBoxes(lineMidpointState, InlineIterator(), TrailingObjects::DoNotCollapseFirstSpace); 3140 } 3141 } 3142 } else if (ignoringSpaces) { 3143 // Stop ignoring spaces and begin at this 3144 // new point. 3145 ignoringSpaces = false; 3146 lastSpaceWordSpacing = applyWordSpacing ? wordSpacing : 0; 3147 wordSpacingForWordMeasurement = (applyWordSpacing && wordMeasurements.last().width) ? wordSpacing : 0; 3148 lastSpace = current.m_pos; // e.g., "Foo goo", don't add in any of the ignored spaces. 3149 stopIgnoringSpaces(lineMidpointState, InlineIterator(0, current.m_obj, current.m_pos)); 3150 } 3151 3152 if (isSVGText && current.m_pos > 0) { 3153 // Force creation of new InlineBoxes for each absolute positioned character (those that start new text chunks). 3154 if (toRenderSVGInlineText(t)->characterStartsNewTextChunk(current.m_pos)) 3155 ensureCharacterGetsLineBox(lineMidpointState, current); 3156 } 3157 3158 if (currentCharacterIsSpace && !previousCharacterIsSpace) { 3159 ignoreStart.m_obj = current.m_obj; 3160 ignoreStart.m_pos = current.m_pos; 3161 } 3162 3163 if (!currentCharacterIsSpace && previousCharacterShouldCollapseIfPreWap) { 3164 if (autoWrap && currentStyle->breakOnlyAfterWhiteSpace()) 3165 lBreak.moveTo(current.m_obj, current.m_pos, current.m_nextBreakablePosition); 3166 } 3167 3168 if (collapseWhiteSpace && currentCharacterIsSpace && !ignoringSpaces) 3169 trailingObjects.setTrailingWhitespace(toRenderText(current.m_obj)); 3170 else if (!currentStyle->collapseWhiteSpace() || !currentCharacterIsSpace) 3171 trailingObjects.clear(); 3172 3173 atStart = false; 3174 nextCharacter: 3175 secondToLastCharacter = lastCharacter; 3176 lastCharacter = c; 3177 } 3178 3179 renderTextInfo.m_lineBreakIterator.setPriorContext(lastCharacter, secondToLastCharacter); 3180 3181 wordMeasurements.grow(wordMeasurements.size() + 1); 3182 WordMeasurement& wordMeasurement = wordMeasurements.last(); 3183 wordMeasurement.renderer = t; 3184 3185 // IMPORTANT: current.m_pos is > length here! 3186 float additionalTmpW = ignoringSpaces ? 0 : textWidth(t, lastSpace, current.m_pos - lastSpace, f, width.currentWidth(), isFixedPitch, collapseWhiteSpace, &wordMeasurement.fallbackFonts, textLayout); 3187 wordMeasurement.startOffset = lastSpace; 3188 wordMeasurement.endOffset = current.m_pos; 3189 wordMeasurement.width = ignoringSpaces ? 0 : additionalTmpW + wordSpacingForWordMeasurement; 3190 additionalTmpW += lastSpaceWordSpacing; 3191 width.addUncommittedWidth(additionalTmpW + inlineLogicalWidth(current.m_obj, !appliedStartWidth, includeEndWidth)); 3192 includeEndWidth = false; 3193 3194 if (!width.fitsOnLine()) { 3195 if (!m_hyphenated && lBreak.previousInSameNode() == softHyphen) 3196 m_hyphenated = true; 3197 3198 if (m_hyphenated) 3199 goto end; 3200 } 3201 } else 3202 ASSERT_NOT_REACHED(); 3203 3204 bool checkForBreak = autoWrap; 3205 if (width.committedWidth() && !width.fitsOnLine() && lBreak.m_obj && currWS == NOWRAP) 3206 checkForBreak = true; 3207 else if (next && current.m_obj->isText() && next->isText() && !next->isBR() && (autoWrap || next->style()->autoWrap())) { 3208 if (autoWrap && currentCharacterIsSpace) 3209 checkForBreak = true; 3210 else { 3211 RenderText* nextText = toRenderText(next); 3212 if (nextText->textLength()) { 3213 UChar c = nextText->characterAt(0); 3214 // If the next item on the line is text, and if we did not end with 3215 // a space, then the next text run continues our word (and so it needs to 3216 // keep adding to the uncommitted width. Just update and continue. 3217 checkForBreak = !currentCharacterIsSpace && (c == ' ' || c == '\t' || (c == '\n' && !next->preservesNewline())); 3218 } else if (nextText->isWordBreak()) 3219 checkForBreak = true; 3220 3221 if (!width.fitsOnLine() && !width.committedWidth()) 3222 width.fitBelowFloats(); 3223 3224 bool canPlaceOnLine = width.fitsOnLine() || !autoWrapWasEverTrueOnLine; 3225 if (canPlaceOnLine && checkForBreak) { 3226 width.commit(); 3227 lBreak.moveToStartOf(next); 3228 } 3229 } 3230 } 3231 3232 if (checkForBreak && !width.fitsOnLine()) { 3233 // if we have floats, try to get below them. 3234 if (currentCharacterIsSpace && !ignoringSpaces && currentStyle->collapseWhiteSpace()) 3235 trailingObjects.clear(); 3236 3237 if (width.committedWidth()) 3238 goto end; 3239 3240 width.fitBelowFloats(); 3241 3242 // |width| may have been adjusted because we got shoved down past a float (thus 3243 // giving us more room), so we need to retest, and only jump to 3244 // the end label if we still don't fit on the line. -dwh 3245 if (!width.fitsOnLine()) 3246 goto end; 3247 } else if (blockStyle->autoWrap() && !width.fitsOnLine() && !width.committedWidth()) { 3248 // If the container autowraps but the current child does not then we still need to ensure that it 3249 // wraps and moves below any floats. 3250 width.fitBelowFloats(); 3251 } 3252 3253 if (!current.m_obj->isFloatingOrOutOfFlowPositioned()) { 3254 last = current.m_obj; 3255 if (last->isReplaced() && autoWrap && (!last->isImage() || allowImagesToBreak) && (!last->isListMarker() || toRenderListMarker(last)->isInside())) { 3256 width.commit(); 3257 lBreak.moveToStartOf(next); 3258 } 3259 } 3260 3261 // Clear out our character space bool, since inline <pre>s don't collapse whitespace 3262 // with adjacent inline normal/nowrap spans. 3263 if (!collapseWhiteSpace) 3264 currentCharacterIsSpace = false; 3265 3266 current.moveToStartOf(next); 3267 atStart = false; 3268 } 3269 3270 if (width.fitsOnLine() || lastWS == NOWRAP) 3271 lBreak.clear(); 3272 3273 end: 3274 ShapeInsideInfo* shapeInfo = m_block->layoutShapeInsideInfo(); 3275 bool segmentAllowsOverflow = !shapeInfo || !shapeInfo->hasSegments(); 3276 3277 if (lBreak == resolver.position() && (!lBreak.m_obj || !lBreak.m_obj->isBR()) && segmentAllowsOverflow) { 3278 // we just add as much as possible 3279 if (blockStyle->whiteSpace() == PRE && !current.m_pos) { 3280 lBreak.moveTo(last, last->isText() ? last->length() : 0); 3281 } else if (lBreak.m_obj) { 3282 // Don't ever break in the middle of a word if we can help it. 3283 // There's no room at all. We just have to be on this line, 3284 // even though we'll spill out. 3285 lBreak.moveTo(current.m_obj, current.m_pos); 3286 } 3287 } 3288 3289 // FIXME Bug 100049: We do not need to consume input in a multi-segment line 3290 // unless no segment will. 3291 // make sure we consume at least one char/object. 3292 if (lBreak == resolver.position() && segmentAllowsOverflow) 3293 lBreak.increment(); 3294 3295 // Sanity check our midpoints. 3296 checkMidpoints(lineMidpointState, lBreak); 3297 3298 trailingObjects.updateMidpointsForTrailingBoxes(lineMidpointState, lBreak, TrailingObjects::CollapseFirstSpace); 3299 3300 // We might have made lBreak an iterator that points past the end 3301 // of the object. Do this adjustment to make it point to the start 3302 // of the next object instead to avoid confusing the rest of the 3303 // code. 3304 if (lBreak.m_pos > 0) { 3305 lBreak.m_pos--; 3306 lBreak.increment(); 3307 } 3308 3309 return lBreak; 3310 } 3311 3312 void RenderBlock::addOverflowFromInlineChildren() 3313 { 3314 LayoutUnit endPadding = hasOverflowClip() ? paddingEnd() : LayoutUnit(); 3315 // FIXME: Need to find another way to do this, since scrollbars could show when we don't want them to. 3316 if (hasOverflowClip() && !endPadding && node() && node()->isRootEditableElement() && style()->isLeftToRightDirection()) 3317 endPadding = 1; 3318 for (RootInlineBox* curr = firstRootBox(); curr; curr = curr->nextRootBox()) { 3319 addLayoutOverflow(curr->paddedLayoutOverflowRect(endPadding)); 3320 LayoutRect visualOverflow = curr->visualOverflowRect(curr->lineTop(), curr->lineBottom()); 3321 addContentsVisualOverflow(visualOverflow); 3322 } 3323 } 3324 3325 void RenderBlock::deleteEllipsisLineBoxes() 3326 { 3327 ETextAlign textAlign = style()->textAlign(); 3328 bool ltr = style()->isLeftToRightDirection(); 3329 bool firstLine = true; 3330 for (RootInlineBox* curr = firstRootBox(); curr; curr = curr->nextRootBox()) { 3331 if (curr->hasEllipsisBox()) { 3332 curr->clearTruncation(); 3333 3334 // Shift the line back where it belongs if we cannot accomodate an ellipsis. 3335 float logicalLeft = pixelSnappedLogicalLeftOffsetForLine(curr->lineTop(), firstLine); 3336 float availableLogicalWidth = logicalRightOffsetForLine(curr->lineTop(), false) - logicalLeft; 3337 float totalLogicalWidth = curr->logicalWidth(); 3338 updateLogicalWidthForAlignment(textAlign, 0, logicalLeft, totalLogicalWidth, availableLogicalWidth, 0); 3339 3340 if (ltr) 3341 curr->adjustLogicalPosition((logicalLeft - curr->logicalLeft()), 0); 3342 else 3343 curr->adjustLogicalPosition(-(curr->logicalLeft() - logicalLeft), 0); 3344 } 3345 firstLine = false; 3346 } 3347 } 3348 3349 void RenderBlock::checkLinesForTextOverflow() 3350 { 3351 // Determine the width of the ellipsis using the current font. 3352 // FIXME: CSS3 says this is configurable, also need to use 0x002E (FULL STOP) if horizontal ellipsis is "not renderable" 3353 const Font& font = style()->font(); 3354 DEFINE_STATIC_LOCAL(AtomicString, ellipsisStr, (&horizontalEllipsis, 1)); 3355 const Font& firstLineFont = firstLineStyle()->font(); 3356 int firstLineEllipsisWidth = firstLineFont.width(constructTextRun(this, firstLineFont, &horizontalEllipsis, 1, firstLineStyle())); 3357 int ellipsisWidth = (font == firstLineFont) ? firstLineEllipsisWidth : font.width(constructTextRun(this, font, &horizontalEllipsis, 1, style())); 3358 3359 // For LTR text truncation, we want to get the right edge of our padding box, and then we want to see 3360 // if the right edge of a line box exceeds that. For RTL, we use the left edge of the padding box and 3361 // check the left edge of the line box to see if it is less 3362 // Include the scrollbar for overflow blocks, which means we want to use "contentWidth()" 3363 bool ltr = style()->isLeftToRightDirection(); 3364 ETextAlign textAlign = style()->textAlign(); 3365 bool firstLine = true; 3366 for (RootInlineBox* curr = firstRootBox(); curr; curr = curr->nextRootBox()) { 3367 // FIXME: Use pixelSnappedLogicalRightOffsetForLine instead of snapping it ourselves once the column workaround in said method has been fixed. 3368 // https://bugs.webkit.org/show_bug.cgi?id=105461 3369 int blockRightEdge = snapSizeToPixel(logicalRightOffsetForLine(curr->lineTop(), firstLine), curr->x()); 3370 int blockLeftEdge = pixelSnappedLogicalLeftOffsetForLine(curr->lineTop(), firstLine); 3371 int lineBoxEdge = ltr ? snapSizeToPixel(curr->x() + curr->logicalWidth(), curr->x()) : snapSizeToPixel(curr->x(), 0); 3372 if ((ltr && lineBoxEdge > blockRightEdge) || (!ltr && lineBoxEdge < blockLeftEdge)) { 3373 // This line spills out of our box in the appropriate direction. Now we need to see if the line 3374 // can be truncated. In order for truncation to be possible, the line must have sufficient space to 3375 // accommodate our truncation string, and no replaced elements (images, tables) can overlap the ellipsis 3376 // space. 3377 3378 LayoutUnit width = firstLine ? firstLineEllipsisWidth : ellipsisWidth; 3379 LayoutUnit blockEdge = ltr ? blockRightEdge : blockLeftEdge; 3380 if (curr->lineCanAccommodateEllipsis(ltr, blockEdge, lineBoxEdge, width)) { 3381 float totalLogicalWidth = curr->placeEllipsis(ellipsisStr, ltr, blockLeftEdge, blockRightEdge, width); 3382 3383 float logicalLeft = 0; // We are only intersted in the delta from the base position. 3384 float truncatedWidth = pixelSnappedLogicalRightOffsetForLine(curr->lineTop(), firstLine); 3385 updateLogicalWidthForAlignment(textAlign, 0, logicalLeft, totalLogicalWidth, truncatedWidth, 0); 3386 if (ltr) 3387 curr->adjustLogicalPosition(logicalLeft, 0); 3388 else 3389 curr->adjustLogicalPosition(-(truncatedWidth - (logicalLeft + totalLogicalWidth)), 0); 3390 } 3391 } 3392 firstLine = false; 3393 } 3394 } 3395 3396 bool RenderBlock::positionNewFloatOnLine(FloatingObject* newFloat, FloatingObject* lastFloatFromPreviousLine, LineInfo& lineInfo, LineWidth& width) 3397 { 3398 if (!positionNewFloats()) 3399 return false; 3400 3401 width.shrinkAvailableWidthForNewFloatIfNeeded(newFloat); 3402 3403 // We only connect floats to lines for pagination purposes if the floats occur at the start of 3404 // the line and the previous line had a hard break (so this line is either the first in the block 3405 // or follows a <br>). 3406 if (!newFloat->m_paginationStrut || !lineInfo.previousLineBrokeCleanly() || !lineInfo.isEmpty()) 3407 return true; 3408 3409 const FloatingObjectSet& floatingObjectSet = m_floatingObjects->set(); 3410 ASSERT(floatingObjectSet.last() == newFloat); 3411 3412 LayoutUnit floatLogicalTop = logicalTopForFloat(newFloat); 3413 int paginationStrut = newFloat->m_paginationStrut; 3414 3415 if (floatLogicalTop - paginationStrut != logicalHeight() + lineInfo.floatPaginationStrut()) 3416 return true; 3417 3418 FloatingObjectSetIterator it = floatingObjectSet.end(); 3419 --it; // Last float is newFloat, skip that one. 3420 FloatingObjectSetIterator begin = floatingObjectSet.begin(); 3421 while (it != begin) { 3422 --it; 3423 FloatingObject* f = *it; 3424 if (f == lastFloatFromPreviousLine) 3425 break; 3426 if (logicalTopForFloat(f) == logicalHeight() + lineInfo.floatPaginationStrut()) { 3427 f->m_paginationStrut += paginationStrut; 3428 RenderBox* o = f->m_renderer; 3429 setLogicalTopForChild(o, logicalTopForChild(o) + marginBeforeForChild(o) + paginationStrut); 3430 if (o->isRenderBlock()) 3431 toRenderBlock(o)->setChildNeedsLayout(MarkOnlyThis); 3432 o->layoutIfNeeded(); 3433 // Save the old logical top before calling removePlacedObject which will set 3434 // isPlaced to false. Otherwise it will trigger an assert in logicalTopForFloat. 3435 LayoutUnit oldLogicalTop = logicalTopForFloat(f); 3436 m_floatingObjects->removePlacedObject(f); 3437 setLogicalTopForFloat(f, oldLogicalTop + paginationStrut); 3438 m_floatingObjects->addPlacedObject(f); 3439 } 3440 } 3441 3442 // Just update the line info's pagination strut without altering our logical height yet. If the line ends up containing 3443 // no content, then we don't want to improperly grow the height of the block. 3444 lineInfo.setFloatPaginationStrut(lineInfo.floatPaginationStrut() + paginationStrut); 3445 return true; 3446 } 3447 3448 LayoutUnit RenderBlock::startAlignedOffsetForLine(LayoutUnit position, bool firstLine) 3449 { 3450 ETextAlign textAlign = style()->textAlign(); 3451 3452 if (textAlign == TASTART) // FIXME: Handle TAEND here 3453 return startOffsetForLine(position, firstLine); 3454 3455 // updateLogicalWidthForAlignment() handles the direction of the block so no need to consider it here 3456 float totalLogicalWidth = 0; 3457 float logicalLeft = logicalLeftOffsetForLine(logicalHeight(), false); 3458 float availableLogicalWidth = logicalRightOffsetForLine(logicalHeight(), false) - logicalLeft; 3459 updateLogicalWidthForAlignment(textAlign, 0, logicalLeft, totalLogicalWidth, availableLogicalWidth, 0); 3460 3461 if (!style()->isLeftToRightDirection()) 3462 return logicalWidth() - logicalLeft; 3463 return logicalLeft; 3464 } 3465 3466 3467 void RenderBlock::layoutLineGridBox() 3468 { 3469 if (style()->lineGrid() == RenderStyle::initialLineGrid()) { 3470 setLineGridBox(0); 3471 return; 3472 } 3473 3474 setLineGridBox(0); 3475 3476 RootInlineBox* lineGridBox = new RootInlineBox(this); 3477 lineGridBox->setHasTextChildren(); // Needed to make the line ascent/descent actually be honored in quirks mode. 3478 lineGridBox->setConstructed(); 3479 GlyphOverflowAndFallbackFontsMap textBoxDataMap; 3480 VerticalPositionCache verticalPositionCache; 3481 lineGridBox->alignBoxesInBlockDirection(logicalHeight(), textBoxDataMap, verticalPositionCache); 3482 3483 setLineGridBox(lineGridBox); 3484 3485 // FIXME: If any of the characteristics of the box change compared to the old one, then we need to do a deep dirtying 3486 // (similar to what happens when the page height changes). Ideally, though, we only do this if someone is actually snapping 3487 // to this grid. 3488 } 3489 3490 } 3491