Title: [290105] trunk
Revision
290105
Author
[email protected]
Date
2022-02-17 18:51:32 -0800 (Thu, 17 Feb 2022)

Log Message

[LFC][IFC] 'vertical-align: bottom' makes inline level boxes stick out of their parent block box
https://bugs.webkit.org/show_bug.cgi?id=236677

Reviewed by Antti Koivisto.

Source/WebCore:

'vertical-align: bottom' pushes the root inline box's baseline down as it stretches the line box downwards.
In most cases this makes the root inline box sit at the bottom of the line box e.g.
  <div>
    some text
    <img style="vertical-align: bottom; height: 100px; width: 10px;">
  </div>
  forms a 100px tall block box where the "some text" sits at the bottom of the line and the tall image fills the rest of the line box all the way to the top.

  Now if we add another inline level box with e.g. middle alignment.
  <div>
    some text
    <img style="vertical-align: middle; height: 80px; width: 10px;">
    <img style="vertical-align: bottom; height: 100px; width: 10px;">
  </div>
  We still form a 100px tall block box where the "some text" content sits at the bottom and the middle aligned box stick out of the block box by about 40px (middle alignment -> 80px/2) as it is aligned based
  on the root inline box's position (which is at the very bottom of the block container).
  Instead the root inline box's baseline should be pull up to make room for the middle aligned image box.

This patch fixes this incorrect behavior by removing the line box relative alignment types from the regular alignment logic and offsetting the root inline box's baseline position at a later step.
What we do here is we simply compute the root inline box's baseline position as if there were no line box relative aligned boxes present and offset the final result if such box happens to stretch the line box.
(going back to the example: without the tall, bottom aligned image box, we compute the root inline box's baseline position to be around 40px to
ensure that the 80px image box fits and later we push it down to 60px (as the 100px image box stretches the line box by 20px).)

Test: fast/inline/vertical-align-bottom.html

* layout/formattingContexts/inline/InlineLineBoxVerticalAligner.cpp:
(WebCore::Layout::LineBoxVerticalAligner::computeLogicalHeightAndAlign const):
(WebCore::Layout::LineBoxVerticalAligner::computeLineBoxLogicalHeight const):
(WebCore::Layout::LineBoxVerticalAligner::computeRootInlineBoxVerticalPosition const):
* layout/formattingContexts/inline/InlineLineBoxVerticalAligner.h:
(WebCore::Layout::LineBoxVerticalAligner::LineBoxHeight::value const):

LayoutTests:

* fast/inline/vertical-align-bottom-expected.html: Added.
* fast/inline/vertical-align-bottom.html: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (290104 => 290105)


--- trunk/LayoutTests/ChangeLog	2022-02-18 02:46:22 UTC (rev 290104)
+++ trunk/LayoutTests/ChangeLog	2022-02-18 02:51:32 UTC (rev 290105)
@@ -1,3 +1,13 @@
+2022-02-17  Alan Bujtas  <[email protected]>
+
+        [LFC][IFC] 'vertical-align: bottom' makes inline level boxes stick out of their parent block box
+        https://bugs.webkit.org/show_bug.cgi?id=236677
+
+        Reviewed by Antti Koivisto.
+
+        * fast/inline/vertical-align-bottom-expected.html: Added.
+        * fast/inline/vertical-align-bottom.html: Added.
+
 2022-02-17  Jon Lee  <[email protected]>
 
         Unreviewed gardening for iOS GPU Process bots.

Added: trunk/LayoutTests/fast/inline/vertical-align-bottom-expected.html (0 => 290105)


--- trunk/LayoutTests/fast/inline/vertical-align-bottom-expected.html	                        (rev 0)
+++ trunk/LayoutTests/fast/inline/vertical-align-bottom-expected.html	2022-02-18 02:51:32 UTC (rev 290105)
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<style>
+div {
+   background-color: green;
+   width: 120px;
+   margin-bottom: 20px;
+}
+
+</style>
+<div style="height: 50px;"></div>
+<div style="height: 100px;"></div>
+<div style="height: 200px;"></div>
+<div style="height: 50px;"></div>
+<div style="height: 50px;"></div>
+<div style="height: 50px;"></div>
\ No newline at end of file

Added: trunk/LayoutTests/fast/inline/vertical-align-bottom.html (0 => 290105)


