⚠ Archived content — this site is no longer maintained.   Current WebKit documentation is at docs.webkit.org.

Changeset 268864 in webkit


Ignore:
Timestamp:
Oct 22, 2020, 7:43:57 AM (6 years ago)
Author:
Alan Bujtas
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.

  1. 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>).

  1. 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.

  1. 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).

  1. Treat <br> as visually non-empty run as it may contribute to the line box visually.
  2. 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):

Location:
trunk/Source/WebCore
Files:
6 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/ChangeLog

    r268862 r268864  
     12020-10-22  Zalan Bujtas  <zalan@apple.com>
     2
     3        [LFC][IFC] Handle line box sizing quirks at the line box level
     4        https://bugs.webkit.org/show_bug.cgi?id=218060
     5
     6        Reviewed by Antti Koivisto.
     7
     8        In this patch the line box vertical sizing behavior (mostly quirk) is moved from the inline box to the line box level.
     9
     10        1. Inline boxes (root and other inline boxes (<span>)) are always sized to their initial height (see layout bounds) regardless of
     11        whether they have content or not.
     12        2. They are set to empty initially and we change them to non-empty as they gain (non-inline-level-box) content
     13        (e.g. <div>root inline box gains content</div>).
     14        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).
     15
     16        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.
     17        This is most visible with simple block containers like this:
     18            <div style="border: 1px solid green"><img></div>
     19        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
     20        (assume 1. the image is taller than the default font with line spacing, 2. font has 4px descent),
     21        there's a 4px gap between the border and the image.
     22
     23        4. While computing the final line rect (InlineFormattingContext::Geometry::computedLineLogicalRect), just rely on the line box height
     24        computation (no need to check whether the line has content).
     25        5. Treat <br> as visually non-empty run as it may contribute to the line box visually.
     26        6. Add <br> quirk behavior so that it only contributes to the line box height when the line is empty.
     27
     28        * layout/inlineformatting/InlineFormattingContext.h:
     29        * layout/inlineformatting/InlineFormattingContextGeometry.cpp:
     30        (WebCore::Layout::LineBoxBuilder::constructInlineLevelBoxes):
     31        (WebCore::Layout::LineBoxBuilder::alignInlineLevelBoxesVerticallyAndComputeLineBoxHeight):
     32        (WebCore::Layout::InlineFormattingContext::Geometry::computedLineLogicalRect const):
     33        * layout/inlineformatting/InlineFormattingContextQuirks.cpp:
     34        (WebCore::Layout::InlineFormattingContext::Quirks::shouldInlineLevelBoxStretchLineBox const):
     35        * layout/inlineformatting/InlineLine.cpp:
     36        (WebCore::Layout::Line::isRunVisuallyNonEmpty const):
     37        * layout/inlineformatting/InlineLineBox.h:
     38        (WebCore::Layout::LineBox::InlineLevelBox::verticalAlign const):
     39
    1402020-10-22  Antti Koivisto  <antti@apple.com>
    241
  • trunk/Source/WebCore/layout/inlineformatting/InlineFormattingContext.h

    r268660 r268864  
    5151        InlineLayoutUnit initialLineHeight() const;
    5252        bool hasSoftWrapOpportunityAtImage() const;
     53        bool shouldInlineLevelBoxStretchLineBox(const LineBox&, const LineBox::InlineLevelBox&) const;
    5354
    5455    private:
  • trunk/Source/WebCore/layout/inlineformatting/InlineFormattingContextGeometry.cpp

    r268823 r268864  
    214214        if (isInitiallyConsideredNonEmpty)
    215215            rootInlineBox->setIsNonEmpty();
    216         if (lineHasImaginaryStrut)
    217             setVerticalGeometryForInlineBox(*rootInlineBox);
     216        setVerticalGeometryForInlineBox(*rootInlineBox);
    218217        lineBox.addRootInlineBox(WTFMove(rootInlineBox));
    219218    };
     
    252251    };
    253252    createWrappedInlineBoxes();
    254 
    255     auto stretchRootInlineBoxIfNeededQuirk = [&] (const auto& layoutBox) {
    256         auto& parentInlineBox = lineBox.inlineLevelBoxForLayoutBox(layoutBox.parent());
    257         if (!isRootInlineBox(parentInlineBox) || !parentInlineBox.isEmpty())
    258             return;
    259         setVerticalGeometryForInlineBox(parentInlineBox);
    260         parentInlineBox.setIsNonEmpty();
    261     };
    262253
    263254    for (auto& run : runs) {
     
    293284        } else if (run.isText() || run.isSoftLineBreak()) {
    294285            // FIXME: Adjust non-empty inline box height when glyphs from the non-primary font stretch the box.
    295             stretchRootInlineBoxIfNeededQuirk(layoutBox);
     286            lineBox.inlineLevelBoxForLayoutBox(layoutBox.parent()).setIsNonEmpty();
    296287        } else if (run.isHardLineBreak()) {
    297288            auto lineBreakBox = LineBox::InlineLevelBox::createLineBreakBox(layoutBox, logicalLeft);
    298289            setVerticalGeometryForInlineBox(*lineBreakBox);
     290            lineBreakBox->setIsNonEmpty();
    299291            lineBox.addInlineLevelBox(WTFMove(lineBreakBox));
    300             stretchRootInlineBoxIfNeededQuirk(layoutBox);
    301292        } else if (run.isWordBreakOpportunity())
    302293            lineBox.addInlineLevelBox(LineBox::InlineLevelBox::createGenericInlineLevelBox(layoutBox, logicalLeft));
     
    315306        InlineLayoutUnit top { 0 };
    316307        InlineLayoutUnit bottom { 0 };
     308        const LineBox::InlineLevelBox* inlineLevelBox { nullptr };
    317309    };
    318310    HashMap<LineBox::InlineLevelBox*, AbsoluteTopAndBottom> absoluteLogicalTopAndBottomMap;
    319311    auto& rootInlineBox = lineBox.rootInlineBox();
    320     absoluteLogicalTopAndBottomMap.add(&rootInlineBox, AbsoluteTopAndBottom { { }, rootInlineBox.layoutBounds().height() });
     312    absoluteLogicalTopAndBottomMap.add(&rootInlineBox, AbsoluteTopAndBottom { { }, rootInlineBox.layoutBounds().height(), &rootInlineBox });
    321313
    322314    auto alignInlineBoxRelativeInlineLevelBoxes = [&] {
     
    352344            auto parentAbsoluteLogicalTop = absoluteLogicalTopAndBottomMap.get(&parentInlineBox).top;
    353345            auto absoluteLogicalTop = parentAbsoluteLogicalTop + logicalTop;
    354             absoluteLogicalTopAndBottomMap.add(inlineLevelBox.get(), AbsoluteTopAndBottom { absoluteLogicalTop, absoluteLogicalTop + inlineLevelBox->layoutBounds().height() });
     346            absoluteLogicalTopAndBottomMap.add(inlineLevelBox.get(), AbsoluteTopAndBottom { absoluteLogicalTop, absoluteLogicalTop + inlineLevelBox->layoutBounds().height(), inlineLevelBox.get() });
    355347        }
    356348    };
     
    358350
    359351    auto lineBoxLogicalHeight = InlineLayoutUnit { };
    360     auto minimumInlineBoxRelativeLogicalTop = InlineLayoutUnit { };
    361352    auto inlineBoxRelativeLogicalHeight = InlineLayoutUnit { };
     353    auto quirks = formattingContext().quirks();
     354    auto maximumBaselineAlignedAscent = rootInlineBox.isEmpty() ? InlineLayoutUnit() : rootInlineBox.layoutBounds().ascent;
    362355    auto computeLineBoxLogicalHeight = [&] {
    363356        // FIXME: Add support for layout bounds based line box height.
    364         auto minimumLogicalTop = InlineLayoutUnit { };
    365         auto maximumlogicalBottom = InlineLayoutUnit { };
     357        auto minimumLogicalTop = Optional<InlineLayoutUnit> { };
     358        auto maximumLogicalBottom = Optional<InlineLayoutUnit> { };
    366359        for (auto absoluteLogicalTopAndBottom : absoluteLogicalTopAndBottomMap.values()) {
    367             minimumLogicalTop = std::min(minimumLogicalTop, absoluteLogicalTopAndBottom.top);
    368             maximumlogicalBottom = std::max(maximumlogicalBottom, absoluteLogicalTopAndBottom.bottom);
    369         }
    370         minimumInlineBoxRelativeLogicalTop = minimumLogicalTop;
    371         inlineBoxRelativeLogicalHeight = maximumlogicalBottom - minimumLogicalTop;
     360            auto& inlineLevelBox = *absoluteLogicalTopAndBottom.inlineLevelBox;
     361            if (!quirks.shouldInlineLevelBoxStretchLineBox(lineBox, inlineLevelBox))
     362                continue;
     363            minimumLogicalTop = std::min(minimumLogicalTop.valueOr(absoluteLogicalTopAndBottom.top), absoluteLogicalTopAndBottom.top);
     364            maximumLogicalBottom = std::max(maximumLogicalBottom.valueOr(absoluteLogicalTopAndBottom.bottom), absoluteLogicalTopAndBottom.bottom);
     365            if (inlineLevelBox.verticalAlign() == VerticalAlign::Baseline)
     366                maximumBaselineAlignedAscent = std::max(maximumBaselineAlignedAscent, inlineLevelBox.layoutBounds().ascent);
     367        }
     368        inlineBoxRelativeLogicalHeight = maximumLogicalBottom.valueOr(InlineLayoutUnit()) - minimumLogicalTop.valueOr(InlineLayoutUnit());
    372369        lineBoxLogicalHeight = inlineBoxRelativeLogicalHeight;
    373370        // Now stretch the line box with the line box relative inline level boxes.
    374         for (auto* lineBoxRelativeInlineLevelBox : lineBoxRelativeInlineLevelBoxes)
     371        for (auto* lineBoxRelativeInlineLevelBox : lineBoxRelativeInlineLevelBoxes) {
     372            if (!quirks.shouldInlineLevelBoxStretchLineBox(lineBox, *lineBoxRelativeInlineLevelBox))
     373                continue;
    375374            lineBoxLogicalHeight = std::max(lineBoxLogicalHeight, lineBoxRelativeInlineLevelBox->layoutBounds().height());
     375        }
    376376    };
    377377    computeLineBoxLogicalHeight();
    378378
    379379    auto adjustRootInlineBoxVerticalPosition = [&] {
    380         if (minimumInlineBoxRelativeLogicalTop >= 0)
    381             return;
    382         rootInlineBox.setLogicalTop(-minimumInlineBoxRelativeLogicalTop);
     380        // FIXME: Add support for cases when the stretching inline boxes are not baseline aligned.
     381        auto logicalTop = maximumBaselineAlignedAscent - rootInlineBox.layoutBounds().ascent;
     382        rootInlineBox.setLogicalTop(logicalTop);
    383383    };
    384384    adjustRootInlineBoxVerticalPosition();
     
    418418InlineRect InlineFormattingContext::Geometry::computedLineLogicalRect(const LineBox& lineBox, const LineBuilder::LineContent& lineContent) const
    419419{
    420     auto isConsideredEmpty = lineContent.runs.isEmpty() || lineBox.isLineVisuallyEmpty();
    421     return { lineContent.logicalTopLeft, lineContent.lineLogicalWidth, isConsideredEmpty ? InlineLayoutUnit() : lineBox.logicalHeight()};
     420    return { lineContent.logicalTopLeft, lineContent.lineLogicalWidth, lineBox.logicalHeight() };
    422421}
    423422
  • trunk/Source/WebCore/layout/inlineformatting/InlineFormattingContextQuirks.cpp

    r268660 r268864  
    4141}
    4242
     43bool InlineFormattingContext::Quirks::shouldInlineLevelBoxStretchLineBox(const LineBox& lineBox, const LineBox::InlineLevelBox& inlineLevelBox) const
     44{
     45    if (inlineLevelBox.isEmpty())
     46        return false;
     47    if (layoutState().inNoQuirksMode())
     48        return true;
     49    if (!inlineLevelBox.isLineBreakBox())
     50        return true;
     51    // <br> in non-standard mode stretches the line box only when the line is empty.
     52    // 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>
     53    return lineBox.isLineVisuallyEmpty();
     54}
     55
    4356bool InlineFormattingContext::Quirks::hasSoftWrapOpportunityAtImage() const
    4457{
  • trunk/Source/WebCore/layout/inlineformatting/InlineLine.cpp

    r268485 r268864  
    354354
    355355    if (run.isLineBreak())
    356         return true;
     356        return false;
    357357
    358358    // Note that this does not check whether the inline container has content. It simply checks if the container itself is considered non-empty.
  • trunk/Source/WebCore/layout/inlineformatting/InlineLineBox.h

    r268825 r268864  
    8181
    8282        const FontMetrics& fontMetrics() const { return layoutBox().style().fontMetrics(); }
     83        VerticalAlign verticalAlign() const { return layoutBox().style().verticalAlign(); }
    8384        const Box& layoutBox() const { return *m_layoutBox; }
    8485
Note: See TracChangeset for help on using the changeset viewer.