Changeset 268642 in webkit


Ignore:
Timestamp:
Oct 17, 2020 6:30:15 AM (4 years ago)
Author:
Alan Bujtas
Message:

[LFC][IFC] Introduce layout bounds based vertical alignment
https://bugs.webkit.org/show_bug.cgi?id=217832

Reviewed by Antti Koivisto.

This patch introduces the layout bounds concept on the inline boxes.
It help with implementing https://www.w3.org/TR/css-inline-3/#line-layout (2.2. Layout Within Line Boxes)
where layout bounds are used extensively (essentially the layout bound drives the inline box's aligned vertical
position within the line box -as opposed to its logical height. Though they may match in many cases.)

With this patch nested inline boxes are positioned relative to their parent inline boxes and
LineBox::logicalRectForInlineLevelBox/logicalRectForTextRun resolves them to be relative to the line box (which then used for
generating display inline runs with relative to the line coordinates).

  • layout/inlineformatting/InlineFormattingContext.cpp:

(WebCore::Layout::InlineFormattingContext::computeGeometryForLineContent):

  • layout/inlineformatting/InlineFormattingContextGeometry.cpp:

(WebCore::Layout::LineBoxBuilder::build):
(WebCore::Layout::LineBoxBuilder::setVerticalGeometryForInlineBox const):
(WebCore::Layout::LineBoxBuilder::constructInlineLevelBoxes):
(WebCore::Layout::LineBoxBuilder::alignInlineLevelBoxesVerticallyAndComputeLineBoxHeight):
(WebCore::Layout::InlineFormattingContext::Geometry::computedLineLogicalRect const):
(WebCore::Layout::LineBoxBuilder::adjustInlineBoxesLogicalHeight): Deleted.

  • layout/inlineformatting/InlineLineBox.cpp:

(WebCore::Layout::LineBox::logicalRectForTextRun const):
(WebCore::Layout::LineBox::logicalRectForInlineLevelBox const):

  • layout/inlineformatting/InlineLineBox.h:

(WebCore::Layout::LineBox::InlineLevelBox::LayoutBounds::height const):
(WebCore::Layout::LineBox::InlineLevelBox::setLayoutBounds):
(WebCore::Layout::LineBox::InlineLevelBox::layoutBounds const):
(WebCore::Layout::LineBox::InlineLevelBox::lineSpacing const): Deleted.
(WebCore::Layout::LineBox::InlineLevelBox::setLineSpacing): Deleted.

