Title: [268864] trunk/Source/WebCore
Revision
268864
Author
[email protected]
Date
2020-10-22 07:43:57 -0700 (Thu, 22 Oct 2020)

Log Message

[LFC][IFC] Handle line box sizing quirks at the line box level
https://bugs.webkit.org/show_bug.cgi?id=218060

Reviewed by Antti Koivisto.

In this patch the line box vertical sizing behavior (mostly quirk) is moved from the inline box to the line box level.

1. Inline boxes (root and other inline boxes (<span>)) are always sized to their initial height (see layout bounds) regardless of
whether they have content or not.
2. They are set to empty initially and we change them to non-empty as they gain (non-inline-level-box) content
(e.g. <div>root inline box gains content</div>).
3. Use this empty flag as input to the line box vertical sizing logic (only non-empty inline boxes contribute to the line box height).

Note that in standard mode the root inline box starts with an imaginary strut which makes it non-empty even when it has no content.
This is most visible with simple block containers like this:
    <div style="border: 1px solid green"><img></div>
In quirks mode the border hugs the image (root inline box does not contribute to the height of the line box), while in standard mode
(assume 1. the image is taller than the default font with line spacing, 2. font has 4px descent),
there's a 4px gap between the border and the image.

4. While computing the final line rect (InlineFormattingContext::Geometry::computedLineLogicalRect), just rely on the line box height
computation (no need to check whether the line has content).
5. Treat <br> as visually non-empty run as it may contribute to the line box visually.
6. Add <br> quirk behavior so that it only contributes to the line box height when the line is empty.

* layout/inlineformatting/InlineFormattingContext.h:
* layout/inlineformatting/InlineFormattingContextGeometry.cpp:
(WebCore::Layout::LineBoxBuilder::constructInlineLevelBoxes):
(WebCore::Layout::LineBoxBuilder::alignInlineLevelBoxesVerticallyAndComputeLineBoxHeight):
(WebCore::Layout::InlineFormattingContext::Geometry::computedLineLogicalRect const):
* layout/inlineformatting/InlineFormattingContextQuirks.cpp:
(WebCore::Layout::InlineFormattingContext::Quirks::shouldInlineLevelBoxStretchLineBox const):
* layout/inlineformatting/InlineLine.cpp:
(WebCore::Layout::Line::isRunVisuallyNonEmpty const):
* layout/inlineformatting/InlineLineBox.h:
(WebCore::Layout::LineBox::InlineLevelBox::verticalAlign const):

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (268863 => 268864)


--- trunk/Source/WebCore/ChangeLog	2020-10-22 13:40:56 UTC (rev 268863)
+++ trunk/Source/WebCore/ChangeLog	2020-10-22 14:43:57 UTC (rev 268864)
@@ -1,3 +1,42 @@
+2020-10-22  Zalan Bujtas  <[email protected]>
+
+        [LFC][IFC] Handle line box sizing quirks at the line box level
+        https://bugs.webkit.org/show_bug.cgi?id=218060
+
+        Reviewed by Antti Koivisto.
+
+        In this patch the line box vertical sizing behavior (mostly quirk) is moved from the inline box to the line box level.
+
+        1. Inline boxes (root and other inline boxes (<span>)) are always sized to their initial height (see layout bounds) regardless of
+        whether they have content or not.
+        2. They are set to empty initially and we change them to non-empty as they gain (non-inline-level-box) content
+        (e.g. <div>root inline box gains content</div>).
+        3. Use this empty flag as input to the line box vertical sizing logic (only non-empty inline boxes contribute to the line box height).
+
+        Note that in standard mode the root inline box starts with an imaginary strut which makes it non-empty even when it has no content.
+        This is most visible with simple block containers like this:
+            <div style="border: 1px solid green"><img></div>
+        In quirks mode the border hugs the image (root inline box does not contribute to the height of the line box), while in standard mode
+        (assume 1. the image is taller than the default font with line spacing, 2. font has 4px descent),
+        there's a 4px gap between the border and the image.
+
+        4. While computing the final line rect (InlineFormattingContext::Geometry::computedLineLogicalRect), just rely on the line box height
+        computation (no need to check whether the line has content).
+        5. Treat <br> as visually non-empty run as it may contribute to the line box visually.
+        6. Add <br> quirk behavior so that it only contributes to the line box height when the line is empty.
+
+        * layout/inlineformatting/InlineFormattingContext.h:
+        * layout/inlineformatting/InlineFormattingContextGeometry.cpp:
+        (WebCore::Layout::LineBoxBuilder::constructInlineLevelBoxes):
+        (WebCore::Layout::LineBoxBuilder::alignInlineLevelBoxesVerticallyAndComputeLineBoxHeight):
+        (WebCore::Layout::InlineFormattingContext::Geometry::computedLineLogicalRect const):
+        * layout/inlineformatting/InlineFormattingContextQuirks.cpp:
+        (WebCore::Layout::InlineFormattingContext::Quirks::shouldInlineLevelBoxStretchLineBox const):
+        * layout/inlineformatting/InlineLine.cpp:
+        (WebCore::Layout::Line::isRunVisuallyNonEmpty const):
+        * layout/inlineformatting/InlineLineBox.h:
+        (WebCore::Layout::LineBox::InlineLevelBox::verticalAlign const):
+
 2020-10-22  Antti Koivisto  <[email protected]>
 
         [LFC][Integration] Lines with outline should be marked having visual overflow

