Bug 223570

Summary: [css-contain] Support contain:size
Product: WebKit Reporter: cathiechen <cathiechen>
Component: Layout and RenderingAssignee: cathiechen <cathiechen>
Status: RESOLVED FIXED    
Severity: Normal CC: bfulgham, cathiechen, cdumez, changseok, darin, dino, eric.carlson, esprehn+autocc, ews-watchlist, fmalita, glenn, gyuyoung.kim, jer.noble, jfernandez, koivisto, kondapallykalyan, mifenton, mmaxfield, pdr, philipj, rbuis, rego, rniwa, sabouhallawa, schenney, sergio, simon.fraser, svillar, webkit-bug-importer, zalan
Priority: P2 Keywords: InRadar
Version: WebKit Nightly Build   
Hardware: Unspecified   
OS: Unspecified   
See Also: https://bugs.webkit.org/show_bug.cgi?id=227232
Bug Depends on:    
Bug Blocks: 172026    
Attachments:
Description Flags
size-containment-WIP
none
WIP-patch-for-size-containment
none
Patch
none
Patch
none
Patch
none
Patch
ews-feeder: commit-queue-
Patch
none
Patch
none
Patch
none
Patch
none
Patch
none
Patch none

Description cathiechen 2021-03-22 02:20:11 PDT
Support contain:size;
Comment 1 Radar WebKit Bug Importer 2021-03-29 09:14:11 PDT
<rdar://problem/75956816>
Comment 2 cathiechen 2021-04-01 09:18:26 PDT
Created attachment 424901 [details]
size-containment-WIP
Comment 3 cathiechen 2021-04-01 09:29:04 PDT
Created attachment 424903 [details]
WIP-patch-for-size-containment
Comment 4 cathiechen 2021-04-12 09:08:06 PDT
Created attachment 425756 [details]
Patch
Comment 5 Rob Buis 2021-04-14 06:48:51 PDT
Comment on attachment 425756 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=425756&action=review

> Source/WebCore/ChangeLog:8
> +        This patch brings initial support of CSS contain:size. According to https://www.w3.org/TR/css-contain-2/#containment-size,

Probably better to refer to contain-1, could also be a footnote ([1]). In general these lines should be kept short IIUC.

