Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement last baseline alignment for Flexbox containers. #4067

Conversation

sammygill
Copy link
Contributor

@sammygill sammygill commented Sep 6, 2022

a7e014a

Implement last baseline alignment for Flexbox containers.
https://bugs.webkit.org/show_bug.cgi?id=244734
rdar://99506835

Reviewed by Brent Fulgham.

In order to implement last baseline alignment for flex containers, we
need to hold a little bit more information. Currently,
RenderFlexibleBox::LineContext holds a maxAscent value, which holds the
value of the furthest baseline in the cross axis. This is then used to
shift the other flex items to this baseline. A new field called
lastBaselineMaxAscent was added to achieve this same functionality for
the last baseline elements. This needs to be a separate field in
scenarios where you may have certain items being aligned by their
baselines and others by their last baselines.

This patch also adds the “margin before” to the first baseline of table
elements since it was not being added before. This is needed in order to
properly align other elements alongside a table with margins. Without
this addition, we were rendering the expectation for both
css-flexbox/flexbox-align-self-baseline-horiz-001a and
css-flexbox/flexbox-align-self-baseline-horiz-001b incorrectly.

* LayoutTests/TestExpectations:
* Source/WebCore/rendering/RenderFlexibleBox.cpp:
(WebCore::RenderFlexibleBox::LineContext::LineContext):
(WebCore::RenderFlexibleBox::marginBoxAscentForChild):
Since marginBoxAscentForChild is being called during alignment purposes,
we can determine what type of alignment is being done here and return
the appropriate ascent (first baseline vs last baseline).

(WebCore::alignmentOffset):
(WebCore::RenderFlexibleBox::layoutAndPlaceChildren):
Depending on whether the item is being first baseline aligned or last
baseline aligned, we need to either update maxAscent or
lastBaselineMaxAscent. Later on when the alignment is being done, we
can check the type of alignment one more time and use the correct
value.

(WebCore::RenderFlexibleBox::alignChildren):
Since items need to be pushed to the end of the cross axis when
wrap-reverse is specified, we can use the same logic to push the items
that are being last baseline aligned. There was also a little bit of
cleanup that I was able to do here. Instead of waiting until all of the
lines have had their items aligned to make any adjustments (e.g. due
to wrap-reverse), we can do it after laying out each line. We can
determine whether or not we need to loop over the line again by checking
the value of minMarginAfterBaseline. If this value was set then that
means that not only is there room to move the items in the flex line,
but also that we need to because the flex container has wrap-reverse
set or some items are being last baseline aligned.

(WebCore::RenderFlexibleBox::childIsParticipatingInBaselineAlignment const):
* Source/WebCore/rendering/RenderFlexibleBox.h:
* Source/WebCore/rendering/RenderTableSection.cpp:
(WebCore::RenderTable::baselinePosition const):
This is not directly related to the last baseline implementation but to
a test case for it. There were 2 expectation files we were rendering
incorrectly
(css-flexbox/flexbox-align-self-baseline-horiz-001a-expected.xhtml and
flexbox-align-self-baseline-horiz-001b-expected.xhtml).

These were using the first baseline for table elements and we were not
computing the correct first baseline. This caused us to fail the tests
even though we were rendering the actual test file correctly.

Canonical link: https://commits.webkit.org/255383@main

c1e411a

Misc iOS, tvOS & watchOS macOS Linux Windows
✅ 🧪 style ✅ 🛠 ios ✅ 🛠 mac ✅ 🛠 wpe 🛠 🧪 win
✅ 🛠 ios-sim ✅ 🛠 mac-debug ✅ 🛠 gtk ✅ 🛠 wincairo
✅ 🧪 webkitperl ✅ 🧪 ios-wk2 ✅ 🛠 mac-AS-debug ✅ 🧪 gtk-wk2
✅ 🧪 api-ios ✅ 🧪 api-mac ✅ 🧪 api-gtk
✅ 🛠 tv 🧪 mac-wk1
✅ 🛠 tv-sim ✅ 🧪 mac-wk2
✅ 🛠 🧪 merge ✅ 🛠 watch ✅ 🧪 mac-AS-debug-wk2
✅ 🛠 watch-sim ✅ 🧪 mac-wk2-stress

@sammygill sammygill self-assigned this Sep 6, 2022
@sammygill sammygill added CSS Cascading Style Sheets implementation WebKit Nightly Build labels Sep 6, 2022
Source/WebCore/rendering/RenderBlockFlow.cpp Outdated Show resolved Hide resolved
Source/WebCore/rendering/RenderBlock.cpp Outdated Show resolved Hide resolved
@sammygill sammygill force-pushed the eng/Add-Flexbox-last-baseline-alignment-for-block-flow-items branch from 4296caa to 4f2c4cd Compare September 15, 2022 19:12
@webkit-early-warning-system
Copy link
Collaborator