Modified: trunk/Source/WebCore/layout/inlineformatting/InlineFormattingContext.h (268863 => 268864)


--- trunk/Source/WebCore/layout/inlineformatting/InlineFormattingContext.h	2020-10-22 13:40:56 UTC (rev 268863)
+++ trunk/Source/WebCore/layout/inlineformatting/InlineFormattingContext.h	2020-10-22 14:43:57 UTC (rev 268864)
@@ -50,6 +50,7 @@
     public:
         InlineLayoutUnit initialLineHeight() const;
         bool hasSoftWrapOpportunityAtImage() const;
+        bool shouldInlineLevelBoxStretchLineBox(const LineBox&, const LineBox::InlineLevelBox&) const;
 
     private:
         friend class InlineFormattingContext;

Modified: trunk/Source/WebCore/layout/inlineformatting/InlineFormattingContextGeometry.cpp (268863 => 268864)


--- trunk/Source/WebCore/layout/inlineformatting/InlineFormattingContextGeometry.cpp	2020-10-22 13:40:56 UTC (rev 268863)
+++ trunk/Source/WebCore/layout/inlineformatting/InlineFormattingContextGeometry.cpp	2020-10-22 14:43:57 UTC (rev 268864)
@@ -213,8 +213,7 @@
         auto isInitiallyConsideredNonEmpty = !lineBox.isLineVisuallyEmpty() && lineHasImaginaryStrut;
         if (isInitiallyConsideredNonEmpty)
             rootInlineBox->setIsNonEmpty();
-        if (lineHasImaginaryStrut)
-            setVerticalGeometryForInlineBox(*rootInlineBox);
+        setVerticalGeometryForInlineBox(*rootInlineBox);
         lineBox.addRootInlineBox(WTFMove(rootInlineBox));
     };
     createRootInlineBox();
@@ -252,14 +251,6 @@
     };
     createWrappedInlineBoxes();
 
-    auto stretchRootInlineBoxIfNeededQuirk = [&] (const auto& layoutBox) {
-        auto& parentInlineBox = lineBox.inlineLevelBoxForLayoutBox(layoutBox.parent());
-        if (!isRootInlineBox(parentInlineBox) || !parentInlineBox.isEmpty())
-            return;
-        setVerticalGeometryForInlineBox(parentInlineBox);
-        parentInlineBox.setIsNonEmpty();
-    };
-
     for (auto& run : runs) {
         auto& layoutBox = run.layoutBox();
         auto logicalLeft = horizontalAligmentOffset + run.logicalLeft();
@@ -292,12 +283,12 @@
             inlineBox.setLogicalWidth(run.logicalRight() - inlineBox.logicalLeft());
         } else if (run.isText() || run.isSoftLineBreak()) {
             // FIXME: Adjust non-empty inline box height when glyphs from the non-primary font stretch the box.
-            stretchRootInlineBoxIfNeededQuirk(layoutBox);
+            lineBox.inlineLevelBoxForLayoutBox(layoutBox.parent()).setIsNonEmpty();
         } else if (run.isHardLineBreak()) {
             auto lineBreakBox = LineBox::InlineLevelBox::createLineBreakBox(layoutBox, logicalLeft);
             setVerticalGeometryForInlineBox(*lineBreakBox);
+            lineBreakBox->setIsNonEmpty();
             lineBox.addInlineLevelBox(WTFMove(lineBreakBox));
-            stretchRootInlineBoxIfNeededQuirk(layoutBox);
         } else if (run.isWordBreakOpportunity())
             lineBox.addInlineLevelBox(LineBox::InlineLevelBox::createGenericInlineLevelBox(layoutBox, logicalLeft));
         else
