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