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