Bug 218087

Summary: [css-logical] Add logical values for 'float' and 'clear'
Product: WebKit Reporter: Simon Fraser (smfr) <simon.fraser>
Component: CSSAssignee: Tim Nguyen (:ntim) <ntim>
Status: RESOLVED FIXED    
Severity: Normal CC: changseok, clopez, darin, esprehn+autocc, ews-watchlist, glenn, gyuyoung.kim, koivisto, kondapallykalyan, macpherson, menard, ntim, pdr, simon.fraser, twilco.o, webkit-bug-importer, youennf, zalan
Priority: P2 Keywords: InRadar
Version: Safari Technology Preview   
Hardware: Unspecified   
OS: Unspecified   
Bug Depends on:    
Bug Blocks: 185977    
Attachments:
Description Flags
Patch
ews-feeder: commit-queue-
Patch
none
Patch
ews-feeder: commit-queue-
Patch
none
Patch
none
Patch none

Description Simon Fraser (smfr) 2020-10-22 10:24:12 PDT
Add logical values for 'float' and 'clear':
https://drafts.csswg.org/css-logical-1/#float-clear
Comment 1 Radar WebKit Bug Importer 2020-10-22 10:24:30 PDT
<rdar://problem/70578855>
Comment 2 Tim Nguyen (:ntim) 2021-04-02 07:26:16 PDT
Created attachment 425018 [details]
Patch
Comment 3 Tim Nguyen (:ntim) 2021-04-02 09:18:39 PDT
Created attachment 425028 [details]
Patch
Comment 4 Tyler Wilcock 2021-04-06 10:07:09 PDT
Comment on attachment 425028 [details]
Patch

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

> Source/WebCore/layout/layouttree/LayoutBox.cpp:208
> +Float Box::physicalFloat() const
> +{
> +    bool isInlineFlipped = containingBlockDirection() == TextDirection::RTL;
> +    Float currentValue = style().floating();
> +
> +    if (currentValue == Float::InlineStart)
> +        return !isInlineFlipped ? Float::Left : Float::Right;
> +
> +    if (currentValue == Float::InlineEnd)
> +        return !isInlineFlipped ? Float::Right : Float::Left;
> +
> +    return currentValue;
> +}

I think you can move `physicalFloat` and `physicalClear` to RenderStyle, which stores the inherited direction property.

https://github.com/WebKit/WebKit/blob/538d690b5989b90efcb107a879dbbae8c018e548/Source/WebCore/rendering/style/RenderStyle.h#L392

Float RenderStyle::physicalFloat() const
{
    bool isLtr = direction() == TextDirection::LTR;
    Float value = style().floating();

    if (currentValue == Float::InlineStart)
        return isLtr ? Float::Left : Float::Right;

    if (currentValue == Float::InlineEnd)
        return isLtr ? Float::Right : Float::Left;

    return value;
}

> Source/WebCore/rendering/style/RenderStyle.h:1872
> +        unsigned clear : 4; // Clear

This can be represented by 3 bits (up to 8 values).

> Source/WebCore/rendering/style/RenderStyle.h:1875
> +        unsigned floating : 4; // Float

