| Differences between
and this patch
- a/Source/WebCore/ChangeLog +39 lines
Lines 1-3 a/Source/WebCore/ChangeLog_sec1
1
2020-10-21  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 NOBODY (OOPS!).
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
1
2020-10-21  Zalan Bujtas  <zalan@apple.com>
40
2020-10-21  Zalan Bujtas  <zalan@apple.com>
2
41
3
        [LFC][Integration] Add content dependent vertical line snapping
42
        [LFC][Integration] Add content dependent vertical line snapping
- a/Source/WebCore/layout/inlineformatting/InlineFormattingContext.h +1 lines
Lines 50-55 public: a/Source/WebCore/layout/inlineformatting/InlineFormattingContext.h_sec1
50
    public:
50
    public:
51
        InlineLayoutUnit initialLineHeight() const;
51
        InlineLayoutUnit initialLineHeight() const;
52
        bool hasSoftWrapOpportunityAtImage() const;
52
        bool hasSoftWrapOpportunityAtImage() const;
53
        bool shouldInlineLevelBoxStretchLineBox(const LineBox&, const LineBox::InlineLevelBox&) const;
53
54
54
    private:
55
    private:
55
        friend class InlineFormattingContext;
56
        friend class InlineFormattingContext;
- a/Source/WebCore/layout/inlineformatting/InlineFormattingContextGeometry.cpp -26 / +30 lines
Lines 213-220 void LineBoxBuilder::constructInlineLevelBoxes(LineBox& lineBox, const Line::Run a/Source/WebCore/layout/inlineformatting/InlineFormattingContextGeometry.cpp_sec1
213
        auto isInitiallyConsideredNonEmpty = !lineBox.isLineVisuallyEmpty() && lineHasImaginaryStrut;
213
        auto isInitiallyConsideredNonEmpty = !lineBox.isLineVisuallyEmpty() && lineHasImaginaryStrut;
214
        if (isInitiallyConsideredNonEmpty)
214
        if (isInitiallyConsideredNonEmpty)
215
            rootInlineBox->setIsNonEmpty();
215
            rootInlineBox->setIsNonEmpty();
216
        if (lineHasImaginaryStrut)
216
        setVerticalGeometryForInlineBox(*rootInlineBox);
217
            setVerticalGeometryForInlineBox(*rootInlineBox);
218
        lineBox.addRootInlineBox(WTFMove(rootInlineBox));
217
        lineBox.addRootInlineBox(WTFMove(rootInlineBox));
219
    };
218
    };
220
    createRootInlineBox();
219
    createRootInlineBox();
Lines 252-265 void LineBoxBuilder::constructInlineLevelBoxes(LineBox& lineBox, const Line::Run a/Source/WebCore/layout/inlineformatting/InlineFormattingContextGeometry.cpp_sec2
252
    };
251
    };
253
    createWrappedInlineBoxes();
252
    createWrappedInlineBoxes();
254
253
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
    };
