Title: [266169] trunk/Source/WebCore
Revision
266169
Author
[email protected]
Date
2020-08-26 09:46:04 -0700 (Wed, 26 Aug 2020)

Log Message

[LFC][IFC] Move inline run alignment logic to a dedicated class
https://bugs.webkit.org/show_bug.cgi?id=214527
<rdar://problem/66115796>

Reviewed by Antti Koivisto.

This is in preparation for moving line alignment logic to LineBox (LineBuilder will not going to be responsible for aligning runs on the line).

* layout/FormattingContext.h:
* layout/inlineformatting/InlineFormattingContext.h:
* layout/inlineformatting/InlineLineBuilder.cpp:
(WebCore::Layout::LineContentAligner::formattingContext const):
(WebCore::Layout::LineContentAligner::layoutState const):
(WebCore::Layout::LineContentAligner::LineContentAligner):
(WebCore::Layout::LineContentAligner::alignHorizontally):
(WebCore::Layout::LineContentAligner::alignVertically):
(WebCore::Layout::LineContentAligner::justifyRuns):
(WebCore::Layout::LineContentAligner::adjustBaselineAndLineHeight):
(WebCore::Layout::LineContentAligner::collectHangingContent const):
(WebCore::Layout::LineContentAligner::runContentHeight const):
(WebCore::Layout::LineBuilder::LineBuilder):
(WebCore::Layout::LineBuilder::~LineBuilder):
(WebCore::Layout::LineBuilder::initialize):
(WebCore::Layout::LineBuilder::clear):
(WebCore::Layout::LineBuilder::close):
(WebCore::Layout::LineBuilder::alignContentVertically): Deleted.
(WebCore::Layout::LineBuilder::justifyRuns): Deleted.
(WebCore::Layout::LineBuilder::alignHorizontally): Deleted.
(WebCore::Layout::LineBuilder::collectHangingContent): Deleted.
(WebCore::Layout::LineBuilder::adjustBaselineAndLineHeight): Deleted.
(WebCore::Layout::LineBuilder::runContentHeight const): Deleted.
(WebCore::Layout::LineBuilder::layoutState const): Deleted.
(WebCore::Layout::LineBuilder::root const): Deleted.
* layout/inlineformatting/InlineLineBuilder.h:
(WebCore::Layout::LineBuilder::availableWidth const):
(WebCore::Layout::LineBuilder::contentLogicalRight const):
(WebCore::Layout::LineBuilder::logicalTop const): Deleted.
(WebCore::Layout::LineBuilder::logicalBottom const): Deleted.
(WebCore::Layout::LineBuilder::logicalLeft const): Deleted.
(WebCore::Layout::LineBuilder::logicalRight const): Deleted.
(WebCore::Layout::LineBuilder::logicalWidth const): Deleted.
(WebCore::Layout::LineBuilder::logicalHeight const): Deleted.
(WebCore::Layout::LineBuilder::baseline const): Deleted.

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (266168 => 266169)


--- trunk/Source/WebCore/ChangeLog	2020-08-26 16:18:18 UTC (rev 266168)
+++ trunk/Source/WebCore/ChangeLog	2020-08-26 16:46:04 UTC (rev 266169)
@@ -1,3 +1,49 @@
+2020-08-26  Zalan Bujtas  <[email protected]>
+
+        [LFC][IFC] Move inline run alignment logic to a dedicated class
+        https://bugs.webkit.org/show_bug.cgi?id=214527
+        <rdar://problem/66115796>
+
+        Reviewed by Antti Koivisto.
+
+        This is in preparation for moving line alignment logic to LineBox (LineBuilder will not going to be responsible for aligning runs on the line).
+
+        * layout/FormattingContext.h:
+        * layout/inlineformatting/InlineFormattingContext.h:
+        * layout/inlineformatting/InlineLineBuilder.cpp:
+        (WebCore::Layout::LineContentAligner::formattingContext const):
+        (WebCore::Layout::LineContentAligner::layoutState const):
+        (WebCore::Layout::LineContentAligner::LineContentAligner):
+        (WebCore::Layout::LineContentAligner::alignHorizontally):
+        (WebCore::Layout::LineContentAligner::alignVertically):
+        (WebCore::Layout::LineContentAligner::justifyRuns):
+        (WebCore::Layout::LineContentAligner::adjustBaselineAndLineHeight):
+        (WebCore::Layout::LineContentAligner::collectHangingContent const):
+        (WebCore::Layout::LineContentAligner::runContentHeight const):
+        (WebCore::Layout::LineBuilder::LineBuilder):
+        (WebCore::Layout::LineBuilder::~LineBuilder):
+        (WebCore::Layout::LineBuilder::initialize):
+        (WebCore::Layout::LineBuilder::clear):
+        (WebCore::Layout::LineBuilder::close):
+        (WebCore::Layout::LineBuilder::alignContentVertically): Deleted.
+        (WebCore::Layout::LineBuilder::justifyRuns): Deleted.
+        (WebCore::Layout::LineBuilder::alignHorizontally): Deleted.
+        (WebCore::Layout::LineBuilder::collectHangingContent): Deleted.
+        (WebCore::Layout::LineBuilder::adjustBaselineAndLineHeight): Deleted.
+        (WebCore::Layout::LineBuilder::runContentHeight const): Deleted.
+        (WebCore::Layout::LineBuilder::layoutState const): Deleted.
+        (WebCore::Layout::LineBuilder::root const): Deleted.
+        * layout/inlineformatting/InlineLineBuilder.h:
+        (WebCore::Layout::LineBuilder::availableWidth const):
+        (WebCore::Layout::LineBuilder::contentLogicalRight const):
+        (WebCore::Layout::LineBuilder::logicalTop const): Deleted.
+        (WebCore::Layout::LineBuilder::logicalBottom const): Deleted.
+        (WebCore::Layout::LineBuilder::logicalLeft const): Deleted.
+        (WebCore::Layout::LineBuilder::logicalRight const): Deleted.
+        (WebCore::Layout::LineBuilder::logicalWidth const): Deleted.
+        (WebCore::Layout::LineBuilder::logicalHeight const): Deleted.
+        (WebCore::Layout::LineBuilder::baseline const): Deleted.
+
 2020-08-26  Chris Dumez  <[email protected]>
 
         REGRESSION (r265908): Crash under Blob::arrayBuffer() / Blob::text() in stress GC

