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

Changeset 286784 in webkit


Ignore:
Timestamp:
Dec 9, 2021, 9:26:58 AM (5 years ago)
Author:
Alan Bujtas
Message:

[LFC][IFC] Refactor bidi inline box boundary handling
https://bugs.webkit.org/show_bug.cgi?id=233880

Reviewed by Antti Koivisto.

This patch remove some code complexity by introducing more suitable data types to handle
content ranges for inline boxes.

  1. The final output of the InlineDisplayContentBuilder is a list of display boxes. It's a simple "one box right after the other" type of setup with continuous geometry (ie next box starts where the previous ends +-horizontal margins) unless the content is embedded in an inline box (e.g. <span>embedded content</span>) Inline box type of display boxes enclose all their descendant content. Normally when the logical order == visual order (unidirectional content), the descendant content ends right before the "inline box end" mark (</span>). It makes it easy to compute the right edge/enclosing width for the inline box type of display boxes.
  2. However the visual order of the bidi content makes the "let's use the inline box end mark" logic unusable as we may see the "inline box end" mark even before we come across any descendant content (there are other types of confusions with random ordering).
  3. Working with a list when the content comes in a nested flavor may introduce some complexity (e.g. need to keep content ranges around).

This patch introduces a very simple DisplayBoxNode type which keeps track of the display boxes in a tree fashion.
The display boxes are still stored in a list, while this newly introduced DisplayBoxNode tree helps traversing
the nested content.
It helps finding out the insertion point at unexpected bidi boundaries (e.g. when nested content from other inline boxes
show up in a logically unrelated inline box) and it is also very valuable when computing visual geometries for those enclosing display boxes.

  • layout/formattingContexts/inline/InlineDisplayContentBuilder.cpp:

(WebCore::Layout::DisplayBoxNode::DisplayBoxNode):
(WebCore::Layout::DisplayBoxNode::appendChild):
(WebCore::Layout::AncestorStack::unwind):
(WebCore::Layout::AncestorStack::push):
(WebCore::Layout::InlineDisplayContentBuilder::ensureDisplayBoxForContainer):
(WebCore::Layout::InlineDisplayContentBuilder::adjustVisualGeometryForChildNode):
(WebCore::Layout::InlineDisplayContentBuilder::insertInlineBoxDisplayBoxForBidiBoundary): Deleted.
(WebCore::Layout::InlineDisplayContentBuilder::adjustInlineBoxDisplayBoxForBidiBoundary): Deleted.
(WebCore::Layout::InlineDisplayContentBuilder::processBidiContent): Deleted.
(WebCore::Layout::InlineDisplayContentBuilder::processOverflownRunsForEllipsis): Deleted.
(WebCore::Layout::InlineDisplayContentBuilder::collectInkOverflowForInlineBoxes): Deleted.
(WebCore::Layout::InlineDisplayContentBuilder::computeIsFirstIsLastBoxForInlineContent): Deleted.

  • layout/formattingContexts/inline/InlineDisplayContentBuilder.h:
  • layout/formattingContexts/inline/display/InlineDisplayBox.h:

(WebCore::InlineDisplay::Box::setLogicalLeft):
(WebCore::InlineDisplay::Box::setLogicalRight):
(WebCore::InlineDisplay::Box::setLogicalRect):
(WebCore::InlineDisplay::Box::setHasContent):

