2 * Copyright (C) 2000 Lars Knoll (knoll@kde.org)
3 * Copyright (C) 2003, 2004, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All right reserved.
4 * Copyright (C) 2010 Google Inc. All rights reserved.
5 * Copyright (C) 2013 Adobe Systems Incorporated.
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.
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.
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.
24 #ifndef BreakingContextInlineHeaders_h
25 #define BreakingContextInlineHeaders_h
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/TrailingObjects.h"
39 #include "core/rendering/shapes/ShapeInsideInfo.h"
40 #include "core/rendering/svg/RenderSVGInlineText.h"
44 // We don't let our line box tree for a single line get any deeper than this.
45 const unsigned cMaxLineDepth = 200;
47 class WordMeasurement {
61 HashSet<const SimpleFontData*> fallbackFonts;
64 class BreakingContext {
66 BreakingContext(InlineBidiResolver& resolver, LineInfo& inLineInfo, LineWidth& lineWidth, RenderTextInfo& inRenderTextInfo, FloatingObject* inLastFloatFromPreviousLine, bool appliedStartWidth, RenderBlockFlow* block)
67 : m_resolver(resolver)
68 , m_current(resolver.position())
69 , m_lineBreak(resolver.position())
71 , m_lastObject(m_current.object())
74 , m_blockStyle(block->style())
75 , m_lineInfo(inLineInfo)
76 , m_renderTextInfo(inRenderTextInfo)
77 , m_lastFloatFromPreviousLine(inLastFloatFromPreviousLine)
81 , m_preservesNewline(false)
83 , m_ignoringSpaces(false)
84 , m_currentCharacterIsSpace(false)
85 , m_currentCharacterShouldCollapseIfPreWap(false)
86 , m_appliedStartWidth(appliedStartWidth)
87 , m_includeEndWidth(true)
89 , m_autoWrapWasEverTrueOnLine(false)
90 , m_floatsFitOnLine(true)
91 , m_collapseWhiteSpace(false)
92 , m_startingNewParagraph(m_lineInfo.previousLineBrokeCleanly())
93 , m_allowImagesToBreak(!block->document().inQuirksMode() || !block->isTableCell() || !m_blockStyle->logicalWidth().isIntrinsicOrAuto())
95 , m_lineMidpointState(resolver.midpointState())
97 m_lineInfo.setPreviousLineBrokeCleanly(false);
100 RenderObject* currentObject() { return m_current.object(); }
101 InlineIterator lineBreak() { return m_lineBreak; }
102 bool atEnd() { return m_atEnd; }
104 void initializeForCurrentObject();
108 void handleBR(EClear&);
109 void handleOutOfFlowPositioned(Vector<RenderBox*>& positionedObjects);
111 void handleEmptyInline();
112 void handleReplaced();
113 bool handleText(WordMeasurements&, bool& hyphenated);
114 void commitAndUpdateLineBreakIfNeeded();
115 InlineIterator handleEndOfLine();
117 void clearLineBreakIfFitsOnLine()
119 if (m_width.fitsOnLine() || m_lastWS == NOWRAP)
124 void skipTrailingWhitespace(InlineIterator&, const LineInfo&);
126 InlineBidiResolver& m_resolver;
128 InlineIterator m_current;
129 InlineIterator m_lineBreak;
130 InlineIterator m_startOfIgnoredSpaces;
132 RenderBlockFlow* m_block;
133 RenderObject* m_lastObject;
134 RenderObject* m_nextObject;
136 RenderStyle* m_currentStyle;
137 RenderStyle* m_blockStyle;
139 LineInfo& m_lineInfo;
141 RenderTextInfo& m_renderTextInfo;
143 FloatingObject* m_lastFloatFromPreviousLine;
147 EWhiteSpace m_currWS;
148 EWhiteSpace m_lastWS;
150 bool m_preservesNewline;
152 bool m_ignoringSpaces;
153 bool m_currentCharacterIsSpace;
154 bool m_currentCharacterShouldCollapseIfPreWap;
155 bool m_appliedStartWidth;
156 bool m_includeEndWidth;
158 bool m_autoWrapWasEverTrueOnLine;
159 bool m_floatsFitOnLine;
160 bool m_collapseWhiteSpace;
161 bool m_startingNewParagraph;
162 bool m_allowImagesToBreak;
165 LineMidpointState& m_lineMidpointState;
167 TrailingObjects m_trailingObjects;
170 inline bool shouldCollapseWhiteSpace(const RenderStyle* style, const LineInfo& lineInfo, WhitespacePosition whitespacePosition)
173 // If a space (U+0020) at the beginning of a line has 'white-space' set to 'normal', 'nowrap', or 'pre-line', it is removed.
174 // 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.
175 // 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.
176 return style->collapseWhiteSpace()
177 || (whitespacePosition == TrailingWhitespace && style->whiteSpace() == PRE_WRAP && (!lineInfo.isEmpty() || !lineInfo.previousLineBrokeCleanly()));
180 inline bool requiresLineBoxForContent(RenderInline* flow, const LineInfo& lineInfo)
182 RenderObject* parent = flow->parent();
183 if (flow->document().inNoQuirksMode()
184 && (flow->style(lineInfo.isFirstLine())->lineHeight() != parent->style(lineInfo.isFirstLine())->lineHeight()
185 || flow->style()->verticalAlign() != parent->style()->verticalAlign()
186 || !parent->style()->font().fontMetrics().hasIdenticalAscentDescentAndLineGap(flow->style()->font().fontMetrics())))
191 inline bool alwaysRequiresLineBox(RenderObject* flow)
193 // FIXME: Right now, we only allow line boxes for inlines that are truly empty.
194 // We need to fix this, though, because at the very least, inlines containing only
195 // ignorable whitespace should should also have line boxes.
196 return isEmptyInline(flow) && toRenderInline(flow)->hasInlineDirectionBordersPaddingOrMargin();
199 inline bool requiresLineBox(const InlineIterator& it, const LineInfo& lineInfo = LineInfo(), WhitespacePosition whitespacePosition = LeadingWhitespace)
201 if (it.object()->isFloatingOrOutOfFlowPositioned())
204 if (it.object()->isRenderInline() && !alwaysRequiresLineBox(it.object()) && !requiresLineBoxForContent(toRenderInline(it.object()), lineInfo))
207 if (!shouldCollapseWhiteSpace(it.object()->style(), lineInfo, whitespacePosition) || it.object()->isBR())
210 UChar current = it.current();
211 bool notJustWhitespace = current != ' ' && current != '\t' && current != softHyphen && (current != '\n' || it.object()->preservesNewline());
212 return notJustWhitespace || isEmptyInline(it.object());
215 inline void setStaticPositions(RenderBlockFlow* block, RenderBox* child)
217 // FIXME: The math here is actually not really right. It's a best-guess approximation that
218 // will work for the common cases
219 RenderObject* containerBlock = child->container();
220 LayoutUnit blockHeight = block->logicalHeight();
221 if (containerBlock->isRenderInline()) {
222 // A relative positioned inline encloses us. In this case, we also have to determine our
223 // position as though we were an inline. Set |staticInlinePosition| and |staticBlockPosition| on the relative positioned
224 // inline so that we can obtain the value later.
225 toRenderInline(containerBlock)->layer()->setStaticInlinePosition(block->startAlignedOffsetForLine(blockHeight, false));
226 toRenderInline(containerBlock)->layer()->setStaticBlockPosition(blockHeight);
228 block->updateStaticInlinePositionForChild(child, blockHeight);
229 child->layer()->setStaticBlockPosition(blockHeight);
232 // FIXME: The entire concept of the skipTrailingWhitespace function is flawed, since we really need to be building
233 // line boxes even for containers that may ultimately collapse away. Otherwise we'll never get positioned
234 // elements quite right. In other words, we need to build this function's work into the normal line
235 // object iteration process.
236 // NB. this function will insert any floating elements that would otherwise
237 // be skipped but it will not position them.
238 inline void BreakingContext::skipTrailingWhitespace(InlineIterator& iterator, const LineInfo& lineInfo)
240 while (!iterator.atEnd() && !requiresLineBox(iterator, lineInfo, TrailingWhitespace)) {
241 RenderObject* object = iterator.object();
242 if (object->isOutOfFlowPositioned())
243 setStaticPositions(m_block, toRenderBox(object));
244 else if (object->isFloating())
245 m_block->insertFloatingObject(toRenderBox(object));
246 iterator.increment();
250 inline void BreakingContext::initializeForCurrentObject()
252 m_currentStyle = m_current.object()->style();
253 m_nextObject = bidiNextSkippingEmptyInlines(m_block, m_current.object());
254 if (m_nextObject && m_nextObject->parent() && !m_nextObject->parent()->isDescendantOf(m_current.object()->parent()))
255 m_includeEndWidth = true;
257 m_currWS = m_current.object()->isReplaced() ? m_current.object()->parent()->style()->whiteSpace() : m_currentStyle->whiteSpace();
258 m_lastWS = m_lastObject->isReplaced() ? m_lastObject->parent()->style()->whiteSpace() : m_lastObject->style()->whiteSpace();
260 m_autoWrap = RenderStyle::autoWrap(m_currWS);
261 m_autoWrapWasEverTrueOnLine = m_autoWrapWasEverTrueOnLine || m_autoWrap;
263 m_preservesNewline = m_current.object()->isSVGInlineText() ? false : RenderStyle::preserveNewline(m_currWS);
265 m_collapseWhiteSpace = RenderStyle::collapseWhiteSpace(m_currWS);
268 inline void BreakingContext::increment()
270 // Clear out our character space bool, since inline <pre>s don't collapse whitespace
271 // with adjacent inline normal/nowrap spans.
272 if (!m_collapseWhiteSpace)
273 m_currentCharacterIsSpace = false;
275 m_current.moveToStartOf(m_nextObject);
279 inline void BreakingContext::handleBR(EClear& clear)
281 if (m_width.fitsOnLine()) {
282 RenderObject* br = m_current.object();
283 m_lineBreak.moveToStartOf(br);
284 m_lineBreak.increment();
286 // A <br> always breaks a line, so don't let the line be collapsed
287 // away. Also, the space at the end of a line with a <br> does not
288 // get collapsed away. It only does this if the previous line broke
289 // cleanly. Otherwise the <br> has no effect on whether the line is
291 if (m_startingNewParagraph)
292 m_lineInfo.setEmpty(false, m_block, &m_width);
293 m_trailingObjects.clear();
294 m_lineInfo.setPreviousLineBrokeCleanly(true);
296 // A <br> with clearance always needs a linebox in case the lines below it get dirtied later and
297 // need to check for floats to clear - so if we're ignoring spaces, stop ignoring them and add a
298 // run for this object.
299 if (m_ignoringSpaces && m_currentStyle->clear() != CNONE)
300 m_lineMidpointState.ensureLineBoxInsideIgnoredSpaces(br);
302 if (!m_lineInfo.isEmpty())
303 clear = m_currentStyle->clear();
308 inline LayoutUnit borderPaddingMarginStart(RenderInline* child)
310 return child->marginStart() + child->paddingStart() + child->borderStart();
313 inline LayoutUnit borderPaddingMarginEnd(RenderInline* child)
315 return child->marginEnd() + child->paddingEnd() + child->borderEnd();
318 inline bool shouldAddBorderPaddingMargin(RenderObject* child, bool &checkSide)
320 if (!child || (child->isText() && !toRenderText(child)->textLength()))
326 inline LayoutUnit inlineLogicalWidth(RenderObject* child, bool start = true, bool end = true)
328 unsigned lineDepth = 1;
329 LayoutUnit extraWidth = 0;
330 RenderObject* parent = child->parent();
331 while (parent->isRenderInline() && lineDepth++ < cMaxLineDepth) {
332 RenderInline* parentAsRenderInline = toRenderInline(parent);
333 if (!isEmptyInline(parentAsRenderInline)) {
334 if (start && shouldAddBorderPaddingMargin(child->previousSibling(), start))
335 extraWidth += borderPaddingMarginStart(parentAsRenderInline);
336 if (end && shouldAddBorderPaddingMargin(child->nextSibling(), end))
337 extraWidth += borderPaddingMarginEnd(parentAsRenderInline);
342 parent = child->parent();
347 inline void BreakingContext::handleOutOfFlowPositioned(Vector<RenderBox*>& positionedObjects)
349 // If our original display wasn't an inline type, then we can
350 // go ahead and determine our static inline position now.
351 RenderBox* box = toRenderBox(m_current.object());
352 bool isInlineType = box->style()->isOriginalDisplayInlineType();
354 m_block->setStaticInlinePositionForChild(box, m_block->logicalHeight(), m_block->startOffsetForContent());
356 // If our original display was an INLINE type, then we can go ahead
357 // and determine our static y position now.
358 box->layer()->setStaticBlockPosition(m_block->logicalHeight());
361 // If we're ignoring spaces, we have to stop and include this object and
362 // then start ignoring spaces again.
363 if (isInlineType || box->container()->isRenderInline()) {
364 if (m_ignoringSpaces)
365 m_lineMidpointState.ensureLineBoxInsideIgnoredSpaces(box);
366 m_trailingObjects.appendBoxIfNeeded(box);
368 positionedObjects.append(box);
370 m_width.addUncommittedWidth(inlineLogicalWidth(box));
371 // Reset prior line break context characters.
372 m_renderTextInfo.m_lineBreakIterator.resetPriorContext();
375 inline void BreakingContext::handleFloat()
377 RenderBox* floatBox = toRenderBox(m_current.object());
378 FloatingObject* floatingObject = m_block->insertFloatingObject(floatBox);
379 // check if it fits in the current line.
380 // If it does, position it now, otherwise, position
381 // it after moving to next line (in newLine() func)
382 // FIXME: Bug 110372: Properly position multiple stacked floats with non-rectangular shape outside.
383 if (m_floatsFitOnLine && m_width.fitsOnLine(m_block->logicalWidthForFloat(floatingObject))) {
384 m_block->positionNewFloatOnLine(floatingObject, m_lastFloatFromPreviousLine, m_lineInfo, m_width);
385 if (m_lineBreak.object() == m_current.object()) {
386 ASSERT(!m_lineBreak.offset());
387 m_lineBreak.increment();
390 m_floatsFitOnLine = false;
392 // Update prior line break context characters, using U+FFFD (OBJECT REPLACEMENT CHARACTER) for floating element.
393 m_renderTextInfo.m_lineBreakIterator.updatePriorContext(replacementCharacter);
396 // This is currently just used for list markers and inline flows that have line boxes. Neither should
397 // have an effect on whitespace at the start of the line.
398 inline bool shouldSkipWhitespaceAfterStartObject(RenderBlockFlow* block, RenderObject* o, LineMidpointState& lineMidpointState)
400 RenderObject* next = bidiNextSkippingEmptyInlines(block, o);
401 while (next && next->isFloatingOrOutOfFlowPositioned())
402 next = bidiNextSkippingEmptyInlines(block, next);
404 if (next && !next->isBR() && next->isText() && toRenderText(next)->textLength() > 0) {
405 RenderText* nextText = toRenderText(next);
406 UChar nextChar = nextText->characterAt(0);
407 if (nextText->style()->isCollapsibleWhiteSpace(nextChar)) {
408 lineMidpointState.startIgnoringSpaces(InlineIterator(0, o, 0));
416 inline void BreakingContext::handleEmptyInline()
418 // This should only end up being called on empty inlines
419 ASSERT(isEmptyInline(m_current.object()));
421 RenderInline* flowBox = toRenderInline(m_current.object());
423 // Now that some inline flows have line boxes, if we are already ignoring spaces, we need
424 // to make sure that we stop to include this object and then start ignoring spaces again.
425 // If this object is at the start of the line, we need to behave like list markers and
426 // start ignoring spaces.
427 bool requiresLineBox = alwaysRequiresLineBox(m_current.object());
428 if (requiresLineBox || requiresLineBoxForContent(flowBox, m_lineInfo)) {
429 // An empty inline that only has line-height, vertical-align or font-metrics will only get a
430 // line box to affect the height of the line if the rest of the line is not empty.
432 m_lineInfo.setEmpty(false, m_block, &m_width);
433 if (m_ignoringSpaces) {
434 m_trailingObjects.clear();
435 m_lineMidpointState.ensureLineBoxInsideIgnoredSpaces(m_current.object());
436 } else if (m_blockStyle->collapseWhiteSpace() && m_resolver.position().object() == m_current.object()
437 && shouldSkipWhitespaceAfterStartObject(m_block, m_current.object(), m_lineMidpointState)) {
438 // Like with list markers, we start ignoring spaces to make sure that any
439 // additional spaces we see will be discarded.
440 m_currentCharacterShouldCollapseIfPreWap = m_currentCharacterIsSpace = true;
441 m_ignoringSpaces = true;
445 m_width.addUncommittedWidth(inlineLogicalWidth(m_current.object()) + borderPaddingMarginStart(flowBox) + borderPaddingMarginEnd(flowBox));
448 inline void BreakingContext::handleReplaced()
450 RenderBox* replacedBox = toRenderBox(m_current.object());
453 m_width.updateAvailableWidth(replacedBox->logicalHeight());
455 // Break on replaced elements if either has normal white-space.
456 if ((m_autoWrap || RenderStyle::autoWrap(m_lastWS)) && (!m_current.object()->isImage() || m_allowImagesToBreak)) {
458 m_lineBreak.moveToStartOf(m_current.object());
461 if (m_ignoringSpaces)
462 m_lineMidpointState.stopIgnoringSpaces(InlineIterator(0, m_current.object(), 0));
464 m_lineInfo.setEmpty(false, m_block, &m_width);
465 m_ignoringSpaces = false;
466 m_currentCharacterShouldCollapseIfPreWap = m_currentCharacterIsSpace = false;
467 m_trailingObjects.clear();
469 // Optimize for a common case. If we can't find whitespace after the list
470 // item, then this is all moot.
471 LayoutUnit replacedLogicalWidth = m_block->logicalWidthForChild(replacedBox) + m_block->marginStartForChild(replacedBox) + m_block->marginEndForChild(replacedBox) + inlineLogicalWidth(m_current.object());
472 if (m_current.object()->isListMarker()) {
473 if (m_blockStyle->collapseWhiteSpace() && shouldSkipWhitespaceAfterStartObject(m_block, m_current.object(), m_lineMidpointState)) {
474 // Like with inline flows, we start ignoring spaces to make sure that any
475 // additional spaces we see will be discarded.
476 m_currentCharacterShouldCollapseIfPreWap = m_currentCharacterIsSpace = true;
477 m_ignoringSpaces = true;
479 if (toRenderListMarker(m_current.object())->isInside())
480 m_width.addUncommittedWidth(replacedLogicalWidth);
482 m_width.addUncommittedWidth(replacedLogicalWidth);
484 if (m_current.object()->isRubyRun())
485 m_width.applyOverhang(toRenderRubyRun(m_current.object()), m_lastObject, m_nextObject);
486 // Update prior line break context characters, using U+FFFD (OBJECT REPLACEMENT CHARACTER) for replaced element.
487 m_renderTextInfo.m_lineBreakIterator.updatePriorContext(replacementCharacter);
490 inline bool iteratorIsBeyondEndOfRenderCombineText(const InlineIterator& iter, RenderCombineText* renderer)
492 return iter.object() == renderer && iter.offset() >= renderer->textLength();
495 inline void nextCharacter(UChar& currentCharacter, UChar& lastCharacter, UChar& secondToLastCharacter)
497 secondToLastCharacter = lastCharacter;
498 lastCharacter = currentCharacter;
501 inline float firstPositiveWidth(const WordMeasurements& wordMeasurements)
503 for (size_t i = 0; i < wordMeasurements.size(); ++i) {
504 if (wordMeasurements[i].width > 0)
505 return wordMeasurements[i].width;
510 inline void updateSegmentsForShapes(RenderBlockFlow* block, const FloatingObject* lastFloatFromPreviousLine, const WordMeasurements& wordMeasurements, LineWidth& width, bool isFirstLine)
512 ASSERT(lastFloatFromPreviousLine);
514 ShapeInsideInfo* shapeInsideInfo = block->layoutShapeInsideInfo();
515 if (!lastFloatFromPreviousLine->isPlaced() || !shapeInsideInfo)
518 bool isHorizontalWritingMode = block->isHorizontalWritingMode();
519 LayoutUnit logicalOffsetFromShapeContainer = block->logicalOffsetFromShapeAncestorContainer(shapeInsideInfo->owner()).height();
521 LayoutUnit lineLogicalTop = block->logicalHeight() + logicalOffsetFromShapeContainer;
522 LayoutUnit lineLogicalHeight = block->lineHeight(isFirstLine, isHorizontalWritingMode ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes);
523 LayoutUnit lineLogicalBottom = lineLogicalTop + lineLogicalHeight;
525 LayoutUnit floatLogicalTop = block->logicalTopForFloat(lastFloatFromPreviousLine);
526 LayoutUnit floatLogicalBottom = block->logicalBottomForFloat(lastFloatFromPreviousLine);
528 bool lineOverlapsWithFloat = (floatLogicalTop < lineLogicalBottom) && (lineLogicalTop < floatLogicalBottom);
529 if (!lineOverlapsWithFloat)
532 float minSegmentWidth = firstPositiveWidth(wordMeasurements);
534 LayoutUnit floatLogicalWidth = block->logicalWidthForFloat(lastFloatFromPreviousLine);
535 LayoutUnit availableLogicalWidth = block->logicalWidth() - block->logicalRightForFloat(lastFloatFromPreviousLine);
536 if (availableLogicalWidth < minSegmentWidth)
537 block->setLogicalHeight(floatLogicalBottom);
539 if (block->logicalHeight() < floatLogicalTop) {
540 shapeInsideInfo->adjustLogicalLineTop(minSegmentWidth + floatLogicalWidth);
541 block->setLogicalHeight(shapeInsideInfo->logicalLineTop() - logicalOffsetFromShapeContainer);
544 lineLogicalTop = block->logicalHeight() + logicalOffsetFromShapeContainer;
546 shapeInsideInfo->updateSegmentsForLine(lineLogicalTop, lineLogicalHeight);
547 width.updateCurrentShapeSegment();
548 width.updateAvailableWidth();
551 inline float measureHyphenWidth(RenderText* renderer, const Font& font, TextDirection textDirection)
553 RenderStyle* style = renderer->style();
554 return font.width(RenderBlockFlow::constructTextRun(renderer, font,
555 style->hyphenString().string(), style, textDirection));
558 ALWAYS_INLINE TextDirection textDirectionFromUnicode(WTF::Unicode::Direction direction)
560 return direction == WTF::Unicode::RightToLeft
561 || direction == WTF::Unicode::RightToLeftArabic ? RTL : LTR;
564 ALWAYS_INLINE float textWidth(RenderText* text, unsigned from, unsigned len, const Font& font, float xPos, bool isFixedPitch, WTF::Unicode::Direction direction, bool collapseWhiteSpace, HashSet<const SimpleFontData*>* fallbackFonts = 0)
566 TextDirection textDirection = textDirectionFromUnicode(direction);
567 GlyphOverflow glyphOverflow;
568 if (isFixedPitch || (!from && len == text->textLength()) || text->style()->hasTextCombine())
569 return text->width(from, len, font, xPos, textDirection, fallbackFonts, &glyphOverflow);
571 TextRun run = RenderBlockFlow::constructTextRun(text, font, text, from, len, text->style(), textDirection);
572 run.setCharactersLength(text->textLength() - from);
573 ASSERT(run.charactersLength() >= run.length());
575 run.setCharacterScanForCodePath(!text->canUseSimpleFontCodePath());
576 run.setTabSize(!collapseWhiteSpace, text->style()->tabSize());
578 return font.width(run, fallbackFonts, &glyphOverflow);
581 inline bool BreakingContext::handleText(WordMeasurements& wordMeasurements, bool& hyphenated)
583 if (!m_current.offset())
584 m_appliedStartWidth = false;
586 RenderText* renderText = toRenderText(m_current.object());
588 bool isSVGText = renderText->isSVGInlineText();
590 // If we have left a no-wrap inline and entered an autowrap inline while ignoring spaces
591 // then we need to mark the start of the autowrap inline as a potential linebreak now.
592 if (m_autoWrap && !RenderStyle::autoWrap(m_lastWS) && m_ignoringSpaces) {
594 m_lineBreak.moveToStartOf(m_current.object());
597 if (renderText->style()->hasTextCombine() && m_current.object()->isCombineText() && !toRenderCombineText(m_current.object())->isCombined()) {
598 RenderCombineText* combineRenderer = toRenderCombineText(m_current.object());
599 combineRenderer->combineText();
600 // The length of the renderer's text may have changed. Increment stale iterator positions
601 if (iteratorIsBeyondEndOfRenderCombineText(m_lineBreak, combineRenderer)) {
602 ASSERT(iteratorIsBeyondEndOfRenderCombineText(m_resolver.position(), combineRenderer));
603 m_lineBreak.increment();
604 m_resolver.position().increment(&m_resolver);
608 RenderStyle* style = renderText->style(m_lineInfo.isFirstLine());
609 const Font& font = style->font();
610 bool isFixedPitch = font.isFixedPitch();
612 unsigned lastSpace = m_current.offset();
613 float wordSpacing = m_currentStyle->wordSpacing();
614 float lastSpaceWordSpacing = 0;
615 float wordSpacingForWordMeasurement = 0;
617 float wrapW = m_width.uncommittedWidth() + inlineLogicalWidth(m_current.object(), !m_appliedStartWidth, true);
619 // Auto-wrapping text should wrap in the middle of a word only if it could not wrap before the word,
620 // which is only possible if the word is the first thing on the line, that is, if |w| is zero.
621 bool breakWords = m_currentStyle->breakWords() && ((m_autoWrap && !m_width.committedWidth()) || m_currWS == PRE);
622 bool midWordBreak = false;
623 bool breakAll = m_currentStyle->wordBreak() == BreakAllWordBreak && m_autoWrap;
624 float hyphenWidth = 0;
631 if (renderText->isWordBreak()) {
633 m_lineBreak.moveToStartOf(m_current.object());
634 ASSERT(m_current.offset() == renderText->textLength());
637 if (m_renderTextInfo.m_text != renderText) {
638 m_renderTextInfo.m_text = renderText;
639 m_renderTextInfo.m_font = &font;
640 m_renderTextInfo.m_lineBreakIterator.resetStringAndReleaseIterator(renderText->text(), style->locale());
641 } else if (m_renderTextInfo.m_font != &font) {
642 m_renderTextInfo.m_font = &font;
645 // Non-zero only when kerning is enabled, in which case we measure
646 // words with their trailing space, then subtract its width.
647 float wordTrailingSpaceWidth = (font.fontDescription().typesettingFeatures() & Kerning) ?
648 font.width(RenderBlockFlow::constructTextRun(
649 renderText, font, &space, 1, style,
650 textDirectionFromUnicode(m_resolver.position().direction()))) + wordSpacing
653 UChar lastCharacter = m_renderTextInfo.m_lineBreakIterator.lastCharacter();
654 UChar secondToLastCharacter = m_renderTextInfo.m_lineBreakIterator.secondToLastCharacter();
655 for (; m_current.offset() < renderText->textLength(); m_current.fastIncrementInTextNode()) {
656 bool previousCharacterIsSpace = m_currentCharacterIsSpace;
657 bool previousCharacterShouldCollapseIfPreWap = m_currentCharacterShouldCollapseIfPreWap;
658 UChar c = m_current.current();
659 m_currentCharacterShouldCollapseIfPreWap = m_currentCharacterIsSpace = c == ' ' || c == '\t' || (!m_preservesNewline && (c == '\n'));
661 if (!m_collapseWhiteSpace || !m_currentCharacterIsSpace)
662 m_lineInfo.setEmpty(false, m_block, &m_width);
664 if (c == softHyphen && m_autoWrap && !hyphenWidth) {
665 hyphenWidth = measureHyphenWidth(renderText, font, textDirectionFromUnicode(m_resolver.position().direction()));
666 m_width.addUncommittedWidth(hyphenWidth);
669 bool applyWordSpacing = false;
671 if ((breakAll || breakWords) && !midWordBreak) {
673 bool midWordBreakIsBeforeSurrogatePair = U16_IS_LEAD(c) && m_current.offset() + 1 < renderText->textLength() && U16_IS_TRAIL((*renderText)[m_current.offset() + 1]);
674 charWidth = textWidth(renderText, m_current.offset(), midWordBreakIsBeforeSurrogatePair ? 2 : 1, font, m_width.committedWidth() + wrapW, isFixedPitch, m_resolver.position().direction(), m_collapseWhiteSpace, 0);
675 midWordBreak = m_width.committedWidth() + wrapW + charWidth > m_width.availableWidth();
678 int nextBreakablePosition = m_current.nextBreakablePosition();
679 bool betweenWords = c == '\n' || (m_currWS != PRE && !m_atStart && isBreakable(m_renderTextInfo.m_lineBreakIterator, m_current.offset(), nextBreakablePosition));
680 m_current.setNextBreakablePosition(nextBreakablePosition);
682 if (betweenWords || midWordBreak) {
683 bool stoppedIgnoringSpaces = false;
684 if (m_ignoringSpaces) {
685 lastSpaceWordSpacing = 0;
686 if (!m_currentCharacterIsSpace) {
687 // Stop ignoring spaces and begin at this
689 m_ignoringSpaces = false;
690 wordSpacingForWordMeasurement = 0;
691 lastSpace = m_current.offset(); // e.g., "Foo goo", don't add in any of the ignored spaces.
692 m_lineMidpointState.stopIgnoringSpaces(InlineIterator(0, m_current.object(), m_current.offset()));
693 stoppedIgnoringSpaces = true;
695 // Just keep ignoring these spaces.
696 nextCharacter(c, lastCharacter, secondToLastCharacter);
701 wordMeasurements.grow(wordMeasurements.size() + 1);
702 WordMeasurement& wordMeasurement = wordMeasurements.last();
704 wordMeasurement.renderer = renderText;
705 wordMeasurement.endOffset = m_current.offset();
706 wordMeasurement.startOffset = lastSpace;
708 float additionalTmpW;
709 if (wordTrailingSpaceWidth && c == ' ')
710 additionalTmpW = textWidth(renderText, lastSpace, m_current.offset() + 1 - lastSpace, font, m_width.currentWidth(), isFixedPitch, m_resolver.position().direction(), m_collapseWhiteSpace, &wordMeasurement.fallbackFonts) - wordTrailingSpaceWidth;
712 additionalTmpW = textWidth(renderText, lastSpace, m_current.offset() - lastSpace, font, m_width.currentWidth(), isFixedPitch, m_resolver.position().direction(), m_collapseWhiteSpace, &wordMeasurement.fallbackFonts);
714 wordMeasurement.width = additionalTmpW + wordSpacingForWordMeasurement;
715 additionalTmpW += lastSpaceWordSpacing;
716 m_width.addUncommittedWidth(additionalTmpW);
717 if (!m_appliedStartWidth) {
718 m_width.addUncommittedWidth(inlineLogicalWidth(m_current.object(), true, false));
719 m_appliedStartWidth = true;
722 if (m_lastFloatFromPreviousLine)
723 updateSegmentsForShapes(m_block, m_lastFloatFromPreviousLine, wordMeasurements, m_width, m_lineInfo.isFirstLine());
725 applyWordSpacing = wordSpacing && m_currentCharacterIsSpace;
727 if (!m_width.committedWidth() && m_autoWrap && !m_width.fitsOnLine())
728 m_width.fitBelowFloats();
730 if (m_autoWrap || breakWords) {
731 // If we break only after white-space, consider the current character
732 // as candidate width for this line.
733 bool lineWasTooWide = false;
734 if (m_width.fitsOnLine() && m_currentCharacterIsSpace && m_currentStyle->breakOnlyAfterWhiteSpace() && !midWordBreak) {
735 float charWidth = textWidth(renderText, m_current.offset(), 1, font, m_width.currentWidth(), isFixedPitch, m_resolver.position().direction(), m_collapseWhiteSpace, &wordMeasurement.fallbackFonts) + (applyWordSpacing ? wordSpacing : 0);
736 // Check if line is too big even without the extra space
737 // at the end of the line. If it is not, do nothing.
738 // If the line needs the extra whitespace to be too long,
739 // then move the line break to the space and skip all
740 // additional whitespace.
741 if (!m_width.fitsOnLine(charWidth)) {
742 lineWasTooWide = true;
743 m_lineBreak.moveTo(m_current.object(), m_current.offset(), m_current.nextBreakablePosition());
744 skipTrailingWhitespace(m_lineBreak, m_lineInfo);
747 if (lineWasTooWide || !m_width.fitsOnLine()) {
748 if (m_lineBreak.atTextParagraphSeparator()) {
749 if (!stoppedIgnoringSpaces && m_current.offset() > 0)
750 m_lineMidpointState.ensureCharacterGetsLineBox(m_current);
751 m_lineBreak.increment();
752 m_lineInfo.setPreviousLineBrokeCleanly(true);
753 wordMeasurement.endOffset = m_lineBreak.offset();
755 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)
757 if (m_lineBreak.offset() && m_lineBreak.offset() != (unsigned)wordMeasurement.endOffset && !wordMeasurement.width) {
759 wordMeasurement.endOffset = m_lineBreak.offset();
760 wordMeasurement.width = charWidth;
763 // Didn't fit. Jump to the end unless there's still an opportunity to collapse whitespace.
764 if (m_ignoringSpaces || !m_collapseWhiteSpace || !m_currentCharacterIsSpace || !previousCharacterIsSpace) {
769 if (!betweenWords || (midWordBreak && !m_autoWrap))
770 m_width.addUncommittedWidth(-additionalTmpW);
772 // Subtract the width of the soft hyphen out since we fit on a line.
773 m_width.addUncommittedWidth(-hyphenWidth);
779 if (c == '\n' && m_preservesNewline) {
780 if (!stoppedIgnoringSpaces && m_current.offset())
781 m_lineMidpointState.ensureCharacterGetsLineBox(m_current);
782 m_lineBreak.moveTo(m_current.object(), m_current.offset(), m_current.nextBreakablePosition());
783 m_lineBreak.increment();
784 m_lineInfo.setPreviousLineBrokeCleanly(true);
788 if (m_autoWrap && betweenWords) {
791 m_lineBreak.moveTo(m_current.object(), m_current.offset(), m_current.nextBreakablePosition());
792 // Auto-wrapping text should not wrap in the middle of a word once it has had an
793 // opportunity to break after a word.
797 if (midWordBreak && !U16_IS_TRAIL(c) && !(WTF::Unicode::category(c) & (WTF::Unicode::Mark_NonSpacing | WTF::Unicode::Mark_Enclosing | WTF::Unicode::Mark_SpacingCombining))) {
798 // Remember this as a breakable position in case
799 // adding the end width forces a break.
800 m_lineBreak.moveTo(m_current.object(), m_current.offset(), m_current.nextBreakablePosition());
801 midWordBreak &= (breakWords || breakAll);
805 lastSpaceWordSpacing = applyWordSpacing ? wordSpacing : 0;
806 wordSpacingForWordMeasurement = (applyWordSpacing && wordMeasurement.width) ? wordSpacing : 0;
807 lastSpace = m_current.offset();
810 if (!m_ignoringSpaces && m_currentStyle->collapseWhiteSpace()) {
811 // If we encounter a newline, or if we encounter a
812 // second space, we need to go ahead and break up this
813 // run and enter a mode where we start collapsing spaces.
814 if (m_currentCharacterIsSpace && previousCharacterIsSpace) {
815 m_ignoringSpaces = true;
817 // We just entered a mode where we are ignoring
818 // spaces. Create a midpoint to terminate the run
819 // before the second space.
820 m_lineMidpointState.startIgnoringSpaces(m_startOfIgnoredSpaces);
821 m_trailingObjects.updateMidpointsForTrailingBoxes(m_lineMidpointState, InlineIterator(), TrailingObjects::DoNotCollapseFirstSpace);
824 } else if (m_ignoringSpaces) {
825 // Stop ignoring spaces and begin at this
827 m_ignoringSpaces = false;
828 lastSpaceWordSpacing = applyWordSpacing ? wordSpacing : 0;
829 wordSpacingForWordMeasurement = (applyWordSpacing && wordMeasurements.last().width) ? wordSpacing : 0;
830 lastSpace = m_current.offset(); // e.g., "Foo goo", don't add in any of the ignored spaces.
831 m_lineMidpointState.stopIgnoringSpaces(InlineIterator(0, m_current.object(), m_current.offset()));
834 if (isSVGText && m_current.offset()) {
835 // Force creation of new InlineBoxes for each absolute positioned character (those that start new text chunks).
836 if (toRenderSVGInlineText(renderText)->characterStartsNewTextChunk(m_current.offset()))
837 m_lineMidpointState.ensureCharacterGetsLineBox(m_current);
840 if (m_currentCharacterIsSpace && !previousCharacterIsSpace) {
841 m_startOfIgnoredSpaces.setObject(m_current.object());
842 m_startOfIgnoredSpaces.setOffset(m_current.offset());
845 if (!m_currentCharacterIsSpace && previousCharacterShouldCollapseIfPreWap) {
846 if (m_autoWrap && m_currentStyle->breakOnlyAfterWhiteSpace())
847 m_lineBreak.moveTo(m_current.object(), m_current.offset(), m_current.nextBreakablePosition());
850 if (m_collapseWhiteSpace && m_currentCharacterIsSpace && !m_ignoringSpaces)
851 m_trailingObjects.setTrailingWhitespace(toRenderText(m_current.object()));
852 else if (!m_currentStyle->collapseWhiteSpace() || !m_currentCharacterIsSpace)
853 m_trailingObjects.clear();
856 nextCharacter(c, lastCharacter, secondToLastCharacter);
859 m_renderTextInfo.m_lineBreakIterator.setPriorContext(lastCharacter, secondToLastCharacter);
861 wordMeasurements.grow(wordMeasurements.size() + 1);
862 WordMeasurement& wordMeasurement = wordMeasurements.last();
863 wordMeasurement.renderer = renderText;
865 // IMPORTANT: current.m_pos is > length here!
866 float additionalTmpW = m_ignoringSpaces ? 0 : textWidth(renderText, lastSpace, m_current.offset() - lastSpace, font, m_width.currentWidth(), isFixedPitch, m_resolver.position().direction(), m_collapseWhiteSpace, &wordMeasurement.fallbackFonts);
867 wordMeasurement.startOffset = lastSpace;
868 wordMeasurement.endOffset = m_current.offset();
869 wordMeasurement.width = m_ignoringSpaces ? 0 : additionalTmpW + wordSpacingForWordMeasurement;
870 additionalTmpW += lastSpaceWordSpacing;
871 m_width.addUncommittedWidth(additionalTmpW + inlineLogicalWidth(m_current.object(), !m_appliedStartWidth, m_includeEndWidth));
872 m_includeEndWidth = false;
874 if (!m_width.fitsOnLine()) {
875 if (!hyphenated && m_lineBreak.previousInSameNode() == softHyphen) {
883 inline void BreakingContext::commitAndUpdateLineBreakIfNeeded()
885 bool checkForBreak = m_autoWrap;
886 if (m_width.committedWidth() && !m_width.fitsOnLine() && m_lineBreak.object() && m_currWS == NOWRAP) {
887 checkForBreak = true;
888 } else if (m_nextObject && m_current.object()->isText() && m_nextObject->isText() && !m_nextObject->isBR() && (m_autoWrap || m_nextObject->style()->autoWrap())) {
889 if (m_autoWrap && m_currentCharacterIsSpace) {
890 checkForBreak = true;
892 RenderText* nextText = toRenderText(m_nextObject);
893 if (nextText->textLength()) {
894 UChar c = nextText->characterAt(0);
895 // If the next item on the line is text, and if we did not end with
896 // a space, then the next text run continues our word (and so it needs to
897 // keep adding to the uncommitted width. Just update and continue.
898 checkForBreak = !m_currentCharacterIsSpace && (c == ' ' || c == '\t' || (c == '\n' && !m_nextObject->preservesNewline()));
899 } else if (nextText->isWordBreak()) {
900 checkForBreak = true;
903 if (!m_width.fitsOnLine() && !m_width.committedWidth())
904 m_width.fitBelowFloats();
906 bool canPlaceOnLine = m_width.fitsOnLine() || !m_autoWrapWasEverTrueOnLine;
907 if (canPlaceOnLine && checkForBreak) {
909 m_lineBreak.moveToStartOf(m_nextObject);
914 if (checkForBreak && !m_width.fitsOnLine()) {
915 // if we have floats, try to get below them.
916 if (m_currentCharacterIsSpace && !m_ignoringSpaces && m_currentStyle->collapseWhiteSpace())
917 m_trailingObjects.clear();
919 if (m_width.committedWidth()) {
924 m_width.fitBelowFloats();
926 // |width| may have been adjusted because we got shoved down past a float (thus
927 // giving us more room), so we need to retest, and only jump to
928 // the end label if we still don't fit on the line. -dwh
929 if (!m_width.fitsOnLine()) {
933 } else if (m_blockStyle->autoWrap() && !m_width.fitsOnLine() && !m_width.committedWidth()) {
934 // If the container autowraps but the current child does not then we still need to ensure that it
935 // wraps and moves below any floats.
936 m_width.fitBelowFloats();
939 if (!m_current.object()->isFloatingOrOutOfFlowPositioned()) {
940 m_lastObject = m_current.object();
941 if (m_lastObject->isReplaced() && m_autoWrap && (!m_lastObject->isImage() || m_allowImagesToBreak) && (!m_lastObject->isListMarker() || toRenderListMarker(m_lastObject)->isInside())) {
943 m_lineBreak.moveToStartOf(m_nextObject);
948 inline IndentTextOrNot requiresIndent(bool isFirstLine, bool isAfterHardLineBreak, RenderStyle* style)
952 if (isAfterHardLineBreak && style->textIndentLine() == TextIndentEachLine)
955 return DoNotIndentText;
960 #endif // BreakingContextInlineHeaders_h