Starting EWS tests for 4f2c4cd. Live statuses available at the PR page, #4067

@webkit-ews-buildbot webkit-ews-buildbot added the merging-blocked Applied to prevent a change from being merged label Sep 15, 2022
@sammygill sammygill removed the merging-blocked Applied to prevent a change from being merged label Sep 22, 2022
@sammygill sammygill force-pushed the eng/Add-Flexbox-last-baseline-alignment-for-block-flow-items branch from 4f2c4cd to ebec868 Compare September 22, 2022 22:31
@sammygill
Copy link
Contributor Author

Updated RenderBlockFlow::lastLineBaseline so that the call to modern layout lastLinePhysicalBaseline was guarded by an if instead of an assert. Now we should fall back to legacy line layout the scenarios where modernLineLayout returns a nullptr

@webkit-ews-buildbot webkit-ews-buildbot added the merging-blocked Applied to prevent a change from being merged label Sep 23, 2022
@sammygill sammygill removed the merging-blocked Applied to prevent a change from being merged label Sep 27, 2022
@sammygill sammygill force-pushed the eng/Add-Flexbox-last-baseline-alignment-for-block-flow-items branch from ebec868 to 49f2d52 Compare September 27, 2022 05:16
@sammygill sammygill force-pushed the eng/Add-Flexbox-last-baseline-alignment-for-block-flow-items branch from 49f2d52 to 63e7fc6 Compare September 27, 2022 17:36
@sammygill sammygill changed the title Implement last baseline for block elements in a flex container. Implement last baseline for block flow elements and last baseline alignment for Flexbox containers Sep 27, 2022
@sammygill sammygill force-pushed the eng/Add-Flexbox-last-baseline-alignment-for-block-flow-items branch from 63e7fc6 to 2ba17d5 Compare September 28, 2022 19:02
@sammygill sammygill changed the title Implement last baseline for block flow elements and last baseline alignment for Flexbox containers Implement last baseline alignment for Flexbox containers. Sep 28, 2022
@sammygill sammygill marked this pull request as draft October 4, 2022 16:43
Copy link
Contributor

@Loirooriol Loirooriol left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It sounds reasonable, but not an expert in baselines nor css-align in general.

Source/WebCore/rendering/RenderBlock.cpp Outdated Show resolved Hide resolved
LayoutUnit firstLineBaseline = m_grid[0].baseline;
if (firstLineBaseline)
return firstLineBaseline + m_rowPos[0];
return firstLineBaseline + m_rowPos[0] + tableMarginBefore;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure how this is related to the last baseline?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is not directly related to the last baseline implementation but to a test case for it. There were 2 expectation files we were rendering incorrectly (css-flexbox/flexbox-align-self-baseline-horiz-001a-expected.xhtml and flexbox-align-self-baseline-horiz-001b-expected.xhtml)

These were using the first baseline for table elements and we were not computing the correct first baseline. This caused us to fail the tests even though we were rendering the actual test file correctly. This was the incorrect way to do it and I moved the logic to RenderTable::baselinePosition. Now it is similar to other objects (e.g. RenderBlock::baselinePosition).

