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