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/RenderCombineText.h" 30 #include "core/rendering/RenderInline.h" 31 #include "core/rendering/RenderLayer.h" 32 #include "core/rendering/RenderListMarker.h" 33 #include "core/rendering/RenderObjectInlines.h" 34 #include "core/rendering/RenderRubyRun.h" 35 #include "core/rendering/TextRunConstructor.h" 36 #include "core/rendering/break_lines.h" 37 #include "core/rendering/line/LineBreaker.h" 38 #include "core/rendering/line/LineInfo.h" 39 #include "core/rendering/line/LineWidth.h" 40 #include "core/rendering/line/RenderTextInfo.h" 41 #include "core/rendering/line/TrailingObjects.h" 42 #include "core/rendering/line/WordMeasurement.h" 43 #include "core/rendering/svg/RenderSVGInlineText.h" 44 45 namespace blink { 46 47 // We don't let our line box tree for a single line get any deeper than this. 48 const unsigned cMaxLineDepth = 200; 49 50 class BreakingContext { 51 public: 52 BreakingContext(InlineBidiResolver& resolver, LineInfo& inLineInfo, LineWidth& lineWidth, RenderTextInfo& inRenderTextInfo, FloatingObject* inLastFloatFromPreviousLine, bool appliedStartWidth, RenderBlockFlow* block) 53 : m_resolver(resolver) 54 , m_current(resolver.position()) 55 , m_lineBreak(resolver.position()) 56 , m_block(block) 57 , m_lastObject(m_current.object()) 58 , m_nextObject(0) 59 , m_currentStyle(0) 60 , m_blockStyle(block->style()) 61 , m_lineInfo(inLineInfo) 62 , m_renderTextInfo(inRenderTextInfo) 63 , m_lastFloatFromPreviousLine(inLastFloatFromPreviousLine) 64 , m_width(lineWidth) 65 , m_currWS(NORMAL) 66 , m_lastWS(NORMAL) 67 , m_preservesNewline(false) 68 , m_atStart(true) 69 , m_ignoringSpaces(false) 70 , m_currentCharacterIsSpace(false) 71 , m_currentCharacterShouldCollapseIfPreWap(false) 72 , m_appliedStartWidth(appliedStartWidth) 73 , m_includeEndWidth(true) 74 , m_autoWrap(false) 75 , m_autoWrapWasEverTrueOnLine(false) 76 , m_floatsFitOnLine(true) 77 , m_collapseWhiteSpace(false) 78 , m_startingNewParagraph(m_lineInfo.previousLineBrokeCleanly()) 79 , m_allowImagesToBreak(!block->document().inQuirksMode() || !block->isTableCell() || !m_blockStyle->logicalWidth().isIntrinsicOrAuto()) 80 , m_atEnd(false) 81 , m_lineMidpointState(resolver.midpointState()) 82 { 83 m_lineInfo.setPreviousLineBrokeCleanly(false); 84 } 85 86 RenderObject* currentObject() { return m_current.object(); } 87 InlineIterator lineBreak() { return m_lineBreak; } 88 bool atEnd() { return m_atEnd; } 89 90 void initializeForCurrentObject(); 91 92 void increment(); 93 94 void handleBR(EClear&); 95 void handleOutOfFlowPositioned(Vector<RenderBox*>& positionedObjects); 96 void handleFloat(); 97 void handleEmptyInline(); 98 void handleReplaced(); 99 bool handleText(WordMeasurements&, bool& hyphenated); 100 void commitAndUpdateLineBreakIfNeeded(); 101 InlineIterator handleEndOfLine(); 102 103 void clearLineBreakIfFitsOnLine() 104 { 105 if (m_width.fitsOnLine() || m_lastWS == NOWRAP) 106 m_lineBreak.clear(); 107 } 108 109 private: 110 void skipTrailingWhitespace(InlineIterator&, const LineInfo&); 111 112 InlineBidiResolver& m_resolver; 113 114 InlineIterator m_current; 115 InlineIterator m_lineBreak; 116 InlineIterator m_startOfIgnoredSpaces; 117 118 RenderBlockFlow* m_block; 119 RenderObject* m_lastObject; 120 RenderObject* m_nextObject; 121 122 RenderStyle* m_currentStyle; 123 RenderStyle* m_blockStyle; 124 125 LineInfo& m_lineInfo; 126 127 RenderTextInfo& m_renderTextInfo; 128 129 FloatingObject* m_lastFloatFromPreviousLine; 130 131 LineWidth m_width; 132 133 EWhiteSpace m_currWS; 134 EWhiteSpace m_lastWS; 135 136 bool m_preservesNewline; 137 bool m_atStart; 138 bool m_ignoringSpaces; 139 bool m_currentCharacterIsSpace; 140 bool m_currentCharacterShouldCollapseIfPreWap; 141 bool m_appliedStartWidth; 142 bool m_includeEndWidth; 143 bool m_autoWrap; 144 bool m_autoWrapWasEverTrueOnLine; 145 bool m_floatsFitOnLine; 146 bool m_collapseWhiteSpace; 147 bool m_startingNewParagraph; 148 bool m_allowImagesToBreak; 149 bool m_atEnd; 150 151 LineMidpointState& m_lineMidpointState; 152 153 TrailingObjects m_trailingObjects; 154 }; 155 156 inline bool shouldCollapseWhiteSpace(const RenderStyle* style, const LineInfo& lineInfo, WhitespacePosition whitespacePosition) 157 { 158 // CSS2 16.6.1 159 // If a space (U+0020) at the beginning of a line has 'white-space' set to 'normal', 'nowrap', or 'pre-line', it is removed. 160 // 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. 161 // 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. 162 return style->collapseWhiteSpace() 163 || (whitespacePosition == TrailingWhitespace && style->whiteSpace() == PRE_WRAP && (!lineInfo.isEmpty() || !lineInfo.previousLineBrokeCleanly())); 164 } 165 166 inline bool requiresLineBoxForContent(RenderInline* flow, const LineInfo& lineInfo) 167 { 168 RenderObject* parent = flow->parent(); 169 if (flow->document().inNoQuirksMode() 170 && (flow->style(lineInfo.isFirstLine())->lineHeight() != parent->style(lineInfo.isFirstLine())->lineHeight() 171 || flow->style()->verticalAlign() != parent->style()->verticalAlign() 172 || !parent->style()->font().fontMetrics().hasIdenticalAscentDescentAndLineGap(flow->style()->font().fontMetrics()))) 173 return true; 174 return false; 175 } 176 177 inline bool alwaysRequiresLineBox(RenderObject* flow) 178 { 179 // FIXME: Right now, we only allow line boxes for inlines that are truly empty. 180 // We need to fix this, though, because at the very least, inlines containing only 181 // ignorable whitespace should should also have line boxes. 182 return isEmptyInline(flow) && toRenderInline(flow)->hasInlineDirectionBordersPaddingOrMargin(); 183 } 184 185 inline bool requiresLineBox(const InlineIterator& it, const LineInfo& lineInfo = LineInfo(), WhitespacePosition whitespacePosition = LeadingWhitespace) 186 { 187 if (it.object()->isFloatingOrOutOfFlowPositioned()) 188 return false; 189 190 if (it.object()->isRenderInline() && !alwaysRequiresLineBox(it.object()) && !requiresLineBoxForContent(toRenderInline(it.object()), lineInfo)) 191 return false; 192 193 if (!shouldCollapseWhiteSpace(it.object()->style(), lineInfo, whitespacePosition) || it.object()->isBR()) 194 return true; 195 196 UChar current = it.current(); 197 bool notJustWhitespace = current != ' ' && current != '\t' && current != softHyphen && (current != '\n' || it.object()->preservesNewline()); 198 return notJustWhitespace || isEmptyInline(it.object()); 199 } 200 201 inline void setStaticPositions(RenderBlockFlow* block, RenderBox* child) 202 { 203 ASSERT(child->isOutOfFlowPositioned()); 204 // FIXME: The math here is actually not really right. It's a best-guess approximation that 205 // will work for the common cases 206 RenderObject* containerBlock = child->container(); 207 LayoutUnit blockHeight = block->logicalHeight(); 208 if (containerBlock->isRenderInline()) { 209 // A relative positioned inline encloses us. In this case, we also have to determine our 210 // position as though we were an inline. Set |staticInlinePosition| and |staticBlockPosition| on the relative positioned 211 // inline so that we can obtain the value later. 212 toRenderInline(containerBlock)->layer()->setStaticInlinePosition(block->startAlignedOffsetForLine(blockHeight, false)); 213 toRenderInline(containerBlock)->layer()->setStaticBlockPosition(blockHeight); 214 215 // If |child| is a leading or trailing positioned object this is its only opportunity to ensure it moves with an inline 216 // container changing width. 217 child->moveWithEdgeOfInlineContainerIfNecessary(child->isHorizontalWritingMode()); 218 } 219 block->updateStaticInlinePositionForChild(child, blockHeight); 220 child->layer()->setStaticBlockPosition(blockHeight); 221 } 222 223 // FIXME: The entire concept of the skipTrailingWhitespace function is flawed, since we really need to be building 224 // line boxes even for containers that may ultimately collapse away. Otherwise we'll never get positioned 225 // elements quite right. In other words, we need to build this function's work into the normal line 226 // object iteration process. 227 // NB. this function will insert any floating elements that would otherwise 228 // be skipped but it will not position them. 229 inline void BreakingContext::skipTrailingWhitespace(InlineIterator& iterator, const LineInfo& lineInfo) 230 { 231 while (!iterator.atEnd() && !requiresLineBox(iterator, lineInfo, TrailingWhitespace)) { 232 RenderObject* object = iterator.object(); 233 if (object->isOutOfFlowPositioned()) 234 setStaticPositions(m_block, toRenderBox(object)); 235 else if (object->isFloating()) 236 m_block->insertFloatingObject(toRenderBox(object)); 237 iterator.increment(); 238 } 239 } 240 241 inline void BreakingContext::initializeForCurrentObject() 242 { 243 m_currentStyle = m_current.object()->style(); 244 m_nextObject = bidiNextSkippingEmptyInlines(m_block, m_current.object()); 245 if (m_nextObject && m_nextObject->parent() && !m_nextObject->parent()->isDescendantOf(m_current.object()->parent())) 246 m_includeEndWidth = true; 247 248 m_currWS = m_current.object()->isReplaced() ? m_current.object()->parent()->style()->whiteSpace() : m_currentStyle->whiteSpace(); 249 m_lastWS = m_lastObject->isReplaced() ? m_lastObject->parent()->style()->whiteSpace() : m_lastObject->style()->whiteSpace(); 250 251 m_autoWrap = RenderStyle::autoWrap(m_currWS); 252 m_autoWrapWasEverTrueOnLine = m_autoWrapWasEverTrueOnLine || m_autoWrap; 253 254 m_preservesNewline = m_current.object()->isSVGInlineText() ? false : RenderStyle::preserveNewline(m_currWS); 255 256 m_collapseWhiteSpace = RenderStyle::collapseWhiteSpace(m_currWS); 257 } 258 259 inline void BreakingContext::increment() 260 { 261 // Clear out our character space bool, since inline <pre>s don't collapse whitespace 262 // with adjacent inline normal/nowrap spans. 263 if (!m_collapseWhiteSpace) 264 m_currentCharacterIsSpace = false; 265 266 m_current.moveToStartOf(m_nextObject); 267 m_atStart = false; 268 } 269 270 inline void BreakingContext::handleBR(EClear& clear) 271 { 272 if (m_width.fitsOnLine()) { 273 RenderObject* br = m_current.object(); 274 m_lineBreak.moveToStartOf(br); 275 m_lineBreak.increment(); 276 277 // A <br> always breaks a line, so don't let the line be collapsed 278 // away. Also, the space at the end of a line with a <br> does not 279 // get collapsed away. It only does this if the previous line broke 280 // cleanly. Otherwise the <br> has no effect on whether the line is 281 // empty or not. 282 if (m_startingNewParagraph) 283 m_lineInfo.setEmpty(false, m_block, &m_width); 284 m_trailingObjects.clear(); 285 m_lineInfo.setPreviousLineBrokeCleanly(true); 286 287 // A <br> with clearance always needs a linebox in case the lines below it get dirtied later and 288 // need to check for floats to clear - so if we're ignoring spaces, stop ignoring them and add a 289 // run for this object. 290 if (m_ignoringSpaces && m_currentStyle->clear() != CNONE) 291 m_lineMidpointState.ensureLineBoxInsideIgnoredSpaces(br); 292 293 if (!m_lineInfo.isEmpty()) 294 clear = m_currentStyle->clear(); 295 } 296 m_atEnd = true; 297 } 298 299 inline LayoutUnit borderPaddingMarginStart(RenderInline* child) 300 { 301 return child->marginStart() + child->paddingStart() + child->borderStart(); 302 } 303 304 inline LayoutUnit borderPaddingMarginEnd(RenderInline* child) 305 { 306 return child->marginEnd() + child->paddingEnd() + child->borderEnd(); 307 } 308 309 inline bool shouldAddBorderPaddingMargin(RenderObject* child, bool &checkSide) 310 { 311 if (!child || (child->isText() && !toRenderText(child)->textLength())) 312 return true; 313 checkSide = false; 314 return checkSide; 315 } 316 317 inline LayoutUnit inlineLogicalWidth(RenderObject* child, bool start = true, bool end = true) 318 { 319 unsigned lineDepth = 1; 320 LayoutUnit extraWidth = 0; 321 RenderObject* parent = child->parent(); 322 while (parent->isRenderInline() && lineDepth++ < cMaxLineDepth) { 323 RenderInline* parentAsRenderInline = toRenderInline(parent); 324 if (!isEmptyInline(parentAsRenderInline)) { 325 if (start && shouldAddBorderPaddingMargin(child->previousSibling(), start)) 326 extraWidth += borderPaddingMarginStart(parentAsRenderInline); 327 if (end && shouldAddBorderPaddingMargin(child->nextSibling(), end)) 328 extraWidth += borderPaddingMarginEnd(parentAsRenderInline); 329 if (!start && !end) 330 return extraWidth; 331 } 332 child = parent; 333 parent = child->parent(); 334 } 335 return extraWidth; 336 } 337 338 inline void BreakingContext::handleOutOfFlowPositioned(Vector<RenderBox*>& positionedObjects) 339 { 340 // If our original display wasn't an inline type, then we can 341 // go ahead and determine our static inline position now. 342 RenderBox* box = toRenderBox(m_current.object()); 343 bool isInlineType = box->style()->isOriginalDisplayInlineType(); 344 if (!isInlineType) { 345 m_block->setStaticInlinePositionForChild(box, m_block->startOffsetForContent()); 346 } else { 347 // If our original display was an INLINE type, then we can go ahead 348 // and determine our static y position now. 349 box->layer()->setStaticBlockPosition(m_block->logicalHeight()); 350 } 351 352 // If we're ignoring spaces, we have to stop and include this object and 353 // then start ignoring spaces again. 354 if (isInlineType || box->container()->isRenderInline()) { 355 if (m_ignoringSpaces) 356 m_lineMidpointState.ensureLineBoxInsideIgnoredSpaces(box); 357 m_trailingObjects.appendObjectIfNeeded(box); 358 } else { 359 positionedObjects.append(box); 360 } 361 m_width.addUncommittedWidth(inlineLogicalWidth(box).toFloat()); 362 // Reset prior line break context characters. 363 m_renderTextInfo.m_lineBreakIterator.resetPriorContext(); 364 } 365 366 inline void BreakingContext::handleFloat() 367 { 368 RenderBox* floatBox = toRenderBox(m_current.object()); 369 FloatingObject* floatingObject = m_block->insertFloatingObject(floatBox); 370 // check if it fits in the current line. 371 // If it does, position it now, otherwise, position 372 // it after moving to next line (in newLine() func) 373 // FIXME: Bug 110372: Properly position multiple stacked floats with non-rectangular shape outside. 374 if (m_floatsFitOnLine && m_width.fitsOnLine(m_block->logicalWidthForFloat(floatingObject).toFloat(), ExcludeWhitespace)) { 375 m_block->positionNewFloatOnLine(floatingObject, m_lastFloatFromPreviousLine, m_lineInfo, m_width); 376 if (m_lineBreak.object() == m_current.object()) { 377 ASSERT(!m_lineBreak.offset()); 378 m_lineBreak.increment(); 379 } 380 } else { 381 m_floatsFitOnLine = false; 382 } 383 // Update prior line break context characters, using U+FFFD (OBJECT REPLACEMENT CHARACTER) for floating element. 384 m_renderTextInfo.m_lineBreakIterator.updatePriorContext(replacementCharacter); 385 } 386 387 // This is currently just used for list markers and inline flows that have line boxes. Neither should 388 // have an effect on whitespace at the start of the line. 389 inline bool shouldSkipWhitespaceAfterStartObject(RenderBlockFlow* block, RenderObject* o, LineMidpointState& lineMidpointState) 390 { 391 RenderObject* next = bidiNextSkippingEmptyInlines(block, o); 392 while (next && next->isFloatingOrOutOfFlowPositioned()) 393 next = bidiNextSkippingEmptyInlines(block, next); 394 395 if (next && !next->isBR() && next->isText() && toRenderText(next)->textLength() > 0) { 396 RenderText* nextText = toRenderText(next); 397 UChar nextChar = nextText->characterAt(0); 398 if (nextText->style()->isCollapsibleWhiteSpace(nextChar)) { 399 lineMidpointState.startIgnoringSpaces(InlineIterator(0, o, 0)); 400 return true; 401 } 402 } 403 404 return false; 405 } 406 407 inline void BreakingContext::handleEmptyInline() 408 { 409 // This should only end up being called on empty inlines 410 ASSERT(isEmptyInline(m_current.object())); 411 412 RenderInline* flowBox = toRenderInline(m_current.object()); 413 414 bool requiresLineBox = alwaysRequiresLineBox(m_current.object()); 415 if (requiresLineBox || requiresLineBoxForContent(flowBox, m_lineInfo)) { 416 // An empty inline that only has line-height, vertical-align or font-metrics will 417 // not force linebox creation (and thus affect the height of the line) if the rest of the line is empty. 418 if (requiresLineBox) 419 m_lineInfo.setEmpty(false, m_block, &m_width); 420 if (m_ignoringSpaces) { 421 // If we are in a run of ignored spaces then ensure we get a linebox if lineboxes are eventually 422 // created for the line... 423 m_trailingObjects.clear(); 424 m_lineMidpointState.ensureLineBoxInsideIgnoredSpaces(m_current.object()); 425 } else if (m_blockStyle->collapseWhiteSpace() && m_resolver.position().object() == m_current.object() 426 && shouldSkipWhitespaceAfterStartObject(m_block, m_current.object(), m_lineMidpointState)) { 427 // If this object is at the start of the line, we need to behave like list markers and 428 // start ignoring spaces. 429 m_currentCharacterShouldCollapseIfPreWap = m_currentCharacterIsSpace = true; 430 m_ignoringSpaces = true; 431 } else { 432 // If we are after a trailing space but aren't ignoring spaces yet then ensure we get a linebox 433 // if we encounter collapsible whitepace. 434 m_trailingObjects.appendObjectIfNeeded(m_current.object()); 435 } 436 } 437 438 m_width.addUncommittedWidth((inlineLogicalWidth(m_current.object()) + borderPaddingMarginStart(flowBox) + borderPaddingMarginEnd(flowBox)).toFloat()); 439 } 440 441 inline void BreakingContext::handleReplaced() 442 { 443 RenderBox* replacedBox = toRenderBox(m_current.object()); 444 445 if (m_atStart) 446 m_width.updateAvailableWidth(replacedBox->logicalHeight()); 447 448 // Break on replaced elements if either has normal white-space. 449 if ((m_autoWrap || RenderStyle::autoWrap(m_lastWS)) && (!m_current.object()->isImage() || m_allowImagesToBreak)) { 450 m_width.commit(); 451 m_lineBreak.moveToStartOf(m_current.object()); 452 } 453 454 if (m_ignoringSpaces) 455 m_lineMidpointState.stopIgnoringSpaces(InlineIterator(0, m_current.object(), 0)); 456 457 m_lineInfo.setEmpty(false, m_block, &m_width); 458 m_ignoringSpaces = false; 459 m_currentCharacterShouldCollapseIfPreWap = m_currentCharacterIsSpace = false; 460 m_trailingObjects.clear(); 461 462 // Optimize for a common case. If we can't find whitespace after the list 463 // item, then this is all moot. 464 LayoutUnit replacedLogicalWidth = m_block->logicalWidthForChild(replacedBox) + m_block->marginStartForChild(replacedBox) + m_block->marginEndForChild(replacedBox) + inlineLogicalWidth(m_current.object()); 465 if (m_current.object()->isListMarker()) { 466 if (m_blockStyle->collapseWhiteSpace() && shouldSkipWhitespaceAfterStartObject(m_block, m_current.object(), m_lineMidpointState)) { 467 // Like with inline flows, we start ignoring spaces to make sure that any 468 // additional spaces we see will be discarded. 469 m_currentCharacterShouldCollapseIfPreWap = m_currentCharacterIsSpace = true; 470 m_ignoringSpaces = true; 471 } 472 if (toRenderListMarker(m_current.object())->isInside()) 473 m_width.addUncommittedWidth(replacedLogicalWidth.toFloat()); 474 } else { 475 m_width.addUncommittedWidth(replacedLogicalWidth.toFloat()); 476 } 477 if (m_current.object()->isRubyRun()) 478 m_width.applyOverhang(toRenderRubyRun(m_current.object()), m_lastObject, m_nextObject); 479 // Update prior line break context characters, using U+FFFD (OBJECT REPLACEMENT CHARACTER) for replaced element. 480 m_renderTextInfo.m_lineBreakIterator.updatePriorContext(replacementCharacter); 481 } 482 483 inline bool iteratorIsBeyondEndOfRenderCombineText(const InlineIterator& iter, RenderCombineText* renderer) 484 { 485 return iter.object() == renderer && iter.offset() >= renderer->textLength(); 486 } 487 488 inline void nextCharacter(UChar& currentCharacter, UChar& lastCharacter, UChar& secondToLastCharacter) 489 { 490 secondToLastCharacter = lastCharacter; 491 lastCharacter = currentCharacter; 492 } 493 494 inline float firstPositiveWidth(const WordMeasurements& wordMeasurements) 495 { 496 for (size_t i = 0; i < wordMeasurements.size(); ++i) { 497 if (wordMeasurements[i].width > 0) 498 return wordMeasurements[i].width; 499 } 500 return 0; 501 } 502 503 inline float measureHyphenWidth(RenderText* renderer, const Font& font, TextDirection textDirection) 504 { 505 RenderStyle* style = renderer->style(); 506 return font.width(constructTextRun(renderer, font, 507 style->hyphenString().string(), style, style->direction())); 508 } 509 510 ALWAYS_INLINE TextDirection textDirectionFromUnicode(WTF::Unicode::Direction direction) 511 { 512 return direction == WTF::Unicode::RightToLeft 513 || direction == WTF::Unicode::RightToLeftArabic ? RTL : LTR; 514 } 515 516 ALWAYS_INLINE float textWidth(RenderText* text, unsigned from, unsigned len, const Font& font, float xPos, bool isFixedPitch, bool collapseWhiteSpace, HashSet<const SimpleFontData*>* fallbackFonts = 0) 517 { 518 GlyphOverflow glyphOverflow; 519 if (isFixedPitch || (!from && len == text->textLength()) || text->style()->hasTextCombine()) 520 return text->width(from, len, font, xPos, text->style()->direction(), fallbackFonts, &glyphOverflow); 521 522 TextRun run = constructTextRun(text, font, text, from, len, text->style()); 523 run.setCharacterScanForCodePath(!text->canUseSimpleFontCodePath()); 524 run.setUseComplexCodePath(!text->canUseSimpleFontCodePath()); 525 run.setTabSize(!collapseWhiteSpace, text->style()->tabSize()); 526 run.setXPos(xPos); 527 return font.width(run, fallbackFonts, &glyphOverflow); 528 } 529 530 inline bool BreakingContext::handleText(WordMeasurements& wordMeasurements, bool& hyphenated) 531 { 532 if (!m_current.offset()) 533 m_appliedStartWidth = false; 534 535 RenderText* renderText = toRenderText(m_current.object()); 536 537 bool isSVGText = renderText->isSVGInlineText(); 538 539 // If we have left a no-wrap inline and entered an autowrap inline while ignoring spaces 540 // then we need to mark the start of the autowrap inline as a potential linebreak now. 541 if (m_autoWrap && !RenderStyle::autoWrap(m_lastWS) && m_ignoringSpaces) { 542 m_width.commit(); 543 m_lineBreak.moveToStartOf(m_current.object()); 544 } 545 546 if (renderText->style()->hasTextCombine() && m_current.object()->isCombineText() && !toRenderCombineText(m_current.object())->isCombined()) { 547 RenderCombineText* combineRenderer = toRenderCombineText(m_current.object()); 548 combineRenderer->combineText(); 549 // The length of the renderer's text may have changed. Increment stale iterator positions 550 if (iteratorIsBeyondEndOfRenderCombineText(m_lineBreak, combineRenderer)) { 551 ASSERT(iteratorIsBeyondEndOfRenderCombineText(m_resolver.position(), combineRenderer)); 552 m_lineBreak.increment(); 553 m_resolver.position().increment(&m_resolver); 554 } 555 } 556 557 RenderStyle* style = renderText->style(m_lineInfo.isFirstLine()); 558 const Font& font = style->font(); 559 bool isFixedPitch = font.isFixedPitch(); 560 561 unsigned lastSpace = m_current.offset(); 562 float wordSpacing = m_currentStyle->wordSpacing(); 563 float lastSpaceWordSpacing = 0; 564 float wordSpacingForWordMeasurement = 0; 565 566 float wrapW = m_width.uncommittedWidth() + inlineLogicalWidth(m_current.object(), !m_appliedStartWidth, true); 567 float charWidth = 0; 568 // Auto-wrapping text should wrap in the middle of a word only if it could not wrap before the word, 569 // which is only possible if the word is the first thing on the line, that is, if |w| is zero. 570 bool breakWords = m_currentStyle->breakWords() && ((m_autoWrap && !m_width.committedWidth()) || m_currWS == PRE); 571 bool midWordBreak = false; 572 bool breakAll = m_currentStyle->wordBreak() == BreakAllWordBreak && m_autoWrap; 573 float hyphenWidth = 0; 574 575 if (isSVGText) { 576 breakWords = false; 577 breakAll = false; 578 } 579 580 if (renderText->isWordBreak()) { 581 m_width.commit(); 582 m_lineBreak.moveToStartOf(m_current.object()); 583 ASSERT(m_current.offset() == renderText->textLength()); 584 } 585 586 if (m_renderTextInfo.m_text != renderText) { 587 m_renderTextInfo.m_text = renderText; 588 m_renderTextInfo.m_font = &font; 589 m_renderTextInfo.m_lineBreakIterator.resetStringAndReleaseIterator(renderText->text(), style->locale()); 590 } else if (m_renderTextInfo.m_font != &font) { 591 m_renderTextInfo.m_font = &font; 592 } 593 594 // Non-zero only when kerning is enabled, in which case we measure 595 // words with their trailing space, then subtract its width. 596 float wordTrailingSpaceWidth = (font.fontDescription().typesettingFeatures() & Kerning) ? 597 font.width(constructTextRun(renderText, font, &space, 1, style, style->direction())) + wordSpacing 598 : 0; 599 600 UChar lastCharacter = m_renderTextInfo.m_lineBreakIterator.lastCharacter(); 601 UChar secondToLastCharacter = m_renderTextInfo.m_lineBreakIterator.secondToLastCharacter(); 602 for (; m_current.offset() < renderText->textLength(); m_current.fastIncrementInTextNode()) { 603 bool previousCharacterIsSpace = m_currentCharacterIsSpace; 604 bool previousCharacterShouldCollapseIfPreWap = m_currentCharacterShouldCollapseIfPreWap; 605 UChar c = m_current.current(); 606 m_currentCharacterShouldCollapseIfPreWap = m_currentCharacterIsSpace = c == ' ' || c == '\t' || (!m_preservesNewline && (c == '\n')); 607 608 if (!m_collapseWhiteSpace || !m_currentCharacterIsSpace) 609 m_lineInfo.setEmpty(false, m_block, &m_width); 610 611 if (c == softHyphen && m_autoWrap && !hyphenWidth) { 612 hyphenWidth = measureHyphenWidth(renderText, font, textDirectionFromUnicode(m_resolver.position().direction())); 613 m_width.addUncommittedWidth(hyphenWidth); 614 } 615 616 bool applyWordSpacing = false; 617 618 if ((breakAll || breakWords) && !midWordBreak) { 619 wrapW += charWidth; 620 bool midWordBreakIsBeforeSurrogatePair = U16_IS_LEAD(c) && m_current.offset() + 1 < renderText->textLength() && U16_IS_TRAIL((*renderText)[m_current.offset() + 1]); 621 charWidth = textWidth(renderText, m_current.offset(), midWordBreakIsBeforeSurrogatePair ? 2 : 1, font, m_width.committedWidth() + wrapW, isFixedPitch, m_collapseWhiteSpace); 622 midWordBreak = m_width.committedWidth() + wrapW + charWidth > m_width.availableWidth(); 623 } 624 625 int nextBreakablePosition = m_current.nextBreakablePosition(); 626 bool betweenWords = c == '\n' || (m_currWS != PRE && !m_atStart && isBreakable(m_renderTextInfo.m_lineBreakIterator, m_current.offset(), nextBreakablePosition)); 627 m_current.setNextBreakablePosition(nextBreakablePosition); 628 629 if (betweenWords || midWordBreak) { 630 bool stoppedIgnoringSpaces = false; 631 if (m_ignoringSpaces) { 632 lastSpaceWordSpacing = 0; 633 if (!m_currentCharacterIsSpace) { 634 // Stop ignoring spaces and begin at this 635 // new point. 636 m_ignoringSpaces = false; 637 wordSpacingForWordMeasurement = 0; 638 lastSpace = m_current.offset(); // e.g., "Foo goo", don't add in any of the ignored spaces. 639 m_lineMidpointState.stopIgnoringSpaces(InlineIterator(0, m_current.object(), m_current.offset())); 640 stoppedIgnoringSpaces = true; 641 } else { 642 // Just keep ignoring these spaces. 643 nextCharacter(c, lastCharacter, secondToLastCharacter); 644 continue; 645 } 646 } 647 648 wordMeasurements.grow(wordMeasurements.size() + 1); 649 WordMeasurement& wordMeasurement = wordMeasurements.last(); 650 651 wordMeasurement.renderer = renderText; 652 wordMeasurement.endOffset = m_current.offset(); 653 wordMeasurement.startOffset = lastSpace; 654 655 float additionalTempWidth; 656 if (wordTrailingSpaceWidth && c == ' ') 657 additionalTempWidth = textWidth(renderText, lastSpace, m_current.offset() + 1 - lastSpace, font, m_width.currentWidth(), isFixedPitch, m_collapseWhiteSpace, &wordMeasurement.fallbackFonts) - wordTrailingSpaceWidth; 658 else 659 additionalTempWidth = textWidth(renderText, lastSpace, m_current.offset() - lastSpace, font, m_width.currentWidth(), isFixedPitch, m_collapseWhiteSpace, &wordMeasurement.fallbackFonts); 660 661 wordMeasurement.width = additionalTempWidth + wordSpacingForWordMeasurement; 662 additionalTempWidth += lastSpaceWordSpacing; 663 m_width.addUncommittedWidth(additionalTempWidth); 664 665 if (m_collapseWhiteSpace && previousCharacterIsSpace && m_currentCharacterIsSpace && additionalTempWidth) 666 m_width.setTrailingWhitespaceWidth(additionalTempWidth); 667 668 if (!m_appliedStartWidth) { 669 m_width.addUncommittedWidth(inlineLogicalWidth(m_current.object(), true, false).toFloat()); 670 m_appliedStartWidth = true; 671 } 672 673 applyWordSpacing = wordSpacing && m_currentCharacterIsSpace; 674 675 if (!m_width.committedWidth() && m_autoWrap && !m_width.fitsOnLine()) 676 m_width.fitBelowFloats(m_lineInfo.isFirstLine()); 677 678 if (m_autoWrap || breakWords) { 679 // If we break only after white-space, consider the current character 680 // as candidate width for this line. 681 bool lineWasTooWide = false; 682 if (m_width.fitsOnLine() && m_currentCharacterIsSpace && m_currentStyle->breakOnlyAfterWhiteSpace() && !midWordBreak) { 683 float charWidth = textWidth(renderText, m_current.offset(), 1, font, m_width.currentWidth(), isFixedPitch, m_collapseWhiteSpace, &wordMeasurement.fallbackFonts) + (applyWordSpacing ? wordSpacing : 0); 684 // Check if line is too big even without the extra space 685 // at the end of the line. If it is not, do nothing. 686 // If the line needs the extra whitespace to be too long, 687 // then move the line break to the space and skip all 688 // additional whitespace. 689 if (!m_width.fitsOnLine(charWidth)) { 690 lineWasTooWide = true; 691 m_lineBreak.moveTo(m_current.object(), m_current.offset(), m_current.nextBreakablePosition()); 692 skipTrailingWhitespace(m_lineBreak, m_lineInfo); 693 } 694 } 695 if (lineWasTooWide || !m_width.fitsOnLine()) { 696 if (m_lineBreak.atTextParagraphSeparator()) { 697 if (!stoppedIgnoringSpaces && m_current.offset() > 0) 698 m_lineMidpointState.ensureCharacterGetsLineBox(m_current); 699 m_lineBreak.increment(); 700 m_lineInfo.setPreviousLineBrokeCleanly(true); 701 wordMeasurement.endOffset = m_lineBreak.offset(); 702 } 703 if (m_lineBreak.object() && m_lineBreak.offset() && m_lineBreak.object()->isText() && toRenderText(m_lineBreak.object())->textLength() && toRenderText(m_lineBreak.object())->characterAt(m_lineBreak.offset() - 1) == softHyphen) 704 hyphenated = true; 705 if (m_lineBreak.offset() && m_lineBreak.offset() != (unsigned)wordMeasurement.endOffset && !wordMeasurement.width) { 706 if (charWidth) { 707 wordMeasurement.endOffset = m_lineBreak.offset(); 708 wordMeasurement.width = charWidth; 709 } 710 } 711 // Didn't fit. Jump to the end unless there's still an opportunity to collapse whitespace. 712 if (m_ignoringSpaces || !m_collapseWhiteSpace || !m_currentCharacterIsSpace || !previousCharacterIsSpace) { 713 m_atEnd = true; 714 return false; 715 } 716 } else { 717 if (!betweenWords || (midWordBreak && !m_autoWrap)) 718 m_width.addUncommittedWidth(-additionalTempWidth); 719 if (hyphenWidth) { 720 // Subtract the width of the soft hyphen out since we fit on a line. 721 m_width.addUncommittedWidth(-hyphenWidth); 722 hyphenWidth = 0; 723 } 724 } 725 } 726 727 if (c == '\n' && m_preservesNewline) { 728 if (!stoppedIgnoringSpaces && m_current.offset()) 729 m_lineMidpointState.ensureCharacterGetsLineBox(m_current); 730 m_lineBreak.moveTo(m_current.object(), m_current.offset(), m_current.nextBreakablePosition()); 731 m_lineBreak.increment(); 732 m_lineInfo.setPreviousLineBrokeCleanly(true); 733 return true; 734 } 735 736 if (m_autoWrap && betweenWords) { 737 m_width.commit(); 738 wrapW = 0; 739 m_lineBreak.moveTo(m_current.object(), m_current.offset(), m_current.nextBreakablePosition()); 740 // Auto-wrapping text should not wrap in the middle of a word once it has had an 741 // opportunity to break after a word. 742 breakWords = false; 743 } 744 745 if (midWordBreak && !U16_IS_TRAIL(c) && !(WTF::Unicode::category(c) & (WTF::Unicode::Mark_NonSpacing | WTF::Unicode::Mark_Enclosing | WTF::Unicode::Mark_SpacingCombining))) { 746 // Remember this as a breakable position in case 747 // adding the end width forces a break. 748 m_lineBreak.moveTo(m_current.object(), m_current.offset(), m_current.nextBreakablePosition()); 749 midWordBreak &= (breakWords || breakAll); 750 } 751 752 if (betweenWords) { 753 lastSpaceWordSpacing = applyWordSpacing ? wordSpacing : 0; 754 wordSpacingForWordMeasurement = (applyWordSpacing && wordMeasurement.width) ? wordSpacing : 0; 755 lastSpace = m_current.offset(); 756 } 757 758 if (!m_ignoringSpaces && m_currentStyle->collapseWhiteSpace()) { 759 // If we encounter a newline, or if we encounter a 760 // second space, we need to go ahead and break up this 761 // run and enter a mode where we start collapsing spaces. 762 if (m_currentCharacterIsSpace && previousCharacterIsSpace) { 763 m_ignoringSpaces = true; 764 765 // We just entered a mode where we are ignoring 766 // spaces. Create a midpoint to terminate the run 767 // before the second space. 768 m_lineMidpointState.startIgnoringSpaces(m_startOfIgnoredSpaces); 769 m_trailingObjects.updateMidpointsForTrailingObjects(m_lineMidpointState, InlineIterator(), TrailingObjects::DoNotCollapseFirstSpace); 770 } 771 } 772 } else if (m_ignoringSpaces) { 773 // Stop ignoring spaces and begin at this 774 // new point. 775 m_ignoringSpaces = false; 776 lastSpaceWordSpacing = applyWordSpacing ? wordSpacing : 0; 777 wordSpacingForWordMeasurement = (applyWordSpacing && wordMeasurements.last().width) ? wordSpacing : 0; 778 lastSpace = m_current.offset(); // e.g., "Foo goo", don't add in any of the ignored spaces. 779 m_lineMidpointState.stopIgnoringSpaces(InlineIterator(0, m_current.object(), m_current.offset())); 780 } 781 782 if (isSVGText && m_current.offset()) { 783 // Force creation of new InlineBoxes for each absolute positioned character (those that start new text chunks). 784 if (toRenderSVGInlineText(renderText)->characterStartsNewTextChunk(m_current.offset())) 785 m_lineMidpointState.ensureCharacterGetsLineBox(m_current); 786 } 787 788 if (m_currentCharacterIsSpace && !previousCharacterIsSpace) { 789 m_startOfIgnoredSpaces.setObject(m_current.object()); 790 m_startOfIgnoredSpaces.setOffset(m_current.offset()); 791 } 792 793 if (!m_currentCharacterIsSpace && previousCharacterShouldCollapseIfPreWap) { 794 if (m_autoWrap && m_currentStyle->breakOnlyAfterWhiteSpace()) 795 m_lineBreak.moveTo(m_current.object(), m_current.offset(), m_current.nextBreakablePosition()); 796 } 797 798 if (m_collapseWhiteSpace && m_currentCharacterIsSpace && !m_ignoringSpaces) 799 m_trailingObjects.setTrailingWhitespace(toRenderText(m_current.object())); 800 else if (!m_currentStyle->collapseWhiteSpace() || !m_currentCharacterIsSpace) 801 m_trailingObjects.clear(); 802 803 m_atStart = false; 804 nextCharacter(c, lastCharacter, secondToLastCharacter); 805 } 806 807 m_renderTextInfo.m_lineBreakIterator.setPriorContext(lastCharacter, secondToLastCharacter); 808 809 wordMeasurements.grow(wordMeasurements.size() + 1); 810 WordMeasurement& wordMeasurement = wordMeasurements.last(); 811 wordMeasurement.renderer = renderText; 812 813 // IMPORTANT: current.m_pos is > length here! 814 float additionalTempWidth = m_ignoringSpaces ? 0 : textWidth(renderText, lastSpace, m_current.offset() - lastSpace, font, m_width.currentWidth(), isFixedPitch, m_collapseWhiteSpace, &wordMeasurement.fallbackFonts); 815 wordMeasurement.startOffset = lastSpace; 816 wordMeasurement.endOffset = m_current.offset(); 817 wordMeasurement.width = m_ignoringSpaces ? 0 : additionalTempWidth + wordSpacingForWordMeasurement; 818 additionalTempWidth += lastSpaceWordSpacing; 819 820 LayoutUnit inlineLogicalTempWidth = inlineLogicalWidth(m_current.object(), !m_appliedStartWidth, m_includeEndWidth); 821 m_width.addUncommittedWidth(additionalTempWidth + inlineLogicalTempWidth); 822 823 if (m_collapseWhiteSpace && m_currentCharacterIsSpace && additionalTempWidth) 824 m_width.setTrailingWhitespaceWidth(additionalTempWidth + inlineLogicalTempWidth); 825 826 m_includeEndWidth = false; 827 828 if (!m_width.fitsOnLine()) { 829 if (!hyphenated && m_lineBreak.previousInSameNode() == softHyphen) { 830 hyphenated = true; 831 m_atEnd = true; 832 } 833 } 834 return false; 835 } 836 837 inline void BreakingContext::commitAndUpdateLineBreakIfNeeded() 838 { 839 bool checkForBreak = m_autoWrap; 840 if (m_width.committedWidth() && !m_width.fitsOnLine() && m_lineBreak.object() && m_currWS == NOWRAP) { 841 checkForBreak = true; 842 } else if (m_nextObject && m_current.object()->isText() && m_nextObject->isText() && !m_nextObject->isBR() && (m_autoWrap || m_nextObject->style()->autoWrap())) { 843 if (m_autoWrap && m_currentCharacterIsSpace) { 844 checkForBreak = true; 845 } else { 846 RenderText* nextText = toRenderText(m_nextObject); 847 if (nextText->textLength()) { 848 UChar c = nextText->characterAt(0); 849 // If the next item on the line is text, and if we did not end with 850 // a space, then the next text run continues our word (and so it needs to 851 // keep adding to the uncommitted width. Just update and continue. 852 checkForBreak = !m_currentCharacterIsSpace && (c == ' ' || c == '\t' || (c == '\n' && !m_nextObject->preservesNewline())); 853 } else if (nextText->isWordBreak()) { 854 checkForBreak = true; 855 } 856 857 if (!m_width.fitsOnLine() && !m_width.committedWidth()) 858 m_width.fitBelowFloats(m_lineInfo.isFirstLine()); 859 860 bool canPlaceOnLine = m_width.fitsOnLine() || !m_autoWrapWasEverTrueOnLine; 861 if (canPlaceOnLine && checkForBreak) { 862 m_width.commit(); 863 m_lineBreak.moveToStartOf(m_nextObject); 864 } 865 } 866 } 867 868 ASSERT_WITH_SECURITY_IMPLICATION(m_currentStyle->refCount() > 0); 869 if (checkForBreak && !m_width.fitsOnLine()) { 870 // if we have floats, try to get below them. 871 if (m_currentCharacterIsSpace && !m_ignoringSpaces && m_currentStyle->collapseWhiteSpace()) 872 m_trailingObjects.clear(); 873 874 if (m_width.committedWidth()) { 875 m_atEnd = true; 876 return; 877 } 878 879 m_width.fitBelowFloats(m_lineInfo.isFirstLine()); 880 881 // |width| may have been adjusted because we got shoved down past a float (thus 882 // giving us more room), so we need to retest, and only jump to 883 // the end label if we still don't fit on the line. -dwh 884 if (!m_width.fitsOnLine()) { 885 m_atEnd = true; 886 return; 887 } 888 } else if (m_blockStyle->autoWrap() && !m_width.fitsOnLine() && !m_width.committedWidth()) { 889 // If the container autowraps but the current child does not then we still need to ensure that it 890 // wraps and moves below any floats. 891 m_width.fitBelowFloats(m_lineInfo.isFirstLine()); 892 } 893 894 if (!m_current.object()->isFloatingOrOutOfFlowPositioned()) { 895 m_lastObject = m_current.object(); 896 if (m_lastObject->isReplaced() && m_autoWrap && (!m_lastObject->isImage() || m_allowImagesToBreak) && (!m_lastObject->isListMarker() || toRenderListMarker(m_lastObject)->isInside())) { 897 m_width.commit(); 898 m_lineBreak.moveToStartOf(m_nextObject); 899 } 900 } 901 } 902 903 inline IndentTextOrNot requiresIndent(bool isFirstLine, bool isAfterHardLineBreak, RenderStyle* style) 904 { 905 IndentTextOrNot shouldIndentText = DoNotIndentText; 906 if (isFirstLine || (isAfterHardLineBreak && style->textIndentLine()) == TextIndentEachLine) 907 shouldIndentText = IndentText; 908 909 if (style->textIndentType() == TextIndentHanging) 910 shouldIndentText = shouldIndentText == IndentText ? DoNotIndentText : IndentText; 911 912 return shouldIndentText; 913 } 914 915 } 916 917 #endif // BreakingContextInlineHeaders_h 918