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 ChangSeok Oh <shivamidow@gmail.com>
6 * Copyright (C) 2013 Adobe Systems Inc. All right reserved.
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
18 * You should have received a copy of the GNU Library General Public License
19 * along with this library; see the file COPYING.LIB. If not, write to
20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
25 #ifndef BreakingContextInlineHeaders_h
26 #define BreakingContextInlineHeaders_h
28 #include "Hyphenation.h"
29 #include "LineBreaker.h"
31 #include "LineWidth.h"
32 #include "RenderCombineText.h"
33 #include "RenderCounter.h"
34 #include "RenderInline.h"
35 #include "RenderListMarker.h"
36 #include "RenderRubyRun.h"
37 #include "TrailingObjects.h"
38 #include "break_lines.h"
39 #include <wtf/unicode/CharacterNames.h>
42 #include "RenderSVGInlineText.h"
45 #if ENABLE(CSS_SHAPES)
46 #include "ShapeInsideInfo.h"
51 // We don't let our line box tree for a single line get any deeper than this.
52 const unsigned cMaxLineDepth = 200;
54 struct WordMeasurement {
67 HashSet<const SimpleFontData*> fallbackFonts;
70 class BreakingContext {
72 BreakingContext(LineBreaker& lineBreaker, InlineBidiResolver& resolver, LineInfo& inLineInfo, LineWidth& lineWidth, RenderTextInfo& inRenderTextInfo, FloatingObject* inLastFloatFromPreviousLine, bool appliedStartWidth, RenderBlockFlow& block)
73 : m_lineBreaker(lineBreaker)
74 , m_resolver(resolver)
75 , m_current(resolver.position())
76 , m_lineBreak(resolver.position())
78 , m_lastObject(m_current.renderer())
81 , m_blockStyle(block.style())
82 , m_lineInfo(inLineInfo)
83 , m_renderTextInfo(inRenderTextInfo)
84 , m_lastFloatFromPreviousLine(inLastFloatFromPreviousLine)
88 , m_preservesNewline(false)
90 , m_ignoringSpaces(false)
91 , m_currentCharacterIsSpace(false)
92 , m_currentCharacterIsWS(false)
93 , m_appliedStartWidth(appliedStartWidth)
94 , m_includeEndWidth(true)
96 , m_autoWrapWasEverTrueOnLine(false)
97 , m_floatsFitOnLine(true)
98 , m_collapseWhiteSpace(false)
99 , m_startingNewParagraph(m_lineInfo.previousLineBrokeCleanly())
100 , m_allowImagesToBreak(!block.document().inQuirksMode() || !block.isTableCell() || !m_blockStyle.logicalWidth().isIntrinsicOrAuto())
102 , m_hadUncommittedWidthBeforeCurrent(false)
103 , m_lineMidpointState(resolver.midpointState())
105 m_lineInfo.setPreviousLineBrokeCleanly(false);
108 RenderObject* currentObject() { return m_current.renderer(); }
109 InlineIterator lineBreak() { return m_lineBreak; }
110 InlineIterator& lineBreakRef() {return m_lineBreak; }
111 LineWidth& lineWidth() { return m_width; }
112 bool atEnd() { return m_atEnd; }
114 void initializeForCurrentObject();
118 void handleBR(EClear&);
119 void handleOutOfFlowPositioned(Vector<RenderBox*>& positionedObjects);
121 void handleEmptyInline();
122 void handleReplaced();
123 bool handleText(WordMeasurements&, bool& hyphenated, unsigned& consecutiveHyphenatedLines);
124 bool canBreakAtThisPosition();
125 void commitAndUpdateLineBreakIfNeeded();
126 InlineIterator handleEndOfLine();
128 void clearLineBreakIfFitsOnLine(bool ignoringTrailingSpace = false)
130 if (m_width.fitsOnLine(ignoringTrailingSpace) || m_lastWS == NOWRAP)
134 void commitLineBreakAtCurrentWidth(RenderObject* object, unsigned offset = 0, int nextBreak = -1)
137 m_lineBreak.moveTo(object, offset, nextBreak);
141 LineBreaker& m_lineBreaker;
142 InlineBidiResolver& m_resolver;
144 InlineIterator m_current;
145 InlineIterator m_lineBreak;
146 InlineIterator m_startOfIgnoredSpaces;
148 RenderBlockFlow& m_block;
149 RenderObject* m_lastObject;
150 RenderObject* m_nextObject;
152 RenderStyle* m_currentStyle;
154 // Firefox and Opera will allow a table cell to grow to fit an image inside it under
155 // very specific circumstances (in order to match common WinIE renderings).
156 // Not supporting the quirk has caused us to mis-render some real sites. (See Bugzilla 10517.)
157 RenderStyle& m_blockStyle;
159 LineInfo& m_lineInfo;
161 RenderTextInfo& m_renderTextInfo;
163 FloatingObject* m_lastFloatFromPreviousLine;
167 EWhiteSpace m_currWS;
168 EWhiteSpace m_lastWS;
170 bool m_preservesNewline;
173 // This variable is used only if whitespace isn't set to PRE, and it tells us whether
174 // or not we are currently ignoring whitespace.
175 bool m_ignoringSpaces;
177 // This variable tracks whether the very last character we saw was a space. We use
178 // this to detect when we encounter a second space so we know we have to terminate
180 bool m_currentCharacterIsSpace;
181 bool m_currentCharacterIsWS;
182 bool m_appliedStartWidth;
183 bool m_includeEndWidth;
185 bool m_autoWrapWasEverTrueOnLine;
186 bool m_floatsFitOnLine;
187 bool m_collapseWhiteSpace;
188 bool m_startingNewParagraph;
189 bool m_allowImagesToBreak;
191 bool m_hadUncommittedWidthBeforeCurrent;
193 LineMidpointState& m_lineMidpointState;
195 TrailingObjects m_trailingObjects;
198 inline void BreakingContext::initializeForCurrentObject()
200 m_hadUncommittedWidthBeforeCurrent = !!m_width.uncommittedWidth();
202 m_currentStyle = &m_current.renderer()->style();
204 ASSERT(m_currentStyle);
206 m_nextObject = bidiNextSkippingEmptyInlines(m_block, m_current.renderer());
207 if (m_nextObject && m_nextObject->parent() && !m_nextObject->parent()->isDescendantOf(m_current.renderer()->parent()))
208 m_includeEndWidth = true;
210 m_currWS = m_current.renderer()->isReplaced() ? m_current.renderer()->parent()->style().whiteSpace() : m_currentStyle->whiteSpace();
211 m_lastWS = m_lastObject->isReplaced() ? m_lastObject->parent()->style().whiteSpace() : m_lastObject->style().whiteSpace();
213 m_autoWrap = RenderStyle::autoWrap(m_currWS);
214 m_autoWrapWasEverTrueOnLine = m_autoWrapWasEverTrueOnLine || m_autoWrap;
217 m_preservesNewline = m_current.renderer()->isSVGInlineText() ? false : RenderStyle::preserveNewline(m_currWS);
219 m_preservesNewline = RenderStyle::preserveNewline(m_currWS);
222 m_collapseWhiteSpace = RenderStyle::collapseWhiteSpace(m_currWS);
225 inline void BreakingContext::increment()
227 // Clear out our character space bool, since inline <pre>s don't collapse whitespace
228 // with adjacent inline normal/nowrap spans.
229 if (!m_collapseWhiteSpace)
230 m_currentCharacterIsSpace = false;
232 m_current.moveToStartOf(m_nextObject);
236 inline void BreakingContext::handleBR(EClear& clear)
238 if (m_width.fitsOnLine()) {
239 RenderObject* br = m_current.renderer();
240 m_lineBreak.moveToStartOf(br);
241 m_lineBreak.increment();
243 // A <br> always breaks a line, so don't let the line be collapsed
244 // away. Also, the space at the end of a line with a <br> does not
245 // get collapsed away. It only does this if the previous line broke
246 // cleanly. Otherwise the <br> has no effect on whether the line is
248 if (m_startingNewParagraph)
249 m_lineInfo.setEmpty(false, &m_block, &m_width);
250 m_trailingObjects.clear();
251 m_lineInfo.setPreviousLineBrokeCleanly(true);
253 // A <br> with clearance always needs a linebox in case the lines below it get dirtied later and
254 // need to check for floats to clear - so if we're ignoring spaces, stop ignoring them and add a
255 // run for this object.
256 if (m_ignoringSpaces && m_currentStyle->clear() != CNONE)
257 m_lineMidpointState.ensureLineBoxInsideIgnoredSpaces(br);
258 // If we were preceded by collapsing space and are in a right-aligned container we need to ensure the space gets
259 // collapsed away so that it doesn't push the text out from the container's right-hand edge.
260 // FIXME: Do this regardless of the container's alignment - will require rebaselining a lot of test results.
261 else if (m_ignoringSpaces && (m_blockStyle.textAlign() == RIGHT || m_blockStyle.textAlign() == WEBKIT_RIGHT))
262 m_lineMidpointState.stopIgnoringSpaces(InlineIterator(0, m_current.renderer(), m_current.offset()));
264 if (!m_lineInfo.isEmpty())
265 clear = m_currentStyle->clear();
270 inline LayoutUnit borderPaddingMarginStart(const RenderInline& child)
272 return child.marginStart() + child.paddingStart() + child.borderStart();
275 inline LayoutUnit borderPaddingMarginEnd(const RenderInline& child)
277 return child.marginEnd() + child.paddingEnd() + child.borderEnd();
280 inline bool shouldAddBorderPaddingMargin(RenderObject* child)
282 // When deciding whether we're at the edge of an inline, adjacent collapsed whitespace is the same as no sibling at all.
283 return !child || (child->isText() && !toRenderText(child)->textLength());
286 inline RenderObject* previousInFlowSibling(RenderObject* child)
288 child = child->previousSibling();
289 while (child && child->isOutOfFlowPositioned())
290 child = child->previousSibling();
294 inline LayoutUnit inlineLogicalWidth(RenderObject* child, bool checkStartEdge = true, bool checkEndEdge = true)
296 unsigned lineDepth = 1;
297 LayoutUnit extraWidth = 0;
298 RenderElement* parent = child->parent();
299 while (parent->isRenderInline() && lineDepth++ < cMaxLineDepth) {
300 const RenderInline& parentAsRenderInline = toRenderInline(*parent);
301 if (!isEmptyInline(parentAsRenderInline)) {
302 checkStartEdge = checkStartEdge && shouldAddBorderPaddingMargin(previousInFlowSibling(child));
304 extraWidth += borderPaddingMarginStart(parentAsRenderInline);
305 checkEndEdge = checkEndEdge && shouldAddBorderPaddingMargin(child->nextSibling());
307 extraWidth += borderPaddingMarginEnd(parentAsRenderInline);
308 if (!checkStartEdge && !checkEndEdge)
312 parent = child->parent();
317 inline void BreakingContext::handleOutOfFlowPositioned(Vector<RenderBox*>& positionedObjects)
319 // If our original display wasn't an inline type, then we can
320 // go ahead and determine our static inline position now.
321 RenderBox* box = toRenderBox(m_current.renderer());
322 bool isInlineType = box->style().isOriginalDisplayInlineType();
324 m_block.setStaticInlinePositionForChild(*box, m_block.logicalHeight(), m_block.startOffsetForContent(m_block.logicalHeight()));
326 // If our original display was an INLINE type, then we can go ahead
327 // and determine our static y position now.
328 box->layer()->setStaticBlockPosition(m_block.logicalHeight());
331 // If we're ignoring spaces, we have to stop and include this object and
332 // then start ignoring spaces again.
333 if (isInlineType || box->container()->isRenderInline()) {
334 if (m_ignoringSpaces)
335 m_lineMidpointState.ensureLineBoxInsideIgnoredSpaces(box);
336 m_trailingObjects.appendBoxIfNeeded(box);
338 positionedObjects.append(box);
340 m_width.addUncommittedWidth(inlineLogicalWidth(box));
341 // Reset prior line break context characters.
342 m_renderTextInfo.m_lineBreakIterator.resetPriorContext();
345 inline void BreakingContext::handleFloat()
347 RenderBox& floatBox = toRenderBox(*m_current.renderer());
348 FloatingObject* floatingObject = m_lineBreaker.insertFloatingObject(floatBox);
349 // check if it fits in the current line.
350 // If it does, position it now, otherwise, position
351 // it after moving to next line (in newLine() func)
352 // FIXME: Bug 110372: Properly position multiple stacked floats with non-rectangular shape outside.
353 if (m_floatsFitOnLine && m_width.fitsOnLineExcludingTrailingWhitespace(m_block.logicalWidthForFloat(floatingObject))) {
354 m_lineBreaker.positionNewFloatOnLine(floatingObject, m_lastFloatFromPreviousLine, m_lineInfo, m_width);
355 if (m_lineBreak.renderer() == m_current.renderer()) {
356 ASSERT(!m_lineBreak.offset());
357 m_lineBreak.increment();
360 m_floatsFitOnLine = false;
361 // Update prior line break context characters, using U+FFFD (OBJECT REPLACEMENT CHARACTER) for floating element.
362 m_renderTextInfo.m_lineBreakIterator.updatePriorContext(replacementCharacter);
365 // This is currently just used for list markers and inline flows that have line boxes. Neither should
366 // have an effect on whitespace at the start of the line.
367 inline bool shouldSkipWhitespaceAfterStartObject(RenderBlockFlow& block, RenderObject* o, LineMidpointState& lineMidpointState)
369 RenderObject* next = bidiNextSkippingEmptyInlines(block, o);
370 while (next && next->isFloatingOrOutOfFlowPositioned())
371 next = bidiNextSkippingEmptyInlines(block, next);
373 if (next && next->isText() && toRenderText(next)->textLength() > 0) {
374 RenderText* nextText = toRenderText(next);
375 UChar nextChar = nextText->characterAt(0);
376 if (nextText->style().isCollapsibleWhiteSpace(nextChar)) {
377 lineMidpointState.startIgnoringSpaces(InlineIterator(0, o, 0));
385 inline void BreakingContext::handleEmptyInline()
387 RenderInline& flowBox = toRenderInline(*m_current.renderer());
389 // This should only end up being called on empty inlines
390 ASSERT(isEmptyInline(flowBox));
392 // Now that some inline flows have line boxes, if we are already ignoring spaces, we need
393 // to make sure that we stop to include this object and then start ignoring spaces again.
394 // If this object is at the start of the line, we need to behave like list markers and
395 // start ignoring spaces.
396 bool requiresLineBox = alwaysRequiresLineBox(flowBox);
397 if (requiresLineBox || requiresLineBoxForContent(flowBox, m_lineInfo)) {
398 // An empty inline that only has line-height, vertical-align or font-metrics will only get a
399 // line box to affect the height of the line if the rest of the line is not empty.
401 m_lineInfo.setEmpty(false, &m_block, &m_width);
402 if (m_ignoringSpaces) {
403 m_trailingObjects.clear();
404 m_lineMidpointState.ensureLineBoxInsideIgnoredSpaces(m_current.renderer());
405 } else if (m_blockStyle.collapseWhiteSpace() && m_resolver.position().renderer() == m_current.renderer()
406 && shouldSkipWhitespaceAfterStartObject(m_block, m_current.renderer(), m_lineMidpointState)) {
407 // Like with list markers, we start ignoring spaces to make sure that any
408 // additional spaces we see will be discarded.
409 m_currentCharacterIsSpace = true;
410 m_currentCharacterIsWS = true;
411 m_ignoringSpaces = true;
413 m_trailingObjects.appendBoxIfNeeded(&flowBox);
416 m_width.addUncommittedWidth(inlineLogicalWidth(m_current.renderer()) + borderPaddingMarginStart(flowBox) + borderPaddingMarginEnd(flowBox));
419 inline void BreakingContext::handleReplaced()
421 RenderBox& replacedBox = toRenderBox(*m_current.renderer());
424 m_width.updateAvailableWidth(replacedBox.logicalHeight());
426 // Break on replaced elements if either has normal white-space.
427 if ((m_autoWrap || RenderStyle::autoWrap(m_lastWS)) && (!m_current.renderer()->isImage() || m_allowImagesToBreak)) {
429 m_lineBreak.moveToStartOf(m_current.renderer());
432 if (m_ignoringSpaces)
433 m_lineMidpointState.stopIgnoringSpaces(InlineIterator(0, m_current.renderer(), 0));
435 m_lineInfo.setEmpty(false, &m_block, &m_width);
436 m_ignoringSpaces = false;
437 m_currentCharacterIsSpace = false;
438 m_currentCharacterIsWS = false;
439 m_trailingObjects.clear();
441 // Optimize for a common case. If we can't find whitespace after the list
442 // item, then this is all moot.
443 LayoutUnit replacedLogicalWidth = m_block.logicalWidthForChild(replacedBox) + m_block.marginStartForChild(replacedBox) + m_block.marginEndForChild(replacedBox) + inlineLogicalWidth(m_current.renderer());
444 if (m_current.renderer()->isListMarker()) {
445 if (m_blockStyle.collapseWhiteSpace() && shouldSkipWhitespaceAfterStartObject(m_block, m_current.renderer(), m_lineMidpointState)) {
446 // Like with inline flows, we start ignoring spaces to make sure that any
447 // additional spaces we see will be discarded.
448 m_currentCharacterIsSpace = true;
449 m_currentCharacterIsWS = false;
450 m_ignoringSpaces = true;
452 if (toRenderListMarker(*m_current.renderer()).isInside())
453 m_width.addUncommittedWidth(replacedLogicalWidth);
455 m_width.addUncommittedWidth(replacedLogicalWidth);
456 if (m_current.renderer()->isRubyRun())
457 m_width.applyOverhang(toRenderRubyRun(m_current.renderer()), m_lastObject, m_nextObject);
458 // Update prior line break context characters, using U+FFFD (OBJECT REPLACEMENT CHARACTER) for replaced element.
459 m_renderTextInfo.m_lineBreakIterator.updatePriorContext(replacementCharacter);
462 inline float firstPositiveWidth(const WordMeasurements& wordMeasurements)
464 for (size_t i = 0; i < wordMeasurements.size(); ++i) {
465 if (wordMeasurements[i].width > 0)
466 return wordMeasurements[i].width;
471 #if ENABLE(CSS_SHAPES)
472 inline void updateSegmentsForShapes(RenderBlockFlow& block, const FloatingObject* lastFloatFromPreviousLine, const WordMeasurements& wordMeasurements, LineWidth& width, bool isFirstLine)
474 ASSERT(lastFloatFromPreviousLine);
476 ShapeInsideInfo* shapeInsideInfo = block.layoutShapeInsideInfo();
477 if (!lastFloatFromPreviousLine->isPlaced() || !shapeInsideInfo)
480 bool isHorizontalWritingMode = block.isHorizontalWritingMode();
481 LayoutUnit logicalOffsetFromShapeContainer = block.logicalOffsetFromShapeAncestorContainer(&shapeInsideInfo->owner()).height();
483 LayoutUnit lineLogicalTop = block.logicalHeight() + logicalOffsetFromShapeContainer;
484 LayoutUnit lineLogicalHeight = block.lineHeight(isFirstLine, isHorizontalWritingMode ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes);
485 LayoutUnit lineLogicalBottom = lineLogicalTop + lineLogicalHeight;
487 LayoutUnit floatLogicalTop = block.logicalTopForFloat(lastFloatFromPreviousLine);
488 LayoutUnit floatLogicalBottom = block.logicalBottomForFloat(lastFloatFromPreviousLine);
490 bool lineOverlapsWithFloat = (floatLogicalTop < lineLogicalBottom) && (lineLogicalTop < floatLogicalBottom);
491 if (!lineOverlapsWithFloat)
494 float minSegmentWidth = firstPositiveWidth(wordMeasurements);
496 LayoutUnit floatLogicalWidth = block.logicalWidthForFloat(lastFloatFromPreviousLine);
497 LayoutUnit availableLogicalWidth = block.logicalWidth() - block.logicalRightForFloat(lastFloatFromPreviousLine);
498 if (availableLogicalWidth < minSegmentWidth)
499 block.setLogicalHeight(floatLogicalBottom);
501 if (block.logicalHeight() < floatLogicalTop) {
502 shapeInsideInfo->adjustLogicalLineTop(minSegmentWidth + floatLogicalWidth);
503 block.setLogicalHeight(shapeInsideInfo->logicalLineTop() - logicalOffsetFromShapeContainer);
506 lineLogicalTop = block.logicalHeight() + logicalOffsetFromShapeContainer;
508 shapeInsideInfo->updateSegmentsForLine(lineLogicalTop, lineLogicalHeight);
509 width.updateCurrentShapeSegment();
510 width.updateAvailableWidth();
514 inline bool iteratorIsBeyondEndOfRenderCombineText(const InlineIterator& iter, RenderCombineText& renderer)
516 return iter.renderer() == &renderer && iter.offset() >= renderer.textLength();
519 inline void nextCharacter(UChar& currentCharacter, UChar& lastCharacter, UChar& secondToLastCharacter)
521 secondToLastCharacter = lastCharacter;
522 lastCharacter = currentCharacter;
525 // FIXME: Don't let counters mark themselves as needing pref width recalcs during layout
526 // so we don't need this hack.
527 inline void updateCounterIfNeeded(RenderText& renderText)
529 if (!renderText.preferredLogicalWidthsDirty() || !renderText.isCounter())
531 toRenderCounter(renderText).updateCounter();
534 inline float measureHyphenWidth(RenderText* renderer, const Font& font, HashSet<const SimpleFontData*>* fallbackFonts = 0)
536 const RenderStyle& style = renderer->style();
537 return font.width(RenderBlock::constructTextRun(renderer, font, style.hyphenString().string(), style), fallbackFonts);
540 ALWAYS_INLINE float textWidth(RenderText* text, unsigned from, unsigned len, const Font& font, float xPos, bool isFixedPitch, bool collapseWhiteSpace, HashSet<const SimpleFontData*>& fallbackFonts, TextLayout* layout = 0)
542 const RenderStyle& style = text->style();
544 GlyphOverflow glyphOverflow;
545 if (isFixedPitch || (!from && len == text->textLength()) || style.hasTextCombine())
546 return text->width(from, len, font, xPos, &fallbackFonts, &glyphOverflow);
549 return Font::width(*layout, from, len, &fallbackFonts);
551 TextRun run = RenderBlock::constructTextRun(text, font, text, from, len, style);
552 run.setCharactersLength(text->textLength() - from);
553 ASSERT(run.charactersLength() >= run.length());
555 run.setCharacterScanForCodePath(!text->canUseSimpleFontCodePath());
556 run.setTabSize(!collapseWhiteSpace, style.tabSize());
558 return font.width(run, &fallbackFonts, &glyphOverflow);
561 // Adding a pair of midpoints before a character will split it out into a new line box.
562 inline void ensureCharacterGetsLineBox(LineMidpointState& lineMidpointState, InlineIterator& textParagraphSeparator)
564 InlineIterator midpoint(0, textParagraphSeparator.renderer(), textParagraphSeparator.offset());
565 lineMidpointState.startIgnoringSpaces(InlineIterator(0, textParagraphSeparator.renderer(), textParagraphSeparator.offset() - 1));
566 lineMidpointState.stopIgnoringSpaces(InlineIterator(0, textParagraphSeparator.renderer(), textParagraphSeparator.offset()));
569 inline void tryHyphenating(RenderText* text, const Font& font, const AtomicString& localeIdentifier, unsigned consecutiveHyphenatedLines, int consecutiveHyphenatedLinesLimit, int minimumPrefixLimit, int minimumSuffixLimit, unsigned lastSpace, unsigned pos, float xPos, int availableWidth, bool isFixedPitch, bool collapseWhiteSpace, int lastSpaceWordSpacing, InlineIterator& lineBreak, int nextBreakable, bool& hyphenated)
571 // Map 'hyphenate-limit-{before,after}: auto;' to 2.
572 unsigned minimumPrefixLength;
573 unsigned minimumSuffixLength;
575 if (minimumPrefixLimit < 0)
576 minimumPrefixLength = 2;
578 minimumPrefixLength = static_cast<unsigned>(minimumPrefixLimit);
580 if (minimumSuffixLimit < 0)
581 minimumSuffixLength = 2;
583 minimumSuffixLength = static_cast<unsigned>(minimumSuffixLimit);
585 if (pos - lastSpace <= minimumSuffixLength)
588 if (consecutiveHyphenatedLinesLimit >= 0 && consecutiveHyphenatedLines >= static_cast<unsigned>(consecutiveHyphenatedLinesLimit))
591 int hyphenWidth = measureHyphenWidth(text, font);
593 float maxPrefixWidth = availableWidth - xPos - hyphenWidth - lastSpaceWordSpacing;
594 // If the maximum width available for the prefix before the hyphen is small, then it is very unlikely
595 // that an hyphenation opportunity exists, so do not bother to look for it.
596 if (maxPrefixWidth <= font.pixelSize() * 5 / 4)
599 const RenderStyle& style = text->style();
600 TextRun run = RenderBlock::constructTextRun(text, font, text, lastSpace, pos - lastSpace, style);
601 run.setCharactersLength(text->textLength() - lastSpace);
602 ASSERT(run.charactersLength() >= run.length());
604 run.setTabSize(!collapseWhiteSpace, style.tabSize());
605 run.setXPos(xPos + lastSpaceWordSpacing);
607 unsigned prefixLength = font.offsetForPosition(run, maxPrefixWidth, false);
608 if (prefixLength < minimumPrefixLength)
611 prefixLength = lastHyphenLocation(text->deprecatedCharacters() + lastSpace, pos - lastSpace, std::min(prefixLength, pos - lastSpace - minimumSuffixLength) + 1, localeIdentifier);
612 if (!prefixLength || prefixLength < minimumPrefixLength)
615 // When lastSapce is a space, which it always is except sometimes at the beginning of a line or after collapsed
616 // space, it should not count towards hyphenate-limit-before.
617 if (prefixLength == minimumPrefixLength) {
618 UChar characterAtLastSpace = text->characterAt(lastSpace);
619 if (characterAtLastSpace == ' ' || characterAtLastSpace == '\n' || characterAtLastSpace == '\t' || characterAtLastSpace == noBreakSpace)
623 ASSERT(pos - lastSpace - prefixLength >= minimumSuffixLength);
626 HashSet<const SimpleFontData*> fallbackFonts;
627 float prefixWidth = hyphenWidth + textWidth(text, lastSpace, prefixLength, font, xPos, isFixedPitch, collapseWhiteSpace, fallbackFonts) + lastSpaceWordSpacing;
628 ASSERT(xPos + prefixWidth <= availableWidth);
630 UNUSED_PARAM(isFixedPitch);
633 lineBreak.moveTo(text, lastSpace + prefixLength, nextBreakable);
637 inline bool BreakingContext::handleText(WordMeasurements& wordMeasurements, bool& hyphenated, unsigned& consecutiveHyphenatedLines)
639 if (!m_current.offset())
640 m_appliedStartWidth = false;
642 RenderText* renderText = toRenderText(m_current.renderer());
645 bool isSVGText = renderText->isSVGInlineText();
648 // If we have left a no-wrap inline and entered an autowrap inline while ignoring spaces
649 // then we need to mark the start of the autowrap inline as a potential linebreak now.
650 if (m_autoWrap && !RenderStyle::autoWrap(m_lastWS) && m_ignoringSpaces)
651 commitLineBreakAtCurrentWidth(m_current.renderer());
653 if (renderText->style().hasTextCombine() && m_current.renderer()->isCombineText() && !toRenderCombineText(*m_current.renderer()).isCombined()) {
654 RenderCombineText& combineRenderer = toRenderCombineText(*m_current.renderer());
655 combineRenderer.combineText();
656 // The length of the renderer's text may have changed. Increment stale iterator positions
657 if (iteratorIsBeyondEndOfRenderCombineText(m_lineBreak, combineRenderer)) {
658 ASSERT(iteratorIsBeyondEndOfRenderCombineText(m_resolver.position(), combineRenderer));
659 m_lineBreak.increment();
660 m_resolver.increment();
664 const RenderStyle& style = lineStyle(*renderText->parent(), m_lineInfo);
665 const Font& font = style.font();
666 bool isFixedPitch = font.isFixedPitch();
667 bool canHyphenate = style.hyphens() == HyphensAuto && WebCore::canHyphenate(style.locale());
669 unsigned lastSpace = m_current.offset();
670 float wordSpacing = m_currentStyle->font().wordSpacing();
671 float lastSpaceWordSpacing = 0;
672 float wordSpacingForWordMeasurement = 0;
674 float wrapW = m_width.uncommittedWidth() + inlineLogicalWidth(m_current.renderer(), !m_appliedStartWidth, true);
676 bool breakNBSP = m_autoWrap && m_currentStyle->nbspMode() == SPACE;
677 // Auto-wrapping text should wrap in the middle of a word only if it could not wrap before the word,
678 // which is only possible if the word is the first thing on the line, that is, if |w| is zero.
679 bool breakWords = m_currentStyle->breakWords() && ((m_autoWrap && !m_width.committedWidth()) || m_currWS == PRE);
680 bool midWordBreak = false;
681 bool breakAll = m_currentStyle->wordBreak() == BreakAllWordBreak && m_autoWrap;
682 float hyphenWidth = 0;
690 if (m_renderTextInfo.m_text != renderText) {
691 updateCounterIfNeeded(*renderText);
692 m_renderTextInfo.m_text = renderText;
693 m_renderTextInfo.m_font = &font;
694 m_renderTextInfo.m_layout = font.createLayout(renderText, m_width.currentWidth(), m_collapseWhiteSpace);
695 m_renderTextInfo.m_lineBreakIterator.resetStringAndReleaseIterator(renderText->text(), style.locale());
696 } else if (m_renderTextInfo.m_layout && m_renderTextInfo.m_font != &font) {
697 m_renderTextInfo.m_font = &font;
698 m_renderTextInfo.m_layout = font.createLayout(renderText, m_width.currentWidth(), m_collapseWhiteSpace);
701 TextLayout* textLayout = m_renderTextInfo.m_layout.get();
703 // Non-zero only when kerning is enabled and TextLayout isn't used, in which case we measure
704 // words with their trailing space, then subtract its width.
705 HashSet<const SimpleFontData*> fallbackFonts;
706 float wordTrailingSpaceWidth = (font.typesettingFeatures() & Kerning) && !textLayout ? font.width(RenderBlock::constructTextRun(renderText, font, &space, 1, style), &fallbackFonts) + wordSpacing : 0;
708 UChar lastCharacter = m_renderTextInfo.m_lineBreakIterator.lastCharacter();
709 UChar secondToLastCharacter = m_renderTextInfo.m_lineBreakIterator.secondToLastCharacter();
710 for (; m_current.offset() < renderText->textLength(); m_current.fastIncrementInTextNode()) {
711 bool previousCharacterIsSpace = m_currentCharacterIsSpace;
712 bool previousCharacterIsWS = m_currentCharacterIsWS;
713 UChar c = m_current.current();
714 m_currentCharacterIsSpace = c == ' ' || c == '\t' || (!m_preservesNewline && (c == '\n'));
716 if (!m_collapseWhiteSpace || !m_currentCharacterIsSpace)
717 m_lineInfo.setEmpty(false, &m_block, &m_width);
719 if (c == softHyphen && m_autoWrap && !hyphenWidth && style.hyphens() != HyphensNone) {
720 hyphenWidth = measureHyphenWidth(renderText, font, &fallbackFonts);
721 m_width.addUncommittedWidth(hyphenWidth);
724 bool applyWordSpacing = false;
726 m_currentCharacterIsWS = m_currentCharacterIsSpace || (breakNBSP && c == noBreakSpace);
728 if ((breakAll || breakWords) && !midWordBreak && (!m_currentCharacterIsSpace || style.whiteSpace() != PRE_WRAP)) {
730 bool midWordBreakIsBeforeSurrogatePair = U16_IS_LEAD(c) && m_current.offset() + 1 < renderText->textLength() && U16_IS_TRAIL((*renderText)[m_current.offset() + 1]);
731 charWidth = textWidth(renderText, m_current.offset(), midWordBreakIsBeforeSurrogatePair ? 2 : 1, font, m_width.committedWidth() + wrapW, isFixedPitch, m_collapseWhiteSpace, fallbackFonts, textLayout);
732 midWordBreak = m_width.committedWidth() + wrapW + charWidth > m_width.availableWidth();
735 int nextBreakablePosition = m_current.nextBreakablePosition();
736 bool betweenWords = c == '\n' || (m_currWS != PRE && !m_atStart && isBreakable(m_renderTextInfo.m_lineBreakIterator, m_current.offset(), nextBreakablePosition, breakNBSP)
737 && (style.hyphens() != HyphensNone || (m_current.previousInSameNode() != softHyphen)));
738 m_current.setNextBreakablePosition(nextBreakablePosition);
740 if (betweenWords || midWordBreak) {
741 bool stoppedIgnoringSpaces = false;
742 if (m_ignoringSpaces) {
743 lastSpaceWordSpacing = 0;
744 if (!m_currentCharacterIsSpace) {
745 // Stop ignoring spaces and begin at this
747 m_ignoringSpaces = false;
748 wordSpacingForWordMeasurement = 0;
749 lastSpace = m_current.offset(); // e.g., "Foo goo", don't add in any of the ignored spaces.
750 m_lineMidpointState.stopIgnoringSpaces(InlineIterator(0, m_current.renderer(), m_current.offset()));
751 stoppedIgnoringSpaces = true;
753 // Just keep ignoring these spaces.
754 nextCharacter(c, lastCharacter, secondToLastCharacter);
759 wordMeasurements.grow(wordMeasurements.size() + 1);
760 WordMeasurement& wordMeasurement = wordMeasurements.last();
762 wordMeasurement.renderer = renderText;
763 wordMeasurement.endOffset = m_current.offset();
764 wordMeasurement.startOffset = lastSpace;
766 float additionalTempWidth;
767 if (wordTrailingSpaceWidth && c == ' ')
768 additionalTempWidth = textWidth(renderText, lastSpace, m_current.offset() + 1 - lastSpace, font, m_width.currentWidth(), isFixedPitch, m_collapseWhiteSpace, wordMeasurement.fallbackFonts, textLayout) - wordTrailingSpaceWidth;
770 additionalTempWidth = textWidth(renderText, lastSpace, m_current.offset() - lastSpace, font, m_width.currentWidth(), isFixedPitch, m_collapseWhiteSpace, wordMeasurement.fallbackFonts, textLayout);
772 if (wordMeasurement.fallbackFonts.isEmpty() && !fallbackFonts.isEmpty())
773 wordMeasurement.fallbackFonts.swap(fallbackFonts);
774 fallbackFonts.clear();
776 wordMeasurement.width = additionalTempWidth + wordSpacingForWordMeasurement;
777 additionalTempWidth += lastSpaceWordSpacing;
778 m_width.addUncommittedWidth(additionalTempWidth);
780 if (m_collapseWhiteSpace && previousCharacterIsSpace && m_currentCharacterIsSpace && additionalTempWidth)
781 m_width.setTrailingWhitespaceWidth(additionalTempWidth);
783 if (!m_appliedStartWidth) {
784 m_width.addUncommittedWidth(inlineLogicalWidth(m_current.renderer(), true, false));
785 m_appliedStartWidth = true;
788 #if ENABLE(CSS_SHAPES)
789 if (m_lastFloatFromPreviousLine)
790 updateSegmentsForShapes(m_block, m_lastFloatFromPreviousLine, wordMeasurements, m_width, m_lineInfo.isFirstLine());
792 applyWordSpacing = wordSpacing && m_currentCharacterIsSpace;
794 if (!m_width.committedWidth() && m_autoWrap && !m_width.fitsOnLine())
795 m_width.fitBelowFloats();
797 if (m_autoWrap || breakWords) {
798 // If we break only after white-space, consider the current character
799 // as candidate width for this line.
800 bool lineWasTooWide = false;
801 if (m_width.fitsOnLine() && m_currentCharacterIsWS && m_currentStyle->breakOnlyAfterWhiteSpace() && !midWordBreak) {
802 float charWidth = textWidth(renderText, m_current.offset(), 1, font, m_width.currentWidth(), isFixedPitch, m_collapseWhiteSpace, wordMeasurement.fallbackFonts, textLayout) + (applyWordSpacing ? wordSpacing : 0);
803 // Check if line is too big even without the extra space
804 // at the end of the line. If it is not, do nothing.
805 // If the line needs the extra whitespace to be too long,
806 // then move the line break to the space and skip all
807 // additional whitespace.
808 if (!m_width.fitsOnLineIncludingExtraWidth(charWidth)) {
809 lineWasTooWide = true;
810 m_lineBreak.moveTo(m_current.renderer(), m_current.offset(), m_current.nextBreakablePosition());
811 m_lineBreaker.skipTrailingWhitespace(m_lineBreak, m_lineInfo);
814 if (lineWasTooWide || !m_width.fitsOnLine()) {
815 if (canHyphenate && !m_width.fitsOnLine()) {
816 tryHyphenating(renderText, font, style.locale(), consecutiveHyphenatedLines, m_blockStyle.hyphenationLimitLines(), style.hyphenationLimitBefore(), style.hyphenationLimitAfter(), lastSpace, m_current.offset(), m_width.currentWidth() - additionalTempWidth, m_width.availableWidth(), isFixedPitch, m_collapseWhiteSpace, lastSpaceWordSpacing, m_lineBreak, m_current.nextBreakablePosition(), m_lineBreaker.m_hyphenated);
817 if (m_lineBreaker.m_hyphenated) {
822 if (m_lineBreak.atTextParagraphSeparator()) {
823 if (!stoppedIgnoringSpaces && m_current.offset() > 0)
824 ensureCharacterGetsLineBox(m_lineMidpointState, m_current);
825 m_lineBreak.increment();
826 m_lineInfo.setPreviousLineBrokeCleanly(true);
827 wordMeasurement.endOffset = m_lineBreak.offset();
829 if (m_lineBreak.renderer() && m_lineBreak.offset() && m_lineBreak.renderer()->isText() && toRenderText(m_lineBreak.renderer())->textLength() && toRenderText(m_lineBreak.renderer())->characterAt(m_lineBreak.offset() - 1) == softHyphen && style.hyphens() != HyphensNone)
831 if (m_lineBreak.offset() && m_lineBreak.offset() != (unsigned)wordMeasurement.endOffset && !wordMeasurement.width) {
833 wordMeasurement.endOffset = m_lineBreak.offset();
834 wordMeasurement.width = charWidth;
837 // Didn't fit. Jump to the end unless there's still an opportunity to collapse whitespace.
838 if (m_ignoringSpaces || !m_collapseWhiteSpace || !m_currentCharacterIsSpace || !previousCharacterIsSpace) {
843 if (!betweenWords || (midWordBreak && !m_autoWrap))
844 m_width.addUncommittedWidth(-additionalTempWidth);
846 // Subtract the width of the soft hyphen out since we fit on a line.
847 m_width.addUncommittedWidth(-hyphenWidth);
853 if (c == '\n' && m_preservesNewline) {
854 if (!stoppedIgnoringSpaces && m_current.offset())
855 ensureCharacterGetsLineBox(m_lineMidpointState, m_current);
856 m_lineBreak.moveTo(m_current.renderer(), m_current.offset(), m_current.nextBreakablePosition());
857 m_lineBreak.increment();
858 m_lineInfo.setPreviousLineBrokeCleanly(true);
862 if (m_autoWrap && betweenWords) {
865 m_lineBreak.moveTo(m_current.renderer(), m_current.offset(), m_current.nextBreakablePosition());
866 // Auto-wrapping text should not wrap in the middle of a word once it has had an
867 // opportunity to break after a word.
871 if (midWordBreak && !U16_IS_TRAIL(c) && !(U_GET_GC_MASK(c) & U_GC_M_MASK)) {
872 // Remember this as a breakable position in case
873 // adding the end width forces a break.
874 m_lineBreak.moveTo(m_current.renderer(), m_current.offset(), m_current.nextBreakablePosition());
875 midWordBreak &= (breakWords || breakAll);
879 lastSpaceWordSpacing = applyWordSpacing ? wordSpacing : 0;
880 wordSpacingForWordMeasurement = (applyWordSpacing && wordMeasurement.width) ? wordSpacing : 0;
881 lastSpace = m_current.offset();
884 if (!m_ignoringSpaces && m_currentStyle->collapseWhiteSpace()) {
885 // If we encounter a newline, or if we encounter a
886 // second space, we need to go ahead and break up this
887 // run and enter a mode where we start collapsing spaces.
888 if (m_currentCharacterIsSpace && previousCharacterIsSpace) {
889 m_ignoringSpaces = true;
891 // We just entered a mode where we are ignoring
892 // spaces. Create a midpoint to terminate the run
893 // before the second space.
894 m_lineMidpointState.startIgnoringSpaces(m_startOfIgnoredSpaces);
895 m_trailingObjects.updateMidpointsForTrailingBoxes(m_lineMidpointState, InlineIterator(), TrailingObjects::DoNotCollapseFirstSpace);
898 } else if (m_ignoringSpaces) {
899 // Stop ignoring spaces and begin at this
901 m_ignoringSpaces = false;
902 lastSpaceWordSpacing = applyWordSpacing ? wordSpacing : 0;
903 wordSpacingForWordMeasurement = (applyWordSpacing && wordMeasurements.last().width) ? wordSpacing : 0;
904 lastSpace = m_current.offset(); // e.g., "Foo goo", don't add in any of the ignored spaces.
905 m_lineMidpointState.stopIgnoringSpaces(InlineIterator(0, m_current.renderer(), m_current.offset()));
908 if (isSVGText && m_current.offset()) {
909 // Force creation of new InlineBoxes for each absolute positioned character (those that start new text chunks).
910 if (toRenderSVGInlineText(renderText)->characterStartsNewTextChunk(m_current.offset()))
911 ensureCharacterGetsLineBox(m_lineMidpointState, m_current);
915 if (m_currentCharacterIsSpace && !previousCharacterIsSpace) {
916 m_startOfIgnoredSpaces.setRenderer(m_current.renderer());
917 m_startOfIgnoredSpaces.setOffset(m_current.offset());
918 // Spaces after right-aligned text and before a line-break get collapsed away completely so that the trailing
919 // space doesn't seem to push the text out from the right-hand edge.
920 // FIXME: Do this regardless of the container's alignment - will require rebaselining a lot of test results.
921 if (m_nextObject && m_nextObject->isBR() && (m_blockStyle.textAlign() == RIGHT || m_blockStyle.textAlign() == WEBKIT_RIGHT)) {
922 m_startOfIgnoredSpaces.setOffset(m_startOfIgnoredSpaces.offset() - 1);
923 // If there's just a single trailing space start ignoring it now so it collapses away.
924 if (m_current.offset() == renderText->textLength() - 1)
925 m_lineMidpointState.startIgnoringSpaces(m_startOfIgnoredSpaces);
929 if (!m_currentCharacterIsWS && previousCharacterIsWS) {
930 if (m_autoWrap && m_currentStyle->breakOnlyAfterWhiteSpace())
931 m_lineBreak.moveTo(m_current.renderer(), m_current.offset(), m_current.nextBreakablePosition());
934 if (m_collapseWhiteSpace && m_currentCharacterIsSpace && !m_ignoringSpaces)
935 m_trailingObjects.setTrailingWhitespace(toRenderText(m_current.renderer()));
936 else if (!m_currentStyle->collapseWhiteSpace() || !m_currentCharacterIsSpace)
937 m_trailingObjects.clear();
940 nextCharacter(c, lastCharacter, secondToLastCharacter);
943 m_renderTextInfo.m_lineBreakIterator.setPriorContext(lastCharacter, secondToLastCharacter);
945 wordMeasurements.grow(wordMeasurements.size() + 1);
946 WordMeasurement& wordMeasurement = wordMeasurements.last();
947 wordMeasurement.renderer = renderText;
949 // IMPORTANT: current.m_pos is > length here!
950 float additionalTempWidth = m_ignoringSpaces ? 0 : textWidth(renderText, lastSpace, m_current.offset() - lastSpace, font, m_width.currentWidth(), isFixedPitch, m_collapseWhiteSpace, wordMeasurement.fallbackFonts, textLayout);
951 wordMeasurement.startOffset = lastSpace;
952 wordMeasurement.endOffset = m_current.offset();
953 wordMeasurement.width = m_ignoringSpaces ? 0 : additionalTempWidth + wordSpacingForWordMeasurement;
954 additionalTempWidth += lastSpaceWordSpacing;
956 float inlineLogicalTempWidth = inlineLogicalWidth(m_current.renderer(), !m_appliedStartWidth, m_includeEndWidth);
957 m_width.addUncommittedWidth(additionalTempWidth + inlineLogicalTempWidth);
959 if (wordMeasurement.fallbackFonts.isEmpty() && !fallbackFonts.isEmpty())
960 wordMeasurement.fallbackFonts.swap(fallbackFonts);
961 fallbackFonts.clear();
963 if (m_collapseWhiteSpace && m_currentCharacterIsSpace && additionalTempWidth)
964 m_width.setTrailingWhitespaceWidth(additionalTempWidth, inlineLogicalTempWidth);
966 m_includeEndWidth = false;
968 if (!m_width.fitsOnLine()) {
970 tryHyphenating(renderText, font, style.locale(), consecutiveHyphenatedLines, m_blockStyle.hyphenationLimitLines(), style.hyphenationLimitBefore(), style.hyphenationLimitAfter(), lastSpace, m_current.offset(), m_width.currentWidth() - additionalTempWidth, m_width.availableWidth(), isFixedPitch, m_collapseWhiteSpace, lastSpaceWordSpacing, m_lineBreak, m_current.nextBreakablePosition(), m_lineBreaker.m_hyphenated);
972 if (!hyphenated && m_lineBreak.previousInSameNode() == softHyphen && style.hyphens() != HyphensNone) {
980 inline bool textBeginsWithBreakablePosition(RenderObject* next)
982 ASSERT(next->isText());
983 RenderText* nextText = toRenderText(next);
984 if (!nextText->textLength())
986 UChar c = nextText->characterAt(0);
987 return c == ' ' || c == '\t' || (c == '\n' && !nextText->preservesNewline());
990 inline bool BreakingContext::canBreakAtThisPosition()
992 // If we are no-wrap and have found a line-breaking opportunity already then we should take it.
993 if (m_width.committedWidth() && !m_width.fitsOnLine(m_currentCharacterIsSpace) && m_currWS == NOWRAP)
996 // Avoid breaking before empty inlines.
997 if (m_nextObject && m_nextObject->isRenderInline() && isEmptyInline(toRenderInline(*m_nextObject)))
1000 // Return early if we autowrap and the current character is a space as we will always want to break at such a position.
1001 if (m_autoWrap && m_currentCharacterIsSpace)
1004 if (m_nextObject && m_nextObject->isLineBreakOpportunity())
1007 bool nextIsAutoWrappingText = (m_nextObject && m_nextObject->isText() && (m_autoWrap || m_nextObject->style().autoWrap()));
1008 if (!nextIsAutoWrappingText)
1010 bool currentIsTextOrEmptyInline = m_current.renderer()->isText() || (m_current.renderer()->isRenderInline() && isEmptyInline(toRenderInline(*m_current.renderer())));
1011 if (!currentIsTextOrEmptyInline)
1014 bool canBreakHere = !m_currentCharacterIsSpace && textBeginsWithBreakablePosition(m_nextObject);
1016 // See if attempting to fit below floats creates more available width on the line.
1017 if (!m_width.fitsOnLine() && !m_width.committedWidth())
1018 m_width.fitBelowFloats();
1020 bool canPlaceOnLine = m_width.fitsOnLine() || !m_autoWrapWasEverTrueOnLine;
1022 if (canPlaceOnLine && canBreakHere)
1023 commitLineBreakAtCurrentWidth(m_nextObject);
1025 return canBreakHere;
1028 inline void BreakingContext::commitAndUpdateLineBreakIfNeeded()
1030 bool checkForBreak = canBreakAtThisPosition();
1032 if (checkForBreak && !m_width.fitsOnLine(m_ignoringSpaces)) {
1033 // if we have floats, try to get below them.
1034 if (m_currentCharacterIsSpace && !m_ignoringSpaces && m_currentStyle->collapseWhiteSpace())
1035 m_trailingObjects.clear();
1037 if (m_width.committedWidth()) {
1042 m_width.fitBelowFloats();
1044 // |width| may have been adjusted because we got shoved down past a float (thus
1045 // giving us more room), so we need to retest, and only jump to
1046 // the end label if we still don't fit on the line. -dwh
1047 if (!m_width.fitsOnLine(m_ignoringSpaces)) {
1051 } else if (m_blockStyle.autoWrap() && !m_width.fitsOnLine() && !m_width.committedWidth()) {
1052 // If the container autowraps but the current child does not then we still need to ensure that it
1053 // wraps and moves below any floats.
1054 m_width.fitBelowFloats();
1057 if (!m_current.renderer()->isFloatingOrOutOfFlowPositioned()) {
1058 m_lastObject = m_current.renderer();
1059 if (m_lastObject->isReplaced() && m_autoWrap && (!m_lastObject->isImage() || m_allowImagesToBreak) && (!m_lastObject->isListMarker() || toRenderListMarker(*m_lastObject).isInside())) {
1061 m_lineBreak.moveToStartOf(m_nextObject);
1066 inline void checkMidpoints(LineMidpointState& lineMidpointState, InlineIterator& lBreak)
1068 // Check to see if our last midpoint is a start point beyond the line break. If so,
1069 // shave it off the list, and shave off a trailing space if the previous end point doesn't
1070 // preserve whitespace.
1071 if (lBreak.renderer() && lineMidpointState.numMidpoints && !(lineMidpointState.numMidpoints % 2)) {
1072 InlineIterator* midpoints = lineMidpointState.midpoints.data();
1073 InlineIterator& endpoint = midpoints[lineMidpointState.numMidpoints - 2];
1074 const InlineIterator& startpoint = midpoints[lineMidpointState.numMidpoints - 1];
1075 InlineIterator currpoint = endpoint;
1076 while (!currpoint.atEnd() && currpoint != startpoint && currpoint != lBreak)
1077 currpoint.increment();
1078 if (currpoint == lBreak) {
1079 // We hit the line break before the start point. Shave off the start point.
1080 lineMidpointState.numMidpoints--;
1081 if (endpoint.renderer()->style().collapseWhiteSpace() && endpoint.renderer()->isText())
1082 endpoint.setOffset(endpoint.offset() - 1);
1087 inline InlineIterator BreakingContext::handleEndOfLine()
1089 #if ENABLE(CSS_SHAPES)
1090 ShapeInsideInfo* shapeInfo = m_block.layoutShapeInsideInfo();
1091 bool segmentAllowsOverflow = !shapeInfo || !shapeInfo->hasSegments();
1093 bool segmentAllowsOverflow = true;
1095 if (segmentAllowsOverflow) {
1096 if (m_lineBreak == m_resolver.position()) {
1097 if (!m_lineBreak.renderer() || !m_lineBreak.renderer()->isBR()) {
1098 // we just add as much as possible
1099 if (m_blockStyle.whiteSpace() == PRE && !m_current.offset()) {
1100 m_lineBreak.moveTo(m_lastObject, m_lastObject->isText() ? m_lastObject->length() : 0);
1101 } else if (m_lineBreak.renderer()) {
1102 // Don't ever break in the middle of a word if we can help it.
1103 // There's no room at all. We just have to be on this line,
1104 // even though we'll spill out.
1105 m_lineBreak.moveTo(m_current.renderer(), m_current.offset());
1108 // make sure we consume at least one char/object.
1109 if (m_lineBreak == m_resolver.position())
1110 m_lineBreak.increment();
1111 } else if (!m_current.offset() && !m_width.committedWidth() && m_width.uncommittedWidth() && !m_hadUncommittedWidthBeforeCurrent) {
1112 // Do not push the current object to the next line, when this line has some content, but it is still considered empty.
1113 // Empty inline elements like <span></span> can produce such lines and now we just ignore these break opportunities
1114 // at the start of a line, if no width has been committed yet.
1115 // Behave as if it was actually empty and consume at least one object.
1116 m_lineBreak.increment();
1120 // Sanity check our midpoints.
1121 checkMidpoints(m_lineMidpointState, m_lineBreak);
1123 m_trailingObjects.updateMidpointsForTrailingBoxes(m_lineMidpointState, m_lineBreak, TrailingObjects::CollapseFirstSpace);
1125 // We might have made lineBreak an iterator that points past the end
1126 // of the object. Do this adjustment to make it point to the start
1127 // of the next object instead to avoid confusing the rest of the
1129 if (m_lineBreak.offset()) {
1130 m_lineBreak.setOffset(m_lineBreak.offset() - 1);
1131 m_lineBreak.increment();
1139 #endif // BreakingContextInlineHeaders_h