@@ -314,10 +305,11 @@
     struct AbsoluteTopAndBottom {
         InlineLayoutUnit top { 0 };
         InlineLayoutUnit bottom { 0 };
+        const LineBox::InlineLevelBox* inlineLevelBox { nullptr };
     };
     HashMap<LineBox::InlineLevelBox*, AbsoluteTopAndBottom> absoluteLogicalTopAndBottomMap;
     auto& rootInlineBox = lineBox.rootInlineBox();
-    absoluteLogicalTopAndBottomMap.add(&rootInlineBox, AbsoluteTopAndBottom { { }, rootInlineBox.layoutBounds().height() });
+    absoluteLogicalTopAndBottomMap.add(&rootInlineBox, AbsoluteTopAndBottom { { }, rootInlineBox.layoutBounds().height(), &rootInlineBox });
 
     auto alignInlineBoxRelativeInlineLevelBoxes = [&] {
         // FIXME: Add proper support for cases when the inline box with line box relative alignment has a child inline box
@@ -351,35 +343,43 @@
             inlineLevelBox->setLogicalTop(logicalTop);
             auto parentAbsoluteLogicalTop = absoluteLogicalTopAndBottomMap.get(&parentInlineBox).top;
             auto absoluteLogicalTop = parentAbsoluteLogicalTop + logicalTop;
-            absoluteLogicalTopAndBottomMap.add(inlineLevelBox.get(), AbsoluteTopAndBottom { absoluteLogicalTop, absoluteLogicalTop + inlineLevelBox->layoutBounds().height() });
+            absoluteLogicalTopAndBottomMap.add(inlineLevelBox.get(), AbsoluteTopAndBottom { absoluteLogicalTop, absoluteLogicalTop + inlineLevelBox->layoutBounds().height(), inlineLevelBox.get() });
         }
     };
     alignInlineBoxRelativeInlineLevelBoxes();
 
     auto lineBoxLogicalHeight = InlineLayoutUnit { };
-    auto minimumInlineBoxRelativeLogicalTop = InlineLayoutUnit { };
     auto inlineBoxRelativeLogicalHeight = InlineLayoutUnit { };
+    auto quirks = formattingContext().quirks();
+    auto maximumBaselineAlignedAscent = rootInlineBox.isEmpty() ? InlineLayoutUnit() : rootInlineBox.layoutBounds().ascent;
     auto computeLineBoxLogicalHeight = [&] {
         // FIXME: Add support for layout bounds based line box height.
-        auto minimumLogicalTop = InlineLayoutUnit { };
-        auto maximumlogicalBottom = InlineLayoutUnit { };
+        auto minimumLogicalTop = Optional<InlineLayoutUnit> { };
+        auto maximumLogicalBottom = Optional<InlineLayoutUnit> { };
         for (auto absoluteLogicalTopAndBottom : absoluteLogicalTopAndBottomMap.values()) {
-            minimumLogicalTop = std::min(minimumLogicalTop, absoluteLogicalTopAndBottom.top);
-            maximumlogicalBottom = std::max(maximumlogicalBottom, absoluteLogicalTopAndBottom.bottom);
+            auto& inlineLevelBox = *absoluteLogicalTopAndBottom.inlineLevelBox;
+            if (!quirks.shouldInlineLevelBoxStretchLineBox(lineBox, inlineLevelBox))
+                continue;
+            minimumLogicalTop = std::min(minimumLogicalTop.valueOr(absoluteLogicalTopAndBottom.top), absoluteLogicalTopAndBottom.top);
+            maximumLogicalBottom = std::max(maximumLogicalBottom.valueOr(absoluteLogicalTopAndBottom.bottom), absoluteLogicalTopAndBottom.bottom);
+            if (inlineLevelBox.verticalAlign() == VerticalAlign::Baseline)
+                maximumBaselineAlignedAscent = std::max(maximumBaselineAlignedAscent, inlineLevelBox.layoutBounds().ascent);
         }
-        minimumInlineBoxRelativeLogicalTop = minimumLogicalTop;
-        inlineBoxRelativeLogicalHeight = maximumlogicalBottom - minimumLogicalTop;
+        inlineBoxRelativeLogicalHeight = maximumLogicalBottom.valueOr(InlineLayoutUnit()) - minimumLogicalTop.valueOr(InlineLayoutUnit());
         lineBoxLogicalHeight = inlineBoxRelativeLogicalHeight;
         // Now stretch the line box with the line box relative inline level boxes.
-        for (auto* lineBoxRelativeInlineLevelBox : lineBoxRelativeInlineLevelBoxes)
+        for (auto* lineBoxRelativeInlineLevelBox : lineBoxRelativeInlineLevelBoxes) {
+            if (!quirks.shouldInlineLevelBoxStretchLineBox(lineBox, *lineBoxRelativeInlineLevelBox))
+                continue;
             lineBoxLogicalHeight = std::max(lineBoxLogicalHeight, lineBoxRelativeInlineLevelBox->layoutBounds().height());
+        }
     };
     computeLineBoxLogicalHeight();
 
     auto adjustRootInlineBoxVerticalPosition = [&] {
-        if (minimumInlineBoxRelativeLogicalTop >= 0)
-            return;
-        rootInlineBox.setLogicalTop(-minimumInlineBoxRelativeLogicalTop);
+        // FIXME: Add support for cases when the stretching inline boxes are not baseline aligned.
+        auto logicalTop = maximumBaselineAlignedAscent - rootInlineBox.layoutBounds().ascent;
+        rootInlineBox.setLogicalTop(logicalTop);
     };
     adjustRootInlineBoxVerticalPosition();
 
