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