[New Block-Inside-Inline Model] Floats need to be allowed to intrude into anonymous...
[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 (replacedBox.isAnonymousInlineBlock())
505         replacedBox.layoutIfNeeded();
506
507     if (m_ignoringSpaces)
508         m_lineMidpointState.stopIgnoringSpaces(InlineIterator(0, m_current.renderer(), 0));
509
510     m_lineInfo.setEmpty(false, &m_block, &m_width);
511     m_ignoringSpaces = false;
512     m_currentCharacterIsSpace = false;
513     m_currentCharacterIsWS = false;
514     m_trailingObjects.clear();
515
516     // Optimize for a common case. If we can't find whitespace after the list
517     // item, then this is all moot.
518     LayoutUnit replacedLogicalWidth = m_block.logicalWidthForChild(replacedBox) + m_block.marginStartForChild(replacedBox) + m_block.marginEndForChild(replacedBox) + inlineLogicalWidth(m_current.renderer());
519     if (is<RenderListMarker>(*m_current.renderer())) {
520         if (m_blockStyle.collapseWhiteSpace() && shouldSkipWhitespaceAfterStartObject(m_block, m_current.renderer(), m_lineMidpointState)) {
521             // Like with inline flows, we start ignoring spaces to make sure that any
522             // additional spaces we see will be discarded.
523             m_currentCharacterIsSpace = true;
524             m_currentCharacterIsWS = false;
525             m_ignoringSpaces = true;
526         }
527         if (downcast<RenderListMarker>(*m_current.renderer()).isInside())
528             m_width.addUncommittedWidth(replacedLogicalWidth);
529     } else
530         m_width.addUncommittedWidth(replacedLogicalWidth);
531     if (is<RenderRubyRun>(*m_current.renderer())) {
532         m_width.applyOverhang(downcast<RenderRubyRun>(m_current.renderer()), m_lastObject, m_nextObject);
533         downcast<RenderRubyRun>(m_current.renderer())->updatePriorContextFromCachedBreakIterator(m_renderTextInfo.m_lineBreakIterator);
534     } else {
535         // Update prior line break context characters, using U+FFFD (OBJECT REPLACEMENT CHARACTER) for replaced element.
536         m_renderTextInfo.m_lineBreakIterator.updatePriorContext(replacementCharacter);
537     }
538     
539     if (replacedBox.isAnonymousInlineBlock()) {
540         m_atEnd = true;
541         m_lineInfo.setPreviousLineBrokeCleanly(true);
542     }
543 }
544
545 inline float firstPositiveWidth(const WordMeasurements& wordMeasurements)
546 {
547     for (size_t i = 0; i < wordMeasurements.size(); ++i) {
548         if (wordMeasurements[i].width > 0)
549             return wordMeasurements[i].width;
550     }
551     return 0;
552 }
553
554 inline bool iteratorIsBeyondEndOfRenderCombineText(const InlineIterator& iter, RenderCombineText& renderer)
555 {
556     return iter.renderer() == &renderer && iter.offset() >= renderer.textLength();
557 }
558
559 inline void nextCharacter(UChar& currentCharacter, UChar& lastCharacter, UChar& secondToLastCharacter)
560 {
561     secondToLastCharacter = lastCharacter;
562     lastCharacter = currentCharacter;
563 }
564
565 // FIXME: Don't let counters mark themselves as needing pref width recalcs during layout
566 // so we don't need this hack.
567 inline void updateCounterIfNeeded(RenderText& renderText)
568 {
569     if (!renderText.preferredLogicalWidthsDirty() || !is<RenderCounter>(renderText))
570         return;
571     downcast<RenderCounter>(renderText).updateCounter();
572 }
573
574 inline float measureHyphenWidth(RenderText& renderer, const FontCascade& font, HashSet<const Font*>* fallbackFonts = 0)
575 {
576     const RenderStyle& style = renderer.style();
577     return font.width(RenderBlock::constructTextRun(&renderer, font, style.hyphenString().string(), style), fallbackFonts);
578 }
579
580 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)
581 {
582     const RenderStyle& style = text.style();
583
584     GlyphOverflow glyphOverflow;
585     if (isFixedPitch || (!from && len == text.textLength()) || style.hasTextCombine())
586         return text.width(from, len, font, xPos, &fallbackFonts, &glyphOverflow);
587
588     if (layout)
589         return FontCascade::width(*layout, from, len, &fallbackFonts);
590
591     TextRun run = RenderBlock::constructTextRun(&text, font, &text, from, len, style);
592     run.setCharactersLength(text.textLength() - from);
593     ASSERT(run.charactersLength() >= run.length());
594
595     run.setCharacterScanForCodePath(!text.canUseSimpleFontCodePath());
596     run.setTabSize(!collapseWhiteSpace, style.tabSize());
597     run.setXPos(xPos);
598     return font.width(run, &fallbackFonts, &glyphOverflow);
599 }
600
601 // Adding a pair of midpoints before a character will split it out into a new line box.
602 inline void ensureCharacterGetsLineBox(LineMidpointState& lineMidpointState, InlineIterator& textParagraphSeparator)
603 {
604     InlineIterator midpoint(0, textParagraphSeparator.renderer(), textParagraphSeparator.offset());
605     lineMidpointState.startIgnoringSpaces(InlineIterator(0, textParagraphSeparator.renderer(), textParagraphSeparator.offset() - 1));
606     lineMidpointState.stopIgnoringSpaces(InlineIterator(0, textParagraphSeparator.renderer(), textParagraphSeparator.offset()));
607 }
608
609 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)
610 {
611     // Map 'hyphenate-limit-{before,after}: auto;' to 2.
612     unsigned minimumPrefixLength;
613     unsigned minimumSuffixLength;
614
615     if (minimumPrefixLimit < 0)
616         minimumPrefixLength = 2;
617     else
618         minimumPrefixLength = static_cast<unsigned>(minimumPrefixLimit);
619
620     if (minimumSuffixLimit < 0)
621         minimumSuffixLength = 2;
622     else
623         minimumSuffixLength = static_cast<unsigned>(minimumSuffixLimit);
624
625     if (pos - lastSpace <= minimumSuffixLength)
626         return;
627
628     if (consecutiveHyphenatedLinesLimit >= 0 && consecutiveHyphenatedLines >= static_cast<unsigned>(consecutiveHyphenatedLinesLimit))
629         return;
630
631     int hyphenWidth = measureHyphenWidth(text, font);
632
633     float maxPrefixWidth = availableWidth - xPos - hyphenWidth - lastSpaceWordSpacing;
634     // If the maximum width available for the prefix before the hyphen is small, then it is very unlikely
635     // that an hyphenation opportunity exists, so do not bother to look for it.
636     if (maxPrefixWidth <= font.pixelSize() * 5 / 4)
637         return;
638
639     const RenderStyle& style = text.style();
640     TextRun run = RenderBlock::constructTextRun(&text, font, &text, lastSpace, pos - lastSpace, style);
641     run.setCharactersLength(text.textLength() - lastSpace);
642     ASSERT(run.charactersLength() >= run.length());
643
644     run.setTabSize(!collapseWhiteSpace, style.tabSize());
645     run.setXPos(xPos + lastSpaceWordSpacing);
646
647     unsigned prefixLength = font.offsetForPosition(run, maxPrefixWidth, false);
648     if (prefixLength < minimumPrefixLength)
649         return;
650
651     prefixLength = lastHyphenLocation(StringView(text.text()).substring(lastSpace, pos - lastSpace), std::min(prefixLength, pos - lastSpace - minimumSuffixLength) + 1, localeIdentifier);
652     if (!prefixLength || prefixLength < minimumPrefixLength)
653         return;
654
655     // When lastSpace is a space, which it always is except sometimes at the beginning of a line or after collapsed
656     // space, it should not count towards hyphenate-limit-before.
657     if (prefixLength == minimumPrefixLength) {
658         UChar characterAtLastSpace = text.characterAt(lastSpace);
659         if (characterAtLastSpace == ' ' || characterAtLastSpace == '\n' || characterAtLastSpace == '\t' || characterAtLastSpace == noBreakSpace)
660             return;
661     }
662
663     ASSERT(pos - lastSpace - prefixLength >= minimumSuffixLength);
664
665 #if !ASSERT_DISABLED
666     HashSet<const Font*> fallbackFonts;
667     float prefixWidth = hyphenWidth + textWidth(text, lastSpace, prefixLength, font, xPos, isFixedPitch, collapseWhiteSpace, fallbackFonts) + lastSpaceWordSpacing;
668     ASSERT(xPos + prefixWidth <= availableWidth);
669 #else
670     UNUSED_PARAM(isFixedPitch);
671 #endif
672
673     lineBreak.moveTo(&text, lastSpace + prefixLength, nextBreakable);
674     hyphenated = true;
675 }
676
677 inline bool BreakingContext::handleText(WordMeasurements& wordMeasurements, bool& hyphenated,  unsigned& consecutiveHyphenatedLines)
678 {
679     if (!m_current.offset())
680         m_appliedStartWidth = false;
681
682     RenderText& renderText = downcast<RenderText>(*m_current.renderer());
683
684     bool isSVGText = renderText.isSVGInlineText();
685
686     // If we have left a no-wrap inline and entered an autowrap inline while ignoring spaces
687     // then we need to mark the start of the autowrap inline as a potential linebreak now.
688     if (m_autoWrap && !RenderStyle::autoWrap(m_lastWS) && m_ignoringSpaces)
689         commitLineBreakAtCurrentWidth(renderText);
690
691     if (renderText.style().hasTextCombine() && is<RenderCombineText>(*m_current.renderer()) && !downcast<RenderCombineText>(*m_current.renderer()).isCombined()) {
692         auto& combineRenderer = downcast<RenderCombineText>(*m_current.renderer());
693         combineRenderer.combineText();
694         // The length of the renderer's text may have changed. Increment stale iterator positions
695         if (iteratorIsBeyondEndOfRenderCombineText(m_lineBreakHistory.current(), combineRenderer)) {
696             ASSERT(iteratorIsBeyondEndOfRenderCombineText(m_resolver.position(), combineRenderer));
697             m_lineBreakHistory.increment();
698             m_resolver.increment();
699         }
700     }
701
702     const RenderStyle& style = lineStyle(renderText, m_lineInfo);
703     const FontCascade& font = style.fontCascade();
704     bool isFixedPitch = font.isFixedPitch();
705     bool canHyphenate = style.hyphens() == HyphensAuto && WebCore::canHyphenate(style.locale());
706
707     unsigned lastSpace = m_current.offset();
708     float wordSpacing = m_currentStyle->fontCascade().wordSpacing();
709     float lastSpaceWordSpacing = 0;
710     float wordSpacingForWordMeasurement = 0;
711
712     float wrapW = m_width.uncommittedWidth() + inlineLogicalWidth(m_current.renderer(), !m_appliedStartWidth, true);
713     float charWidth = 0;
714     bool breakNBSP = m_autoWrap && m_currentStyle->nbspMode() == SPACE;
715     // Auto-wrapping text should wrap in the middle of a word only if it could not wrap before the word,
716     // which is only possible if the word is the first thing on the line, that is, if |w| is zero.
717     bool breakWords = m_currentStyle->breakWords() && ((m_autoWrap && !m_width.committedWidth()) || m_currWS == PRE);
718     bool midWordBreak = false;
719     bool breakAll = m_currentStyle->wordBreak() == BreakAllWordBreak && m_autoWrap;
720     float hyphenWidth = 0;
721     bool isLooseCJKMode = false;
722
723     if (isSVGText) {
724         breakWords = false;
725         breakAll = false;
726     }
727
728     if (m_renderTextInfo.m_text != &renderText) {
729         updateCounterIfNeeded(renderText);
730         m_renderTextInfo.m_text = &renderText;
731         m_renderTextInfo.m_font = &font;
732         m_renderTextInfo.m_layout = font.createLayout(&renderText, m_width.currentWidth(), m_collapseWhiteSpace);
733         m_renderTextInfo.m_lineBreakIterator.resetStringAndReleaseIterator(renderText.text(), style.locale(), mapLineBreakToIteratorMode(m_blockStyle.lineBreak()));
734         isLooseCJKMode = m_renderTextInfo.m_lineBreakIterator.isLooseCJKMode();
735     } else if (m_renderTextInfo.m_layout && m_renderTextInfo.m_font != &font) {
736         m_renderTextInfo.m_font = &font;
737         m_renderTextInfo.m_layout = font.createLayout(&renderText, m_width.currentWidth(), m_collapseWhiteSpace);
738     }
739
740     TextLayout* textLayout = m_renderTextInfo.m_layout.get();
741
742     // Non-zero only when kerning is enabled and TextLayout isn't used, in which case we measure
743     // words with their trailing space, then subtract its width.
744     HashSet<const Font*> fallbackFonts;
745     float wordTrailingSpaceWidth = (font.typesettingFeatures() & Kerning) && !textLayout ? font.width(RenderBlock::constructTextRun(&renderText, font, &space, 1, style), &fallbackFonts) + wordSpacing : 0;
746
747     UChar lastCharacter = m_renderTextInfo.m_lineBreakIterator.lastCharacter();
748     UChar secondToLastCharacter = m_renderTextInfo.m_lineBreakIterator.secondToLastCharacter();
749     for (; m_current.offset() < renderText.textLength(); m_current.fastIncrementInTextNode()) {
750         bool previousCharacterIsSpace = m_currentCharacterIsSpace;
751         bool previousCharacterIsWS = m_currentCharacterIsWS;
752         UChar c = m_current.current();
753         m_currentCharacterIsSpace = c == ' ' || c == '\t' || (!m_preservesNewline && (c == '\n'));
754
755         if (!m_collapseWhiteSpace || !m_currentCharacterIsSpace)
756             m_lineInfo.setEmpty(false, &m_block, &m_width);
757
758         if (c == softHyphen && m_autoWrap && !hyphenWidth && style.hyphens() != HyphensNone) {
759             hyphenWidth = measureHyphenWidth(renderText, font, &fallbackFonts);
760             m_width.addUncommittedWidth(hyphenWidth);
761         }
762
763         bool applyWordSpacing = false;
764
765         m_currentCharacterIsWS = m_currentCharacterIsSpace || (breakNBSP && c == noBreakSpace);
766
767         if ((breakAll || breakWords) && !midWordBreak && (!m_currentCharacterIsSpace || style.whiteSpace() != PRE_WRAP)) {
768             wrapW += charWidth;
769             bool midWordBreakIsBeforeSurrogatePair = U16_IS_LEAD(c) && m_current.offset() + 1 < renderText.textLength() && U16_IS_TRAIL(renderText[m_current.offset() + 1]);
770             charWidth = textWidth(renderText, m_current.offset(), midWordBreakIsBeforeSurrogatePair ? 2 : 1, font, m_width.committedWidth() + wrapW, isFixedPitch, m_collapseWhiteSpace, fallbackFonts, textLayout);
771             midWordBreak = m_width.committedWidth() + wrapW + charWidth > m_width.availableWidth();
772         }
773
774         int nextBreakablePosition = m_current.nextBreakablePosition();
775         bool betweenWords = c == '\n' || (m_currWS != PRE && !m_atStart && isBreakable(m_renderTextInfo.m_lineBreakIterator, m_current.offset(), nextBreakablePosition, breakNBSP, isLooseCJKMode)
776             && (style.hyphens() != HyphensNone || (m_current.previousInSameNode() != softHyphen)));
777         m_current.setNextBreakablePosition(nextBreakablePosition);
778
779         if (betweenWords || midWordBreak) {
780             bool stoppedIgnoringSpaces = false;
781             if (m_ignoringSpaces) {
782                 lastSpaceWordSpacing = 0;
783                 if (!m_currentCharacterIsSpace) {
784                     // Stop ignoring spaces and begin at this
785                     // new point.
786                     m_ignoringSpaces = false;
787                     wordSpacingForWordMeasurement = 0;
788                     lastSpace = m_current.offset(); // e.g., "Foo    goo", don't add in any of the ignored spaces.
789                     m_lineMidpointState.stopIgnoringSpaces(InlineIterator(0, m_current.renderer(), m_current.offset()));
790                     stoppedIgnoringSpaces = true;
791                 } else {
792                     // Just keep ignoring these spaces.
793                     nextCharacter(c, lastCharacter, secondToLastCharacter);
794                     continue;
795                 }
796             }
797
798             wordMeasurements.grow(wordMeasurements.size() + 1);
799             WordMeasurement& wordMeasurement = wordMeasurements.last();
800
801             wordMeasurement.renderer = &renderText;
802             wordMeasurement.endOffset = m_current.offset();
803             wordMeasurement.startOffset = lastSpace;
804
805             float additionalTempWidth;
806             if (wordTrailingSpaceWidth && c == ' ')
807                 additionalTempWidth = textWidth(renderText, lastSpace, m_current.offset() + 1 - lastSpace, font, m_width.currentWidth(), isFixedPitch, m_collapseWhiteSpace, wordMeasurement.fallbackFonts, textLayout) - wordTrailingSpaceWidth;
808             else
809                 additionalTempWidth = textWidth(renderText, lastSpace, m_current.offset() - lastSpace, font, m_width.currentWidth(), isFixedPitch, m_collapseWhiteSpace, wordMeasurement.fallbackFonts, textLayout);
810
811             if (wordMeasurement.fallbackFonts.isEmpty() && !fallbackFonts.isEmpty())
812                 wordMeasurement.fallbackFonts.swap(fallbackFonts);
813             fallbackFonts.clear();
814
815             wordMeasurement.width = additionalTempWidth + wordSpacingForWordMeasurement;
816             additionalTempWidth += lastSpaceWordSpacing;
817             m_width.addUncommittedWidth(additionalTempWidth);
818
819             if (m_collapseWhiteSpace && previousCharacterIsSpace && m_currentCharacterIsSpace && additionalTempWidth)
820                 m_width.setTrailingWhitespaceWidth(additionalTempWidth);
821
822             if (!m_appliedStartWidth) {
823                 m_width.addUncommittedWidth(inlineLogicalWidth(m_current.renderer(), true, false));
824                 m_appliedStartWidth = true;
825             }
826
827             applyWordSpacing = wordSpacing && m_currentCharacterIsSpace;
828
829             if (!m_width.committedWidth() && m_autoWrap && !m_width.fitsOnLine())
830                 m_width.fitBelowFloats(m_lineInfo.isFirstLine());
831
832             if (m_autoWrap || breakWords) {
833                 // If we break only after white-space, consider the current character
834                 // as candidate width for this line.
835                 bool lineWasTooWide = false;
836                 if (m_width.fitsOnLine() && m_currentCharacterIsWS && m_currentStyle->breakOnlyAfterWhiteSpace() && !midWordBreak) {
837                     float charWidth = textWidth(renderText, m_current.offset(), 1, font, m_width.currentWidth(), isFixedPitch, m_collapseWhiteSpace, wordMeasurement.fallbackFonts, textLayout) + (applyWordSpacing ? wordSpacing : 0);
838                     // Check if line is too big even without the extra space
839                     // at the end of the line. If it is not, do nothing.
840                     // If the line needs the extra whitespace to be too long,
841                     // then move the line break to the space and skip all
842                     // additional whitespace.
843                     if (!m_width.fitsOnLineIncludingExtraWidth(charWidth)) {
844                         lineWasTooWide = true;
845                         m_lineBreakHistory.push([&](InlineIterator& modifyMe) {
846                             modifyMe.moveTo(m_current.renderer(), m_current.offset(), m_current.nextBreakablePosition());
847                             m_lineBreaker.skipTrailingWhitespace(modifyMe, m_lineInfo);
848                         });
849                     }
850                 }
851                 if (lineWasTooWide || !m_width.fitsOnLine()) {
852                     if (canHyphenate && !m_width.fitsOnLine()) {
853                         m_lineBreakHistory.push([&](InlineIterator& modifyMe) {
854                             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);
855                         });
856                         if (m_lineBreaker.m_hyphenated) {
857                             m_atEnd = true;
858                             return false;
859                         }
860                     }
861                     if (m_lineBreakHistory.atTextParagraphSeparator()) {
862                         if (!stoppedIgnoringSpaces && m_current.offset() > 0)
863                             ensureCharacterGetsLineBox(m_lineMidpointState, m_current);
864                         m_lineBreakHistory.increment();
865                         m_lineInfo.setPreviousLineBrokeCleanly(true);
866                         wordMeasurement.endOffset = m_lineBreakHistory.offset();
867                     }
868                     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)
869                         hyphenated = true;
870                     if (m_lineBreakHistory.offset() && m_lineBreakHistory.offset() != (unsigned)wordMeasurement.endOffset && !wordMeasurement.width) {
871                         if (charWidth) {
872                             wordMeasurement.endOffset = m_lineBreakHistory.offset();
873                             wordMeasurement.width = charWidth;
874                         }
875                     }
876                     // Didn't fit. Jump to the end unless there's still an opportunity to collapse whitespace.
877                     if (m_ignoringSpaces || !m_collapseWhiteSpace || !m_currentCharacterIsSpace || !previousCharacterIsSpace) {
878                         m_atEnd = true;
879                         return false;
880                     }
881                 } else {
882                     if (!betweenWords || (midWordBreak && !m_autoWrap))
883                         m_width.addUncommittedWidth(-additionalTempWidth);
884                     if (hyphenWidth) {
885                         // Subtract the width of the soft hyphen out since we fit on a line.
886                         m_width.addUncommittedWidth(-hyphenWidth);
887                         hyphenWidth = 0;
888                     }
889                 }
890             }
891
892             if (c == '\n' && m_preservesNewline) {
893                 if (!stoppedIgnoringSpaces && m_current.offset())
894                     ensureCharacterGetsLineBox(m_lineMidpointState, m_current);
895                 commitLineBreakAtCurrentWidth(*m_current.renderer(), m_current.offset(), m_current.nextBreakablePosition());
896                 m_lineBreakHistory.increment();
897                 m_lineInfo.setPreviousLineBrokeCleanly(true);
898                 return true;
899             }
900
901             if (m_autoWrap && betweenWords) {
902                 commitLineBreakAtCurrentWidth(*m_current.renderer(), m_current.offset(), m_current.nextBreakablePosition());
903                 wrapW = 0;
904                 // Auto-wrapping text should not wrap in the middle of a word once it has had an
905                 // opportunity to break after a word.
906                 breakWords = false;
907             }
908
909             if (midWordBreak && !U16_IS_TRAIL(c) && !(U_GET_GC_MASK(c) & U_GC_M_MASK)) {
910                 // Remember this as a breakable position in case
911                 // adding the end width forces a break.
912                 m_lineBreakHistory.moveTo(m_current.renderer(), m_current.offset(), m_current.nextBreakablePosition());
913                 midWordBreak &= (breakWords || breakAll);
914             }
915
916             if (betweenWords) {
917                 lastSpaceWordSpacing = applyWordSpacing ? wordSpacing : 0;
918                 wordSpacingForWordMeasurement = (applyWordSpacing && wordMeasurement.width) ? wordSpacing : 0;
919                 lastSpace = m_current.offset();
920             }
921
922             if (!m_ignoringSpaces && m_currentStyle->collapseWhiteSpace()) {
923                 // If we encounter a newline, or if we encounter a
924                 // second space, we need to go ahead and break up this
925                 // run and enter a mode where we start collapsing spaces.
926                 if (m_currentCharacterIsSpace && previousCharacterIsSpace) {
927                     m_ignoringSpaces = true;
928
929                     // We just entered a mode where we are ignoring
930                     // spaces. Create a midpoint to terminate the run
931                     // before the second space.
932                     m_lineMidpointState.startIgnoringSpaces(m_startOfIgnoredSpaces);
933                     m_trailingObjects.updateMidpointsForTrailingBoxes(m_lineMidpointState, InlineIterator(), TrailingObjects::DoNotCollapseFirstSpace);
934                 }
935             }
936         } else if (m_ignoringSpaces) {
937             // Stop ignoring spaces and begin at this
938             // new point.
939             m_ignoringSpaces = false;
940             lastSpaceWordSpacing = applyWordSpacing ? wordSpacing : 0;
941             wordSpacingForWordMeasurement = (applyWordSpacing && wordMeasurements.last().width) ? wordSpacing : 0;
942             lastSpace = m_current.offset(); // e.g., "Foo    goo", don't add in any of the ignored spaces.
943             m_lineMidpointState.stopIgnoringSpaces(InlineIterator(0, m_current.renderer(), m_current.offset()));
944         }
945
946         if (isSVGText && m_current.offset()) {
947             // Force creation of new InlineBoxes for each absolute positioned character (those that start new text chunks).
948             if (downcast<RenderSVGInlineText>(renderText).characterStartsNewTextChunk(m_current.offset()))
949                 ensureCharacterGetsLineBox(m_lineMidpointState, m_current);
950         }
951
952         if (m_currentCharacterIsSpace && !previousCharacterIsSpace) {
953             m_startOfIgnoredSpaces.setRenderer(m_current.renderer());
954             m_startOfIgnoredSpaces.setOffset(m_current.offset());
955             // Spaces after right-aligned text and before a line-break get collapsed away completely so that the trailing
956             // space doesn't seem to push the text out from the right-hand edge.
957             // FIXME: Do this regardless of the container's alignment - will require rebaselining a lot of test results.
958             if (m_nextObject && m_startOfIgnoredSpaces.offset() && m_nextObject->isBR() && (m_blockStyle.textAlign() == RIGHT || m_blockStyle.textAlign() == WEBKIT_RIGHT)) {
959                 m_startOfIgnoredSpaces.setOffset(m_startOfIgnoredSpaces.offset() - 1);
960                 // If there's just a single trailing space start ignoring it now so it collapses away.
961                 if (m_current.offset() == renderText.textLength() - 1)
962                     m_lineMidpointState.startIgnoringSpaces(m_startOfIgnoredSpaces);
963             }
964         }
965
966         if (!m_currentCharacterIsWS && previousCharacterIsWS) {
967             if (m_autoWrap && m_currentStyle->breakOnlyAfterWhiteSpace())
968                 m_lineBreakHistory.moveTo(m_current.renderer(), m_current.offset(), m_current.nextBreakablePosition());
969         }
970
971         if (m_collapseWhiteSpace && m_currentCharacterIsSpace && !m_ignoringSpaces)
972             m_trailingObjects.setTrailingWhitespace(downcast<RenderText>(m_current.renderer()));
973         else if (!m_currentStyle->collapseWhiteSpace() || !m_currentCharacterIsSpace)
974             m_trailingObjects.clear();
975
976         m_atStart = false;
977         nextCharacter(c, lastCharacter, secondToLastCharacter);
978     }
979
980     m_renderTextInfo.m_lineBreakIterator.setPriorContext(lastCharacter, secondToLastCharacter);
981
982     wordMeasurements.grow(wordMeasurements.size() + 1);
983     WordMeasurement& wordMeasurement = wordMeasurements.last();
984     wordMeasurement.renderer = &renderText;
985
986     // IMPORTANT: current.m_pos is > length here!
987     float additionalTempWidth = m_ignoringSpaces ? 0 : textWidth(renderText, lastSpace, m_current.offset() - lastSpace, font, m_width.currentWidth(), isFixedPitch, m_collapseWhiteSpace, wordMeasurement.fallbackFonts, textLayout);
988     wordMeasurement.startOffset = lastSpace;
989     wordMeasurement.endOffset = m_current.offset();
990     wordMeasurement.width = m_ignoringSpaces ? 0 : additionalTempWidth + wordSpacingForWordMeasurement;
991     additionalTempWidth += lastSpaceWordSpacing;
992
993     float inlineLogicalTempWidth = inlineLogicalWidth(m_current.renderer(), !m_appliedStartWidth, m_includeEndWidth);
994     m_width.addUncommittedWidth(additionalTempWidth + inlineLogicalTempWidth);
995
996     if (wordMeasurement.fallbackFonts.isEmpty() && !fallbackFonts.isEmpty())
997         wordMeasurement.fallbackFonts.swap(fallbackFonts);
998     fallbackFonts.clear();
999
1000     if (m_collapseWhiteSpace && m_currentCharacterIsSpace && additionalTempWidth)
1001         m_width.setTrailingWhitespaceWidth(additionalTempWidth, inlineLogicalTempWidth);
1002
1003     m_includeEndWidth = false;
1004
1005     if (!m_width.fitsOnLine()) {
1006         if (canHyphenate) {
1007             m_lineBreakHistory.push([&](InlineIterator& modifyMe) {
1008                 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);
1009             });
1010         }
1011
1012         if (!hyphenated && m_lineBreakHistory.previousInSameNode() == softHyphen && style.hyphens() != HyphensNone) {
1013             hyphenated = true;
1014             m_atEnd = true;
1015         }
1016     }
1017     return false;
1018 }
1019
1020 inline bool textBeginsWithBreakablePosition(RenderText& nextText)
1021 {
1022     UChar c = nextText.characterAt(0);
1023     return c == ' ' || c == '\t' || (c == '\n' && !nextText.preservesNewline());
1024 }
1025
1026 inline bool BreakingContext::canBreakAtThisPosition()
1027 {
1028     // If we are no-wrap and have found a line-breaking opportunity already then we should take it.
1029     if (m_width.committedWidth() && !m_width.fitsOnLine(m_currentCharacterIsSpace) && m_currWS == NOWRAP)
1030         return true;
1031
1032     // Avoid breaking on empty inlines.
1033     if (is<RenderInline>(*m_current.renderer()) && isEmptyInline(downcast<RenderInline>(*m_current.renderer())))
1034         return false;
1035
1036     // Avoid breaking before empty inlines (as long as the current object isn't replaced).
1037     if (!m_current.renderer()->isReplaced() && is<RenderInline>(m_nextObject) && isEmptyInline(downcast<RenderInline>(*m_nextObject)))
1038         return false;
1039
1040     // Return early if we autowrap and the current character is a space as we will always want to break at such a position.
1041     if (m_autoWrap && m_currentCharacterIsSpace)
1042         return true;
1043
1044     if (m_nextObject && m_nextObject->isLineBreakOpportunity())
1045         return m_autoWrap;
1046
1047     bool nextIsAutoWrappingText = is<RenderText>(m_nextObject) && (m_autoWrap || m_nextObject->style().autoWrap());
1048     if (!nextIsAutoWrappingText)
1049         return m_autoWrap;
1050     RenderText& nextRenderText = downcast<RenderText>(*m_nextObject);
1051     bool currentIsTextOrEmptyInline = is<RenderText>(*m_current.renderer()) || (is<RenderInline>(*m_current.renderer()) && isEmptyInline(downcast<RenderInline>(*m_current.renderer())));
1052     if (!currentIsTextOrEmptyInline)
1053         return m_autoWrap && !m_current.renderer()->isRubyRun();
1054
1055     bool canBreakHere = !m_currentCharacterIsSpace && textBeginsWithBreakablePosition(nextRenderText);
1056
1057     // See if attempting to fit below floats creates more available width on the line.
1058     if (!m_width.fitsOnLine() && !m_width.committedWidth())
1059         m_width.fitBelowFloats(m_lineInfo.isFirstLine());
1060
1061     bool canPlaceOnLine = m_width.fitsOnLine() || !m_autoWrapWasEverTrueOnLine;
1062
1063     if (canPlaceOnLine && canBreakHere)
1064         commitLineBreakAtCurrentWidth(nextRenderText);
1065
1066     return canBreakHere;
1067 }
1068
1069 inline void BreakingContext::commitAndUpdateLineBreakIfNeeded()
1070 {
1071     bool checkForBreak = canBreakAtThisPosition();
1072
1073     if (checkForBreak && !m_width.fitsOnLine(m_ignoringSpaces)) {
1074         // if we have floats, try to get below them.
1075         if (m_currentCharacterIsSpace && !m_ignoringSpaces && m_currentStyle->collapseWhiteSpace())
1076             m_trailingObjects.clear();
1077
1078         if (m_width.committedWidth()) {
1079             m_atEnd = true;
1080             return;
1081         }
1082
1083         m_width.fitBelowFloats(m_lineInfo.isFirstLine());
1084
1085         // |width| may have been adjusted because we got shoved down past a float (thus
1086         // giving us more room), so we need to retest, and only jump to
1087         // the end label if we still don't fit on the line. -dwh
1088         if (!m_width.fitsOnLine(m_ignoringSpaces)) {
1089             m_atEnd = true;
1090             return;
1091         }
1092     } else if (m_blockStyle.autoWrap() && !m_width.fitsOnLine() && !m_width.committedWidth()) {
1093         // If the container autowraps but the current child does not then we still need to ensure that it
1094         // wraps and moves below any floats.
1095         m_width.fitBelowFloats(m_lineInfo.isFirstLine());
1096     }
1097
1098     if (!m_current.renderer()->isFloatingOrOutOfFlowPositioned()) {
1099         m_lastObject = m_current.renderer();
1100         if (m_lastObject->isReplaced() && m_autoWrap && !m_lastObject->isRubyRun() && (!m_lastObject->isImage() || m_allowImagesToBreak) && (!is<RenderListMarker>(*m_lastObject) || downcast<RenderListMarker>(*m_lastObject).isInside()))
1101             commitLineBreakAtCurrentWidth(*m_nextObject);
1102     }
1103 }
1104
1105 inline TrailingObjects::CollapseFirstSpaceOrNot checkMidpoints(LineMidpointState& lineMidpointState, const InlineIterator& lBreak)
1106 {
1107     // Check to see if our last midpoint is a start point beyond the line break. If so,
1108     // shave it off the list, and shave off a trailing space if the previous end point doesn't
1109     // preserve whitespace.
1110     if (lBreak.renderer() && lineMidpointState.numMidpoints() && !(lineMidpointState.numMidpoints() % 2)) {
1111         InlineIterator* midpoints = lineMidpointState.midpoints().data();
1112         InlineIterator& endpoint = midpoints[lineMidpointState.numMidpoints() - 2];
1113         const InlineIterator& startpoint = midpoints[lineMidpointState.numMidpoints() - 1];
1114         InlineIterator currpoint = endpoint;
1115         while (!currpoint.atEnd() && currpoint != startpoint && currpoint != lBreak)
1116             currpoint.increment();
1117         if (currpoint == lBreak) {
1118             // We hit the line break before the start point. Shave off the start point.
1119             lineMidpointState.decreaseNumMidpoints();
1120             if (endpoint.renderer()->style().collapseWhiteSpace() && endpoint.renderer()->isText()) {
1121                 endpoint.fastDecrement();
1122                 return TrailingObjects::DoNotCollapseFirstSpace;
1123             }
1124         }
1125     }
1126     return TrailingObjects::CollapseFirstSpace;
1127 }
1128
1129 inline InlineIterator BreakingContext::handleEndOfLine()
1130 {
1131     if (m_lineBreakHistory.current() == m_resolver.position()) {
1132         if (!m_lineBreakHistory.renderer() || !m_lineBreakHistory.renderer()->isBR()) {
1133             // we just add as much as possible
1134             if (m_blockStyle.whiteSpace() == PRE && !m_current.offset())
1135                 commitLineBreakAtCurrentWidth(*m_lastObject, m_lastObject->isText() ? m_lastObject->length() : 0);
1136             else if (m_lineBreakHistory.renderer()) {
1137                 // Don't ever break in the middle of a word if we can help it.
1138                 // There's no room at all. We just have to be on this line,
1139                 // even though we'll spill out.
1140                 commitLineBreakAtCurrentWidth(*m_current.renderer(), m_current.offset());
1141             }
1142         }
1143         // make sure we consume at least one char/object.
1144         if (m_lineBreakHistory.current() == m_resolver.position())
1145             m_lineBreakHistory.increment();
1146     } else if (!m_current.offset() && !m_width.committedWidth() && m_width.uncommittedWidth() && !m_hadUncommittedWidthBeforeCurrent) {
1147         // Do not push the current object to the next line, when this line has some content, but it is still considered empty.
1148         // Empty inline elements like <span></span> can produce such lines and now we just ignore these break opportunities
1149         // at the start of a line, if no width has been committed yet.
1150         // Behave as if it was actually empty and consume at least one object.
1151         m_lineBreakHistory.increment();
1152     }
1153
1154     // Sanity check our midpoints.
1155     TrailingObjects::CollapseFirstSpaceOrNot collapsed = checkMidpoints(m_lineMidpointState, m_lineBreakHistory.current());
1156
1157     m_trailingObjects.updateMidpointsForTrailingBoxes(m_lineMidpointState, m_lineBreakHistory.current(), collapsed);
1158
1159     // We might have made lineBreak an iterator that points past the end
1160     // of the object. Do this adjustment to make it point to the start
1161     // of the next object instead to avoid confusing the rest of the
1162     // code.
1163     if (m_lineBreakHistory.offset()) {
1164         m_lineBreakHistory.update([](InlineIterator& modifyMe) {
1165             modifyMe.setOffset(modifyMe.offset() - 1);
1166             modifyMe.increment();
1167         });
1168     }
1169
1170 #if ENABLE(CSS_TRAILING_WORD)
1171     if (m_blockStyle.trailingWord() == TrailingWord::PartiallyBalanced)
1172         return optimalLineBreakLocationForTrailingWord();
1173 #endif
1174     return m_lineBreakHistory.current();
1175 }
1176
1177 #if ENABLE(CSS_TRAILING_WORD)
1178 inline InlineIterator BreakingContext::optimalLineBreakLocationForTrailingWord()
1179 {
1180     const unsigned longTrailingWordLength = 20;
1181     const float optimalTrailingLineRatio = 0.1;
1182     InlineIterator lineBreak = m_lineBreakHistory.current();
1183     if (!lineBreak.renderer() || !m_lineInfo.isFirstLine() || bidiNextSkippingEmptyInlines(*lineBreak.root(), lineBreak.renderer()) || !is<RenderText>(lineBreak.renderer()))
1184         return lineBreak;
1185     RenderText& renderText = downcast<RenderText>(*lineBreak.renderer());
1186     // Don't even bother measuring if our remaining line has many characters
1187     if (renderText.textLength() == lineBreak.offset() || renderText.textLength() - lineBreak.offset() > longTrailingWordLength)
1188         return lineBreak;
1189     bool isLooseCJKMode = m_renderTextInfo.m_text != &renderText && m_renderTextInfo.m_lineBreakIterator.isLooseCJKMode();
1190     bool breakNBSP = m_autoWrap && m_currentStyle->nbspMode() == SPACE;
1191     int nextBreakablePosition = lineBreak.nextBreakablePosition();
1192     isBreakable(m_renderTextInfo.m_lineBreakIterator, lineBreak.offset() + 1, nextBreakablePosition, breakNBSP, isLooseCJKMode);
1193     if (nextBreakablePosition < 0 || static_cast<unsigned>(nextBreakablePosition) != renderText.textLength())
1194         return lineBreak;
1195     const RenderStyle& style = lineStyle(renderText, m_lineInfo);
1196     const FontCascade& font = style.fontCascade();
1197     HashSet<const Font*> dummyFonts;
1198     InlineIterator best = lineBreak;
1199     for (size_t i = 1; i < m_lineBreakHistory.historyLength(); ++i) {
1200         const InlineIterator& candidate = m_lineBreakHistory.get(i);
1201         if (candidate.renderer() != lineBreak.renderer())
1202             return best;
1203         float width = textWidth(renderText, candidate.offset(), renderText.textLength() - candidate.offset(), font, 0, font.isFixedPitch(), m_collapseWhiteSpace, dummyFonts);
1204         if (width > m_width.availableWidth())
1205             return best;
1206         if (width / m_width.availableWidth() > optimalTrailingLineRatio) // Subsequent line is long enough
1207             return candidate;
1208         best = candidate;
1209     }
1210     return best;
1211 }
1212 #endif
1213
1214 }
1215
1216 #endif // BreakingContext_h