Source/WebCore/rendering/RenderFlexibleBox.cpp Outdated Show resolved Hide resolved
Source/WebCore/rendering/RenderFlexibleBox.cpp Outdated Show resolved Hide resolved
Source/WebCore/rendering/RenderFlexibleBox.cpp Outdated Show resolved Hide resolved
Source/WebCore/rendering/RenderFlexibleBox.cpp Outdated Show resolved Hide resolved
Source/WebCore/rendering/RenderFlexibleBox.cpp Outdated Show resolved Hide resolved
@sammygill sammygill force-pushed the eng/Add-Flexbox-last-baseline-alignment-for-block-flow-items branch from 2ba17d5 to 8710e23 Compare October 5, 2022 21:41
@sammygill sammygill marked this pull request as ready for review October 5, 2022 21:42
Comment on lines 2387 to 2393
bool RenderFlexibleBox::childIsParticipatingInBaselineAlignment(const RenderBox& child) const
{
auto alignment = alignmentForChild(child);
return (alignment == ItemPosition::Baseline || alignment == ItemPosition::LastBaseline) && !hasAutoMarginsInCrossAxis(child);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method only has a single caller and the logic is short, so adding it seems unnecessary to me.
By removing it you can reuse the alignmentForChild(child).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. It was useful before, but now after the cleanup it is much less useful. Changed.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's no longer used, remove it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed


bool containerHasWrapReverse = style().flexWrap() == FlexWrap::Reverse;
auto shouldAdjustItemTowardsCrossAxis = [this, containerHasWrapReverse](const RenderBox& box, ItemPosition alignment) {
return (containerHasWrapReverse && alignment == ItemPosition::Baseline && !hasAutoMarginsInCrossAxis(box)) || (alignment == ItemPosition::LastBaseline && !containerHasWrapReverse);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why check !hasAutoMarginsInCrossAxis(box) for the 1st baseline but not for the last one?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it should be checked in both cases, then the logic could be shared with another conditional:

    auto something = [containerHasWrapReverse](ItemPosition alignment) { 
        return (containerHasWrapReverse && alignment == ItemPosition::Baseline) || (alignment == ItemPosition::LastBaseline && !containerHasWrapReverse); 
    };
// ...
            if (something(position))
                minMarginAfterBaseline = std::min(minMarginAfterBaseline, availableAlignmentSpaceForChild(lineCrossAxisExtent, flexItem.box) - offset);
// ...
                if (!hasAutoMarginsInCrossAxis(flexItem.box) && something(alignmentForChild(flexItem.box)))
                    adjustAlignmentForChild(flexItem.box, minMarginAfterBaseline);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes it should be for both. I made the adjustment, but let me know if you see any other issues with this

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove !hasAutoMarginsInCrossAxis(box) from shouldAdjustItemTowardsCrossAxis since the caller that needs it is already checking it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed now

@sammygill sammygill force-pushed the eng/Add-Flexbox-last-baseline-alignment-for-block-flow-items branch from c97879e to 9336c2b Compare October 5, 2022 23:45
@sammygill sammygill force-pushed the eng/Add-Flexbox-last-baseline-alignment-for-block-flow-items branch from 9336c2b to 14fc529 Compare October 6, 2022 00:05
@sammygill sammygill force-pushed the eng/Add-Flexbox-last-baseline-alignment-for-block-flow-items branch from 14fc529 to 2cffcf6 Compare October 6, 2022 00:10
@webkit-ews-buildbot webkit-ews-buildbot added the merging-blocked Applied to prevent a change from being merged label Oct 6, 2022
Copy link
Contributor

@brentfulgham brentfulgham left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM with the changes Loirooriol (and I) suggested.

Source/WebCore/rendering/RenderFlexibleBox.cpp Outdated Show resolved Hide resolved
Source/WebCore/rendering/RenderTable.cpp Outdated Show resolved Hide resolved
Copy link
Contributor

@Loirooriol Loirooriol left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Informal LGTM with the suggested changes

Comment on lines 2387 to 2393
bool RenderFlexibleBox::childIsParticipatingInBaselineAlignment(const RenderBox& child) const
{
auto alignment = alignmentForChild(child);
return (alignment == ItemPosition::Baseline || alignment == ItemPosition::LastBaseline) && !hasAutoMarginsInCrossAxis(child);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's no longer used, remove it.


bool containerHasWrapReverse = style().flexWrap() == FlexWrap::Reverse;
auto shouldAdjustItemTowardsCrossAxis = [this, containerHasWrapReverse](const RenderBox& box, ItemPosition alignment) {
return (containerHasWrapReverse && alignment == ItemPosition::Baseline && !hasAutoMarginsInCrossAxis(box)) || (alignment == ItemPosition::LastBaseline && !containerHasWrapReverse);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove !hasAutoMarginsInCrossAxis(box) from shouldAdjustItemTowardsCrossAxis since the caller that needs it is already checking it?

});
auto baselinePos = firstLineBaseline();
if (baselinePos)
return (direction == HorizontalLine ? marginTop() : marginRight()) + firstLineBaseline().value();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I get that you need this fix for some tests, but it's actually fixing the reference, not the flexbox tests, so it seems strange to do it here.
I would prefer to do this change in its own PR, and have a specific test not involving flexbox, like comparing

<div style="display: inline-block; background: lime;">line 1</div>
<div style="display: inline-table; margin-top: 20px; background: aqua;">line 1<br/>line 2</div>

with this reference:

<div style="display: inline-block; margin-top: 20px; background: lime; vertical-align: top">line 1</div>
<div style="display: inline-block; margin-top: 20px; background: aqua;">line 1<br/>line 2</div>

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense. I refactored it out and created its own PR

#5153

@sammygill sammygill removed the merging-blocked Applied to prevent a change from being merged label Oct 7, 2022
@sammygill sammygill force-pushed the eng/Add-Flexbox-last-baseline-alignment-for-block-flow-items branch from 2cffcf6 to 132311a Compare October 7, 2022 19:41
@sammygill
Copy link
Contributor Author

sammygill commented Oct 7, 2022

@webkit-ews-buildbot webkit-ews-buildbot added the merging-blocked Applied to prevent a change from being merged label Oct 8, 2022
@sammygill sammygill removed the merging-blocked Applied to prevent a change from being merged label Oct 10, 2022
@sammygill sammygill force-pushed the eng/Add-Flexbox-last-baseline-alignment-for-block-flow-items branch from 132311a to 6857a96 Compare October 10, 2022 19:21
@sammygill
Copy link
Contributor Author

Rebaselined test expectations with the new tests imported from WPT

@webkit-ews-buildbot webkit-ews-buildbot added the merging-blocked Applied to prevent a change from being merged label Oct 11, 2022
@sammygill sammygill removed the merging-blocked Applied to prevent a change from being merged label Oct 11, 2022
@sammygill sammygill force-pushed the eng/Add-Flexbox-last-baseline-alignment-for-block-flow-items branch from 6857a96 to c1e411a Compare October 11, 2022 01:11
@rreno rreno added the merge-queue Applied to send a pull request to merge-queue label Oct 11, 2022
https://bugs.webkit.org/show_bug.cgi?id=244734
rdar://99506835

Reviewed by Brent Fulgham.

In order to implement last baseline alignment for flex containers, we
need to hold a little bit more information. Currently,
RenderFlexibleBox::LineContext holds a maxAscent value, which holds the
value of the furthest baseline in the cross axis. This is then used to
shift the other flex items to this baseline. A new field called
lastBaselineMaxAscent was added to achieve this same functionality for
the last baseline elements. This needs to be a separate field in
scenarios where you may have certain items being aligned by their
baselines and others by their last baselines.

This patch also adds the “margin before” to the first baseline of table
elements since it was not being added before. This is needed in order to
properly align other elements alongside a table with margins. Without
this addition, we were rendering the expectation for both
css-flexbox/flexbox-align-self-baseline-horiz-001a and
css-flexbox/flexbox-align-self-baseline-horiz-001b incorrectly.

* LayoutTests/TestExpectations:
* Source/WebCore/rendering/RenderFlexibleBox.cpp:
(WebCore::RenderFlexibleBox::LineContext::LineContext):
(WebCore::RenderFlexibleBox::marginBoxAscentForChild):
Since marginBoxAscentForChild is being called during alignment purposes,
we can determine what type of alignment is being done here and return
the appropriate ascent (first baseline vs last baseline).

(WebCore::alignmentOffset):
(WebCore::RenderFlexibleBox::layoutAndPlaceChildren):
Depending on whether the item is being first baseline aligned or last
baseline aligned, we need to either update maxAscent or
lastBaselineMaxAscent. Later on when the alignment is being done, we
can check the type of alignment one more time and use the correct
value.

(WebCore::RenderFlexibleBox::alignChildren):
Since items need to be pushed to the end of the cross axis when
wrap-reverse is specified, we can use the same logic to push the items
that are being last baseline aligned. There was also a little bit of
cleanup that I was able to do here. Instead of waiting until all of the
lines have had their items aligned to make any adjustments (e.g. due
to wrap-reverse), we can do it after laying out each line. We can
determine whether or not we need to loop over the line again by checking
the value of minMarginAfterBaseline. If this value was set then that
means that not only is there room to move the items in the flex line,
but also that we need to because the flex container has wrap-reverse
set or some items are being last baseline aligned.

(WebCore::RenderFlexibleBox::childIsParticipatingInBaselineAlignment const):
* Source/WebCore/rendering/RenderFlexibleBox.h:
* Source/WebCore/rendering/RenderTableSection.cpp:
(WebCore::RenderTable::baselinePosition const):
This is not directly related to the last baseline implementation but to
a test case for it. There were 2 expectation files we were rendering
incorrectly
(css-flexbox/flexbox-align-self-baseline-horiz-001a-expected.xhtml and
flexbox-align-self-baseline-horiz-001b-expected.xhtml).

These were using the first baseline for table elements and we were not
computing the correct first baseline. This caused us to fail the tests
even though we were rendering the actual test file correctly.

Canonical link: https://commits.webkit.org/255383@main
@webkit-early-warning-system webkit-early-warning-system force-pushed the eng/Add-Flexbox-last-baseline-alignment-for-block-flow-items branch from c1e411a to a7e014a Compare October 11, 2022 15:35
@webkit-commit-queue
Copy link
Collaborator

Committed 255383@main (a7e014a): https://commits.webkit.org/255383@main

Reviewed commits have been landed. Closing PR #4067 and removing active labels.

@webkit-commit-queue webkit-commit-queue removed the merge-queue Applied to send a pull request to merge-queue label Oct 11, 2022
@webkit-early-warning-system webkit-early-warning-system merged commit a7e014a into WebKit:main Oct 11, 2022
@sammygill sammygill deleted the eng/Add-Flexbox-last-baseline-alignment-for-block-flow-items branch November 30, 2022 19:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CSS Cascading Style Sheets implementation
Projects
None yet
9 participants