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 * Copyright (C) 2013 Adobe Systems Incorporated. 6 * 7 * This library is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU Library General Public 9 * License as published by the Free Software Foundation; either 10 * version 2 of the License, or (at your option) any later version. 11 * 12 * This library is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Library General Public License for more details. 16 * 17 * You should have received a copy of the GNU Library General Public License 18 * along with this library; see the file COPYING.LIB. If not, write to 19 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 20 * Boston, MA 02110-1301, USA. 21 * 22 */ 23 24 #ifndef BreakingContextInlineHeaders_h 25 #define BreakingContextInlineHeaders_h 26 27 #include "core/rendering/InlineIterator.h" 28 #include "core/rendering/InlineTextBox.h" 29 #include "core/rendering/LineWidth.h" 30 #include "core/rendering/RenderCombineText.h" 31 #include "core/rendering/RenderInline.h" 32 #include "core/rendering/break_lines.h" 33 #include "core/rendering/line/LineInfo.h" 34 #include "core/rendering/shapes/ShapeInsideInfo.h" 35 #include "core/rendering/svg/RenderSVGInlineText.h" 36 37 namespace WebCore { 38 39 using namespace std; 40 using namespace WTF; 41 using namespace Unicode; 42 43 // We don't let our line box tree for a single line get any deeper than this. 44 const unsigned cMaxLineDepth = 200; 45 46 struct RenderTextInfo { 47 // Destruction of m_layout requires TextLayout to be a complete type, so the constructor and destructor are made non-inline to avoid compilation errors. 48 RenderTextInfo(); 49 ~RenderTextInfo(); 50 51 RenderText* m_text; 52 OwnPtr<TextLayout> m_layout; 53 LazyLineBreakIterator m_lineBreakIterator; 54 const Font* m_font; 55 56 void createLayout(RenderText* renderText, float xPos, bool collapseWhiteSpace) 57 { 58 #if OS(MACOSX) 59 m_layout = m_font->createLayoutForMacComplexText(RenderBlockFlow::constructTextRun(renderText, *m_font, renderText, renderText->style()), renderText->textLength(), xPos, collapseWhiteSpace); 60 #else 61 m_layout = nullptr; 62 #endif 63 } 64 }; 65 66 class WordMeasurement { 67 public: 68 WordMeasurement() 69 : renderer(0) 70 , width(0) 71 , startOffset(0) 72 , endOffset(0) 73 { 74 } 75 76 RenderText* renderer; 77 float width; 78 int startOffset; 79 int endOffset; 80 HashSet<const SimpleFontData*> fallbackFonts; 81 }; 82 83 // Don't call this directly. Use one of the descriptive helper functions below. 84 inline void deprecatedAddMidpoint(LineMidpointState& lineMidpointState, const InlineIterator& midpoint) 85 { 86 if (lineMidpointState.midpoints.size() <= lineMidpointState.numMidpoints) 87 lineMidpointState.midpoints.grow(lineMidpointState.numMidpoints + 10); 88 89 InlineIterator* midpoints = lineMidpointState.midpoints.data(); 90 midpoints[lineMidpointState.numMidpoints++] = midpoint; 91 } 92 93 inline void startIgnoringSpaces(LineMidpointState& lineMidpointState, const InlineIterator& midpoint) 94 { 95 ASSERT(!(lineMidpointState.numMidpoints % 2)); 96 deprecatedAddMidpoint(lineMidpointState, midpoint); 97 } 98 99 inline void stopIgnoringSpaces(LineMidpointState& lineMidpointState, const InlineIterator& midpoint) 100 { 101 ASSERT(lineMidpointState.numMidpoints % 2); 102 deprecatedAddMidpoint(lineMidpointState, midpoint); 103 } 104 105 // When ignoring spaces, this needs to be called for objects that need line boxes such as RenderInlines or 106 // hard line breaks to ensure that they're not ignored. 107 inline void ensureLineBoxInsideIgnoredSpaces(LineMidpointState& lineMidpointState, RenderObject* renderer) 108 { 109 InlineIterator midpoint(0, renderer, 0); 110 stopIgnoringSpaces(lineMidpointState, midpoint); 111 startIgnoringSpaces(lineMidpointState, midpoint); 112 } 113 114 // Adding a pair of midpoints before a character will split it out into a new line box. 115 inline void ensureCharacterGetsLineBox(LineMidpointState& lineMidpointState, InlineIterator& textParagraphSeparator) 116 { 117 InlineIterator midpoint(0, textParagraphSeparator.object(), textParagraphSeparator.m_pos); 118 startIgnoringSpaces(lineMidpointState, InlineIterator(0, textParagraphSeparator.object(), textParagraphSeparator.m_pos - 1)); 119 stopIgnoringSpaces(lineMidpointState, InlineIterator(0, textParagraphSeparator.object(), textParagraphSeparator.m_pos)); 120 } 121 122 class TrailingObjects { 123 public: 124 TrailingObjects(); 125 void setTrailingWhitespace(RenderText*); 126 void clear(); 127 void appendBoxIfNeeded(RenderBox*); 128 129 enum CollapseFirstSpaceOrNot { DoNotCollapseFirstSpace, CollapseFirstSpace }; 130 131 void updateMidpointsForTrailingBoxes(LineMidpointState&, const InlineIterator& lBreak, CollapseFirstSpaceOrNot); 132 133 private: 134 RenderText* m_whitespace; 135 Vector<RenderBox*, 4> m_boxes; 136 }; 137 138 TrailingObjects::TrailingObjects() 139 : m_whitespace(0) 140 { 141 } 142 143 inline void TrailingObjects::setTrailingWhitespace(RenderText* whitespace) 144 { 145 ASSERT(whitespace); 146 m_whitespace = whitespace; 147 } 148 149 inline void TrailingObjects::clear() 150 { 151 m_whitespace = 0; 152 // Using resize(0) rather than clear() here saves 2% on 153 // PerformanceTests/Layout/line-layout.html because we avoid freeing and 154 // re-allocating the underlying buffer repeatedly. 155 m_boxes.resize(0); 156 } 157 158 inline void TrailingObjects::appendBoxIfNeeded(RenderBox* box) 159 { 160 if (m_whitespace) 161 m_boxes.append(box); 162 } 163 164 void TrailingObjects::updateMidpointsForTrailingBoxes(LineMidpointState& lineMidpointState, const InlineIterator& lBreak, CollapseFirstSpaceOrNot collapseFirstSpace) 165 { 166 if (!m_whitespace) 167 return; 168 169 // This object is either going to be part of the last midpoint, or it is going to be the actual endpoint. 170 // In both cases we just decrease our pos by 1 level to exclude the space, allowing it to - in effect - collapse into the newline. 171 if (lineMidpointState.numMidpoints % 2) { 172 // Find the trailing space object's midpoint. 173 int trailingSpaceMidpoint = lineMidpointState.numMidpoints - 1; 174 for ( ; trailingSpaceMidpoint > 0 && lineMidpointState.midpoints[trailingSpaceMidpoint].object() != m_whitespace; --trailingSpaceMidpoint) { } 175 ASSERT(trailingSpaceMidpoint >= 0); 176 if (collapseFirstSpace == CollapseFirstSpace) 177 lineMidpointState.midpoints[trailingSpaceMidpoint].m_pos--; 178 179 // Now make sure every single trailingPositionedBox following the trailingSpaceMidpoint properly stops and starts 180 // ignoring spaces. 181 size_t currentMidpoint = trailingSpaceMidpoint + 1; 182 for (size_t i = 0; i < m_boxes.size(); ++i) { 183 if (currentMidpoint >= lineMidpointState.numMidpoints) { 184 // We don't have a midpoint for this box yet. 185 ensureLineBoxInsideIgnoredSpaces(lineMidpointState, m_boxes[i]); 186 } else { 187 ASSERT(lineMidpointState.midpoints[currentMidpoint].object() == m_boxes[i]); 188 ASSERT(lineMidpointState.midpoints[currentMidpoint + 1].object() == m_boxes[i]); 189 } 190 currentMidpoint += 2; 191 } 192 } else if (!lBreak.object()) { 193 ASSERT(m_whitespace->isText()); 194 ASSERT(collapseFirstSpace == CollapseFirstSpace); 195 // Add a new end midpoint that stops right at the very end. 196 unsigned length = m_whitespace->textLength(); 197 unsigned pos = length >= 2 ? length - 2 : UINT_MAX; 198 InlineIterator endMid(0, m_whitespace, pos); 199 startIgnoringSpaces(lineMidpointState, endMid); 200 for (size_t i = 0; i < m_boxes.size(); ++i) { 201 ensureLineBoxInsideIgnoredSpaces(lineMidpointState, m_boxes[i]); 202 } 203 } 204 } 205 206 class BreakingContext { 207 public: 208 BreakingContext(InlineBidiResolver& resolver, LineInfo& inLineInfo, LineWidth& lineWidth, RenderTextInfo& inRenderTextInfo, FloatingObject* inLastFloatFromPreviousLine, bool appliedStartWidth, RenderBlockFlow* block) 209 : m_resolver(resolver) 210 , m_current(resolver.position()) 211 , m_lineBreak(resolver.position()) 212 , m_block(block) 213 , m_lastObject(m_current.object()) 214 , m_nextObject(0) 215 , m_currentStyle(0) 216 , m_blockStyle(block->style()) 217 , m_lineInfo(inLineInfo) 218 , m_renderTextInfo(inRenderTextInfo) 219 , m_lastFloatFromPreviousLine(inLastFloatFromPreviousLine) 220 , m_width(lineWidth) 221 , m_currWS(NORMAL) 222 , m_lastWS(NORMAL) 223 , m_preservesNewline(false) 224 , m_atStart(true) 225 , m_ignoringSpaces(false) 226 , m_currentCharacterIsSpace(false) 227 , m_currentCharacterShouldCollapseIfPreWap(false) 228 , m_appliedStartWidth(appliedStartWidth) 229 , m_includeEndWidth(true) 230 , m_autoWrap(false) 231 , m_autoWrapWasEverTrueOnLine(false) 232 , m_floatsFitOnLine(true) 233 , m_collapseWhiteSpace(false) 234 , m_startingNewParagraph(m_lineInfo.previousLineBrokeCleanly()) 235 , m_allowImagesToBreak(!block->document().inQuirksMode() || !block->isTableCell() || !m_blockStyle->logicalWidth().isIntrinsicOrAuto()) 236 , m_atEnd(false) 237 , m_lineMidpointState(resolver.midpointState()) 238 { 239 m_lineInfo.setPreviousLineBrokeCleanly(false); 240 } 241 242 RenderObject* currentObject() { return m_current.object(); } 243 InlineIterator lineBreak() { return m_lineBreak; } 244 bool atEnd() { return m_atEnd; } 245 246 void initializeForCurrentObject(); 247 248 void increment(); 249 250 void handleBR(EClear&); 251 void handleOutOfFlowPositioned(Vector<RenderBox*>& positionedObjects); 252 void handleFloat(); 253 void handleEmptyInline(); 254 void handleReplaced(); 255 bool handleText(WordMeasurements&, bool& hyphenated); 256 void commitAndUpdateLineBreakIfNeeded(); 257 InlineIterator handleEndOfLine(); 258 259 void clearLineBreakIfFitsOnLine() 260 { 261 if (m_width.fitsOnLine() || m_lastWS == NOWRAP) 262 m_lineBreak.clear(); 263 } 264 265 private: 266 void skipTrailingWhitespace(InlineIterator&, const LineInfo&); 267 268 InlineBidiResolver& m_resolver; 269 270 InlineIterator m_current; 271 InlineIterator m_lineBreak; 272 InlineIterator m_startOfIgnoredSpaces; 273 274 RenderBlockFlow* m_block; 275 RenderObject* m_lastObject; 276 RenderObject* m_nextObject; 277 278 RenderStyle* m_currentStyle; 279 RenderStyle* m_blockStyle; 280 281 LineInfo& m_lineInfo; 282 283 RenderTextInfo& m_renderTextInfo; 284 285 FloatingObject* m_lastFloatFromPreviousLine; 286 287 LineWidth m_width; 288 289 EWhiteSpace m_currWS; 290 EWhiteSpace m_lastWS; 291 292 bool m_preservesNewline; 293 bool m_atStart; 294 bool m_ignoringSpaces; 295 bool m_currentCharacterIsSpace; 296 bool m_currentCharacterShouldCollapseIfPreWap; 297 bool m_appliedStartWidth; 298 bool m_includeEndWidth; 299 bool m_autoWrap; 300 bool m_autoWrapWasEverTrueOnLine; 301 bool m_floatsFitOnLine; 302 bool m_collapseWhiteSpace; 303 bool m_startingNewParagraph; 304 bool m_allowImagesToBreak; 305 bool m_atEnd; 306 307 LineMidpointState& m_lineMidpointState; 308 309 TrailingObjects m_trailingObjects; 310 }; 311 312 enum WhitespacePosition { LeadingWhitespace, TrailingWhitespace }; 313 314 inline bool shouldCollapseWhiteSpace(const RenderStyle* style, const LineInfo& lineInfo, WhitespacePosition whitespacePosition) 315 { 316 // CSS2 16.6.1 317 // If a space (U+0020) at the beginning of a line has 'white-space' set to 'normal', 'nowrap', or 'pre-line', it is removed. 318 // 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. 319 // 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. 320 return style->collapseWhiteSpace() 321 || (whitespacePosition == TrailingWhitespace && style->whiteSpace() == PRE_WRAP && (!lineInfo.isEmpty() || !lineInfo.previousLineBrokeCleanly())); 322 } 323 324 inline bool requiresLineBoxForContent(RenderInline* flow, const LineInfo& lineInfo) 325 { 326 RenderObject* parent = flow->parent(); 327 if (flow->document().inNoQuirksMode() 328 && (flow->style(lineInfo.isFirstLine())->lineHeight() != parent->style(lineInfo.isFirstLine())->lineHeight() 329 || flow->style()->verticalAlign() != parent->style()->verticalAlign() 330 || !parent->style()->font().fontMetrics().hasIdenticalAscentDescentAndLineGap(flow->style()->font().fontMetrics()))) 331 return true; 332 return false; 333 } 334 335 inline bool alwaysRequiresLineBox(RenderObject* flow) 336 { 337 // FIXME: Right now, we only allow line boxes for inlines that are truly empty. 338 // We need to fix this, though, because at the very least, inlines containing only 339 // ignorable whitespace should should also have line boxes. 340 return isEmptyInline(flow) && toRenderInline(flow)->hasInlineDirectionBordersPaddingOrMargin(); 341 } 342 343 inline bool requiresLineBox(const InlineIterator& it, const LineInfo& lineInfo = LineInfo(), WhitespacePosition whitespacePosition = LeadingWhitespace) 344 { 345 if (it.object()->isFloatingOrOutOfFlowPositioned()) 346 return false; 347 348 if (it.object()->isRenderInline() && !alwaysRequiresLineBox(it.object()) && !requiresLineBoxForContent(toRenderInline(it.object()), lineInfo)) 349 return false; 350 351 if (!shouldCollapseWhiteSpace(it.object()->style(), lineInfo, whitespacePosition) || it.object()->isBR()) 352 return true; 353 354 UChar current = it.current(); 355 bool notJustWhitespace = current != ' ' && current != '\t' && current != softHyphen && (current != '\n' || it.object()->preservesNewline()); 356 return notJustWhitespace || isEmptyInline(it.object()); 357 } 358 359 inline void setStaticPositions(RenderBlockFlow* block, RenderBox* child) 360 { 361 // FIXME: The math here is actually not really right. It's a best-guess approximation that 362 // will work for the common cases 363 RenderObject* containerBlock = child->container(); 364 LayoutUnit blockHeight = block->logicalHeight(); 365 if (containerBlock->isRenderInline()) { 366 // A relative positioned inline encloses us. In this case, we also have to determine our 367 // position as though we were an inline. Set |staticInlinePosition| and |staticBlockPosition| on the relative positioned 368 // inline so that we can obtain the value later. 369 toRenderInline(containerBlock)->layer()->setStaticInlinePosition(block->startAlignedOffsetForLine(blockHeight, false)); 370 toRenderInline(containerBlock)->layer()->setStaticBlockPosition(blockHeight); 371 } 372 block->updateStaticInlinePositionForChild(child, blockHeight); 373 child->layer()->setStaticBlockPosition(blockHeight); 374 } 375 376 // FIXME: The entire concept of the skipTrailingWhitespace function is flawed, since we really need to be building 377 // line boxes even for containers that may ultimately collapse away. Otherwise we'll never get positioned 378 // elements quite right. In other words, we need to build this function's work into the normal line 379 // object iteration process. 380 // NB. this function will insert any floating elements that would otherwise 381 // be skipped but it will not position them. 382 inline void BreakingContext::skipTrailingWhitespace(InlineIterator& iterator, const LineInfo& lineInfo) 383 { 384 while (!iterator.atEnd() && !requiresLineBox(iterator, lineInfo, TrailingWhitespace)) { 385 RenderObject* object = iterator.object(); 386 if (object->isOutOfFlowPositioned()) 387 setStaticPositions(m_block, toRenderBox(object)); 388 else if (object->isFloating()) 389 m_block->insertFloatingObject(toRenderBox(object)); 390 iterator.increment(); 391 } 392 } 393 394 inline void BreakingContext::initializeForCurrentObject() 395 { 396 m_currentStyle = m_current.object()->style(); 397 m_nextObject = bidiNextSkippingEmptyInlines(m_block, m_current.object()); 398 if (m_nextObject && m_nextObject->parent() && !m_nextObject->parent()->isDescendantOf(m_current.object()->parent())) 399 m_includeEndWidth = true; 400 401 m_currWS = m_current.object()->isReplaced() ? m_current.object()->parent()->style()->whiteSpace() : m_currentStyle->whiteSpace(); 402 m_lastWS = m_lastObject->isReplaced() ? m_lastObject->parent()->style()->whiteSpace() : m_lastObject->style()->whiteSpace(); 403 404 m_autoWrap = RenderStyle::autoWrap(m_currWS); 405 m_autoWrapWasEverTrueOnLine = m_autoWrapWasEverTrueOnLine || m_autoWrap; 406 407 m_preservesNewline = m_current.object()->isSVGInlineText() ? false : RenderStyle::preserveNewline(m_currWS); 408 409 m_collapseWhiteSpace = RenderStyle::collapseWhiteSpace(m_currWS); 410 } 411 412 inline void BreakingContext::increment() 413 { 414 // Clear out our character space bool, since inline <pre>s don't collapse whitespace 415 // with adjacent inline normal/nowrap spans. 416 if (!m_collapseWhiteSpace) 417 m_currentCharacterIsSpace = false; 418 419 m_current.moveToStartOf(m_nextObject); 420 m_atStart = false; 421 } 422 423 inline void BreakingContext::handleBR(EClear& clear) 424 { 425 if (m_width.fitsOnLine()) { 426 RenderObject* br = m_current.object(); 427 m_lineBreak.moveToStartOf(br); 428 m_lineBreak.increment(); 429 430 // A <br> always breaks a line, so don't let the line be collapsed 431 // away. Also, the space at the end of a line with a <br> does not 432 // get collapsed away. It only does this if the previous line broke 433 // cleanly. Otherwise the <br> has no effect on whether the line is 434 // empty or not. 435 if (m_startingNewParagraph) 436 m_lineInfo.setEmpty(false, m_block, &m_width); 437 m_trailingObjects.clear(); 438 m_lineInfo.setPreviousLineBrokeCleanly(true); 439 440 // A <br> with clearance always needs a linebox in case the lines below it get dirtied later and 441 // need to check for floats to clear - so if we're ignoring spaces, stop ignoring them and add a 442 // run for this object. 443 if (m_ignoringSpaces && m_currentStyle->clear() != CNONE) 444 ensureLineBoxInsideIgnoredSpaces(m_lineMidpointState, br); 445 446 if (!m_lineInfo.isEmpty()) 447 clear = m_currentStyle->clear(); 448 } 449 m_atEnd = true; 450 } 451 452 inline LayoutUnit borderPaddingMarginStart(RenderInline* child) 453 { 454 return child->marginStart() + child->paddingStart() + child->borderStart(); 455 } 456 457 inline LayoutUnit borderPaddingMarginEnd(RenderInline* child) 458 { 459 return child->marginEnd() + child->paddingEnd() + child->borderEnd(); 460 } 461 462 inline bool shouldAddBorderPaddingMargin(RenderObject* child, bool &checkSide) 463 { 464 if (!child || (child->isText() && !toRenderText(child)->textLength())) 465 return true; 466 checkSide = false; 467 return checkSide; 468 } 469 470 inline LayoutUnit inlineLogicalWidth(RenderObject* child, bool start = true, bool end = true) 471 { 472 unsigned lineDepth = 1; 473 LayoutUnit extraWidth = 0; 474 RenderObject* parent = child->parent(); 475 while (parent->isRenderInline() && lineDepth++ < cMaxLineDepth) { 476 RenderInline* parentAsRenderInline = toRenderInline(parent); 477 if (!isEmptyInline(parentAsRenderInline)) { 478 if (start && shouldAddBorderPaddingMargin(child->previousSibling(), start)) 479 extraWidth += borderPaddingMarginStart(parentAsRenderInline); 480 if (end && shouldAddBorderPaddingMargin(child->nextSibling(), end)) 481 extraWidth += borderPaddingMarginEnd(parentAsRenderInline); 482 if (!start && !end) 483 return extraWidth; 484 } 485 child = parent; 486 parent = child->parent(); 487 } 488 return extraWidth; 489 } 490 491 inline void BreakingContext::handleOutOfFlowPositioned(Vector<RenderBox*>& positionedObjects) 492 { 493 // If our original display wasn't an inline type, then we can 494 // go ahead and determine our static inline position now. 495 RenderBox* box = toRenderBox(m_current.object()); 496 bool isInlineType = box->style()->isOriginalDisplayInlineType(); 497 if (!isInlineType) { 498 m_block->setStaticInlinePositionForChild(box, m_block->logicalHeight(), m_block->startOffsetForContent(m_block->logicalHeight())); 499 } else { 500 // If our original display was an INLINE type, then we can go ahead 501 // and determine our static y position now. 502 box->layer()->setStaticBlockPosition(m_block->logicalHeight()); 503 } 504 505 // If we're ignoring spaces, we have to stop and include this object and 506 // then start ignoring spaces again. 507 if (isInlineType || box->container()->isRenderInline()) { 508 if (m_ignoringSpaces) 509 ensureLineBoxInsideIgnoredSpaces(m_lineMidpointState, box); 510 m_trailingObjects.appendBoxIfNeeded(box); 511 } else { 512 positionedObjects.append(box); 513 } 514 m_width.addUncommittedWidth(inlineLogicalWidth(box)); 515 // Reset prior line break context characters. 516 m_renderTextInfo.m_lineBreakIterator.resetPriorContext(); 517 } 518 519 inline void BreakingContext::handleFloat() 520 { 521 RenderBox* floatBox = toRenderBox(m_current.object()); 522 FloatingObject* floatingObject = m_block->insertFloatingObject(floatBox); 523 // check if it fits in the current line. 524 // If it does, position it now, otherwise, position 525 // it after moving to next line (in newLine() func) 526 // FIXME: Bug 110372: Properly position multiple stacked floats with non-rectangular shape outside. 527 if (m_floatsFitOnLine && m_width.fitsOnLine(m_block->logicalWidthForFloat(floatingObject))) { 528 m_block->positionNewFloatOnLine(floatingObject, m_lastFloatFromPreviousLine, m_lineInfo, m_width); 529 if (m_lineBreak.object() == m_current.object()) { 530 ASSERT(!m_lineBreak.m_pos); 531 m_lineBreak.increment(); 532 } 533 } else { 534 m_floatsFitOnLine = false; 535 } 536 // Update prior line break context characters, using U+FFFD (OBJECT REPLACEMENT CHARACTER) for floating element. 537 m_renderTextInfo.m_lineBreakIterator.updatePriorContext(replacementCharacter); 538 } 539 540 // This is currently just used for list markers and inline flows that have line boxes. Neither should 541 // have an effect on whitespace at the start of the line. 542 inline bool shouldSkipWhitespaceAfterStartObject(RenderBlockFlow* block, RenderObject* o, LineMidpointState& lineMidpointState) 543 { 544 RenderObject* next = bidiNextSkippingEmptyInlines(block, o); 545 while (next && next->isFloatingOrOutOfFlowPositioned()) 546 next = bidiNextSkippingEmptyInlines(block, next); 547 548 if (next && !next->isBR() && next->isText() && toRenderText(next)->textLength() > 0) { 549 RenderText* nextText = toRenderText(next); 550 UChar nextChar = nextText->characterAt(0); 551 if (nextText->style()->isCollapsibleWhiteSpace(nextChar)) { 552 startIgnoringSpaces(lineMidpointState, InlineIterator(0, o, 0)); 553 return true; 554 } 555 } 556 557 return false; 558 } 559 560 inline void BreakingContext::handleEmptyInline() 561 { 562 // This should only end up being called on empty inlines 563 ASSERT(isEmptyInline(m_current.object())); 564 565 RenderInline* flowBox = toRenderInline(m_current.object()); 566 567 // Now that some inline flows have line boxes, if we are already ignoring spaces, we need 568 // to make sure that we stop to include this object and then start ignoring spaces again. 569 // If this object is at the start of the line, we need to behave like list markers and 570 // start ignoring spaces. 571 bool requiresLineBox = alwaysRequiresLineBox(m_current.object()); 572 if (requiresLineBox || requiresLineBoxForContent(flowBox, m_lineInfo)) { 573 // An empty inline that only has line-height, vertical-align or font-metrics will only get a 574 // line box to affect the height of the line if the rest of the line is not empty. 575 if (requiresLineBox) 576 m_lineInfo.setEmpty(false, m_block, &m_width); 577 if (m_ignoringSpaces) { 578 m_trailingObjects.clear(); 579 ensureLineBoxInsideIgnoredSpaces(m_lineMidpointState, m_current.object()); 580 } else if (m_blockStyle->collapseWhiteSpace() && m_resolver.position().object() == m_current.object() 581 && shouldSkipWhitespaceAfterStartObject(m_block, m_current.object(), m_lineMidpointState)) { 582 // Like with list markers, we start ignoring spaces to make sure that any 583 // additional spaces we see will be discarded. 584 m_currentCharacterShouldCollapseIfPreWap = m_currentCharacterIsSpace = true; 585 m_ignoringSpaces = true; 586 } 587 } 588 589 m_width.addUncommittedWidth(inlineLogicalWidth(m_current.object()) + borderPaddingMarginStart(flowBox) + borderPaddingMarginEnd(flowBox)); 590 } 591 592 inline void BreakingContext::handleReplaced() 593 { 594 RenderBox* replacedBox = toRenderBox(m_current.object()); 595 596 if (m_atStart) 597 m_width.updateAvailableWidth(replacedBox->logicalHeight()); 598 599 // Break on replaced elements if either has normal white-space. 600 if ((m_autoWrap || RenderStyle::autoWrap(m_lastWS)) && (!m_current.object()->isImage() || m_allowImagesToBreak)) { 601 m_width.commit(); 602 m_lineBreak.moveToStartOf(m_current.object()); 603 } 604 605 if (m_ignoringSpaces) 606 stopIgnoringSpaces(m_lineMidpointState, InlineIterator(0, m_current.object(), 0)); 607 608 m_lineInfo.setEmpty(false, m_block, &m_width); 609 m_ignoringSpaces = false; 610 m_currentCharacterShouldCollapseIfPreWap = m_currentCharacterIsSpace = false; 611 m_trailingObjects.clear(); 612 613 // Optimize for a common case. If we can't find whitespace after the list 614 // item, then this is all moot. 615 LayoutUnit replacedLogicalWidth = m_block->logicalWidthForChild(replacedBox) + m_block->marginStartForChild(replacedBox) + m_block->marginEndForChild(replacedBox) + inlineLogicalWidth(m_current.object()); 616 if (m_current.object()->isListMarker()) { 617 if (m_blockStyle->collapseWhiteSpace() && shouldSkipWhitespaceAfterStartObject(m_block, m_current.object(), m_lineMidpointState)) { 618 // Like with inline flows, we start ignoring spaces to make sure that any 619 // additional spaces we see will be discarded. 620 m_currentCharacterShouldCollapseIfPreWap = m_currentCharacterIsSpace = true; 621 m_ignoringSpaces = true; 622 } 623 if (toRenderListMarker(m_current.object())->isInside()) 624 m_width.addUncommittedWidth(replacedLogicalWidth); 625 } else { 626 m_width.addUncommittedWidth(replacedLogicalWidth); 627 } 628 if (m_current.object()->isRubyRun()) 629 m_width.applyOverhang(toRenderRubyRun(m_current.object()), m_lastObject, m_nextObject); 630 // Update prior line break context characters, using U+FFFD (OBJECT REPLACEMENT CHARACTER) for replaced element. 631 m_renderTextInfo.m_lineBreakIterator.updatePriorContext(replacementCharacter); 632 } 633 634 inline bool iteratorIsBeyondEndOfRenderCombineText(const InlineIterator& iter, RenderCombineText* renderer) 635 { 636 return iter.object() == renderer && iter.m_pos >= renderer->textLength(); 637 } 638 639 inline void nextCharacter(UChar& currentCharacter, UChar& lastCharacter, UChar& secondToLastCharacter) 640 { 641 secondToLastCharacter = lastCharacter; 642 lastCharacter = currentCharacter; 643 } 644 645 inline float firstPositiveWidth(const WordMeasurements& wordMeasurements) 646 { 647 for (size_t i = 0; i < wordMeasurements.size(); ++i) { 648 if (wordMeasurements[i].width > 0) 649 return wordMeasurements[i].width; 650 } 651 return 0; 652 } 653 654 inline void updateSegmentsForShapes(RenderBlockFlow* block, const FloatingObject* lastFloatFromPreviousLine, const WordMeasurements& wordMeasurements, LineWidth& width, bool isFirstLine) 655 { 656 ASSERT(lastFloatFromPreviousLine); 657 658 ShapeInsideInfo* shapeInsideInfo = block->layoutShapeInsideInfo(); 659 if (!lastFloatFromPreviousLine->isPlaced() || !shapeInsideInfo) 660 return; 661 662 bool isHorizontalWritingMode = block->isHorizontalWritingMode(); 663 LayoutUnit logicalOffsetFromShapeContainer = block->logicalOffsetFromShapeAncestorContainer(shapeInsideInfo->owner()).height(); 664 665 LayoutUnit lineLogicalTop = block->logicalHeight() + logicalOffsetFromShapeContainer; 666 LayoutUnit lineLogicalHeight = block->lineHeight(isFirstLine, isHorizontalWritingMode ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes); 667 LayoutUnit lineLogicalBottom = lineLogicalTop + lineLogicalHeight; 668 669 LayoutUnit floatLogicalTop = block->logicalTopForFloat(lastFloatFromPreviousLine); 670 LayoutUnit floatLogicalBottom = block->logicalBottomForFloat(lastFloatFromPreviousLine); 671 672 bool lineOverlapsWithFloat = (floatLogicalTop < lineLogicalBottom) && (lineLogicalTop < floatLogicalBottom); 673 if (!lineOverlapsWithFloat) 674 return; 675 676 float minSegmentWidth = firstPositiveWidth(wordMeasurements); 677 678 LayoutUnit floatLogicalWidth = block->logicalWidthForFloat(lastFloatFromPreviousLine); 679 LayoutUnit availableLogicalWidth = block->logicalWidth() - block->logicalRightForFloat(lastFloatFromPreviousLine); 680 if (availableLogicalWidth < minSegmentWidth) 681 block->setLogicalHeight(floatLogicalBottom); 682 683 if (block->logicalHeight() < floatLogicalTop) { 684 shapeInsideInfo->adjustLogicalLineTop(minSegmentWidth + floatLogicalWidth); 685 block->setLogicalHeight(shapeInsideInfo->logicalLineTop() - logicalOffsetFromShapeContainer); 686 } 687 688 lineLogicalTop = block->logicalHeight() + logicalOffsetFromShapeContainer; 689 690 shapeInsideInfo->updateSegmentsForLine(lineLogicalTop, lineLogicalHeight); 691 width.updateCurrentShapeSegment(); 692 width.updateAvailableWidth(); 693 } 694 695 inline float measureHyphenWidth(RenderText* renderer, const Font& font) 696 { 697 RenderStyle* style = renderer->style(); 698 return font.width(RenderBlockFlow::constructTextRun(renderer, font, style->hyphenString().string(), style)); 699 } 700 701 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) 702 { 703 GlyphOverflow glyphOverflow; 704 if (isFixedPitch || (!from && len == text->textLength()) || text->style()->hasTextCombine()) 705 return text->width(from, len, font, xPos, fallbackFonts, &glyphOverflow); 706 707 if (layout) 708 return Font::width(*layout, from, len, fallbackFonts); 709 710 TextRun run = RenderBlockFlow::constructTextRun(text, font, text, from, len, text->style()); 711 run.setCharactersLength(text->textLength() - from); 712 ASSERT(run.charactersLength() >= run.length()); 713 714 run.setCharacterScanForCodePath(!text->canUseSimpleFontCodePath()); 715 run.setTabSize(!collapseWhiteSpace, text->style()->tabSize()); 716 run.setXPos(xPos); 717 return font.width(run, fallbackFonts, &glyphOverflow); 718 } 719 720 inline bool BreakingContext::handleText(WordMeasurements& wordMeasurements, bool& hyphenated) 721 { 722 if (!m_current.m_pos) 723 m_appliedStartWidth = false; 724 725 RenderText* renderText = toRenderText(m_current.object()); 726 727 bool isSVGText = renderText->isSVGInlineText(); 728 729 if (renderText->style()->hasTextCombine() && m_current.object()->isCombineText() && !toRenderCombineText(m_current.object())->isCombined()) { 730 RenderCombineText* combineRenderer = toRenderCombineText(m_current.object()); 731 combineRenderer->combineText(); 732 // The length of the renderer's text may have changed. Increment stale iterator positions 733 if (iteratorIsBeyondEndOfRenderCombineText(m_lineBreak, combineRenderer)) { 734 ASSERT(iteratorIsBeyondEndOfRenderCombineText(m_resolver.position(), combineRenderer)); 735 m_lineBreak.increment(); 736 m_resolver.position().increment(&m_resolver); 737 } 738 } 739 740 RenderStyle* style = renderText->style(m_lineInfo.isFirstLine()); 741 const Font& font = style->font(); 742 bool isFixedPitch = font.isFixedPitch(); 743 744 unsigned lastSpace = m_current.m_pos; 745 float wordSpacing = m_currentStyle->wordSpacing(); 746 float lastSpaceWordSpacing = 0; 747 float wordSpacingForWordMeasurement = 0; 748 749 float wrapW = m_width.uncommittedWidth() + inlineLogicalWidth(m_current.object(), !m_appliedStartWidth, true); 750 float charWidth = 0; 751 // Auto-wrapping text should wrap in the middle of a word only if it could not wrap before the word, 752 // which is only possible if the word is the first thing on the line, that is, if |w| is zero. 753 bool breakWords = m_currentStyle->breakWords() && ((m_autoWrap && !m_width.committedWidth()) || m_currWS == PRE); 754 bool midWordBreak = false; 755 bool breakAll = m_currentStyle->wordBreak() == BreakAllWordBreak && m_autoWrap; 756 float hyphenWidth = 0; 757 758 if (isSVGText) { 759 breakWords = false; 760 breakAll = false; 761 } 762 763 if (renderText->isWordBreak()) { 764 m_width.commit(); 765 m_lineBreak.moveToStartOf(m_current.object()); 766 ASSERT(m_current.m_pos == renderText->textLength()); 767 } 768 769 if (m_renderTextInfo.m_text != renderText) { 770 m_renderTextInfo.m_text = renderText; 771 m_renderTextInfo.m_font = &font; 772 m_renderTextInfo.createLayout(renderText, m_width.currentWidth(), m_collapseWhiteSpace); 773 m_renderTextInfo.m_lineBreakIterator.resetStringAndReleaseIterator(renderText->text(), style->locale()); 774 } else if (m_renderTextInfo.m_layout && m_renderTextInfo.m_font != &font) { 775 m_renderTextInfo.m_font = &font; 776 m_renderTextInfo.createLayout(renderText, m_width.currentWidth(), m_collapseWhiteSpace); 777 } 778 779 TextLayout* textLayout = m_renderTextInfo.m_layout.get(); 780 781 // Non-zero only when kerning is enabled and TextLayout isn't used, in which case we measure 782 // words with their trailing space, then subtract its width. 783 float wordTrailingSpaceWidth = (font.typesettingFeatures() & Kerning) && !textLayout ? font.width(RenderBlockFlow::constructTextRun(renderText, font, &space, 1, style)) + wordSpacing : 0; 784 785 UChar lastCharacter = m_renderTextInfo.m_lineBreakIterator.lastCharacter(); 786 UChar secondToLastCharacter = m_renderTextInfo.m_lineBreakIterator.secondToLastCharacter(); 787 for (; m_current.m_pos < renderText->textLength(); m_current.fastIncrementInTextNode()) { 788 bool previousCharacterIsSpace = m_currentCharacterIsSpace; 789 bool previousCharacterShouldCollapseIfPreWap = m_currentCharacterShouldCollapseIfPreWap; 790 UChar c = m_current.current(); 791 m_currentCharacterShouldCollapseIfPreWap = m_currentCharacterIsSpace = c == ' ' || c == '\t' || (!m_preservesNewline && (c == '\n')); 792 793 if (!m_collapseWhiteSpace || !m_currentCharacterIsSpace) 794 m_lineInfo.setEmpty(false, m_block, &m_width); 795 796 if (c == softHyphen && m_autoWrap && !hyphenWidth) { 797 hyphenWidth = measureHyphenWidth(renderText, font); 798 m_width.addUncommittedWidth(hyphenWidth); 799 } 800 801 bool applyWordSpacing = false; 802 803 if ((breakAll || breakWords) && !midWordBreak) { 804 wrapW += charWidth; 805 bool midWordBreakIsBeforeSurrogatePair = U16_IS_LEAD(c) && m_current.m_pos + 1 < renderText->textLength() && U16_IS_TRAIL((*renderText)[m_current.m_pos + 1]); 806 charWidth = textWidth(renderText, m_current.m_pos, midWordBreakIsBeforeSurrogatePair ? 2 : 1, font, m_width.committedWidth() + wrapW, isFixedPitch, m_collapseWhiteSpace, 0, textLayout); 807 midWordBreak = m_width.committedWidth() + wrapW + charWidth > m_width.availableWidth(); 808 } 809 810 bool betweenWords = c == '\n' || (m_currWS != PRE && !m_atStart && isBreakable(m_renderTextInfo.m_lineBreakIterator, m_current.m_pos, m_current.m_nextBreakablePosition)); 811 812 if (betweenWords || midWordBreak) { 813 bool stoppedIgnoringSpaces = false; 814 if (m_ignoringSpaces) { 815 lastSpaceWordSpacing = 0; 816 if (!m_currentCharacterIsSpace) { 817 // Stop ignoring spaces and begin at this 818 // new point. 819 m_ignoringSpaces = false; 820 wordSpacingForWordMeasurement = 0; 821 lastSpace = m_current.m_pos; // e.g., "Foo goo", don't add in any of the ignored spaces. 822 stopIgnoringSpaces(m_lineMidpointState, InlineIterator(0, m_current.object(), m_current.m_pos)); 823 stoppedIgnoringSpaces = true; 824 } else { 825 // Just keep ignoring these spaces. 826 nextCharacter(c, lastCharacter, secondToLastCharacter); 827 continue; 828 } 829 } 830 831 wordMeasurements.grow(wordMeasurements.size() + 1); 832 WordMeasurement& wordMeasurement = wordMeasurements.last(); 833 834 wordMeasurement.renderer = renderText; 835 wordMeasurement.endOffset = m_current.m_pos; 836 wordMeasurement.startOffset = lastSpace; 837 838 float additionalTmpW; 839 if (wordTrailingSpaceWidth && c == ' ') 840 additionalTmpW = textWidth(renderText, lastSpace, m_current.m_pos + 1 - lastSpace, font, m_width.currentWidth(), isFixedPitch, m_collapseWhiteSpace, &wordMeasurement.fallbackFonts, textLayout) - wordTrailingSpaceWidth; 841 else 842 additionalTmpW = textWidth(renderText, lastSpace, m_current.m_pos - lastSpace, font, m_width.currentWidth(), isFixedPitch, m_collapseWhiteSpace, &wordMeasurement.fallbackFonts, textLayout); 843 844 wordMeasurement.width = additionalTmpW + wordSpacingForWordMeasurement; 845 additionalTmpW += lastSpaceWordSpacing; 846 m_width.addUncommittedWidth(additionalTmpW); 847 if (!m_appliedStartWidth) { 848 m_width.addUncommittedWidth(inlineLogicalWidth(m_current.object(), true, false)); 849 m_appliedStartWidth = true; 850 } 851 852 if (m_lastFloatFromPreviousLine) 853 updateSegmentsForShapes(m_block, m_lastFloatFromPreviousLine, wordMeasurements, m_width, m_lineInfo.isFirstLine()); 854 855 applyWordSpacing = wordSpacing && m_currentCharacterIsSpace; 856 857 if (!m_width.committedWidth() && m_autoWrap && !m_width.fitsOnLine()) 858 m_width.fitBelowFloats(); 859 860 if (m_autoWrap || breakWords) { 861 // If we break only after white-space, consider the current character 862 // as candidate width for this line. 863 bool lineWasTooWide = false; 864 if (m_width.fitsOnLine() && m_currentCharacterIsSpace && m_currentStyle->breakOnlyAfterWhiteSpace() && !midWordBreak) { 865 float charWidth = textWidth(renderText, m_current.m_pos, 1, font, m_width.currentWidth(), isFixedPitch, m_collapseWhiteSpace, &wordMeasurement.fallbackFonts, textLayout) + (applyWordSpacing ? wordSpacing : 0); 866 // Check if line is too big even without the extra space 867 // at the end of the line. If it is not, do nothing. 868 // If the line needs the extra whitespace to be too long, 869 // then move the line break to the space and skip all 870 // additional whitespace. 871 if (!m_width.fitsOnLine(charWidth)) { 872 lineWasTooWide = true; 873 m_lineBreak.moveTo(m_current.object(), m_current.m_pos, m_current.m_nextBreakablePosition); 874 skipTrailingWhitespace(m_lineBreak, m_lineInfo); 875 } 876 } 877 if (lineWasTooWide || !m_width.fitsOnLine()) { 878 if (m_lineBreak.atTextParagraphSeparator()) { 879 if (!stoppedIgnoringSpaces && m_current.m_pos > 0) 880 ensureCharacterGetsLineBox(m_lineMidpointState, m_current); 881 m_lineBreak.increment(); 882 m_lineInfo.setPreviousLineBrokeCleanly(true); 883 wordMeasurement.endOffset = m_lineBreak.m_pos; 884 } 885 if (m_lineBreak.object() && m_lineBreak.m_pos && m_lineBreak.object()->isText() && toRenderText(m_lineBreak.object())->textLength() && toRenderText(m_lineBreak.object())->characterAt(m_lineBreak.m_pos - 1) == softHyphen) 886 hyphenated = true; 887 if (m_lineBreak.m_pos && m_lineBreak.m_pos != (unsigned)wordMeasurement.endOffset && !wordMeasurement.width) { 888 if (charWidth) { 889 wordMeasurement.endOffset = m_lineBreak.m_pos; 890 wordMeasurement.width = charWidth; 891 } 892 } 893 // Didn't fit. Jump to the end unless there's still an opportunity to collapse whitespace. 894 if (m_ignoringSpaces || !m_collapseWhiteSpace || !m_currentCharacterIsSpace || !previousCharacterIsSpace) { 895 m_atEnd = true; 896 return false; 897 } 898 } else { 899 if (!betweenWords || (midWordBreak && !m_autoWrap)) 900 m_width.addUncommittedWidth(-additionalTmpW); 901 if (hyphenWidth) { 902 // Subtract the width of the soft hyphen out since we fit on a line. 903 m_width.addUncommittedWidth(-hyphenWidth); 904 hyphenWidth = 0; 905 } 906 } 907 } 908 909 if (c == '\n' && m_preservesNewline) { 910 if (!stoppedIgnoringSpaces && m_current.m_pos > 0) 911 ensureCharacterGetsLineBox(m_lineMidpointState, m_current); 912 m_lineBreak.moveTo(m_current.object(), m_current.m_pos, m_current.m_nextBreakablePosition); 913 m_lineBreak.increment(); 914 m_lineInfo.setPreviousLineBrokeCleanly(true); 915 return true; 916 } 917 918 if (m_autoWrap && betweenWords) { 919 m_width.commit(); 920 wrapW = 0; 921 m_lineBreak.moveTo(m_current.object(), m_current.m_pos, m_current.m_nextBreakablePosition); 922 // Auto-wrapping text should not wrap in the middle of a word once it has had an 923 // opportunity to break after a word. 924 breakWords = false; 925 } 926 927 if (midWordBreak && !U16_IS_TRAIL(c) && !(category(c) & (Mark_NonSpacing | Mark_Enclosing | Mark_SpacingCombining))) { 928 // Remember this as a breakable position in case 929 // adding the end width forces a break. 930 m_lineBreak.moveTo(m_current.object(), m_current.m_pos, m_current.m_nextBreakablePosition); 931 midWordBreak &= (breakWords || breakAll); 932 } 933 934 if (betweenWords) { 935 lastSpaceWordSpacing = applyWordSpacing ? wordSpacing : 0; 936 wordSpacingForWordMeasurement = (applyWordSpacing && wordMeasurement.width) ? wordSpacing : 0; 937 lastSpace = m_current.m_pos; 938 } 939 940 if (!m_ignoringSpaces && m_currentStyle->collapseWhiteSpace()) { 941 // If we encounter a newline, or if we encounter a 942 // second space, we need to go ahead and break up this 943 // run and enter a mode where we start collapsing spaces. 944 if (m_currentCharacterIsSpace && previousCharacterIsSpace) { 945 m_ignoringSpaces = true; 946 947 // We just entered a mode where we are ignoring 948 // spaces. Create a midpoint to terminate the run 949 // before the second space. 950 startIgnoringSpaces(m_lineMidpointState, m_startOfIgnoredSpaces); 951 m_trailingObjects.updateMidpointsForTrailingBoxes(m_lineMidpointState, InlineIterator(), TrailingObjects::DoNotCollapseFirstSpace); 952 } 953 } 954 } else if (m_ignoringSpaces) { 955 // Stop ignoring spaces and begin at this 956 // new point. 957 m_ignoringSpaces = false; 958 lastSpaceWordSpacing = applyWordSpacing ? wordSpacing : 0; 959 wordSpacingForWordMeasurement = (applyWordSpacing && wordMeasurements.last().width) ? wordSpacing : 0; 960 lastSpace = m_current.m_pos; // e.g., "Foo goo", don't add in any of the ignored spaces. 961 stopIgnoringSpaces(m_lineMidpointState, InlineIterator(0, m_current.object(), m_current.m_pos)); 962 } 963 964 if (isSVGText && m_current.m_pos > 0) { 965 // Force creation of new InlineBoxes for each absolute positioned character (those that start new text chunks). 966 if (toRenderSVGInlineText(renderText)->characterStartsNewTextChunk(m_current.m_pos)) 967 ensureCharacterGetsLineBox(m_lineMidpointState, m_current); 968 } 969 970 if (m_currentCharacterIsSpace && !previousCharacterIsSpace) { 971 m_startOfIgnoredSpaces.setObject(m_current.object()); 972 m_startOfIgnoredSpaces.m_pos = m_current.m_pos; 973 } 974 975 if (!m_currentCharacterIsSpace && previousCharacterShouldCollapseIfPreWap) { 976 if (m_autoWrap && m_currentStyle->breakOnlyAfterWhiteSpace()) 977 m_lineBreak.moveTo(m_current.object(), m_current.m_pos, m_current.m_nextBreakablePosition); 978 } 979 980 if (m_collapseWhiteSpace && m_currentCharacterIsSpace && !m_ignoringSpaces) 981 m_trailingObjects.setTrailingWhitespace(toRenderText(m_current.object())); 982 else if (!m_currentStyle->collapseWhiteSpace() || !m_currentCharacterIsSpace) 983 m_trailingObjects.clear(); 984 985 m_atStart = false; 986 nextCharacter(c, lastCharacter, secondToLastCharacter); 987 } 988 989 m_renderTextInfo.m_lineBreakIterator.setPriorContext(lastCharacter, secondToLastCharacter); 990 991 wordMeasurements.grow(wordMeasurements.size() + 1); 992 WordMeasurement& wordMeasurement = wordMeasurements.last(); 993 wordMeasurement.renderer = renderText; 994 995 // IMPORTANT: current.m_pos is > length here! 996 float additionalTmpW = m_ignoringSpaces ? 0 : textWidth(renderText, lastSpace, m_current.m_pos - lastSpace, font, m_width.currentWidth(), isFixedPitch, m_collapseWhiteSpace, &wordMeasurement.fallbackFonts, textLayout); 997 wordMeasurement.startOffset = lastSpace; 998 wordMeasurement.endOffset = m_current.m_pos; 999 wordMeasurement.width = m_ignoringSpaces ? 0 : additionalTmpW + wordSpacingForWordMeasurement; 1000 additionalTmpW += lastSpaceWordSpacing; 1001 m_width.addUncommittedWidth(additionalTmpW + inlineLogicalWidth(m_current.object(), !m_appliedStartWidth, m_includeEndWidth)); 1002 m_includeEndWidth = false; 1003 1004 if (!m_width.fitsOnLine()) { 1005 if (!hyphenated && m_lineBreak.previousInSameNode() == softHyphen) { 1006 hyphenated = true; 1007 m_atEnd = true; 1008 } 1009 } 1010 return false; 1011 } 1012 1013 inline void BreakingContext::commitAndUpdateLineBreakIfNeeded() 1014 { 1015 bool checkForBreak = m_autoWrap; 1016 if (m_width.committedWidth() && !m_width.fitsOnLine() && m_lineBreak.object() && m_currWS == NOWRAP) { 1017 checkForBreak = true; 1018 } else if (m_nextObject && m_current.object()->isText() && m_nextObject->isText() && !m_nextObject->isBR() && (m_autoWrap || m_nextObject->style()->autoWrap())) { 1019 if (m_autoWrap && m_currentCharacterIsSpace) { 1020 checkForBreak = true; 1021 } else { 1022 RenderText* nextText = toRenderText(m_nextObject); 1023 if (nextText->textLength()) { 1024 UChar c = nextText->characterAt(0); 1025 // If the next item on the line is text, and if we did not end with 1026 // a space, then the next text run continues our word (and so it needs to 1027 // keep adding to the uncommitted width. Just update and continue. 1028 checkForBreak = !m_currentCharacterIsSpace && (c == ' ' || c == '\t' || (c == '\n' && !m_nextObject->preservesNewline())); 1029 } else if (nextText->isWordBreak()) { 1030 checkForBreak = true; 1031 } 1032 1033 if (!m_width.fitsOnLine() && !m_width.committedWidth()) 1034 m_width.fitBelowFloats(); 1035 1036 bool canPlaceOnLine = m_width.fitsOnLine() || !m_autoWrapWasEverTrueOnLine; 1037 if (canPlaceOnLine && checkForBreak) { 1038 m_width.commit(); 1039 m_lineBreak.moveToStartOf(m_nextObject); 1040 } 1041 } 1042 } 1043 1044 if (checkForBreak && !m_width.fitsOnLine()) { 1045 // if we have floats, try to get below them. 1046 if (m_currentCharacterIsSpace && !m_ignoringSpaces && m_currentStyle->collapseWhiteSpace()) 1047 m_trailingObjects.clear(); 1048 1049 if (m_width.committedWidth()) { 1050 m_atEnd = true; 1051 return; 1052 } 1053 1054 m_width.fitBelowFloats(); 1055 1056 // |width| may have been adjusted because we got shoved down past a float (thus 1057 // giving us more room), so we need to retest, and only jump to 1058 // the end label if we still don't fit on the line. -dwh 1059 if (!m_width.fitsOnLine()) { 1060 m_atEnd = true; 1061 return; 1062 } 1063 } else if (m_blockStyle->autoWrap() && !m_width.fitsOnLine() && !m_width.committedWidth()) { 1064 // If the container autowraps but the current child does not then we still need to ensure that it 1065 // wraps and moves below any floats. 1066 m_width.fitBelowFloats(); 1067 } 1068 1069 if (!m_current.object()->isFloatingOrOutOfFlowPositioned()) { 1070 m_lastObject = m_current.object(); 1071 if (m_lastObject->isReplaced() && m_autoWrap && (!m_lastObject->isImage() || m_allowImagesToBreak) && (!m_lastObject->isListMarker() || toRenderListMarker(m_lastObject)->isInside())) { 1072 m_width.commit(); 1073 m_lineBreak.moveToStartOf(m_nextObject); 1074 } 1075 } 1076 } 1077 1078 inline void checkMidpoints(LineMidpointState& lineMidpointState, InlineIterator& lBreak) 1079 { 1080 // Check to see if our last midpoint is a start point beyond the line break. If so, 1081 // shave it off the list, and shave off a trailing space if the previous end point doesn't 1082 // preserve whitespace. 1083 if (lBreak.object() && lineMidpointState.numMidpoints && !(lineMidpointState.numMidpoints % 2)) { 1084 InlineIterator* midpoints = lineMidpointState.midpoints.data(); 1085 InlineIterator& endpoint = midpoints[lineMidpointState.numMidpoints - 2]; 1086 const InlineIterator& startpoint = midpoints[lineMidpointState.numMidpoints - 1]; 1087 InlineIterator currpoint = endpoint; 1088 while (!currpoint.atEnd() && currpoint != startpoint && currpoint != lBreak) 1089 currpoint.increment(); 1090 if (currpoint == lBreak) { 1091 // We hit the line break before the start point. Shave off the start point. 1092 lineMidpointState.numMidpoints--; 1093 if (endpoint.object()->style()->collapseWhiteSpace() && endpoint.object()->isText()) 1094 endpoint.m_pos--; 1095 } 1096 } 1097 } 1098 1099 InlineIterator BreakingContext::handleEndOfLine() 1100 { 1101 ShapeInsideInfo* shapeInfo = m_block->layoutShapeInsideInfo(); 1102 bool segmentAllowsOverflow = !shapeInfo || !shapeInfo->hasSegments(); 1103 1104 if (m_lineBreak == m_resolver.position() && (!m_lineBreak.object() || !m_lineBreak.object()->isBR()) && segmentAllowsOverflow) { 1105 // we just add as much as possible 1106 if (m_blockStyle->whiteSpace() == PRE && !m_current.m_pos) { 1107 m_lineBreak.moveTo(m_lastObject, m_lastObject->isText() ? m_lastObject->length() : 0); 1108 } else if (m_lineBreak.object()) { 1109 // Don't ever break in the middle of a word if we can help it. 1110 // There's no room at all. We just have to be on this line, 1111 // even though we'll spill out. 1112 m_lineBreak.moveTo(m_current.object(), m_current.m_pos); 1113 } 1114 } 1115 1116 // FIXME Bug 100049: We do not need to consume input in a multi-segment line 1117 // unless no segment will. 1118 // make sure we consume at least one char/object. 1119 if (m_lineBreak == m_resolver.position() && segmentAllowsOverflow) 1120 m_lineBreak.increment(); 1121 1122 // Sanity check our midpoints. 1123 checkMidpoints(m_lineMidpointState, m_lineBreak); 1124 1125 m_trailingObjects.updateMidpointsForTrailingBoxes(m_lineMidpointState, m_lineBreak, TrailingObjects::CollapseFirstSpace); 1126 1127 // We might have made lineBreak an iterator that points past the end 1128 // of the object. Do this adjustment to make it point to the start 1129 // of the next object instead to avoid confusing the rest of the 1130 // code. 1131 if (m_lineBreak.m_pos > 0) { 1132 // This loop enforces the invariant that line breaks should never point 1133 // at an empty inline. See http://crbug.com/305904. 1134 do { 1135 m_lineBreak.m_pos--; 1136 m_lineBreak.increment(); 1137 } while (!m_lineBreak.atEnd() && isEmptyInline(m_lineBreak.object())); 1138 } 1139 1140 return m_lineBreak; 1141 } 1142 1143 } 1144 1145 #endif // BreakingContextInlineHeaders_h 1146