--- trunk/LayoutTests/fast/inline/vertical-align-bottom.html	                        (rev 0)
+++ trunk/LayoutTests/fast/inline/vertical-align-bottom.html	2022-02-18 02:51:32 UTC (rev 290105)
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<style>
+.block {
+   background-color: green;
+   width: 120px;
+   margin-bottom: 20px;
+   color: transparent;
+   font-family: Ahem;
+   font-size: 20px;
+}
+
+.aligned-box {
+  display: inline-block;
+  background-color: green;
+  width: 50px;
+  height: 50px;
+}
+
+.non-lineBox-relative {
+   vertical-align: baseline;
+   width: 50px;
+   height: 20px;
+}
+
+.bottom {
+  vertical-align: bottom;
+
+  width: 50px;
+  height: 50px;
+}
+
+.top {
+  vertical-align: top;
+}
+</style>
+<!-- The non-line-box relative inline level boxes should not stick out of their parent block box -->
+<div class=block>X<div class="aligned-box non-lineBox-relative">X</div><div class="aligned-box bottom"></div></div>
+<div class=block>X<div class="aligned-box non-lineBox-relative" style="height: 100px;">X</div><div class="aligned-box bottom"></div></div>
+<div class=block>X<div class="aligned-box non-lineBox-relative" style="padding-bottom: 100px; height: 100px;">X</div><div class="aligned-box bottom"></div></div>
+<div class=block>X<div class="aligned-box non-lineBox-relative">X</div><div class="aligned-box bottom"></div></div>
+<div class=block>X<div class="aligned-box non-lineBox-relative" style="height: 30px; vertical-align: middle">X</div><div class="aligned-box bottom"></div></div>
+<div class=block>X<div class="aligned-box non-lineBox-relative">X</div><div class="aligned-box top"></div></div>

Modified: trunk/Source/WebCore/ChangeLog (290104 => 290105)


--- trunk/Source/WebCore/ChangeLog	2022-02-18 02:46:22 UTC (rev 290104)
+++ trunk/Source/WebCore/ChangeLog	2022-02-18 02:51:32 UTC (rev 290105)
@@ -1,3 +1,42 @@
+2022-02-17  Alan Bujtas  <[email protected]>
+
+        [LFC][IFC] 'vertical-align: bottom' makes inline level boxes stick out of their parent block box
+        https://bugs.webkit.org/show_bug.cgi?id=236677
+
+        Reviewed by Antti Koivisto.
+
+        'vertical-align: bottom' pushes the root inline box's baseline down as it stretches the line box downwards.
+        In most cases this makes the root inline box sit at the bottom of the line box e.g.
+          <div>
+            some text
+            <img style="vertical-align: bottom; height: 100px; width: 10px;">
+          </div>
+          forms a 100px tall block box where the "some text" sits at the bottom of the line and the tall image fills the rest of the line box all the way to the top.
+
+          Now if we add another inline level box with e.g. middle alignment.
+          <div>
+            some text
+            <img style="vertical-align: middle; height: 80px; width: 10px;">
+            <img style="vertical-align: bottom; height: 100px; width: 10px;">
+          </div>
+          We still form a 100px tall block box where the "some text" content sits at the bottom and the middle aligned box stick out of the block box by about 40px (middle alignment -> 80px/2) as it is aligned based
+          on the root inline box's position (which is at the very bottom of the block container).
+          Instead the root inline box's baseline should be pull up to make room for the middle aligned image box.
+
+        This patch fixes this incorrect behavior by removing the line box relative alignment types from the regular alignment logic and offsetting the root inline box's baseline position at a later step.
+        What we do here is we simply compute the root inline box's baseline position as if there were no line box relative aligned boxes present and offset the final result if such box happens to stretch the line box.
+        (going back to the example: without the tall, bottom aligned image box, we compute the root inline box's baseline position to be around 40px to
+        ensure that the 80px image box fits and later we push it down to 60px (as the 100px image box stretches the line box by 20px).)
+
+        Test: fast/inline/vertical-align-bottom.html
+
+        * layout/formattingContexts/inline/InlineLineBoxVerticalAligner.cpp:
+        (WebCore::Layout::LineBoxVerticalAligner::computeLogicalHeightAndAlign const):
+        (WebCore::Layout::LineBoxVerticalAligner::computeLineBoxLogicalHeight const):
+        (WebCore::Layout::LineBoxVerticalAligner::computeRootInlineBoxVerticalPosition const):
+        * layout/formattingContexts/inline/InlineLineBoxVerticalAligner.h:
+        (WebCore::Layout::LineBoxVerticalAligner::LineBoxHeight::value const):
+
 2022-02-17  Kate Cheney  <[email protected]>
 
         Refactor share menu item presentation

Modified: trunk/Source/WebCore/layout/formattingContexts/inline/InlineLineBoxVerticalAligner.cpp (290104 => 290105)


--- trunk/Source/WebCore/layout/formattingContexts/inline/InlineLineBoxVerticalAligner.cpp	2022-02-18 02:46:22 UTC (rev 290104)
+++ trunk/Source/WebCore/layout/formattingContexts/inline/InlineLineBoxVerticalAligner.cpp	2022-02-18 02:51:32 UTC (rev 290105)
@@ -83,9 +83,9 @@
     // 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;