262
263
    for (auto& run : runs) {
254
    for (auto& run : runs) {
264
        auto& layoutBox = run.layoutBox();
255
        auto& layoutBox = run.layoutBox();
265
        auto logicalLeft = horizontalAligmentOffset + run.logicalLeft();
256
        auto logicalLeft = horizontalAligmentOffset + run.logicalLeft();
Lines 292-303 void LineBoxBuilder::constructInlineLevelBoxes(LineBox& lineBox, const Line::Run a/Source/WebCore/layout/inlineformatting/InlineFormattingContextGeometry.cpp_sec3
292
            inlineBox.setLogicalWidth(run.logicalRight() - inlineBox.logicalLeft());
283
            inlineBox.setLogicalWidth(run.logicalRight() - inlineBox.logicalLeft());
293
        } else if (run.isText() || run.isSoftLineBreak()) {
284
        } else if (run.isText() || run.isSoftLineBreak()) {
294
            // FIXME: Adjust non-empty inline box height when glyphs from the non-primary font stretch the box.
285
            // 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();
296
        } else if (run.isHardLineBreak()) {
287
        } else if (run.isHardLineBreak()) {
297
            auto lineBreakBox = LineBox::InlineLevelBox::createLineBreakBox(layoutBox, logicalLeft);
288
            auto lineBreakBox = LineBox::InlineLevelBox::createLineBreakBox(layoutBox, logicalLeft);
298
            setVerticalGeometryForInlineBox(*lineBreakBox);
289
            setVerticalGeometryForInlineBox(*lineBreakBox);
290
            lineBreakBox->setIsNonEmpty();
299
            lineBox.addInlineLevelBox(WTFMove(lineBreakBox));
291
            lineBox.addInlineLevelBox(WTFMove(lineBreakBox));
300
            stretchRootInlineBoxIfNeededQuirk(layoutBox);
301
        } else if (run.isWordBreakOpportunity())
292
        } else if (run.isWordBreakOpportunity())
302
            lineBox.addInlineLevelBox(LineBox::InlineLevelBox::createGenericInlineLevelBox(layoutBox, logicalLeft));
293
            lineBox.addInlineLevelBox(LineBox::InlineLevelBox::createGenericInlineLevelBox(layoutBox, logicalLeft));
303
        else
294
        else
Lines 314-319 void LineBoxBuilder::alignInlineLevelBoxesVerticallyAndComputeLineBoxHeight(Line a/Source/WebCore/layout/inlineformatting/InlineFormattingContextGeometry.cpp_sec4
314
    struct AbsoluteTopAndBottom {
305
    struct AbsoluteTopAndBottom {
315
        InlineLayoutUnit top { 0 };
306
        InlineLayoutUnit top { 0 };
316
        InlineLayoutUnit bottom { 0 };
307
        InlineLayoutUnit bottom { 0 };
308
        const LineBox::InlineLevelBox* inlineLevelBox { nullptr };
317
    };
309
    };
318
    HashMap<LineBox::InlineLevelBox*, AbsoluteTopAndBottom> absoluteLogicalTopAndBottomMap;
310
    HashMap<LineBox::InlineLevelBox*, AbsoluteTopAndBottom> absoluteLogicalTopAndBottomMap;
319
    auto& rootInlineBox = lineBox.rootInlineBox();
311
    auto& rootInlineBox = lineBox.rootInlineBox();
Lines 351-385 void LineBoxBuilder::alignInlineLevelBoxesVerticallyAndComputeLineBoxHeight(Line a/Source/WebCore/layout/inlineformatting/InlineFormattingContextGeometry.cpp_sec5
351
            inlineLevelBox->setLogicalTop(logicalTop);
343
            inlineLevelBox->setLogicalTop(logicalTop);
352
            auto parentAbsoluteLogicalTop = absoluteLogicalTopAndBottomMap.get(&parentInlineBox).top;
344
            auto parentAbsoluteLogicalTop = absoluteLogicalTopAndBottomMap.get(&parentInlineBox).top;
353
            auto absoluteLogicalTop = parentAbsoluteLogicalTop + logicalTop;
345
            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() });
355
        }
347
        }
356
    };
348
    };
357
    alignInlineBoxRelativeInlineLevelBoxes();
349
    alignInlineBoxRelativeInlineLevelBoxes();
358
350
359
    auto lineBoxLogicalHeight = InlineLayoutUnit { };
351
    auto lineBoxLogicalHeight = InlineLayoutUnit { };
360
    auto minimumInlineBoxRelativeLogicalTop = InlineLayoutUnit { };
361
    auto inlineBoxRelativeLogicalHeight = InlineLayoutUnit { };
352
    auto inlineBoxRelativeLogicalHeight = InlineLayoutUnit { };
353
    auto quirks = formattingContext().quirks();
354
    auto maximumBaselineAlignedAscent = rootInlineBox.isEmpty() ? InlineLayoutUnit() : rootInlineBox.layoutBounds().ascent;