Modified: trunk/Source/WebCore/layout/FormattingContext.h (266168 => 266169)


--- trunk/Source/WebCore/layout/FormattingContext.h	2020-08-26 16:18:18 UTC (rev 266168)
+++ trunk/Source/WebCore/layout/FormattingContext.h	2020-08-26 16:46:04 UTC (rev 266169)
@@ -109,10 +109,11 @@
     const Display::Box& geometryForBox(const Box&, Optional<EscapeReason> = WTF::nullopt) const;
     const ContainerBox& root() const { return *m_root; }
 
+    LayoutState& layoutState() const;
+
 protected:
     using LayoutQueue = Vector<const Box*>;
 
-    LayoutState& layoutState() const;
     const FormattingState& formattingState() const { return m_formattingState; }
     FormattingState& formattingState() { return m_formattingState; }
 

Modified: trunk/Source/WebCore/layout/inlineformatting/InlineLineBuilder.cpp (266168 => 266169)


--- trunk/Source/WebCore/layout/inlineformatting/InlineLineBuilder.cpp	2020-08-26 16:18:18 UTC (rev 266168)
+++ trunk/Source/WebCore/layout/inlineformatting/InlineLineBuilder.cpp	2020-08-26 16:46:04 UTC (rev 266169)
@@ -64,79 +64,86 @@
     m_width =  0;
 }
 
-LineBuilder::LineBuilder(const InlineFormattingContext& inlineFormattingContext, Optional<TextAlignMode> horizontalAlignment, IntrinsicSizing intrinsicSizing)
-    : m_inlineFormattingContext(inlineFormattingContext)
-    , m_trimmableTrailingContent(m_runs)
-    , m_horizontalAlignment(horizontalAlignment)
-    , m_isIntrinsicSizing(intrinsicSizing == IntrinsicSizing::Yes)
-    , m_shouldIgnoreTrailingLetterSpacing(RuntimeEnabledFeatures::sharedFeatures().layoutFormattingContextIntegrationEnabled())
-{
-}
+class LineContentAligner {
+public:
+    LineContentAligner(const InlineFormattingContext&, LineBox&, LineBuilder::RunList&, InlineLayoutUnit availableWidth);
 
-LineBuilder::~LineBuilder()
-{
-}
+    void alignHorizontally(TextAlignMode, LineBuilder::IsLastLineWithInlineContent);
+    void alignVertically();
+    void adjustBaselineAndLineHeight();
 
-void LineBuilder::initialize(const Constraints& constraints)
-{
-    m_lineLogicalWidth = constraints.availableLogicalWidth;
-    m_hasIntrusiveFloat = constraints.lineIsConstrainedByFloat;
-    auto initialLineHeight = constraints.lineHeight;
-    auto lineRect = Display::InlineRect { constraints.logicalTopLeft, 0_lu, initialLineHeight };
-    auto ascentAndDescent = LineBuilder::halfLeadingMetrics(root().style().fontMetrics(), initialLineHeight);
-    m_lineBox = LineBox { lineRect, ascentAndDescent };
-    if (!layoutState().inNoQuirksMode())
-        m_initialStrut = AscentAndDescent { ascentAndDescent.ascent, initialLineHeight - ascentAndDescent.ascent };
-    else
-        m_initialStrut = { };
-    clear();
-#if ASSERT_ENABLED
-    m_isClosed = false;
-#endif
-}
+private:
+    void justifyRuns(InlineLayoutUnit availableWidth);
+    HangingContent collectHangingContent(LineBuilder::IsLastLineWithInlineContent) const;
+    InlineLayoutUnit runContentHeight(const LineBuilder::Run&) const;
 
-void LineBuilder::clear()
+    const InlineFormattingContext& formattingContext() const { return m_inlineFormattingContext; }
+    LayoutState& layoutState() const { return formattingContext().layoutState(); }
+
+    const InlineFormattingContext& m_inlineFormattingContext;
+    LineBox& m_lineBox;
+    LineBuilder::RunList& m_runs;
+    InlineLayoutUnit m_availableWidth;
+};
+
+LineContentAligner::LineContentAligner(const InlineFormattingContext& inlineFormattingContext, LineBox& lineBox, LineBuilder::RunList& runs, InlineLayoutUnit availableWidth)
+    : m_inlineFormattingContext(inlineFormattingContext)
+    , m_lineBox(lineBox)
+    , m_runs(runs)
+    , m_availableWidth(availableWidth)
 {
-    m_lineBox.setLogicalWidth({ });
-    m_lineBox.setIsConsideredEmpty();
-    m_runs.clear();
-    m_trimmableTrailingContent.reset();
-    m_lineIsVisuallyEmptyBeforeTrimmableTrailingContent = { };
 }
 