This can be represented by 3 bits (up to 8 values).
Comment 5 Tim Nguyen (:ntim) 2021-04-06 10:31:50 PDT
(In reply to Tyler Wilcock from comment #4)
> Comment on attachment 425028 [details]
> Patch
> 
> View in context:
> https://bugs.webkit.org/attachment.cgi?id=425028&action=review
> 
> > Source/WebCore/layout/layouttree/LayoutBox.cpp:208
> > +Float Box::physicalFloat() const
> > +{
> > +    bool isInlineFlipped = containingBlockDirection() == TextDirection::RTL;
> > +    Float currentValue = style().floating();
> > +
> > +    if (currentValue == Float::InlineStart)
> > +        return !isInlineFlipped ? Float::Left : Float::Right;
> > +
> > +    if (currentValue == Float::InlineEnd)
> > +        return !isInlineFlipped ? Float::Right : Float::Left;
> > +
> > +    return currentValue;
> > +}
> 
> I think you can move `physicalFloat` and `physicalClear` to RenderStyle,
> which stores the inherited direction property.
> 
> https://github.com/WebKit/WebKit/blob/
> 538d690b5989b90efcb107a879dbbae8c018e548/Source/WebCore/rendering/style/
> RenderStyle.h#L392
> 
> Float RenderStyle::physicalFloat() const
> {
>     bool isLtr = direction() == TextDirection::LTR;
>     Float value = style().floating();
> 
>     if (currentValue == Float::InlineStart)
>         return isLtr ? Float::Left : Float::Right;
> 
>     if (currentValue == Float::InlineEnd)
>         return isLtr ? Float::Right : Float::Left;
> 
>     return value;
> }


This needs to be done at the layout level, the spec clearly says: "The mapping on these properties uses the writing mode of the element’s containing block."

Doing this in `RenderStyle` fails because in this case:

> <div style="direction: rtl"><div style="display: inline; direction: ltr;"><div style="float: inline-start"></div></div></div>

should be like `float: right`, not `float: left`.


> > Source/WebCore/rendering/style/RenderStyle.h:1872
> > +        unsigned clear : 4; // Clear
> 
> This can be represented by 3 bits (up to 8 values).
> 
> > Source/WebCore/rendering/style/RenderStyle.h:1875
> > +        unsigned floating : 4; // Float
> 
> This can be represented by 3 bits (up to 8 values).

Thanks! Will fix.
Comment 6 Tyler Wilcock 2021-04-06 10:47:30 PDT
> This needs to be done at the layout level, the spec clearly says: "The mapping on these properties uses the writing mode of the element’s containing block."
> Doing this in `RenderStyle` fails because in this case:

Ah, my mistake.  Thanks for the explanation.
Comment 7 Tyler Wilcock 2021-04-06 10:57:42 PDT
Comment on attachment 425028 [details]
Patch

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

> Source/WebCore/rendering/RenderObject.cpp:2113
> +Float RenderObject::physicalFloat() const
> +{
> +    bool isInlineFlipped = containingBlockDirection() == TextDirection::RTL;
> +    Float currentValue = style().floating();
> +
> +    if (currentValue == Float::InlineStart)
> +        return !isInlineFlipped ? Float::Left : Float::Right;
> +
> +    if (currentValue == Float::InlineEnd)
> +        return !isInlineFlipped ? Float::Right : Float::Left;
> +
> +    return currentValue;
> +}

Would these read better as the following?

if (currentValue == Float::InlineStart)
    return isLtr ? Float::Left : Float::Right;

if (currentValue == Float::InlineEnd)
    return isLtr ? Float::Right : Float::Left;

Or, keeping `isInlineFlipped`:

if (currentValue == Float::InlineStart)
    return isInlineFlipped ? Float::Right : Float::Left;

if (currentValue == Float::InlineEnd)
    return isInlineFlipped ? Float::Left : Float::Right;

> Source/WebCore/rendering/line/BreakingContext.h:296
> +        if (m_ignoringSpaces && currentObject()->physicalClear() != Clear::None)

Can you use the existing `br` local ~10 lines up vs. calling `currentObject()`?

> Source/WebCore/rendering/line/BreakingContext.h:305
> +            clear = currentObject()->physicalClear();

Ditto.
Comment 8 zalan 2021-04-06 11:18:50 PDT
Antti and I had a chat on this and since he is planning on making some changes of how computed styles are exposed to the layout process, I think for now we should go with something like this:
- add 2 static helper functions to RenderStyle (we've got a few of these e.g. collapseWhiteSpace). These functions would take a renderer and return something like enum class UsedFloat(Clear) { } to make it clear that these are not the computed values (these functions would eventually move over to one of the new style classes)
- try not use the name physical in the context of layout. It makes it confusing especially when you start involving vertical layout.
- containingBlockDirection() could be just a simple 
 auto containingBlockDirection = renderer.containingBlock().style().direction(); I would not add such a single-use function to the renderer interface. 

e.g
class RenderStyle {
...
    enum class UsedFloat { No, Left, Right };
    static UsedFloat usedFloat(const RenderElement&);
...
}
This is still rather error-prone (like how do you know whether to call this instead of the original RenderStyle function) but this is supposed to be temporary as the upcoming refactoring will scope it properly.
(and you could just ignore the LFC codepath for now. or alternatively you could make it a bit more generic by not taking the renderer -but the LFC codebase is not enabled so not really a priority)
Comment 9 Darin Adler 2021-04-06 14:36:17 PDT
Comment on attachment 425028 [details]
Patch

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

> Source/WebCore/layout/layouttree/LayoutBox.cpp:198
> +    bool isInlineFlipped = containingBlockDirection() == TextDirection::RTL;

Given we only need this for start/end, is it OK that we are computing it unconditionally? Will the extra work have a measurable performance impact?

> Source/WebCore/layout/layouttree/LayoutBox.cpp:212
> +    bool isInlineFlipped = containingBlockDirection() == TextDirection::RTL;

Ditto.

> Source/WebCore/rendering/RenderObject.cpp:2103
> +    bool isInlineFlipped = containingBlockDirection() == TextDirection::RTL;

Is there a way to avoid having the code be so repetitive? This is the third almost-identical function of four.

> LayoutTests/ChangeLog:14
> +        * imported/w3c/web-platform-tests/css/css-logical/logical-values-float-clear-1.html: Added.
> +        * imported/w3c/web-platform-tests/css/css-logical/logical-values-float-clear-2.html: Added.
> +        * imported/w3c/web-platform-tests/css/css-logical/logical-values-float-clear-3.html: Added.
> +        * imported/w3c/web-platform-tests/css/css-logical/logical-values-float-clear-4.html: Added.

ChangeLog does not mention TestExpectations, nor explain the changes in that file.

> LayoutTests/TestExpectations:2947
> +webkit.org/b/224104 imported/w3c/web-platform-tests/css/css-logical/logical-values-float-clear-reftest.html [ Skip ]

I understand that this test fails. But I don’t understand why we need to skip this test instead of expecting a failure now. Why is that?
Comment 10 Tim Nguyen (:ntim) 2021-04-16 06:54:45 PDT
Created attachment 426221 [details]
Patch
Comment 11 zalan 2021-04-16 07:14:09 PDT
Comment on attachment 426221 [details]
Patch

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

This looks much better now. Thanks!

> Source/WebCore/rendering/ComplexLineLayout.cpp:1362
> +                UsedClear clear = RenderStyle::usedClear(*lastObject);

auto clear =

> Source/WebCore/rendering/RenderBlock.cpp:2345
> +            UsedClear childUsedClear = RenderStyle::usedClear(*child);

auto childUsedClear =

> Source/WebCore/rendering/RenderBlockFlow.cpp:2895
> +    UsedClear usedClear = RenderStyle::usedClear(child);

auto

> Source/WebCore/rendering/RenderBlockFlow.cpp:4328
> +                    UsedFloat prevFloatValue = RenderStyle::usedFloat(*prevFloat);
> +                    UsedClear childClearValue = RenderStyle::usedClear(*child);

auto and please change prev to previous

> Source/WebCore/rendering/style/RenderStyle.cpp:2668
> +UsedClear RenderStyle::usedClear(const RenderObject& renderer)

We should never call this on a RenderText so it could just be a RenderElement (however some of the call sites may still pass in a RenderObject. I'd give it a try but don't not a must -same for ::UsedFloat)

> Source/WebCore/rendering/style/RenderStyle.cpp:2670
> +    Clear specifiedValue = renderer.style().clear();

auto and computedValue

> Source/WebCore/rendering/style/RenderStyle.cpp:2691
> +    Float specifiedValue = renderer.style().floating();

auto and computedValue
Comment 12 zalan 2021-04-16 07:14:55 PDT
Antti may wanna look at the css bits but I am sure it's all good. Will talk to him and r+ it later today.
Comment 13 Antti Koivisto 2021-04-16 07:21:24 PDT
Comment on attachment 426221 [details]
Patch

looks good
Comment 14 Antti Koivisto 2021-04-16 07:23:23 PDT
Comment on attachment 426221 [details]
Patch

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

> Source/WebCore/rendering/style/RenderStyle.cpp:2689
> +UsedFloat RenderStyle::usedFloat(const RenderObject& renderer)

At some point we should add something like RenderStyleHelpers.h where we can put this sort of things as standalone functions. RenderStyle is big enough as-is, semantics should go somewhere else.
Comment 15 Antti Koivisto 2021-04-16 08:50:02 PDT
Comment on attachment 426221 [details]
Patch

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

> Source/WebCore/rendering/RenderBlockFlow.cpp:4327
> +                    UsedFloat prevFloatValue = RenderStyle::usedFloat(*prevFloat);

Aren't you potentially dereferencing a nullptr here?
Comment 16 Antti Koivisto 2021-04-16 08:52:41 PDT
Comment on attachment 426221 [details]
Patch

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

> Source/WebCore/rendering/line/BreakingContext.h:296
> +        if (m_ignoringSpaces && RenderStyle::usedClear(*currentObject()) != UsedClear::None)

What guarantees currentObject() is non-null here?

> Source/WebCore/rendering/line/BreakingContext.h:305
> +            usedClear = RenderStyle::usedClear(*currentObject());

And here
Comment 17 Tim Nguyen (:ntim) 2021-04-16 09:27:27 PDT
Created attachment 426235 [details]
Patch
Comment 18 Tim Nguyen (:ntim) 2021-04-16 09:32:14 PDT
Created attachment 426238 [details]
Patch
Comment 19 zalan 2021-04-17 15:09:50 PDT
Created attachment 426350 [details]
Patch
Comment 20 EWS 2021-04-17 18:03:28 PDT
Committed r276216 (236698@main): <https://commits.webkit.org/236698@main>

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