Skip to content

Commit

Permalink
[css-flex][baseline-alignment] Baseline aligned flex items should be …
Browse files Browse the repository at this point in the history
…also aligned using their fallback alignment.

https://bugs.webkit.org/show_bug.cgi?id=256947
rdar://109496710

Reviewed by Alan Baradlay.

css-align-3 states the following to be done after items have been aligned
within their baseline sharing groups:

9.3. Aligning Boxes by Baseline step 3:
Position the aligned baseline-sharing group within the alignment container
according to its fallback alignment. The fallback alignment of a
baseline-sharing group is the fallback alignment of its items as
resolved to physical directions.

For flex layout we only need to consider the case where we may need to
push a baseline sharing group towards the cross axis end of the flexbox.
This is because when we align items within the baseline sharing group,
we align the items with respect to the item that has the highest ascent
in the flexbox. In other words, the item that has this max ascent value,
will already be placed flush against the cross axis start of the flexbox,
so the baseline sharing group could not be pushed closer towards it.

shouldAdjustItemTowardsCrossAxisEnd is a small helper function that will
help us determine if we need to move a baseline sharing group towards
the end of the cross axis. This is done by iterating over each item in
the baseline sharing group and taking 3 things into consideration:

1.) The alignment of the flex item (first vs last baseline)
2.) The used writing mode of the flex item
3.) The direction of the flexbox's cross axis

We need these 3 things in order to be able to resolve the fallback
alignment of the group/item in terms of physical directions.

First baseline aligned items need to be be adjusted when their block
flow direction is opposite of the cross axis direction. This is because
the self-start will be in that direction. Example:

<div style="outline: 1px solid black; display: flex; flex-direction: column; align-items: baseline; width: 200px; height: 200px">
  <div style="writing-mode: vertical-rl;">
    one <br/> two <br/> three
  </div>
</div>

Last baseline aligned items need to be adjusted when their block
direction is the same as the flexbox's cross axis direction. self-end
will be in that direction. Example:

<div style="outline: 1px solid black; display: flex; flex-direction: column; align-items: last baseline; width: 200px; height: 200px">
  <div style="writing-mode: vertical-lr;">
    one <br/> two <br/> three
  </div>
</div>

* LayoutTests/imported/w3c/web-platform-tests/css/css-flexbox/align-items-baseline-column-vert-rl-flexbox-item-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-flexbox/align-items-baseline-column-vert-rl-grid-item-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-flexbox/align-items-baseline-column-vert-rl-items-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-flexbox/align-items-baseline-column-vert-rl-table-item-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-flexbox/alignment/flex-align-baseline-001-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-flexbox/alignment/flex-align-baseline-002-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-flexbox/alignment/flex-align-baseline-003-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-flexbox/alignment/flex-align-baseline-004-expected.txt:
* Source/WebCore/rendering/RenderFlexibleBox.cpp:
(WebCore::RenderFlexibleBox::performBaselineAlignment):
* Source/WebCore/rendering/RenderFlexibleBox.h:
(WebCore::RenderFlexibleBox::crossAxisDirection const):
* Source/WebCore/rendering/style/RenderStyle.h:
* Source/WebCore/rendering/style/RenderStyleInlines.h:
(WebCore::RenderStyle::isRowFlexDirection const):

Canonical link: https://commits.webkit.org/267026@main
  • Loading branch information
sammygill committed Aug 18, 2023
1 parent fbfd1da commit c4b5ac7
Show file tree
Hide file tree
Showing 12 changed files with 102 additions and 85 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,6 @@ two
lines
hello

FAIL #target > div 1 assert_equals:
<div class="inner" data-offset-x="146">
<div>two<br>lines</div>
</div>
offsetLeft expected 146 but got 21
FAIL #target > div 2 assert_equals:
<div data-offset-x="191">hello</div>
offsetLeft expected 191 but got 66
PASS #target > div 1
PASS #target > div 2

Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,6 @@ two
lines
hello

FAIL #target > div 1 assert_equals:
<div class="inner" data-offset-x="146">
<div>two<br>lines</div>
</div>
offsetLeft expected 146 but got 21
FAIL #target > div 2 assert_equals:
<div data-offset-x="191">hello</div>
offsetLeft expected 191 but got 66
PASS #target > div 1
PASS #target > div 2

Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,6 @@ two
lines
hello