Location:
trunk/Source/WebCore
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/ChangeLog

    r268641 r268642  
     12020-10-17  Zalan Bujtas  <zalan@apple.com>
     2
     3        [LFC][IFC] Introduce layout bounds based vertical alignment
     4        https://bugs.webkit.org/show_bug.cgi?id=217832
     5
     6        Reviewed by Antti Koivisto.
     7
     8        This patch introduces the layout bounds concept on the inline boxes.
     9        It help with implementing https://www.w3.org/TR/css-inline-3/#line-layout (2.2. Layout Within Line Boxes)
     10        where layout bounds are used extensively (essentially the layout bound drives the inline box's aligned vertical
     11        position within the line box -as opposed to its logical height. Though they may match in many cases.)
     12
     13        With this patch nested inline boxes are positioned relative to their parent inline boxes and
     14        LineBox::logicalRectForInlineLevelBox/logicalRectForTextRun resolves them to be relative to the line box (which then used for
     15        generating display inline runs with relative to the line coordinates).
     16
     17        * layout/inlineformatting/InlineFormattingContext.cpp:
     18        (WebCore::Layout::InlineFormattingContext::computeGeometryForLineContent):
     19        * layout/inlineformatting/InlineFormattingContextGeometry.cpp:
     20        (WebCore::Layout::LineBoxBuilder::build):
     21        (WebCore::Layout::LineBoxBuilder::setVerticalGeometryForInlineBox const):
     22        (WebCore::Layout::LineBoxBuilder::constructInlineLevelBoxes):
     23        (WebCore::Layout::LineBoxBuilder::alignInlineLevelBoxesVerticallyAndComputeLineBoxHeight):
     24        (WebCore::Layout::InlineFormattingContext::Geometry::computedLineLogicalRect const):
     25        (WebCore::Layout::LineBoxBuilder::adjustInlineBoxesLogicalHeight): Deleted.
     26        * layout/inlineformatting/InlineLineBox.cpp:
     27        (WebCore::Layout::LineBox::logicalRectForTextRun const):
     28        (WebCore::Layout::LineBox::logicalRectForInlineLevelBox const):
     29        * layout/inlineformatting/InlineLineBox.h:
     30        (WebCore::Layout::LineBox::InlineLevelBox::LayoutBounds::height const):
     31        (WebCore::Layout::LineBox::InlineLevelBox::setLayoutBounds):
     32        (WebCore::Layout::LineBox::InlineLevelBox::layoutBounds const):
     33        (WebCore::Layout::LineBox::InlineLevelBox::lineSpacing const): Deleted.
     34        (WebCore::Layout::LineBox::InlineLevelBox::setLineSpacing): Deleted.
     35
    1362020-10-17  Antti Koivisto  <antti@apple.com>
    237
  • trunk/Source/WebCore/layout/inlineformatting/InlineFormattingContext.cpp

    r268315 r268642  
    442442                formattingState.addLineRun({ lineIndex, lineRun.layoutBox(), lineBox.logicalRectForTextRun(lineRun), lineRun.expansion(), lineRun.textContent() });
    443443            else if (lineRun.isBox())
    444                 formattingState.addLineRun({ lineIndex, lineRun.layoutBox(), lineBox.inlineLevelBoxForLayoutBox(lineRun.layoutBox()).logicalRect(), lineRun.expansion(), { } });
     444                formattingState.addLineRun({ lineIndex, lineRun.layoutBox(), lineBox.logicalRectForInlineLevelBox(lineRun.layoutBox()), lineRun.expansion(), { } });
    445445        }
    446446    };
  • trunk/Source/WebCore/layout/inlineformatting/InlineFormattingContextGeometry.cpp

    r268485 r268642  
    4848    void setVerticalGeometryForInlineBox(LineBox::InlineLevelBox&) const;
    4949    void constructInlineLevelBoxes(LineBox&, const Line::RunList&);
    50     void adjustInlineBoxesLogicalHeight(LineBox&);
    5150    void alignInlineLevelBoxesVerticallyAndComputeLineBoxHeight(LineBox&);
    5251
     
    161160        lineBox.setHorizontalAlignmentOffset(*horizontalAlignmentOffset);
    162161    constructInlineLevelBoxes(lineBox, runs);
    163     adjustInlineBoxesLogicalHeight(lineBox);
    164162    alignInlineLevelBoxesVerticallyAndComputeLineBoxHeight(lineBox);
    165163    return lineBox;
     
    170168    ASSERT(inlineLevelBox.isInlineBox() || inlineLevelBox.isLineBreakBox());
    171169    auto& fontMetrics = inlineLevelBox.fontMetrics();
    172     InlineLayoutUnit logicalHeight = fontMetrics.height();
    173     InlineLayoutUnit baseline = fontMetrics.ascent();
    174 
     170    InlineLayoutUnit ascent = fontMetrics.ascent();
     171    InlineLayoutUnit descent = fontMetrics.descent();
     172    auto logicalHeight = ascent + descent;
     173
     174    // FIXME: Adjust layout bounds with fallback font when applicable.
     175    auto& style = inlineLevelBox.layoutBox().style();
     176    auto lineHeight = style.lineHeight();
     177    if (lineHeight.isNegative()) {
     178        // If line-height computes to normal and either text-edge is leading or this is the root inline box,
     179        // the font’s line gap metric may also be incorporated into A and D by adding half to each side as half-leading.
     180        auto lineSpacing = &inlineLevelBox.layoutBox() == &rootBox() ? fontMetrics.lineSpacing() - logicalHeight : InlineLayoutUnit();
     181        ascent += lineSpacing / 2;
     182        descent += lineSpacing / 2;
     183    } else {
     184        InlineLayoutUnit lineHeight = style.computedLineHeight();
     185        InlineLayoutUnit halfLeading = (lineHeight - (ascent + descent)) / 2;
     186        ascent += halfLeading;
     187        descent += halfLeading;
     188    }
     189    // We need this to match legacy layout integral positioning.
     190    ascent = floorf(ascent);
     191    descent = ceil(descent);
     192
     193    inlineLevelBox.setLayoutBounds(LineBox::InlineLevelBox::LayoutBounds { ascent, descent });
     194    inlineLevelBox.setBaseline(ascent);
     195    inlineLevelBox.setDescent(descent);
    175196    inlineLevelBox.setLogicalHeight(logicalHeight);
    176     inlineLevelBox.setBaseline(baseline);
    177     inlineLevelBox.setDescent(logicalHeight - baseline);
    178     if (auto lineSpacing = fontMetrics.lineSpacing() - logicalHeight)
    179         inlineLevelBox.setLineSpacing(lineSpacing);
    180     inlineLevelBox.setIsNonEmpty();
    181197}
    182198
     
    187203    auto createRootInlineBox = [&] {
    188204        auto rootInlineBox = LineBox::InlineLevelBox::createRootInlineBox(rootBox(), horizontalAligmentOffset, lineBox.logicalWidth());
     205        setVerticalGeometryForInlineBox(*rootInlineBox);
    189206
    190207        auto lineHasImaginaryStrut = !layoutState().inQuirksMode();
    191208        auto isInitiallyConsideredNonEmpty = !lineBox.isLineVisuallyEmpty() && lineHasImaginaryStrut;
    192209        if (isInitiallyConsideredNonEmpty)
    193             setVerticalGeometryForInlineBox(*rootInlineBox);
     210            rootInlineBox->setIsNonEmpty();
    194211        lineBox.addRootInlineBox(WTFMove(rootInlineBox));
    195212    };
     
    244261            auto atomicInlineLevelBox = LineBox::InlineLevelBox::createAtomicInlineLevelBox(layoutBox, logicalLeft, { run.logicalWidth(), logicalHeight });
    245262            atomicInlineLevelBox->setBaseline(baseline);
     263            atomicInlineLevelBox->setLayoutBounds(LineBox::InlineLevelBox::LayoutBounds { baseline, { } });
    246264            if (logicalHeight)
    247265                atomicInlineLevelBox->setIsNonEmpty();
     
    250268            auto initialLogicalWidth = lineBox.logicalWidth() - run.logicalLeft();
    251269            ASSERT(initialLogicalWidth >= 0);
    252             lineBox.addInlineLevelBox(LineBox::InlineLevelBox::createInlineBox(layoutBox, logicalLeft, initialLogicalWidth));
     270            auto inlineBox = LineBox::InlineLevelBox::createInlineBox(layoutBox, logicalLeft, initialLogicalWidth);
     271            setVerticalGeometryForInlineBox(*inlineBox);
     272            lineBox.addInlineLevelBox(WTFMove(inlineBox));
    253273        } else if (run.isContainerEnd()) {
    254274            // Adjust the logical width when the inline level container closes on this line.
     
    257277            inlineBox.setLogicalWidth(run.logicalRight() - inlineBox.logicalLeft());
    258278        } else if (run.isText() || run.isSoftLineBreak()) {
    259             auto& parentBox = layoutBox.parent();
    260             auto& parentInlineBox = &parentBox == &rootBox() ? lineBox.rootInlineBox() : lineBox.inlineLevelBoxForLayoutBox(parentBox);
    261279            // FIXME: Adjust non-empty inline box height when glyphs from the non-primary font stretch the box.
    262             if (parentInlineBox.isEmpty())
    263                 setVerticalGeometryForInlineBox(parentInlineBox);
     280            // FIXME: Add quirk inline box stretching.
    264281        } else if (run.isHardLineBreak()) {
    265282            auto lineBreakBox = LineBox::InlineLevelBox::createLineBreakBox(layoutBox, logicalLeft);
     
    273290}
    274291
    275 void LineBoxBuilder::adjustInlineBoxesLogicalHeight(LineBox& lineBox)
    276 {
    277     // By traversing the inline box list backwards, it's guaranteed that descendant inline boxes are sized first.
    278     for (auto& inlineLevelBox : WTF::makeReversedRange(lineBox.nonRootInlineLevelBoxes())) {
    279         if (inlineLevelBox->isEmpty())
    280             continue;
    281 
    282         auto& layoutBox = inlineLevelBox->layoutBox();
    283         auto& parentInlineBox = lineBox.inlineLevelBoxForLayoutBox(layoutBox.parent());
    284         ASSERT(parentInlineBox.isInlineBox());
    285         switch (layoutBox.style().verticalAlign()) {
    286         case VerticalAlign::Top:
    287         case VerticalAlign::Bottom:
    288             // top and bottom alignments only stretch the line box. They don't stretch any of the inline boxes, not even the root inline box.
    289             break;
    290         case VerticalAlign::TextTop: {
    291             auto parentTextLogicalTop = parentInlineBox.baseline() - parentInlineBox.fontMetrics().ascent();
    292             parentInlineBox.setLogicalHeight(std::max(parentInlineBox.logicalHeight(), parentTextLogicalTop + inlineLevelBox->logicalHeight()));
    293             break;
    294         }
    295         case VerticalAlign::Baseline: {
    296             auto baselineOverflow = std::max(0.0f, inlineLevelBox->baseline() - parentInlineBox.baseline());
    297             if (baselineOverflow) {
    298                 parentInlineBox.setBaseline(parentInlineBox.baseline() + baselineOverflow);
    299                 parentInlineBox.setLogicalHeight(parentInlineBox.logicalHeight() + baselineOverflow);
     292void LineBoxBuilder::alignInlineLevelBoxesVerticallyAndComputeLineBoxHeight(LineBox& lineBox)
     293{
     294    // This function (partially) implements:
     295    // 2.2. Layout Within Line Boxes
     296    // https://www.w3.org/TR/css-inline-3/#line-layout
     297    Vector<LineBox::InlineLevelBox*> lineBoxRelativeInlineLevelBoxes;
     298    struct AbsoluteTopAndBottom {
     299        InlineLayoutUnit top { 0 };
     300        InlineLayoutUnit bottom { 0 };
     301    };
     302    HashMap<LineBox::InlineLevelBox*, AbsoluteTopAndBottom> absoluteLogicalTopAndBottomMap;
     303    auto& rootInlineBox = lineBox.rootInlineBox();
     304    absoluteLogicalTopAndBottomMap.add(&rootInlineBox, AbsoluteTopAndBottom { { }, rootInlineBox.layoutBounds().height() });
     305
     306    auto alignInlineBoxRelativeInlineLevelBoxes = [&] {
     307        // FIXME: Add proper support for cases when the inline box with line box relative alignment has a child inline box
     308        // with non-line box relative alignment.
     309        for (auto& inlineLevelBox : lineBox.nonRootInlineLevelBoxes()) {
     310            auto& layoutBox = inlineLevelBox->layoutBox();
     311            auto verticalAlignment = layoutBox.style().verticalAlign();
     312            if (inlineLevelBox->hasLineBoxRelativeAlignment()) {
     313                lineBoxRelativeInlineLevelBoxes.append(inlineLevelBox.get());
     314                continue;
    300315            }
    301             auto parentInlineBoxBelowBaseline = parentInlineBox.logicalHeight() - parentInlineBox.baseline();
    302             auto inlineBoxBelowBaseline = inlineLevelBox->logicalHeight() - inlineLevelBox->baseline();
    303             auto belowBaselineOverflow = std::max(0.0f, inlineBoxBelowBaseline - parentInlineBoxBelowBaseline);
    304             if (belowBaselineOverflow)
    305                 parentInlineBox.setLogicalHeight(parentInlineBox.logicalHeight() + belowBaselineOverflow);
    306             break;
    307         }
    308         case VerticalAlign::TextBottom: {
    309             auto parentTextLogicalBottom = parentInlineBox.baseline() + parentInlineBox.descent().valueOr(InlineLayoutUnit { });
    310             auto overflow = std::max(0.0f, inlineLevelBox->logicalHeight() - parentTextLogicalBottom);
    311             if (overflow) {
    312                 // TextBottom pushes the baseline downward the same way 'bottom' does.
    313                 parentInlineBox.setLogicalHeight(parentInlineBox.logicalHeight() + overflow);
    314                 parentInlineBox.setBaseline(parentInlineBox.baseline() + overflow);
     316            auto& parentInlineBox = lineBox.inlineLevelBoxForLayoutBox(layoutBox.parent());
     317            auto logicalTop = InlineLayoutUnit { };
     318            switch (verticalAlignment) {
     319            case VerticalAlign::Baseline:
     320                logicalTop = parentInlineBox.baseline() - inlineLevelBox->baseline();
     321                break;
     322            case VerticalAlign::TextTop:
     323                logicalTop = { };
     324                break;
     325            case VerticalAlign::TextBottom:
     326                logicalTop = parentInlineBox.layoutBounds().height() - inlineLevelBox->layoutBounds().height();
     327                break;
     328            case VerticalAlign::Middle:
     329                logicalTop = parentInlineBox.baseline() - (inlineLevelBox->layoutBounds().height() / 2 + parentInlineBox.fontMetrics().xHeight() / 2);
     330                break;
     331            default:
     332                ASSERT_NOT_IMPLEMENTED_YET();
     333                break;
    315334            }
    316             break;
    317         }
    318         case VerticalAlign::Middle: {
    319             auto logicalTop = parentInlineBox.baseline() - (inlineLevelBox->logicalHeight() / 2 + parentInlineBox.fontMetrics().xHeight() / 2);
    320             if (logicalTop < 0) {
    321                 auto overflow = -logicalTop;
    322                 // Child inline box with middle alignment pushes the baseline down when overflows.
    323                 parentInlineBox.setBaseline(parentInlineBox.baseline() + overflow);
    324                 parentInlineBox.setLogicalHeight(parentInlineBox.logicalHeight() + overflow);
     335            inlineLevelBox->setLogicalTop(logicalTop);
     336            auto parentAbsoluteLogicalTop = absoluteLogicalTopAndBottomMap.get(&parentInlineBox).top;
     337            auto absoluteLogicalTop = parentAbsoluteLogicalTop + logicalTop;
     338            absoluteLogicalTopAndBottomMap.add(inlineLevelBox.get(), AbsoluteTopAndBottom { absoluteLogicalTop, absoluteLogicalTop + inlineLevelBox->layoutBounds().height() });
     339        }
     340    };
     341    alignInlineBoxRelativeInlineLevelBoxes();
     342
     343    auto lineBoxLogicalHeight = InlineLayoutUnit { };
     344    auto minimumInlineBoxRelativeLogicalTop = InlineLayoutUnit { };
     345    auto inlineBoxRelativeLogicalHeight = InlineLayoutUnit { };
     346    auto computeLineBoxLogicalHeight = [&] {
     347        // FIXME: Add support for layout bounds based line box height.
     348        auto minimumLogicalTop = InlineLayoutUnit { };
     349        auto maximumlogicalBottom = InlineLayoutUnit { };
     350        for (auto absoluteLogicalTopAndBottom : absoluteLogicalTopAndBottomMap.values()) {
     351            minimumLogicalTop = std::min(minimumLogicalTop, absoluteLogicalTopAndBottom.top);
     352            maximumlogicalBottom = std::max(maximumlogicalBottom, absoluteLogicalTopAndBottom.bottom);
     353        }
     354        minimumInlineBoxRelativeLogicalTop = minimumLogicalTop;
     355        inlineBoxRelativeLogicalHeight = maximumlogicalBottom - minimumLogicalTop;
     356        lineBoxLogicalHeight = inlineBoxRelativeLogicalHeight;
     357        // Now stretch the line box with the line box relative inline level boxes.
     358        for (auto* lineBoxRelativeInlineLevelBox : lineBoxRelativeInlineLevelBoxes)
     359            lineBoxLogicalHeight = std::max(lineBoxLogicalHeight, lineBoxRelativeInlineLevelBox->layoutBounds().height());
     360    };
     361    computeLineBoxLogicalHeight();
     362
     363    auto adjustRootInlineBoxVerticalPosition = [&] {
     364        if (minimumInlineBoxRelativeLogicalTop >= 0)
     365            return;
     366        rootInlineBox.setLogicalTop(-minimumInlineBoxRelativeLogicalTop);
     367    };
     368    adjustRootInlineBoxVerticalPosition();
     369
     370    auto alignLineBoxRelativeInlineLevelBoxes = [&] {
     371        for (auto* inlineLevelBox : lineBoxRelativeInlineLevelBoxes) {
     372            auto& layoutBox = inlineLevelBox->layoutBox();
     373            auto verticalAlignment = layoutBox.style().verticalAlign();
     374            auto logicalTop = InlineLayoutUnit { };
     375            auto rootInlineBoxOffset = InlineLayoutUnit { };
     376            switch (verticalAlignment) {
     377            case VerticalAlign::Top:
    325378                logicalTop = { };
     379                break;
     380            case VerticalAlign::Bottom:
     381                logicalTop = lineBoxLogicalHeight - inlineLevelBox->layoutBounds().height();
     382                rootInlineBoxOffset = inlineLevelBox->layoutBounds().height() - inlineBoxRelativeLogicalHeight;
     383                break;
     384            default:
     385                ASSERT_NOT_IMPLEMENTED_YET();
     386                break;
    326387            }
    327             auto logicalBottom = logicalTop + inlineLevelBox->logicalHeight();
    328             parentInlineBox.setLogicalHeight(std::max(parentInlineBox.logicalHeight(), logicalBottom));
    329             break;
    330         }
    331         default:
    332             ASSERT_NOT_IMPLEMENTED_YET();
    333             break;
    334         }
    335     }
    336 }
    337 
    338 void LineBoxBuilder::alignInlineLevelBoxesVerticallyAndComputeLineBoxHeight(LineBox& lineBox)
    339 {
    340     // Inline boxes are in the coordinate system of the line box (and not in the coordinate system of their parents).
    341     // Starting with the root inline box, position the ancestors first so that the descendant line boxes see absolute vertical positions.
    342     auto& rootInlineBox = lineBox.rootInlineBox();
    343     auto contentLogicalHeight = InlineLayoutUnit { };
    344     auto alignRootInlineBox = [&] {
    345         contentLogicalHeight = rootInlineBox.logicalHeight();
    346         for (auto& inlineLevelBox : lineBox.nonRootInlineLevelBoxes()) {
    347             auto verticalAlign = inlineLevelBox->layoutBox().style().verticalAlign();
    348             if (verticalAlign == VerticalAlign::Bottom) {
    349                 // bottom align always pushes the root inline box downwards.
    350                 auto overflow = std::max(0.0f, inlineLevelBox->logicalBottom() - rootInlineBox.logicalBottom());
    351                 rootInlineBox.setLogicalTop(rootInlineBox.logicalTop() + overflow);
    352                 contentLogicalHeight += overflow;
    353             } else if (verticalAlign == VerticalAlign::Top)
    354                 contentLogicalHeight = std::max(contentLogicalHeight, inlineLevelBox->logicalHeight());
    355         }
    356     };
    357     alignRootInlineBox();
    358 
    359     for (auto& inlineLevelBox : lineBox.nonRootInlineLevelBoxes()) {
    360         auto inlineLevelBoxLogicalTop = InlineLayoutUnit { };
    361         auto& layoutBox = inlineLevelBox->layoutBox();
    362         auto& parentInlineBox = lineBox.inlineLevelBoxForLayoutBox(layoutBox.parent());
    363         ASSERT(parentInlineBox.isInlineBox());
    364         switch (layoutBox.style().verticalAlign()) {
    365         case VerticalAlign::Baseline:
    366             inlineLevelBoxLogicalTop = parentInlineBox.logicalTop() + parentInlineBox.baseline() - inlineLevelBox->baseline();
    367             break;
    368         case VerticalAlign::TextTop:
    369             inlineLevelBoxLogicalTop = parentInlineBox.logicalTop() + parentInlineBox.baseline() - parentInlineBox.fontMetrics().ascent();
    370             break;
    371         case VerticalAlign::TextBottom: {
    372             auto parentTextLogicalBottom = parentInlineBox.logicalTop() + parentInlineBox.baseline() + parentInlineBox.fontMetrics().descent();
    373             inlineLevelBoxLogicalTop = parentTextLogicalBottom - inlineLevelBox->logicalHeight();
    374             break;
    375         }
    376         case VerticalAlign::Top:
    377             inlineLevelBoxLogicalTop = InlineLayoutUnit { };
    378             break;
    379         case VerticalAlign::Bottom:
    380             inlineLevelBoxLogicalTop = contentLogicalHeight - inlineLevelBox->logicalHeight();
    381             break;
    382         case VerticalAlign::Middle:
    383             inlineLevelBoxLogicalTop = parentInlineBox.logicalTop() + parentInlineBox.baseline() - (inlineLevelBox->logicalHeight() / 2 + parentInlineBox.fontMetrics().xHeight() / 2);
    384             break;
    385         default:
    386             ASSERT_NOT_IMPLEMENTED_YET();
    387             break;
    388         }
    389         inlineLevelBox->setLogicalTop(inlineLevelBoxLogicalTop);
    390     }
    391     if (!lineBox.isLineVisuallyEmpty())
    392         lineBox.setLogicalHeight(contentLogicalHeight);
     388            inlineLevelBox->setLogicalTop(logicalTop);
     389            if (rootInlineBoxOffset > 0)
     390                rootInlineBox.setLogicalTop(rootInlineBox.logicalTop() + rootInlineBoxOffset);
     391        }
     392    };
     393    alignLineBoxRelativeInlineLevelBoxes();
     394    lineBox.setLogicalHeight(lineBoxLogicalHeight);
    393395}
    394396
     
    398400}
    399401
    400 InlineFormattingContext::Geometry::LineRectAndLineBoxOffset InlineFormattingContext::Geometry::computedLineLogicalRect(const LineBox& lineBox, const RenderStyle& rootStyle, const LineBuilder::LineContent& lineContent) const
    401 {
    402     // Compute the line height and the line box vertical offset.
    403     // The line height is either the line-height value (negative value means line height is not set) or the font metrics's line spacing/line box height.
    404     // The line box is then positioned using the half leading centering.
    405     //   ___________________________________________  line
    406     // |                    ^                       |
    407     // |                    | line spacing          |
    408     // |                    v                       |
    409     // | -------------------------------------------|---------  LineBox
    410     // ||    ^                                      |         |
    411     // ||    | line box height                      |         |
    412     // ||----v--------------------------------------|-------- | alignment baseline
    413     // | -------------------------------------------|---------
    414     // |                    ^                       |   ^
    415     // |                    | line spacing          |   |
    416     // |____________________v_______________________|  scrollable overflow
    417     //
     402InlineFormattingContext::Geometry::LineRectAndLineBoxOffset InlineFormattingContext::Geometry::computedLineLogicalRect(const LineBox& lineBox, const RenderStyle&, const LineBuilder::LineContent& lineContent) const
     403{
    418404    if (lineContent.runs.isEmpty() || lineBox.isLineVisuallyEmpty())
    419405        return { { }, { lineContent.logicalTopLeft, lineContent.lineLogicalWidth, { } } };
    420406
    421407    auto lineBoxLogicalHeight = lineBox.logicalHeight();
    422     auto lineLogicalHeight = InlineLayoutUnit { };
    423     if (rootStyle.lineHeight().isNegative()) {
    424         // Negative line height value means the line height is driven by the content.
    425         auto usedLineSpacing = [&] {
    426             auto logicalTopWithLineSpacing = InlineLayoutUnit { };
    427             auto logicalBottomWithLineSpacing = lineBoxLogicalHeight;
    428             for (auto& inlineLevelBox : lineBox.inlineLevelBoxList()) {
    429                 if (auto lineSpacing = inlineLevelBox->lineSpacing()) {
    430                     // FIXME: check if line spacing is distributed evenly.
    431                     logicalTopWithLineSpacing = std::min(logicalTopWithLineSpacing, inlineLevelBox->logicalTop() - *lineSpacing / 2);
    432                     logicalBottomWithLineSpacing = std::max(logicalBottomWithLineSpacing, inlineLevelBox->logicalBottom() + *lineSpacing / 2);
    433                 }
    434             }
    435             return -logicalTopWithLineSpacing + (logicalBottomWithLineSpacing - lineBoxLogicalHeight);
    436         };
    437         lineLogicalHeight = lineBox.logicalHeight() + usedLineSpacing();
    438     } else
    439         lineLogicalHeight = rootStyle.computedLineHeight();
    440 
     408    auto lineLogicalHeight = lineBox.logicalHeight();
    441409    auto logicalRect = InlineRect { lineContent.logicalTopLeft, lineContent.lineLogicalWidth, lineLogicalHeight};
    442410    // Inline tree height is all integer based.
  • trunk/Source/WebCore/layout/inlineformatting/InlineLineBox.cpp

    r268399 r268642  
    4040}
    4141
     42bool LineBox::InlineLevelBox::hasLineBoxRelativeAlignment() const
     43{
     44    auto verticalAlignment = layoutBox().style().verticalAlign();
     45    return verticalAlignment == VerticalAlign::Top || verticalAlignment == VerticalAlign::Bottom;
     46}
     47
    4248LineBox::LineBox(InlineLayoutUnit contentLogicalWidth, IsLineVisuallyEmpty isLineVisuallyEmpty)
    4349    : m_logicalSize(contentLogicalWidth, { })
     
    6167{
    6268    ASSERT(run.isText() || run.isLineBreak());
    63     auto& parentInlineBox = inlineLevelBoxForLayoutBox(run.layoutBox().parent());
    64     ASSERT(parentInlineBox.isInlineBox());
    65     auto& fontMetrics = parentInlineBox.fontMetrics();
    66     auto runlogicalTop = parentInlineBox.logicalTop() + parentInlineBox.baseline() - fontMetrics.ascent();
     69    auto* parentInlineBox = &inlineLevelBoxForLayoutBox(run.layoutBox().parent());
     70    ASSERT(parentInlineBox->isInlineBox());
     71    auto& fontMetrics = parentInlineBox->fontMetrics();
     72    auto runlogicalTop = parentInlineBox->logicalTop() + parentInlineBox->baseline() - fontMetrics.ascent();
     73
     74    while (parentInlineBox != m_rootInlineBox.get() && !parentInlineBox->hasLineBoxRelativeAlignment()) {
     75        parentInlineBox = &inlineLevelBoxForLayoutBox(parentInlineBox->layoutBox().parent());
     76        ASSERT(parentInlineBox->isInlineBox());
     77        runlogicalTop += parentInlineBox->logicalTop();
     78    }
    6779    InlineLayoutUnit logicalHeight = fontMetrics.height();
    6880    return { runlogicalTop, m_horizontalAlignmentOffset.valueOr(InlineLayoutUnit { }) + run.logicalLeft(), run.logicalWidth(), logicalHeight };
     81}
     82
     83InlineRect LineBox::logicalRectForInlineLevelBox(const Box& layoutBox) const
     84{
     85    auto* inlineBox = &inlineLevelBoxForLayoutBox(layoutBox);
     86    auto inlineBoxLogicalRect = inlineBox->logicalRect();
     87    auto inlineBoxAbsolutelogicalTop = inlineBox->logicalTop();
     88
     89    while (inlineBox != m_rootInlineBox.get() && !inlineBox->hasLineBoxRelativeAlignment()) {
     90        inlineBox = &inlineLevelBoxForLayoutBox(inlineBox->layoutBox().parent());
     91        ASSERT(inlineBox->isInlineBox());
     92        inlineBoxAbsolutelogicalTop += inlineBox->logicalTop();
     93    }
     94    return { inlineBoxAbsolutelogicalTop, m_horizontalAlignmentOffset.valueOr(InlineLayoutUnit { }) + inlineBoxLogicalRect.left(), inlineBoxLogicalRect.width(), inlineBoxLogicalRect.height() };
    6995}
    7096
  • trunk/Source/WebCore/layout/inlineformatting/InlineLineBox.h

    r268399 r268642  
    8080        void setIsNonEmpty() { m_isEmpty = false; }
    8181
    82         Optional<InlineLayoutUnit> lineSpacing() const { return m_lineSpacing; }
    8382        const FontMetrics& fontMetrics() const { return layoutBox().style().fontMetrics(); }
    8483        const Box& layoutBox() const { return *m_layoutBox; }
     
    8685        bool isInlineBox() const { return m_type == Type::InlineBox || m_type == Type::RootInlineBox; }
    8786        bool isLineBreakBox() const { return m_type == Type::LineBreakBox; }
     87        bool hasLineBoxRelativeAlignment() const;
    8888
    8989        enum class Type {
     
    105105        void setBaseline(InlineLayoutUnit baseline) { m_baseline = baseline; }
    106106        void setDescent(InlineLayoutUnit descent) { m_descent = descent; }
    107         void setLineSpacing(InlineLayoutUnit lineSpacing) { m_lineSpacing = lineSpacing; }
     107
     108        // See https://www.w3.org/TR/css-inline-3/#layout-bounds
     109        struct LayoutBounds {
     110            InlineLayoutUnit height() const { return ascent + descent; }
     111
     112            InlineLayoutUnit ascent { 0 };
     113            InlineLayoutUnit descent { 0 };
     114        };
     115        void setLayoutBounds(const LayoutBounds& layoutBounds) { m_layoutBounds = layoutBounds; }
     116        LayoutBounds layoutBounds() const { return m_layoutBounds; }
    108117
    109118    private:
    110119        WeakPtr<const Box> m_layoutBox;
    111120        InlineRect m_logicalRect;
     121        LayoutBounds m_layoutBounds;
    112122        InlineLayoutUnit m_baseline { 0 };
    113123        Optional<InlineLayoutUnit> m_descent;
    114         Optional<InlineLayoutUnit> m_lineSpacing;
    115124        bool m_isEmpty { true };
    116125        Type m_type { Type::InlineBox };
     
    128137
    129138    const InlineLevelBox& inlineLevelBoxForLayoutBox(const Box& layoutBox) const { return *m_inlineLevelBoxRectMap.get(&layoutBox); }
     139
    130140    InlineRect logicalRectForTextRun(const Line::Run&) const;
     141    InlineRect logicalRectForInlineLevelBox(const Box&) const;
     142
    131143    auto inlineLevelBoxList() const { return m_inlineLevelBoxRectMap.values(); }
    132144    bool containsInlineLevelBox(const Box& layoutBox) const { return m_inlineLevelBoxRectMap.contains(&layoutBox); }
Note: See TracChangeset for help on using the changeset viewer.