-void LineBuilder::close(IsLastLineWithInlineContent isLastLineWithInlineContent)
+void LineContentAligner::alignHorizontally(TextAlignMode horizontalAlignment, LineBuilder::IsLastLineWithInlineContent isLastLine)
 {
-#if ASSERT_ENABLED
-    m_isClosed = true;
-#endif
-    // 1. Remove trimmable trailing content.
-    // 2. Align merged runs both vertically and horizontally.
-    removeTrailingTrimmableContent();
-    visuallyCollapsePreWrapOverflowContent();
-    if (m_isIntrinsicSizing)
+    auto hangingContent = collectHangingContent(isLastLine);
+    auto availableWidth = m_availableWidth + hangingContent.width();
+    if (m_runs.isEmpty() || availableWidth <= 0)
         return;
 
-    auto hangingContent = collectHangingContent(isLastLineWithInlineContent);
-    adjustBaselineAndLineHeight();
-    if (isVisuallyEmpty()) {
-        m_lineBox.resetAlignmentBaseline();
-        m_lineBox.setLogicalHeight({ });
+    if (horizontalAlignment == TextAlignMode::Justify) {
+        justifyRuns(availableWidth);
+        return;
     }
-    // Remove descent when all content is baseline aligned but none of them have descent.
-    if (formattingContext().quirks().lineDescentNeedsCollapsing(m_runs)) {
-        m_lineBox.shrinkVertically(m_lineBox.ascentAndDescent().descent);
-        m_lineBox.resetDescent();
-    }
-    alignContentVertically();
-    alignHorizontally(hangingContent, isLastLineWithInlineContent);
+
+    auto adjustmentForAlignment = [&] (auto availableWidth) -> Optional<InlineLayoutUnit> {
+        switch (horizontalAlignment) {
+        case TextAlignMode::Left:
+        case TextAlignMode::WebKitLeft:
+        case TextAlignMode::Start:
+            return { };
+        case TextAlignMode::Right:
+        case TextAlignMode::WebKitRight:
+        case TextAlignMode::End:
+            return std::max<InlineLayoutUnit>(availableWidth, 0);
+        case TextAlignMode::Center:
+        case TextAlignMode::WebKitCenter:
+            return std::max<InlineLayoutUnit>(availableWidth / 2, 0);
+        case TextAlignMode::Justify:
+            ASSERT_NOT_REACHED();
+            break;
+        }
+        ASSERT_NOT_REACHED();
+        return { };
+    };
+
+    auto adjustment = adjustmentForAlignment(availableWidth);
+    if (!adjustment)
+        return;
+    // Horizontal alignment means that we not only adjust the runs but also make sure
+    // that the line box is aligned as well
+    // e.g. <div style="text-align: center; width: 100px;">centered text</div> : the line box will also be centered
+    // as opposed to start at 0px all the way to [centered text] run's right edge.
+    m_lineBox.moveHorizontally(*adjustment);
+    for (auto& run : m_runs)
+        run.moveHorizontally(*adjustment);
 }
 
-void LineBuilder::alignContentVertically()
+void LineContentAligner::alignVertically()
 {
-    ASSERT(!m_isIntrinsicSizing);
     auto scrollableOverflowRect = m_lineBox.logicalRect();
     for (auto& run : m_runs) {
-        InlineLayoutUnit logicalTop = 0;
+        auto logicalTop = InlineLayoutUnit { };
         auto& layoutBox = run.layoutBox();
         auto verticalAlign = layoutBox.style().verticalAlign();
         auto ascent = layoutBox.style().fontMetrics().ascent();
@@ -144,10 +151,10 @@
         switch (verticalAlign) {
         case VerticalAlign::Baseline:
             if (run.isLineBreak() || run.isText())
-                logicalTop = baseline() - ascent;
+                logicalTop = m_lineBox.alignmentBaseline() - ascent;
             else if (run.isContainerStart()) {
                 auto& boxGeometry = formattingContext().geometryForBox(layoutBox);
-                logicalTop = baseline() - ascent - boxGeometry.borderTop() - boxGeometry.paddingTop().valueOr(0);
+                logicalTop = m_lineBox.alignmentBaseline() - ascent - boxGeometry.borderTop() - boxGeometry.paddingTop().valueOr(0);
             } else if (layoutBox.isInlineBlockBox() && layoutBox.establishesInlineFormattingContext()) {
                 auto& formattingState = layoutState().establishedInlineFormattingState(downcast<ContainerBox>(layoutBox));
                 // Spec makes us generate at least one line -even if it is empty.
@@ -166,10 +173,10 @@
                 //
                 auto& boxGeometry = formattingContext().geometryForBox(layoutBox);
                 auto baselineFromMarginBox = boxGeometry.marginBefore() + boxGeometry.borderTop() + boxGeometry.paddingTop().valueOr(0) + inlineBlockBaseline;
-                logicalTop = baseline() - baselineFromMarginBox;
+                logicalTop = m_lineBox.alignmentBaseline() - baselineFromMarginBox;
             } else {
                 auto& boxGeometry = formattingContext().geometryForBox(layoutBox);
-                logicalTop = baseline() - (boxGeometry.verticalBorder() + boxGeometry.verticalPadding().valueOr(0_lu) + run.logicalRect().height() + boxGeometry.marginAfter());
+                logicalTop = m_lineBox.alignmentBaseline() - (boxGeometry.verticalBorder() + boxGeometry.verticalPadding().valueOr(0_lu) + run.logicalRect().height() + boxGeometry.marginAfter());
             }
             break;
         case VerticalAlign::Top:
@@ -176,7 +183,7 @@
             logicalTop = 0_lu;
             break;
         case VerticalAlign::Bottom:
-            logicalTop = logicalBottom() - run.logicalRect().height();
+            logicalTop = m_lineBox.logicalBottom() - run.logicalRect().height();
             break;
         default:
             ASSERT_NOT_IMPLEMENTED_YET();
@@ -186,18 +193,18 @@
         // Adjust scrollable overflow if the run overflows the line.
         scrollableOverflowRect.expandVerticallyToContain(run.logicalRect());
         // Convert runs from relative to the line top/left to the formatting root's border box top/left.
-        run.moveVertically(this->logicalTop());
-        run.moveHorizontally(this->logicalLeft());
+        run.moveVertically(m_lineBox.logicalTop());
+        run.moveHorizontally(m_lineBox.logicalLeft());
     }
     m_lineBox.setScrollableOverflow(scrollableOverflowRect);
 }
 
-void LineBuilder::justifyRuns(InlineLayoutUnit availableWidth)
+void LineContentAligner::justifyRuns(InlineLayoutUnit availableWidth)
 {
     ASSERT(availableWidth > 0);
     // Collect the expansion opportunity numbers and find the last run with content.
     auto expansionOpportunityCount = 0;
-    Run* lastRunWithContent = nullptr;
+    LineBuilder::Run* lastRunWithContent = nullptr;
     for (auto& run : m_runs) {
         expansionOpportunityCount += run.expansionOpportunityCount();
         if (run.isText() || run.isBox())
@@ -229,14 +236,200 @@
     }
 }
 
-void LineBuilder::alignHorizontally(const HangingContent& hangingContent, IsLastLineWithInlineContent isLastLine)
+void LineContentAligner::adjustBaselineAndLineHeight()
 {
-    ASSERT(!m_isIntrinsicSizing);
+    unsigned inlineContainerNestingLevel = 0;
+    auto hasSeenDirectTextOrLineBreak = false;
+    for (auto& run : m_runs) {
+        auto& layoutBox = run.layoutBox();
+        auto& style = layoutBox.style();
 
-    auto availableWidth = this->availableWidth() + hangingContent.width();
-    if (m_runs.isEmpty() || availableWidth <= 0)
+        run.setLogicalHeight(runContentHeight(run));
+
+        if (run.isText() || run.isLineBreak()) {
+            if (inlineContainerNestingLevel) {
+                // We've already adjusted the line height/baseline through the parent inline container. 
+                continue;
+            }
+            if (hasSeenDirectTextOrLineBreak) {
+                // e.g div>first text</div> or <div><span>nested<span>first direct text</div>.
+                continue;
+            }
+            hasSeenDirectTextOrLineBreak = true;
+            continue;
+        }
+
+        if (run.isContainerStart()) {
+            ++inlineContainerNestingLevel;
+            // Inline containers stretch the line by their font size.
+            // Vertical margins, paddings and borders don't contribute to the line height.
+            auto& fontMetrics = style.fontMetrics();
+            if (style.verticalAlign() == VerticalAlign::Baseline) {
+                auto halfLeading = LineBuilder::halfLeadingMetrics(fontMetrics, style.computedLineHeight());
+                // Both halfleading ascent and descent could be negative (tall font vs. small line-height value)
+                if (halfLeading.descent > 0)
+                    m_lineBox.setDescentIfGreater(halfLeading.descent);
+                if (halfLeading.ascent > 0)
+                    m_lineBox.setAscentIfGreater(halfLeading.ascent);
+                m_lineBox.setLogicalHeightIfGreater(m_lineBox.ascentAndDescent().height());
+            } else
+                m_lineBox.setLogicalHeightIfGreater(fontMetrics.height());
+            continue;
+        }
+
+        if (run.isContainerEnd()) {
+            // The line's baseline and height have already been adjusted at ContainerStart.
+            ASSERT(inlineContainerNestingLevel);
+            --inlineContainerNestingLevel;
+            continue;
+        }
+
+        if (run.isBox()) {
+            auto& boxGeometry = formattingContext().geometryForBox(layoutBox);
+            auto marginBoxHeight = boxGeometry.marginBoxHeight();
+
+            switch (style.verticalAlign()) {
+            case VerticalAlign::Baseline: {
+                if (layoutBox.isInlineBlockBox() && layoutBox.establishesInlineFormattingContext()) {
+                    // Inline-blocks with inline content always have baselines.
+                    auto& formattingState = layoutState().establishedInlineFormattingState(downcast<ContainerBox>(layoutBox));
+                    // There has to be at least one line -even if it is empty.
+                    auto& lastLineBox = formattingState.displayInlineContent()->lineBoxes.last();
+                    auto beforeHeight = boxGeometry.marginBefore() + boxGeometry.borderTop() + boxGeometry.paddingTop().valueOr(0);
+                    m_lineBox.setAlignmentBaselineIfGreater(beforeHeight + lastLineBox.baseline());
+                    m_lineBox.setLogicalHeightIfGreater(marginBoxHeight);
+                } else {
+                    // Non inline-block boxes sit on the baseline (including their bottom margin).
+                    m_lineBox.setAscentIfGreater(marginBoxHeight);
+                    // Ignore negative descent (yes, negative descent is a thing).
+                    m_lineBox.setLogicalHeightIfGreater(marginBoxHeight + std::max<InlineLayoutUnit>(0, m_lineBox.ascentAndDescent().descent));
+                }
+                break;
+            }
+            case VerticalAlign::Top:
+                // Top align content never changes the baseline, it only pushes the bottom of the line further down.
+                m_lineBox.setLogicalHeightIfGreater(marginBoxHeight);
+                break;
+            case VerticalAlign::Bottom: {
+                // Bottom aligned, tall content pushes the baseline further down from the line top.
+                auto lineLogicalHeight = m_lineBox.logicalHeight();
+                if (marginBoxHeight > lineLogicalHeight) {
+                    m_lineBox.setLogicalHeightIfGreater(marginBoxHeight);
+                    m_lineBox.setAlignmentBaselineIfGreater(m_lineBox.alignmentBaseline() + (marginBoxHeight - lineLogicalHeight));
+                }
+                break;
+            }
+            default:
+                ASSERT_NOT_IMPLEMENTED_YET();
+                break;
+            }
+            continue;
+        }
+    }
+}
+
+HangingContent LineContentAligner::collectHangingContent(LineBuilder::IsLastLineWithInlineContent isLastLineWithInlineContent) const
+{
+    auto hangingContent = HangingContent { };
+    if (isLastLineWithInlineContent == LineBuilder::IsLastLineWithInlineContent::Yes)
+        hangingContent.setIsConditional();
+    for (auto& run : WTF::makeReversedRange(m_runs)) {
+        if (run.isContainerStart() || run.isContainerEnd())
+            continue;
+        if (run.isLineBreak()) {
+            hangingContent.setIsConditional();
+            continue;
+        }
+        if (!run.hasTrailingWhitespace())
+            break;
+        // Check if we have a preserved or hung whitespace.
+        if (run.style().whiteSpace() != WhiteSpace::PreWrap)
+            break;
+        // This is either a normal or conditionally hanging trailing whitespace.
+        hangingContent.expand(run.trailingWhitespaceWidth());
+    }
+    return hangingContent;
+}
+
+InlineLayoutUnit LineContentAligner::runContentHeight(const LineBuilder::Run& run) const
+{
+    auto& fontMetrics = run.style().fontMetrics();
+    if (run.isText() || run.isLineBreak())
+        return fontMetrics.height();
+
+    if (run.isContainerStart() || run.isContainerEnd())
+        return fontMetrics.height();
+
+    auto& layoutBox = run.layoutBox();
+    auto& boxGeometry = formattingContext().geometryForBox(layoutBox);
+    if (layoutBox.isReplacedBox() || layoutBox.isFloatingPositioned())
+        return boxGeometry.contentBoxHeight();
+
+    // Non-replaced inline box (e.g. inline-block). It looks a bit misleading but their margin box is considered the content height here.
+    return boxGeometry.marginBoxHeight();
+}
+
+LineBuilder::LineBuilder(const InlineFormattingContext& inlineFormattingContext, Optional<TextAlignMode> horizontalAlignment, IntrinsicSizing intrinsicSizing)
+    : m_inlineFormattingContext(inlineFormattingContext)
+    , m_trimmableTrailingContent(m_runs)
+    , m_horizontalAlignment(horizontalAlignment)
+    , m_isIntrinsicSizing(intrinsicSizing == IntrinsicSizing::Yes)
+    , m_shouldIgnoreTrailingLetterSpacing(RuntimeEnabledFeatures::sharedFeatures().layoutFormattingContextIntegrationEnabled())
+{
+}
+
+LineBuilder::~LineBuilder()
+{
+}
+
+void LineBuilder::initialize(const Constraints& constraints)
+{
+    m_lineLogicalWidth = constraints.availableLogicalWidth;
+    m_hasIntrusiveFloat = constraints.lineIsConstrainedByFloat;
+    auto initialLineHeight = constraints.lineHeight;
+    auto lineRect = Display::InlineRect { constraints.logicalTopLeft, 0_lu, initialLineHeight };
+    auto ascentAndDescent = LineBuilder::halfLeadingMetrics(formattingContext().root().style().fontMetrics(), initialLineHeight);
+    m_lineBox = LineBox { lineRect, ascentAndDescent };
+    clear();
+#if ASSERT_ENABLED
+    m_isClosed = false;
+#endif
+}
+
+void LineBuilder::clear()
+{
+    m_lineBox.setLogicalWidth({ });
+    m_lineBox.setIsConsideredEmpty();
+    m_runs.clear();
+    m_trimmableTrailingContent.reset();
+    m_lineIsVisuallyEmptyBeforeTrimmableTrailingContent = { };
+}
+
+void LineBuilder::close(IsLastLineWithInlineContent isLastLine)
+{
+#if ASSERT_ENABLED
+    m_isClosed = true;
+#endif
+    // 1. Remove trimmable trailing content.
+    // 2. Align merged runs both vertically and horizontally.
+    removeTrailingTrimmableContent();
+    visuallyCollapsePreWrapOverflowContent();
+    if (m_isIntrinsicSizing)
         return;
 
+    auto contentAligner = LineContentAligner { formattingContext(), m_lineBox, m_runs, availableWidth() };
+    contentAligner.adjustBaselineAndLineHeight();
+    if (isVisuallyEmpty()) {
+        m_lineBox.resetAlignmentBaseline();
+        m_lineBox.setLogicalHeight({ });
+    }
+    // Remove descent when all content is baseline aligned but none of them have descent.
+    if (formattingContext().quirks().lineDescentNeedsCollapsing(m_runs)) {
+        m_lineBox.shrinkVertically(m_lineBox.ascentAndDescent().descent);
+        m_lineBox.resetDescent();
+    }
+    contentAligner.alignVertically();
+
     auto computedHorizontalAlignment = [&] {
         ASSERT(m_horizontalAlignment);
         if (m_horizontalAlignment != TextAlignMode::Justify)
@@ -244,47 +437,11 @@
         // Text is justified according to the method specified by the text-justify property,
         // in order to exactly fill the line box. Unless otherwise specified by text-align-last,
         // the last line before a forced break or the end of the block is start-aligned.
-        if (m_runs.last().isLineBreak() || isLastLine == IsLastLineWithInlineContent::Yes)
+        if (m_runs.last().isLineBreak() || isLastLine == LineBuilder::IsLastLineWithInlineContent::Yes)
             return TextAlignMode::Start;
         return TextAlignMode::Justify;
-    }();
-
-    if (computedHorizontalAlignment == TextAlignMode::Justify) {
-        justifyRuns(availableWidth);
-        return;
-    }
-
-    auto adjustmentForAlignment = [] (auto horizontalAlignment, auto availableWidth) -> Optional<InlineLayoutUnit> {
-        switch (horizontalAlignment) {
-        case TextAlignMode::Left:
-        case TextAlignMode::WebKitLeft:
-        case TextAlignMode::Start:
-            return { };
-        case TextAlignMode::Right:
-        case TextAlignMode::WebKitRight:
-        case TextAlignMode::End:
-            return std::max<InlineLayoutUnit>(availableWidth, 0);
-        case TextAlignMode::Center:
-        case TextAlignMode::WebKitCenter:
-            return std::max<InlineLayoutUnit>(availableWidth / 2, 0);
-        case TextAlignMode::Justify:
-            ASSERT_NOT_REACHED();
-            break;
-        }
-        ASSERT_NOT_REACHED();
-        return { };
     };
-
-    auto adjustment = adjustmentForAlignment(computedHorizontalAlignment, availableWidth);
-    if (!adjustment)
-        return;
-    // Horizontal alignment means that we not only adjust the runs but also make sure
-    // that the line box is aligned as well
-    // e.g. <div style="text-align: center; width: 100px;">centered text</div> : the line box will also be centered
-    // as opposed to start at 0px all the way to [centered text] run's right edge.
-    m_lineBox.moveHorizontally(*adjustment);
-    for (auto& run : m_runs)
-        run.moveHorizontally(*adjustment);
+    contentAligner.alignHorizontally(computedHorizontalAlignment(), isLastLine);
 }
 
 void LineBuilder::removeTrailingTrimmableContent()
@@ -365,31 +522,6 @@
     m_lineBox.shrinkHorizontally(trimmedContentWidth);
 }
 
-HangingContent LineBuilder::collectHangingContent(IsLastLineWithInlineContent isLastLineWithInlineContent)
-{
-    auto hangingContent = HangingContent { };
-    // Can't setup hanging content with removable trailing whitespace.
-    ASSERT(m_trimmableTrailingContent.isEmpty());
-    if (isLastLineWithInlineContent == IsLastLineWithInlineContent::Yes)
-        hangingContent.setIsConditional();
-    for (auto& run : WTF::makeReversedRange(m_runs)) {
-        if (run.isContainerStart() || run.isContainerEnd())
-            continue;
-        if (run.isLineBreak()) {
-            hangingContent.setIsConditional();
-            continue;
-        }
-        if (!run.hasTrailingWhitespace())
-            break;
-        // Check if we have a preserved or hung whitespace.
-        if (run.style().whiteSpace() != WhiteSpace::PreWrap)
-            break;
-        // This is either a normal or conditionally hanging trailing whitespace.
-        hangingContent.expand(run.trailingWhitespaceWidth());
-    }
-    return hangingContent;
-}
-
 void LineBuilder::moveLogicalLeft(InlineLayoutUnit delta)
 {
     if (!delta)
@@ -548,128 +680,6 @@
     m_runs.append({ downcast<InlineSoftLineBreakItem>(inlineItem), contentLogicalWidth() });
 }
 
-void LineBuilder::adjustBaselineAndLineHeight()
-{
-    unsigned inlineContainerNestingLevel = 0;
-    auto hasSeenDirectTextOrLineBreak = false;
-    for (auto& run : m_runs) {
-        auto& layoutBox = run.layoutBox();
-        auto& style = layoutBox.style();
-
-        run.setLogicalHeight(runContentHeight(run));
-
-        if (run.isText() || run.isLineBreak()) {
-            // For text content we set the baseline either through the initial strut (set by the formatting context root) or
-            // through the inline container (start). Normally the text content itself does not stretch the line.
-            if (!m_initialStrut) {
-                // We are in standards mode where the baseline and line height are explict.
-                continue;
-            }
-            if (inlineContainerNestingLevel) {
-                // We've already adjusted the line height/baseline through the parent inline container. 
-                continue;
-            }
-            if (hasSeenDirectTextOrLineBreak) {
-                // e.g div>first text</div> or <div><span>nested<span>first direct text</div>.
-                continue;
-            }
-            hasSeenDirectTextOrLineBreak = true;
-            // We are in quirks mode where the font-metrics might change the line line height/baseline and this is the first text content on the line
-            // outside of an inline container.
-            m_lineBox.setAscentIfGreater(m_initialStrut->ascent);
-            m_lineBox.setDescentIfGreater(m_initialStrut->descent);
-            m_lineBox.setLogicalHeightIfGreater(m_initialStrut->height());
-            continue;
-        }
-
-        if (run.isContainerStart()) {
-            ++inlineContainerNestingLevel;
-            // Inline containers stretch the line by their font size.
-            // Vertical margins, padding and borders don't contribute to the line height.
-            auto& fontMetrics = style.fontMetrics();
-            if (style.verticalAlign() == VerticalAlign::Baseline) {
-                auto halfLeading = halfLeadingMetrics(fontMetrics, style.computedLineHeight());
-                // Both halfleading ascent and descent could be negative (tall font vs. small line-height value)
-                if (halfLeading.descent > 0)
-                    m_lineBox.setDescentIfGreater(halfLeading.descent);
-                if (halfLeading.ascent > 0)
-                    m_lineBox.setAscentIfGreater(halfLeading.ascent);
-                m_lineBox.setLogicalHeightIfGreater(m_lineBox.ascentAndDescent().height());
-            } else
-                m_lineBox.setLogicalHeightIfGreater(fontMetrics.height());
-            continue;
-        }
-
-        if (run.isContainerEnd()) {
-            // The line's baseline and height have already been adjusted at ContainerStart.
-            ASSERT(inlineContainerNestingLevel);
-            --inlineContainerNestingLevel;
-            continue;
-        }
-
-        if (run.isBox()) {
-            auto& boxGeometry = formattingContext().geometryForBox(layoutBox);
-            auto marginBoxHeight = boxGeometry.marginBoxHeight();
-
-            switch (style.verticalAlign()) {
-            case VerticalAlign::Baseline: {
-                if (layoutBox.isInlineBlockBox() && layoutBox.establishesInlineFormattingContext()) {
-                    // Inline-blocks with inline content always have baselines.
-                    auto& formattingState = layoutState().establishedInlineFormattingState(downcast<ContainerBox>(layoutBox));
-                    // There has to be at least one line -even if it is empty.
-                    auto& lastLineBox = formattingState.displayInlineContent()->lineBoxes.last();
-                    auto beforeHeight = boxGeometry.marginBefore() + boxGeometry.borderTop() + boxGeometry.paddingTop().valueOr(0);
-                    m_lineBox.setAlignmentBaselineIfGreater(beforeHeight + lastLineBox.baseline());
-                    m_lineBox.setLogicalHeightIfGreater(marginBoxHeight);
-                } else {
-                    // Non inline-block boxes sit on the baseline (including their bottom margin).
-                    m_lineBox.setAscentIfGreater(marginBoxHeight);
-                    // Ignore negative descent (yes, negative descent is a thing).
-                    m_lineBox.setLogicalHeightIfGreater(marginBoxHeight + std::max<InlineLayoutUnit>(0, m_lineBox.ascentAndDescent().descent));
-                }
-                break;
-            }
-            case VerticalAlign::Top:
-                // Top align content never changes the baseline, it only pushes the bottom of the line further down.
-                m_lineBox.setLogicalHeightIfGreater(marginBoxHeight);
-                break;
-            case VerticalAlign::Bottom: {
-                // Bottom aligned, tall content pushes the baseline further down from the line top.
-                auto lineLogicalHeight = m_lineBox.logicalHeight();
-                if (marginBoxHeight > lineLogicalHeight) {
-                    m_lineBox.setLogicalHeightIfGreater(marginBoxHeight);
-                    m_lineBox.setAlignmentBaselineIfGreater(m_lineBox.alignmentBaseline() + (marginBoxHeight - lineLogicalHeight));
-                }
-                break;
-            }
-            default:
-                ASSERT_NOT_IMPLEMENTED_YET();
-                break;
-            }
-            continue;
-        }
-    }
-}
-
-InlineLayoutUnit LineBuilder::runContentHeight(const Run& run) const
-{
-    ASSERT(!m_isIntrinsicSizing);
-    auto& fontMetrics = run.style().fontMetrics();
-    if (run.isText() || run.isLineBreak())
-        return fontMetrics.height();
-
-    if (run.isContainerStart() || run.isContainerEnd())
-        return fontMetrics.height();
-
-    auto& layoutBox = run.layoutBox();
-    auto& boxGeometry = formattingContext().geometryForBox(layoutBox);
-    if (layoutBox.isReplacedBox() || layoutBox.isFloatingPositioned())
-        return boxGeometry.contentBoxHeight();
-
-    // Non-replaced inline box (e.g. inline-block). It looks a bit misleading but their margin box is considered the content height here.
-    return boxGeometry.marginBoxHeight();
-}
-
 bool LineBuilder::isVisuallyNonEmpty(const Run& run) const
 {
     if (run.isText())
@@ -716,21 +726,11 @@
     return { adjustedAscent, adjustedDescent };
 }
 
-LayoutState& LineBuilder::layoutState() const
-{ 
-    return formattingContext().layoutState();
-}
-
 const InlineFormattingContext& LineBuilder::formattingContext() const
 {
     return m_inlineFormattingContext;
 }
 
-const ContainerBox& LineBuilder::root() const
-{
-    return formattingContext().root();
-}
-
 LineBuilder::TrimmableTrailingContent::TrimmableTrailingContent(RunList& runs)
     : m_runs(runs)
 {

Modified: trunk/Source/WebCore/layout/inlineformatting/InlineLineBuilder.h (266168 => 266169)


--- trunk/Source/WebCore/layout/inlineformatting/InlineLineBuilder.h	2020-08-26 16:18:18 UTC (rev 266168)
+++ trunk/Source/WebCore/layout/inlineformatting/InlineLineBuilder.h	2020-08-26 16:46:04 UTC (rev 266169)
@@ -35,13 +35,11 @@
 namespace WebCore {
 namespace Layout {
 
-struct HangingContent;
 class InlineFormattingContext;
 class InlineSoftLineBreakItem;
+class LineContentAligner;
 
 class LineBuilder {
-    struct ContinuousContent;
-
 public:
     struct Constraints {
         InlineLayoutPoint logicalTopLeft;
@@ -64,7 +62,7 @@
     void clear();
     bool isVisuallyEmpty() const { return m_lineBox.isConsideredEmpty(); }
     bool hasIntrusiveFloat() const { return m_hasIntrusiveFloat; }
-    InlineLayoutUnit availableWidth() const { return logicalWidth() - contentLogicalWidth(); }
+    InlineLayoutUnit availableWidth() const { return m_lineLogicalWidth - contentLogicalWidth(); }
 
     InlineLayoutUnit trimmableTrailingWidth() const { return m_trimmableTrailingContent.width(); }
     bool isTrailingRunFullyTrimmable() const { return m_trimmableTrailingContent.isTrailingRunFullyTrimmable(); }
@@ -92,6 +90,7 @@
 
     private:
         friend class LineBuilder;
+        friend class LineContentAligner;
 
         Run(const InlineTextItem&, InlineLayoutUnit logicalLeft, InlineLayoutUnit logicalWidth, bool needsHypen);
         Run(const InlineSoftLineBreakItem&, InlineLayoutUnit logicalLeft);
@@ -149,18 +148,8 @@
     static AscentAndDescent halfLeadingMetrics(const FontMetrics&, InlineLayoutUnit lineLogicalHeight);
 
 private:
-    InlineLayoutUnit logicalTop() const { return m_lineBox.logicalTop(); }
-    InlineLayoutUnit logicalBottom() const { return m_lineBox.logicalBottom(); }
-
-    InlineLayoutUnit logicalLeft() const { return m_lineBox.logicalLeft(); }
-    InlineLayoutUnit logicalRight() const { return logicalLeft() + logicalWidth(); }
-
-    InlineLayoutUnit logicalWidth() const { return m_lineLogicalWidth; }
-    InlineLayoutUnit logicalHeight() const { return m_lineBox.logicalHeight(); }
-
     InlineLayoutUnit contentLogicalWidth() const { return m_lineBox.logicalWidth(); }
     InlineLayoutUnit contentLogicalRight() const { return m_lineBox.logicalRight(); }
-    InlineLayoutUnit baseline() const { return m_lineBox.alignmentBaseline(); }
 
     struct InlineRunDetails {
         InlineLayoutUnit logicalWidth { 0 };
@@ -177,20 +166,10 @@
 
     void removeTrailingTrimmableContent();
     void visuallyCollapsePreWrapOverflowContent();
-    HangingContent collectHangingContent(IsLastLineWithInlineContent);
-    void alignHorizontally(const HangingContent&, IsLastLineWithInlineContent);
-    void alignContentVertically();
 
-    void adjustBaselineAndLineHeight();
-    InlineLayoutUnit runContentHeight(const Run&) const;
-
-    void justifyRuns(InlineLayoutUnit availableWidth);
-
     bool isVisuallyNonEmpty(const Run&) const;
 
-    LayoutState& layoutState() const;
     const InlineFormattingContext& formattingContext() const;
-    const ContainerBox& root() const;
 
     struct TrimmableTrailingContent {
         TrimmableTrailingContent(RunList&);
@@ -218,7 +197,6 @@
     const InlineFormattingContext& m_inlineFormattingContext;
     RunList m_runs;
     TrimmableTrailingContent m_trimmableTrailingContent;
-    Optional<AscentAndDescent> m_initialStrut;
     InlineLayoutUnit m_lineLogicalWidth { 0 };
     Optional<TextAlignMode> m_horizontalAlignment;
     bool m_isIntrinsicSizing { false };
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to