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

Changeset 246468 in webkit


Ignore:
Timestamp:
Jun 15, 2019, 12:30:06 PM (7 years ago)
Author:
Alan Bujtas
Message:

[LFC][BFC] Fix available width for non-floating positioned float avoiders.
https://bugs.webkit.org/show_bug.cgi?id=198886
<rdar://problem/51773643>

Reviewed by Antti Koivisto.

Normally the available width for an in-flow block level box is the width of the containing block's content box.
However a non-floating positioned float avoider box might be constrained by existing floats.
The idea here is that we pre-compute(estimate) the vertical position and check the current floating context for
left and right floats. These floats contrain the available width and this computed value should be used instead of the containing block's
content box's width whe calculating the used width for width: auto.

  • layout/FormattingContext.cpp:

(WebCore::Layout::mapHorizontalPositionToAncestor):
(WebCore::Layout::FormattingContext::mapLeftToAncestor):
(WebCore::Layout::FormattingContext::mapRightToAncestor):
(WebCore::Layout::FormattingContext::mapPointToAncestor):
(WebCore::Layout::FormattingContext::mapCoordinateToAncestor): Deleted.

  • layout/FormattingContext.h:
  • layout/blockformatting/BlockFormattingContext.cpp:

(WebCore::Layout::BlockFormattingContext::usedAvailableWidthForFloatAvoider const):
(WebCore::Layout::BlockFormattingContext::layoutFormattingContextRoot const):
(WebCore::Layout::BlockFormattingContext::computeStaticVerticalPosition const):
(WebCore::Layout::BlockFormattingContext::computeStaticHorizontalPosition const):
(WebCore::Layout::BlockFormattingContext::computeStaticPosition const):
(WebCore::Layout::BlockFormattingContext::computeEstimatedVerticalPositionForFormattingRoot const):
(WebCore::Layout::BlockFormattingContext::computeWidthAndMargin const):

  • layout/blockformatting/BlockFormattingContext.h:

(WebCore::Layout::BlockFormattingContext::computeWidthAndMargin):

  • layout/blockformatting/BlockFormattingContextGeometry.cpp:

(WebCore::Layout::BlockFormattingContext::Geometry::staticVerticalPosition):
(WebCore::Layout::BlockFormattingContext::Geometry::staticHorizontalPosition):
(WebCore::Layout::BlockFormattingContext::Geometry::staticPosition):

  • layout/floats/FloatingState.cpp:

(WebCore::Layout::FloatingState::constraints const):

  • layout/layouttree/LayoutBlockContainer.cpp:

(WebCore::Layout::BlockContainer::establishesInlineFormattingContextOnly const):

  • layout/layouttree/LayoutBlockContainer.h:
  • layout/layouttree/LayoutBox.cpp:

(WebCore::Layout::Box::isFloatAvoider const):

  • layout/layouttree/LayoutBox.h:

(WebCore::Layout::Box::establishesInlineFormattingContextOnly const):

