Title: [281479] trunk/Source/WebCore
Revision
281479
Author
za...@apple.com
Date
2021-08-23 17:20:27 -0700 (Mon, 23 Aug 2021)

Log Message

[LFC][IFC] Decouple line box building and vertical aligning
https://bugs.webkit.org/show_bug.cgi?id=229162
<rdar://problem/82260272>

Reviewed by Antti Koivisto.

This is in preparation for supporting incremental inline layout.
We should be able to vertically align the inline level boxes on any line without rebuilding the LineBox.

* Sources.txt:
* WebCore.xcodeproj/project.pbxproj:
* layout/formattingContexts/inline/InlineFormattingGeometry.cpp:
(WebCore::Layout::LineBoxBuilder::build):
(WebCore::Layout::LineBoxBuilder::constructAndAlignInlineLevelBoxes):
(WebCore::Layout::LineBoxBuilder::SimplifiedVerticalAlignment::rootInlineBoxLogicalTop const): Deleted.
(WebCore::Layout::LineBoxBuilder::SimplifiedVerticalAlignment::lineBoxHeight const): Deleted.
(WebCore::Layout::LineBoxBuilder::SimplifiedVerticalAlignment::setEnabled): Deleted.
(WebCore::Layout::LineBoxBuilder::SimplifiedVerticalAlignment::isEnabled const): Deleted.
(): Deleted.
(WebCore::Layout::LineBoxBuilder::computeLineBoxHeightAndAlignInlineLevelBoxesVertically): Deleted.
(WebCore::Layout::LineBoxBuilder::SimplifiedVerticalAlignment::SimplifiedVerticalAlignment): Deleted.
(WebCore::Layout::LineBoxBuilder::SimplifiedVerticalAlignment::canUseSimplifiedAlignment): Deleted.
(WebCore::Layout::LineBoxBuilder::SimplifiedVerticalAlignment::align): Deleted.
(WebCore::Layout::LineBoxBuilder::SimplifiedVerticalAlignment::adjust): Deleted.
* layout/formattingContexts/inline/InlineLevelBox.h:
* layout/formattingContexts/inline/InlineLineBox.h:

Modified Paths

Added Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (281478 => 281479)


--- trunk/Source/WebCore/ChangeLog	2021-08-23 23:52:10 UTC (rev 281478)
+++ trunk/Source/WebCore/ChangeLog	2021-08-24 00:20:27 UTC (rev 281479)
@@ -1,3 +1,32 @@
+2021-08-23  Alan Bujtas  <za...@apple.com>
+
+        [LFC][IFC] Decouple line box building and vertical aligning
+        https://bugs.webkit.org/show_bug.cgi?id=229162
+        <rdar://problem/82260272>
+
+        Reviewed by Antti Koivisto.
+
+        This is in preparation for supporting incremental inline layout.
+        We should be able to vertically align the inline level boxes on any line without rebuilding the LineBox.
+
+        * Sources.txt:
+        * WebCore.xcodeproj/project.pbxproj:
+        * layout/formattingContexts/inline/InlineFormattingGeometry.cpp:
+        (WebCore::Layout::LineBoxBuilder::build):
+        (WebCore::Layout::LineBoxBuilder::constructAndAlignInlineLevelBoxes):
+        (WebCore::Layout::LineBoxBuilder::SimplifiedVerticalAlignment::rootInlineBoxLogicalTop const): Deleted.
+        (WebCore::Layout::LineBoxBuilder::SimplifiedVerticalAlignment::lineBoxHeight const): Deleted.
+        (WebCore::Layout::LineBoxBuilder::SimplifiedVerticalAlignment::setEnabled): Deleted.
+        (WebCore::Layout::LineBoxBuilder::SimplifiedVerticalAlignment::isEnabled const): Deleted.
+        (): Deleted.
+        (WebCore::Layout::LineBoxBuilder::computeLineBoxHeightAndAlignInlineLevelBoxesVertically): Deleted.
+        (WebCore::Layout::LineBoxBuilder::SimplifiedVerticalAlignment::SimplifiedVerticalAlignment): Deleted.
+        (WebCore::Layout::LineBoxBuilder::SimplifiedVerticalAlignment::canUseSimplifiedAlignment): Deleted.
+        (WebCore::Layout::LineBoxBuilder::SimplifiedVerticalAlignment::align): Deleted.
+        (WebCore::Layout::LineBoxBuilder::SimplifiedVerticalAlignment::adjust): Deleted.
+        * layout/formattingContexts/inline/InlineLevelBox.h:
+        * layout/formattingContexts/inline/InlineLineBox.h:
+
 2021-08-23  Alex Christensen  <achristen...@webkit.org>
 
         Setting window.location.href to an invalid URL should throw a TypeError

Modified: trunk/Source/WebCore/Sources.txt (281478 => 281479)


--- trunk/Source/WebCore/Sources.txt	2021-08-23 23:52:10 UTC (rev 281478)
+++ trunk/Source/WebCore/Sources.txt	2021-08-24 00:20:27 UTC (rev 281479)
@@ -1460,6 +1460,7 @@
 layout/formattingContexts/inline/InlineLine.cpp
 layout/formattingContexts/inline/InlineLineBox.cpp
 layout/formattingContexts/inline/InlineLineBuilder.cpp
+layout/formattingContexts/inline/InlineLineBoxVerticalAligner.cpp
 layout/formattingContexts/inline/InlineTextItem.cpp
 layout/formattingContexts/inline/text/TextUtil.cpp
 layout/integration/LayoutIntegrationBoxTree.cpp

Modified: trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj (281478 => 281479)


--- trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj	2021-08-23 23:52:10 UTC (rev 281478)
+++ trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj	2021-08-24 00:20:27 UTC (rev 281479)
@@ -2147,6 +2147,7 @@
 		6F56B44726479E6200AAE257 /* FormattingQuirks.h in Headers */ = {isa = PBXBuildFile; fileRef = 6F56B44626479E6100AAE257 /* FormattingQuirks.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		6F6383F62427AF4A00DABA53 /* LayoutInitialContainingBlock.h in Headers */ = {isa = PBXBuildFile; fileRef = 6F6383F42427AF4900DABA53 /* LayoutInitialContainingBlock.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		6F6DF36F264722EA0093E834 /* BlockMarginCollapse.h in Headers */ = {isa = PBXBuildFile; fileRef = 6F6DF36E264722EA0093E834 /* BlockMarginCollapse.h */; settings = {ATTRIBUTES = (Private, ); }; };
+		6F6EE74326D44EE300374CDA /* InlineLineBoxVerticalAligner.h in Headers */ = {isa = PBXBuildFile; fileRef = 6F6EE74126D44EE200374CDA /* InlineLineBoxVerticalAligner.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		6F7CA3C6208C2957002F29AB /* LayoutState.h in Headers */ = {isa = PBXBuildFile; fileRef = 6F7CA3C4208C2956002F29AB /* LayoutState.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		6F7CA3CA208C2B2E002F29AB /* InlineFormattingContext.h in Headers */ = {isa = PBXBuildFile; fileRef = 6F7CA3C8208C2B2E002F29AB /* InlineFormattingContext.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		6F91421125152138004E4FEA /* InlineLineGeometry.h in Headers */ = {isa = PBXBuildFile; fileRef = 6F91420F25152137004E4FEA /* InlineLineGeometry.h */; settings = {ATTRIBUTES = (Private, ); }; };
@@ -9975,6 +9976,8 @@
 		6F6638D4249E268B001925FC /* TableWrapperBlockFormattingQuirks.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = TableWrapperBlockFormattingQuirks.cpp; sourceTree = "<group>"; };
 		6F69A79924D6FAB800E6B85D /* InlineLineBox.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = InlineLineBox.cpp; sourceTree = "<group>"; };
 		6F6DF36E264722EA0093E834 /* BlockMarginCollapse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BlockMarginCollapse.h; sourceTree = "<group>"; };
+		6F6EE74126D44EE200374CDA /* InlineLineBoxVerticalAligner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InlineLineBoxVerticalAligner.h; sourceTree = "<group>"; };
+		6F6EE74426D44F0300374CDA /* InlineLineBoxVerticalAligner.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = InlineLineBoxVerticalAligner.cpp; sourceTree = "<group>"; };
 		6F70DEDD251126F300F0FC78 /* FlexFormattingGeometry.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FlexFormattingGeometry.cpp; sourceTree = "<group>"; };
 		6F73918C2106CEDD006AF262 /* LayoutUnits.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LayoutUnits.h; sourceTree = "<group>"; };
 		6F7B8CEC23626E6600C9FF15 /* InlineItem.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = InlineItem.cpp; sourceTree = "<group>"; };
@@ -18042,6 +18045,8 @@
 				6F0CD694229ED32700C5994E /* InlineLine.h */,
 				6F69A79924D6FAB800E6B85D /* InlineLineBox.cpp */,
 				6FB2400323DFF12700796458 /* InlineLineBox.h */,
+				6F6EE74426D44F0300374CDA /* InlineLineBoxVerticalAligner.cpp */,
+				6F6EE74126D44EE200374CDA /* InlineLineBoxVerticalAligner.h */,
 				6F25B200220A85AB0000011B /* InlineLineBuilder.cpp */,
 				6F26EB46234004A5006906E2 /* InlineLineBuilder.h */,
 				6F91420F25152137004E4FEA /* InlineLineGeometry.h */,
@@ -32433,6 +32438,7 @@
 				6FAAE71326A2814B00E07502 /* InlineLevelBox.h in Headers */,
 				6F0CD695229ED32700C5994E /* InlineLine.h in Headers */,
 				6FB2400523DFF12800796458 /* InlineLineBox.h in Headers */,
+				6F6EE74326D44EE300374CDA /* InlineLineBoxVerticalAligner.h in Headers */,
 				6F26EB48234004A5006906E2 /* InlineLineBuilder.h in Headers */,
 				6F91421125152138004E4FEA /* InlineLineGeometry.h in Headers */,
 				6FC3F9472516756700A49BEA /* InlineLineRun.h in Headers */,

Modified: trunk/Source/WebCore/layout/formattingContexts/inline/InlineFormattingGeometry.cpp (281478 => 281479)


--- trunk/Source/WebCore/layout/formattingContexts/inline/InlineFormattingGeometry.cpp	2021-08-23 23:52:10 UTC (rev 281478)
+++ trunk/Source/WebCore/layout/formattingContexts/inline/InlineFormattingGeometry.cpp	2021-08-24 00:20:27 UTC (rev 281479)
@@ -32,6 +32,7 @@
 #include "FormattingContext.h"
 #include "InlineFormattingContext.h"
 #include "InlineFormattingQuirks.h"
+#include "InlineLineBoxVerticalAligner.h"
 #include "LayoutBox.h"
 #include "LayoutContainerBox.h"
 #include "LayoutReplacedBox.h"
@@ -47,32 +48,8 @@
     InlineFormattingGeometry::LineBoxAndGeometry build(const LineBuilder::LineContent&);
 
 private:
-    struct SimplifiedVerticalAlignment {
-        SimplifiedVerticalAlignment(const InlineLevelBox& rootInlineBox);
-
-        static bool canUseSimplifiedAlignment(const InlineLevelBox& rootInlineBox, const InlineLevelBox&, std::optional<const BoxGeometry> inlineLevelBoxGeometry);
-
-        void align(InlineLevelBox&);
-
-        InlineLayoutUnit rootInlineBoxLogicalTop() const { return m_rootInlineBoxLogicalTop; }
-        InlineLayoutUnit lineBoxHeight() const { return m_lineBoxLogicalBottom - m_lineBoxLogicalTop; }
-
-        void setEnabled(bool enabled) { m_isEnabled = enabled; }
-        bool isEnabled() const { return m_isEnabled; }
-
-    private:
-        void adjust(const InlineLevelBox&);
-
-        const InlineLevelBox& m_rootInlineBox;
-        bool m_isEnabled { true };
-        InlineLayoutUnit m_lineBoxLogicalTop { 0 };
-        InlineLayoutUnit m_lineBoxLogicalBottom { 0 };
-        InlineLayoutUnit m_rootInlineBoxLogicalTop { 0 };
-    };
-
     void setVerticalGeometryForInlineBox(InlineLevelBox&) const;
-    void constructAndAlignInlineLevelBoxes(LineBox&, const Line::RunList&);
-    void computeLineBoxHeightAndAlignInlineLevelBoxesVertically(LineBox&);
+    InlineLayoutUnit constructAndAlignInlineLevelBoxes(LineBox&, const Line::RunList&);
 
     const InlineFormattingContext& formattingContext() const { return m_inlineFormattingContext; }
     const Box& rootBox() const { return formattingContext().root(); }
@@ -82,7 +59,6 @@
 
 private:
     const InlineFormattingContext& m_inlineFormattingContext;
-    InlineLayoutUnit m_logicalHeight { 0 };
 };
 
 static InlineLayoutUnit hangingGlyphWidth(InlineLayoutUnit extraHorizontalSpace, const Line::RunList& runs, bool isLastLineWithInlineContent)
@@ -165,10 +141,10 @@
     auto contentLogicalLeft = Layout::horizontalAlignmentOffset(runs, rootBox().style().textAlign(), lineContent.lineLogicalWidth, contentLogicalWidth, lineContent.isLastLineWithInlineContent).value_or(InlineLayoutUnit { });
     auto lineBox = LineBox { rootBox(), contentLogicalLeft, contentLogicalWidth, lineContent.nonSpanningInlineLevelBoxCount };
 
-    constructAndAlignInlineLevelBoxes(lineBox, runs);
+    auto lineBoxLogicalHeight = constructAndAlignInlineLevelBoxes(lineBox, runs);
 
     auto lineGeometry = [&] {
-        auto lineBoxLogicalRect = InlineRect { lineContent.logicalTopLeft, lineContent.lineLogicalWidth, m_logicalHeight };
+        auto lineBoxLogicalRect = InlineRect { lineContent.logicalTopLeft, lineContent.lineLogicalWidth, lineBoxLogicalHeight };
         auto& rootInlineBox = lineBox.rootInlineBox();
         auto enclosingTopAndBottom = LineGeometry::EnclosingTopAndBottom { rootInlineBox.logicalTop(), rootInlineBox.logicalBottom() };
 
@@ -229,23 +205,18 @@
     inlineLevelBox.setLayoutBounds(InlineLevelBox::LayoutBounds { floorf(ascent), ceil(descent) });
 }
 
-void LineBoxBuilder::constructAndAlignInlineLevelBoxes(LineBox& lineBox, const Line::RunList& runs)
+InlineLayoutUnit LineBoxBuilder::constructAndAlignInlineLevelBoxes(LineBox& lineBox, const Line::RunList& runs)
 {
     auto& rootInlineBox = lineBox.rootInlineBox();
     setVerticalGeometryForInlineBox(rootInlineBox);
 
-    auto simplifiedVerticalAlignment = SimplifiedVerticalAlignment { rootInlineBox };
     // FIXME: Add fast path support for line-height content.
-    simplifiedVerticalAlignment.setEnabled(layoutState().inStandardsMode() && rootBox().style().lineHeight().isNegative());
-
-    auto simplifiedAlignVerticallyIfApplicable = [&](auto& inlineLevelBox, std::optional<const BoxGeometry> boxGeometry) {
-        if (!simplifiedVerticalAlignment.isEnabled())
+    // FIXME: We should always be able to exercise the fast path when the line has no content at all, even in non-standards mode or with line-height set.
+    auto canUseSimplifiedAlignment = layoutState().inStandardsMode() && rootBox().style().lineHeight().isNegative();
+    auto updateCanUseSimplifiedAlignment = [&](auto& inlineLevelBox, std::optional<const BoxGeometry> boxGeometry = std::nullopt) {
+        if (!canUseSimplifiedAlignment)
             return;
-        if (!SimplifiedVerticalAlignment::canUseSimplifiedAlignment(rootInlineBox, inlineLevelBox, boxGeometry)) {
-            simplifiedVerticalAlignment.setEnabled(false);
-            return;
-        }
-        simplifiedVerticalAlignment.align(inlineLevelBox);
+        canUseSimplifiedAlignment = LineBoxVerticalAligner::canUseSimplifiedAlignmentForInlineLevelBox(rootInlineBox, inlineLevelBox, boxGeometry);
     };
 
     auto createWrappedInlineBoxes = [&] {
@@ -279,7 +250,7 @@
         for (auto* layoutBox : WTF::makeReversedRange(layoutBoxesWithoutInlineBoxes)) {
             auto inlineBox = InlineLevelBox::createInlineBox(*layoutBox, rootInlineBox.logicalLeft(), rootInlineBox.logicalWidth());
             setVerticalGeometryForInlineBox(inlineBox);
-            simplifiedAlignVerticallyIfApplicable(inlineBox, { });
+            updateCanUseSimplifiedAlignment(inlineBox);
             lineBox.addInlineLevelBox(WTFMove(inlineBox));
         }
     };
@@ -333,7 +304,7 @@
             auto atomicInlineLevelBox = InlineLevelBox::createAtomicInlineLevelBox(layoutBox, logicalLeft, { inlineLevelBoxGeometry.borderBoxWidth(), marginBoxHeight });
             atomicInlineLevelBox.setBaseline(ascent);
             atomicInlineLevelBox.setLayoutBounds(InlineLevelBox::LayoutBounds { ascent, marginBoxHeight - ascent });
-            simplifiedAlignVerticallyIfApplicable(atomicInlineLevelBox, inlineLevelBoxGeometry);
+            updateCanUseSimplifiedAlignment(atomicInlineLevelBox, inlineLevelBoxGeometry);
             lineBox.addInlineLevelBox(WTFMove(atomicInlineLevelBox));
             continue;
         }
@@ -346,7 +317,7 @@
             ASSERT(initialLogicalWidth >= 0);
             auto inlineBox = InlineLevelBox::createInlineBox(layoutBox, logicalLeft + marginStart, initialLogicalWidth);
             setVerticalGeometryForInlineBox(inlineBox);
-            simplifiedAlignVerticallyIfApplicable(inlineBox, { });
+            updateCanUseSimplifiedAlignment(inlineBox);
             lineBox.addInlineLevelBox(WTFMove(inlineBox));
             continue;
         }
@@ -363,7 +334,7 @@
             // When the content pulls the </span> to the logical left direction (e.g. negative letter space)
             // make sure we don't end up with negative logical width on the inline box.
             inlineBox.setLogicalWidth(std::max(0.f, inlineBoxLogicalRight - inlineBox.logicalLeft()));
-            simplifiedAlignVerticallyIfApplicable(inlineBox, { });
+            updateCanUseSimplifiedAlignment(inlineBox);
             continue;
         }
         if (run.isText() || run.isSoftLineBreak()) {
@@ -374,7 +345,7 @@
         if (run.isHardLineBreak()) {
             auto lineBreakBox = InlineLevelBox::createLineBreakBox(layoutBox, logicalLeft);
             setVerticalGeometryForInlineBox(lineBreakBox);
-            simplifiedAlignVerticallyIfApplicable(lineBreakBox, { });
+            updateCanUseSimplifiedAlignment(lineBreakBox);
             lineBox.addInlineLevelBox(WTFMove(lineBreakBox));
             continue;
         }
@@ -386,326 +357,12 @@
     }
 
     lineBox.setHasContent(lineHasContent);
-    if (!lineHasContent) {
-        m_logicalHeight = { };
-        rootInlineBox.setLogicalTop(-rootInlineBox.baseline());
-    } else if (simplifiedVerticalAlignment.isEnabled()) {
-        // We should always be able to exercise the fast path when the line has no content at all, even in non-standards mode or with line-height set.
-        m_logicalHeight = simplifiedVerticalAlignment.lineBoxHeight();
-        rootInlineBox.setLogicalTop(simplifiedVerticalAlignment.rootInlineBoxLogicalTop());
-    } else
-        computeLineBoxHeightAndAlignInlineLevelBoxesVertically(lineBox);
-}
 
-void LineBoxBuilder::computeLineBoxHeightAndAlignInlineLevelBoxesVertically(LineBox& lineBox)
-{
-    ASSERT(lineBox.hasContent());
-    // This function (partially) implements:
-    // 2.2. Layout Within Line Boxes
-    // https://www.w3.org/TR/css-inline-3/#line-layout
-    // 1. Compute the line box height using the layout bounds geometry. This height computation strictly uses layout bounds and not normal inline level box geometries.
-    // 2. Compute the baseline/logical top position of the root inline box. Aligned boxes push the root inline box around inside the line box.
-    // 3. Finally align the inline level boxes using (mostly) normal inline level box geometries.
-    auto& rootInlineBox = lineBox.rootInlineBox();
-    auto& formattingGeometry = formattingContext().formattingGeometry();
-
-    auto computeLineBoxLogicalHeight = [&] {
-        // Line box height computation is based on the layout bounds of the inline boxes and not their logical (ascent/descent) dimensions.
-        struct AbsoluteTopAndBottom {
-            InlineLayoutUnit top { 0 };
-            InlineLayoutUnit bottom { 0 };
-        };
-        HashMap<const InlineLevelBox*, AbsoluteTopAndBottom> inlineLevelBoxAbsoluteTopAndBottomMap;
-
-        auto minimumLogicalTop = std::optional<InlineLayoutUnit> { };
-        auto maximumLogicalBottom = std::optional<InlineLayoutUnit> { };
-        if (formattingGeometry.inlineLevelBoxAffectsLineBox(rootInlineBox, lineBox)) {
-            minimumLogicalTop = InlineLayoutUnit { };
-            maximumLogicalBottom = rootInlineBox.layoutBounds().height();
-            inlineLevelBoxAbsoluteTopAndBottomMap.add(&rootInlineBox, AbsoluteTopAndBottom { *minimumLogicalTop, *maximumLogicalBottom });
-        } else
-            inlineLevelBoxAbsoluteTopAndBottomMap.add(&rootInlineBox, AbsoluteTopAndBottom { });
-
-        Vector<InlineLevelBox*> lineBoxRelativeInlineLevelBoxes;
-        for (auto& inlineLevelBox : lineBox.nonRootInlineLevelBoxes()) {
-            auto& layoutBox = inlineLevelBox.layoutBox();
-            if (inlineLevelBox.hasLineBoxRelativeAlignment()) {
-                lineBoxRelativeInlineLevelBoxes.append(&inlineLevelBox);
-                continue;
-            }
-            auto& parentInlineBox = lineBox.inlineLevelBoxForLayoutBox(layoutBox.parent());
-            // Logical top is relative to the parent inline box's layout bounds.
-            // Note that this logical top is not the final logical top of the inline level box.
-            // This is the logical top in the context of the layout bounds geometry which may be very different from the inline box's normal geometry.
-            auto logicalTop = InlineLayoutUnit { };
-            switch (inlineLevelBox.verticalAlign()) {
-            case VerticalAlign::Baseline: {
-                auto logicalTopOffsetFromParentBaseline = inlineLevelBox.layoutBounds().ascent;
-                logicalTop = parentInlineBox.layoutBounds().ascent - logicalTopOffsetFromParentBaseline;
-                break;
-            }
-            case VerticalAlign::Middle: {
-                auto logicalTopOffsetFromParentBaseline = inlineLevelBox.layoutBounds().height() / 2 + parentInlineBox.style().fontMetrics().xHeight() / 2;
-                logicalTop = parentInlineBox.layoutBounds().ascent - logicalTopOffsetFromParentBaseline;
-                break;
-            }
-            case VerticalAlign::BaselineMiddle: {
-                auto logicalTopOffsetFromParentBaseline = inlineLevelBox.layoutBounds().height() / 2;
-                logicalTop = parentInlineBox.layoutBounds().ascent - logicalTopOffsetFromParentBaseline;
-                break;
-            }
-            case VerticalAlign::Length: {
-                auto& style = inlineLevelBox.style();
-                auto logicalTopOffsetFromParentBaseline = floatValueForLength(style.verticalAlignLength(), style.computedLineHeight()) + inlineLevelBox.layoutBounds().ascent;
-                logicalTop = parentInlineBox.layoutBounds().ascent - logicalTopOffsetFromParentBaseline;
-                break;
-            }
-            case VerticalAlign::TextTop: {
-                // Note that text-top aligns with the inline box's font metrics top (ascent) and not the layout bounds top.
-                logicalTop = parentInlineBox.layoutBounds().ascent - parentInlineBox.baseline();
-                break;
-            }
-            case VerticalAlign::TextBottom: {
-                // Note that text-bottom aligns with the inline box's font metrics bottom (descent) and not the layout bounds bottom.
-                auto parentInlineBoxLayoutBounds = parentInlineBox.layoutBounds();
-                auto parentInlineBoxLogicalBottom = parentInlineBoxLayoutBounds.height() - parentInlineBoxLayoutBounds.descent + parentInlineBox.descent().value_or(InlineLayoutUnit());
-                logicalTop = parentInlineBoxLogicalBottom - inlineLevelBox.layoutBounds().height();
-                break;
-            }
-            case VerticalAlign::Sub: {
-                auto logicalTopOffsetFromParentBaseline = inlineLevelBox.layoutBounds().ascent - (parentInlineBox.style().fontCascade().size() / 5 + 1);
-                logicalTop = parentInlineBox.layoutBounds().ascent - logicalTopOffsetFromParentBaseline;
-                break;
-            }
-            case VerticalAlign::Super: {
-                auto logicalTopOffsetFromParentBaseline = inlineLevelBox.layoutBounds().ascent + parentInlineBox.style().fontCascade().size() / 3 + 1;
-                logicalTop = parentInlineBox.layoutBounds().ascent - logicalTopOffsetFromParentBaseline;
-                break;
-            }
-            default:
-                ASSERT_NOT_IMPLEMENTED_YET();
-                break;
-            }
-            auto parentInlineBoxAbsoluteTopAndBottom = inlineLevelBoxAbsoluteTopAndBottomMap.get(&parentInlineBox);
-            auto absoluteLogicalTop = parentInlineBoxAbsoluteTopAndBottom.top + logicalTop;
-            auto absoluteLogicalBottom = absoluteLogicalTop + inlineLevelBox.layoutBounds().height();
-            inlineLevelBoxAbsoluteTopAndBottomMap.add(&inlineLevelBox, AbsoluteTopAndBottom { absoluteLogicalTop, absoluteLogicalBottom });
-            // Stretch the min/max absolute values if applicable.
-            if (formattingGeometry.inlineLevelBoxAffectsLineBox(inlineLevelBox, lineBox)) {
-                minimumLogicalTop = std::min(minimumLogicalTop.value_or(absoluteLogicalTop), absoluteLogicalTop);
-                maximumLogicalBottom = std::max(maximumLogicalBottom.value_or(absoluteLogicalBottom), absoluteLogicalBottom);
-            }
-        }
-        // The line box height computation is as follows:
-        // 1. Stretch the line box with the non-line-box relative aligned inline box absolute top and bottom values.
-        // 2. Check if the line box relative aligned inline boxes (top, bottom etc) have enough room and stretch the line box further if needed.
-        auto lineBoxLogicalHeight = maximumLogicalBottom.value_or(InlineLayoutUnit()) - minimumLogicalTop.value_or(InlineLayoutUnit());
-        for (auto* lineBoxRelativeInlineLevelBox : lineBoxRelativeInlineLevelBoxes) {
-            if (!formattingGeometry.inlineLevelBoxAffectsLineBox(*lineBoxRelativeInlineLevelBox, lineBox))
-                continue;
-            lineBoxLogicalHeight = std::max(lineBoxLogicalHeight, lineBoxRelativeInlineLevelBox->layoutBounds().height());
-        }
-        m_logicalHeight = lineBoxLogicalHeight;
-    };
-    computeLineBoxLogicalHeight();
-
-    auto computeRootInlineBoxVerticalPosition = [&] {
-        HashMap<const InlineLevelBox*, InlineLayoutUnit> inlineLevelBoxAbsoluteBaselineOffsetMap;
-        inlineLevelBoxAbsoluteBaselineOffsetMap.add(&rootInlineBox, InlineLayoutUnit { });
-
-        auto maximumTopOffsetFromRootInlineBoxBaseline = std::optional<InlineLayoutUnit> { };
-        if (formattingGeometry.inlineLevelBoxAffectsLineBox(rootInlineBox, lineBox))
-            maximumTopOffsetFromRootInlineBoxBaseline = rootInlineBox.layoutBounds().ascent;
-
-        for (auto& inlineLevelBox : lineBox.nonRootInlineLevelBoxes()) {
-            auto absoluteBaselineOffset = InlineLayoutUnit { };
-            if (!inlineLevelBox.hasLineBoxRelativeAlignment()) {
-                auto& layoutBox = inlineLevelBox.layoutBox();
-                auto& parentInlineBox = lineBox.inlineLevelBoxForLayoutBox(layoutBox.parent());
-                auto baselineOffsetFromParentBaseline = InlineLayoutUnit { };
-
-                switch (inlineLevelBox.verticalAlign()) {
-                case VerticalAlign::Baseline:
-                    baselineOffsetFromParentBaseline = { };
-                    break;
-                case VerticalAlign::Middle: {
-                    auto logicalTopOffsetFromParentBaseline = (inlineLevelBox.layoutBounds().height() / 2 + parentInlineBox.style().fontMetrics().xHeight() / 2);
-                    baselineOffsetFromParentBaseline = logicalTopOffsetFromParentBaseline - inlineLevelBox.layoutBounds().ascent;
-                    break;
-                }
-                case VerticalAlign::BaselineMiddle: {
-                    auto logicalTopOffsetFromParentBaseline = inlineLevelBox.layoutBounds().height() / 2;
-                    baselineOffsetFromParentBaseline = logicalTopOffsetFromParentBaseline - inlineLevelBox.layoutBounds().ascent;
-                    break;
-                }
-                case VerticalAlign::Length: {
-                    auto& style = inlineLevelBox.style();
-                    auto verticalAlignOffset = floatValueForLength(style.verticalAlignLength(), style.computedLineHeight());
-                    auto logicalTopOffsetFromParentBaseline = verticalAlignOffset + inlineLevelBox.baseline();
-                    baselineOffsetFromParentBaseline = logicalTopOffsetFromParentBaseline - inlineLevelBox.baseline();
-                    break;
-                }
-                case VerticalAlign::TextTop:
-                    baselineOffsetFromParentBaseline = parentInlineBox.baseline() - inlineLevelBox.layoutBounds().ascent;
-                    break;
-                case VerticalAlign::TextBottom:
-                    baselineOffsetFromParentBaseline = inlineLevelBox.layoutBounds().descent - *parentInlineBox.descent();
-                    break;
-                case VerticalAlign::Sub:
-                    baselineOffsetFromParentBaseline = -(parentInlineBox.style().fontCascade().size() / 5 + 1);
-                    break;
-                case VerticalAlign::Super:
-                    baselineOffsetFromParentBaseline = parentInlineBox.style().fontCascade().size() / 3 + 1;
-                    break;
-                default:
-                    ASSERT_NOT_IMPLEMENTED_YET();
-                    break;
-                }
-                absoluteBaselineOffset = inlineLevelBoxAbsoluteBaselineOffsetMap.get(&parentInlineBox) + baselineOffsetFromParentBaseline;
-            } else {
-                switch (inlineLevelBox.verticalAlign()) {
-                case VerticalAlign::Top: {
-                    absoluteBaselineOffset = rootInlineBox.layoutBounds().ascent - inlineLevelBox.layoutBounds().ascent;
-                    break;
-                }
-                case VerticalAlign::Bottom: {
-                    absoluteBaselineOffset = inlineLevelBox.layoutBounds().descent - rootInlineBox.layoutBounds().descent;
-                    break;
-                }
-                default:
-                    ASSERT_NOT_IMPLEMENTED_YET();
-                    break;
-                }
-            }
-            inlineLevelBoxAbsoluteBaselineOffsetMap.add(&inlineLevelBox, absoluteBaselineOffset);
-            auto affectsRootInlineBoxVerticalPosition = formattingGeometry.inlineLevelBoxAffectsLineBox(inlineLevelBox, lineBox);
-            if (affectsRootInlineBoxVerticalPosition) {
-                auto topOffsetFromRootInlineBoxBaseline = absoluteBaselineOffset + inlineLevelBox.layoutBounds().ascent;
-                if (maximumTopOffsetFromRootInlineBoxBaseline)
-                    maximumTopOffsetFromRootInlineBoxBaseline = std::max(*maximumTopOffsetFromRootInlineBoxBaseline, topOffsetFromRootInlineBoxBaseline);
-                else {
-                    // We are is quirk mode and the root inline box has no content. The root inline box's baseline is anchored at 0.
-                    // However negative ascent (e.g negative top margin) can "push" the root inline box upwards and have a negative value.
-                    maximumTopOffsetFromRootInlineBoxBaseline = inlineLevelBox.layoutBounds().ascent >= 0
-                        ? std::max(0.0f, topOffsetFromRootInlineBoxBaseline)
-                        : topOffsetFromRootInlineBoxBaseline;
-                }
-            }
-        }
-        auto rootInlineBoxLogicalTop = maximumTopOffsetFromRootInlineBoxBaseline.value_or(0.f) - rootInlineBox.baseline();
-        rootInlineBox.setLogicalTop(rootInlineBoxLogicalTop);
-    };
-    computeRootInlineBoxVerticalPosition();
-
-    auto alignInlineLevelBoxes = [&] {
-        for (auto& inlineLevelBox : lineBox.nonRootInlineLevelBoxes()) {
-            auto& layoutBox = inlineLevelBox.layoutBox();
-            auto& parentInlineBox = lineBox.inlineLevelBoxForLayoutBox(layoutBox.parent());
-            auto logicalTop = InlineLayoutUnit { };
-            switch (inlineLevelBox.verticalAlign()) {
-            case VerticalAlign::Baseline:
-                logicalTop = parentInlineBox.baseline() - inlineLevelBox.baseline();
-                break;
-            case VerticalAlign::Middle: {
-                auto logicalTopOffsetFromParentBaseline = (inlineLevelBox.logicalHeight() / 2 + parentInlineBox.style().fontMetrics().xHeight() / 2);
-                logicalTop = parentInlineBox.baseline() - logicalTopOffsetFromParentBaseline;
-                break;
-            }
-            case VerticalAlign::BaselineMiddle: {
-                auto logicalTopOffsetFromParentBaseline = inlineLevelBox.logicalHeight() / 2;
-                logicalTop = parentInlineBox.baseline() - logicalTopOffsetFromParentBaseline;
-                break;
-            }
-            case VerticalAlign::Length: {
-                auto& style = inlineLevelBox.style();
-                auto verticalAlignOffset = floatValueForLength(style.verticalAlignLength(), style.computedLineHeight());
-                auto logicalTopOffsetFromParentBaseline = verticalAlignOffset + inlineLevelBox.baseline();
-                logicalTop = parentInlineBox.baseline() - logicalTopOffsetFromParentBaseline;
-                break;
-            }
-            case VerticalAlign::Sub: {
-                auto logicalTopOffsetFromParentBaseline = inlineLevelBox.baseline() - (parentInlineBox.style().fontCascade().size() / 5 + 1);
-                logicalTop = parentInlineBox.baseline() - logicalTopOffsetFromParentBaseline;
-                break;
-            }
-            case VerticalAlign::Super: {
-                auto logicalTopOffsetFromParentBaseline = inlineLevelBox.baseline() + parentInlineBox.style().fontCascade().size() / 3 + 1;
-                logicalTop = parentInlineBox.baseline() - logicalTopOffsetFromParentBaseline;
-                break;
-            }
-            // Note that (text)top/bottom align their layout bounds.
-            case VerticalAlign::TextTop:
-                logicalTop = inlineLevelBox.layoutBounds().ascent - inlineLevelBox.baseline();
-                break;
-            case VerticalAlign::TextBottom:
-                logicalTop = parentInlineBox.logicalHeight() - inlineLevelBox.layoutBounds().descent - inlineLevelBox.baseline();
-                break;
-            case VerticalAlign::Top:
-                // Note that this logical top is not relative to the parent inline box.
-                logicalTop = inlineLevelBox.layoutBounds().ascent - inlineLevelBox.baseline();
-                break;
-            case VerticalAlign::Bottom:
-                // Note that this logical top is not relative to the parent inline box.
-                logicalTop = m_logicalHeight - inlineLevelBox.layoutBounds().descent - inlineLevelBox.baseline();
-                break;
-            default:
-                ASSERT_NOT_IMPLEMENTED_YET();
-                break;
-            }
-            inlineLevelBox.setLogicalTop(logicalTop);
-        }
-    };
-    alignInlineLevelBoxes();
+    auto verticalAligner = LineBoxVerticalAligner { formattingContext() };
+    canUseSimplifiedAlignment = canUseSimplifiedAlignment || !lineHasContent;
+    return verticalAligner.computeLogicalHeightAndAlign(lineBox, canUseSimplifiedAlignment);
 }
 
-LineBoxBuilder::SimplifiedVerticalAlignment::SimplifiedVerticalAlignment(const InlineLevelBox& rootInlineBox)
-    : m_rootInlineBox(rootInlineBox)
-{
-    adjust(rootInlineBox);
-}
-
-bool LineBoxBuilder::SimplifiedVerticalAlignment::canUseSimplifiedAlignment(const InlineLevelBox& rootInlineBox, const InlineLevelBox& inlineLevelBox, std::optional<const BoxGeometry> inlineLevelBoxGeometry)
-{
-    if (inlineLevelBox.isAtomicInlineLevelBox()) {
-        ASSERT(inlineLevelBoxGeometry);
-        // Baseline aligned, non-stretchy direct children are considered to be simple for now.
-        auto& layoutBox = inlineLevelBox.layoutBox();
-        return &layoutBox.parent() == &rootInlineBox.layoutBox()
-            && layoutBox.style().verticalAlign() == VerticalAlign::Baseline
-            && !inlineLevelBoxGeometry->marginBefore()
-            && !inlineLevelBoxGeometry->marginAfter()
-            && inlineLevelBoxGeometry->marginBoxHeight() <= rootInlineBox.baseline();
-    }
-    if (inlineLevelBox.isLineBreakBox()) {
-        // Baseline aligned, non-stretchy line breaks e.g. <div><span><br></span></div> but not <div><span style="font-size: 100px;"><br></span></div>.
-        return inlineLevelBox.layoutBox().style().verticalAlign() == VerticalAlign::Baseline && inlineLevelBox.baseline() <= rootInlineBox.baseline();
-    }
-    if (inlineLevelBox.isInlineBox()) {
-        // Baseline aligned, non-stretchy inline boxes e.g. <div><span></span></div> but not <div><span style="font-size: 100px;"></span></div>.
-        return inlineLevelBox.layoutBox().style().verticalAlign() == VerticalAlign::Baseline && inlineLevelBox.layoutBounds() == rootInlineBox.layoutBounds();
-    }
-    return false;
-}
-
-void LineBoxBuilder::SimplifiedVerticalAlignment::align(InlineLevelBox& inlineLevelBox)
-{
-    if (inlineLevelBox.isAtomicInlineLevelBox() || inlineLevelBox.isLineBreakBox() || inlineLevelBox.isInlineBox()) {
-        // Only baseline alignment for now.
-        inlineLevelBox.setLogicalTop(m_rootInlineBox.baseline() - inlineLevelBox.baseline());
-        adjust(inlineLevelBox);
-        return;
-    }
-    ASSERT_NOT_IMPLEMENTED_YET();
-}
-
-void LineBoxBuilder::SimplifiedVerticalAlignment::adjust(const InlineLevelBox& inlineLevelBox)
-{
-    auto layoutBoundsLogicalTop = m_rootInlineBox.layoutBounds().ascent - inlineLevelBox.layoutBounds().ascent;
-    m_lineBoxLogicalTop = std::min(m_lineBoxLogicalTop, layoutBoundsLogicalTop);
-    m_lineBoxLogicalBottom = std::max(m_lineBoxLogicalBottom, layoutBoundsLogicalTop + inlineLevelBox.layoutBounds().height());
-    m_rootInlineBoxLogicalTop = std::max(m_rootInlineBoxLogicalTop, inlineLevelBox.layoutBounds().ascent - m_rootInlineBox.baseline());
-}
-
 InlineFormattingGeometry::InlineFormattingGeometry(const InlineFormattingContext& inlineFormattingContext)
     : FormattingGeometry(inlineFormattingContext)
 {

Modified: trunk/Source/WebCore/layout/formattingContexts/inline/InlineLevelBox.h (281478 => 281479)


--- trunk/Source/WebCore/layout/formattingContexts/inline/InlineLevelBox.h	2021-08-23 23:52:10 UTC (rev 281478)
+++ trunk/Source/WebCore/layout/formattingContexts/inline/InlineLevelBox.h	2021-08-24 00:20:27 UTC (rev 281479)
@@ -37,7 +37,7 @@
 
 class LineBox;
 class LineBoxBuilder;
-struct SimplifiedVerticalAlignment;
+class LineBoxVerticalAligner;
 
 class InlineLevelBox {
 public:
@@ -84,9 +84,9 @@
     InlineLevelBox() = default;
 
 private:
-    friend struct SimplifiedVerticalAlignment;
+    friend class LineBox;
     friend class LineBoxBuilder;
-    friend class LineBox;
+    friend class LineBoxVerticalAligner;
 
     const InlineRect& logicalRect() const { return m_logicalRect; }
     InlineLayoutUnit logicalTop() const { return m_logicalRect.top(); }

Modified: trunk/Source/WebCore/layout/formattingContexts/inline/InlineLineBox.h (281478 => 281479)


--- trunk/Source/WebCore/layout/formattingContexts/inline/InlineLineBox.h	2021-08-23 23:52:10 UTC (rev 281478)
+++ trunk/Source/WebCore/layout/formattingContexts/inline/InlineLineBox.h	2021-08-24 00:20:27 UTC (rev 281479)
@@ -40,7 +40,7 @@
 class BoxGeometry;
 class InlineFormattingContext;
 class LineBoxBuilder;
-struct SimplifiedVerticalAlignment;
+class LineBoxVerticalAligner;
 
 //   ____________________________________________________________ Line Box
 // |                                    --------------------
@@ -82,6 +82,7 @@
 
 private:
     friend class LineBoxBuilder;
+    friend class LineBoxVerticalAligner;
 
     void addInlineLevelBox(InlineLevelBox&&);
     InlineLevelBoxList& nonRootInlineLevelBoxes() { return m_nonRootInlineLevelBoxList; }

Added: trunk/Source/WebCore/layout/formattingContexts/inline/InlineLineBoxVerticalAligner.cpp (0 => 281479)


--- trunk/Source/WebCore/layout/formattingContexts/inline/InlineLineBoxVerticalAligner.cpp	                        (rev 0)
+++ trunk/Source/WebCore/layout/formattingContexts/inline/InlineLineBoxVerticalAligner.cpp	2021-08-24 00:20:27 UTC (rev 281479)
@@ -0,0 +1,371 @@
+/*
+ * Copyright (C) 2021 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "InlineLineBoxVerticalAligner.h"
+
+#if ENABLE(LAYOUT_FORMATTING_CONTEXT)
+
+namespace WebCore {
+namespace Layout {
+
+LineBoxVerticalAligner::LineBoxVerticalAligner(const InlineFormattingContext& inlineFormattingContext)
+    : m_inlineFormattingGeometry(inlineFormattingContext)
+{
+}
+
+bool LineBoxVerticalAligner::canUseSimplifiedAlignmentForInlineLevelBox(const InlineLevelBox& rootInlineBox, const InlineLevelBox& inlineLevelBox, std::optional<const BoxGeometry> inlineLevelBoxGeometry)
+{
+    if (inlineLevelBox.isAtomicInlineLevelBox()) {
+        ASSERT(inlineLevelBoxGeometry);
+        // Baseline aligned, non-stretchy direct children are considered to be simple for now.
+        auto& layoutBox = inlineLevelBox.layoutBox();
+        return &layoutBox.parent() == &rootInlineBox.layoutBox()
+            && layoutBox.style().verticalAlign() == VerticalAlign::Baseline
+            && !inlineLevelBoxGeometry->marginBefore()
+            && !inlineLevelBoxGeometry->marginAfter()
+            && inlineLevelBoxGeometry->marginBoxHeight() <= rootInlineBox.baseline();
+    }
+    if (inlineLevelBox.isLineBreakBox()) {
+        // Baseline aligned, non-stretchy line breaks e.g. <div><span><br></span></div> but not <div><span style="font-size: 100px;"><br></span></div>.
+        return inlineLevelBox.layoutBox().style().verticalAlign() == VerticalAlign::Baseline && inlineLevelBox.baseline() <= rootInlineBox.baseline();
+    }
+    if (inlineLevelBox.isInlineBox()) {
+        // Baseline aligned, non-stretchy inline boxes e.g. <div><span></span></div> but not <div><span style="font-size: 100px;"></span></div>.
+        return inlineLevelBox.layoutBox().style().verticalAlign() == VerticalAlign::Baseline && inlineLevelBox.layoutBounds() == rootInlineBox.layoutBounds();
+    }
+    return false;
+}
+
+InlineLayoutUnit LineBoxVerticalAligner::computeLogicalHeightAndAlign(LineBox& lineBox, bool useSimplifiedAlignment) const
+{
+    if (useSimplifiedAlignment)
+        return simplifiedVerticalAlignment(lineBox);
+    // This function (partially) implements:
+    // 2.2. Layout Within Line Boxes
+    // https://www.w3.org/TR/css-inline-3/#line-layout
+    // 1. Compute the line box height using the layout bounds geometry. This height computation strictly uses layout bounds and not normal inline level box geometries.
+    // 2. Compute the baseline/logical top position of the root inline box. Aligned boxes push the root inline box around inside the line box.
+    // 3. Finally align the inline level boxes using (mostly) normal inline level box geometries.
+    ASSERT(lineBox.hasContent());
+    auto lineBoxLogicalHeight = computeLineBoxLogicalHeight(lineBox);
+    computeRootInlineBoxVerticalPosition(lineBox);
+    alignInlineLevelBoxes(lineBox, lineBoxLogicalHeight);
+    return lineBoxLogicalHeight;
+}
+
+InlineLayoutUnit LineBoxVerticalAligner::simplifiedVerticalAlignment(LineBox& lineBox) const
+{
+    auto& rootInlineBox = lineBox.rootInlineBox();
+    auto rootInlineBoxBaseline = rootInlineBox.baseline();
+
+    if (!lineBox.hasContent()) {
+        rootInlineBox.setLogicalTop(-rootInlineBoxBaseline);
+        return { };
+    }
+
+    auto rootInlineBoxLayoutBounds = rootInlineBox.layoutBounds();
+
+    auto lineBoxLogicalTop = InlineLayoutUnit { 0 };
+    auto lineBoxLogicalBottom = rootInlineBoxLayoutBounds.height();
+    auto rootInlineBoxLogicalTop = rootInlineBoxLayoutBounds.ascent - rootInlineBoxBaseline;
+    for (auto& inlineLevelBox : lineBox.nonRootInlineLevelBoxes()) {
+        // Only baseline alignment for now.
+        inlineLevelBox.setLogicalTop(rootInlineBoxBaseline - inlineLevelBox.baseline());
+        auto layoutBounds = inlineLevelBox.layoutBounds();
+
+        auto layoutBoundsLogicalTop = rootInlineBoxLayoutBounds.ascent - layoutBounds.ascent;
+        lineBoxLogicalTop = std::min(lineBoxLogicalTop, layoutBoundsLogicalTop);
+        lineBoxLogicalBottom = std::max(lineBoxLogicalBottom, layoutBoundsLogicalTop + layoutBounds.height());
+        rootInlineBoxLogicalTop = std::max(rootInlineBoxLogicalTop, layoutBounds.ascent - rootInlineBoxBaseline);
+    }
+    rootInlineBox.setLogicalTop(rootInlineBoxLogicalTop);
+    return lineBoxLogicalBottom - lineBoxLogicalTop;
+}
+
+InlineLayoutUnit LineBoxVerticalAligner::computeLineBoxLogicalHeight(LineBox& lineBox) const
+{
+    // This function (partially) implements:
+    // 2.2. Layout Within Line Boxes
+    // https://www.w3.org/TR/css-inline-3/#line-layout
+    // 1. Compute the line box height using the layout bounds geometry. This height computation strictly uses layout bounds and not normal inline level box geometries.
+    // 2. Compute the baseline/logical top position of the root inline box. Aligned boxes push the root inline box around inside the line box.
+    // 3. Finally align the inline level boxes using (mostly) normal inline level box geometries.
+    auto& rootInlineBox = lineBox.rootInlineBox();
+    auto& formattingGeometry = this->formattingGeometry();
+
+    // Line box height computation is based on the layout bounds of the inline boxes and not their logical (ascent/descent) dimensions.
+    struct AbsoluteTopAndBottom {
+        InlineLayoutUnit top { 0 };
+        InlineLayoutUnit bottom { 0 };
+    };
+    HashMap<const InlineLevelBox*, AbsoluteTopAndBottom> inlineLevelBoxAbsoluteTopAndBottomMap;
+
+    auto minimumLogicalTop = std::optional<InlineLayoutUnit> { };
+    auto maximumLogicalBottom = std::optional<InlineLayoutUnit> { };
+    if (formattingGeometry.inlineLevelBoxAffectsLineBox(rootInlineBox, lineBox)) {
+        minimumLogicalTop = InlineLayoutUnit { };
+        maximumLogicalBottom = rootInlineBox.layoutBounds().height();
+        inlineLevelBoxAbsoluteTopAndBottomMap.add(&rootInlineBox, AbsoluteTopAndBottom { *minimumLogicalTop, *maximumLogicalBottom });
+    } else
+        inlineLevelBoxAbsoluteTopAndBottomMap.add(&rootInlineBox, AbsoluteTopAndBottom { });
+
+    Vector<InlineLevelBox*> lineBoxRelativeInlineLevelBoxes;
+    for (auto& inlineLevelBox : lineBox.nonRootInlineLevelBoxes()) {
+        auto& layoutBox = inlineLevelBox.layoutBox();
+        if (inlineLevelBox.hasLineBoxRelativeAlignment()) {
+            lineBoxRelativeInlineLevelBoxes.append(&inlineLevelBox);
+            continue;
+        }
+        auto& parentInlineBox = lineBox.inlineLevelBoxForLayoutBox(layoutBox.parent());
+        // Logical top is relative to the parent inline box's layout bounds.
+        // Note that this logical top is not the final logical top of the inline level box.
+        // This is the logical top in the context of the layout bounds geometry which may be very different from the inline box's normal geometry.
+        auto logicalTop = InlineLayoutUnit { };
+        switch (inlineLevelBox.verticalAlign()) {
+        case VerticalAlign::Baseline: {
+            auto logicalTopOffsetFromParentBaseline = inlineLevelBox.layoutBounds().ascent;
+            logicalTop = parentInlineBox.layoutBounds().ascent - logicalTopOffsetFromParentBaseline;
+            break;
+        }
+        case VerticalAlign::Middle: {
+            auto logicalTopOffsetFromParentBaseline = inlineLevelBox.layoutBounds().height() / 2 + parentInlineBox.style().fontMetrics().xHeight() / 2;
+            logicalTop = parentInlineBox.layoutBounds().ascent - logicalTopOffsetFromParentBaseline;
+            break;
+        }
+        case VerticalAlign::BaselineMiddle: {
+            auto logicalTopOffsetFromParentBaseline = inlineLevelBox.layoutBounds().height() / 2;
+            logicalTop = parentInlineBox.layoutBounds().ascent - logicalTopOffsetFromParentBaseline;
+            break;
+        }
+        case VerticalAlign::Length: {
+            auto& style = inlineLevelBox.style();
+            auto logicalTopOffsetFromParentBaseline = floatValueForLength(style.verticalAlignLength(), style.computedLineHeight()) + inlineLevelBox.layoutBounds().ascent;
+            logicalTop = parentInlineBox.layoutBounds().ascent - logicalTopOffsetFromParentBaseline;
+            break;
+        }
+        case VerticalAlign::TextTop: {
+            // Note that text-top aligns with the inline box's font metrics top (ascent) and not the layout bounds top.
+            logicalTop = parentInlineBox.layoutBounds().ascent - parentInlineBox.baseline();
+            break;
+        }
+        case VerticalAlign::TextBottom: {
+            // Note that text-bottom aligns with the inline box's font metrics bottom (descent) and not the layout bounds bottom.
+            auto parentInlineBoxLayoutBounds = parentInlineBox.layoutBounds();
+            auto parentInlineBoxLogicalBottom = parentInlineBoxLayoutBounds.height() - parentInlineBoxLayoutBounds.descent + parentInlineBox.descent().value_or(InlineLayoutUnit());
+            logicalTop = parentInlineBoxLogicalBottom - inlineLevelBox.layoutBounds().height();
+            break;
+        }
+        case VerticalAlign::Sub: {
+            auto logicalTopOffsetFromParentBaseline = inlineLevelBox.layoutBounds().ascent - (parentInlineBox.style().fontCascade().size() / 5 + 1);
+            logicalTop = parentInlineBox.layoutBounds().ascent - logicalTopOffsetFromParentBaseline;
+            break;
+        }
+        case VerticalAlign::Super: {
+            auto logicalTopOffsetFromParentBaseline = inlineLevelBox.layoutBounds().ascent + parentInlineBox.style().fontCascade().size() / 3 + 1;
+            logicalTop = parentInlineBox.layoutBounds().ascent - logicalTopOffsetFromParentBaseline;
+            break;
+        }
+        default:
+            ASSERT_NOT_IMPLEMENTED_YET();
+            break;
+        }
+        auto parentInlineBoxAbsoluteTopAndBottom = inlineLevelBoxAbsoluteTopAndBottomMap.get(&parentInlineBox);
+        auto absoluteLogicalTop = parentInlineBoxAbsoluteTopAndBottom.top + logicalTop;
+        auto absoluteLogicalBottom = absoluteLogicalTop + inlineLevelBox.layoutBounds().height();
+        inlineLevelBoxAbsoluteTopAndBottomMap.add(&inlineLevelBox, AbsoluteTopAndBottom { absoluteLogicalTop, absoluteLogicalBottom });
+        // Stretch the min/max absolute values if applicable.
+        if (formattingGeometry.inlineLevelBoxAffectsLineBox(inlineLevelBox, lineBox)) {
+            minimumLogicalTop = std::min(minimumLogicalTop.value_or(absoluteLogicalTop), absoluteLogicalTop);
+            maximumLogicalBottom = std::max(maximumLogicalBottom.value_or(absoluteLogicalBottom), absoluteLogicalBottom);
+        }
+    }
+    // The line box height computation is as follows:
+    // 1. Stretch the line box with the non-line-box relative aligned inline box absolute top and bottom values.
+    // 2. Check if the line box relative aligned inline boxes (top, bottom etc) have enough room and stretch the line box further if needed.
+    auto lineBoxLogicalHeight = maximumLogicalBottom.value_or(InlineLayoutUnit()) - minimumLogicalTop.value_or(InlineLayoutUnit());
+    for (auto* lineBoxRelativeInlineLevelBox : lineBoxRelativeInlineLevelBoxes) {
+        if (!formattingGeometry.inlineLevelBoxAffectsLineBox(*lineBoxRelativeInlineLevelBox, lineBox))
+            continue;
+        lineBoxLogicalHeight = std::max(lineBoxLogicalHeight, lineBoxRelativeInlineLevelBox->layoutBounds().height());
+    }
+    return lineBoxLogicalHeight;
+}
+
+void LineBoxVerticalAligner::computeRootInlineBoxVerticalPosition(LineBox& lineBox) const
+{
+    auto& rootInlineBox = lineBox.rootInlineBox();
+    auto& formattingGeometry = this->formattingGeometry();
+
+    HashMap<const InlineLevelBox*, InlineLayoutUnit> inlineLevelBoxAbsoluteBaselineOffsetMap;
+    inlineLevelBoxAbsoluteBaselineOffsetMap.add(&rootInlineBox, InlineLayoutUnit { });
+
+    auto maximumTopOffsetFromRootInlineBoxBaseline = std::optional<InlineLayoutUnit> { };
+    if (formattingGeometry.inlineLevelBoxAffectsLineBox(rootInlineBox, lineBox))
+        maximumTopOffsetFromRootInlineBoxBaseline = rootInlineBox.layoutBounds().ascent;
+
+    for (auto& inlineLevelBox : lineBox.nonRootInlineLevelBoxes()) {
+        auto absoluteBaselineOffset = InlineLayoutUnit { };
+        if (!inlineLevelBox.hasLineBoxRelativeAlignment()) {
+            auto& layoutBox = inlineLevelBox.layoutBox();
+            auto& parentInlineBox = lineBox.inlineLevelBoxForLayoutBox(layoutBox.parent());
+            auto baselineOffsetFromParentBaseline = InlineLayoutUnit { };
+
+            switch (inlineLevelBox.verticalAlign()) {
+            case VerticalAlign::Baseline:
+                baselineOffsetFromParentBaseline = { };
+                break;
+            case VerticalAlign::Middle: {
+                auto logicalTopOffsetFromParentBaseline = (inlineLevelBox.layoutBounds().height() / 2 + parentInlineBox.style().fontMetrics().xHeight() / 2);
+                baselineOffsetFromParentBaseline = logicalTopOffsetFromParentBaseline - inlineLevelBox.layoutBounds().ascent;
+                break;
+            }
+            case VerticalAlign::BaselineMiddle: {
+                auto logicalTopOffsetFromParentBaseline = inlineLevelBox.layoutBounds().height() / 2;
+                baselineOffsetFromParentBaseline = logicalTopOffsetFromParentBaseline - inlineLevelBox.layoutBounds().ascent;
+                break;
+            }
+            case VerticalAlign::Length: {
+                auto& style = inlineLevelBox.style();
+                auto verticalAlignOffset = floatValueForLength(style.verticalAlignLength(), style.computedLineHeight());
+                auto logicalTopOffsetFromParentBaseline = verticalAlignOffset + inlineLevelBox.baseline();
+                baselineOffsetFromParentBaseline = logicalTopOffsetFromParentBaseline - inlineLevelBox.baseline();
+                break;
+            }
+            case VerticalAlign::TextTop:
+                baselineOffsetFromParentBaseline = parentInlineBox.baseline() - inlineLevelBox.layoutBounds().ascent;
+                break;
+            case VerticalAlign::TextBottom:
+                baselineOffsetFromParentBaseline = inlineLevelBox.layoutBounds().descent - *parentInlineBox.descent();
+                break;
+            case VerticalAlign::Sub:
+                baselineOffsetFromParentBaseline = -(parentInlineBox.style().fontCascade().size() / 5 + 1);
+                break;
+            case VerticalAlign::Super:
+                baselineOffsetFromParentBaseline = parentInlineBox.style().fontCascade().size() / 3 + 1;
+                break;
+            default:
+                ASSERT_NOT_IMPLEMENTED_YET();
+                break;
+            }
+            absoluteBaselineOffset = inlineLevelBoxAbsoluteBaselineOffsetMap.get(&parentInlineBox) + baselineOffsetFromParentBaseline;
+        } else {
+            switch (inlineLevelBox.verticalAlign()) {
+            case VerticalAlign::Top: {
+                absoluteBaselineOffset = rootInlineBox.layoutBounds().ascent - inlineLevelBox.layoutBounds().ascent;
+                break;
+            }
+            case VerticalAlign::Bottom: {
+                absoluteBaselineOffset = inlineLevelBox.layoutBounds().descent - rootInlineBox.layoutBounds().descent;
+                break;
+            }
+            default:
+                ASSERT_NOT_IMPLEMENTED_YET();
+                break;
+            }
+        }
+        inlineLevelBoxAbsoluteBaselineOffsetMap.add(&inlineLevelBox, absoluteBaselineOffset);
+        auto affectsRootInlineBoxVerticalPosition = formattingGeometry.inlineLevelBoxAffectsLineBox(inlineLevelBox, lineBox);
+        if (affectsRootInlineBoxVerticalPosition) {
+            auto topOffsetFromRootInlineBoxBaseline = absoluteBaselineOffset + inlineLevelBox.layoutBounds().ascent;
+            if (maximumTopOffsetFromRootInlineBoxBaseline)
+                maximumTopOffsetFromRootInlineBoxBaseline = std::max(*maximumTopOffsetFromRootInlineBoxBaseline, topOffsetFromRootInlineBoxBaseline);
+            else {
+                // We are is quirk mode and the root inline box has no content. The root inline box's baseline is anchored at 0.
+                // However negative ascent (e.g negative top margin) can "push" the root inline box upwards and have a negative value.
+                maximumTopOffsetFromRootInlineBoxBaseline = inlineLevelBox.layoutBounds().ascent >= 0
+                    ? std::max(0.0f, topOffsetFromRootInlineBoxBaseline)
+                    : topOffsetFromRootInlineBoxBaseline;
+            }
+        }
+    }
+    auto rootInlineBoxLogicalTop = maximumTopOffsetFromRootInlineBoxBaseline.value_or(0.f) - rootInlineBox.baseline();
+    rootInlineBox.setLogicalTop(rootInlineBoxLogicalTop);
+}
+
+void LineBoxVerticalAligner::alignInlineLevelBoxes(LineBox& lineBox, InlineLayoutUnit lineBoxLogicalHeight) const
+{
+    for (auto& inlineLevelBox : lineBox.nonRootInlineLevelBoxes()) {
+        auto& layoutBox = inlineLevelBox.layoutBox();
+        auto& parentInlineBox = lineBox.inlineLevelBoxForLayoutBox(layoutBox.parent());
+        auto logicalTop = InlineLayoutUnit { };
+        switch (inlineLevelBox.verticalAlign()) {
+        case VerticalAlign::Baseline:
+            logicalTop = parentInlineBox.baseline() - inlineLevelBox.baseline();
+            break;
+        case VerticalAlign::Middle: {
+            auto logicalTopOffsetFromParentBaseline = (inlineLevelBox.logicalHeight() / 2 + parentInlineBox.style().fontMetrics().xHeight() / 2);
+            logicalTop = parentInlineBox.baseline() - logicalTopOffsetFromParentBaseline;
+            break;
+        }
+        case VerticalAlign::BaselineMiddle: {
+            auto logicalTopOffsetFromParentBaseline = inlineLevelBox.logicalHeight() / 2;
+            logicalTop = parentInlineBox.baseline() - logicalTopOffsetFromParentBaseline;
+            break;
+        }
+        case VerticalAlign::Length: {
+            auto& style = inlineLevelBox.style();
+            auto verticalAlignOffset = floatValueForLength(style.verticalAlignLength(), style.computedLineHeight());
+            auto logicalTopOffsetFromParentBaseline = verticalAlignOffset + inlineLevelBox.baseline();
+            logicalTop = parentInlineBox.baseline() - logicalTopOffsetFromParentBaseline;
+            break;
+        }
+        case VerticalAlign::Sub: {
+            auto logicalTopOffsetFromParentBaseline = inlineLevelBox.baseline() - (parentInlineBox.style().fontCascade().size() / 5 + 1);
+            logicalTop = parentInlineBox.baseline() - logicalTopOffsetFromParentBaseline;
+            break;
+        }
+        case VerticalAlign::Super: {
+            auto logicalTopOffsetFromParentBaseline = inlineLevelBox.baseline() + parentInlineBox.style().fontCascade().size() / 3 + 1;
+            logicalTop = parentInlineBox.baseline() - logicalTopOffsetFromParentBaseline;
+            break;
+        }
+        // Note that (text)top/bottom align their layout bounds.
+        case VerticalAlign::TextTop:
+            logicalTop = inlineLevelBox.layoutBounds().ascent - inlineLevelBox.baseline();
+            break;
+        case VerticalAlign::TextBottom:
+            logicalTop = parentInlineBox.logicalHeight() - inlineLevelBox.layoutBounds().descent - inlineLevelBox.baseline();
+            break;
+        case VerticalAlign::Top:
+            // Note that this logical top is not relative to the parent inline box.
+            logicalTop = inlineLevelBox.layoutBounds().ascent - inlineLevelBox.baseline();
+            break;
+        case VerticalAlign::Bottom:
+            // Note that this logical top is not relative to the parent inline box.
+            logicalTop = lineBoxLogicalHeight - inlineLevelBox.layoutBounds().descent - inlineLevelBox.baseline();
+            break;
+        default:
+            ASSERT_NOT_IMPLEMENTED_YET();
+            break;
+        }
+        inlineLevelBox.setLogicalTop(logicalTop);
+    }
+}
+
+}
+}
+
+#endif

Added: trunk/Source/WebCore/layout/formattingContexts/inline/InlineLineBoxVerticalAligner.h (0 => 281479)


--- trunk/Source/WebCore/layout/formattingContexts/inline/InlineLineBoxVerticalAligner.h	                        (rev 0)
+++ trunk/Source/WebCore/layout/formattingContexts/inline/InlineLineBoxVerticalAligner.h	2021-08-24 00:20:27 UTC (rev 281479)
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2021 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#if ENABLE(LAYOUT_FORMATTING_CONTEXT)
+
+#include "InlineFormattingGeometry.h"
+
+namespace WebCore {
+namespace Layout {
+
+class LineBoxVerticalAligner {
+public:
+    LineBoxVerticalAligner(const InlineFormattingContext&);
+    InlineLayoutUnit computeLogicalHeightAndAlign(LineBox&, bool useSimplifiedAlignment) const;
+
+    static bool canUseSimplifiedAlignmentForInlineLevelBox(const InlineLevelBox& rootInlineBox, const InlineLevelBox&, std::optional<const BoxGeometry> nlineLevelBoxGeometry);
+
+private:
+    InlineLayoutUnit simplifiedVerticalAlignment(LineBox&) const;
+
+    InlineLayoutUnit computeLineBoxLogicalHeight(LineBox&) const;
+    void computeRootInlineBoxVerticalPosition(LineBox&) const;
+    void alignInlineLevelBoxes(LineBox&, InlineLayoutUnit lineBoxLogicalHeight) const;
+
+    const InlineFormattingGeometry& formattingGeometry() const { return m_inlineFormattingGeometry; }
+
+private:
+    const InlineFormattingGeometry m_inlineFormattingGeometry;
+};
+
+}
+}
+
+#endif
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to