+    computeRootInlineBoxVerticalPosition(lineBox, lineBoxLogicalHeight);
+    alignInlineLevelBoxes(lineBox, lineBoxLogicalHeight.value());
+    return lineBoxLogicalHeight.value();
 }
 
 InlineLayoutUnit LineBoxVerticalAligner::simplifiedVerticalAlignment(LineBox& lineBox) const
@@ -117,7 +117,7 @@
     return lineBoxLogicalBottom - lineBoxLogicalTop;
 }
 
-InlineLayoutUnit LineBoxVerticalAligner::computeLineBoxLogicalHeight(LineBox& lineBox) const
+LineBoxVerticalAligner::LineBoxHeight LineBoxVerticalAligner::computeLineBoxLogicalHeight(LineBox& lineBox) const
 {
     // This function (partially) implements:
     // 2.2. Layout Within Line Boxes
@@ -217,20 +217,31 @@
     // 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 = valueOrDefault(maximumLogicalBottom) - valueOrDefault(minimumLogicalTop);
+    auto nonBottomAlignedBoxesMaximumHeight = valueOrDefault(maximumLogicalBottom) - valueOrDefault(minimumLogicalTop);
+    auto bottomAlignedBoxesMaximumHeight = std::optional<InlineLayoutUnit> { };
     for (auto* lineBoxRelativeInlineLevelBox : lineBoxRelativeInlineLevelBoxes) {
         if (!formattingGeometry.inlineLevelBoxAffectsLineBox(*lineBoxRelativeInlineLevelBox, lineBox))
             continue;
-        lineBoxLogicalHeight = std::max(lineBoxLogicalHeight, lineBoxRelativeInlineLevelBox->layoutBounds().height());
+        // This line box relative aligned inline level box stretches the line box.
+        auto inlineLevelBoxHeight = lineBoxRelativeInlineLevelBox->layoutBounds().height();
+        if (lineBoxRelativeInlineLevelBox->verticalAlign().type == VerticalAlign::Top) {
+            nonBottomAlignedBoxesMaximumHeight = std::max(inlineLevelBoxHeight, nonBottomAlignedBoxesMaximumHeight);
+            continue;
+        }
+        if (lineBoxRelativeInlineLevelBox->verticalAlign().type == VerticalAlign::Bottom) {
+            bottomAlignedBoxesMaximumHeight = std::max(inlineLevelBoxHeight, bottomAlignedBoxesMaximumHeight.value_or(0.f));
+            continue;
+        }
+        ASSERT_NOT_REACHED();
     }
-    return lineBoxLogicalHeight;
+    return { nonBottomAlignedBoxesMaximumHeight, bottomAlignedBoxesMaximumHeight };
 }
 
-void LineBoxVerticalAligner::computeRootInlineBoxVerticalPosition(LineBox& lineBox) const
+void LineBoxVerticalAligner::computeRootInlineBoxVerticalPosition(LineBox& lineBox, const LineBoxHeight& lineBoxHeight) const
 {
     auto& rootInlineBox = lineBox.rootInlineBox();
     auto& formattingGeometry = this->formattingGeometry();
-    Vector<unsigned> inlineLevelBoxIndexesWithLineBoxRelativeAlignment;
+    auto hasTopAlignedInlineLevelBox = false;
 
     HashMap<const InlineLevelBox*, InlineLayoutUnit> inlineLevelBoxAbsoluteBaselineOffsetMap;
     inlineLevelBoxAbsoluteBaselineOffsetMap.add(&rootInlineBox, InlineLayoutUnit { });
@@ -250,13 +261,13 @@
         auto verticalAlign = inlineLevelBox.verticalAlign();
 
         if (inlineLevelBox.hasLineBoxRelativeAlignment()) {
-            if (verticalAlign.type == VerticalAlign::Top)
+            if (verticalAlign.type == VerticalAlign::Top) {
+                hasTopAlignedInlineLevelBox = hasTopAlignedInlineLevelBox || affectsRootInlineBoxVerticalPosition(inlineLevelBox);
                 inlineLevelBoxAbsoluteBaselineOffsetMap.add(&inlineLevelBox, rootInlineBox.layoutBounds().ascent - inlineLevelBox.layoutBounds().ascent);
-            else if (verticalAlign.type == VerticalAlign::Bottom)
+            } else if (verticalAlign.type == VerticalAlign::Bottom)
                 inlineLevelBoxAbsoluteBaselineOffsetMap.add(&inlineLevelBox, inlineLevelBox.layoutBounds().descent - rootInlineBox.layoutBounds().descent);
             else
                 ASSERT_NOT_REACHED();
-            inlineLevelBoxIndexesWithLineBoxRelativeAlignment.append(index);
             continue;
         }
         auto& layoutBox = inlineLevelBox.layoutBox();
@@ -315,24 +326,20 @@
         }
     }
 
