Spelling dots are drawn in the wrong place
[WebKit-https.git] / Source / WebCore / rendering / SimpleLineLayout.cpp
index e7f1640..e3fea3f 100644 (file)
@@ -26,6 +26,7 @@
 #include "config.h"
 #include "SimpleLineLayout.h"
 
+#include "DocumentMarkerController.h"
 #include "FontCache.h"
 #include "Frame.h"
 #include "GraphicsContext.h"
@@ -40,7 +41,9 @@
 #include "PaintInfo.h"
 #include "RenderBlockFlow.h"
 #include "RenderChildIterator.h"
+#include "RenderFragmentedFlow.h"
 #include "RenderLineBreak.h"
+#include "RenderMultiColumnFlow.h"
 #include "RenderStyle.h"
 #include "RenderText.h"
 #include "RenderTextControl.h"
 #include "Settings.h"
 #include "SimpleLineLayoutFlowContents.h"
 #include "SimpleLineLayoutFunctions.h"
+#include "SimpleLineLayoutResolver.h"
 #include "SimpleLineLayoutTextFragmentIterator.h"
 #include "Text.h"
 #include "TextPaintStyle.h"
-#include "TextStream.h"
+#include <pal/Logging.h>
 
 namespace WebCore {
 namespace SimpleLineLayout {
 
 #ifndef NDEBUG
-void printSimpleLineLayoutCoverage();
-void printSimpleLineLayoutBlockList();
-void toggleSimpleLineLayout();
-#endif
-
-enum AvoidanceReason_ : uint64_t {
-    FlowIsInsideRegion                    = 1LLU  << 0,
-    FlowHasHorizonalWritingMode           = 1LLU  << 1,
-    FlowHasOutline                        = 1LLU  << 2,
-    FlowIsRuby                            = 1LLU  << 3,
-    FlowIsPaginated                       = 1LLU  << 4,
-    FlowHasTextOverflow                   = 1LLU  << 5,
-    FlowIsDepricatedFlexBox               = 1LLU  << 6,
-    FlowParentIsPlaceholderElement        = 1LLU  << 7,
-    FlowParentIsTextAreaWithWrapping      = 1LLU  << 8,
-    FlowHasNonSupportedChild              = 1LLU  << 9,
-    FlowHasUnsupportedFloat               = 1LLU  << 10,
-    FlowHasUnsupportedUnderlineDecoration = 1LLU  << 11,
-    FlowHasJustifiedNonLatinText          = 1LLU  << 12,
-    FlowHasOverflowNotVisible             = 1LLU  << 13,
-    FlowHasWebKitNBSPMode                 = 1LLU  << 14,
-    FlowIsNotLTR                          = 1LLU  << 15,
-    FlowHasLineBoxContainProperty         = 1LLU  << 16,
-    FlowIsNotTopToBottom                  = 1LLU  << 17,
-    FlowHasLineBreak                      = 1LLU  << 18,
-    FlowHasNonNormalUnicodeBiDi           = 1LLU  << 19,
-    FlowHasRTLOrdering                    = 1LLU  << 20,
-    FlowHasLineAlignEdges                 = 1LLU  << 21,
-    FlowHasLineSnap                       = 1LLU  << 22,
-    FlowHasTextEmphasisFillOrMark         = 1LLU  << 23,
-    FlowHasTextShadow                     = 1LLU  << 24,
-    FlowHasPseudoFirstLine                = 1LLU  << 25,
-    FlowHasPseudoFirstLetter              = 1LLU  << 26,
-    FlowHasTextCombine                    = 1LLU  << 27,
-    FlowHasTextFillBox                    = 1LLU  << 28,
-    FlowHasBorderFitLines                 = 1LLU  << 29,
-    FlowHasNonAutoLineBreak               = 1LLU  << 30,
-    FlowHasNonAutoTrailingWord            = 1LLU  << 31,
-    FlowHasSVGFont                        = 1LLU  << 32,
-    FlowTextIsEmpty                       = 1LLU  << 33,
-    FlowTextHasSoftHyphen                 = 1LLU  << 34,
-    FlowTextHasDirectionCharacter         = 1LLU  << 35,
-    FlowIsMissingPrimaryFont              = 1LLU  << 36,
-    FlowPrimaryFontIsInsufficient         = 1LLU  << 37,
-    FlowTextIsCombineText                 = 1LLU  << 38,
-    FlowTextIsRenderCounter               = 1LLU  << 39,
-    FlowTextIsRenderQuote                 = 1LLU  << 40,
-    FlowTextIsTextFragment                = 1LLU  << 41,
-    FlowTextIsSVGInlineText               = 1LLU  << 42,
-    FlowHasComplexFontCodePath            = 1LLU  << 43,
-    FeatureIsDisabled                     = 1LLU  << 44,
-    FlowHasNoParent                       = 1LLU  << 45,
-    FlowHasNoChild                        = 1LLU  << 46,
-    FlowChildIsSelected                   = 1LLU  << 47,
-    FlowHasHangingPunctuation             = 1LLU  << 48,
-    FlowFontHasOverflowGlyph              = 1LLU  << 49,
-    FlowTextHasSurrogatePair              = 1LLU  << 50,
-    EndOfReasons                          = 1LLU  << 51
-};
-const unsigned NoReason = 0;
-
-typedef uint64_t AvoidanceReason;
-typedef uint64_t AvoidanceReasonFlags;
-
-enum class IncludeReasons { First , All };
-
-#ifndef NDEBUG
 #define SET_REASON_AND_RETURN_IF_NEEDED(reason, reasons, includeReasons) { \
         reasons |= reason; \
         if (includeReasons == IncludeReasons::First) \
@@ -175,6 +112,15 @@ static AvoidanceReasonFlags canUseForText(const CharacterType* text, unsigned le
 {
     AvoidanceReasonFlags reasons = { };
     auto& primaryFont = fontCascade.primaryFont();
+    auto& fontMetrics = primaryFont.fontMetrics();
+    auto availableSpaceForGlyphAscent = fontMetrics.ascent();
+    auto availableSpaceForGlyphDescent = fontMetrics.descent();
+    if (lineHeightConstraint) {
+        auto lineHeightPadding = *lineHeightConstraint - fontMetrics.height();
+        availableSpaceForGlyphAscent += lineHeightPadding / 2;
+        availableSpaceForGlyphDescent += lineHeightPadding / 2;
+    }
+
     for (unsigned i = 0; i < length; ++i) {
         auto character = text[i];
         if (FontCascade::treatAsSpace(character))
@@ -191,8 +137,11 @@ static AvoidanceReasonFlags canUseForText(const CharacterType* text, unsigned le
         if (!glyphData.isValid() || glyphData.font != &primaryFont)
             SET_REASON_AND_RETURN_IF_NEEDED(FlowPrimaryFontIsInsufficient, reasons, includeReasons);
 
-        if (lineHeightConstraint && primaryFont.boundsForGlyph(glyphData.glyph).height() > *lineHeightConstraint)
-            SET_REASON_AND_RETURN_IF_NEEDED(FlowFontHasOverflowGlyph, reasons, includeReasons);
+        if (lineHeightConstraint) {
+            auto bounds = primaryFont.boundsForGlyph(glyphData.glyph);
+            if (ceilf(-bounds.y()) > availableSpaceForGlyphAscent || ceilf(bounds.maxY()) > availableSpaceForGlyphDescent)
+                SET_REASON_AND_RETURN_IF_NEEDED(FlowFontHasOverflowGlyph, reasons, includeReasons);
+        }
     }
     return reasons;
 }
@@ -210,15 +159,15 @@ static AvoidanceReasonFlags canUseForFontAndText(const RenderBlockFlow& flow, In
     // We assume that all lines have metrics based purely on the primary font.
     const auto& style = flow.style();
     auto& fontCascade = style.fontCascade();
-    if (fontCascade.primaryFont().isLoading())
+    if (fontCascade.primaryFont().isInterstitial())
         SET_REASON_AND_RETURN_IF_NEEDED(FlowIsMissingPrimaryFont, reasons, includeReasons);
     std::optional<float> lineHeightConstraint;
     if (style.lineBoxContain() & LineBoxContainGlyphs)
         lineHeightConstraint = lineHeightFromFlow(flow).toFloat();
-    bool flowIsJustified = style.textAlign() == JUSTIFY;
+    bool flowIsJustified = style.textAlign() == TextAlignMode::Justify;
     for (const auto& textRenderer : childrenOfType<RenderText>(flow)) {
         // FIXME: Do not return until after checking all children.
-        if (!textRenderer.textLength())
+        if (textRenderer.text().isEmpty())
             SET_REASON_AND_RETURN_IF_NEEDED(FlowTextIsEmpty, reasons, includeReasons);
         if (textRenderer.isCombineText())
             SET_REASON_AND_RETURN_IF_NEEDED(FlowTextIsCombineText, reasons, includeReasons);
@@ -234,7 +183,7 @@ static AvoidanceReasonFlags canUseForFontAndText(const RenderBlockFlow& flow, In
             // No need to check the code path at this point. We already know it can't be simple.
             SET_REASON_AND_RETURN_IF_NEEDED(FlowHasComplexFontCodePath, reasons, includeReasons);
         } else {
-            TextRun run(textRenderer.text());
+            TextRun run(String(textRenderer.text()));
             run.setCharacterScanForCodePath(false);
             if (style.fontCascade().codePath(run) != FontCascade::Simple)
                 SET_REASON_AND_RETURN_IF_NEEDED(FlowHasComplexFontCodePath, reasons, includeReasons);
@@ -250,12 +199,12 @@ static AvoidanceReasonFlags canUseForFontAndText(const RenderBlockFlow& flow, In
 static AvoidanceReasonFlags canUseForStyle(const RenderStyle& style, IncludeReasons includeReasons)
 {
     AvoidanceReasonFlags reasons = { };
-    if (style.textOverflow())
+    if (style.textOverflow() == TextOverflow::Ellipsis)
         SET_REASON_AND_RETURN_IF_NEEDED(FlowHasTextOverflow, reasons, includeReasons);
-    if ((style.textDecorationsInEffect() & TextDecorationUnderline) && style.textUnderlinePosition() == TextUnderlinePositionUnder)
+    if ((style.textDecorationsInEffect() & TextDecoration::Underline) && style.textUnderlinePosition() == TextUnderlinePosition::Under)
         SET_REASON_AND_RETURN_IF_NEEDED(FlowHasUnsupportedUnderlineDecoration, reasons, includeReasons);
     // Non-visible overflow should be pretty easy to support.
-    if (style.overflowX() != OVISIBLE || style.overflowY() != OVISIBLE)
+    if (style.overflowX() != Overflow::Visible || style.overflowY() != Overflow::Visible)
         SET_REASON_AND_RETURN_IF_NEEDED(FlowHasOverflowNotVisible, reasons, includeReasons);
     if (!style.isLeftToRightDirection())
         SET_REASON_AND_RETURN_IF_NEEDED(FlowIsNotLTR, reasons, includeReasons);
@@ -263,49 +212,54 @@ static AvoidanceReasonFlags canUseForStyle(const RenderStyle& style, IncludeReas
         SET_REASON_AND_RETURN_IF_NEEDED(FlowHasLineBoxContainProperty, reasons, includeReasons);
     if (style.writingMode() != TopToBottomWritingMode)
         SET_REASON_AND_RETURN_IF_NEEDED(FlowIsNotTopToBottom, reasons, includeReasons);
-    if (style.lineBreak() != LineBreakAuto)
+    if (style.lineBreak() != LineBreak::Auto)
         SET_REASON_AND_RETURN_IF_NEEDED(FlowHasLineBreak, reasons, includeReasons);
     if (style.unicodeBidi() != UBNormal)
         SET_REASON_AND_RETURN_IF_NEEDED(FlowHasNonNormalUnicodeBiDi, reasons, includeReasons);
-    if (style.rtlOrdering() != LogicalOrder)
+    if (style.rtlOrdering() != Order::Logical)
         SET_REASON_AND_RETURN_IF_NEEDED(FlowHasRTLOrdering, reasons, includeReasons);
-    if (style.lineAlign() != LineAlignNone)
+    if (style.lineAlign() != LineAlign::None)
         SET_REASON_AND_RETURN_IF_NEEDED(FlowHasLineAlignEdges, reasons, includeReasons);
-    if (style.lineSnap() != LineSnapNone)
+    if (style.lineSnap() != LineSnap::None)
         SET_REASON_AND_RETURN_IF_NEEDED(FlowHasLineSnap, reasons, includeReasons);
-    if (style.textEmphasisFill() != TextEmphasisFillFilled || style.textEmphasisMark() != TextEmphasisMarkNone)
+    if (style.textEmphasisFill() != TextEmphasisFill::Filled || style.textEmphasisMark() != TextEmphasisMark::None)
         SET_REASON_AND_RETURN_IF_NEEDED(FlowHasTextEmphasisFillOrMark, reasons, includeReasons);
     if (style.textShadow())
         SET_REASON_AND_RETURN_IF_NEEDED(FlowHasTextShadow, reasons, includeReasons);
-    if (style.hasPseudoStyle(FIRST_LINE))
+    if (style.hasPseudoStyle(PseudoId::FirstLine))
         SET_REASON_AND_RETURN_IF_NEEDED(FlowHasPseudoFirstLine, reasons, includeReasons);
-    if (style.hasPseudoStyle(FIRST_LETTER))
+    if (style.hasPseudoStyle(PseudoId::FirstLetter))
         SET_REASON_AND_RETURN_IF_NEEDED(FlowHasPseudoFirstLetter, reasons, includeReasons);
     if (style.hasTextCombine())
         SET_REASON_AND_RETURN_IF_NEEDED(FlowHasTextCombine, reasons, includeReasons);
-    if (style.backgroundClip() == TextFillBox)
+    if (style.backgroundClip() == FillBox::Text)
         SET_REASON_AND_RETURN_IF_NEEDED(FlowHasTextFillBox, reasons, includeReasons);
-    if (style.borderFit() == BorderFitLines)
+    if (style.borderFit() == BorderFit::Lines)
         SET_REASON_AND_RETURN_IF_NEEDED(FlowHasBorderFitLines, reasons, includeReasons);
-    if (style.lineBreak() != LineBreakAuto)
+    if (style.lineBreak() != LineBreak::Auto)
         SET_REASON_AND_RETURN_IF_NEEDED(FlowHasNonAutoLineBreak, reasons, includeReasons);
-    if (style.nbspMode() != NBNORMAL)
+    if (style.nbspMode() != NBSPMode::Normal)
         SET_REASON_AND_RETURN_IF_NEEDED(FlowHasWebKitNBSPMode, reasons, includeReasons);
 #if ENABLE(CSS_TRAILING_WORD)
     if (style.trailingWord() != TrailingWord::Auto)
         SET_REASON_AND_RETURN_IF_NEEDED(FlowHasNonAutoTrailingWord, reasons, includeReasons);
 #endif
+    if (style.hyphens() == Hyphens::Auto) {
+        auto textReasons = canUseForText(style.hyphenString(), style.fontCascade(), std::nullopt, false, includeReasons);
+        if (textReasons != NoReason)
+            SET_REASON_AND_RETURN_IF_NEEDED(textReasons, reasons, includeReasons);
+    }
     return reasons;
 }
 
-static AvoidanceReasonFlags canUseForWithReason(const RenderBlockFlow& flow, IncludeReasons includeReasons)
+AvoidanceReasonFlags canUseForWithReason(const RenderBlockFlow& flow, IncludeReasons includeReasons)
 {
 #ifndef NDEBUG
     static std::once_flag onceFlag;
     std::call_once(onceFlag, [] {
-        registerNotifyCallback("com.apple.WebKit.showSimpleLineLayoutCoverage", printSimpleLineLayoutCoverage);
-        registerNotifyCallback("com.apple.WebKit.showSimpleLineLayoutReasons", printSimpleLineLayoutBlockList);
-        registerNotifyCallback("com.apple.WebKit.toggleSimpleLineLayout", toggleSimpleLineLayout);
+        PAL::registerNotifyCallback("com.apple.WebKit.showSimpleLineLayoutCoverage", WTF::Function<void()> { printSimpleLineLayoutCoverage });
+        PAL::registerNotifyCallback("com.apple.WebKit.showSimpleLineLayoutReasons", WTF::Function<void()> { printSimpleLineLayoutBlockList });
+        PAL::registerNotifyCallback("com.apple.WebKit.toggleSimpleLineLayout", WTF::Function<void()> { toggleSimpleLineLayout });
     });
 #endif
     AvoidanceReasonFlags reasons = { };
@@ -315,15 +269,28 @@ static AvoidanceReasonFlags canUseForWithReason(const RenderBlockFlow& flow, Inc
         SET_REASON_AND_RETURN_IF_NEEDED(FlowHasNoParent, reasons, includeReasons);
     if (!flow.firstChild())
         SET_REASON_AND_RETURN_IF_NEEDED(FlowHasNoChild, reasons, includeReasons);
-    if (flow.flowThreadState() != RenderObject::NotInsideFlowThread)
-        SET_REASON_AND_RETURN_IF_NEEDED(FlowIsInsideRegion, reasons, includeReasons);
+    if (flow.fragmentedFlowState() != RenderObject::NotInsideFragmentedFlow) {
+        auto* fragmentedFlow = flow.enclosingFragmentedFlow();
+        if (!is<RenderMultiColumnFlow>(fragmentedFlow))
+            SET_REASON_AND_RETURN_IF_NEEDED(FlowIsInsideANonMultiColumnThread, reasons, includeReasons);
+        auto& columnThread = downcast<RenderMultiColumnFlow>(*fragmentedFlow);
+        if (columnThread.parent() != &flow.view())
+            SET_REASON_AND_RETURN_IF_NEEDED(MultiColumnFlowIsNotTopLevel, reasons, includeReasons);
+        if (columnThread.hasColumnSpanner())
+            SET_REASON_AND_RETURN_IF_NEEDED(MultiColumnFlowHasColumnSpanner, reasons, includeReasons);
+        auto& style = flow.style();
+        if (style.verticalAlign() != VerticalAlign::Baseline)
+            SET_REASON_AND_RETURN_IF_NEEDED(MultiColumnFlowVerticalAlign, reasons, includeReasons);
+        if (style.isFloating())
+            SET_REASON_AND_RETURN_IF_NEEDED(MultiColumnFlowIsFloating, reasons, includeReasons);
+    }
     if (!flow.isHorizontalWritingMode())
         SET_REASON_AND_RETURN_IF_NEEDED(FlowHasHorizonalWritingMode, reasons, includeReasons);
     if (flow.hasOutline())
         SET_REASON_AND_RETURN_IF_NEEDED(FlowHasOutline, reasons, includeReasons);
     if (flow.isRubyText() || flow.isRubyBase())
         SET_REASON_AND_RETURN_IF_NEEDED(FlowIsRuby, reasons, includeReasons);
-    if (flow.style().hangingPunctuation() != NoHangingPunctuation)
+    if (!flow.style().hangingPunctuation().isEmpty())
         SET_REASON_AND_RETURN_IF_NEEDED(FlowHasHangingPunctuation, reasons, includeReasons);
     
     // Printing does pagination without a flow thread.
@@ -331,7 +298,7 @@ static AvoidanceReasonFlags canUseForWithReason(const RenderBlockFlow& flow, Inc
         SET_REASON_AND_RETURN_IF_NEEDED(FlowIsPaginated, reasons, includeReasons);
     if (flow.firstLineBlock())
         SET_REASON_AND_RETURN_IF_NEEDED(FlowHasPseudoFirstLine, reasons, includeReasons);
-    if (flow.isAnonymousBlock() && flow.parent()->style().textOverflow())
+    if (flow.isAnonymousBlock() && flow.parent()->style().textOverflow() == TextOverflow::Ellipsis)
         SET_REASON_AND_RETURN_IF_NEEDED(FlowHasTextOverflow, reasons, includeReasons);
     if (flow.parent()->isDeprecatedFlexibleBox())
         SET_REASON_AND_RETURN_IF_NEEDED(FlowIsDepricatedFlexBox, reasons, includeReasons);
@@ -347,10 +314,13 @@ static AvoidanceReasonFlags canUseForWithReason(const RenderBlockFlow& flow, Inc
         if (child->selectionState() != RenderObject::SelectionNone)
             SET_REASON_AND_RETURN_IF_NEEDED(FlowChildIsSelected, reasons, includeReasons);
         if (is<RenderText>(*child)) {
+            const auto& renderText = downcast<RenderText>(*child);
+            if (!renderText.document().markers().markersFor(renderText.textNode()).isEmpty())
+                SET_REASON_AND_RETURN_IF_NEEDED(FlowIncludesDocumentMarkers, reasons, includeReasons);
             child = child->nextSibling();
             continue;
         }
-        if (is<RenderLineBreak>(child) && !downcast<RenderLineBreak>(*child).isWBR() && child->style().clear() == CNONE) {
+        if (is<RenderLineBreak>(child) && !downcast<RenderLineBreak>(*child).isWBR() && child->style().clear() == Clear::None) {
             child = child->nextSibling();
             continue;
         }
@@ -389,23 +359,23 @@ bool canUseFor(const RenderBlockFlow& flow)
     return canUseForWithReason(flow, IncludeReasons::First) == NoReason;
 }
 
-static float computeLineLeft(ETextAlign textAlign, float availableWidth, float committedWidth, float logicalLeftOffset)
+static float computeLineLeft(TextAlignMode textAlign, float availableWidth, float committedWidth, float logicalLeftOffset)
 {
     float remainingWidth = availableWidth - committedWidth;
     float left = logicalLeftOffset;
     switch (textAlign) {
-    case LEFT:
-    case WEBKIT_LEFT:
-    case TASTART:
+    case TextAlignMode::Left:
+    case TextAlignMode::WebKitLeft:
+    case TextAlignMode::Start:
         return left;
-    case RIGHT:
-    case WEBKIT_RIGHT:
-    case TAEND:
+    case TextAlignMode::Right:
+    case TextAlignMode::WebKitRight:
+    case TextAlignMode::End:
         return left + std::max<float>(remainingWidth, 0);
-    case CENTER:
-    case WEBKIT_CENTER:
+    case TextAlignMode::Center:
+    case TextAlignMode::WebKitCenter:
         return left + std::max<float>(remainingWidth / 2, 0);
-    case JUSTIFY:
+    case TextAlignMode::Justify:
         ASSERT_NOT_REACHED();
         break;
     }
@@ -413,6 +383,12 @@ static float computeLineLeft(ETextAlign textAlign, float availableWidth, float c
     return 0;
 }
 
+static void revertAllRunsOnCurrentLine(Layout::RunVector& runs)
+{
+    while (!runs.isEmpty() && !runs.last().isEndOfLine)
+        runs.removeLast();
+}
+
 static void revertRuns(Layout::RunVector& runs, unsigned positionToRevertTo, float width)
 {
     while (runs.size()) {
@@ -444,6 +420,8 @@ public:
         ASSERT(!m_fragments);
         m_fragments.emplace();
     }
+    void setHyphenationDisabled() { m_hyphenationDisabled = true; }
+    bool isHyphenationDisabled() const { return m_hyphenationDisabled; }
 
     float availableWidth() const { return m_availableWidth; }
     float logicalLeftOffset() const { return m_logicalLeftOffset; }
@@ -558,11 +536,26 @@ public:
 
     void removeTrailingWhitespace(Layout::RunVector& runs)
     {
-        if (m_lastFragment.type() != TextFragmentIterator::TextFragment::Whitespace || m_lastFragment.end() == m_lastNonWhitespaceFragment.end())
+        if (m_lastFragment.type() != TextFragmentIterator::TextFragment::Whitespace)
+            return;
+        if (m_lastNonWhitespaceFragment) {
+            auto needsReverting = m_lastNonWhitespaceFragment->end() != m_lastFragment.end();
+            // Trailing whitespace fragment might actually have zero length.
+            ASSERT(needsReverting || !m_trailingWhitespaceWidth);
+            if (needsReverting) {
+                revertRuns(runs, m_lastNonWhitespaceFragment->end(), m_trailingWhitespaceWidth);
+                m_runsWidth -= m_trailingWhitespaceWidth;
+            }
+            m_trailingWhitespaceWidth = 0;
+            m_lastFragment = *m_lastNonWhitespaceFragment;
             return;
-        revertRuns(runs, m_lastNonWhitespaceFragment.end(), m_trailingWhitespaceWidth);
-        m_runsWidth -= m_trailingWhitespaceWidth;
-        m_lastFragment = m_lastNonWhitespaceFragment;
+        }
+        // This line is all whitespace.
+        revertAllRunsOnCurrentLine(runs);
+        m_runsWidth = 0;
+        m_trailingWhitespaceWidth = 0;
+        // FIXME: Make m_lastFragment optional.
+        m_lastFragment = TextFragmentIterator::TextFragment();
     }
 
 private:
@@ -577,7 +570,7 @@ private:
     float m_runsWidth { 0 };
     TextFragmentIterator::TextFragment m_overflowedFragment;
     TextFragmentIterator::TextFragment m_lastFragment;
-    TextFragmentIterator::TextFragment m_lastNonWhitespaceFragment;
+    std::optional<TextFragmentIterator::TextFragment> m_lastNonWhitespaceFragment;
     TextFragmentIterator::TextFragment m_lastCompleteFragment;
     float m_uncompletedWidth { 0 };
     float m_trailingWhitespaceWidth { 0 }; // Use this to remove trailing whitespace without re-mesuring the text.
@@ -585,33 +578,10 @@ private:
     // Having one character on the line does not necessarily mean it actually fits.
     // First character of the first fragment might be forced on to the current line even if it does not fit.
     bool m_firstCharacterFits { false };
+    bool m_hyphenationDisabled { false };
     std::optional<Vector<TextFragmentIterator::TextFragment, 30>> m_fragments;
 };
 
-class FragmentForwardIterator : public std::iterator<std::forward_iterator_tag, unsigned> {
-public:
-    FragmentForwardIterator(unsigned fragmentIndex)
-        : m_fragmentIndex(fragmentIndex)
-    {
-    }
-
-    FragmentForwardIterator& operator++()
-    {
-        ++m_fragmentIndex;
-        return *this;
-    }
-
-    bool operator!=(const FragmentForwardIterator& other) const { return m_fragmentIndex != other.m_fragmentIndex; }
-    bool operator==(const FragmentForwardIterator& other) const { return m_fragmentIndex == other.m_fragmentIndex; }
-    unsigned operator*() const { return m_fragmentIndex; }
-
-private:
-    unsigned m_fragmentIndex { 0 };
-};
-
-static FragmentForwardIterator begin(const TextFragmentIterator::TextFragment& fragment)  { return FragmentForwardIterator(fragment.start()); }
-static FragmentForwardIterator end(const TextFragmentIterator::TextFragment& fragment)  { return FragmentForwardIterator(fragment.end()); }
-
 static bool preWrap(const TextFragmentIterator::Style& style)
 {
     return style.wrapLines && !style.collapseWhitespace;
@@ -632,33 +602,46 @@ static void removeTrailingWhitespace(LineState& lineState, Layout::RunVector& ru
     lineState.removeTrailingWhitespace(runs);
 }
 
-static void updateLineConstrains(const RenderBlockFlow& flow, LineState& line, const TextFragmentIterator::Style& style, bool isFirstLine)
+static void updateLineConstrains(const RenderBlockFlow& flow, LineState& line, const LineState& previousLine, unsigned& numberOfPrecedingLinesWithHyphen, const TextFragmentIterator::Style& style, bool isFirstLine)
 {
     bool shouldApplyTextIndent = !flow.isAnonymous() || flow.parent()->firstChild() == &flow;
     LayoutUnit height = flow.logicalHeight();
-    line.setLogicalLeftOffset(flow.logicalLeftOffsetForLine(height, DoNotIndentText) + (shouldApplyTextIndent && isFirstLine ? flow.textIndentOffset() : LayoutUnit(0)));
-    float logicalRightOffset = flow.logicalRightOffsetForLine(height, DoNotIndentText);
+    LayoutUnit logicalHeight = flow.minLineHeightForReplacedRenderer(false, 0);
+    line.setLogicalLeftOffset(flow.logicalLeftOffsetForLine(height, DoNotIndentText, logicalHeight) + (shouldApplyTextIndent && isFirstLine ? flow.textIndentOffset() : LayoutUnit(0)));
+    float logicalRightOffset = flow.logicalRightOffsetForLine(height, DoNotIndentText, logicalHeight);
     line.setAvailableWidth(std::max<float>(0, logicalRightOffset - line.logicalLeftOffset()));
-    if (style.textAlign == JUSTIFY)
+    if (style.textAlign == TextAlignMode::Justify)
         line.setNeedsAllFragments();
+    numberOfPrecedingLinesWithHyphen = (previousLine.isEmpty() || !previousLine.lastFragment().hasHyphen()) ? 0 : numberOfPrecedingLinesWithHyphen + 1;
+    if (style.hyphenLimitLines && numberOfPrecedingLinesWithHyphen >= *style.hyphenLimitLines)
+        line.setHyphenationDisabled();
+    line.setCollapedWhitespaceWidth(style.font.spaceWidth() + style.wordSpacing);
 }
 
-static std::optional<unsigned> hyphenPositionForFragment(unsigned splitPosition, TextFragmentIterator::TextFragment& fragmentToSplit,
-    const TextFragmentIterator& textFragmentIterator, float availableWidth)
+struct SplitFragmentData {
+    unsigned position;
+    float width;
+};
+static std::optional<unsigned> hyphenPositionForFragment(SplitFragmentData splitData, const TextFragmentIterator::TextFragment& fragmentToSplit,
+    const LineState& line, const TextFragmentIterator& textFragmentIterator, float availableWidth)
 {
     auto& style = textFragmentIterator.style();
-    bool shouldHyphenate = style.shouldHyphenate && (!style.hyphenLimitLines || fragmentToSplit.wrappingWithHyphenCounter() < *style.hyphenLimitLines);
-    if (!shouldHyphenate)
+    if (!style.shouldHyphenate || line.isHyphenationDisabled())
         return std::nullopt;
 
-    if (!enoughWidthForHyphenation(availableWidth, style.font.pixelSize()))
+    // FIXME: This is a workaround for webkit.org/b/169613. See maxPrefixWidth computation in tryHyphenating().
+    // It does not work properly with non-collapsed leading tabs when font is enlarged.
+    auto adjustedAvailableWidth = availableWidth - style.hyphenStringWidth;
+    if (!line.isEmpty())
+        adjustedAvailableWidth += style.font.spaceWidth();
+    if (!enoughWidthForHyphenation(adjustedAvailableWidth, style.font.pixelSize()))
         return std::nullopt;
 
     // We might be able to fit the hyphen at the split position.
-    auto splitPositionWithHyphen = splitPosition;
+    auto splitPositionWithHyphen = splitData.position;
     // Find a splitting position where hyphen surely fits.
     unsigned start = fragmentToSplit.start();
-    auto leftSideWidth = textFragmentIterator.textWidth(start, splitPosition, 0);
+    auto leftSideWidth = splitData.width;
     while (leftSideWidth + style.hyphenStringWidth > availableWidth) {
         if (--splitPositionWithHyphen <= start)
             return std::nullopt; // No space for hyphen.
@@ -668,22 +651,60 @@ static std::optional<unsigned> hyphenPositionForFragment(unsigned splitPosition,
     return textFragmentIterator.lastHyphenPosition(fragmentToSplit, splitPositionWithHyphen + 1);
 }
 
-static TextFragmentIterator::TextFragment splitFragmentToFitLine(TextFragmentIterator::TextFragment& fragmentToSplit, float availableWidth, bool keepAtLeastOneCharacter, const TextFragmentIterator& textFragmentIterator)
+static SplitFragmentData split(const TextFragmentIterator::TextFragment& fragment, float availableWidth,
+    const TextFragmentIterator& textFragmentIterator)
+{
+    ASSERT(availableWidth >= 0);
+    auto left = fragment.start();
+    // Pathological case of (extremely)long string and narrow lines.
+    // Adjust the range so that we can pick a reasonable midpoint.
+    auto averageCharacterWidth = fragment.width() / fragment.length();
+    auto right = std::min<unsigned>(left + (2 * availableWidth / averageCharacterWidth), fragment.end() - 1);
+    // Preserve the left width for the final split position so that we don't need to remeasure the left side again.
+    float leftSideWidth = 0;
+    while (left < right) {
+        auto middle = (left + right) / 2;
+        auto width = textFragmentIterator.textWidth(fragment.start(), middle + 1, 0);
+        if (width < availableWidth) {
+            left = middle + 1;
+            leftSideWidth = width;
+        } else if (width > availableWidth)
+            right = middle;
+        else {
+            right = middle + 1;
+            leftSideWidth = width;
+            break;
+        }
+    }
+    return { right, leftSideWidth };
+}
+
+static TextFragmentIterator::TextFragment splitFragmentToFitLine(TextFragmentIterator::TextFragment& fragmentToSplit,
+    const LineState& line, const TextFragmentIterator& textFragmentIterator)
 {
-    // FIXME: add surrogate pair support.
-    unsigned start = fragmentToSplit.start();
-    auto it = std::upper_bound(begin(fragmentToSplit), end(fragmentToSplit), availableWidth, [&textFragmentIterator, start](float availableWidth, unsigned index) {
-        // FIXME: use the actual left position of the line (instead of 0) to calculated width. It might give false width for tab characters.
-        return availableWidth < textFragmentIterator.textWidth(start, index + 1, 0);
-    });
-    unsigned splitPosition = (*it);
+    auto availableWidth = line.availableWidth() - line.width();
+    auto splitFragmentData = split(fragmentToSplit, availableWidth, textFragmentIterator);
+    std::optional<unsigned> hyphenPosition = std::nullopt;
     // Does first character fit this line?
-    if (splitPosition == start) {
-        if (keepAtLeastOneCharacter)
-            ++splitPosition;
-    } else if (auto hyphenPosition = hyphenPositionForFragment(splitPosition, fragmentToSplit, textFragmentIterator, availableWidth))
-        return fragmentToSplit.splitWithHyphen(*hyphenPosition, textFragmentIterator);
-    return fragmentToSplit.split(splitPosition, textFragmentIterator);
+    if (splitFragmentData.position == fragmentToSplit.start()) {
+        // Keep at least one character on empty lines.
+        if (line.isEmpty())
+            splitFragmentData.width = textFragmentIterator.textWidth(fragmentToSplit.start(), ++splitFragmentData.position, 0);
+    } else {
+        hyphenPosition = hyphenPositionForFragment(splitFragmentData, fragmentToSplit, line, textFragmentIterator, availableWidth);
+        if (hyphenPosition) {
+            splitFragmentData.position = *hyphenPosition;
+            splitFragmentData.width = textFragmentIterator.textWidth(fragmentToSplit.start(), splitFragmentData.position, 0);
+        }
+    }
+    // If the right side surely does not fit the (next)line, we don't need the width to be kerning/ligature adjusted.
+    // Part of it gets re-measured as the left side during next split.
+    // This saves measuring long chunk of text repeatedly (see pathological case at ::split).
+    auto rightSideWidth = fragmentToSplit.width() - splitFragmentData.width;
+    if (rightSideWidth < 2 * availableWidth)
+        rightSideWidth = textFragmentIterator.textWidth(splitFragmentData.position, fragmentToSplit.end(), 0);
+    return hyphenPosition ? fragmentToSplit.splitWithHyphen(splitFragmentData.position, textFragmentIterator.style().hyphenStringWidth,
+        splitFragmentData.width, rightSideWidth) : fragmentToSplit.split(splitFragmentData.position, splitFragmentData.width, rightSideWidth);
 }
 
 enum PreWrapLineBreakRule { Preserve, Ignore };
@@ -694,12 +715,16 @@ static TextFragmentIterator::TextFragment consumeLineBreakIfNeeded(const TextFra
     if (!fragment.isLineBreak())
         return fragment;
 
-    if (preWrap(textFragmentIterator.style()) && preWrapLineBreakRule != PreWrapLineBreakRule::Ignore)
-        return fragment;
-
+    bool isHardLinebreak = fragment.type() == TextFragmentIterator::TextFragment::HardLineBreak;
     // <br> always produces a run. (required by testing output)
-    if (fragment.type() == TextFragmentIterator::TextFragment::HardLineBreak)
+    if (isHardLinebreak)
         line.appendFragmentAndCreateRunIfNeeded(fragment, runs);
+
+    auto& style = textFragmentIterator.style();
+    if (style.preserveNewline && preWrapLineBreakRule == PreWrapLineBreakRule::Preserve) {
+        if (!isHardLinebreak)
+            return fragment;
+    }
     return textFragmentIterator.nextTextFragment();
 }
 
@@ -716,18 +741,26 @@ static TextFragmentIterator::TextFragment skipWhitespaceIfNeeded(const TextFragm
 
 static TextFragmentIterator::TextFragment firstFragment(TextFragmentIterator& textFragmentIterator, LineState& currentLine, const LineState& previousLine, Layout::RunVector& runs)
 {
-    // Handle overflowed fragment from previous line.
-    TextFragmentIterator::TextFragment firstFragment(previousLine.overflowedFragment());
-
-    if (firstFragment.isEmpty())
-        firstFragment = textFragmentIterator.nextTextFragment();
-    else if (firstFragment.type() == TextFragmentIterator::TextFragment::Whitespace && preWrap(textFragmentIterator.style()) && previousLine.firstCharacterFits()) {
-        // 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.
-        firstFragment = textFragmentIterator.nextTextFragment();
+    // Handle overflow fragment from previous line.
+    auto overflowedFragment = previousLine.overflowedFragment();
+    if (overflowedFragment.isEmpty())
+        return skipWhitespaceIfNeeded(textFragmentIterator.nextTextFragment(), textFragmentIterator);
+
+    if (overflowedFragment.type() != TextFragmentIterator::TextFragment::Whitespace)
+        return overflowedFragment;
+
+    // Leading whitespace handling.
+    auto& style = textFragmentIterator.style();
+    // Special overflow pre-wrap whitespace handling: skip the overflowed whitespace (even when style says not-collapsible)
+    // if we manage to fit at least one character on the previous line.
+    auto preWrapIsOn = preWrap(style);
+    if ((style.collapseWhitespace || preWrapIsOn) && previousLine.firstCharacterFits()) {
         // If skipping the whitespace puts us on a newline, skip the newline too as we already wrapped the line.
-        firstFragment = consumeLineBreakIfNeeded(firstFragment, textFragmentIterator, currentLine, runs, PreWrapLineBreakRule::Ignore);
+        auto firstFragmentCandidate = consumeLineBreakIfNeeded(textFragmentIterator.nextTextFragment(), textFragmentIterator, currentLine, runs,
+            preWrapIsOn ? PreWrapLineBreakRule::Ignore : PreWrapLineBreakRule::Preserve);
+        return skipWhitespaceIfNeeded(firstFragmentCandidate, textFragmentIterator);
     }
-    return skipWhitespaceIfNeeded(firstFragment, textFragmentIterator);
+    return skipWhitespaceIfNeeded(overflowedFragment, textFragmentIterator);
 }
 
 static void forceFragmentToLine(LineState& line, TextFragmentIterator& textFragmentIterator, Layout::RunVector& runs, const TextFragmentIterator::TextFragment& fragment)
@@ -755,15 +788,14 @@ static void forceFragmentToLine(LineState& line, TextFragmentIterator& textFragm
 static bool createLineRuns(LineState& line, const LineState& previousLine, Layout::RunVector& runs, TextFragmentIterator& textFragmentIterator)
 {
     const auto& style = textFragmentIterator.style();
-    line.setCollapedWhitespaceWidth(style.spaceWidth + style.wordSpacing);
     bool lineCanBeWrapped = style.wrapLines || style.breakFirstWordOnOverflow || style.breakAnyWordOnOverflow;
     auto fragment = firstFragment(textFragmentIterator, line, previousLine, runs);
     while (fragment.type() != TextFragmentIterator::TextFragment::ContentEnd) {
-        // Hard linebreak.
+        // Hard and soft linebreaks.
         if (fragment.isLineBreak()) {
             // 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.)
             if (line.isEmpty() || fragment.type() == TextFragmentIterator::TextFragment::HardLineBreak) {
-                if (style.textAlign == RIGHT || style.textAlign == WEBKIT_RIGHT)
+                if (style.textAlign == TextAlignMode::Right || style.textAlign == TextAlignMode::WebKitRight)
                     line.removeTrailingWhitespace(runs);
                 line.appendFragmentAndCreateRunIfNeeded(fragment, runs);
             }
@@ -779,18 +811,20 @@ static bool createLineRuns(LineState& line, const LineState& previousLine, Layou
             bool emptyLine = line.isEmpty();
             // Whitespace fragment.
             if (fragment.type() == TextFragmentIterator::TextFragment::Whitespace) {
-                if (!style.collapseWhitespace) {
-                    // Split the fragment; (modified)fragment stays on this line, overflowedFragment is pushed to next line.
-                    line.setOverflowedFragment(splitFragmentToFitLine(fragment, line.availableWidth() - line.width(), emptyLine, textFragmentIterator));
-                    line.appendFragmentAndCreateRunIfNeeded(fragment, runs);
+                if (style.collapseWhitespace) {
+                    // Push collapased whitespace to the next line.
+                    line.setOverflowedFragment(fragment);
+                    break;
                 }
-                // When whitespace collapse is on, whitespace that doesn't fit is simply skipped.
+                // Split the whitespace; left part stays on this line, right is pushed to next line.
+                line.setOverflowedFragment(splitFragmentToFitLine(fragment, line, textFragmentIterator));
+                line.appendFragmentAndCreateRunIfNeeded(fragment, runs);
                 break;
             }
             // Non-whitespace fragment. (!style.wrapLines: bug138102(preserve existing behavior)
             if (((emptyLine && style.breakFirstWordOnOverflow) || style.breakAnyWordOnOverflow) || !style.wrapLines) {
                 // Split the fragment; (modified)fragment stays on this line, overflowedFragment is pushed to next line.
-                line.setOverflowedFragment(splitFragmentToFitLine(fragment, line.availableWidth() - line.width(), emptyLine, textFragmentIterator));
+                line.setOverflowedFragment(splitFragmentToFitLine(fragment, line, textFragmentIterator));
                 line.appendFragmentAndCreateRunIfNeeded(fragment, runs);
                 break;
             }
@@ -799,7 +833,7 @@ static bool createLineRuns(LineState& line, const LineState& previousLine, Layou
             if (style.shouldHyphenate) {
                 auto fragmentToSplit = fragment;
                 // Split and check if we actually ended up with a hyphen.
-                auto overflowFragment = splitFragmentToFitLine(fragmentToSplit, line.availableWidth() - line.width(), emptyLine, textFragmentIterator);
+                auto overflowFragment = splitFragmentToFitLine(fragmentToSplit, line, textFragmentIterator);
                 if (fragmentToSplit.hasHyphen()) {
                     line.setOverflowedFragment(overflowFragment);
                     line.appendFragmentAndCreateRunIfNeeded(fragmentToSplit, runs);
@@ -873,12 +907,12 @@ static void justifyRuns(const LineState& line, Layout::RunVector& runs, unsigned
     }
 }
 
-static ETextAlign textAlignForLine(const TextFragmentIterator::Style& style, bool lastLine)
+static TextAlignMode textAlignForLine(const TextFragmentIterator::Style& style, bool lastLine)
 {
-    // Fallback to LEFT (START) alignment for non-collapsable content and for the last line before a forced break or the end of the block.
+    // Fallback to TextAlignMode::Left (START) alignment for non-collapsable content and for the last line before a forced break or the end of the block.
     auto textAlign = style.textAlign;
-    if (textAlign == JUSTIFY && (!style.collapseWhitespace || lastLine))
-        textAlign = LEFT;
+    if (textAlign == TextAlignMode::Justify && (!style.collapseWhitespace || lastLine))
+        textAlign = TextAlignMode::Left;
     return textAlign;
 }
 
@@ -895,7 +929,7 @@ static void closeLineEndingAndAdjustRuns(LineState& line, Layout::RunVector& run
     auto firstRunIndex = lastRunIndexOfPreviousLine ? lastRunIndexOfPreviousLine.value() + 1 : 0;
     auto lineLogicalLeft = line.logicalLeftOffset();
     auto textAlign = textAlignForLine(style, lastLineInFlow || (line.lastFragment().isValid() && line.lastFragment().type() == TextFragmentIterator::TextFragment::HardLineBreak));
-    if (textAlign == JUSTIFY)
+    if (textAlign == TextAlignMode::Justify)
         justifyRuns(line, runs, firstRunIndex);
     else
         lineLogicalLeft = computeLineLeft(textAlign, line.availableWidth(), line.width(), line.logicalLeftOffset());
@@ -912,6 +946,7 @@ static void createTextRuns(Layout::RunVector& runs, RenderBlockFlow& flow, unsig
     LayoutUnit borderAndPaddingBefore = flow.borderAndPaddingBefore();
     LayoutUnit lineHeight = lineHeightFromFlow(flow);
     LineState line;
+    unsigned numberOfPrecedingLinesWithHyphen = 0;
     bool isEndOfContent = false;
     TextFragmentIterator textFragmentIterator = TextFragmentIterator(flow);
     std::optional<unsigned> lastRunIndexOfPreviousLine;
@@ -919,7 +954,7 @@ static void createTextRuns(Layout::RunVector& runs, RenderBlockFlow& flow, unsig
         flow.setLogicalHeight(lineHeight * lineCount + borderAndPaddingBefore);
         LineState previousLine = line;
         line = LineState();
-        updateLineConstrains(flow, line, textFragmentIterator.style(), !lineCount);
+        updateLineConstrains(flow, line, previousLine, numberOfPrecedingLinesWithHyphen, textFragmentIterator.style(), !lineCount);
         isEndOfContent = createLineRuns(line, previousLine, runs, textFragmentIterator);
         closeLineEndingAndAdjustRuns(line, runs, lastRunIndexOfPreviousLine, lineCount, textFragmentIterator, isEndOfContent);
         if (runs.size())
@@ -931,326 +966,35 @@ std::unique_ptr<Layout> create(RenderBlockFlow& flow)
 {
     unsigned lineCount = 0;
     Layout::RunVector runs;
-
     createTextRuns(runs, flow, lineCount);
-    return Layout::create(runs, lineCount);
+    return Layout::create(runs, lineCount, flow);
 }
 
-std::unique_ptr<Layout> Layout::create(const RunVector& runVector, unsigned lineCount)
+std::unique_ptr<Layout> Layout::create(const RunVector& runVector, unsigned lineCount, const RenderBlockFlow& blockFlow)
 {
     void* slot = WTF::fastMalloc(sizeof(Layout) + sizeof(Run) * runVector.size());
-    return std::unique_ptr<Layout>(new (NotNull, slot) Layout(runVector, lineCount));
+    return std::unique_ptr<Layout>(new (NotNull, slot) Layout(runVector, lineCount, blockFlow));
 }
 
-Layout::Layout(const RunVector& runVector, unsigned lineCount)
+Layout::Layout(const RunVector& runVector, unsigned lineCount, const RenderBlockFlow& blockFlow)
     : m_lineCount(lineCount)
     , m_runCount(runVector.size())
+    , m_blockFlowRenderer(blockFlow)
 {
     memcpy(m_runs, runVector.data(), m_runCount * sizeof(Run));
 }
 
-#ifndef NDEBUG
-static void printReason(AvoidanceReason reason, TextStream& stream)
-{
-    switch (reason) {
-    case FlowIsInsideRegion:
-        stream << "flow is inside region";
-        break;
-    case FlowHasHorizonalWritingMode:
-        stream << "horizontal writing mode";
-        break;
-    case FlowHasOutline:
-        stream << "outline";
-        break;
-    case FlowIsRuby:
-        stream << "ruby";
-        break;
-    case FlowHasHangingPunctuation:
-        stream << "hanging punctuation";
-        break;
-    case FlowIsPaginated:
-        stream << "paginated";
-        break;
-    case FlowHasTextOverflow:
-        stream << "text-overflow";
-        break;
-    case FlowIsDepricatedFlexBox:
-        stream << "depricatedFlexBox";
-        break;
-    case FlowParentIsPlaceholderElement:
-        stream << "placeholder element";
-        break;
-    case FlowParentIsTextAreaWithWrapping:
-        stream << "wrapping textarea";
-        break;
-    case FlowHasNonSupportedChild:
-        stream << "nested renderers";
-        break;
-    case FlowHasUnsupportedFloat:
-        stream << "complicated float";
-        break;
-    case FlowHasUnsupportedUnderlineDecoration:
-        stream << "text-underline-position: under";
-        break;
-    case FlowHasJustifiedNonLatinText:
-        stream << "text-align: justify with non-latin text";
-        break;
-    case FlowHasOverflowNotVisible:
-        stream << "overflow: hidden | scroll | auto";
-        break;
-    case FlowHasWebKitNBSPMode:
-        stream << "-webkit-nbsp-mode: space";
-        break;
-    case FlowIsNotLTR:
-        stream << "dir is not LTR";
-        break;
-    case FlowHasLineBoxContainProperty:
-        stream << "line-box-contain value indicates variable line height";
-        break;
-    case FlowIsNotTopToBottom:
-        stream << "non top-to-bottom flow";
-        break;
-    case FlowHasLineBreak:
-        stream << "line-break property";
-        break;
-    case FlowHasNonNormalUnicodeBiDi:
-        stream << "non-normal Unicode bidi";
-        break;
-    case FlowHasRTLOrdering:
-        stream << "-webkit-rtl-ordering";
-        break;
-    case FlowHasLineAlignEdges:
-        stream << "-webkit-line-align edges";
-        break;
-    case FlowHasLineSnap:
-        stream << "-webkit-line-snap property";
-        break;
-    case FlowHasTextEmphasisFillOrMark:
-        stream << "text-emphasis (fill/mark)";
-        break;
-    case FlowHasPseudoFirstLine:
-        stream << "first-line";
-        break;
-    case FlowHasPseudoFirstLetter:
-        stream << "first-letter";
-        break;
-    case FlowHasTextCombine:
-        stream << "text combine";
-        break;
-    case FlowHasTextFillBox:
-        stream << "background-color (text-fill)";
-        break;
-    case FlowHasBorderFitLines:
-        stream << "-webkit-border-fit";
-        break;
-    case FlowHasNonAutoLineBreak:
-        stream << "line-break is not auto";
-        break;
-    case FlowHasNonAutoTrailingWord:
-        stream << "-apple-trailing-word is not auto";
-        break;
-    case FlowHasSVGFont:
-        stream << "SVG font";
-        break;
-    case FlowTextHasSoftHyphen:
-        stream << "soft hyphen character";
-        break;
-    case FlowTextHasDirectionCharacter:
-        stream << "direction character";
-        break;
-    case FlowIsMissingPrimaryFont:
-        stream << "missing primary font";
-        break;
-    case FlowPrimaryFontIsInsufficient:
-        stream << "missing glyph or glyph needs another font";
-        break;
-    case FlowTextIsCombineText:
-        stream << "text is combine";
-        break;
-    case FlowTextIsRenderCounter:
-        stream << "unsupported RenderCounter";
-        break;
-    case FlowTextIsRenderQuote:
-        stream << "unsupported RenderQuote";
-        break;
-    case FlowTextIsTextFragment:
-        stream << "unsupported TextFragment";
-        break;
-    case FlowTextIsSVGInlineText:
-        stream << "unsupported SVGInlineText";
-        break;
-    case FlowHasComplexFontCodePath:
-        stream << "text with complex font codepath";
-        break;
-    case FlowHasTextShadow:
-        stream << "text-shadow";
-        break;
-    case FlowChildIsSelected:
-        stream << "selected content";
-        break;
-    case FlowFontHasOverflowGlyph:
-        stream << "-webkit-line-box-contain: glyphs with overflowing text.";
-        break;
-    case FlowTextHasSurrogatePair:
-        stream << "surrogate pair";
-        break;
-    case FlowTextIsEmpty:
-    case FlowHasNoChild:
-    case FlowHasNoParent:
-    case FeatureIsDisabled:
-    default:
-        break;
-    }
-}
-
-static void printReasons(AvoidanceReasonFlags reasons, TextStream& stream)
+const RunResolver& Layout::runResolver() const
 {
-    bool first = true;
-    for (auto reasonItem = EndOfReasons >> 1; reasonItem != NoReason; reasonItem >>= 1) {
-        if (!(reasons & reasonItem))
-            continue;
-        stream << (first ? " " : ", ");
-        first = false;
-        printReason(reasonItem, stream);
-    }
+    if (!m_runResolver)
+        m_runResolver = std::make_unique<RunResolver>(m_blockFlowRenderer, *this);
+    return *m_runResolver;
 }
 
-static void printTextForSubtree(const RenderObject& renderer, unsigned& charactersLeft, TextStream& stream)
+Layout::~Layout()
 {
-    if (!charactersLeft)
-        return;
-    if (is<RenderText>(renderer)) {
-        String text = downcast<RenderText>(renderer).text();
-        text = text.stripWhiteSpace();
-        unsigned len = std::min(charactersLeft, text.length());
-        stream << text.left(len);
-        charactersLeft -= len;
-        return;
-    }
-    if (!is<RenderElement>(renderer))
-        return;
-    for (const auto* child = downcast<RenderElement>(renderer).firstChild(); child; child = child->nextSibling())
-        printTextForSubtree(*child, charactersLeft, stream);
+    simpleLineLayoutWillBeDeleted(*this);
 }
 
-static unsigned textLengthForSubtree(const RenderObject& renderer)
-{
-    if (is<RenderText>(renderer))
-        return downcast<RenderText>(renderer).textLength();
-    if (!is<RenderElement>(renderer))
-        return 0;
-    unsigned textLength = 0;
-    for (const auto* child = downcast<RenderElement>(renderer).firstChild(); child; child = child->nextSibling())
-        textLength += textLengthForSubtree(*child);
-    return textLength;
-}
-
-static void collectNonEmptyLeafRenderBlockFlows(const RenderObject& renderer, HashSet<const RenderBlockFlow*>& leafRenderers)
-{
-    if (is<RenderText>(renderer)) {
-        if (!downcast<RenderText>(renderer).textLength())
-            return;
-        // Find RenderBlockFlow ancestor.
-        for (const auto* current = renderer.parent(); current; current = current->parent()) {
-            if (!is<RenderBlockFlow>(current))
-                continue;
-            leafRenderers.add(downcast<RenderBlockFlow>(current));
-            break;
-        }
-        return;
-    }
-    if (!is<RenderElement>(renderer))
-        return;
-    for (const auto* child = downcast<RenderElement>(renderer).firstChild(); child; child = child->nextSibling())
-        collectNonEmptyLeafRenderBlockFlows(*child, leafRenderers);
-}
-
-static void collectNonEmptyLeafRenderBlockFlowsForCurrentPage(HashSet<const RenderBlockFlow*>& leafRenderers)
-{
-    for (const auto* document : Document::allDocuments()) {
-        if (!document->renderView() || document->pageCacheState() != Document::NotInPageCache)
-            continue;
-        if (!document->isHTMLDocument() && !document->isXHTMLDocument())
-            continue;
-        collectNonEmptyLeafRenderBlockFlows(*document->renderView(), leafRenderers);
-    }
-}
-
-void toggleSimpleLineLayout()
-{
-    for (auto* document : Document::allDocuments()) {
-        auto& settings = document->mutableSettings();
-        settings.setSimpleLineLayoutEnabled(!settings.simpleLineLayoutEnabled());
-    }
-}
-
-void printSimpleLineLayoutBlockList()
-{
-    HashSet<const RenderBlockFlow*> leafRenderers;
-    collectNonEmptyLeafRenderBlockFlowsForCurrentPage(leafRenderers);
-    if (!leafRenderers.size()) {
-        WTFLogAlways("No text found in this document\n");
-        return;
-    }
-    TextStream stream;
-    stream << "---------------------------------------------------\n";
-    for (const auto* flow : leafRenderers) {
-        auto reason = canUseForWithReason(*flow, IncludeReasons::All);
-        if (reason == NoReason)
-            continue;
-        unsigned printedLength = 30;
-        stream << "\"";
-        printTextForSubtree(*flow, printedLength, stream);
-        for (;printedLength > 0; --printedLength)
-            stream << " ";
-        stream << "\"(" << textLengthForSubtree(*flow) << "):";
-        printReasons(reason, stream);
-        stream << "\n";
-    }
-    stream << "---------------------------------------------------\n";
-    WTFLogAlways("%s", stream.release().utf8().data());
-}
-
-void printSimpleLineLayoutCoverage()
-{
-    HashSet<const RenderBlockFlow*> leafRenderers;
-    collectNonEmptyLeafRenderBlockFlowsForCurrentPage(leafRenderers);
-    if (!leafRenderers.size()) {
-        WTFLogAlways("No text found in this document\n");
-        return;
-    }
-    TextStream stream;
-    HashMap<AvoidanceReason, unsigned> flowStatistics;
-    unsigned textLength = 0;
-    unsigned unsupportedTextLength = 0;
-    unsigned numberOfUnsupportedLeafBlocks = 0;
-    for (const auto* flow : leafRenderers) {
-        auto flowLength = textLengthForSubtree(*flow);
-        textLength += flowLength;
-        auto reasons = canUseForWithReason(*flow, IncludeReasons::All);
-        if (reasons == NoReason)
-            continue;
-        ++numberOfUnsupportedLeafBlocks;
-        unsupportedTextLength += flowLength;
-        for (auto reasonItem = EndOfReasons >> 1; reasonItem != NoReason; reasonItem >>= 1) {
-            if (!(reasons & reasonItem))
-                continue;
-            auto result = flowStatistics.add(reasonItem, flowLength);
-            if (!result.isNewEntry)
-                result.iterator->value += flowLength;
-        }
-    }
-    stream << "---------------------------------------------------\n";
-    stream << "Number of text blocks: total(" <<  leafRenderers.size() << ") non-simple(" << numberOfUnsupportedLeafBlocks << ")\nText length: total(" <<
-        textLength << ") non-simple(" << unsupportedTextLength << ")\n";
-    for (const auto reasonEntry : flowStatistics) {
-        printReason(reasonEntry.key, stream);
-        stream << ": " << (float)reasonEntry.value / (float)textLength * 100 << "%\n";
-    }
-    stream << "simple line layout coverage: " << (float)(textLength - unsupportedTextLength) / (float)textLength * 100 << "%\n";
-    stream << "---------------------------------------------------\n";
-    WTFLogAlways("%s", stream.release().utf8().data());
-}
-#endif
 }
 }