eb45013c84fad192a66e4a1315a1c9980c998a22
[WebKit-https.git] / Source / WebCore / rendering / SimpleLineLayout.cpp
1 /*
2  * Copyright (C) 2013 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "SimpleLineLayout.h"
28
29 #include "FontCache.h"
30 #include "Frame.h"
31 #include "GraphicsContext.h"
32 #include "HTMLTextFormControlElement.h"
33 #include "HitTestLocation.h"
34 #include "HitTestRequest.h"
35 #include "HitTestResult.h"
36 #include "InlineTextBox.h"
37 #include "LineWidth.h"
38 #include "Logging.h"
39 #include "PaintInfo.h"
40 #include "RenderBlockFlow.h"
41 #include "RenderChildIterator.h"
42 #include "RenderLineBreak.h"
43 #include "RenderStyle.h"
44 #include "RenderText.h"
45 #include "RenderTextControl.h"
46 #include "RenderView.h"
47 #include "Settings.h"
48 #include "SimpleLineLayoutFlowContents.h"
49 #include "SimpleLineLayoutFunctions.h"
50 #include "SimpleLineLayoutTextFragmentIterator.h"
51 #include "Text.h"
52 #include "TextPaintStyle.h"
53 #include "TextStream.h"
54
55 namespace WebCore {
56 namespace SimpleLineLayout {
57
58 #ifndef NDEBUG
59 void printSimpleLineLayoutCoverage();
60 void printSimpleLineLayoutBlockList();
61 void toggleSimpleLineLayout();
62 #endif
63
64 enum AvoidanceReason_ : uint64_t {
65     FlowIsInsideRegion                    = 1LLU  << 0,
66     FlowHasHorizonalWritingMode           = 1LLU  << 1,
67     FlowHasOutline                        = 1LLU  << 2,
68     FlowIsRuby                            = 1LLU  << 3,
69     FlowIsPaginated                       = 1LLU  << 4,
70     FlowHasTextOverflow                   = 1LLU  << 5,
71     FlowIsDepricatedFlexBox               = 1LLU  << 6,
72     FlowParentIsPlaceholderElement        = 1LLU  << 7,
73     FlowParentIsTextAreaWithWrapping      = 1LLU  << 8,
74     FlowHasNonSupportedChild              = 1LLU  << 9,
75     FlowHasUnsupportedFloat               = 1LLU  << 10,
76     FlowHasUnsupportedUnderlineDecoration = 1LLU  << 11,
77     FlowHasJustifiedNonLatinText          = 1LLU  << 12,
78     FlowHasOverflowVisible                = 1LLU  << 13,
79     FlowIsNotLTR                          = 1LLU  << 14,
80     FlowHasLineBoxContainProperty         = 1LLU  << 15,
81     FlowIsNotTopToBottom                  = 1LLU  << 16,
82     FlowHasLineBreak                      = 1LLU  << 17,
83     FlowHasNonNormalUnicodeBiDi           = 1LLU  << 18,
84     FlowHasRTLOrdering                    = 1LLU  << 19,
85     FlowHasLineAlignEdges                 = 1LLU  << 20,
86     FlowHasLineSnap                       = 1LLU  << 21,
87     FlowHasHypensAuto                     = 1LLU  << 22,
88     FlowHasTextEmphasisFillOrMark         = 1LLU  << 23,
89     FlowHasTextShadow                     = 1LLU  << 24,
90     FlowHasPseudoFirstLine                = 1LLU  << 25,
91     FlowHasPseudoFirstLetter              = 1LLU  << 26,
92     FlowHasTextCombine                    = 1LLU  << 27,
93     FlowHasTextFillBox                    = 1LLU  << 28,
94     FlowHasBorderFitLines                 = 1LLU  << 29,
95     FlowHasNonAutoLineBreak               = 1LLU  << 30,
96     FlowHasNonAutoTrailingWord            = 1LLU  << 31,
97     FlowHasSVGFont                        = 1LLU  << 32,
98     FlowTextIsEmpty                       = 1LLU  << 33,
99     FlowTextHasNoBreakSpace               = 1LLU  << 34,
100     FlowTextHasSoftHyphen                 = 1LLU  << 35,
101     FlowTextHasDirectionCharacter         = 1LLU  << 36,
102     FlowIsMissingPrimaryFont              = 1LLU  << 37,
103     FlowFontIsMissingGlyph                = 1LLU  << 38,
104     FlowTextIsCombineText                 = 1LLU  << 39,
105     FlowTextIsRenderCounter               = 1LLU  << 40,
106     FlowTextIsRenderQuote                 = 1LLU  << 41,
107     FlowTextIsTextFragment                = 1LLU  << 42,
108     FlowTextIsSVGInlineText               = 1LLU  << 43,
109     FlowFontIsNotSimple                   = 1LLU  << 44,
110     FeatureIsDisabled                     = 1LLU  << 45,
111     FlowHasNoParent                       = 1LLU  << 46,
112     FlowHasNoChild                        = 1LLU  << 47,
113     FlowChildIsSelected                   = 1LLU  << 48,
114     FlowHasHangingPunctuation             = 1LLU  << 49,
115     EndOfReasons                          = 1LLU  << 50
116 };
117 const unsigned NoReason = 0;
118
119 typedef uint64_t AvoidanceReason;
120 typedef uint64_t AvoidanceReasonFlags;
121
122 enum class IncludeReasons { First , All };
123
124 #ifndef NDEBUG
125 #define SET_REASON_AND_RETURN_IF_NEEDED(reason, reasons, includeReasons) { \
126         reasons |= reason; \
127         if (includeReasons == IncludeReasons::First) \
128             return reasons; \
129     }
130 #else
131 #define SET_REASON_AND_RETURN_IF_NEEDED(reason, reasons, includeReasons) { \
132         ASSERT_UNUSED(includeReasons, includeReasons == IncludeReasons::First); \
133         reasons |= reason; \
134         return reasons; \
135     }
136 #endif
137
138 template <typename CharacterType>
139 static AvoidanceReasonFlags canUseForText(const CharacterType* text, unsigned length, const Font& font, IncludeReasons includeReasons)
140 {
141     AvoidanceReasonFlags reasons = { };
142     // FIXME: <textarea maxlength=0> generates empty text node.
143     if (!length)
144         SET_REASON_AND_RETURN_IF_NEEDED(FlowTextIsEmpty, reasons, includeReasons);
145
146     for (unsigned i = 0; i < length; ++i) {
147         UChar character = text[i];
148         if (character == ' ')
149             continue;
150
151         // These would be easy to support.
152         if (character == noBreakSpace)
153             SET_REASON_AND_RETURN_IF_NEEDED(FlowTextHasNoBreakSpace, reasons, includeReasons);
154         if (character == softHyphen)
155             SET_REASON_AND_RETURN_IF_NEEDED(FlowTextHasSoftHyphen, reasons, includeReasons);
156
157         UCharDirection direction = u_charDirection(character);
158         if (direction == U_RIGHT_TO_LEFT || direction == U_RIGHT_TO_LEFT_ARABIC
159             || direction == U_RIGHT_TO_LEFT_EMBEDDING || direction == U_RIGHT_TO_LEFT_OVERRIDE
160             || direction == U_LEFT_TO_RIGHT_EMBEDDING || direction == U_LEFT_TO_RIGHT_OVERRIDE
161             || direction == U_POP_DIRECTIONAL_FORMAT || direction == U_BOUNDARY_NEUTRAL)
162             SET_REASON_AND_RETURN_IF_NEEDED(FlowTextHasDirectionCharacter, reasons, includeReasons);
163
164         if (!font.glyphForCharacter(character))
165             SET_REASON_AND_RETURN_IF_NEEDED(FlowFontIsMissingGlyph, reasons, includeReasons);
166     }
167     return reasons;
168 }
169
170 static AvoidanceReasonFlags canUseForText(const RenderText& textRenderer, const Font& font, IncludeReasons includeReasons)
171 {
172     if (textRenderer.is8Bit())
173         return canUseForText(textRenderer.characters8(), textRenderer.textLength(), font, includeReasons);
174     return canUseForText(textRenderer.characters16(), textRenderer.textLength(), font, includeReasons);
175 }
176
177 static AvoidanceReasonFlags canUseForFontAndText(const RenderBlockFlow& flow, IncludeReasons includeReasons)
178 {
179     AvoidanceReasonFlags reasons = { };
180     // We assume that all lines have metrics based purely on the primary font.
181     const auto& style = flow.style();
182     auto& primaryFont = style.fontCascade().primaryFont();
183     if (primaryFont.isLoading())
184         SET_REASON_AND_RETURN_IF_NEEDED(FlowIsMissingPrimaryFont, reasons, includeReasons);
185
186     for (const auto& textRenderer : childrenOfType<RenderText>(flow)) {
187         if (style.textAlign() == JUSTIFY && !textRenderer.originalText().containsOnlyLatin1())
188             SET_REASON_AND_RETURN_IF_NEEDED(FlowHasJustifiedNonLatinText, reasons, includeReasons);
189         if (textRenderer.isCombineText())
190             SET_REASON_AND_RETURN_IF_NEEDED(FlowTextIsCombineText, reasons, includeReasons);
191         if (textRenderer.isCounter())
192             SET_REASON_AND_RETURN_IF_NEEDED(FlowTextIsRenderCounter, reasons, includeReasons);
193         if (textRenderer.isQuote())
194             SET_REASON_AND_RETURN_IF_NEEDED(FlowTextIsRenderQuote, reasons, includeReasons);
195         if (textRenderer.isTextFragment())
196             SET_REASON_AND_RETURN_IF_NEEDED(FlowTextIsTextFragment, reasons, includeReasons);
197         if (textRenderer.isSVGInlineText())
198             SET_REASON_AND_RETURN_IF_NEEDED(FlowTextIsSVGInlineText, reasons, includeReasons);
199         if (style.fontCascade().codePath(TextRun(textRenderer.text())) != FontCascade::Simple)
200             SET_REASON_AND_RETURN_IF_NEEDED(FlowFontIsNotSimple, reasons, includeReasons);
201
202         auto textReasons = canUseForText(textRenderer, primaryFont, includeReasons);
203         if (textReasons != NoReason)
204             SET_REASON_AND_RETURN_IF_NEEDED(textReasons, reasons, includeReasons);
205     }
206     return reasons;
207 }
208
209 static AvoidanceReasonFlags canUseForStyle(const RenderStyle& style, IncludeReasons includeReasons)
210 {
211     AvoidanceReasonFlags reasons = { };
212     if (style.textOverflow())
213         SET_REASON_AND_RETURN_IF_NEEDED(FlowHasTextOverflow, reasons, includeReasons);
214     if ((style.textDecorationsInEffect() & TextDecorationUnderline) && style.textUnderlinePosition() == TextUnderlinePositionUnder)
215         SET_REASON_AND_RETURN_IF_NEEDED(FlowHasUnsupportedUnderlineDecoration, reasons, includeReasons);
216     // Non-visible overflow should be pretty easy to support.
217     if (style.overflowX() != OVISIBLE || style.overflowY() != OVISIBLE)
218         SET_REASON_AND_RETURN_IF_NEEDED(FlowHasOverflowVisible, reasons, includeReasons);
219     if (!style.isLeftToRightDirection())
220         SET_REASON_AND_RETURN_IF_NEEDED(FlowIsNotLTR, reasons, includeReasons);
221     if (style.lineBoxContain() != RenderStyle::initialLineBoxContain())
222         SET_REASON_AND_RETURN_IF_NEEDED(FlowHasLineBoxContainProperty, reasons, includeReasons);
223     if (style.writingMode() != TopToBottomWritingMode)
224         SET_REASON_AND_RETURN_IF_NEEDED(FlowIsNotTopToBottom, reasons, includeReasons);
225     if (style.lineBreak() != LineBreakAuto)
226         SET_REASON_AND_RETURN_IF_NEEDED(FlowHasLineBreak, reasons, includeReasons);
227     if (style.unicodeBidi() != UBNormal)
228         SET_REASON_AND_RETURN_IF_NEEDED(FlowHasNonNormalUnicodeBiDi, reasons, includeReasons);
229     if (style.rtlOrdering() != LogicalOrder)
230         SET_REASON_AND_RETURN_IF_NEEDED(FlowHasRTLOrdering, reasons, includeReasons);
231     if (style.lineAlign() != LineAlignNone)
232         SET_REASON_AND_RETURN_IF_NEEDED(FlowHasLineAlignEdges, reasons, includeReasons);
233     if (style.lineSnap() != LineSnapNone)
234         SET_REASON_AND_RETURN_IF_NEEDED(FlowHasLineSnap, reasons, includeReasons);
235     if (style.hyphens() == HyphensAuto)
236         SET_REASON_AND_RETURN_IF_NEEDED(FlowHasHypensAuto, reasons, includeReasons);
237     if (style.textEmphasisFill() != TextEmphasisFillFilled || style.textEmphasisMark() != TextEmphasisMarkNone)
238         SET_REASON_AND_RETURN_IF_NEEDED(FlowHasTextEmphasisFillOrMark, reasons, includeReasons);
239     if (style.textShadow())
240         SET_REASON_AND_RETURN_IF_NEEDED(FlowHasTextShadow, reasons, includeReasons);
241     if (style.hasPseudoStyle(FIRST_LINE))
242         SET_REASON_AND_RETURN_IF_NEEDED(FlowHasPseudoFirstLine, reasons, includeReasons);
243     if (style.hasPseudoStyle(FIRST_LETTER))
244         SET_REASON_AND_RETURN_IF_NEEDED(FlowHasPseudoFirstLetter, reasons, includeReasons);
245     if (style.hasTextCombine())
246         SET_REASON_AND_RETURN_IF_NEEDED(FlowHasTextCombine, reasons, includeReasons);
247     if (style.backgroundClip() == TextFillBox)
248         SET_REASON_AND_RETURN_IF_NEEDED(FlowHasTextFillBox, reasons, includeReasons);
249     if (style.borderFit() == BorderFitLines)
250         SET_REASON_AND_RETURN_IF_NEEDED(FlowHasBorderFitLines, reasons, includeReasons);
251     if (style.lineBreak() != LineBreakAuto)
252         SET_REASON_AND_RETURN_IF_NEEDED(FlowHasNonAutoLineBreak, reasons, includeReasons);
253 #if ENABLE(CSS_TRAILING_WORD)
254     if (style.trailingWord() != TrailingWord::Auto)
255         SET_REASON_AND_RETURN_IF_NEEDED(FlowHasNonAutoTrailingWord, reasons, includeReasons);
256 #endif
257     return reasons;
258 }
259
260 static AvoidanceReasonFlags canUseForWithReason(const RenderBlockFlow& flow, IncludeReasons includeReasons)
261 {
262 #ifndef NDEBUG
263     static std::once_flag onceFlag;
264     std::call_once(onceFlag, [] {
265         registerNotifyCallback("com.apple.WebKit.showSimpleLineLayoutCoverage", printSimpleLineLayoutCoverage);
266         registerNotifyCallback("com.apple.WebKit.showSimpleLineLayoutReasons", printSimpleLineLayoutBlockList);
267         registerNotifyCallback("com.apple.WebKit.toggleSimpleLineLayout", toggleSimpleLineLayout);
268     });
269 #endif
270     AvoidanceReasonFlags reasons = { };
271     if (!flow.settings().simpleLineLayoutEnabled())
272         SET_REASON_AND_RETURN_IF_NEEDED(FeatureIsDisabled, reasons, includeReasons);
273     if (!flow.parent())
274         SET_REASON_AND_RETURN_IF_NEEDED(FlowHasNoParent, reasons, includeReasons);
275     if (!flow.firstChild())
276         SET_REASON_AND_RETURN_IF_NEEDED(FlowHasNoChild, reasons, includeReasons);
277     if (flow.flowThreadState() != RenderObject::NotInsideFlowThread)
278         SET_REASON_AND_RETURN_IF_NEEDED(FlowIsInsideRegion, reasons, includeReasons);
279     if (!flow.isHorizontalWritingMode())
280         SET_REASON_AND_RETURN_IF_NEEDED(FlowHasHorizonalWritingMode, reasons, includeReasons);
281     if (flow.hasOutline())
282         SET_REASON_AND_RETURN_IF_NEEDED(FlowHasOutline, reasons, includeReasons);
283     if (flow.isRubyText() || flow.isRubyBase())
284         SET_REASON_AND_RETURN_IF_NEEDED(FlowIsRuby, reasons, includeReasons);
285     if (flow.style().hangingPunctuation() != NoHangingPunctuation)
286         SET_REASON_AND_RETURN_IF_NEEDED(FlowHasHangingPunctuation, reasons, includeReasons);
287     
288     // Printing does pagination without a flow thread.
289     if (flow.document().paginated())
290         SET_REASON_AND_RETURN_IF_NEEDED(FlowIsPaginated, reasons, includeReasons);
291     if (flow.firstLineBlock())
292         SET_REASON_AND_RETURN_IF_NEEDED(FlowHasPseudoFirstLine, reasons, includeReasons);
293     if (flow.isAnonymousBlock() && flow.parent()->style().textOverflow())
294         SET_REASON_AND_RETURN_IF_NEEDED(FlowHasTextOverflow, reasons, includeReasons);
295     if (flow.parent()->isDeprecatedFlexibleBox())
296         SET_REASON_AND_RETURN_IF_NEEDED(FlowIsDepricatedFlexBox, reasons, includeReasons);
297     // FIXME: Placeholders do something strange.
298     if (is<RenderTextControl>(*flow.parent()) && downcast<RenderTextControl>(*flow.parent()).textFormControlElement().placeholderElement())
299         SET_REASON_AND_RETURN_IF_NEEDED(FlowParentIsPlaceholderElement, reasons, includeReasons);
300     // FIXME: Implementation of wrap=hard looks into lineboxes.
301     if (flow.parent()->isTextArea() && flow.parent()->element()->hasAttributeWithoutSynchronization(HTMLNames::wrapAttr))
302         SET_REASON_AND_RETURN_IF_NEEDED(FlowParentIsTextAreaWithWrapping, reasons, includeReasons);
303     // This currently covers <blockflow>#text</blockflow>, <blockflow>#text<br></blockflow> and mutiple (sibling) RenderText cases.
304     // The <blockflow><inline>#text</inline></blockflow> case is also popular and should be relatively easy to cover.
305     for (const auto* child = flow.firstChild(); child;) {
306         if (child->selectionState() != RenderObject::SelectionNone)
307             SET_REASON_AND_RETURN_IF_NEEDED(FlowChildIsSelected, reasons, includeReasons);
308         if (is<RenderText>(*child)) {
309             child = child->nextSibling();
310             continue;
311         }
312         if (is<RenderLineBreak>(child) && !downcast<RenderLineBreak>(*child).isWBR() && child->style().clear() == CNONE) {
313             child = child->nextSibling();
314             continue;
315         }
316         SET_REASON_AND_RETURN_IF_NEEDED(FlowHasNonSupportedChild, reasons, includeReasons);
317         break;
318     }
319     auto styleReasons = canUseForStyle(flow.style(), includeReasons);
320     if (styleReasons != NoReason)
321         SET_REASON_AND_RETURN_IF_NEEDED(styleReasons, reasons, includeReasons);
322     // We can't use the code path if any lines would need to be shifted below floats. This is because we don't keep per-line y coordinates.
323     if (flow.containsFloats()) {
324         float minimumWidthNeeded = std::numeric_limits<float>::max();
325         for (const auto& textRenderer : childrenOfType<RenderText>(flow)) {
326             minimumWidthNeeded = std::min(minimumWidthNeeded, textRenderer.minLogicalWidth());
327
328             for (auto& floatingObject : *flow.floatingObjectSet()) {
329                 ASSERT(floatingObject);
330                 // if a float has a shape, we cannot tell if content will need to be shifted until after we lay it out,
331                 // since the amount of space is not uniform for the height of the float.
332                 if (floatingObject->renderer().shapeOutsideInfo())
333                     SET_REASON_AND_RETURN_IF_NEEDED(FlowHasUnsupportedFloat, reasons, includeReasons);
334                 float availableWidth = flow.availableLogicalWidthForLine(floatingObject->y(), DoNotIndentText);
335                 if (availableWidth < minimumWidthNeeded)
336                     SET_REASON_AND_RETURN_IF_NEEDED(FlowHasUnsupportedFloat, reasons, includeReasons);
337             }
338         }
339     }
340     auto fontAndTextReasons = canUseForFontAndText(flow, includeReasons);
341     if (fontAndTextReasons != NoReason)
342         SET_REASON_AND_RETURN_IF_NEEDED(fontAndTextReasons, reasons, includeReasons);
343     return reasons;
344 }
345
346 bool canUseFor(const RenderBlockFlow& flow)
347 {
348     return canUseForWithReason(flow, IncludeReasons::First) == NoReason;
349 }
350
351 static float computeLineLeft(ETextAlign textAlign, float availableWidth, float committedWidth, float logicalLeftOffset)
352 {
353     float remainingWidth = availableWidth - committedWidth;
354     float left = logicalLeftOffset;
355     switch (textAlign) {
356     case LEFT:
357     case WEBKIT_LEFT:
358     case TASTART:
359         return left;
360     case RIGHT:
361     case WEBKIT_RIGHT:
362     case TAEND:
363         return left + std::max<float>(remainingWidth, 0);
364     case CENTER:
365     case WEBKIT_CENTER:
366         return left + std::max<float>(remainingWidth / 2, 0);
367     case JUSTIFY:
368         ASSERT_NOT_REACHED();
369         break;
370     }
371     ASSERT_NOT_REACHED();
372     return 0;
373 }
374
375 static void revertRuns(Layout::RunVector& runs, unsigned length, float width)
376 {
377     while (length) {
378         ASSERT(runs.size());
379         Run& lastRun = runs.last();
380         unsigned lastRunLength = lastRun.end - lastRun.start;
381         if (lastRunLength > length) {
382             lastRun.logicalRight -= width;
383             lastRun.end -= length;
384             break;
385         }
386         length -= lastRunLength;
387         width -= (lastRun.logicalRight - lastRun.logicalLeft);
388         runs.removeLast();
389     }
390 }
391
392 class LineState {
393 public:
394     void setAvailableWidth(float width) { m_availableWidth = width; }
395     void setCollapedWhitespaceWidth(float width) { m_collapsedWhitespaceWidth = width; }
396     void setLogicalLeftOffset(float offset) { m_logicalLeftOffset = offset; }
397     void setOverflowedFragment(const TextFragmentIterator::TextFragment& fragment) { m_overflowedFragment = fragment; }
398
399     float availableWidth() const { return m_availableWidth; }
400     float logicalLeftOffset() const { return m_logicalLeftOffset; }
401     const TextFragmentIterator::TextFragment& overflowedFragment() const { return m_overflowedFragment; }
402     bool hasTrailingWhitespace() const { return m_trailingWhitespaceLength; }
403     std::optional<TextFragmentIterator::TextFragment> lastFragment() const
404     {
405         if (m_fragments.size())
406             return m_fragments.last();
407         return std::nullopt;
408     }
409     bool isWhitespaceOnly() const { return m_trailingWhitespaceWidth && m_runsWidth == m_trailingWhitespaceWidth; }
410     bool fits(float extra) const { return m_availableWidth >= m_runsWidth + extra; }
411     bool firstCharacterFits() const { return m_firstCharacterFits; }
412     float width() const { return m_runsWidth; }
413     std::pair<unsigned, bool> expansionOpportunityCount(unsigned from, unsigned to) const
414     {
415         // linebreak runs are special.
416         if (from == to)
417             return std::make_pair(0, false);
418         unsigned expansionOpportunityCount = 0;
419         auto previousFragmentType = TextFragmentIterator::TextFragment::ContentEnd;
420         for (const auto& fragment : m_fragments) {
421             if (fragment.end() <= from)
422                 continue;
423             auto currentFragmentType = fragment.type();
424             auto expansionOpportunity = this->expansionOpportunity(currentFragmentType, previousFragmentType);
425             if (expansionOpportunity)
426                 ++expansionOpportunityCount;
427             previousFragmentType = currentFragmentType;
428             if (fragment.end() >= to)
429                 return std::make_pair(expansionOpportunityCount, expansionOpportunity);
430         }
431         ASSERT_NOT_REACHED();
432         return std::make_pair(expansionOpportunityCount, false);
433     }
434
435     bool isEmpty() const
436     {
437         if (!m_fragments.size())
438             return true;
439         if (!m_lastCompleteFragment.isEmpty())
440             return false;
441         return m_fragments.last().overlapsToNextRenderer();
442     }
443
444     void appendFragmentAndCreateRunIfNeeded(const TextFragmentIterator::TextFragment& fragment, Layout::RunVector& runs)
445     {
446         // Adjust end position while collapsing.
447         unsigned endPosition = fragment.isCollapsed() ? fragment.start() + 1 : fragment.end();
448         // New line needs new run.
449         if (!m_runsWidth)
450             runs.append(Run(fragment.start(), endPosition, m_runsWidth, m_runsWidth + fragment.width(), false));
451         else {
452             const auto& lastFragment = m_fragments.last();
453             // Advance last completed fragment when the previous fragment is all set (including multiple parts across renderers)
454             if ((lastFragment.type() != fragment.type()) || !lastFragment.overlapsToNextRenderer())
455                 m_lastCompleteFragment = lastFragment;
456             // Collapse neighbouring whitespace, if they are across multiple renderers and are not collapsed yet.
457             if (lastFragment.isCollapsible() && fragment.isCollapsible()) {
458                 ASSERT(lastFragment.isLastInRenderer());
459                 if (!lastFragment.isCollapsed()) {
460                     // Line width needs to be reset so that now it takes collapsing into consideration.
461                     m_runsWidth -= (lastFragment.width() - m_collapsedWhitespaceWidth);
462                 }
463                 // This fragment is collapsed completely. No run is needed.
464                 return;
465             }
466             if (lastFragment.isLastInRenderer() || lastFragment.isCollapsed())
467                 runs.append(Run(fragment.start(), endPosition, m_runsWidth, m_runsWidth + fragment.width(), false));
468             else {
469                 Run& lastRun = runs.last();
470                 lastRun.end = endPosition;
471                 lastRun.logicalRight += fragment.width();
472             }
473         }
474         m_fragments.append(fragment);
475         m_runsWidth += fragment.width();
476
477         if (fragment.type() == TextFragmentIterator::TextFragment::Whitespace) {
478             m_trailingWhitespaceLength += endPosition - fragment.start();
479             m_trailingWhitespaceWidth += fragment.width();
480         } else {
481             m_trailingWhitespaceLength = 0;
482             m_trailingWhitespaceWidth = 0;
483         }
484
485         if (!m_firstCharacterFits)
486             m_firstCharacterFits = fragment.start() + 1 > endPosition || m_runsWidth <= m_availableWidth;
487     }
488
489     TextFragmentIterator::TextFragment revertToLastCompleteFragment(Layout::RunVector& runs)
490     {
491         ASSERT(m_fragments.size());
492         unsigned revertLength = 0;
493         float revertWidth = 0;
494         while (m_fragments.size()) {
495             const auto& current = m_fragments.last();
496             if (current == m_lastCompleteFragment)
497                 break;
498             revertLength += current.end() - current.start();
499             revertWidth += current.width();
500             m_fragments.removeLast();
501         }
502         m_runsWidth -= revertWidth;
503         if (revertLength)
504             revertRuns(runs, revertLength, revertWidth);
505         return m_lastCompleteFragment;
506     }
507
508     void removeTrailingWhitespace(Layout::RunVector& runs)
509     {
510         if (!m_trailingWhitespaceLength)
511             return;
512         revertRuns(runs, m_trailingWhitespaceLength, m_trailingWhitespaceWidth);
513         m_runsWidth -= m_trailingWhitespaceWidth;
514         ASSERT(m_fragments.last().type() == TextFragmentIterator::TextFragment::Whitespace);
515         while (m_fragments.size()) {
516             const auto& current = m_fragments.last();
517             if (current.type() != TextFragmentIterator::TextFragment::Whitespace)
518                 break;
519 #if !ASSERT_DISABLED
520             m_trailingWhitespaceLength -= (current.isCollapsed() ? 1 : current.end() - current.start());
521             m_trailingWhitespaceWidth -= current.width();
522 #endif
523             m_fragments.removeLast();
524         }
525 #if !ASSERT_DISABLED
526         ASSERT(!m_trailingWhitespaceLength);
527         ASSERT(!m_trailingWhitespaceWidth);
528 #endif
529         m_trailingWhitespaceLength = 0;
530         m_trailingWhitespaceWidth = 0;
531     }
532
533 private:
534     bool expansionOpportunity(TextFragmentIterator::TextFragment::Type currentFragmentType, TextFragmentIterator::TextFragment::Type previousFragmentType) const
535     {
536         return (currentFragmentType == TextFragmentIterator::TextFragment::Whitespace
537             || (currentFragmentType == TextFragmentIterator::TextFragment::NonWhitespace && previousFragmentType == TextFragmentIterator::TextFragment::NonWhitespace));
538     }
539
540     float m_availableWidth { 0 };
541     float m_logicalLeftOffset { 0 };
542     TextFragmentIterator::TextFragment m_overflowedFragment;
543     float m_runsWidth { 0 };
544     TextFragmentIterator::TextFragment m_lastCompleteFragment;
545     float m_trailingWhitespaceWidth { 0 }; // Use this to remove trailing whitespace without re-mesuring the text.
546     unsigned m_trailingWhitespaceLength { 0 };
547     float m_collapsedWhitespaceWidth { 0 };
548     // Having one character on the line does not necessarily mean it actually fits.
549     // First character of the first fragment might be forced on to the current line even if it does not fit.
550     bool m_firstCharacterFits { false };
551     Vector<TextFragmentIterator::TextFragment> m_fragments;
552 };
553
554 class FragmentForwardIterator : public std::iterator<std::forward_iterator_tag, unsigned> {
555 public:
556     FragmentForwardIterator(unsigned fragmentIndex)
557         : m_fragmentIndex(fragmentIndex)
558     {
559     }
560
561     FragmentForwardIterator& operator++()
562     {
563         ++m_fragmentIndex;
564         return *this;
565     }
566
567     bool operator!=(const FragmentForwardIterator& other) const { return m_fragmentIndex != other.m_fragmentIndex; }
568     bool operator==(const FragmentForwardIterator& other) const { return m_fragmentIndex == other.m_fragmentIndex; }
569     unsigned operator*() const { return m_fragmentIndex; }
570
571 private:
572     unsigned m_fragmentIndex { 0 };
573 };
574
575 static FragmentForwardIterator begin(const TextFragmentIterator::TextFragment& fragment)  { return FragmentForwardIterator(fragment.start()); }
576 static FragmentForwardIterator end(const TextFragmentIterator::TextFragment& fragment)  { return FragmentForwardIterator(fragment.end()); }
577
578 static bool preWrap(const TextFragmentIterator::Style& style)
579 {
580     return style.wrapLines && !style.collapseWhitespace;
581 }
582     
583 static void removeTrailingWhitespace(LineState& lineState, Layout::RunVector& runs, const TextFragmentIterator& textFragmentIterator)
584 {
585     if (!lineState.hasTrailingWhitespace())
586         return;
587
588     // Remove collapsed whitespace, or non-collapsed pre-wrap whitespace, unless it's the only content on the line -so removing the whitesapce would produce an empty line.
589     const auto& style = textFragmentIterator.style();
590     bool collapseWhitespace = style.collapseWhitespace | preWrap(style);
591     if (!collapseWhitespace)
592         return;
593
594     if (preWrap(style) && lineState.isWhitespaceOnly())
595         return;
596
597     lineState.removeTrailingWhitespace(runs);
598 }
599
600 static void updateLineConstrains(const RenderBlockFlow& flow, LineState& line, bool isFirstLine)
601 {
602     bool shouldApplyTextIndent = !flow.isAnonymous() || flow.parent()->firstChild() == &flow;
603     LayoutUnit height = flow.logicalHeight();
604     LayoutUnit logicalHeight = flow.minLineHeightForReplacedRenderer(false, 0);
605     float logicalRightOffset = flow.logicalRightOffsetForLine(height, DoNotIndentText, logicalHeight);
606     line.setLogicalLeftOffset(flow.logicalLeftOffsetForLine(height, DoNotIndentText, logicalHeight) +
607         (shouldApplyTextIndent && isFirstLine ? flow.textIndentOffset() : LayoutUnit(0)));
608     line.setAvailableWidth(std::max<float>(0, logicalRightOffset - line.logicalLeftOffset()));
609 }
610
611 static TextFragmentIterator::TextFragment splitFragmentToFitLine(TextFragmentIterator::TextFragment& fragmentToSplit, float availableWidth, bool keepAtLeastOneCharacter, const TextFragmentIterator& textFragmentIterator)
612 {
613     // FIXME: add surrogate pair support.
614     unsigned start = fragmentToSplit.start();
615     auto it = std::upper_bound(begin(fragmentToSplit), end(fragmentToSplit), availableWidth, [&textFragmentIterator, start](float availableWidth, unsigned index) {
616         // FIXME: use the actual left position of the line (instead of 0) to calculated width. It might give false width for tab characters.
617         return availableWidth < textFragmentIterator.textWidth(start, index + 1, 0);
618     });
619     unsigned splitPosition = (*it);
620     if (keepAtLeastOneCharacter && splitPosition == fragmentToSplit.start())
621         ++splitPosition;
622     return fragmentToSplit.split(splitPosition, textFragmentIterator);
623 }
624
625 enum PreWrapLineBreakRule { Preserve, Ignore };
626
627 static TextFragmentIterator::TextFragment consumeLineBreakIfNeeded(const TextFragmentIterator::TextFragment& fragment, TextFragmentIterator& textFragmentIterator, LineState& line, Layout::RunVector& runs,
628     PreWrapLineBreakRule preWrapLineBreakRule = PreWrapLineBreakRule::Preserve)
629 {
630     if (!fragment.isLineBreak())
631         return fragment;
632
633     if (preWrap(textFragmentIterator.style()) && preWrapLineBreakRule != PreWrapLineBreakRule::Ignore)
634         return fragment;
635
636     // <br> always produces a run. (required by testing output)
637     if (fragment.type() == TextFragmentIterator::TextFragment::HardLineBreak)
638         line.appendFragmentAndCreateRunIfNeeded(fragment, runs);
639     return textFragmentIterator.nextTextFragment();
640 }
641
642 static TextFragmentIterator::TextFragment skipWhitespaceIfNeeded(const TextFragmentIterator::TextFragment& fragment, TextFragmentIterator& textFragmentIterator)
643 {
644     if (!textFragmentIterator.style().collapseWhitespace)
645         return fragment;
646
647     TextFragmentIterator::TextFragment firstNonWhitespaceFragment = fragment;
648     while (firstNonWhitespaceFragment.type() == TextFragmentIterator::TextFragment::Whitespace)
649         firstNonWhitespaceFragment = textFragmentIterator.nextTextFragment();
650     return firstNonWhitespaceFragment;
651 }
652
653 static TextFragmentIterator::TextFragment firstFragment(TextFragmentIterator& textFragmentIterator, LineState& currentLine, const LineState& previousLine, Layout::RunVector& runs)
654 {
655     // Handle overflowed fragment from previous line.
656     TextFragmentIterator::TextFragment firstFragment(previousLine.overflowedFragment());
657
658     if (firstFragment.isEmpty())
659         firstFragment = textFragmentIterator.nextTextFragment();
660     else if (firstFragment.type() == TextFragmentIterator::TextFragment::Whitespace && preWrap(textFragmentIterator.style()) && previousLine.firstCharacterFits()) {
661         // Special overflow pre-wrap whitespace handling: skip the overflowed whitespace (even when style says not-collapsible) if we managed to fit at least one character on the previous line.
662         firstFragment = textFragmentIterator.nextTextFragment();
663         // If skipping the whitespace puts us on a newline, skip the newline too as we already wrapped the line.
664         firstFragment = consumeLineBreakIfNeeded(firstFragment, textFragmentIterator, currentLine, runs, PreWrapLineBreakRule::Ignore);
665     }
666     return skipWhitespaceIfNeeded(firstFragment, textFragmentIterator);
667 }
668
669 static void forceFragmentToLine(LineState& line, TextFragmentIterator& textFragmentIterator, Layout::RunVector& runs, const TextFragmentIterator::TextFragment& fragment)
670 {
671     line.appendFragmentAndCreateRunIfNeeded(fragment, runs);
672     // Check if there are more fragments to add to the current line.
673     auto nextFragment = textFragmentIterator.nextTextFragment();
674     if (fragment.overlapsToNextRenderer()) {
675         while (true) {
676             if (nextFragment.type() != fragment.type())
677                 break;
678             line.appendFragmentAndCreateRunIfNeeded(nextFragment, runs);
679             // Does it overlap to the next segment?
680             if (!nextFragment.overlapsToNextRenderer())
681                 return;
682             nextFragment = textFragmentIterator.nextTextFragment();
683         }
684     }
685     // When the forced fragment is followed by either whitespace and/or line break, consume them too, otherwise we end up with an extra whitespace and/or line break.
686     nextFragment = skipWhitespaceIfNeeded(nextFragment, textFragmentIterator);
687     nextFragment = consumeLineBreakIfNeeded(nextFragment, textFragmentIterator, line, runs);
688     line.setOverflowedFragment(nextFragment);
689 }
690
691 static bool createLineRuns(LineState& line, const LineState& previousLine, Layout::RunVector& runs, TextFragmentIterator& textFragmentIterator)
692 {
693     const auto& style = textFragmentIterator.style();
694     line.setCollapedWhitespaceWidth(style.spaceWidth + style.wordSpacing);
695     bool lineCanBeWrapped = style.wrapLines || style.breakFirstWordOnOverflow || style.breakAnyWordOnOverflow;
696     auto fragment = firstFragment(textFragmentIterator, line, previousLine, runs);
697     while (fragment.type() != TextFragmentIterator::TextFragment::ContentEnd) {
698         // Hard linebreak.
699         if (fragment.isLineBreak()) {
700             // Add the new line fragment only if there's nothing on the line. (otherwise the extra new line character would show up at the end of the content.)
701             if (line.isEmpty() || fragment.type() == TextFragmentIterator::TextFragment::HardLineBreak) {
702                 if (style.textAlign == RIGHT || style.textAlign == WEBKIT_RIGHT)
703                     line.removeTrailingWhitespace(runs);
704                 line.appendFragmentAndCreateRunIfNeeded(fragment, runs);
705             }
706             break;
707         }
708         if (lineCanBeWrapped && !line.fits(fragment.width())) {
709             // Overflow wrapping behaviour:
710             // 1. Whitesapce collapse on: whitespace is skipped. Jump to next line.
711             // 2. Whitespace collapse off: whitespace is wrapped.
712             // 3. First, non-whitespace fragment is either wrapped or kept on the line. (depends on overflow-wrap)
713             // 5. Non-whitespace fragment when there's already another fragment on the line either gets wrapped (word-break: break-all)
714             // or gets pushed to the next line.
715             bool emptyLine = line.isEmpty();
716             // Whitespace fragment.
717             if (fragment.type() == TextFragmentIterator::TextFragment::Whitespace) {
718                 if (!style.collapseWhitespace) {
719                     // Split the fragment; (modified)fragment stays on this line, overflowedFragment is pushed to next line.
720                     line.setOverflowedFragment(splitFragmentToFitLine(fragment, line.availableWidth() - line.width(), emptyLine, textFragmentIterator));
721                     line.appendFragmentAndCreateRunIfNeeded(fragment, runs);
722                 }
723                 // When whitespace collapse is on, whitespace that doesn't fit is simply skipped.
724                 break;
725             }
726             // Non-whitespace fragment. (!style.wrapLines: bug138102(preserve existing behavior)
727             if (((emptyLine && style.breakFirstWordOnOverflow) || style.breakAnyWordOnOverflow) || !style.wrapLines) {
728                 // Split the fragment; (modified)fragment stays on this line, overflowedFragment is pushed to next line.
729                 line.setOverflowedFragment(splitFragmentToFitLine(fragment, line.availableWidth() - line.width(), emptyLine, textFragmentIterator));
730                 line.appendFragmentAndCreateRunIfNeeded(fragment, runs);
731                 break;
732             }
733             // Non-breakable non-whitespace first fragment. Add it to the current line. -it overflows though.
734             ASSERT(fragment.type() == TextFragmentIterator::TextFragment::NonWhitespace);
735             if (emptyLine) {
736                 forceFragmentToLine(line, textFragmentIterator, runs, fragment);
737                 break;
738             }
739             // Non-breakable non-whitespace fragment when there's already content on the line. Push it to the next line.
740             ASSERT(line.lastFragment());
741             if (line.lastFragment().value().overlapsToNextRenderer()) {
742                 // Check if this fragment is a continuation of a previous segment. In such cases, we need to remove them all.
743                 const auto& lastCompleteFragment = line.revertToLastCompleteFragment(runs);
744                 textFragmentIterator.revertToEndOfFragment(lastCompleteFragment);
745                 break;
746             }
747             line.setOverflowedFragment(fragment);
748             break;
749         }
750         line.appendFragmentAndCreateRunIfNeeded(fragment, runs);
751         // Find the next text fragment.
752         fragment = textFragmentIterator.nextTextFragment(line.width());
753     }
754     return (fragment.type() == TextFragmentIterator::TextFragment::ContentEnd && line.overflowedFragment().isEmpty()) || line.overflowedFragment().type() == TextFragmentIterator::TextFragment::ContentEnd;
755 }
756
757 static ExpansionBehavior expansionBehavior(bool isAfterExpansion, bool lastRunOnLine)
758 {
759     ExpansionBehavior expansionBehavior;
760     expansionBehavior = isAfterExpansion ? ForbidLeadingExpansion : AllowLeadingExpansion;
761     expansionBehavior |= lastRunOnLine ? ForbidTrailingExpansion : AllowTrailingExpansion;
762     return expansionBehavior;
763 }
764
765 static void justifyRuns(const LineState& line, Layout::RunVector& runs, unsigned firstRunIndex)
766 {
767     ASSERT(runs.size());
768     auto widthToDistribute = line.availableWidth() - line.width();
769     if (widthToDistribute <= 0)
770         return;
771
772     auto lastRunIndex = runs.size() - 1;
773     ASSERT(firstRunIndex <= lastRunIndex);
774     Vector<std::pair<unsigned, ExpansionBehavior>> expansionOpportunityList;
775     unsigned expansionOpportunityCountOnThisLine = 0;
776     auto isAfterExpansion = true;
777     for (auto i = firstRunIndex; i <= lastRunIndex; ++i) {
778         const auto& run = runs.at(i);
779         unsigned opportunityCountInRun = 0;
780         std::tie(opportunityCountInRun, isAfterExpansion) = line.expansionOpportunityCount(run.start, run.end);
781         expansionOpportunityList.append(std::make_pair(opportunityCountInRun, expansionBehavior(isAfterExpansion, i == lastRunIndex)));
782         expansionOpportunityCountOnThisLine += opportunityCountInRun;
783     }
784     if (!expansionOpportunityCountOnThisLine)
785         return;
786
787     ASSERT(expansionOpportunityList.size() == lastRunIndex - firstRunIndex + 1);
788     auto expansion = widthToDistribute / expansionOpportunityCountOnThisLine;
789     float accumulatedExpansion = 0;
790     for (auto i = firstRunIndex; i <= lastRunIndex; ++i) {
791         auto& run = runs.at(i);
792         unsigned opportunityCountInRun;
793         std::tie(opportunityCountInRun, run.expansionBehavior) = expansionOpportunityList.at(i - firstRunIndex);
794         run.expansion = opportunityCountInRun * expansion;
795         run.logicalLeft += accumulatedExpansion;
796         run.logicalRight += (accumulatedExpansion + run.expansion);
797         accumulatedExpansion += run.expansion;
798     }
799 }
800
801 static ETextAlign textAlignForLine(const TextFragmentIterator::Style& style, bool lastLine)
802 {
803     // Fallback to LEFT (START) alignment for non-collapsable content and for the last line before a forced break or the end of the block.
804     auto textAlign = style.textAlign;
805     if (textAlign == JUSTIFY && (!style.collapseWhitespace || lastLine))
806         textAlign = LEFT;
807     return textAlign;
808 }
809
810 static void closeLineEndingAndAdjustRuns(LineState& line, Layout::RunVector& runs, std::optional<unsigned> lastRunIndexOfPreviousLine, unsigned& lineCount,
811     const TextFragmentIterator& textFragmentIterator, bool lastLineInFlow)
812 {
813     if (!runs.size() || (lastRunIndexOfPreviousLine && runs.size() - 1 == lastRunIndexOfPreviousLine.value()))
814         return;
815     removeTrailingWhitespace(line, runs, textFragmentIterator);
816     if (!runs.size())
817         return;
818     // Adjust runs' position by taking line's alignment into account.
819     const auto& style = textFragmentIterator.style();
820     auto firstRunIndex = lastRunIndexOfPreviousLine ? lastRunIndexOfPreviousLine.value() + 1 : 0;
821     auto lineLogicalLeft = line.logicalLeftOffset();
822     auto textAlign = textAlignForLine(style, lastLineInFlow || (line.lastFragment() && line.lastFragment().value().type() == TextFragmentIterator::TextFragment::HardLineBreak));
823     if (textAlign == JUSTIFY)
824         justifyRuns(line, runs, firstRunIndex);
825     else
826         lineLogicalLeft = computeLineLeft(textAlign, line.availableWidth(), line.width(), line.logicalLeftOffset());
827     for (auto i = firstRunIndex; i < runs.size(); ++i) {
828         runs[i].logicalLeft += lineLogicalLeft;
829         runs[i].logicalRight += lineLogicalLeft;
830     }
831     runs.last().isEndOfLine = true;
832     ++lineCount;
833 }
834
835 static void createTextRuns(Layout::RunVector& runs, RenderBlockFlow& flow, unsigned& lineCount)
836 {
837     LayoutUnit borderAndPaddingBefore = flow.borderAndPaddingBefore();
838     LayoutUnit lineHeight = lineHeightFromFlow(flow);
839     LineState line;
840     bool isEndOfContent = false;
841     TextFragmentIterator textFragmentIterator = TextFragmentIterator(flow);
842     std::optional<unsigned> lastRunIndexOfPreviousLine;
843     do {
844         flow.setLogicalHeight(lineHeight * lineCount + borderAndPaddingBefore);
845         LineState previousLine = line;
846         line = LineState();
847         updateLineConstrains(flow, line, !lineCount);
848         isEndOfContent = createLineRuns(line, previousLine, runs, textFragmentIterator);
849         closeLineEndingAndAdjustRuns(line, runs, lastRunIndexOfPreviousLine, lineCount, textFragmentIterator, isEndOfContent);
850         if (runs.size())
851             lastRunIndexOfPreviousLine = runs.size() - 1;
852     } while (!isEndOfContent);
853 }
854
855 std::unique_ptr<Layout> create(RenderBlockFlow& flow)
856 {
857     unsigned lineCount = 0;
858     Layout::RunVector runs;
859
860     createTextRuns(runs, flow, lineCount);
861     return Layout::create(runs, lineCount);
862 }
863
864 std::unique_ptr<Layout> Layout::create(const RunVector& runVector, unsigned lineCount)
865 {
866     void* slot = WTF::fastMalloc(sizeof(Layout) + sizeof(Run) * runVector.size());
867     return std::unique_ptr<Layout>(new (NotNull, slot) Layout(runVector, lineCount));
868 }
869
870 Layout::Layout(const RunVector& runVector, unsigned lineCount)
871     : m_lineCount(lineCount)
872     , m_runCount(runVector.size())
873 {
874     memcpy(m_runs, runVector.data(), m_runCount * sizeof(Run));
875 }
876
877 #ifndef NDEBUG
878 static void printReason(AvoidanceReason reason, TextStream& stream)
879 {
880     switch (reason) {
881     case FlowIsInsideRegion:
882         stream << "flow is inside region";
883         break;
884     case FlowHasHorizonalWritingMode:
885         stream << "horizontal writing mode";
886         break;
887     case FlowHasOutline:
888         stream << "outline";
889         break;
890     case FlowIsRuby:
891         stream << "ruby";
892         break;
893     case FlowHasHangingPunctuation:
894         stream << "hanging punctuation";
895         break;
896     case FlowIsPaginated:
897         stream << "paginated";
898         break;
899     case FlowHasTextOverflow:
900         stream << "text-overflow";
901         break;
902     case FlowIsDepricatedFlexBox:
903         stream << "depricatedFlexBox";
904         break;
905     case FlowParentIsPlaceholderElement:
906         stream << "placeholder element";
907         break;
908     case FlowParentIsTextAreaWithWrapping:
909         stream << "wrapping textarea";
910         break;
911     case FlowHasNonSupportedChild:
912         stream << "nested renderers";
913         break;
914     case FlowHasUnsupportedFloat:
915         stream << "complicated float";
916         break;
917     case FlowHasUnsupportedUnderlineDecoration:
918         stream << "text-underline-position: under";
919         break;
920     case FlowHasJustifiedNonLatinText:
921         stream << "text-align: justify with non-latin text";
922         break;
923     case FlowHasOverflowVisible:
924         stream << "overflow: visible";
925         break;
926     case FlowIsNotLTR:
927         stream << "dir is not LTR";
928         break;
929     case FlowHasLineBoxContainProperty:
930         stream << "line-box-contain property";
931         break;
932     case FlowIsNotTopToBottom:
933         stream << "non top-to-bottom flow";
934         break;
935     case FlowHasLineBreak:
936         stream << "line-break property";
937         break;
938     case FlowHasNonNormalUnicodeBiDi:
939         stream << "non-normal Unicode bidi";
940         break;
941     case FlowHasRTLOrdering:
942         stream << "-webkit-rtl-ordering";
943         break;
944     case FlowHasLineAlignEdges:
945         stream << "-webkit-line-align edges";
946         break;
947     case FlowHasLineSnap:
948         stream << "-webkit-line-snap property";
949         break;
950     case FlowHasHypensAuto:
951         stream << "hyphen: auto";
952         break;
953     case FlowHasTextEmphasisFillOrMark:
954         stream << "text-emphasis (fill/mark)";
955         break;
956     case FlowHasPseudoFirstLine:
957         stream << "first-line";
958         break;
959     case FlowHasPseudoFirstLetter:
960         stream << "first-letter";
961         break;
962     case FlowHasTextCombine:
963         stream << "text combine";
964         break;
965     case FlowHasTextFillBox:
966         stream << "background-color (text-fill)";
967         break;
968     case FlowHasBorderFitLines:
969         stream << "-webkit-border-fit";
970         break;
971     case FlowHasNonAutoLineBreak:
972         stream << "line-break is not auto";
973         break;
974     case FlowHasNonAutoTrailingWord:
975         stream << "-apple-trailing-word is not auto";
976         break;
977     case FlowHasSVGFont:
978         stream << "SVG font";
979         break;
980     case FlowTextHasNoBreakSpace:
981         stream << "No-break-space character";
982         break;
983     case FlowTextHasSoftHyphen:
984         stream << "soft hyphen character";
985         break;
986     case FlowTextHasDirectionCharacter:
987         stream << "direction character";
988         break;
989     case FlowIsMissingPrimaryFont:
990         stream << "missing primary font";
991         break;
992     case FlowFontIsMissingGlyph:
993         stream << "missing glyph";
994         break;
995     case FlowTextIsCombineText:
996         stream << "text is combine";
997         break;
998     case FlowTextIsRenderCounter:
999         stream << "unsupported RenderCounter";
1000         break;
1001     case FlowTextIsRenderQuote:
1002         stream << "unsupported RenderQuote";
1003         break;
1004     case FlowTextIsTextFragment:
1005         stream << "unsupported TextFragment";
1006         break;
1007     case FlowTextIsSVGInlineText:
1008         stream << "unsupported SVGInlineText";
1009         break;
1010     case FlowFontIsNotSimple:
1011         stream << "complext font";
1012         break;
1013     case FlowHasTextShadow:
1014         stream << "text-shadow";
1015         break;
1016     case FlowChildIsSelected:
1017         stream << "selected content";
1018         break;
1019     case FlowTextIsEmpty:
1020     case FlowHasNoChild:
1021     case FlowHasNoParent:
1022     case FeatureIsDisabled:
1023     default:
1024         break;
1025     }
1026 }
1027
1028 static void printReasons(AvoidanceReasonFlags reasons, TextStream& stream)
1029 {
1030     bool first = true;
1031     for (auto reasonItem = EndOfReasons >> 1; reasonItem != NoReason; reasonItem >>= 1) {
1032         if (!(reasons & reasonItem))
1033             continue;
1034         stream << (first ? " " : ", ");
1035         first = false;
1036         printReason(reasonItem, stream);
1037     }
1038 }
1039
1040 static void printTextForSubtree(const RenderObject& renderer, unsigned& charactersLeft, TextStream& stream)
1041 {
1042     if (!charactersLeft)
1043         return;
1044     if (is<RenderText>(renderer)) {
1045         String text = downcast<RenderText>(renderer).text();
1046         text = text.stripWhiteSpace();
1047         unsigned len = std::min(charactersLeft, text.length());
1048         stream << text.left(len);
1049         charactersLeft -= len;
1050         return;
1051     }
1052     if (!is<RenderElement>(renderer))
1053         return;
1054     for (const auto* child = downcast<RenderElement>(renderer).firstChild(); child; child = child->nextSibling())
1055         printTextForSubtree(*child, charactersLeft, stream);
1056 }
1057
1058 static unsigned textLengthForSubtree(const RenderObject& renderer)
1059 {
1060     if (is<RenderText>(renderer))
1061         return downcast<RenderText>(renderer).textLength();
1062     if (!is<RenderElement>(renderer))
1063         return 0;
1064     unsigned textLength = 0;
1065     for (const auto* child = downcast<RenderElement>(renderer).firstChild(); child; child = child->nextSibling())
1066         textLength += textLengthForSubtree(*child);
1067     return textLength;
1068 }
1069
1070 static void collectNonEmptyLeafRenderBlockFlows(const RenderObject& renderer, HashSet<const RenderBlockFlow*>& leafRenderers)
1071 {
1072     if (is<RenderText>(renderer)) {
1073         if (!downcast<RenderText>(renderer).textLength())
1074             return;
1075         // Find RenderBlockFlow ancestor.
1076         for (const auto* current = renderer.parent(); current; current = current->parent()) {
1077             if (!is<RenderBlockFlow>(current))
1078                 continue;
1079             leafRenderers.add(downcast<RenderBlockFlow>(current));
1080             break;
1081         }
1082         return;
1083     }
1084     if (!is<RenderElement>(renderer))
1085         return;
1086     for (const auto* child = downcast<RenderElement>(renderer).firstChild(); child; child = child->nextSibling())
1087         collectNonEmptyLeafRenderBlockFlows(*child, leafRenderers);
1088 }
1089
1090 static void collectNonEmptyLeafRenderBlockFlowsForCurrentPage(HashSet<const RenderBlockFlow*>& leafRenderers)
1091 {
1092     for (const auto* document : Document::allDocuments()) {
1093         if (!document->renderView() || document->pageCacheState() != Document::NotInPageCache)
1094             continue;
1095         if (!document->isHTMLDocument() && !document->isXHTMLDocument())
1096             continue;
1097         collectNonEmptyLeafRenderBlockFlows(*document->renderView(), leafRenderers);
1098     }
1099 }
1100
1101 void toggleSimpleLineLayout()
1102 {
1103     for (const auto* document : Document::allDocuments()) {
1104         auto* settings = document->settings();
1105         if (!settings)
1106             continue;
1107         settings->setSimpleLineLayoutEnabled(!settings->simpleLineLayoutEnabled());
1108     }
1109 }
1110
1111 void printSimpleLineLayoutBlockList()
1112 {
1113     HashSet<const RenderBlockFlow*> leafRenderers;
1114     collectNonEmptyLeafRenderBlockFlowsForCurrentPage(leafRenderers);
1115     if (!leafRenderers.size()) {
1116         WTFLogAlways("No text found in this document\n");
1117         return;
1118     }
1119     TextStream stream;
1120     stream << "---------------------------------------------------\n";
1121     for (const auto* flow : leafRenderers) {
1122         auto reason = canUseForWithReason(*flow, IncludeReasons::All);
1123         if (reason == NoReason)
1124             continue;
1125         unsigned printedLength = 30;
1126         stream << "\"";
1127         printTextForSubtree(*flow, printedLength, stream);
1128         for (;printedLength > 0; --printedLength)
1129             stream << " ";
1130         stream << "\"(" << textLengthForSubtree(*flow) << "):";
1131         printReasons(reason, stream);
1132         stream << "\n";
1133     }
1134     stream << "---------------------------------------------------\n";
1135     WTFLogAlways("%s", stream.release().utf8().data());
1136 }
1137
1138 void printSimpleLineLayoutCoverage()
1139 {
1140     HashSet<const RenderBlockFlow*> leafRenderers;
1141     collectNonEmptyLeafRenderBlockFlowsForCurrentPage(leafRenderers);
1142     if (!leafRenderers.size()) {
1143         WTFLogAlways("No text found in this document\n");
1144         return;
1145     }
1146     TextStream stream;
1147     HashMap<AvoidanceReason, unsigned> flowStatistics;
1148     unsigned textLength = 0;
1149     unsigned unsupportedTextLength = 0;
1150     unsigned numberOfUnsupportedLeafBlocks = 0;
1151     for (const auto* flow : leafRenderers) {
1152         auto flowLength = textLengthForSubtree(*flow);
1153         textLength += flowLength;
1154         auto reasons = canUseForWithReason(*flow, IncludeReasons::All);
1155         if (reasons == NoReason)
1156             continue;
1157         ++numberOfUnsupportedLeafBlocks;
1158         unsupportedTextLength += flowLength;
1159         for (auto reasonItem = EndOfReasons >> 1; reasonItem != NoReason; reasonItem >>= 1) {
1160             if (!(reasons & reasonItem))
1161                 continue;
1162             auto result = flowStatistics.add(reasonItem, flowLength);
1163             if (!result.isNewEntry)
1164                 result.iterator->value += flowLength;
1165         }
1166     }
1167     stream << "---------------------------------------------------\n";
1168     stream << "Number of text blocks: total(" <<  leafRenderers.size() << ") non-simple(" << numberOfUnsupportedLeafBlocks << ")\nText length: total(" <<
1169         textLength << ") non-simple(" << unsupportedTextLength << ")\n";
1170     for (const auto reasonEntry : flowStatistics) {
1171         printReason(reasonEntry.key, stream);
1172         stream << ": " << (float)reasonEntry.value / (float)textLength * 100 << "%\n";
1173     }
1174     stream << "simple line layout coverage: " << (float)(textLength - unsupportedTextLength) / (float)textLength * 100 << "%\n";
1175     stream << "---------------------------------------------------\n";
1176     WTFLogAlways("%s", stream.release().utf8().data());
1177 }
1178 #endif
1179 }
1180 }