@@ -417,8 +417,7 @@
 
 InlineRect InlineFormattingContext::Geometry::computedLineLogicalRect(const LineBox& lineBox, const LineBuilder::LineContent& lineContent) const
 {
-    auto isConsideredEmpty = lineContent.runs.isEmpty() || lineBox.isLineVisuallyEmpty();
-    return { lineContent.logicalTopLeft, lineContent.lineLogicalWidth, isConsideredEmpty ? InlineLayoutUnit() : lineBox.logicalHeight()};
+    return { lineContent.logicalTopLeft, lineContent.lineLogicalWidth, lineBox.logicalHeight() };
 }
 
 InlineLayoutUnit InlineFormattingContext::Geometry::logicalTopForNextLine(const LineBuilder::LineContent& lineContent, InlineLayoutUnit previousLineLogicalBottom, const FloatingContext& floatingContext) const

Modified: trunk/Source/WebCore/layout/inlineformatting/InlineFormattingContextQuirks.cpp (268863 => 268864)


--- trunk/Source/WebCore/layout/inlineformatting/InlineFormattingContextQuirks.cpp	2020-10-22 13:40:56 UTC (rev 268863)
+++ trunk/Source/WebCore/layout/inlineformatting/InlineFormattingContextQuirks.cpp	2020-10-22 14:43:57 UTC (rev 268864)
@@ -40,6 +40,19 @@
     return root.style().fontMetrics().floatHeight();
 }
 
+bool InlineFormattingContext::Quirks::shouldInlineLevelBoxStretchLineBox(const LineBox& lineBox, const LineBox::InlineLevelBox& inlineLevelBox) const
+{
+    if (inlineLevelBox.isEmpty())
+        return false;
+    if (layoutState().inNoQuirksMode())
+        return true;
+    if (!inlineLevelBox.isLineBreakBox())
+        return true;
+    // <br> in non-standard mode stretches the line box only when the line is empty.
+    // e.g. <div><span><br></span></div> will stretch but <div>this will not stretch to 200px<span style="font-size: 200px;"><br></span></div>
+    return lineBox.isLineVisuallyEmpty();
+}
+
 bool InlineFormattingContext::Quirks::hasSoftWrapOpportunityAtImage() const
 {
     return !formattingContext().root().isTableCell();

Modified: trunk/Source/WebCore/layout/inlineformatting/InlineLine.cpp (268863 => 268864)


--- trunk/Source/WebCore/layout/inlineformatting/InlineLine.cpp	2020-10-22 13:40:56 UTC (rev 268863)
+++ trunk/Source/WebCore/layout/inlineformatting/InlineLine.cpp	2020-10-22 14:43:57 UTC (rev 268864)
@@ -353,7 +353,7 @@
         return true;
 
     if (run.isLineBreak())
-        return true;
+        return false;
 
     // Note that this does not check whether the inline container has content. It simply checks if the container itself is considered non-empty.
     if (run.isContainerStart() || run.isContainerEnd()) {

Modified: trunk/Source/WebCore/layout/inlineformatting/InlineLineBox.h (268863 => 268864)


--- trunk/Source/WebCore/layout/inlineformatting/InlineLineBox.h	2020-10-22 13:40:56 UTC (rev 268863)
+++ trunk/Source/WebCore/layout/inlineformatting/InlineLineBox.h	2020-10-22 14:43:57 UTC (rev 268864)
@@ -80,6 +80,7 @@
         void setIsNonEmpty() { m_isEmpty = false; }
 
         const FontMetrics& fontMetrics() const { return layoutBox().style().fontMetrics(); }
+        VerticalAlign verticalAlign() const { return layoutBox().style().verticalAlign(); }
         const Box& layoutBox() const { return *m_layoutBox; }
 
         bool isInlineBox() const { return m_type == Type::InlineBox || m_type == Type::RootInlineBox; }
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to