FAIL #target > div 1 assert_equals:
<div data-offset-x="161">two<br>lines</div>
offsetLeft expected 161 but got 21
FAIL #target > div 2 assert_equals:
<div data-offset-x="201">hello</div>
offsetLeft expected 201 but got 61
PASS #target > div 1
PASS #target > div 2

Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,6 @@ two
lines
hello

FAIL #target > * 1 assert_equals:
<table class="inner" data-offset-x="140">
<tbody><tr>
<td style="vertical-align: baseline;">
<div>two<br>lines</div>
</td>
</tr>
</tbody></table>
offsetLeft expected 140 but got 21
FAIL #target > * 2 assert_equals:
<div data-offset-x="188">hello</div>
offsetLeft expected 188 but got 69
PASS #target > * 1
PASS #target > * 2

Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,10 @@ line2
line1
line2

FAIL #target > div 1 assert_equals:
<div data-offset-x="120">line1<br>line2</div>
offsetLeft expected 120 but got 15
PASS #target > div 1
PASS #target > div 2
PASS #target > div 3
PASS #target > div 4
FAIL #target > div 5 assert_equals:
<div data-offset-x="35">line1<br>line2</div>
offsetLeft expected 35 but got 140
FAIL #target > div 6 assert_equals:
<div data-offset-x="42">line1<br>line2</div>
offsetLeft expected 42 but got 147
PASS #target > div 5
PASS #target > div 6

Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,10 @@ line2
line1
line2

FAIL #target > div 1 assert_equals:
<div data-offset-x="120">line1<br>line2</div>
offsetLeft expected 120 but got 15
PASS #target > div 1
PASS #target > div 2
PASS #target > div 3
PASS #target > div 4
FAIL #target > div 5 assert_equals:
<div data-offset-x="35">line1<br>line2</div>
offsetLeft expected 35 but got 140
FAIL #target > div 6 assert_equals:
<div data-offset-x="42">line1<br>line2</div>
offsetLeft expected 42 but got 147
PASS #target > div 5
PASS #target > div 6

Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,9 @@ line1
line2

PASS #target > div 1
FAIL #target > div 2 assert_equals:
<div data-offset-x="105">line1<br>line2</div>
offsetLeft expected 105 but got 0
FAIL #target > div 3 assert_equals:
<div data-offset-x="126">line1<br>line2</div>
offsetLeft expected 126 but got 21
FAIL #target > div 4 assert_equals:
<div data-offset-x="20">line1<br>line2</div>
offsetLeft expected 20 but got 125
PASS #target > div 2
PASS #target > div 3
PASS #target > div 4
PASS #target > div 5
PASS #target > div 6

Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,10 @@ line2
line1
line2

FAIL #target > div 1 assert_equals:
<div data-offset-x="15">line1<br>line2</div>
offsetLeft expected 15 but got 120
PASS #target > div 1
PASS #target > div 2
PASS #target > div 3
PASS #target > div 4
FAIL #target > div 5 assert_equals:
<div data-offset-x="140">line1<br>line2</div>
offsetLeft expected 140 but got 35
FAIL #target > div 6 assert_equals:
<div data-offset-x="147">line1<br>line2</div>
offsetLeft expected 147 but got 42
PASS #target > div 5
PASS #target > div 6