-    for (auto index : inlineLevelBoxIndexesWithLineBoxRelativeAlignment) {
-        auto& inlineLevelBox = nonRootInlineLevelBoxes[index];
-        if (!affectsRootInlineBoxVerticalPosition(inlineLevelBox))
-            continue;
-
-        if (inlineLevelBox.verticalAlign().type == VerticalAlign::Bottom) {
-            auto baselineOffset = inlineLevelBox.layoutBounds().descent - rootInlineBox.layoutBounds().descent;
-            auto topOffsetFromRootInlineBoxBaseline = baselineOffset + inlineLevelBox.layoutBounds().ascent;
-            maximumTopOffsetFromRootInlineBoxBaseline = std::max(*maximumTopOffsetFromRootInlineBoxBaseline, topOffsetFromRootInlineBoxBaseline);
-        } else if (inlineLevelBox.verticalAlign().type == VerticalAlign::Top) {
-            // vertical-align: top is a line box relative alignment. It stretches the line box downwards meaning that it does not affect
-            // the root inline box's baseline position, but in quirks mode we have to ensure that the root inline box does not end up at 0px.
-            if (!maximumTopOffsetFromRootInlineBoxBaseline)
-                maximumTopOffsetFromRootInlineBoxBaseline = rootInlineBox.ascent();
-        } else
-            ASSERT_NOT_REACHED();
+    if (!maximumTopOffsetFromRootInlineBoxBaseline && hasTopAlignedInlineLevelBox) {
+        // vertical-align: top is a line box relative alignment. It stretches the line box downwards meaning that it does not affect
+        // the root inline box's baseline position, but in quirks mode we have to ensure that the root inline box does not end up at 0px.
+        maximumTopOffsetFromRootInlineBoxBaseline = rootInlineBox.ascent();
     }
-    auto rootInlineBoxLogicalTop = maximumTopOffsetFromRootInlineBoxBaseline.value_or(0.f) - rootInlineBox.ascent();
+    // vertical-align: bottom stretches the top of the line box pushing the root inline box downwards.
+    auto bottomAlignedBoxStretch = InlineLayoutUnit { };
+    if (lineBoxHeight.bottomAlignedBoxesMaximumHeight) {
+        // Negative vertical margin can make aligned boxes have negative height. 
+        bottomAlignedBoxStretch = std::max(0.f, lineBoxHeight.nonBottomAlignedBoxesMaximumHeight < 0
+            ? *lineBoxHeight.bottomAlignedBoxesMaximumHeight
+            : std::max(0.f, *lineBoxHeight.bottomAlignedBoxesMaximumHeight - lineBoxHeight.nonBottomAlignedBoxesMaximumHeight));
+    }
+    auto rootInlineBoxLogicalTop = bottomAlignedBoxStretch + maximumTopOffsetFromRootInlineBoxBaseline.value_or(0.f) - rootInlineBox.ascent();
     rootInlineBox.setLogicalTop(rootInlineBoxLogicalTop);
 }
 

Modified: trunk/Source/WebCore/layout/formattingContexts/inline/InlineLineBoxVerticalAligner.h (290104 => 290105)


--- trunk/Source/WebCore/layout/formattingContexts/inline/InlineLineBoxVerticalAligner.h	2022-02-18 02:46:22 UTC (rev 290104)
+++ trunk/Source/WebCore/layout/formattingContexts/inline/InlineLineBoxVerticalAligner.h	2022-02-18 02:51:32 UTC (rev 290105)
@@ -41,8 +41,14 @@
 private:
     InlineLayoutUnit simplifiedVerticalAlignment(LineBox&) const;
 
-    InlineLayoutUnit computeLineBoxLogicalHeight(LineBox&) const;
-    void computeRootInlineBoxVerticalPosition(LineBox&) const;
+    struct LineBoxHeight {
+        InlineLayoutUnit value() const { return std::max(nonBottomAlignedBoxesMaximumHeight, bottomAlignedBoxesMaximumHeight.value_or(0.f)); }
+
+        InlineLayoutUnit nonBottomAlignedBoxesMaximumHeight { 0 };
+        std::optional<InlineLayoutUnit> bottomAlignedBoxesMaximumHeight { };
+    };
+    LineBoxHeight computeLineBoxLogicalHeight(LineBox&) const;
+    void computeRootInlineBoxVerticalPosition(LineBox&, const LineBoxHeight&) const;
     void alignInlineLevelBoxes(LineBox&, InlineLayoutUnit lineBoxLogicalHeight) const;
 
     const InlineFormattingGeometry& formattingGeometry() const { return m_inlineFormattingGeometry; }
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to