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