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

Changeset 267434 in webkit


Ignore:
Timestamp:
Sep 22, 2020, 1:07:19 PM (6 years ago)
Author:
Alan Bujtas
Message:

[LFC][IFC] Add support for multiline inline box geometry.
https://bugs.webkit.org/show_bug.cgi?id=216835

Reviewed by Antti Koivisto.

This patch computes the geometry for inline boxes spanning multiple lines (e.g. <span>first line<br>next line</span).
However this is not the getBoundingClientRect() type of geometry where we provide geometry for each fragments. This is more like the
element.offset* geometry where we compute the enclosing rectangle for all the fragments.

  • layout/inlineformatting/InlineFormattingContext.cpp:

(WebCore::Layout::InlineFormattingContext::layoutInFlowContent): Decouple the run construction and inline box geometry update logic,
where we loop through the runs and create "line runs" when needed and then we loop through the inline boxes on the current line and
update the box geometries.

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

  • layout/inlineformatting/InlineFormattingState.h:

(WebCore::Layout::InlineFormattingState::lineBoxes const):
(WebCore::Layout::InlineFormattingState::addLineBox):
(WebCore::Layout::InlineFormattingState::clearLineAndRuns):
(WebCore::Layout::InlineFormattingState::shrinkToFit):

  • layout/inlineformatting/InlineLineBox.h:

(WebCore::Layout::LineBox::containsInlineLevelBox const):