Location:
trunk/Source/WebCore
Files:
11 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/ChangeLog

    r246467 r246468  
     12019-06-15  Zalan Bujtas  <zalan@apple.com>
     2
     3        [LFC][BFC] Fix available width for non-floating positioned float avoiders.
     4        https://bugs.webkit.org/show_bug.cgi?id=198886
     5        <rdar://problem/51773643>
     6
     7        Reviewed by Antti Koivisto.
     8
     9        Normally the available width for an in-flow block level box is the width of the containing block's content box.
     10        However a non-floating positioned float avoider box might be constrained by existing floats.
     11        The idea here is that we pre-compute(estimate) the vertical position and check the current floating context for
     12        left and right floats. These floats contrain the available width and this computed value should be used instead of the containing block's
     13        content box's width whe calculating the used width for width: auto.
     14
     15        * layout/FormattingContext.cpp:
     16        (WebCore::Layout::mapHorizontalPositionToAncestor):
     17        (WebCore::Layout::FormattingContext::mapLeftToAncestor):
     18        (WebCore::Layout::FormattingContext::mapRightToAncestor):
     19        (WebCore::Layout::FormattingContext::mapPointToAncestor):
     20        (WebCore::Layout::FormattingContext::mapCoordinateToAncestor): Deleted.
     21        * layout/FormattingContext.h:
     22        * layout/blockformatting/BlockFormattingContext.cpp:
     23        (WebCore::Layout::BlockFormattingContext::usedAvailableWidthForFloatAvoider const):
     24        (WebCore::Layout::BlockFormattingContext::layoutFormattingContextRoot const):
     25        (WebCore::Layout::BlockFormattingContext::computeStaticVerticalPosition const):
     26        (WebCore::Layout::BlockFormattingContext::computeStaticHorizontalPosition const):
     27        (WebCore::Layout::BlockFormattingContext::computeStaticPosition const):
     28        (WebCore::Layout::BlockFormattingContext::computeEstimatedVerticalPositionForFormattingRoot const):
     29        (WebCore::Layout::BlockFormattingContext::computeWidthAndMargin const):
     30        * layout/blockformatting/BlockFormattingContext.h:
     31        (WebCore::Layout::BlockFormattingContext::computeWidthAndMargin):
     32        * layout/blockformatting/BlockFormattingContextGeometry.cpp:
     33        (WebCore::Layout::BlockFormattingContext::Geometry::staticVerticalPosition):
     34        (WebCore::Layout::BlockFormattingContext::Geometry::staticHorizontalPosition):
     35        (WebCore::Layout::BlockFormattingContext::Geometry::staticPosition):
     36        * layout/floats/FloatingState.cpp:
     37        (WebCore::Layout::FloatingState::constraints const):
     38        * layout/layouttree/LayoutBlockContainer.cpp:
     39        (WebCore::Layout::BlockContainer::establishesInlineFormattingContextOnly const):
     40        * layout/layouttree/LayoutBlockContainer.h:
     41        * layout/layouttree/LayoutBox.cpp:
     42        (WebCore::Layout::Box::isFloatAvoider const):
     43        * layout/layouttree/LayoutBox.h:
     44        (WebCore::Layout::Box::establishesInlineFormattingContextOnly const):
     45
    1462019-06-15  Ludovico de Nittis  <ludovico.denittis@collabora.com>
    247
  • trunk/Source/WebCore/layout/FormattingContext.cpp

    r241295 r246468  
    166166}
    167167
     168static LayoutUnit mapHorizontalPositionToAncestor(const LayoutState& layoutState, LayoutUnit horizontalPosition, const Container& containingBlock, const Container& ancestor)
     169{
     170    // "horizontalPosition" is in the coordinate system of the "containingBlock". -> map from containingBlock to ancestor.
     171    if (&containingBlock == &ancestor)
     172        return horizontalPosition;
     173    ASSERT(containingBlock.isDescendantOf(ancestor));
     174    for (auto* container = &containingBlock; container && container != &ancestor; container = container->containingBlock())
     175        horizontalPosition += layoutState.displayBoxForLayoutBox(*container).left();
     176    return horizontalPosition;
     177}
     178
     179// FIXME: turn these into templates.
     180LayoutUnit FormattingContext::mapLeftToAncestor(const LayoutState& layoutState, const Box& layoutBox, const Container& ancestor)
     181{
     182    ASSERT(layoutBox.containingBlock());
     183    return mapHorizontalPositionToAncestor(layoutState, layoutState.displayBoxForLayoutBox(layoutBox).left(), *layoutBox.containingBlock(), ancestor);
     184}
     185
     186LayoutUnit FormattingContext::mapRightToAncestor(const LayoutState& layoutState, const Box& layoutBox, const Container& ancestor)
     187{
     188    ASSERT(layoutBox.containingBlock());
     189    return mapHorizontalPositionToAncestor(layoutState, layoutState.displayBoxForLayoutBox(layoutBox).right(), *layoutBox.containingBlock(), ancestor);
     190}
     191
    168192Display::Box FormattingContext::mapBoxToAncestor(const LayoutState& layoutState, const Box& layoutBox, const Container& ancestor)
    169193{
     
    197221}
    198222
    199 Point FormattingContext::mapCoordinateToAncestor(const LayoutState& layoutState, Point position, const Container& containingBlock, const Container& ancestor)
     223Point FormattingContext::mapPointToAncestor(const LayoutState& layoutState, Point position, const Container& containingBlock, const Container& ancestor)
    200224{
    201225    auto mappedPosition = position;
  • trunk/Source/WebCore/layout/FormattingContext.h

    r241545 r246468  
    6161    static Display::Box mapBoxToAncestor(const LayoutState&, const Box&, const Container& ancestor);
    6262    static LayoutUnit mapTopToAncestor(const LayoutState&, const Box&, const Container& ancestor);
    63     static Point mapCoordinateToAncestor(const LayoutState&, Point, const Container& containingBlock, const Container& ancestor);
     63    static LayoutUnit mapLeftToAncestor(const LayoutState&, const Box&, const Container& ancestor);
     64    static LayoutUnit mapRightToAncestor(const LayoutState&, const Box&, const Container& ancestor);
     65    static Point mapPointToAncestor(const LayoutState&, Point, const Container& containingBlock, const Container& ancestor);
    6466
    6567protected:
  • trunk/Source/WebCore/layout/blockformatting/BlockFormattingContext.cpp

    r241869 r246468  
    119119}
    120120
     121Optional<LayoutUnit> BlockFormattingContext::usedAvailableWidthForFloatAvoider(const FloatingContext& floatingContext, const Box& layoutBox) const
     122{
     123    // Normally the available width for an in-flow block level box is the width of the containing block's content box.
     124    // However (and can't find it anywhere in the spec) non-floating positioned float avoider block level boxes are constrained by existing floats.
     125    if (!layoutBox.isFloatAvoider() || layoutBox.isFloatingPositioned())
     126        return { };
     127    auto& floatingState = floatingContext.floatingState();
     128    if (floatingState.isEmpty())
     129        return { };
     130    // Vertical static position is not computed yet, so let's just estimate it for now.
     131    auto& formattingRoot = downcast<Container>(root());
     132    auto verticalPosition = FormattingContext::mapTopToAncestor(layoutState(), layoutBox, formattingRoot);
     133    auto constraints = floatingState.constraints({ verticalPosition }, formattingRoot);
     134    if (!constraints.left && !constraints.right)
     135        return { };
     136    auto& containingBlock = downcast<Container>(*layoutBox.containingBlock());
     137    auto& containingBlockDisplayBox = layoutState().displayBoxForLayoutBox(containingBlock);
     138    auto availableWidth = containingBlockDisplayBox.contentBoxWidth();
     139
     140    LayoutUnit containingBlockLeft;
     141    LayoutUnit containingBlockRight = containingBlockDisplayBox.right();
     142    if (&containingBlock != &formattingRoot) {
     143        // Move containing block left/right to the root's coordinate system.
     144        containingBlockLeft = FormattingContext::mapLeftToAncestor(layoutState(), containingBlock, formattingRoot);
     145        containingBlockRight = FormattingContext::mapRightToAncestor(layoutState(), containingBlock, formattingRoot);
     146    }
     147    auto containingBlockContentBoxLeft = containingBlockLeft + containingBlockDisplayBox.borderLeft() + containingBlockDisplayBox.paddingLeft().valueOr(0);
     148    auto containingBlockContentBoxRight = containingBlockRight - containingBlockDisplayBox.borderRight() + containingBlockDisplayBox.paddingRight().valueOr(0);
     149
     150    // Shrink the available space if the floats are actually intruding at this vertical position.
     151    availableWidth -= (std::max<LayoutUnit>(0, constraints.left.valueOr(PositionInContextRoot { 0 }) - containingBlockContentBoxLeft)
     152        + std::max<LayoutUnit>(0, containingBlockContentBoxRight - constraints.right.valueOr(PositionInContextRoot { containingBlockContentBoxRight })));
     153    return availableWidth;
     154}
     155
    121156void BlockFormattingContext::layoutFormattingContextRoot(FloatingContext& floatingContext, const Box& layoutBox) const
    122157{
     158    ASSERT(layoutBox.establishesFormattingContext());
    123159    // Start laying out this formatting root in the formatting contenxt it lives in.
    124160    LOG_WITH_STREAM(FormattingContextLayout, stream << "[Compute] -> [Position][Border][Padding][Width][Margin] -> for layoutBox(" << &layoutBox << ")");
    125161    computeBorderAndPadding(layoutBox);
    126     computeWidthAndMargin(layoutBox);
    127     computeStaticPosition(floatingContext, layoutBox);
     162    computeStaticVerticalPosition(floatingContext, layoutBox);
     163
     164    computeWidthAndMargin(layoutBox, usedAvailableWidthForFloatAvoider(floatingContext, layoutBox));
     165    computeStaticHorizontalPosition(layoutBox);
    128166    // Swich over to the new formatting context (the one that the root creates).
    129167    auto formattingContext = layoutState().createFormattingContext(layoutBox);
     
    173211}
    174212
    175 void BlockFormattingContext::computeStaticPosition(const FloatingContext& floatingContext, const Box& layoutBox) const
    176 {
    177     auto& layoutState = this->layoutState();
    178     layoutState.displayBoxForLayoutBox(layoutBox).setTopLeft(Geometry::staticPosition(layoutState, layoutBox));
     213void BlockFormattingContext::computeStaticVerticalPosition(const FloatingContext& floatingContext, const Box& layoutBox) const
     214{
     215    auto& layoutState = this->layoutState();
     216    layoutState.displayBoxForLayoutBox(layoutBox).setTop(Geometry::staticVerticalPosition(layoutState, layoutBox));
    179217    if (layoutBox.hasFloatClear())
    180218        computeEstimatedVerticalPositionForFloatClear(floatingContext, layoutBox);
    181219    else if (layoutBox.establishesFormattingContext())
    182220        computeEstimatedVerticalPositionForFormattingRoot(layoutBox);
     221}
     222
     223void BlockFormattingContext::computeStaticHorizontalPosition(const Box& layoutBox) const
     224{
     225    layoutState().displayBoxForLayoutBox(layoutBox).setLeft(Geometry::staticHorizontalPosition(layoutState(), layoutBox));
     226}
     227
     228void BlockFormattingContext::computeStaticPosition(const FloatingContext& floatingContext, const Box& layoutBox) const
     229{
     230    computeStaticVerticalPosition(floatingContext, layoutBox);
     231    computeStaticHorizontalPosition(layoutBox);
    183232}
    184233
     
    227276    ASSERT(!layoutBox.hasFloatClear());
    228277
    229     auto avoidsFloats = layoutBox.isFloatingPositioned() || layoutBox.establishesBlockFormattingContext();
    230     if (avoidsFloats)
     278    if (layoutBox.isFloatingPositioned()) {
    231279        computeEstimatedVerticalPositionForAncestors(layoutBox);
     280        return;
     281    }
     282
     283    computeEstimatedVerticalPosition(layoutBox);
     284    computeEstimatedVerticalPositionForAncestors(layoutBox);
    232285
    233286    // If the inline formatting root is also the root for the floats (happens when the root box also establishes a block formatting context)
    234287    // the floats are in the coordinate system of this root. No need to find the final vertical position.
    235     auto inlineContextInheritsFloats = layoutBox.establishesInlineFormattingContext() && !layoutBox.establishesBlockFormattingContext();
     288    auto inlineContextInheritsFloats = layoutBox.establishesInlineFormattingContextOnly();
    236289    if (inlineContextInheritsFloats) {
    237290        computeEstimatedVerticalPosition(layoutBox);
     
    296349}
    297350
    298 void BlockFormattingContext::computeWidthAndMargin(const Box& layoutBox) const
    299 {
    300     auto& layoutState = this->layoutState();
    301     auto containingBlockWidth = layoutState.displayBoxForLayoutBox(*layoutBox.containingBlock()).contentBoxWidth();
     351void BlockFormattingContext::computeWidthAndMargin(const Box& layoutBox, Optional<LayoutUnit> usedAvailableWidth) const
     352{
     353    auto& layoutState = this->layoutState();
     354
     355    LayoutUnit availableWidth;
     356    if (usedAvailableWidth)
     357        availableWidth = *usedAvailableWidth;
     358    else
     359        availableWidth = layoutState.displayBoxForLayoutBox(*layoutBox.containingBlock()).contentBoxWidth();
    302360
    303361    auto compute = [&](Optional<LayoutUnit> usedWidth) -> WidthAndMargin {
    304         auto usedValues = UsedHorizontalValues { containingBlockWidth, usedWidth, { } };
     362        auto usedValues = UsedHorizontalValues { availableWidth, usedWidth, { } };
    305363        if (layoutBox.isInFlow())
    306364            return Geometry::inFlowWidthAndMargin(layoutState, layoutBox, usedValues);
     
    315373    auto widthAndMargin = compute({ });
    316374
    317     if (auto maxWidth = Geometry::computedValueIfNotAuto(layoutBox.style().logicalMaxWidth(), containingBlockWidth)) {
     375    if (auto maxWidth = Geometry::computedValueIfNotAuto(layoutBox.style().logicalMaxWidth(), availableWidth)) {
    318376        auto maxWidthAndMargin = compute(maxWidth);
    319377        if (widthAndMargin.width > maxWidthAndMargin.width)
     
    321379    }
    322380
    323     auto minWidth = Geometry::computedValueIfNotAuto(layoutBox.style().logicalMinWidth(), containingBlockWidth).valueOr(0);
     381    auto minWidth = Geometry::computedValueIfNotAuto(layoutBox.style().logicalMinWidth(), availableWidth).valueOr(0);
    324382    auto minWidthAndMargin = compute(minWidth);
    325383    if (widthAndMargin.width < minWidthAndMargin.width)
  • trunk/Source/WebCore/layout/blockformatting/BlockFormattingContext.h

    r241263 r246468  
    5656    void placeInFlowPositionedChildren(const Box&) const;
    5757
    58     void computeWidthAndMargin(const Box&) const;
     58    void computeWidthAndMargin(const Box&, Optional<LayoutUnit> usedAvailableWidth = { }) const;
    5959    void computeHeightAndMargin(const Box&) const;
    6060
     61    void computeStaticHorizontalPosition(const Box&) const;
     62    void computeStaticVerticalPosition(const FloatingContext&, const Box&) const;
    6163    void computeStaticPosition(const FloatingContext&, const Box&) const;
    6264    void computeFloatingPosition(const FloatingContext&, const Box&) const;
     
    7880
    7981        static Point staticPosition(const LayoutState&, const Box&);
     82        static LayoutUnit staticVerticalPosition(const LayoutState&, const Box&);
     83        static LayoutUnit staticHorizontalPosition(const LayoutState&, const Box&);
    8084
    8185        static bool intrinsicWidthConstraintsNeedChildrenWidth(const Box&);
     
    132136    void removeEstimatedMarginBefore(const Box& layoutBox) const { m_estimatedMarginBeforeList.remove(&layoutBox); }
    133137    bool hasEstimatedMarginBefore(const Box&) const;
     138    Optional<LayoutUnit> usedAvailableWidthForFloatAvoider(const FloatingContext&, const Box&) const;
    134139#ifndef NDEBUG
    135140    EstimatedMarginBefore estimatedMarginBefore(const Box& layoutBox) const { return m_estimatedMarginBeforeList.get(&layoutBox); }
  • trunk/Source/WebCore/layout/blockformatting/BlockFormattingContextGeometry.cpp

    r245776 r246468  
    223223}
    224224
    225 Point BlockFormattingContext::Geometry::staticPosition(const LayoutState& layoutState, const Box& layoutBox)
     225LayoutUnit BlockFormattingContext::Geometry::staticVerticalPosition(const LayoutState& layoutState, const Box& layoutBox)
    226226{
    227227    // https://www.w3.org/TR/CSS22/visuren.html#block-formatting
     
    229229    // The vertical distance between two sibling boxes is determined by the 'margin' properties.
    230230    // Vertical margins between adjacent block-level boxes in a block formatting context collapse.
    231     // In a block formatting context, each box's left outer edge touches the left edge of the containing block (for right-to-left formatting, right edges touch).
    232 
    233     LayoutUnit top;
    234     auto& containingBlockDisplayBox = layoutState.displayBoxForLayoutBox(*layoutBox.containingBlock());
    235231    if (auto* previousInFlowSibling = layoutBox.previousInFlowSibling()) {
    236232        auto& previousInFlowDisplayBox = layoutState.displayBoxForLayoutBox(*previousInFlowSibling);
    237         top = previousInFlowDisplayBox.bottom() + previousInFlowDisplayBox.marginAfter();
    238     } else
    239         top = containingBlockDisplayBox.contentBoxTop();
    240 
    241     auto left = containingBlockDisplayBox.contentBoxLeft() + layoutState.displayBoxForLayoutBox(layoutBox).marginStart();
    242     LOG_WITH_STREAM(FormattingContextLayout, stream << "[Position] -> static -> top(" << top << "px) left(" << left << "px) layoutBox(" << &layoutBox << ")");
    243     return { left, top };
     233        return previousInFlowDisplayBox.bottom() + previousInFlowDisplayBox.marginAfter();
     234    }
     235    return layoutState.displayBoxForLayoutBox(*layoutBox.containingBlock()).contentBoxTop();
     236}
     237
     238LayoutUnit BlockFormattingContext::Geometry::staticHorizontalPosition(const LayoutState& layoutState, const Box& layoutBox)
     239{
     240    // https://www.w3.org/TR/CSS22/visuren.html#block-formatting
     241    // In a block formatting context, each box's left outer edge touches the left edge of the containing block (for right-to-left formatting, right edges touch).
     242    return layoutState.displayBoxForLayoutBox(*layoutBox.containingBlock()).contentBoxLeft() + layoutState.displayBoxForLayoutBox(layoutBox).marginStart();
     243}
     244
     245Point BlockFormattingContext::Geometry::staticPosition(const LayoutState& layoutState, const Box& layoutBox)
     246{
     247    return { staticHorizontalPosition(layoutState, layoutBox), staticVerticalPosition(layoutState, layoutBox) };
    244248}
    245249
  • trunk/Source/WebCore/layout/floats/FloatingState.cpp

    r240240 r246468  
    122122
    123123    if (coordinateMappingIsRequired)
    124         adjustedPosition = FormattingContext::mapCoordinateToAncestor(m_layoutState, adjustedPosition, downcast<Container>(formattingContextRoot), downcast<Container>(root()));
     124        adjustedPosition = FormattingContext::mapPointToAncestor(m_layoutState, adjustedPosition, downcast<Container>(formattingContextRoot), downcast<Container>(root()));
    125125
    126126    Constraints constraints;
  • trunk/Source/WebCore/layout/layouttree/LayoutBlockContainer.cpp

    r239427 r246468  
    5454}
    5555
     56bool BlockContainer::establishesInlineFormattingContextOnly() const
     57{
     58    return establishesInlineFormattingContext() && !establishesBlockFormattingContext();
     59}
     60
    5661}
    5762}
  • trunk/Source/WebCore/layout/layouttree/LayoutBlockContainer.h

    r239427 r246468  
    4343
    4444    bool establishesInlineFormattingContext() const final;
    45 
     45    bool establishesInlineFormattingContextOnly() const final;
    4646};
    4747
  • trunk/Source/WebCore/layout/layouttree/LayoutBox.cpp

    r241591 r246468  
    135135}
    136136
     137bool Box::isFloatAvoider() const
     138{
     139    return establishesBlockFormattingContext() || isFloatingPositioned();
     140}
     141
    137142const Container* Box::containingBlock() const
    138143{
  • trunk/Source/WebCore/layout/layouttree/LayoutBox.h

    r245776 r246468  
    7979    bool establishesBlockFormattingContextOnly() const;
    8080    virtual bool establishesInlineFormattingContext() const { return false; }
     81    virtual bool establishesInlineFormattingContextOnly() const { return false; }
    8182
    8283    bool isInFlow() const { return !isFloatingOrOutOfFlowPositioned(); }
     
    9293    bool isRightFloatingPositioned() const;
    9394    bool hasFloatClear() const;
     95    bool isFloatAvoider() const;
    9496
    9597    bool isFloatingOrOutOfFlowPositioned() const { return isFloatingPositioned() || isOutOfFlowPositioned(); }
Note: See TracChangeset for help on using the changeset viewer.