362
    auto computeLineBoxLogicalHeight = [&] {
355
    auto computeLineBoxLogicalHeight = [&] {
356
        auto shouldStretchLineBox = [&](const auto& inlineLevelBox) {
357
            if (inlineLevelBox.isEmpty())
358
                return false;
359
            return quirks.shouldInlineLevelBoxStretchLineBox(lineBox, inlineLevelBox);
360
        };
363
        // FIXME: Add support for layout bounds based line box height.
361
        // FIXME: Add support for layout bounds based line box height.
364
        auto minimumLogicalTop = InlineLayoutUnit { };
362
        auto minimumLogicalTop = Optional<InlineLayoutUnit> { };
365
        auto maximumlogicalBottom = InlineLayoutUnit { };
363
        auto maximumLogicalBottom = Optional<InlineLayoutUnit> { };
366
        for (auto absoluteLogicalTopAndBottom : absoluteLogicalTopAndBottomMap.values()) {
364
        for (auto absoluteLogicalTopAndBottom : absoluteLogicalTopAndBottomMap.values()) {
367
            minimumLogicalTop = std::min(minimumLogicalTop, absoluteLogicalTopAndBottom.top);
365
            auto& inlineLevelBox = *absoluteLogicalTopAndBottom.inlineLevelBox;
368
            maximumlogicalBottom = std::max(maximumlogicalBottom, absoluteLogicalTopAndBottom.bottom);
366
            if (!shouldStretchLineBox(inlineLevelBox))
367
                continue;
368
            minimumLogicalTop = std::min(minimumLogicalTop.valueOr(absoluteLogicalTopAndBottom.top), absoluteLogicalTopAndBottom.top);
369
            maximumLogicalBottom = std::max(maximumLogicalBottom.valueOr(absoluteLogicalTopAndBottom.bottom), absoluteLogicalTopAndBottom.bottom);
370
            if (inlineLevelBox.verticalAlign() == VerticalAlign::Baseline)
371
                maximumBaselineAlignedAscent = std::max(maximumBaselineAlignedAscent, inlineLevelBox.layoutBounds().ascent);
369
        }
372
        }
370
        minimumInlineBoxRelativeLogicalTop = minimumLogicalTop;
373
        inlineBoxRelativeLogicalHeight = maximumLogicalBottom.valueOr(InlineLayoutUnit()) - minimumLogicalTop.valueOr(InlineLayoutUnit());
371
        inlineBoxRelativeLogicalHeight = maximumlogicalBottom - minimumLogicalTop;
372
        lineBoxLogicalHeight = inlineBoxRelativeLogicalHeight;
374
        lineBoxLogicalHeight = inlineBoxRelativeLogicalHeight;
373
        // Now stretch the line box with the line box relative inline level boxes.
375
        // Now stretch the line box with the line box relative inline level boxes.
374
        for (auto* lineBoxRelativeInlineLevelBox : lineBoxRelativeInlineLevelBoxes)
376
        for (auto* lineBoxRelativeInlineLevelBox : lineBoxRelativeInlineLevelBoxes) {
377
            if (!shouldStretchLineBox(*lineBoxRelativeInlineLevelBox))
378
                continue;
375
            lineBoxLogicalHeight = std::max(lineBoxLogicalHeight, lineBoxRelativeInlineLevelBox->layoutBounds().height());
379
            lineBoxLogicalHeight = std::max(lineBoxLogicalHeight, lineBoxRelativeInlineLevelBox->layoutBounds().height());
380
        }
376
    };
381
    };
377
    computeLineBoxLogicalHeight();
382
    computeLineBoxLogicalHeight();
378
383
379
    auto adjustRootInlineBoxVerticalPosition = [&] {
384
    auto adjustRootInlineBoxVerticalPosition = [&] {
380
        if (minimumInlineBoxRelativeLogicalTop >= 0)
385
        // FIXME: Add support for cases when the stretching inline boxes are not baseline aligned.
381
            return;
386
        auto logicalTop = maximumBaselineAlignedAscent - rootInlineBox.layoutBounds().ascent;
382
        rootInlineBox.setLogicalTop(-minimumInlineBoxRelativeLogicalTop);
387
        rootInlineBox.setLogicalTop(logicalTop);
383
    };
388
    };
384
    adjustRootInlineBoxVerticalPosition();
389
    adjustRootInlineBoxVerticalPosition();