Location:
trunk/Source/WebCore
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/ChangeLog

    r267432 r267434  
     12020-09-22  Zalan Bujtas  <zalan@apple.com>
     2
     3        [LFC][IFC] Add support for multiline inline box geometry.
     4        https://bugs.webkit.org/show_bug.cgi?id=216835
     5
     6        Reviewed by Antti Koivisto.
     7
     8        This patch computes the geometry for inline boxes spanning multiple lines (e.g. <span>first line<br>next line</span).
     9        However this is not the getBoundingClientRect() type of geometry where we provide geometry for each fragments. This is more like the
     10        element.offset* geometry where we compute the enclosing rectangle for all the fragments.
     11
     12        * layout/inlineformatting/InlineFormattingContext.cpp:
     13        (WebCore::Layout::InlineFormattingContext::layoutInFlowContent): Decouple the run construction and inline box geometry update logic,
     14        where we loop through the runs and create "line runs" when needed and then we loop through the inline boxes on the current line and
     15        update the box geometries.
     16 
     17        (WebCore::Layout::InlineFormattingContext::computeGeometryForLineContent):
     18        * layout/inlineformatting/InlineFormattingState.h:
     19        (WebCore::Layout::InlineFormattingState::lineBoxes const):
     20        (WebCore::Layout::InlineFormattingState::addLineBox):
     21        (WebCore::Layout::InlineFormattingState::clearLineAndRuns):
     22        (WebCore::Layout::InlineFormattingState::shrinkToFit):
     23        * layout/inlineformatting/InlineLineBox.h:
     24        (WebCore::Layout::LineBox::containsInlineLevelBox const):
     25
    1262020-09-22  Chris Dumez  <cdumez@apple.com>
    227
  • trunk/Source/WebCore/layout/inlineformatting/InlineFormattingContext.cpp

    r267325 r267434  
    114114            }
    115115        } else if (layoutBox->isInlineBox()) {
    116             // Text wrapper boxes (anonymous inline level boxes) and <br>s don't generate display boxes (only display runs).
     116            // Text wrapper boxes (anonymous inline level boxes) and <br>s don't have box geometries (they only generate runs).
    117117            if (!layoutBox->isInlineTextBox() && !layoutBox->isLineBreakBox()) {
    118118                // Inline boxes (<span>) can't get sized/positioned yet. At this point we can only compute their margins, borders and padding.
     
    397397    auto& formattingState = this->formattingState();
    398398    auto geometry = this->geometry();
    399     const auto lineBox = geometry.lineBoxForLineContent(lineContent);
     399
     400    formattingState.addLineBox(geometry.lineBoxForLineContent(lineContent));
     401    const auto& lineBox = formattingState.lineBoxes().last();
     402
    400403    auto lineRectAndLineBoxOffset = geometry.computedLineLogicalRect(lineBox, root().style(), lineContent);
    401404    auto lineLogicalRect = lineRectAndLineBoxOffset.logicalRect;
    402405    auto lineBoxVerticalOffset = lineRectAndLineBoxOffset.lineBoxVerticalOffset;
    403     auto lineIndex = formattingState.lines().size();
    404 
    405     auto constructLineGeometry = [&] {
    406         auto lineBoxLogicalRect = InlineRect { lineLogicalRect.top() + lineBoxVerticalOffset, lineLogicalRect.left(), lineBox.logicalWidth(), lineBox.logicalHeight() };
    407         formattingState.addLine({ lineLogicalRect, lineBoxLogicalRect, lineBoxVerticalOffset + lineBox.alignmentBaseline(), lineBox.horizontalAlignmentOffset().valueOr(InlineLayoutUnit { }) });
    408     };
    409     constructLineGeometry();
    410 
    411     if (!lineContent.floats.isEmpty()) {
     406
     407    auto updateFloatGeometry = [&] {
     408        if (lineContent.floats.isEmpty())
     409            return;
    412410        auto floatingContext = FloatingContext { root(), *this, formattingState.floatingState() };
    413411        // Move floats to their final position.
     
    422420            floatingContext.append(floatBox);
    423421        }
    424     }
    425 
    426     for (auto& lineRun : lineContent.runs) {
    427         auto& layoutBox = lineRun.layoutBox();
    428         // Inline level containers (<span>) don't generate display runs and neither do completely collapsed runs.
    429         auto initiatesInlineRun = lineRun.isText() || lineRun.isLineBreak() || lineRun.isBox();
    430         if (initiatesInlineRun) {
    431             auto logicalRect = lineRun.isBox() ? lineBox.inlineBoxForLayoutBox(layoutBox).logicalRect() : lineBox.logicalRectForTextRun(lineRun);
    432             formattingState.addLineRun({ lineIndex, layoutBox, logicalRect, lineRun.expansion(), lineRun.textContent() });
    433         }
    434 
    435         // FIXME: Since <br> and <wbr> runs have associated DOM elements, we might need to construct a display box here.
    436         auto needsBoxGeometry = lineRun.isBox() || lineRun.isContainerStart();
    437         if (needsBoxGeometry) {
     422    };
     423    updateFloatGeometry();
     424
     425    auto constructLineRuns = [&] {
     426        auto lineIndex = formattingState.lines().size();
     427        // Create the inline runs on the current line. This is mostly text and atomic inline runs.
     428        for (auto& lineRun : lineContent.runs) {
     429            if (lineRun.isText() || lineRun.isLineBreak())
     430                formattingState.addLineRun({ lineIndex, lineRun.layoutBox(), lineBox.logicalRectForTextRun(lineRun), lineRun.expansion(), lineRun.textContent() });
     431            else if (lineRun.isBox())
     432                formattingState.addLineRun({ lineIndex, lineRun.layoutBox(), lineBox.inlineBoxForLayoutBox(lineRun.layoutBox()).logicalRect(), lineRun.expansion(), { } });
     433        }
     434    };
     435    constructLineRuns();
     436
     437    auto updateBoxGeometry = [&] {
     438        // Grab the inline boxes (even those that don't have associated layout boxes on the current line due to line wrapping)
     439        // and update their geometries.
     440        for (auto& inlineBox : lineBox.inlineBoxList()) {
     441            auto& layoutBox = inlineBox->layoutBox();
     442            if (&layoutBox == &root()) {
     443                // Ignore root inline box.
     444                continue;
     445            }
    438446            auto& boxGeometry = formattingState.boxGeometry(layoutBox);
    439             auto& inlineBox = lineBox.inlineBoxForLayoutBox(layoutBox);
    440             auto topLeft = inlineBox.logicalRect().topLeft();
    441             topLeft.move({ }, lineBoxVerticalOffset);
     447            auto logicalTopLeft = inlineBox->logicalRect().topLeft();
     448
     449            logicalTopLeft.move({ }, lineBoxVerticalOffset);
    442450            if (layoutBox.isInFlowPositioned())
    443                 topLeft += geometry.inFlowPositionedPositionOffset(layoutBox, horizontalConstraints);
    444             boxGeometry.setLogicalTopLeft(toLayoutPoint(topLeft));
    445             if (lineRun.isContainerStart()) {
    446                 auto marginBoxWidth = inlineBox.logicalWidth();
    447                 auto contentBoxWidth = marginBoxWidth - (boxGeometry.marginStart() + boxGeometry.borderLeft() + boxGeometry.paddingLeft().valueOr(0));
    448                 // FIXME: Fix it for multiline.
     451                logicalTopLeft += geometry.inFlowPositionedPositionOffset(layoutBox, horizontalConstraints);
     452
     453            if (layoutBox.isAtomicInlineLevelBox()) {
     454                // Atomic inline boxes are all set. Their margin/border/content box geometries are already computed. We just have to position them here.
     455                boxGeometry.setLogicalTopLeft(toLayoutPoint(logicalTopLeft));
     456                continue;
     457            }
     458            auto marginBoxWidth = inlineBox->logicalWidth();
     459            auto contentBoxWidth = marginBoxWidth - (boxGeometry.marginStart() + boxGeometry.borderLeft() + boxGeometry.paddingLeft().valueOr(0));
     460            // Non-atomic inline level boxes may or may not be wrapped and have geometries on multiple lines.
     461            int previousLineIndex = formattingState.lineBoxes().size() - 2;
     462            auto isSpanningInlineBox = previousLineIndex > 0 && formattingState.lineBoxes()[previousLineIndex].containsInlineLevelBox(layoutBox);
     463            if (!isSpanningInlineBox) {
     464                // This box showed up on this line the first time.
     465                boxGeometry.setLogicalTopLeft(toLayoutPoint(logicalTopLeft));
    449466                boxGeometry.setContentBoxWidth(toLayoutUnit(contentBoxWidth));
    450                 boxGeometry.setContentBoxHeight(toLayoutUnit(inlineBox.logicalHeight()));
    451             }
    452         }
    453     }
     467                boxGeometry.setContentBoxHeight(toLayoutUnit(inlineBox->logicalHeight()));
     468                continue;
     469            }
     470            // This is a just a simple box geometry for the line spanning inline box. getBoundingClientRect looks into each line boxes (will turn into fragmented boxes).
     471            boxGeometry.setLogicalLeft(std::min(boxGeometry.logicalLeft(), toLayoutUnit(logicalTopLeft.x())));
     472            boxGeometry.setContentBoxWidth(std::max(toLayoutUnit(contentBoxWidth), boxGeometry.contentBoxWidth()));
     473            boxGeometry.setContentBoxHeight(boxGeometry.contentBoxHeight() + toLayoutUnit(inlineBox->logicalHeight()));
     474        }
     475    };
     476    updateBoxGeometry();
     477
     478    auto constructLineGeometry = [&] {
     479        auto lineBoxLogicalRect = InlineRect { lineLogicalRect.top() + lineBoxVerticalOffset, lineLogicalRect.left(), lineBox.logicalWidth(), lineBox.logicalHeight() };
     480        formattingState.addLine({ lineLogicalRect, lineBoxLogicalRect, lineBoxVerticalOffset + lineBox.alignmentBaseline(), lineBox.horizontalAlignmentOffset().valueOr(InlineLayoutUnit { }) });
     481    };
     482    constructLineGeometry();
    454483    return lineLogicalRect;
    455484}
  • trunk/Source/WebCore/layout/inlineformatting/InlineFormattingState.h

    r267325 r267434  
    3030#include "FormattingState.h"
    3131#include "InlineItem.h"
     32#include "InlineLineBox.h"
    3233#include "InlineLineGeometry.h"
    3334#include "InlineLineRun.h"
     
    3940using InlineItems = Vector<InlineItem>;
    4041using InlineLines = Vector<InlineLineGeometry>;
     42using InlineLineBoxes = Vector<LineBox>;
    4143using InlineLineRuns = Vector<LineRun>;
    4244
     
    5658    void addLine(const InlineLineGeometry& line) { m_lines.append(line); }
    5759
     60    const InlineLineBoxes& lineBoxes() const { return m_lineBoxes; }
     61    void addLineBox(LineBox&& lineBox) { m_lineBoxes.append(WTFMove(lineBox)); }
     62
    5863    const InlineLineRuns& lineRuns() const { return m_lineRuns; }
    5964    InlineLineRuns& lineRuns() { return m_lineRuns; }
     
    6772    InlineItems m_inlineItems;
    6873    InlineLines m_lines;
     74    InlineLineBoxes m_lineBoxes;
    6975    InlineLineRuns m_lineRuns;
    7076};
     
    7379{
    7480    m_lines.clear();
     81    m_lineBoxes.clear();
    7582    m_lineRuns.clear();
    7683}
     
    7986{
    8087    m_lines.shrinkToFit();
     88    m_lineBoxes.shrinkToFit();
    8189    m_lineRuns.shrinkToFit();
    8290}
  • trunk/Source/WebCore/layout/inlineformatting/InlineLineBox.h

    r267234 r267434  
    112112    const InlineBox& inlineBoxForLayoutBox(const Box& layoutBox) const { return *m_inlineBoxRectMap.get(&layoutBox); }
    113113    InlineRect logicalRectForTextRun(const Line::Run&) const;
    114 
    115     using InlineBoxMap = HashMap<const Box*, InlineBox*>;
    116114    auto inlineBoxList() const { return m_inlineBoxRectMap.values(); }
     115    bool containsInlineLevelBox(const Box& layoutBox) const { return m_inlineBoxRectMap.contains(&layoutBox); }
    117116
    118117    InlineLayoutUnit alignmentBaseline() const { return m_rootInlineBox->logicalTop() + m_rootInlineBox->baseline(); }
     
    141140    InlineBoxList m_nonRootInlineBoxList;
    142141
    143     InlineBoxMap m_inlineBoxRectMap;
     142    HashMap<const Box*, InlineBox*> m_inlineBoxRectMap;
    144143};
    145144
Note: See TracChangeset for help on using the changeset viewer.