68 changes: 53 additions & 15 deletions Source/WebCore/rendering/RenderFlexibleBox.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2409,34 +2409,72 @@ void RenderFlexibleBox::performBaselineAlignment(LineState& lineState)
{
ASSERT(lineState.baselineAlignmentState);

LayoutUnit minMarginAfterBaseline = LayoutUnit::max();
auto lineCrossAxisExtent = lineState.crossAxisExtent;
bool containerHasWrapReverse = style().flexWrap() == FlexWrap::Reverse;
auto shouldAdjustItemTowardsCrossAxis = [containerHasWrapReverse](ItemPosition alignment) {
return (containerHasWrapReverse && alignment == ItemPosition::Baseline) || (alignment == ItemPosition::LastBaseline && !containerHasWrapReverse);

auto flexItemWritingModeForBaselineAlignment = [&](const RenderBox& flexItem) {
if (mainAxisIsChildInlineAxis(flexItem))
return flexItem.style().writingMode();

// css-align-3: 9.1. Determining the Baselines of a Box
// In general, the writing mode of the box, shape, or other object being aligned is used to determine
// the line-under and line-over edges for synthesis. However, when that writing mode’s block flow direction
// is parallel to the axis of the alignment context, an axis-compatible writing mode must be assumed:

// If the box establishing the alignment context has a block flow direction that is orthogonal to the
// axis of the alignment context, use its writing mode.
if (style().isRowFlexDirection())
return style().writingMode();

// Otherwise:
//
// If the box’s own writing mode is vertical, assume horizontal-tb.
// If the box’s own writing mode is horizontal, assume vertical-lr if
// direction is ltr and vertical-rl if direction is rtl.
if (!flexItem.isHorizontalWritingMode())
return WritingMode::HorizontalTb;
return style().direction() == TextDirection::LTR ? WritingMode::VerticalLr : WritingMode::VerticalRl;
};

auto shouldAdjustItemTowardsCrossAxisEnd = [&](const BlockFlowDirection& flexItemBlockFlowDirection, ItemPosition alignment) {
ASSERT(alignment == ItemPosition::Baseline || alignment == ItemPosition::LastBaseline);

// The direction in which we are aligning (i.e. direction of the cross axis) must be parallel with the direction of the flex item's used writing mode
ASSERT_IMPLIES(crossAxisDirection() == RenderFlexibleBox::Direction::TopToBottom || crossAxisDirection() == RenderFlexibleBox::Direction::BottomToTop, flexItemBlockFlowDirection == RenderFlexibleBox::Direction::TopToBottom || flexItemBlockFlowDirection == RenderFlexibleBox::Direction::BottomToTop);
ASSERT_IMPLIES(crossAxisDirection() == RenderFlexibleBox::Direction::LeftToRight || crossAxisDirection() == RenderFlexibleBox::Direction::RightToLeft, flexItemBlockFlowDirection == RenderFlexibleBox::Direction::LeftToRight || flexItemBlockFlowDirection == RenderFlexibleBox::Direction::RightToLeft);

// For first baseline aligned items, if its block direction is the opposite of
// the cross axis direction, then that means its fallback alignment (safe self-start)
// is in the direction of the end of the cross axis
//
// For last baseline aligned items, if its block direction is in the same direction as
// the cross axis direction, then that means its fallback alignment (safe self-end) is
// in the direction of the end of the cross axis
if (alignment == ItemPosition::Baseline)
return crossAxisDirection() != flexItemBlockFlowDirection;
return crossAxisDirection() == flexItemBlockFlowDirection;
};

for (auto& baselineSharingGroup : lineState.baselineAlignmentState.value().sharedGroups()) {
LayoutUnit minMarginAfterBaseline = LayoutUnit::max();
for (auto& flexItem : baselineSharingGroup) {
auto position = alignmentForChild(flexItem);
ASSERT(position == ItemPosition::Baseline || position == ItemPosition::LastBaseline);
auto offset = alignmentOffset(availableAlignmentSpaceForChild(lineCrossAxisExtent, flexItem), position, marginBoxAscentForChild(flexItem), baselineSharingGroup.maxAscent(), containerHasWrapReverse);
adjustAlignmentForChild(flexItem, offset);

if (shouldAdjustItemTowardsCrossAxis(position))
if (shouldAdjustItemTowardsCrossAxisEnd(writingModeToBlockFlowDirection(flexItemWritingModeForBaselineAlignment(flexItem)), position))
minMarginAfterBaseline = std::min(minMarginAfterBaseline, availableAlignmentSpaceForChild(lineCrossAxisExtent, flexItem) - offset);
}
}

// wrap-reverse flips the cross axis start and end. For baseline alignment,
// this means we need to align the after edge of baseline elements with the
// after edge of the flex line. We can also use the same logic for last
// baseline alignment since those items must be pushed towards the cross
// start as well.
if (minMarginAfterBaseline) {
for (const auto& flexItem : lineState.flexItems) {
if (shouldAdjustItemTowardsCrossAxis(alignmentForChild(flexItem.box)) && !hasAutoMarginsInCrossAxis(flexItem.box))
adjustAlignmentForChild(flexItem.box, minMarginAfterBaseline);
// css-align-3 9.3 part 3:
// Position the aligned baseline-sharing group within the alignment container according to its
// fallback alignment. The fallback alignment of a baseline-sharing group is the fallback alignment
// of its items as resolved to physical directions.
if (minMarginAfterBaseline) {
for (auto& flexItem : baselineSharingGroup) {
if (shouldAdjustItemTowardsCrossAxisEnd(writingModeToBlockFlowDirection(flexItemWritingModeForBaselineAlignment(flexItem)), alignmentForChild(flexItem)) && !hasAutoMarginsInCrossAxis(flexItem))
adjustAlignmentForChild(flexItem, minMarginAfterBaseline);
}
}
}
}
Expand Down
27 changes: 27 additions & 0 deletions Source/WebCore/rendering/RenderFlexibleBox.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@

#include "OrderIterator.h"
#include "RenderBlock.h"
#include "RenderStyleInlines.h"

namespace WebCore {

Expand All @@ -47,6 +48,8 @@ class RenderFlexibleBox : public RenderBlock {
RenderFlexibleBox(Document&, RenderStyle&&);
virtual ~RenderFlexibleBox();

using Direction = BlockFlowDirection;

bool isFlexibleBox() const override { return true; }

ASCIILiteral renderName() const override;
Expand All @@ -66,6 +69,30 @@ class RenderFlexibleBox : public RenderBlock {
void paintChildren(PaintInfo& forSelf, const LayoutPoint&, PaintInfo& forChild, bool usePrintRect) override;

bool isHorizontalFlow() const;
inline Direction crossAxisDirection() const
{
switch (writingModeToBlockFlowDirection(style().writingMode())) {
case BlockFlowDirection::TopToBottom:
if (style().isRowFlexDirection())
return (style().flexWrap() == FlexWrap::Reverse) ? Direction::BottomToTop : Direction::TopToBottom;
return (style().flexWrap() == FlexWrap::Reverse) ? Direction::RightToLeft : Direction::LeftToRight;
case BlockFlowDirection::BottomToTop:
if (style().isRowFlexDirection())
return (style().flexWrap() == FlexWrap::Reverse) ? Direction::TopToBottom : Direction::BottomToTop;
return (style().flexWrap() == FlexWrap::Reverse) ? Direction::RightToLeft : Direction::LeftToRight;
case BlockFlowDirection::LeftToRight:
if (style().isRowFlexDirection())
return (style().flexWrap() == FlexWrap::Reverse) ? Direction::RightToLeft : Direction::LeftToRight;
return (style().flexWrap() == FlexWrap::Reverse) ? Direction::BottomToTop : Direction::TopToBottom;
case BlockFlowDirection::RightToLeft:
if (style().isRowFlexDirection())
return (style().flexWrap() == FlexWrap::Reverse) ? Direction::LeftToRight : Direction::RightToLeft;
return (style().flexWrap() == FlexWrap::Reverse) ? Direction::BottomToTop : Direction::TopToBottom;
default:
ASSERT_NOT_REACHED();
return Direction::TopToBottom;
}
}

const OrderIterator& orderIterator() const { return m_orderIterator; }

Expand Down
1 change: 1 addition & 0 deletions Source/WebCore/rendering/style/RenderStyle.h
Original file line number Diff line number Diff line change
Expand Up @@ -731,6 +731,7 @@ class RenderStyle {
inline const StyleSelfAlignmentData& alignItems() const;
inline const StyleSelfAlignmentData& alignSelf() const;
inline FlexDirection flexDirection() const;
inline bool isRowFlexDirection() const;
inline bool isColumnFlexDirection() const;
inline bool isReverseFlexDirection() const;
inline FlexWrap flexWrap() const;
Expand Down
1 change: 1 addition & 0 deletions Source/WebCore/rendering/style/RenderStyleInlines.h
Original file line number Diff line number Diff line change
Expand Up @@ -502,6 +502,7 @@ inline Length RenderStyle::initialWordSpacing() { return zeroLength(); }
constexpr WritingMode RenderStyle::initialWritingMode() { return WritingMode::HorizontalTb; }
inline InputSecurity RenderStyle::inputSecurity() const { return static_cast<InputSecurity>(m_nonInheritedData->rareData->inputSecurity); }
inline bool RenderStyle::isColumnFlexDirection() const { return flexDirection() == FlexDirection::Column || flexDirection() == FlexDirection::ColumnReverse; }
inline bool RenderStyle::isRowFlexDirection() const { return flexDirection() == FlexDirection::Row || flexDirection() == FlexDirection::RowReverse; }
constexpr bool RenderStyle::isDisplayBlockLevel() const { return isDisplayBlockType(display()); }
constexpr bool RenderStyle::isDisplayDeprecatedFlexibleBox(DisplayType display) { return display == DisplayType::Box || display == DisplayType::InlineBox; }
constexpr bool RenderStyle::isDisplayFlexibleBox(DisplayType display) { return display == DisplayType::Flex || display == DisplayType::InlineFlex; }
Expand Down

0 comments on commit c4b5ac7

Please sign in to comment.