Simple line layout:: Add text-decoration support.
[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     FlowIsJustifyAligned                  = 1LLU  << 12,
78     FlowHasOverflowVisible                = 1LLU  << 13,
79     FlowIsNotLTR                          = 1LLU  << 14,
80     FlowHasLineBoxContainProperty         = 1LLU  << 15,
81     FlowIsNotTopToBottom                  = 1LLU  << 16,
82     FlowHasLineBreak                      = 1LLU  << 17,
83     FlowHasWordBreak                      = 1LLU  << 18,
84     FlowHasNonNormalUnicodeBiDi           = 1LLU  << 19,
85     FlowHasRTLOrdering                    = 1LLU  << 20,
86     FlowHasLineAlignEdges                 = 1LLU  << 21,
87     FlowHasLineSnap                       = 1LLU  << 22,
88     FlowHasHypensAuto                     = 1LLU  << 23,
89     FlowHasTextEmphasisFillOrMark         = 1LLU  << 24,
90     FlowHasTextShadow                     = 1LLU  << 25,
91     FlowHasPseudoFirstLine                = 1LLU  << 26,
92     FlowHasPseudoFirstLetter              = 1LLU  << 27,
93     FlowHasTextCombine                    = 1LLU  << 28,
94     FlowHasTextFillBox                    = 1LLU  << 29,
95     FlowHasBorderFitLines                 = 1LLU  << 30,
96     FlowHasNonAutoLineBreak               = 1LLU  << 31,
97     FlowHasNonAutoTrailingWord            = 1LLU  << 32,
98     FlowHasSVGFont                        = 1LLU  << 33,
99     FlowTextIsEmpty                       = 1LLU  << 34,
100     FlowTextHasNoBreakSpace               = 1LLU  << 35,
101     FlowTextHasSoftHyphen                 = 1LLU  << 36,
102     FlowTextHasDirectionCharacter         = 1LLU  << 37,
103     FlowIsMissingPrimaryFont              = 1LLU  << 38,
104     FlowFontIsMissingGlyph                = 1LLU  << 39,
105     FlowTextIsCombineText                 = 1LLU  << 40,
106     FlowTextIsRenderCounter               = 1LLU  << 41,
107     FlowTextIsRenderQuote                 = 1LLU  << 42,
108     FlowTextIsTextFragment                = 1LLU  << 43,
109     FlowTextIsSVGInlineText               = 1LLU  << 44,
110     FlowFontIsNotSimple                   = 1LLU  << 45,
111     FeatureIsDisabled                     = 1LLU  << 46,
112     FlowHasNoParent                       = 1LLU  << 47,
113     FlowHasNoChild                        = 1LLU  << 48,
114     FlowChildIsSelected                   = 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     if (primaryFont.isSVGFont())
186         SET_REASON_AND_RETURN_IF_NEEDED(FlowHasSVGFont, reasons, includeReasons);
187
188     for (const auto& textRenderer : childrenOfType<RenderText>(flow)) {
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     if (style.textAlign() == JUSTIFY)
217         SET_REASON_AND_RETURN_IF_NEEDED(FlowIsJustifyAligned, reasons, includeReasons);
218     // Non-visible overflow should be pretty easy to support.
219     if (style.overflowX() != OVISIBLE || style.overflowY() != OVISIBLE)
220         SET_REASON_AND_RETURN_IF_NEEDED(FlowHasOverflowVisible, reasons, includeReasons);
221     if (!style.isLeftToRightDirection())
222         SET_REASON_AND_RETURN_IF_NEEDED(FlowIsNotLTR, reasons, includeReasons);
223     if (style.lineBoxContain() != RenderStyle::initialLineBoxContain())
224         SET_REASON_AND_RETURN_IF_NEEDED(FlowHasLineBoxContainProperty, reasons, includeReasons);
225     if (style.writingMode() != TopToBottomWritingMode)
226         SET_REASON_AND_RETURN_IF_NEEDED(FlowIsNotTopToBottom, reasons, includeReasons);
227     if (style.lineBreak() != LineBreakAuto)
228         SET_REASON_AND_RETURN_IF_NEEDED(FlowHasLineBreak, reasons, includeReasons);
229     if (style.wordBreak() != NormalWordBreak)
230         SET_REASON_AND_RETURN_IF_NEEDED(FlowHasWordBreak, reasons, includeReasons);
231     if (style.unicodeBidi() != UBNormal)
232         SET_REASON_AND_RETURN_IF_NEEDED(FlowHasNonNormalUnicodeBiDi, reasons, includeReasons);
233     if (style.rtlOrdering() != LogicalOrder)
234         SET_REASON_AND_RETURN_IF_NEEDED(FlowHasRTLOrdering, reasons, includeReasons);
235     if (style.lineAlign() != LineAlignNone)
236         SET_REASON_AND_RETURN_IF_NEEDED(FlowHasLineAlignEdges, reasons, includeReasons);
237     if (style.lineSnap() != LineSnapNone)
238         SET_REASON_AND_RETURN_IF_NEEDED(FlowHasLineSnap, reasons, includeReasons);
239     if (style.hyphens() == HyphensAuto)
240         SET_REASON_AND_RETURN_IF_NEEDED(FlowHasHypensAuto, reasons, includeReasons);
241     if (style.textEmphasisFill() != TextEmphasisFillFilled || style.textEmphasisMark() != TextEmphasisMarkNone)
242         SET_REASON_AND_RETURN_IF_NEEDED(FlowHasTextEmphasisFillOrMark, reasons, includeReasons);
243     if (style.textShadow())
244         SET_REASON_AND_RETURN_IF_NEEDED(FlowHasTextShadow, reasons, includeReasons);
245     if (style.hasPseudoStyle(FIRST_LINE))
246         SET_REASON_AND_RETURN_IF_NEEDED(FlowHasPseudoFirstLine, reasons, includeReasons);
247     if (style.hasPseudoStyle(FIRST_LETTER))
248         SET_REASON_AND_RETURN_IF_NEEDED(FlowHasPseudoFirstLetter, reasons, includeReasons);
249     if (style.hasTextCombine())
250         SET_REASON_AND_RETURN_IF_NEEDED(FlowHasTextCombine, reasons, includeReasons);
251     if (style.backgroundClip() == TextFillBox)
252         SET_REASON_AND_RETURN_IF_NEEDED(FlowHasTextFillBox, reasons, includeReasons);
253     if (style.borderFit() == BorderFitLines)
254         SET_REASON_AND_RETURN_IF_NEEDED(FlowHasBorderFitLines, reasons, includeReasons);
255     if (style.lineBreak() != LineBreakAuto)
256         SET_REASON_AND_RETURN_IF_NEEDED(FlowHasNonAutoLineBreak, reasons, includeReasons);
257 #if ENABLE(CSS_TRAILING_WORD)
258     if (style.trailingWord() != TrailingWord::Auto)
259         SET_REASON_AND_RETURN_IF_NEEDED(FlowHasNonAutoTrailingWord, reasons, includeReasons);
260 #endif
261     return reasons;
262 }
263
264 static AvoidanceReasonFlags canUseForWithReason(const RenderBlockFlow& flow, IncludeReasons includeReasons)
265 {
266 #ifndef NDEBUG
267     static std::once_flag onceFlag;
268     std::call_once(onceFlag, [] {
269         registerNotifyCallback("com.apple.WebKit.showSimpleLineLayoutCoverage", printSimpleLineLayoutCoverage);
270         registerNotifyCallback("com.apple.WebKit.showSimpleLineLayoutReasons", printSimpleLineLayoutBlockList);
271         registerNotifyCallback("com.apple.WebKit.toggleSimpleLineLayout", toggleSimpleLineLayout);
272     });
273 #endif
274     AvoidanceReasonFlags reasons = { };
275     if (!flow.frame().settings().simpleLineLayoutEnabled())
276         SET_REASON_AND_RETURN_IF_NEEDED(FeatureIsDisabled, reasons, includeReasons);
277     if (!flow.parent())
278         SET_REASON_AND_RETURN_IF_NEEDED(FlowHasNoParent, reasons, includeReasons);
279     if (!flow.firstChild())
280         SET_REASON_AND_RETURN_IF_NEEDED(FlowHasNoChild, reasons, includeReasons);
281     if (flow.flowThreadState() != RenderObject::NotInsideFlowThread)
282         SET_REASON_AND_RETURN_IF_NEEDED(FlowIsInsideRegion, reasons, includeReasons);
283     if (!flow.isHorizontalWritingMode())
284         SET_REASON_AND_RETURN_IF_NEEDED(FlowHasHorizonalWritingMode, reasons, includeReasons);
285     if (flow.hasOutline())
286         SET_REASON_AND_RETURN_IF_NEEDED(FlowHasOutline, reasons, includeReasons);
287     if (flow.isRubyText() || flow.isRubyBase())
288         SET_REASON_AND_RETURN_IF_NEEDED(FlowIsRuby, reasons, includeReasons);
289     // Printing does pagination without a flow thread.
290     if (flow.document().paginated())
291         SET_REASON_AND_RETURN_IF_NEEDED(FlowIsPaginated, reasons, includeReasons);
292     if (flow.firstLineBlock())
293         SET_REASON_AND_RETURN_IF_NEEDED(FlowHasPseudoFirstLine, reasons, includeReasons);
294     if (flow.isAnonymousBlock() && flow.parent()->style().textOverflow())
295         SET_REASON_AND_RETURN_IF_NEEDED(FlowHasTextOverflow, reasons, includeReasons);
296     if (flow.parent()->isDeprecatedFlexibleBox())
297         SET_REASON_AND_RETURN_IF_NEEDED(FlowIsDepricatedFlexBox, reasons, includeReasons);
298     // FIXME: Placeholders do something strange.
299     if (is<RenderTextControl>(*flow.parent()) && downcast<RenderTextControl>(*flow.parent()).textFormControlElement().placeholderElement())
300         SET_REASON_AND_RETURN_IF_NEEDED(FlowParentIsPlaceholderElement, reasons, includeReasons);
301     // FIXME: Implementation of wrap=hard looks into lineboxes.
302     if (flow.parent()->isTextArea() && flow.parent()->element()->fastHasAttribute(HTMLNames::wrapAttr))
303         SET_REASON_AND_RETURN_IF_NEEDED(FlowParentIsTextAreaWithWrapping, reasons, includeReasons);
304     // This currently covers <blockflow>#text</blockflow>, <blockflow>#text<br></blockflow> and mutiple (sibling) RenderText cases.
305     // The <blockflow><inline>#text</inline></blockflow> case is also popular and should be relatively easy to cover.
306     for (const auto* child = flow.firstChild(); child;) {
307         if (child->selectionState() != RenderObject::SelectionNone)
308             SET_REASON_AND_RETURN_IF_NEEDED(FlowChildIsSelected, reasons, includeReasons);
309         if (is<RenderText>(*child)) {
310             child = child->nextSibling();
311             continue;
312         }
313         if (is<RenderLineBreak>(child) && !downcast<RenderLineBreak>(*child).isWBR() && child->style().clear() == CNONE) {
314             child = child->nextSibling();
315             continue;
316         }
317         SET_REASON_AND_RETURN_IF_NEEDED(FlowHasNonSupportedChild, reasons, includeReasons);
318         break;
319     }
320     auto styleReasons = canUseForStyle(flow.style(), includeReasons);
321     if (styleReasons != NoReason)
322         SET_REASON_AND_RETURN_IF_NEEDED(styleReasons, reasons, includeReasons);
323     // 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.
324     if (flow.containsFloats()) {
325         float minimumWidthNeeded = std::numeric_limits<float>::max();
326         for (const auto& textRenderer : childrenOfType<RenderText>(flow)) {
327             minimumWidthNeeded = std::min(minimumWidthNeeded, textRenderer.minLogicalWidth());
328
329             for (auto& floatingObject : *flow.floatingObjectSet()) {
330                 ASSERT(floatingObject);
331 #if ENABLE(CSS_SHAPES)
332                 // if a float has a shape, we cannot tell if content will need to be shifted until after we lay it out,
333                 // since the amount of space is not uniform for the height of the float.
334                 if (floatingObject->renderer().shapeOutsideInfo())
335                     SET_REASON_AND_RETURN_IF_NEEDED(FlowHasUnsupportedFloat, reasons, includeReasons);
336 #endif
337                 float availableWidth = flow.availableLogicalWidthForLine(floatingObject->y(), false);
338                 if (availableWidth < minimumWidthNeeded)
339                     SET_REASON_AND_RETURN_IF_NEEDED(FlowHasUnsupportedFloat, reasons, includeReasons);
340             }
341         }
342     }
343     auto fontAndTextReasons = canUseForFontAndText(flow, includeReasons);
344     if (fontAndTextReasons != NoReason)
345         SET_REASON_AND_RETURN_IF_NEEDED(fontAndTextReasons, reasons, includeReasons);
346     return reasons;
347 }
348
349 bool canUseFor(const RenderBlockFlow& flow)
350 {
351     return canUseForWithReason(flow, IncludeReasons::First) == NoReason;
352 }
353
354 static float computeLineLeft(ETextAlign textAlign, float availableWidth, float committedWidth, float logicalLeftOffset)
355 {
356     float remainingWidth = availableWidth - committedWidth;
357     float left = logicalLeftOffset;
358     switch (textAlign) {
359     case LEFT:
360     case WEBKIT_LEFT:
361     case TASTART:
362         return left;
363     case RIGHT:
364     case WEBKIT_RIGHT:
365     case TAEND:
366         return left + std::max<float>(remainingWidth, 0);
367     case CENTER:
368     case WEBKIT_CENTER:
369         return left + std::max<float>(remainingWidth / 2, 0);
370     case JUSTIFY:
371         ASSERT_NOT_REACHED();
372         break;
373     }
374     ASSERT_NOT_REACHED();
375     return 0;
376 }
377
378 static void revertRuns(Layout::RunVector& runs, unsigned length, float width)
379 {
380     while (length) {
381         ASSERT(runs.size());
382         Run& lastRun = runs.last();
383         unsigned lastRunLength = lastRun.end - lastRun.start;
384         if (lastRunLength > length) {
385             lastRun.logicalRight -= width;
386             lastRun.end -= length;
387             break;
388         }
389         length -= lastRunLength;
390         width -= (lastRun.logicalRight - lastRun.logicalLeft);
391         runs.removeLast();
392     }
393 }
394
395 class LineState {
396 public:
397     void setAvailableWidth(float width) { m_availableWidth = width; }
398     void setCollapedWhitespaceWidth(float width) { m_collapsedWhitespaceWidth = width; }
399     void setLogicalLeftOffset(float offset) { m_logicalLeftOffset = offset; }
400     void setOverflowedFragment(const TextFragmentIterator::TextFragment& fragment) { m_overflowedFragment = fragment; }
401
402     float availableWidth() const { return m_availableWidth; }
403     float logicalLeftOffset() const { return m_logicalLeftOffset; }
404     const TextFragmentIterator::TextFragment& overflowedFragment() const { return m_overflowedFragment; }
405     bool hasTrailingWhitespace() const { return m_trailingWhitespaceLength; }
406     TextFragmentIterator::TextFragment lastFragment() const { return m_fragments.last(); }
407     bool isWhitespaceOnly() const { return m_trailingWhitespaceWidth && m_runsWidth == m_trailingWhitespaceWidth; }
408     bool fits(float extra) const { return m_availableWidth >= m_runsWidth + extra; }
409     bool firstCharacterFits() const { return m_firstCharacterFits; }
410     float width() const { return m_runsWidth; }
411     bool isEmpty() const
412     {
413         if (!m_fragments.size())
414             return true;
415         if (!m_lastCompleteFragment.isEmpty())
416             return false;
417         return m_fragments.last().overlapsToNextRenderer();
418     }
419
420     void appendFragmentAndCreateRunIfNeeded(const TextFragmentIterator::TextFragment& fragment, Layout::RunVector& runs)
421     {
422         // Adjust end position while collapsing.
423         unsigned endPosition = fragment.isCollapsed() ? fragment.start() + 1 : fragment.end();
424         // New line needs new run.
425         if (!m_runsWidth)
426             runs.append(Run(fragment.start(), endPosition, m_runsWidth, m_runsWidth + fragment.width(), false));
427         else {
428             const auto& lastFragment = m_fragments.last();
429             // Advance last completed fragment when the previous fragment is all set (including multiple parts across renderers)
430             if ((lastFragment.type() != fragment.type()) || !lastFragment.overlapsToNextRenderer())
431                 m_lastCompleteFragment = lastFragment;
432             // Collapse neighbouring whitespace, if they are across multiple renderers and are not collapsed yet.
433             if (lastFragment.isCollapsible() && fragment.isCollapsible()) {
434                 ASSERT(lastFragment.isLastInRenderer());
435                 if (!lastFragment.isCollapsed()) {
436                     // Line width needs to be reset so that now it takes collapsing into consideration.
437                     m_runsWidth -= (lastFragment.width() - m_collapsedWhitespaceWidth);
438                 }
439                 // This fragment is collapsed completely. No run is needed.
440                 return;
441             }
442             if (lastFragment.isLastInRenderer() || lastFragment.isCollapsed())
443                 runs.append(Run(fragment.start(), endPosition, m_runsWidth, m_runsWidth + fragment.width(), false));
444             else {
445                 Run& lastRun = runs.last();
446                 lastRun.end = endPosition;
447                 lastRun.logicalRight += fragment.width();
448             }
449         }
450         m_fragments.append(fragment);
451         m_runsWidth += fragment.width();
452
453         if (fragment.type() == TextFragmentIterator::TextFragment::Whitespace) {
454             m_trailingWhitespaceLength += endPosition - fragment.start();
455             m_trailingWhitespaceWidth += fragment.width();
456         } else {
457             m_trailingWhitespaceLength = 0;
458             m_trailingWhitespaceWidth = 0;
459         }
460
461         if (!m_firstCharacterFits)
462             m_firstCharacterFits = fragment.start() + 1 > endPosition || m_runsWidth <= m_availableWidth;
463     }
464
465     TextFragmentIterator::TextFragment revertToLastCompleteFragment(Layout::RunVector& runs)
466     {
467         ASSERT(m_fragments.size());
468         unsigned revertLength = 0;
469         float revertWidth = 0;
470         while (m_fragments.size()) {
471             const auto& current = m_fragments.last();
472             if (current == m_lastCompleteFragment)
473                 break;
474             revertLength += current.end() - current.start();
475             revertWidth += current.width();
476             m_fragments.removeLast();
477         }
478         m_runsWidth -= revertWidth;
479         if (revertLength)
480             revertRuns(runs, revertLength, revertWidth);
481         return m_lastCompleteFragment;
482     }
483
484     void removeTrailingWhitespace(Layout::RunVector& runs)
485     {
486         if (!m_trailingWhitespaceLength)
487             return;
488         revertRuns(runs, m_trailingWhitespaceLength, m_trailingWhitespaceWidth);
489         m_runsWidth -= m_trailingWhitespaceWidth;
490         ASSERT(m_fragments.last().type() == TextFragmentIterator::TextFragment::Whitespace);
491         while (m_fragments.size()) {
492             const auto& current = m_fragments.last();
493             if (current.type() != TextFragmentIterator::TextFragment::Whitespace)
494                 break;
495 #if !ASSERT_DISABLED
496             m_trailingWhitespaceLength -= (current.isCollapsed() ? 1 : current.end() - current.start());
497             m_trailingWhitespaceWidth -= current.width();
498 #endif
499             m_fragments.removeLast();
500         }
501 #if !ASSERT_DISABLED
502         ASSERT(!m_trailingWhitespaceLength);
503         ASSERT(!m_trailingWhitespaceWidth);
504 #endif
505         m_trailingWhitespaceLength = 0;
506         m_trailingWhitespaceWidth = 0;
507     }
508
509 private:
510     float m_availableWidth { 0 };
511     float m_logicalLeftOffset { 0 };
512     TextFragmentIterator::TextFragment m_overflowedFragment;
513     float m_runsWidth { 0 };
514     TextFragmentIterator::TextFragment m_lastCompleteFragment;
515     float m_trailingWhitespaceWidth { 0 }; // Use this to remove trailing whitespace without re-mesuring the text.
516     unsigned m_trailingWhitespaceLength { 0 };
517     float m_collapsedWhitespaceWidth { 0 };
518     // Having one character on the line does not necessarily mean it actually fits.
519     // First character of the first fragment might be forced on to the current line even if it does not fit.
520     bool m_firstCharacterFits { false };
521     Vector<TextFragmentIterator::TextFragment> m_fragments;
522 };
523
524 class FragmentForwardIterator : public std::iterator<std::forward_iterator_tag, unsigned> {
525 public:
526     FragmentForwardIterator(unsigned fragmentIndex)
527         : m_fragmentIndex(fragmentIndex)
528     {
529     }
530
531     FragmentForwardIterator& operator++()
532     {
533         ++m_fragmentIndex;
534         return *this;
535     }
536
537     bool operator!=(const FragmentForwardIterator& other) const { return m_fragmentIndex != other.m_fragmentIndex; }
538     unsigned operator*() const { return m_fragmentIndex; }
539
540 private:
541     unsigned m_fragmentIndex { 0 };
542 };
543
544 static FragmentForwardIterator begin(const TextFragmentIterator::TextFragment& fragment)  { return FragmentForwardIterator(fragment.start()); }
545 static FragmentForwardIterator end(const TextFragmentIterator::TextFragment& fragment)  { return FragmentForwardIterator(fragment.end()); }
546
547 static bool preWrap(const TextFragmentIterator::Style& style)
548 {
549     return style.wrapLines && !style.collapseWhitespace;
550 }
551     
552 static void removeTrailingWhitespace(LineState& lineState, Layout::RunVector& runs, const TextFragmentIterator& textFragmentIterator)
553 {
554     if (!lineState.hasTrailingWhitespace())
555         return;
556
557     // 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.
558     const auto& style = textFragmentIterator.style();
559     bool collapseWhitespace = style.collapseWhitespace | preWrap(style);
560     if (!collapseWhitespace)
561         return;
562
563     if (preWrap(style) && lineState.isWhitespaceOnly())
564         return;
565
566     lineState.removeTrailingWhitespace(runs);
567 }
568
569 static void updateLineConstrains(const RenderBlockFlow& flow, LineState& line, bool isFirstLine)
570 {
571     bool shouldApplyTextIndent = !flow.isAnonymous() || flow.parent()->firstChild() == &flow;
572     LayoutUnit height = flow.logicalHeight();
573     LayoutUnit logicalHeight = flow.minLineHeightForReplacedRenderer(false, 0);
574     float logicalRightOffset = flow.logicalRightOffsetForLine(height, false, logicalHeight);
575     line.setLogicalLeftOffset(flow.logicalLeftOffsetForLine(height, false, logicalHeight) + (shouldApplyTextIndent && isFirstLine ? flow.textIndentOffset() : LayoutUnit(0)));
576     line.setAvailableWidth(std::max<float>(0, logicalRightOffset - line.logicalLeftOffset()));
577 }
578
579 static TextFragmentIterator::TextFragment splitFragmentToFitLine(TextFragmentIterator::TextFragment& fragmentToSplit, float availableWidth, bool keepAtLeastOneCharacter, const TextFragmentIterator& textFragmentIterator)
580 {
581     // FIXME: add surrogate pair support.
582     unsigned start = fragmentToSplit.start();
583     auto it = std::upper_bound(begin(fragmentToSplit), end(fragmentToSplit), availableWidth, [&textFragmentIterator, start](float availableWidth, unsigned index) {
584         // FIXME: use the actual left position of the line (instead of 0) to calculated width. It might give false width for tab characters.
585         return availableWidth < textFragmentIterator.textWidth(start, index + 1, 0);
586     });
587     unsigned splitPosition = (*it);
588     if (keepAtLeastOneCharacter && splitPosition == fragmentToSplit.start())
589         ++splitPosition;
590     return fragmentToSplit.split(splitPosition, textFragmentIterator);
591 }
592
593 enum PreWrapLineBreakRule { Preserve, Ignore };
594
595 static TextFragmentIterator::TextFragment consumeLineBreakIfNeeded(const TextFragmentIterator::TextFragment& fragment, TextFragmentIterator& textFragmentIterator, LineState& line, Layout::RunVector& runs,
596     PreWrapLineBreakRule preWrapLineBreakRule = PreWrapLineBreakRule::Preserve)
597 {
598     if (!fragment.isLineBreak())
599         return fragment;
600
601     if (preWrap(textFragmentIterator.style()) && preWrapLineBreakRule != PreWrapLineBreakRule::Ignore)
602         return fragment;
603
604     // <br> always produces a run. (required by testing output)
605     if (fragment.type() == TextFragmentIterator::TextFragment::HardLineBreak)
606         line.appendFragmentAndCreateRunIfNeeded(fragment, runs);
607     return textFragmentIterator.nextTextFragment();
608 }
609
610 static TextFragmentIterator::TextFragment skipWhitespaceIfNeeded(const TextFragmentIterator::TextFragment& fragment, TextFragmentIterator& textFragmentIterator)
611 {
612     if (!textFragmentIterator.style().collapseWhitespace)
613         return fragment;
614
615     TextFragmentIterator::TextFragment firstNonWhitespaceFragment = fragment;
616     while (firstNonWhitespaceFragment.type() == TextFragmentIterator::TextFragment::Whitespace)
617         firstNonWhitespaceFragment = textFragmentIterator.nextTextFragment();
618     return firstNonWhitespaceFragment;
619 }
620
621 static TextFragmentIterator::TextFragment firstFragment(TextFragmentIterator& textFragmentIterator, LineState& currentLine, const LineState& previousLine, Layout::RunVector& runs)
622 {
623     // Handle overflowed fragment from previous line.
624     TextFragmentIterator::TextFragment firstFragment(previousLine.overflowedFragment());
625
626     if (firstFragment.isEmpty())
627         firstFragment = textFragmentIterator.nextTextFragment();
628     else if (firstFragment.type() == TextFragmentIterator::TextFragment::Whitespace && preWrap(textFragmentIterator.style()) && previousLine.firstCharacterFits()) {
629         // 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.
630         firstFragment = textFragmentIterator.nextTextFragment();
631         // If skipping the whitespace puts us on a newline, skip the newline too as we already wrapped the line.
632         firstFragment = consumeLineBreakIfNeeded(firstFragment, textFragmentIterator, currentLine, runs, PreWrapLineBreakRule::Ignore);
633     }
634     return skipWhitespaceIfNeeded(firstFragment, textFragmentIterator);
635 }
636
637 static void forceFragmentToLine(LineState& line, TextFragmentIterator& textFragmentIterator, Layout::RunVector& runs, const TextFragmentIterator::TextFragment& fragment)
638 {
639     line.appendFragmentAndCreateRunIfNeeded(fragment, runs);
640     // Check if there are more fragments to add to the current line.
641     auto nextFragment = textFragmentIterator.nextTextFragment();
642     if (fragment.overlapsToNextRenderer()) {
643         while (true) {
644             if (nextFragment.type() != fragment.type())
645                 break;
646             line.appendFragmentAndCreateRunIfNeeded(nextFragment, runs);
647             // Does it overlap to the next segment?
648             if (!nextFragment.overlapsToNextRenderer())
649                 return;
650             nextFragment = textFragmentIterator.nextTextFragment();
651         }
652     }
653     // 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.
654     nextFragment = skipWhitespaceIfNeeded(nextFragment, textFragmentIterator);
655     nextFragment = consumeLineBreakIfNeeded(nextFragment, textFragmentIterator, line, runs);
656     line.setOverflowedFragment(nextFragment);
657 }
658
659 static bool createLineRuns(LineState& line, const LineState& previousLine, Layout::RunVector& runs, TextFragmentIterator& textFragmentIterator)
660 {
661     const auto& style = textFragmentIterator.style();
662     line.setCollapedWhitespaceWidth(style.spaceWidth + style.wordSpacing);
663     bool lineCanBeWrapped = style.wrapLines || style.breakWordOnOverflow;
664     auto fragment = firstFragment(textFragmentIterator, line, previousLine, runs);
665     while (fragment.type() != TextFragmentIterator::TextFragment::ContentEnd) {
666         // Hard linebreak.
667         if (fragment.isLineBreak()) {
668             // 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.)
669             if (line.isEmpty() || fragment.type() == TextFragmentIterator::TextFragment::HardLineBreak) {
670                 if (style.textAlign == RIGHT || style.textAlign == WEBKIT_RIGHT)
671                     line.removeTrailingWhitespace(runs);
672                 line.appendFragmentAndCreateRunIfNeeded(fragment, runs);
673             }
674             break;
675         }
676         if (lineCanBeWrapped && !line.fits(fragment.width())) {
677             // Overflow wrapping behaviour:
678             // 1. Whitesapce collapse on: whitespace is skipped. Jump to next line.
679             // 2. Whitespace collapse off: whitespace is wrapped.
680             // 3. First, non-whitespace fragment is either wrapped or kept on the line. (depends on overflow-wrap)
681             // 4. Non-whitespace fragment when there's already another fragment on the line gets pushed to the next line.
682             bool emptyLine = line.isEmpty();
683             // Whitespace fragment.
684             if (fragment.type() == TextFragmentIterator::TextFragment::Whitespace) {
685                 if (!style.collapseWhitespace) {
686                     // Split the fragment; (modified)fragment stays on this line, overflowedFragment is pushed to next line.
687                     line.setOverflowedFragment(splitFragmentToFitLine(fragment, line.availableWidth() - line.width(), emptyLine, textFragmentIterator));
688                     line.appendFragmentAndCreateRunIfNeeded(fragment, runs);
689                 }
690                 // When whitespace collapse is on, whitespace that doesn't fit is simply skipped.
691                 break;
692             }
693             // Non-whitespace fragment. (!style.wrapLines: bug138102(preserve existing behavior)
694             if ((emptyLine && style.breakWordOnOverflow) || !style.wrapLines) {
695                 // Split the fragment; (modified)fragment stays on this line, overflowedFragment is pushed to next line.
696                 line.setOverflowedFragment(splitFragmentToFitLine(fragment, line.availableWidth() - line.width(), emptyLine, textFragmentIterator));
697                 line.appendFragmentAndCreateRunIfNeeded(fragment, runs);
698                 break;
699             }
700             // Non-breakable non-whitespace first fragment. Add it to the current line. -it overflows though.
701             ASSERT(fragment.type() == TextFragmentIterator::TextFragment::NonWhitespace);
702             if (emptyLine) {
703                 forceFragmentToLine(line, textFragmentIterator, runs, fragment);
704                 break;
705             }
706             // Non-breakable non-whitespace fragment when there's already content on the line. Push it to the next line.
707             if (line.lastFragment().overlapsToNextRenderer()) {
708                 // Check if this fragment is a continuation of a previous segment. In such cases, we need to remove them all.
709                 const auto& lastCompleteFragment = line.revertToLastCompleteFragment(runs);
710                 textFragmentIterator.revertToEndOfFragment(lastCompleteFragment);
711                 break;
712             }
713             line.setOverflowedFragment(fragment);
714             break;
715         }
716         line.appendFragmentAndCreateRunIfNeeded(fragment, runs);
717         // Find the next text fragment.
718         fragment = textFragmentIterator.nextTextFragment(line.width());
719     }
720     return (fragment.type() == TextFragmentIterator::TextFragment::ContentEnd && line.overflowedFragment().isEmpty()) || line.overflowedFragment().type() == TextFragmentIterator::TextFragment::ContentEnd;
721 }
722
723 static void closeLineEndingAndAdjustRuns(LineState& line, Layout::RunVector& runs, unsigned previousRunCount, unsigned& lineCount, const TextFragmentIterator& textFragmentIterator)
724 {
725     if (previousRunCount == runs.size())
726         return;
727     ASSERT(runs.size());
728     removeTrailingWhitespace(line, runs, textFragmentIterator);
729     if (!runs.size())
730         return;
731     // Adjust runs' position by taking line's alignment into account.
732     if (float lineLogicalLeft = computeLineLeft(textFragmentIterator.style().textAlign, line.availableWidth(), line.width(), line.logicalLeftOffset())) {
733         for (unsigned i = previousRunCount; i < runs.size(); ++i) {
734             runs[i].logicalLeft += lineLogicalLeft;
735             runs[i].logicalRight += lineLogicalLeft;
736         }
737     }
738     runs.last().isEndOfLine = true;
739     ++lineCount;
740 }
741
742 static void createTextRuns(Layout::RunVector& runs, RenderBlockFlow& flow, unsigned& lineCount)
743 {
744     LayoutUnit borderAndPaddingBefore = flow.borderAndPaddingBefore();
745     LayoutUnit lineHeight = lineHeightFromFlow(flow);
746     LineState line;
747     bool isEndOfContent = false;
748     TextFragmentIterator textFragmentIterator = TextFragmentIterator(flow);
749     do {
750         flow.setLogicalHeight(lineHeight * lineCount + borderAndPaddingBefore);
751         LineState previousLine = line;
752         unsigned previousRunCount = runs.size();
753         line = LineState();
754         updateLineConstrains(flow, line, !lineCount);
755         isEndOfContent = createLineRuns(line, previousLine, runs, textFragmentIterator);
756         closeLineEndingAndAdjustRuns(line, runs, previousRunCount, lineCount, textFragmentIterator);
757     } while (!isEndOfContent);
758 }
759
760 std::unique_ptr<Layout> create(RenderBlockFlow& flow)
761 {
762     unsigned lineCount = 0;
763     Layout::RunVector runs;
764
765     createTextRuns(runs, flow, lineCount);
766     for (auto& renderer : childrenOfType<RenderObject>(flow)) {
767         ASSERT(is<RenderText>(renderer) || is<RenderLineBreak>(renderer));
768         renderer.clearNeedsLayout();
769     }
770     return Layout::create(runs, lineCount);
771 }
772
773 std::unique_ptr<Layout> Layout::create(const RunVector& runVector, unsigned lineCount)
774 {
775     void* slot = WTF::fastMalloc(sizeof(Layout) + sizeof(Run) * runVector.size());
776     return std::unique_ptr<Layout>(new (NotNull, slot) Layout(runVector, lineCount));
777 }
778
779 Layout::Layout(const RunVector& runVector, unsigned lineCount)
780     : m_lineCount(lineCount)
781     , m_runCount(runVector.size())
782 {
783     memcpy(m_runs, runVector.data(), m_runCount * sizeof(Run));
784 }
785
786 #ifndef NDEBUG
787 static void printReason(AvoidanceReason reason, TextStream& stream)
788 {
789     switch (reason) {
790     case FlowIsInsideRegion:
791         stream << "flow is inside region";
792         break;
793     case FlowHasHorizonalWritingMode:
794         stream << "horizontal writing mode";
795         break;
796     case FlowHasOutline:
797         stream << "outline";
798         break;
799     case FlowIsRuby:
800         stream << "ruby";
801         break;
802     case FlowIsPaginated:
803         stream << "paginated";
804         break;
805     case FlowHasTextOverflow:
806         stream << "text-overflow";
807         break;
808     case FlowIsDepricatedFlexBox:
809         stream << "depricatedFlexBox";
810         break;
811     case FlowParentIsPlaceholderElement:
812         stream << "placeholder element";
813         break;
814     case FlowParentIsTextAreaWithWrapping:
815         stream << "wrapping textarea";
816         break;
817     case FlowHasNonSupportedChild:
818         stream << "nested renderers";
819         break;
820     case FlowHasUnsupportedFloat:
821         stream << "complicated float";
822         break;
823     case FlowHasUnsupportedUnderlineDecoration:
824         stream << "text-underline-position: under";
825         break;
826     case FlowIsJustifyAligned:
827         stream << "text-align: justify";
828         break;
829     case FlowHasOverflowVisible:
830         stream << "overflow: visible";
831         break;
832     case FlowIsNotLTR:
833         stream << "dir is not LTR";
834         break;
835     case FlowHasLineBoxContainProperty:
836         stream << "line-box-contain property";
837         break;
838     case FlowIsNotTopToBottom:
839         stream << "non top-to-bottom flow";
840         break;
841     case FlowHasLineBreak:
842         stream << "line-break property";
843         break;
844     case FlowHasWordBreak:
845         stream << "word-break property";
846         break;
847     case FlowHasNonNormalUnicodeBiDi:
848         stream << "non-normal Unicode bidi";
849         break;
850     case FlowHasRTLOrdering:
851         stream << "-webkit-rtl-ordering";
852         break;
853     case FlowHasLineAlignEdges:
854         stream << "-webkit-line-align edges";
855         break;
856     case FlowHasLineSnap:
857         stream << "-webkit-line-snap property";
858         break;
859     case FlowHasHypensAuto:
860         stream << "hyphen: auto";
861         break;
862     case FlowHasTextEmphasisFillOrMark:
863         stream << "text-emphasis (fill/mark)";
864         break;
865     case FlowHasPseudoFirstLine:
866         stream << "first-line";
867         break;
868     case FlowHasPseudoFirstLetter:
869         stream << "first-letter";
870         break;
871     case FlowHasTextCombine:
872         stream << "text combine";
873         break;
874     case FlowHasTextFillBox:
875         stream << "background-color (text-fill)";
876         break;
877     case FlowHasBorderFitLines:
878         stream << "-webkit-border-fit";
879         break;
880     case FlowHasNonAutoLineBreak:
881         stream << "line-break is not auto";
882         break;
883     case FlowHasNonAutoTrailingWord:
884         stream << "-apple-trailing-word is not auto";
885         break;
886     case FlowHasSVGFont:
887         stream << "SVG font";
888         break;
889     case FlowTextHasNoBreakSpace:
890         stream << "No-break-space character";
891         break;
892     case FlowTextHasSoftHyphen:
893         stream << "soft hyphen character";
894         break;
895     case FlowTextHasDirectionCharacter:
896         stream << "direction character";
897         break;
898     case FlowIsMissingPrimaryFont:
899         stream << "missing primary font";
900         break;
901     case FlowFontIsMissingGlyph:
902         stream << "missing glyph";
903         break;
904     case FlowTextIsCombineText:
905         stream << "text is combine";
906         break;
907     case FlowTextIsRenderCounter:
908         stream << "unsupported RenderCounter";
909         break;
910     case FlowTextIsRenderQuote:
911         stream << "unsupported RenderQuote";
912         break;
913     case FlowTextIsTextFragment:
914         stream << "unsupported TextFragment";
915         break;
916     case FlowTextIsSVGInlineText:
917         stream << "unsupported SVGInlineText";
918         break;
919     case FlowFontIsNotSimple:
920         stream << "complext font";
921         break;
922     case FlowHasTextShadow:
923         stream << "text-shadow";
924         break;
925     case FlowChildIsSelected:
926         stream << "selected content";
927         break;
928     case FlowTextIsEmpty:
929     case FlowHasNoChild:
930     case FlowHasNoParent:
931     case FeatureIsDisabled:
932     default:
933         break;
934     }
935 }
936
937 static void printReasons(AvoidanceReasonFlags reasons, TextStream& stream)
938 {
939     bool first = true;
940     for (auto reasonItem = EndOfReasons >> 1; reasonItem != NoReason; reasonItem >>= 1) {
941         if (!(reasons & reasonItem))
942             continue;
943         stream << (first ? " " : ", ");
944         first = false;
945         printReason(reasonItem, stream);
946     }
947 }
948
949 static void printTextForSubtree(const RenderObject& renderer, unsigned& charactersLeft, TextStream& stream)
950 {
951     if (!charactersLeft)
952         return;
953     if (is<RenderText>(renderer)) {
954         String text = downcast<RenderText>(renderer).text();
955         text = text.stripWhiteSpace();
956         unsigned len = std::min(charactersLeft, text.length());
957         stream << text.left(len);
958         charactersLeft -= len;
959         return;
960     }
961     if (!is<RenderElement>(renderer))
962         return;
963     for (const auto* child = downcast<RenderElement>(renderer).firstChild(); child; child = child->nextSibling())
964         printTextForSubtree(*child, charactersLeft, stream);
965 }
966
967 static unsigned textLengthForSubtree(const RenderObject& renderer)
968 {
969     if (is<RenderText>(renderer))
970         return downcast<RenderText>(renderer).textLength();
971     if (!is<RenderElement>(renderer))
972         return 0;
973     unsigned textLength = 0;
974     for (const auto* child = downcast<RenderElement>(renderer).firstChild(); child; child = child->nextSibling())
975         textLength += textLengthForSubtree(*child);
976     return textLength;
977 }
978
979 static void collectNonEmptyLeafRenderBlockFlows(const RenderObject& renderer, HashSet<const RenderBlockFlow*>& leafRenderers)
980 {
981     if (is<RenderText>(renderer)) {
982         if (!downcast<RenderText>(renderer).textLength())
983             return;
984         // Find RenderBlockFlow ancestor.
985         for (const auto* current = renderer.parent(); current; current = current->parent()) {
986             if (!is<RenderBlockFlow>(current))
987                 continue;
988             leafRenderers.add(downcast<RenderBlockFlow>(current));
989             break;
990         }
991         return;
992     }
993     if (!is<RenderElement>(renderer))
994         return;
995     for (const auto* child = downcast<RenderElement>(renderer).firstChild(); child; child = child->nextSibling())
996         collectNonEmptyLeafRenderBlockFlows(*child, leafRenderers);
997 }
998
999 static void collectNonEmptyLeafRenderBlockFlowsForCurrentPage(HashSet<const RenderBlockFlow*>& leafRenderers)
1000 {
1001     for (const auto* document : Document::allDocuments()) {
1002         if (!document->renderView() || document->inPageCache())
1003             continue;
1004         if (!document->isHTMLDocument() && !document->isXHTMLDocument())
1005             continue;
1006         collectNonEmptyLeafRenderBlockFlows(*document->renderView(), leafRenderers);
1007     }
1008 }
1009
1010 void toggleSimpleLineLayout()
1011 {
1012     for (const auto* document : Document::allDocuments()) {
1013         auto* settings = document->settings();
1014         if (!settings)
1015             continue;
1016         settings->setSimpleLineLayoutEnabled(!settings->simpleLineLayoutEnabled());
1017     }
1018 }
1019
1020 void printSimpleLineLayoutBlockList()
1021 {
1022     HashSet<const RenderBlockFlow*> leafRenderers;
1023     collectNonEmptyLeafRenderBlockFlowsForCurrentPage(leafRenderers);
1024     if (!leafRenderers.size()) {
1025         WTFLogAlways("No text found in this document\n");
1026         return;
1027     }
1028     TextStream stream;
1029     stream << "---------------------------------------------------\n";
1030     for (const auto* flow : leafRenderers) {
1031         auto reason = canUseForWithReason(*flow, IncludeReasons::All);
1032         if (reason == NoReason)
1033             continue;
1034         unsigned printedLength = 30;
1035         stream << "\"";
1036         printTextForSubtree(*flow, printedLength, stream);
1037         for (;printedLength > 0; --printedLength)
1038             stream << " ";
1039         stream << "\"(" << textLengthForSubtree(*flow) << "):";
1040         printReasons(reason, stream);
1041         stream << "\n";
1042     }
1043     stream << "---------------------------------------------------\n";
1044     WTFLogAlways("%s", stream.release().utf8().data());
1045 }
1046
1047 void printSimpleLineLayoutCoverage()
1048 {
1049     HashSet<const RenderBlockFlow*> leafRenderers;
1050     collectNonEmptyLeafRenderBlockFlowsForCurrentPage(leafRenderers);
1051     if (!leafRenderers.size()) {
1052         WTFLogAlways("No text found in this document\n");
1053         return;
1054     }
1055     TextStream stream;
1056     HashMap<AvoidanceReason, unsigned> flowStatistics;
1057     unsigned textLength = 0;
1058     unsigned unsupportedTextLength = 0;
1059     unsigned numberOfUnsupportedLeafBlocks = 0;
1060     for (const auto* flow : leafRenderers) {
1061         auto flowLength = textLengthForSubtree(*flow);
1062         textLength += flowLength;
1063         auto reasons = canUseForWithReason(*flow, IncludeReasons::All);
1064         if (reasons == NoReason)
1065             continue;
1066         ++numberOfUnsupportedLeafBlocks;
1067         unsupportedTextLength += flowLength;
1068         for (auto reasonItem = EndOfReasons >> 1; reasonItem != NoReason; reasonItem >>= 1) {
1069             if (!(reasons & reasonItem))
1070                 continue;
1071             auto result = flowStatistics.add(reasonItem, flowLength);
1072             if (!result.isNewEntry)
1073                 result.iterator->value += flowLength;
1074         }
1075     }
1076     stream << "---------------------------------------------------\n";
1077     stream << "Number of text blocks: total(" <<  leafRenderers.size() << ") non-simple(" << numberOfUnsupportedLeafBlocks << ")\nText length: total(" <<
1078         textLength << ") non-simple(" << unsupportedTextLength << ")\n";
1079     for (const auto reasonEntry : flowStatistics) {
1080         printReason(reasonEntry.key, stream);
1081         stream << ": " << (float)reasonEntry.value / (float)textLength * 100 << "%\n";
1082     }
1083     stream << "simple line layout coverage: " << (float)(textLength - unsupportedTextLength) / (float)textLength * 100 << "%\n";
1084     stream << "---------------------------------------------------\n";
1085     WTFLogAlways("%s", stream.release().utf8().data());
1086 }
1087 #endif
1088 }
1089 }