385
390
Lines 417-424 LineBox InlineFormattingContext::Geometry::lineBoxForLineContent(const LineBuild a/Source/WebCore/layout/inlineformatting/InlineFormattingContextGeometry.cpp_sec6
417
422
418
InlineRect InlineFormattingContext::Geometry::computedLineLogicalRect(const LineBox& lineBox, const LineBuilder::LineContent& lineContent) const
423
InlineRect InlineFormattingContext::Geometry::computedLineLogicalRect(const LineBox& lineBox, const LineBuilder::LineContent& lineContent) const
419
{
424
{
420
    auto isConsideredEmpty = lineContent.runs.isEmpty() || lineBox.isLineVisuallyEmpty();
425
    return { lineContent.logicalTopLeft, lineContent.lineLogicalWidth, lineBox.logicalHeight() };
421
    return { lineContent.logicalTopLeft, lineContent.lineLogicalWidth, isConsideredEmpty ? InlineLayoutUnit() : lineBox.logicalHeight()};
422
}
426
}
423
427
424
InlineLayoutUnit InlineFormattingContext::Geometry::logicalTopForNextLine(const LineBuilder::LineContent& lineContent, InlineLayoutUnit previousLineLogicalBottom, const FloatingContext& floatingContext) const
428
InlineLayoutUnit InlineFormattingContext::Geometry::logicalTopForNextLine(const LineBuilder::LineContent& lineContent, InlineLayoutUnit previousLineLogicalBottom, const FloatingContext& floatingContext) const
- a/Source/WebCore/layout/inlineformatting/InlineFormattingContextQuirks.cpp +11 lines
Lines 40-45 InlineLayoutUnit InlineFormattingContext::Quirks::initialLineHeight() const a/Source/WebCore/layout/inlineformatting/InlineFormattingContextQuirks.cpp_sec1
40
    return root.style().fontMetrics().floatHeight();
40
    return root.style().fontMetrics().floatHeight();
41
}
41
}
42
42
43
bool InlineFormattingContext::Quirks::shouldInlineLevelBoxStretchLineBox(const LineBox& lineBox, const LineBox::InlineLevelBox& inlineLevelBox) const
44
{
45
    if (layoutState().inNoQuirksMode())
46
        return true;
47
    if (!inlineLevelBox.isLineBreakBox())
48
        return true;
49
    // <br> in non-standard mode stretches the line box only when the line is empty.
50
    // 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>
51
    return lineBox.isLineVisuallyEmpty();
52
}
53
43
bool InlineFormattingContext::Quirks::hasSoftWrapOpportunityAtImage() const
54
bool InlineFormattingContext::Quirks::hasSoftWrapOpportunityAtImage() const
44
{
55
{
45
    return !formattingContext().root().isTableCell();
56
    return !formattingContext().root().isTableCell();
- a/Source/WebCore/layout/inlineformatting/InlineLine.cpp -1 / +1 lines
Lines 353-359 bool Line::isRunVisuallyNonEmpty(const Run& run) const a/Source/WebCore/layout/inlineformatting/InlineLine.cpp_sec1
353
        return true;
353
        return true;
354
354
355
    if (run.isLineBreak())
355
    if (run.isLineBreak())
356
        return true;
356
        return false;
357
357
358
    // Note that this does not check whether the inline container has content. It simply checks if the container itself is considered non-empty.
358
    // Note that this does not check whether the inline container has content. It simply checks if the container itself is considered non-empty.
359
    if (run.isContainerStart() || run.isContainerEnd()) {
359
    if (run.isContainerStart() || run.isContainerEnd()) {
- a/Source/WebCore/layout/inlineformatting/InlineLineBox.h +1 lines
Lines 80-85 public: a/Source/WebCore/layout/inlineformatting/InlineLineBox.h_sec1
80
        void setIsNonEmpty() { m_isEmpty = false; }
80
        void setIsNonEmpty() { m_isEmpty = false; }
81
81
82
        const FontMetrics& fontMetrics() const { return layoutBox().style().fontMetrics(); }
82
        const FontMetrics& fontMetrics() const { return layoutBox().style().fontMetrics(); }
83
        VerticalAlign verticalAlign() const { return layoutBox().style().verticalAlign(); }
83
        const Box& layoutBox() const { return *m_layoutBox; }
84
        const Box& layoutBox() const { return *m_layoutBox; }
84
85
85
        bool isInlineBox() const { return m_type == Type::InlineBox || m_type == Type::RootInlineBox; }
86
        bool isInlineBox() const { return m_type == Type::InlineBox || m_type == Type::RootInlineBox; }

Return to Bug 218060