> Source/WebCore/rendering/GridTrackSizingAlgorithm.cpp:1152
> +    if (!m_strategy->isComputingSizeContainment()) {

The strategy stuff probably needs explaining in the Changelog.

> Source/WebCore/rendering/RenderBlock.cpp:1078
> +        downcast<RenderBlockFlow>(*this).adjustSizeContainmentChildForPagination(r, r.logicalTop());

The pagination probably needs explaining in the Changelog.

> Source/WebCore/rendering/RenderBox.cpp:2880
> +        auto borderAndPading = RenderBox::borderBefore() + RenderBox::paddingBefore() + RenderBox::borderAfter() + RenderBox::paddingAfter();

There is borderAndPaddingBefore and borderAndPaddingAfter. *borderAndPadding*

> Source/WebCore/rendering/RenderMultiColumnSet.cpp:240
> +    if (columnCount() <= computedColumnCount() /*&& m_minSpaceShortage <= 0*/) {

For the final patch we should have no code in comments.

> Source/WebCore/rendering/RenderMultiColumnSet.cpp:245
> +    if (forcedBreaksCount() >= computedColumnCount() /*&& m_minSpaceShortage <= 0*/) {

Ditto.

> Source/WebCore/rendering/RenderMultiColumnSet.cpp:257
> +        return m_computedColumnHeight + sizeContainmentShortage; // So bail out rather than looping infinitely.

The sizeContainmentShortage probably also needs ChangeLog explanation.
Comment 6 cathiechen 2021-04-14 08:23:41 PDT
Comment on attachment 425756 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=425756&action=review

Hi Rob,
Thanks for the review!

>> Source/WebCore/ChangeLog:8
>> +        This patch brings initial support of CSS contain:size. According to https://www.w3.org/TR/css-contain-2/#containment-size,
> 
> Probably better to refer to contain-1, could also be a footnote ([1]). In general these lines should be kept short IIUC.

Done, thanks!

>> Source/WebCore/rendering/GridTrackSizingAlgorithm.cpp:1152
>> +    if (!m_strategy->isComputingSizeContainment()) {
> 
> The strategy stuff probably needs explaining in the Changelog.

Yeah, will explain in the latest version:)

>> Source/WebCore/rendering/RenderBlock.cpp:1078
>> +        downcast<RenderBlockFlow>(*this).adjustSizeContainmentChildForPagination(r, r.logicalTop());
> 
> The pagination probably needs explaining in the Changelog.

Ditto.

>> Source/WebCore/rendering/RenderBox.cpp:2880
>> +        auto borderAndPading = RenderBox::borderBefore() + RenderBox::paddingBefore() + RenderBox::borderAfter() + RenderBox::paddingAfter();
> 
> There is borderAndPaddingBefore and borderAndPaddingAfter. *borderAndPadding*

There is a slight difference for fieldset. borderAndPaddingBefore add extra intrinsicBorderForFieldset for fieldset. but size containment doesn't need that.
I will add a comment in the new patch.

>> Source/WebCore/rendering/RenderMultiColumnSet.cpp:240
>> +    if (columnCount() <= computedColumnCount() /*&& m_minSpaceShortage <= 0*/) {
> 
> For the final patch we should have no code in comments.

Ah, done! Thanks!

>> Source/WebCore/rendering/RenderMultiColumnSet.cpp:257
>> +        return m_computedColumnHeight + sizeContainmentShortage; // So bail out rather than looping infinitely.
> 
> The sizeContainmentShortage probably also needs ChangeLog explanation.

Done!
Comment 7 cathiechen 2021-04-14 08:33:56 PDT
Created attachment 425987 [details]
Patch
Comment 8 cathiechen 2021-04-14 23:51:43 PDT
Comment on attachment 425987 [details]
Patch

Hi,
I think this patch is ready for review. Thanks!
Comment 9 Darin Adler 2021-04-17 21:16:11 PDT
Looks good to me, but I think it should be reviewed by someone who’s been working in this area recently.
Comment 10 cathiechen 2021-04-19 10:03:15 PDT
Created attachment 426439 [details]
Patch
Comment 11 cathiechen 2021-04-19 10:17:39 PDT
(In reply to Darin Adler from comment #9)
> Looks good to me, but I think it should be reviewed by someone who’s been
> working in this area recently.

Hi Darin,
Thanks! I added Simon to the cc list:)

The new patch is now rebased on the code which supports contain:layout.
Luckily we have 3 more tests passed in this patch.
  imported/w3c/web-platform-tests/css/css-contain/contain-animation-001.html
  imported/w3c/web-platform-tests/css/css-contain/contain-layout-size-003.html
  imported/w3c/web-platform-tests/css/css-contain/contain-strict-003.html
Comment 12 cathiechen 2021-04-20 04:29:16 PDT
Created attachment 426537 [details]
Patch
Comment 13 cathiechen 2021-04-20 06:29:51 PDT
Created attachment 426543 [details]
Patch
Comment 14 Sergio Villar Senin 2021-05-03 04:23:13 PDT
Comment on attachment 426543 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=426543&action=review

Nice work. I've mostly focused of the parts I know the most like grid.

> Source/WebCore/rendering/GridTrackSizingAlgorithm.cpp:1186
>      for (auto trackIndex : m_contentSizedTracksIndex) {

So, if I understood this correctly, this loop is the only thing you have to do for the case of computing content:size. It that's the case then I'd do the following:
1- move the loop into a lambda inside this method.
2- at the beginning of the method do something like
if (m_strategy->isComputingSizeContainment()) {
   execTheCallback();
   return;
}

That would allow you to keep the method almost untouched (except for the lambda) and there would be no need to change indentation and all that.

> Source/WebCore/rendering/GridTrackSizingAlgorithm.cpp:1247
> +            m_sizingState = ColumnSizingSecondIteration;

I am not sure I understand this change. I guess you basically compute content:size as if nothing had happened, so the first iteration of row computing could happen. My question is, why not adding a new state to the state machine? Something that would happen after RowSizingFirstIteration?

> Source/WebCore/rendering/RenderBox.cpp:2880
> +        // borderAndPaddingLogicalHeight contains intrinsicBorderForFieldset() which add extra border for fieldset, so we can't use it directly.

Are you sure this is correct? I don't see any intrinsicBorderForFieldset() in the computation of borderAndPaddingLogicalHeight()

> Source/WebCore/rendering/RenderDeprecatedFlexibleBox.cpp:211
> +            }

Same comment here as the method in grid. I'd create a lambda with the code bellow that just assigns the maxLogicalWidth and then adds the scrollbarwith and then at the beginning of the method
if (shouldApplySizeContainment(*this)) {
   callLambda();
   return;
}
.... body of the method untouched ...
callLambda()

> Source/WebCore/rendering/RenderFlexibleBox.cpp:141
> +        }

Ditto for this method. If the only thing you have to do is to add the scrollbarWidth to the min and max for the case of size containment, then move it to a lambda and
if (shouldApplySizeContainment()) {
 lambda();
 return;
}
... body of the method untouched ...
lambda()

> Source/WebCore/rendering/RenderGrid.cpp:467
> +        return 0;

I am not sure I understand this change. Auto-fit is basically like auto-fill except that empty columns collapse. The fact that they collapse do not mean that they don't exist. But here we're saying that there are no auto-fit tracks. Maybe I am missing something?
Comment 15 cathiechen 2021-05-04 08:01:06 PDT
Comment on attachment 426543 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=426543&action=review

Hi Sergio,
Thanks for the review!

>> Source/WebCore/rendering/GridTrackSizingAlgorithm.cpp:1186
>>      for (auto trackIndex : m_contentSizedTracksIndex) {
> 
> So, if I understood this correctly, this loop is the only thing you have to do for the case of computing content:size. It that's the case then I'd do the following:
> 1- move the loop into a lambda inside this method.
> 2- at the beginning of the method do something like
> if (m_strategy->isComputingSizeContainment()) {
>    execTheCallback();
>    return;
> }
> 
> That would allow you to keep the method almost untouched (except for the lambda) and there would be no need to change indentation and all that.

Done! Yeah, a lambda function makes it more readable, the code is put to resolveIntrinsicTrackSizesFromChildren().
`sizeTrackToFitNonSpanningItem(span, *gridItem, track);` changes track sizes, so we also need to put this loop into the lambda.
And we need to expand tack.growthLimit() to track.baseSize() if tack.growthLimit() is infinity which is handled at the end of this function, so instead of retrun early, it will be changed to 
    if (!m_strategy->isComputingSizeContainment())
        resolveIntrinsicTrackSizesFromChildren();

>> Source/WebCore/rendering/GridTrackSizingAlgorithm.cpp:1247
>> +            m_sizingState = ColumnSizingSecondIteration;
> 
> I am not sure I understand this change. I guess you basically compute content:size as if nothing had happened, so the first iteration of row computing could happen. My question is, why not adding a new state to the state machine? Something that would happen after RowSizingFirstIteration?

Yes, in RenderGrid::layoutBlock it computes track height twice if shouldApplySizeContainment(*this).
  * computeTrackSizesForIndefiniteSize(m_trackSizingAlgorithm, ForRows); To compute trackBasedLogicalHeight as if there's no content. trackBasedLogicalHeight is used in the second time.
  * computeTrackSizesForDefiniteSize(ForRows, trackBasedLogicalHeight); Using trackBasedLogicalHeight computed by the first time to distribute the track sizes.
So the state stays in RowSizingFirstIteration twice.
I will add a new state RowSizingExtraIterationForSizeContainment to indicate that is the extra try for size containment.

>> Source/WebCore/rendering/RenderBox.cpp:2880
>> +        // borderAndPaddingLogicalHeight contains intrinsicBorderForFieldset() which add extra border for fieldset, so we can't use it directly.
> 
> Are you sure this is correct? I don't see any intrinsicBorderForFieldset() in the computation of borderAndPaddingLogicalHeight()

Yes, borderAndPaddingLogicalHeight calls borderBefore() and borderAfter(). In RenderBlock::borderBefore(), it adds intrinsicBorderForFieldset().
I'll point out it is specific to RenderBlock in the comment.

>> Source/WebCore/rendering/RenderDeprecatedFlexibleBox.cpp:211
>> +            }
> 
> Same comment here as the method in grid. I'd create a lambda with the code bellow that just assigns the maxLogicalWidth and then adds the scrollbarwith and then at the beginning of the method
> if (shouldApplySizeContainment(*this)) {
>    callLambda();
>    return;
> }
> .... body of the method untouched ...
> callLambda()

Done! Put the code to computeIntrinsicLogicalWidthsFromChildren.
And also we need to handle the scrollbar width at the end of function, the code is now looked like this:
    if (!shouldApplySizeContainment(*this))
        computeIntrinsicLogicalWidthsFromChildren();

>> Source/WebCore/rendering/RenderFlexibleBox.cpp:141
>> +        }
> 
> Ditto for this method. If the only thing you have to do is to add the scrollbarWidth to the min and max for the case of size containment, then move it to a lambda and
> if (shouldApplySizeContainment()) {
>  lambda();
>  return;
> }
> ... body of the method untouched ...
> lambda()

Done! Create a lambda function computeIntrinsicLogicalWidthsFromChildren. And it looks like this now:
    if (!shouldApplySizeContainment(*this))
        computeIntrinsicLogicalWidthsFromChildren();

>> Source/WebCore/rendering/RenderGrid.cpp:467
>> +        return 0;
> 
> I am not sure I understand this change. Auto-fit is basically like auto-fill except that empty columns collapse. The fact that they collapse do not mean that they don't exist. But here we're saying that there are no auto-fit tracks. Maybe I am missing something?

Hmmm, make sense. I think I should check size containment in computeEmptyTracksForAutoRepeat() instead.
Comment 16 cathiechen 2021-05-04 08:07:17 PDT
Created attachment 427668 [details]
Patch
Comment 17 cathiechen 2021-05-05 01:21:49 PDT
Created attachment 427740 [details]
Patch
Comment 18 cathiechen 2021-05-05 01:27:30 PDT
(In reply to cathiechen from comment #15)
> Comment on attachment 426543 [details]
> Patch
> 
> View in context:
> https://bugs.webkit.org/attachment.cgi?id=426543&action=review
> 
> Hi Sergio,
> Thanks for the review!
> 
> >> Source/WebCore/rendering/GridTrackSizingAlgorithm.cpp:1186
> >>      for (auto trackIndex : m_contentSizedTracksIndex) {
> > 
> > So, if I understood this correctly, this loop is the only thing you have to do for the case of computing content:size. It that's the case then I'd do the following:
> > 1- move the loop into a lambda inside this method.
> > 2- at the beginning of the method do something like
> > if (m_strategy->isComputingSizeContainment()) {
> >    execTheCallback();
> >    return;
> > }
> > 
> > That would allow you to keep the method almost untouched (except for the lambda) and there would be no need to change indentation and all that.
> 
> Done! Yeah, a lambda function makes it more readable, the code is put to
> resolveIntrinsicTrackSizesFromChildren().
> `sizeTrackToFitNonSpanningItem(span, *gridItem, track);` changes track
> sizes, so we also need to put this loop into the lambda.
> And we need to expand tack.growthLimit() to track.baseSize() if
> tack.growthLimit() is infinity which is handled at the end of this function,
> so instead of retrun early, it will be changed to 
>     if (!m_strategy->isComputingSizeContainment())
>         resolveIntrinsicTrackSizesFromChildren();
> 

Sorry, I misunderstood this! The new patch puts the code handle infinity growthLimit into a lambda instead:)
Comment 19 Sergio Villar Senin 2021-05-05 01:41:21 PDT
Comment on attachment 427740 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=427740&action=review

I've reviewed the grid and flex parts and they both look great to me now.

> Source/WebCore/rendering/RenderDeprecatedFlexibleBox.cpp:190
> +    auto handleScrollbarWidth = [&]() {

Nit: addScrollbarWidth ?

> Source/WebCore/rendering/RenderFlexibleBox.cpp:86
> +    auto handleScrollbarWidth = [&]() {

Nit: maybe addScrollbarWidth?
Comment 20 cathiechen 2021-05-05 01:45:32 PDT
Comment on attachment 427740 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=427740&action=review

>> Source/WebCore/rendering/RenderDeprecatedFlexibleBox.cpp:190
>> +    auto handleScrollbarWidth = [&]() {
> 
> Nit: addScrollbarWidth ?

Done, thanks!

>> Source/WebCore/rendering/RenderFlexibleBox.cpp:86
>> +    auto handleScrollbarWidth = [&]() {
> 
> Nit: maybe addScrollbarWidth?

Done!
Comment 21 cathiechen 2021-05-05 01:49:15 PDT
Created attachment 427744 [details]
Patch
Comment 22 zalan 2021-05-10 21:04:57 PDT
Comment on attachment 427744 [details]
Patch

This is one of those "hard to review" patches. it's really difficult to tell if it covers all the cases or whether it does not cause any unexpected states in the renderers. I even added "contain: size" to LFC to have a better understanding but it did not really help. Guess we'll just need to deal with the potential fallout from this.
Comment 23 Rob Buis 2021-05-10 23:59:37 PDT
Comment on attachment 427744 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=427744&action=review

> Source/WebCore/rendering/RenderBox.cpp:2865
> +        auto borderAndPading = RenderBox::borderBefore() + RenderBox::paddingBefore() + RenderBox::borderAfter() + RenderBox::paddingAfter();

Can use borderAndPaddingBefore/borderAndPaddingAfter here.
Comment 24 cathiechen 2021-05-11 01:34:44 PDT
Created attachment 428256 [details]
Patch
Comment 25 cathiechen 2021-05-11 01:38:10 PDT
(In reply to Rob Buis from comment #23)
> Comment on attachment 427744 [details]
> Patch
> 
> View in context:
> https://bugs.webkit.org/attachment.cgi?id=427744&action=review
> 
> > Source/WebCore/rendering/RenderBox.cpp:2865
> > +        auto borderAndPading = RenderBox::borderBefore() + RenderBox::paddingBefore() + RenderBox::borderAfter() + RenderBox::paddingAfter();
> 
> Can use borderAndPaddingBefore/borderAndPaddingAfter here.

Thanks, Rob!

The borderAndPadding* interfaces eventually call RenderBlock::borderBefore which addes intrinsicBorderForFieldset(). I've added more detailed explanation in the comments.
Comment 26 cathiechen 2021-05-11 01:42:47 PDT
(In reply to zalan from comment #22)
> Comment on attachment 427744 [details]
> Patch
> 
> This is one of those "hard to review" patches. it's really difficult to tell
> if it covers all the cases or whether it does not cause any unexpected
> states in the renderers. I even added "contain: size" to LFC to have a
> better understanding but it did not really help. Guess we'll just need to
> deal with the potential fallout from this.

Hi zalan,

Thanks a lot for doing that!
I think I can keep eyes on the follow-up bugs if any.
Comment 27 EWS 2021-05-11 04:57:20 PDT
Zalan Bujtas found in /Volumes/Data/worker/Commit-Queue/build/LayoutTests/ChangeLog does not appear to be a valid reviewer according to contributors.json.
/Volumes/Data/worker/Commit-Queue/build/LayoutTests/ChangeLog neither lists a valid reviewer nor contains the string "Unreviewed" or "Rubber stamp" (case insensitive).
Comment 28 cathiechen 2021-05-11 05:12:04 PDT
Created attachment 428264 [details]
Patch
Comment 29 EWS 2021-05-11 05:49:53 PDT
Committed r277321 (237580@main): <https://commits.webkit.org/237580@main>

All reviewed patches have been landed. Closing bug and clearing flags on attachment 428264 [details].