Location:
trunk/Source/WebCore
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/ChangeLog

    r286782 r286784  
     12021-12-09  Alan Bujtas  <zalan@apple.com>
     2
     3        [LFC][IFC] Refactor bidi inline box boundary handling
     4        https://bugs.webkit.org/show_bug.cgi?id=233880
     5
     6        Reviewed by Antti Koivisto.
     7
     8        This patch remove some code complexity by introducing more suitable data types to handle
     9        content ranges for inline boxes.
     10
     11        1. The final output of the InlineDisplayContentBuilder is a list of display boxes.
     12          It's a simple "one box right after the other" type of setup with continuous geometry (ie next box starts where
     13          the previous ends +-horizontal margins) unless the content is embedded in an inline box (e.g. <span>embedded content</span>)
     14          Inline box type of display boxes enclose all their descendant content.
     15          Normally when the logical order == visual order (unidirectional content), the
     16          descendant content ends right before the "inline box end" mark (</span>).
     17          It makes it easy to compute the right edge/enclosing width for the inline box type of display boxes.
     18        2. However the visual order of the bidi content makes the "let's use the inline box end mark" logic unusable as
     19          we may see the "inline box end" mark even before we come across any descendant content
     20          (there are other types of confusions with random ordering).
     21        3. Working with a list when the content comes in a nested flavor may introduce some complexity (e.g. need to keep content ranges around).
     22
     23        This patch introduces a very simple DisplayBoxNode type which keeps track of the display boxes in a tree fashion.
     24        The display boxes are still stored in a list, while this newly introduced DisplayBoxNode tree helps traversing
     25        the nested content.
     26        It helps finding out the insertion point at unexpected bidi boundaries (e.g. when nested content from other inline boxes
     27        show up in a logically unrelated inline box) and it is also very valuable when computing visual geometries for those enclosing display boxes.
     28
     29        * layout/formattingContexts/inline/InlineDisplayContentBuilder.cpp:
     30        (WebCore::Layout::DisplayBoxNode::DisplayBoxNode):
     31        (WebCore::Layout::DisplayBoxNode::appendChild):
     32        (WebCore::Layout::AncestorStack::unwind):
     33        (WebCore::Layout::AncestorStack::push):
     34        (WebCore::Layout::InlineDisplayContentBuilder::ensureDisplayBoxForContainer):
     35        (WebCore::Layout::InlineDisplayContentBuilder::adjustVisualGeometryForChildNode):
     36        (WebCore::Layout::InlineDisplayContentBuilder::insertInlineBoxDisplayBoxForBidiBoundary): Deleted.
     37        (WebCore::Layout::InlineDisplayContentBuilder::adjustInlineBoxDisplayBoxForBidiBoundary): Deleted.
     38        (WebCore::Layout::InlineDisplayContentBuilder::processBidiContent): Deleted.
     39        (WebCore::Layout::InlineDisplayContentBuilder::processOverflownRunsForEllipsis): Deleted.
     40        (WebCore::Layout::InlineDisplayContentBuilder::collectInkOverflowForInlineBoxes): Deleted.
     41        (WebCore::Layout::InlineDisplayContentBuilder::computeIsFirstIsLastBoxForInlineContent): Deleted.
     42        * layout/formattingContexts/inline/InlineDisplayContentBuilder.h:
     43        * layout/formattingContexts/inline/display/InlineDisplayBox.h:
     44        (WebCore::InlineDisplay::Box::setLogicalLeft):
     45        (WebCore::InlineDisplay::Box::setLogicalRight):
     46        (WebCore::InlineDisplay::Box::setLogicalRect):
     47        (WebCore::InlineDisplay::Box::setHasContent):
     48
    1492021-12-09  Alan Bujtas  <zalan@apple.com>
    250
  • trunk/Source/WebCore/layout/formattingContexts/inline/InlineDisplayContentBuilder.cpp

    r286782 r286784  
    267267}
    268268
    269 void InlineDisplayContentBuilder::insertInlineBoxDisplayBoxForBidiBoundary(const InlineLevelBox& inlineBox, const InlineRect& inlineBoxRect, bool isFirstInlineBoxFragment, size_t insertionPoint, DisplayBoxes& boxes)
    270 {
    271     ASSERT(inlineBox.isInlineBox());
    272 
    273     auto isFirstLastBox = OptionSet<InlineDisplay::Box::PositionWithinInlineLevelBox> { };
    274     if (inlineBox.isFirstBox() && isFirstInlineBoxFragment)
    275         isFirstLastBox.add({ InlineDisplay::Box::PositionWithinInlineLevelBox::First });
    276     if (inlineBox.isLastBox())
    277         isFirstLastBox.add({ InlineDisplay::Box::PositionWithinInlineLevelBox::Last });
    278 
    279     // FIXME: Compute ink overflow.
    280     boxes.insert(insertionPoint, { m_lineIndex
    281         , InlineDisplay::Box::Type::NonRootInlineBox
    282         , inlineBox.layoutBox()
    283         , UBIDI_DEFAULT_LTR
    284         , inlineBoxRect
    285         , inlineBoxRect
    286         , { }
    287         , { }
    288         , true
    289         , isFirstLastBox });
    290 }
    291 
    292 void InlineDisplayContentBuilder::adjustInlineBoxDisplayBoxForBidiBoundary(InlineDisplay::Box& displayBox, const InlineRect& inlineBoxRect)
    293 {
    294     UNUSED_PARAM(displayBox);
    295     UNUSED_PARAM(inlineBoxRect);
    296 }
    297 
    298269void InlineDisplayContentBuilder::processNonBidiContent(const LineBuilder::LineContent& lineContent, const LineBox& lineBox, const InlineLayoutPoint& lineBoxLogicalTopLeft, DisplayBoxes& boxes)
    299270{
     
    350321}
    351322
     323struct DisplayBoxNode {
     324    WTF_MAKE_STRUCT_FAST_ALLOCATED;
     325    DisplayBoxNode() = default;
     326    DisplayBoxNode(size_t index, DisplayBoxNode* parent)
     327        : index(index)
     328        , parent(parent)
     329        {
     330        }
     331
     332    void appendChild(size_t childIndex) { children.append(makeUnique<DisplayBoxNode>(childIndex, this)); }
     333
     334    size_t index { 0 };
     335    DisplayBoxNode* parent { nullptr };
     336    Vector<std::unique_ptr<DisplayBoxNode>> children;
     337};
     338
     339struct AncestorStack {
     340    DisplayBoxNode* unwind(const ContainerBox& containerBox)
     341    {
     342        // Unwind the stack all the way to container box.
     343        if (!m_set.contains(&containerBox))
     344            return nullptr;
     345        while (m_set.last() != &containerBox) {
     346            m_stack.removeLast();
     347            m_set.removeLast();
     348        }
     349        // Root is always a common ancestor.
     350        ASSERT(!m_stack.isEmpty());
     351        return m_stack.last();
     352    }
     353
     354    void push(DisplayBoxNode& displayBoxNode, const ContainerBox& containerBox)
     355    {
     356        m_stack.append(&displayBoxNode);
     357        ASSERT(!m_set.contains(&containerBox));
     358        m_set.add(&containerBox);
     359    }
     360
     361private:
     362    Vector<DisplayBoxNode*> m_stack;
     363    ListHashSet<const ContainerBox*> m_set;
     364};
     365
     366DisplayBoxNode& InlineDisplayContentBuilder::ensureDisplayBoxForContainer(const ContainerBox& containerBox, AncestorStack& ancestorStack, DisplayBoxes& boxes)
     367{
     368    ASSERT(containerBox.isInlineBox() || &containerBox == &root());
     369    if (auto* lowestCommonAncestor = ancestorStack.unwind(containerBox))
     370        return *lowestCommonAncestor;
     371    auto& enclosingDisplayBoxNodeForContainer = ensureDisplayBoxForContainer(containerBox.parent(), ancestorStack, boxes);
     372    boxes.append({ m_lineIndex, InlineDisplay::Box::Type::NonRootInlineBox, containerBox, UBIDI_DEFAULT_LTR, { }, { }, { }, { }, true, { } });
     373
     374    enclosingDisplayBoxNodeForContainer.appendChild(boxes.size() - 1);
     375    auto& displayBoxNodeForContainer = *enclosingDisplayBoxNodeForContainer.children.last();
     376    ancestorStack.push(displayBoxNodeForContainer, containerBox);
     377    return displayBoxNodeForContainer;
     378}
     379
     380void InlineDisplayContentBuilder::adjustVisualGeometryForChildNode(const DisplayBoxNode& displayBoxNode, InlineLayoutUnit& contentRightInVisualOrder, InlineLayoutUnit lineBoxLogicalTop, DisplayBoxes& boxes, const LineBox& lineBox)
     381{
     382    // Non-inline box display boxes just need a horizontal adjustment while
     383    // inline box type of display boxes need
     384    // 1. horizontal adjustment and margin/border/padding start offsetting on the first box
     385    // 2. right edge computation including descendant content width and margin/border/padding end offsetting on the last box
     386    ASSERT(displayBoxNode.index);
     387    auto& displayBox = boxes[displayBoxNode.index];
     388    auto& layoutBox = displayBox.layoutBox();
     389
     390    if (!displayBox.isNonRootInlineBox()) {
     391        displayBox.setLogicalLeft(contentRightInVisualOrder);
     392        contentRightInVisualOrder += displayBox.logicalWidth();
     393        if (displayBox.isAtomicInlineLevelBox() || displayBox.isGenericInlineLevelBox())
     394            contentRightInVisualOrder += formattingState().boxGeometry(layoutBox).marginEnd();
     395        return;
     396    }
     397
     398    auto& boxGeometry = formattingState().boxGeometry(layoutBox);
     399    auto beforeInlineBoxContent = [&] {
     400        auto logicalRect = lineBox.logicalBorderBoxForInlineBox(layoutBox, boxGeometry);
     401        auto visualRect = InlineRect { lineBoxLogicalTop + logicalRect.top(), contentRightInVisualOrder, { }, logicalRect.height() };
     402        // FIXME: Add support for ink overflow.
     403        if (!displayBox.isFirstForLayoutBox())
     404            return displayBox.setLogicalRect(visualRect, visualRect);
     405
     406        contentRightInVisualOrder += boxGeometry.marginStart();
     407        auto visualRectWithMarginStart = InlineRect { visualRect.top(), contentRightInVisualOrder, visualRect.width(), visualRect.height() };
     408        displayBox.setLogicalRect(visualRectWithMarginStart, visualRectWithMarginStart);
     409        contentRightInVisualOrder += boxGeometry.borderAndPaddingStart();
     410    };
     411    beforeInlineBoxContent();
     412
     413    for (auto& childDisplayBoxNode : displayBoxNode.children)
     414        adjustVisualGeometryForChildNode(*childDisplayBoxNode, contentRightInVisualOrder, lineBoxLogicalTop, boxes, lineBox);
     415
     416    auto afterInlineBoxContent = [&] {
     417        if (!displayBox.isLastForLayoutBox())
     418            return displayBox.setLogicalRight(contentRightInVisualOrder);
     419
     420        contentRightInVisualOrder += boxGeometry.borderAndPaddingEnd();
     421        displayBox.setLogicalRight(contentRightInVisualOrder);
     422        contentRightInVisualOrder += boxGeometry.marginEnd();
     423    };
     424    afterInlineBoxContent();
     425
     426    setInlineBoxGeometry(layoutBox, displayBox.logicalRect(), displayBox.isFirstForLayoutBox());
     427    if (lineBox.inlineLevelBoxForLayoutBox(layoutBox).hasContent())
     428        displayBox.setHasContent();
     429}
     430
    352431void InlineDisplayContentBuilder::processBidiContent(const LineBuilder::LineContent& lineContent, const LineBox& lineBox, const InlineLayoutPoint& lineBoxLogicalTopLeft, DisplayBoxes& boxes)
    353432{
    354433    ASSERT(lineContent.visualOrderList.size() == lineContent.runs.size());
    355434
    356     Vector<Range<size_t>> inlineBoxRangeList;
    357     auto needsNonRootInlineBoxDisplayBox = false;
    358     auto createDisplayBoxesInVisualOrderForContentRuns = [&] {
     435    AncestorStack ancestorStack;
     436    DisplayBoxNode rootDisplayBoxNode = { };
     437    ancestorStack.push(rootDisplayBoxNode, root());
     438
     439    auto contentStartInVisualOrder = InlineLayoutUnit { };
     440    auto createDisplayBoxesInVisualOrder = [&] {
    359441        auto rootInlineBoxRect = lineBox.logicalRectForRootInlineBox();
    360         auto contentRightInVisualOrder = InlineLayoutUnit { };
    361442        // First visual run's initial content position depends on the block's inline direction.
    362443        if (!root().style().isLeftToRightDirection()) {
    363444            // FIXME: This needs the block end position instead of the lineLogicalWidth.
    364             contentRightInVisualOrder += lineContent.lineLogicalWidth - rootInlineBoxRect.width();
     445            contentStartInVisualOrder += lineContent.lineLogicalWidth - rootInlineBoxRect.width();
    365446        }
    366447        // Adjust the content start position with the (text)alignment offset (root inline box has the alignment offset and not the individual runs).
    367         contentRightInVisualOrder += rootInlineBoxRect.left();
    368 
     448        contentStartInVisualOrder += rootInlineBoxRect.left();
     449
     450        auto contentRightInVisualOrder = contentStartInVisualOrder;
    369451        auto& runs = lineContent.runs;
    370452        for (size_t i = 0; i < runs.size(); ++i) {
    371453            auto visualIndex = lineContent.visualOrderList[i];
    372454            auto& lineRun = runs[visualIndex];
    373 
    374             auto isContentRun = !lineRun.isInlineBoxStart() && !lineRun.isLineSpanningInlineBoxStart() && !lineRun.isInlineBoxEnd();
     455            auto& layoutBox = lineRun.layoutBox();
     456
     457            auto isContentRun = !lineRun.isInlineBoxStart() && !lineRun.isLineSpanningInlineBoxStart() && !lineRun.isInlineBoxEnd() && !lineRun.isWordBreakOpportunity();
    375458            if (!isContentRun) {
    376                 needsNonRootInlineBoxDisplayBox = true;
     459                // FIXME: Add support for inline boxes with no content.
    377460                continue;
    378461            }
     
    384467            };
    385468
     469            auto& parentDisplayBoxNode = ensureDisplayBoxForContainer(layoutBox.parent(), ancestorStack, boxes);
    386470            if (lineRun.isText()) {
    387471                auto visualRect = visualRectRelativeToRoot(lineBox.logicalRectForTextRun(lineRun));
    388472                appendTextDisplayBox(lineRun, visualRect, boxes);
    389473                contentRightInVisualOrder += visualRect.width();
    390                 continue;
    391             }
    392             if (lineRun.isSoftLineBreak()) {
     474            } else if (lineRun.isSoftLineBreak()) {
    393475                ASSERT(!visualRectRelativeToRoot(lineBox.logicalRectForTextRun(lineRun)).width());
    394476                appendSoftLineBreakDisplayBox(lineRun, visualRectRelativeToRoot(lineBox.logicalRectForTextRun(lineRun)), boxes);
    395                 continue;
    396             }
    397             if (lineRun.isHardLineBreak()) {
    398                 ASSERT(!visualRectRelativeToRoot(lineBox.logicalRectForLineBreakBox(lineRun.layoutBox())).width());
    399                 appendHardLineBreakDisplayBox(lineRun, visualRectRelativeToRoot(lineBox.logicalRectForLineBreakBox(lineRun.layoutBox())), boxes);
    400                 continue;
    401             }
    402             if (lineRun.isBox()) {
    403                 auto& layoutBox = lineRun.layoutBox();
     477            } else if (lineRun.isHardLineBreak()) {
     478                ASSERT(!visualRectRelativeToRoot(lineBox.logicalRectForLineBreakBox(layoutBox)).width());
     479                appendHardLineBreakDisplayBox(lineRun, visualRectRelativeToRoot(lineBox.logicalRectForLineBreakBox(layoutBox)), boxes);
     480            } else if (lineRun.isBox()) {
    404481                auto& boxGeometry = formattingState().boxGeometry(layoutBox);
    405482                auto visualRect = visualRectRelativeToRoot(lineBox.logicalBorderBoxForAtomicInlineLevelBox(layoutBox, boxGeometry));
     
    407484                appendAtomicInlineLevelDisplayBox(lineRun, visualRect, boxes);
    408485                contentRightInVisualOrder += boxGeometry.marginStart() + visualRect.width() + boxGeometry.marginEnd();
    409                 continue;
    410486            }
    411             ASSERT(lineRun.isWordBreakOpportunity());
    412         }
    413     };
    414     createDisplayBoxesInVisualOrderForContentRuns();
    415 
    416     auto needsDisplayBoxHorizontalAdjustment = false;
    417     auto createDisplayBoxesInVisualOrderForInlineBoxes = [&] {
    418         // Visual order could introduce gaps and/or inject runs outside from the current inline box content.
    419         // In such cases, we need to "close" and "open" display boxes for these inline box fragments
    420         // to accommodate the current content.
    421         // We do it by finding the lowest common ancestor of the last and the current content display boxes and
    422         // traverse both ancestor chains and close/open the parent (inline) boxes.
    423         // (open here means to create a new display box, while close means to simply pop it out of parentBoxStack).
    424         // <div>a<span id=first>b&#8238;g</span>f<span id=second>e&#8237;c</span>d</div>
    425         // produces the following output (note the #8238; #8237; RTL/LTR control characters):
    426         // abcdefg
    427         // with the following, fragmented inline boxes:
    428         // a[first open]b[first close][second open]c[second close]d[second open]e[second close]f[first open]g[first close]
    429         HashMap<const Box*, size_t> inlineBoxDisplayBoxMap;
    430         ListHashSet<const Box*> parentBoxStack;
    431         parentBoxStack.add(&root());
    432 
    433         ASSERT(boxes[0].isRootInlineBox());
    434         for (size_t index = 1; index < boxes.size(); ++index) {
    435             auto& parentBox = boxes[index].layoutBox().parent();
    436             ASSERT(parentBox.isInlineBox() || &parentBox == &root());
    437 
    438             auto runParentIsCurrentInlineBox = &parentBox == parentBoxStack.last();
    439             if (runParentIsCurrentInlineBox) {
    440                 // We've got the correct inline box as parent. Nothing to do here.
    441                 continue;
     487            parentDisplayBoxNode.appendChild(boxes.size() - 1);
     488        }
     489    };
     490    createDisplayBoxesInVisualOrder();
     491
     492    if (!rootDisplayBoxNode.children.isEmpty()) {
     493        auto computeIsFirstIsLastBox = [&] {
     494            HashMap<const Box*, size_t> lastDisplayBoxIndexes;
     495            ASSERT(boxes[0].isRootInlineBox());
     496            for (size_t index = 1; index < boxes.size(); ++index) {
     497                auto& displayBox = boxes[index];
     498                if (!displayBox.isNonRootInlineBox())
     499                    continue;
     500                auto& layoutBox = displayBox.layoutBox();
     501                auto isFirstBoxOnCurrentLine = lastDisplayBoxIndexes.set(&layoutBox, index).isNewEntry;
     502                if (lineBox.inlineLevelBoxForLayoutBox(layoutBox).isFirstBox() && isFirstBoxOnCurrentLine)
     503                    displayBox.setIsFirstForLayoutBox(true);
    442504            }
    443             auto parentBoxStackEnd = parentBoxStack.end();
    444             Vector<const Box*> inlineBoxNeedingDisplayBoxList;
    445             for (auto* ancestor = &parentBox; ancestor; ancestor = &ancestor->parent()) {
    446                 ASSERT(ancestor == &root() || ancestor->isInlineBox());
    447                 auto parentBoxIterator = parentBoxStack.find(ancestor);
    448                 if (parentBoxIterator != parentBoxStackEnd) {
    449                     // This is the lowest common ancestor.
    450                     // Let's traverse both ancestor chains and create/close display boxes as needed.
    451                     Vector<const Box*> inlineBoxFragmentsToClose;
    452                     for (auto it = ++parentBoxIterator; it != parentBoxStackEnd; ++it)
    453                         inlineBoxFragmentsToClose.append(*it);
    454 
    455                     for (auto* inlineBox : makeReversedRange(inlineBoxFragmentsToClose)) {
    456                         ASSERT(inlineBox->isInlineBox());
    457                         ASSERT(inlineBoxDisplayBoxMap.contains(inlineBox));
    458                         inlineBoxRangeList.append({ inlineBoxDisplayBoxMap.get(inlineBox), index });
    459                         parentBoxStack.remove(inlineBox);
    460                     }
    461 
    462                     // Insert new display boxes for inline box fragments on bidi boundary.
    463                     for (auto* inlineBox : makeReversedRange(inlineBoxNeedingDisplayBoxList)) {
    464                         ASSERT(inlineBox->isInlineBox());
    465                         parentBoxStack.add(inlineBox);
    466 
    467                         auto createAndInsertDisplayBoxForInlineBoxFragment = [&] {
    468                             // Make sure that the "previous" display box for this particular inline box is not tracked as the "last box".
    469                             auto lastDisplayBoxForInlineBoxIndex = inlineBoxDisplayBoxMap.take(inlineBox);
    470                             auto isFirstFragment = !lastDisplayBoxForInlineBoxIndex;
    471                             if (!isFirstFragment)
    472                                 boxes[lastDisplayBoxForInlineBoxIndex].setIsLastForLayoutBox(false);
    473                             inlineBoxDisplayBoxMap.set(inlineBox, index);
    474 
    475                             auto& boxGeometry = formattingState().boxGeometry(*inlineBox);
    476                             auto visualRect = lineBox.logicalBorderBoxForInlineBox(*inlineBox, boxGeometry);
    477                             // Use the current content left as the starting point for this display box.
    478                             visualRect.setLeft(boxes[index].logicalLeft());
    479                             visualRect.moveVertically(lineBoxLogicalTopLeft.y());
    480                             // Visual width is not yet known.
    481                             visualRect.setWidth({ });
    482                             insertInlineBoxDisplayBoxForBidiBoundary(lineBox.inlineLevelBoxForLayoutBox(*inlineBox), visualRect, isFirstFragment, index, boxes);
    483                             ++index;
    484                             // Need to push the rest of the content when this inline box has margin/border/padding.
    485                             needsDisplayBoxHorizontalAdjustment = needsDisplayBoxHorizontalAdjustment
    486                                 || boxGeometry.horizontalBorder()
    487                                 || boxGeometry.horizontalPadding().value_or(0)
    488                                 || boxGeometry.marginStart()
    489                                 || boxGeometry.marginEnd();
    490                         };
    491                         createAndInsertDisplayBoxForInlineBoxFragment();
    492                     }
    493                     break;
    494                 }
    495                 // root may not be the lowest but always a common ancestor.
    496                 ASSERT(ancestor != &root());
    497                 inlineBoxNeedingDisplayBoxList.append(ancestor);
     505            for (auto index : lastDisplayBoxIndexes.values()) {
     506                if (lineBox.inlineLevelBoxForLayoutBox(boxes[index].layoutBox()).isLastBox())
     507                    boxes[index].setIsLastForLayoutBox(true);
    498508            }
    499         }
    500         // "Close" the remaining inline boxes on the stack (excluding the root).
    501         while (parentBoxStack.size() > 1) {
    502             auto* parentInlineBox = parentBoxStack.takeLast();
    503             ASSERT(inlineBoxDisplayBoxMap.contains(parentInlineBox));
    504             inlineBoxRangeList.append({ inlineBoxDisplayBoxMap.get(parentInlineBox), boxes.size() });
    505         }
    506     };
    507     if (needsNonRootInlineBoxDisplayBox)
    508         createDisplayBoxesInVisualOrderForInlineBoxes();
    509 
    510     auto adjustVisualGeometryWithInlineBoxes = [&] {
    511         size_t currentInlineBox = 0;
    512         auto accumulatedOffset = InlineLayoutUnit { };
    513 
    514         ASSERT(boxes[0].isRootInlineBox());
    515         for (size_t index = 1; index < boxes.size(); ++index) {
    516             auto& displayBox = boxes[index];
    517             displayBox.moveHorizontally(accumulatedOffset);
    518 
    519             while (currentInlineBox < inlineBoxRangeList.size() && index == inlineBoxRangeList[currentInlineBox].end() - 1) {
    520                 // We are at the end of the inline box content.
    521                 // Let's compute the inline box width and offset the rest of the content with padding/border/margin end.
    522                 auto inlineBoxRange = inlineBoxRangeList[currentInlineBox++];
    523                 auto& inlineBoxDisplayBox = boxes[inlineBoxRange.begin()];
    524                 ASSERT(inlineBoxDisplayBox.isNonRootInlineBox());
    525 
    526                 auto& boxGeometry = formattingState().boxGeometry(inlineBoxDisplayBox.layoutBox());
    527                 auto contentRight = displayBox.logicalRight();
    528                 if (inlineBoxDisplayBox.isLastForLayoutBox()) {
    529                     accumulatedOffset += boxGeometry.borderAndPaddingEnd() + boxGeometry.marginEnd();
    530                     inlineBoxDisplayBox.setLogicalRight(contentRight + boxGeometry.borderAndPaddingEnd());
    531                 } else
    532                     inlineBoxDisplayBox.setLogicalRight(contentRight);
    533             }
    534             if (displayBox.isNonRootInlineBox() && displayBox.isFirstForLayoutBox()) {
    535                 auto& layoutBox = displayBox.layoutBox();
    536                 auto& boxGeometry = formattingState().boxGeometry(layoutBox);
    537 
    538                 displayBox.moveHorizontally(boxGeometry.marginStart());
    539                 accumulatedOffset += boxGeometry.marginStart() + boxGeometry.borderAndPaddingStart();
    540             }
    541         }
    542     };
    543     if (needsDisplayBoxHorizontalAdjustment)
     509        };
     510        computeIsFirstIsLastBox();
     511
     512        auto adjustVisualGeometryWithInlineBoxes = [&] {
     513            auto contentRightInVisualOrder = lineBoxLogicalTopLeft.x() + contentStartInVisualOrder;
     514            for (auto& childDisplayBoxNode : rootDisplayBoxNode.children)
     515                adjustVisualGeometryForChildNode(*childDisplayBoxNode, contentRightInVisualOrder, lineBoxLogicalTopLeft.y(), boxes, lineBox);
     516        };
    544517        adjustVisualGeometryWithInlineBoxes();
    545 
    546     auto computeInlineBoxGeometry = [&] {
    547         ASSERT(!inlineBoxRangeList.isEmpty());
    548         for (auto& inlineBoxRange : inlineBoxRangeList) {
    549             auto& inlineBoxDisplayBox = boxes[inlineBoxRange.begin()];
    550             setInlineBoxGeometry(inlineBoxDisplayBox.layoutBox(), inlineBoxDisplayBox.logicalRect(), inlineBoxDisplayBox.isFirstForLayoutBox());
    551         }
    552     };
    553     if (needsNonRootInlineBoxDisplayBox)
    554         computeInlineBoxGeometry();
     518    }
    555519}
    556520
  • trunk/Source/WebCore/layout/formattingContexts/inline/InlineDisplayContentBuilder.h

    r286782 r286784  
    3434namespace Layout {
    3535
     36struct AncestorStack;
    3637class ContainerBox;
     38struct DisplayBoxNode;
    3739class InlineFormattingState;
    3840class LineBox;
     
    5860    void appendInlineBoxDisplayBox(const Line::Run&, const InlineLevelBox&, const InlineRect&, bool linehasContent, DisplayBoxes&);
    5961    void appendSpanningInlineBoxDisplayBox(const Line::Run&, const InlineLevelBox&, const InlineRect&, DisplayBoxes&);
    60     void insertInlineBoxDisplayBoxForBidiBoundary(const InlineLevelBox&, const InlineRect&, bool isFirstInlineBoxFragment, size_t insertionPoint, DisplayBoxes&);
    61     void adjustInlineBoxDisplayBoxForBidiBoundary(InlineDisplay::Box&, const InlineRect&);
    6262
    6363    void setInlineBoxGeometry(const Box&, const InlineRect&, bool isFirstInlineBoxFragment);
     64    void adjustVisualGeometryForChildNode(const DisplayBoxNode&, InlineLayoutUnit& accumulatedOffset, InlineLayoutUnit lineBoxLogicalTop, DisplayBoxes&, const LineBox&);
     65    DisplayBoxNode& ensureDisplayBoxForContainer(const ContainerBox&, AncestorStack&, DisplayBoxes&);
    6466
    6567    const ContainerBox& root() const { return m_formattingContextRoot; }
  • trunk/Source/WebCore/layout/formattingContexts/inline/display/InlineDisplayBox.h

    r286541 r286784  
    119119    void adjustInkOverflow(const Layout::InlineRect& childBorderBox) { return m_inkOverflow.expandToContain(childBorderBox); }
    120120    void truncate(Layout::InlineLayoutUnit truncatedwidth = 0.f);
    121     void setLogicalRight(Layout::InlineLayoutUnit right) { m_logicalRect.setRight(right); }
     121    void setLogicalLeft(Layout::InlineLayoutUnit left)
     122    {
     123        auto offset = left - logicalLeft();
     124        m_logicalRect.setLeft(left);
     125        m_inkOverflow.setLeft(m_inkOverflow.left() + offset);
     126    }
     127    void setLogicalRight(Layout::InlineLayoutUnit right)
     128    {
     129        auto offset = right - logicalRight();
     130        m_logicalRect.setRight(right);
     131        m_inkOverflow.setRight(m_inkOverflow.right() + offset);
     132    }
     133    void setLogicalRect(const Layout::InlineRect& rect, const Layout::InlineRect& inkOverflow)
     134    {
     135        m_logicalRect = rect;
     136        m_inkOverflow = inkOverflow;
     137    }
     138    void setHasContent() { m_hasContent = true; }
    122139
    123140    std::optional<Text>& text() { return m_text; }
Note: See TracChangeset for help on using the changeset viewer.