Implement overflow-clip-margin parsing.#58276
Conversation
|
EWS run on previous version of this PR (hash 8a90933) Details |
|
@surajt6 - Congratulations on your first PR on WebKit, please don't hesitate to ask any question and you can join WebKit OpenSource Slack channel for any help or input as well. |
| private: | ||
| OverflowClipMarginType m_type; | ||
| Length m_length; | ||
| OverflowClipReferenceBox m_clipBox; |
There was a problem hiding this comment.
I realize this is still a draft, so perhaps you were going to change this, but this should use something like the following for the underlying type:
Variant<OverflowClipReferenceBox, Length, SpaceSeparatedTuple<OverflowClipReferenceBox, Length>>
That way, only the valid state is initialized.
There was a problem hiding this comment.
Variant's a lot better than what I wrote. I was mostly following StyleAspectRatio.h and StyleAspectRatio.cpp for my implementation. I'll change it. It makes the rest of the Class a lot cleaner.
| PaddingBox | ||
| }; | ||
|
|
||
| enum class OverflowClipMarginType : uint8_t { |
There was a problem hiding this comment.
You shouldn't need this if you use a Variant, but even if you decide not to, this can be internal and private to OverflowClipMargin.
There was a problem hiding this comment.
I'll use variant and remove this.
| @@ -1208,6 +1220,8 @@ WTF::TextStream& operator<<(WTF::TextStream&, OutlineStyle); | |||
| WTF::TextStream& operator<<(WTF::TextStream&, WebCore::Overflow); | |||
| WTF::TextStream& operator<<(WTF::TextStream&, OverflowAlignment); | |||
| WTF::TextStream& operator<<(WTF::TextStream&, OverflowWrap); | |||
| WTF::TextStream& operator<<(WTF::TextStream&, OverflowClipReferenceBox); | |||
There was a problem hiding this comment.
These aren't needed for types that implemented in CSSPrimitiveValueMappings.h using the macros. (I haven't yet removed all the ones I could here, but this is a good reminder to me to do so).
There was a problem hiding this comment.
Sure, I'll remove it.
| @@ -234,6 +234,18 @@ enum class Overflow : uint8_t { | |||
| PagedY | |||
| }; | |||
|
|
|||
| enum class OverflowClipReferenceBox : uint8_t { | |||
There was a problem hiding this comment.
I would consider calling this VisualBox, and adding a spec link to https://drafts.csswg.org/css-box-4/#typedef-visual-box.
There was a problem hiding this comment.
Great suggestion! I'll add VisualBox and update it here.
|
|
||
| auto CSSValueConversion<OverflowClipMargin>::operator()(BuilderState& state, const CSSValue& value) -> OverflowClipMargin | ||
| { | ||
| if (RefPtr primitiveValue = requiredDowncast<CSSPrimitiveValue>(state, value)) { |
There was a problem hiding this comment.
This should be a dynamic_downcast. requiredDowncast will tell the builder the conversion failed if the cast isn't successful. That means the CSSValueList code below will never be successfully run.
There was a problem hiding this comment.
Makes sense. I'll update it.
| case OverflowClipReferenceBox::ContentBox: | ||
| return { overflowClipReferenceBox }; | ||
| }; | ||
| } |
There was a problem hiding this comment.
After this, you should add:
| } | |
| } | |
| state.setCurrentPropertyInvalidAtComputedValueTime(); | |
| return 0_css_px; |
This will make sure that any other types of primitive values force an invalid style resolution.
There was a problem hiding this comment.
Sure, I'll update it. I want to confirm my understanding of what this actually does. Essentially the setCurrentPropertyInvalidAtComputedValueTime will make sure that the overflow-clip-margin's computed value is set to 0px if it's invalid.
Take for example StyleBorderRadius.cpp. Why is setCurrentPropertyInvalidAtComputedValueTime not called here - https://searchfox.org/wubkat/source/Source/WebCore/style/values/borders/StyleBorderRadius.cpp#71 ?
And for StyleWebKitTextStrokeWidth.cpp, it's only called once in line 58 but not in line 42 -
https://searchfox.org/wubkat/source/Source/WebCore/style/values/non-standard/StyleWebKitTextStrokeWidth.cpp#38. Why is that the case?
There was a problem hiding this comment.
The setCurrentPropertyInvalidAtComputedValueTime() stuff is largely "belt and suspenders" precautions against a specific weird edge case where rather than the CSSValue coming from the CSSParser, it comes from the CSS typedom. This can happen by using things like the StylePropertyMap interface to set CSS values. There are plans to fix this in a more comprehensive way, but for now, being conservative in CSSValueConversion like this works.
Sure, I'll update it. I want to confirm my understanding of what this actually does. Essentially the setCurrentPropertyInvalidAtComputedValueTime will make sure that the overflow-clip-margin's computed value is set to 0px if it's invalid.
Not quite. When setCurrentPropertyInvalidAtComputedValueTime() is used, it sets a bit in the style builder that tells it that no matter what value ends up being set, it should be unset. If you look in Style::Builder::applyProperty(), at the end of the function, there is logic that checks isCurrentPropertyInvalidAtComputedValueTime() and applies the unset.
We usually return the default value for the property when we call setCurrentPropertyInvalidAtComputedValueTime() just for simplicity. It doesn't really matter what is returned in those cases.
Take for example StyleBorderRadius.cpp. Why is setCurrentPropertyInvalidAtComputedValueTime not called here - https://searchfox.org/wubkat/source/Source/WebCore/style/values/borders/StyleBorderRadius.cpp#71 ?
I think that check for if (!value.isPair()) is probably unnecessary and wrong, since the requiredPairDowncast will perform the same check.
And for StyleWebKitTextStrokeWidth.cpp, it's only called once in line 58 but not in line 42 -
https://searchfox.org/wubkat/source/Source/WebCore/style/values/non-standard/StyleWebKitTextStrokeWidth.cpp#38. Why is that the case?
It's not necessary to call it for line 42 because that is what the requiredDowncast() above it did. requiredDowncast is a dynamicDowncast that in addition to checking the type, calls setCurrentPropertyInvalidAtComputedValueTime() if the type check fails.
We need to do it on line 58 because we are narrowing our check beyond just CSSPrimitiveValue, and we don't have a specific "required" function for that case.
There was a problem hiding this comment.
Ah, I get it now. Thanks for the clear explanation.
| } | ||
| } | ||
|
|
||
| if (auto list = requiredListDowncast<CSSValueList, CSSValue, 2>(state, value)) { |
There was a problem hiding this comment.
This can be written a bit more succinctly using an early return and a more specific subtype.
| if (auto list = requiredListDowncast<CSSValueList, CSSValue, 2>(state, value)) { | |
| auto list = requiredListDowncast<CSSValueList, CSSPrimitiveValue, 2>(state, value); | |
| if (!list) | |
| return 0_css_px; |
There was a problem hiding this comment.
That's a lot cleaner. I'll update it.
| RefPtr value0 = requiredDowncast<CSSPrimitiveValue>(state, item0); | ||
| RefPtr value1 = requiredDowncast<CSSPrimitiveValue>(state, item1); | ||
|
|
||
| if (value0->isValueID() && value1->isLength()) { |
There was a problem hiding this comment.
In cases like these, we allow the values to be in any order. So this could be written as:
std::optional<OverflowClipReferenceBox> referenceBox;
std::optional<OverflowClipMargin::Length> length;
for (Ref item : *list) {
if (item->isValueID()) {
if (referenceBox) {
state.setCurrentPropertyInvalidAtComputedValueTime();
return 0_css_px;
}
referenceBox = toStyleFromCSSValue<OverflowClipReferenceBox>(state, item);
} else if (item->isLength()) {
if (length) {
state.setCurrentPropertyInvalidAtComputedValueTime();
return 0_css_px;
}
length = toStyleFromCSSValue<OverflowClipMargin::Length>(state, item);
} else {
state.setCurrentPropertyInvalidAtComputedValueTime();
return 0_css_px;
}
}
return { *referenceBox, *length };There was a problem hiding this comment.
Sure I'll update it. I'm curious as to when the value order is reversed. The auto generated parser makes sure that the CSSValueList always has referenceBox as first value. In what situation would the CSSValueList have length as the first value?
There was a problem hiding this comment.
I'd have to dive deeper to find out if there is a way to get it to happen, but the short answer is that the CSS typedom (e.g. StylePropertyMap) can also be the source of the CSSValue passed to CSSValueConversion. For a case like this, there would have to be another property had a similar (but with the reverse default ordering) grammar, and allowed <length> <visual-box>. If such a property existed, one could do something like:
var element = document.getElementById("element");
element.attributeStyleMap.set('overflow-clip-margin', CSSStyleValue.parse('made-up-property', '30px border-box'))
As a result of this other source, we choose to be conservative in the CSSValueConversion codepath.
| auto overflowClipReferenceBox = fromCSSValue<OverflowClipReferenceBox>(*primitiveValue); | ||
| switch (overflowClipReferenceBox) { | ||
| case OverflowClipReferenceBox::PaddingBox: | ||
| return { 0_css_px }; |
There was a problem hiding this comment.
You probably don't want to do this. Even if the behavior will end up being that it should act like a length of 0px is set, the resolved value (that is what the value returned from getComputedStyle is called) will still need to be padding-box.
There was a problem hiding this comment.
Yup that makes a lot of sense. I realized later that this code has nothing to do with CSS value canonicalization.
Correct, the parser generator doesn't support canonicalization yet. If you are feeling ambitious, the syntax we would use for the grammar would probably be something like: |
Thanks for confirming. I haven't yet explored how the script to generate parser works and I'll look into it while I'm working on overflow-clip-margin. I first want to understand the exact semantics of using the default annotation with respect to parsing in this case.
Then for this expression - Please confirm if I'm understanding this correctly. |
8a90933 to
79d4534
Compare
|
EWS run on previous version of this PR (hash 79d4534) Details
|
|
I've updated the PR following the feedback. I've also removed any layout/rendering changes and only confined my changes to parsing. The following tests now pass successfully - http://wpt.live/css/css-overflow/parsing/overflow-clip-margin.html TODOAdd parsing behind a feature flag |
Yeah, that seems right. As mentioned, this isn't supported yet, so it's possible the syntax I suggested would not be sufficient. We might have to add something to state what should happen when the default is matched in all cases, like: We would probably want to go through more examples of things that still require manually parsing and see if the syntax would work. But, please, don't try to do the generator changes at the same time as adding the new property. They can be totally separate. |
79d4534 to
c01bdc6
Compare
|
EWS run on previous version of this PR (hash c01bdc6) Details |
|
@weinig I have updated the PR with a new commit and now this PR only includes the CSS property parsing logic to keep it small. The parsing is also behind a feature flag. Style computation and layout/rendering will for overflow-clip-margin will be implemented in future PRs. I also went ahead and updated some css-overflow layout tests related to the overflow-clip-margin property. I've tried to incorporate all of the feedback in earlier revisions. Thanks again for taking the time to answer my questions and reviewing this PR. Cheers! |
| "animation-type": "not animatable", | ||
| "inherited": false, | ||
| "initial": "0px", | ||
| "values": ["border-box", "content-box", "padding-box"], |
There was a problem hiding this comment.
Our convention is put each of these elements in the array on their own line.
| @@ -14343,6 +14362,14 @@ | |||
| "url": "https://drafts.fxtf.org/css-masking-1/#typedef-geometry-box" | |||
| } | |||
| }, | |||
| "<visual-box>": { | |||
| "grammar": "<box>", | |||
There was a problem hiding this comment.
I would put the full grammar here. <box> isn't even in a spec anymore, so others should probably be using <visual-box> going forward.
|
|
||
| bool operator==(const OverflowClipMargin&) const = default; | ||
| private: | ||
|
|
There was a problem hiding this comment.
Please remove this newline.
| return WTF::switchOn(m_value, std::forward<F>(f)...); | ||
| } | ||
|
|
||
| bool operator==(const OverflowClipMargin&) const = default; |
There was a problem hiding this comment.
Please add a newline under this.
| // https://drafts.csswg.org/css-overflow/#overflow-clip-margin | ||
| struct OverflowClipMargin { | ||
| using Length = Style::Length<CSS::Nonnegative, float>; | ||
| using VisualBox = CSSBoxType; |
There was a problem hiding this comment.
CSSBoxType is really too broad for this. I would stick with adding a new VisualBox type.
| using Length = Style::Length<CSS::Nonnegative, float>; | ||
| using VisualBox = CSSBoxType; | ||
|
|
||
| OverflowClipMargin(CSS::ValueLiteral<CSS::LengthUnit::Px> length) |
There was a problem hiding this comment.
You are going to need more constructors. If you want to leave that for the next change, that is fine.
There was a problem hiding this comment.
I'll leave that for the next change.
| namespace WebCore { | ||
| namespace Style { | ||
|
|
||
| using namespace CSS::Literals; |
There was a problem hiding this comment.
I would move this to be in the body of CSSValueConversion::operator() to constrain it.
| #include "CSSPrimitiveValue.h" | ||
| #include "CSSPropertyParserConsumer+CSSPrimitiveValueResolver.h" | ||
| #include "CSSPropertyParserConsumer+Primitives.h" | ||
| #include "CSSPropertyParserConsumer+Shapes.h" |
There was a problem hiding this comment.
These are not needed. I'll remove them.
| #include "CSSPropertyParserConsumer+CSSPrimitiveValueResolver.h" | ||
| #include "CSSPropertyParserConsumer+Primitives.h" | ||
| #include "CSSPropertyParserConsumer+Shapes.h" | ||
| #include "CSSPropertyParserConsumer+URL.h" |
There was a problem hiding this comment.
Nope. I'll remove them.
| continue; | ||
| break; | ||
| } | ||
|
|
There was a problem hiding this comment.
You need to check that at least one of the values got parsed here.
if (!visualBox && !length)
return nullptr;
There was a problem hiding this comment.
This is the code that's below the the for loop
CSSValueListBuilder list;
// Default value is padding-box
if (visualBox && !isValueID(visualBox, CSSValueID::CSSValuePaddingBox))
list.append(visualBox.releaseNonNull());
// Default value is 0px
if (length && !length->isZero().value_or(true))
list.append(length.releaseNonNull());
if (list.isEmpty())
return { CSSPrimitiveValue::create(0, CSSUnitType::CSS_PX) };
Isn't the list.isEmpty() sufficient to check that at least one of the values got parsed? Or is there a specific reason we want return a nullptr when both of them empty?
There was a problem hiding this comment.
I realized later that we need this. I added it in the new revision.
| list.append(visualBox.releaseNonNull()); | ||
|
|
||
| // Default value is 0px | ||
| if (length && !length->isZero().value_or(true)) |
There was a problem hiding this comment.
Why is value_or(true) right here?
There was a problem hiding this comment.
(Note, isZero() will return nullopt when it can't be determined if the value is zero, like when calc() is used).
There was a problem hiding this comment.
I initially wrote value_or(true) because if length is empty then we would not want to add it to the list. But now I realize that it won't work when calc() is used. It should be value_or(false). I'll change it.
|
Looks like there aren't any parsing tests that utilize calc(). Please add some. |
| humanReadableName: "CSS Overflow Clip Margin" | ||
| humanReadableDescription: "Enable CSS Overflow Clip Margin" |
There was a problem hiding this comment.
| humanReadableName: "CSS Overflow Clip Margin" | |
| humanReadableDescription: "Enable CSS Overflow Clip Margin" | |
| humanReadableName: "CSS overflow-clip-margin" | |
| humanReadableDescription: "Enable CSS overflow-clip-margin" |
small nit to match the style of the others
There was a problem hiding this comment.
Sure, I'll update the text.
Sure, I'll add some |
c01bdc6 to
c63c7ee
Compare
|
EWS run on previous version of this PR (hash c63c7ee) Details |
| test_valid_value("overflow-clip-margin", "calc(100px - 50px)", "calc(50px)"); | ||
| test_valid_value("overflow-clip-margin", "calc(100px - 100px)", "calc(0px)"); | ||
| test_valid_value("overflow-clip-margin", "calc(0.5em - 100px)"); | ||
| test_invalid_value("overflow-clip-margin", "calc(100% - 0.5em)"); | ||
| test_invalid_value("overflow-clip-margin", "calc(100% - 50px)"); | ||
|
|
||
| test_valid_value("overflow-clip-margin", 'padding-box calc(0.5em - 100px)', 'calc(0.5em - 100px)'); | ||
| test_valid_value("overflow-clip-margin", 'padding-box calc(100px - 100px)', 'calc(0px)'); | ||
| test_valid_value("overflow-clip-margin", 'border-box calc(0.5em - 100px)', 'border-box calc(0.5em - 100px)'); | ||
| test_invalid_value("overflow-clip-margin", 'border-box calc(0.5em - 100%)'); |
There was a problem hiding this comment.
These tests mimic the output of Firefox Dev Edition. I added the tests under the LayoutTests/imported directory. Please let me know if I need to add them in a new file in a different location.
| @@ -1232,5 +1238,6 @@ WTF::TextStream& operator<<(WTF::TextStream&, MaskType); | |||
| WTF::TextStream& operator<<(WTF::TextStream&, ShapeRendering); | |||
| WTF::TextStream& operator<<(WTF::TextStream&, TextAnchor); | |||
| WTF::TextStream& operator<<(WTF::TextStream&, VectorEffect); | |||
| WTF::TextStream& operator<<(WTF::TextStream&, VisualBox); | |||
There was a problem hiding this comment.
This is needed. When I don't add it, the build fails with the following error -
/Volumes/wk/WebKit/Source/WebCore/css/values/CSSValueAggregates.h:1798:54: error: invalid operands to binary expression
('TextStream' and 'const WebCore::VisualBox')
1798 | WTF::switchOn(value, [&](const auto& value) { ts << value; });
There was a problem hiding this comment.
If you add a mapping in CSSPrimitiveValueMappings.h it won't be. That will probably be something you need to add in the next PR though. So feel free to keep this and then remove it later.
c63c7ee to
4f46041
Compare
|
EWS run on previous version of this PR (hash 4f46041) Details |
|
I apologize for the silly build failures in EWS. The build was passing on my Mac. I did a clean and rebuild to try to reproduce the error but couldn't. The build failures seem simple to fix after reading through the error logs. In addition, even though the Mac build passed on EWS, there are a few layout test failures. I'm working on fixing the layout tests and the build failures on iOS, visionOS, tvOS & watchOS platforms. I will push another revision with the fixes. @weinig - your feedback is still welcome while I work through the EWS errors. |
4f46041 to
c5655d2
Compare
|
EWS run on previous version of this PR (hash c5655d2) Details |
| @@ -232,6 +232,7 @@ PASS outline-style | |||
| PASS outline-width | |||
| PASS overflow-anchor | |||
| PASS overflow-block | |||
| FAIL overflow-clip-margin assert_not_equals: Should get a different computed value. got disallowed value "0px" | |||
There was a problem hiding this comment.
This will pass once I add support for overflow-clip-margin computed values.
| FAIL Can set 'overflow-clip-margin' to the 'content-box' keyword: content-box assert_equals: expected "CSSKeywordValue" but got "CSSUnitValue" | ||
| FAIL Can set 'overflow-clip-margin' to the 'padding-box' keyword: padding-box assert_equals: expected "CSSKeywordValue" but got "CSSUnitValue" | ||
| PASS Can set 'overflow-clip-margin' to a length: 0px | ||
| FAIL Can set 'overflow-clip-margin' to a length: -3.14em Invalid values |
There was a problem hiding this comment.
Setting to overflow-clip-margin to -3.14em results in Type Error: Invalid Values as it only accepts non-negative values. This behavior is consistent with Chrome and Firefox as well. testsuite.js needs to be update to treat this test as a PASS. I added a FIXME in the test case file to fix this issue.
| RefPtr<CSSPrimitiveValue> length; | ||
| auto tryConsumeLength = [&length](CSSParserTokenRange& range, CSS::PropertyParserState& state) -> bool { | ||
| auto consumeLength = [](CSSParserTokenRange& range, CSS::PropertyParserState& state) -> RefPtr<CSSPrimitiveValue> { | ||
| return CSSPrimitiveValueResolver<CSS::Length<CSS::Range { 0, CSS::Range::infinity } >>::consumeAndResolve(range, state, { .unitlessZeroLength = UnitlessZeroQuirk::Allow }); |
There was a problem hiding this comment.
This can be simplified down to
return CSSPrimitiveValueResolver<CSS::Length<CSS::Nonnegative>>::consumeAndResolve(range, state);
unitlessZeroLength = UnitlessZeroQuirk::Allow is the default.
There was a problem hiding this comment.
I will update this in the next PR as I would prefer not to push a new revision with just this minor change.
| @@ -1232,5 +1238,6 @@ WTF::TextStream& operator<<(WTF::TextStream&, MaskType); | |||
| WTF::TextStream& operator<<(WTF::TextStream&, ShapeRendering); | |||
| WTF::TextStream& operator<<(WTF::TextStream&, TextAnchor); | |||
| WTF::TextStream& operator<<(WTF::TextStream&, VectorEffect); | |||
| WTF::TextStream& operator<<(WTF::TextStream&, VisualBox); | |||
There was a problem hiding this comment.
If you add a mapping in CSSPrimitiveValueMappings.h it won't be. That will probably be something you need to add in the next PR though. So feel free to keep this and then remove it later.
c5655d2 to
3f38288
Compare
|
EWS run on current version of this PR (hash 3f38288) Details |
https://bugs.webkit.org/show_bug.cgi?id=308225 Reviewed by Sam Weinig. Parse overflow-clip-margin CSS property value behind a feature flag. Added additional tests that use calc(). * LayoutTests/imported/w3c/web-platform-tests/css/css-cascade/all-prop-initial-xml-expected.txt: * LayoutTests/imported/w3c/web-platform-tests/css/css-cascade/all-prop-revert-layer-expected.txt: * LayoutTests/imported/w3c/web-platform-tests/css/css-overflow/overflow-no-interpolation-expected.txt: * LayoutTests/imported/w3c/web-platform-tests/css/css-overflow/parsing/overflow-clip-margin-computed-expected.txt: * LayoutTests/imported/w3c/web-platform-tests/css/css-overflow/parsing/overflow-clip-margin-expected.txt: * LayoutTests/imported/w3c/web-platform-tests/css/css-overflow/parsing/overflow-clip-margin.html: * LayoutTests/imported/w3c/web-platform-tests/css/css-typed-om/the-stylepropertymap/properties/overflow-clip-margin-expected.txt: * LayoutTests/imported/w3c/web-platform-tests/css/css-typed-om/the-stylepropertymap/properties/overflow-clip-margin.html: * LayoutTests/platform/glib/imported/w3c/web-platform-tests/css/css-cascade/all-prop-initial-xml-expected.txt: * Source/WTF/Scripts/Preferences/UnifiedWebPreferences.yaml: * Source/WebCore/Headers.cmake: * Source/WebCore/Sources.txt: * Source/WebCore/WebCore.xcodeproj/project.pbxproj: * Source/WebCore/css/CSSPrimitiveValueMappings.h: * Source/WebCore/css/CSSProperties.json: * Source/WebCore/css/parser/CSSPropertyParserConsumer+Overflow.cpp: Added. (WebCore::CSSPropertyParserHelpers::consumeOverflowClipMargin): * Source/WebCore/css/parser/CSSPropertyParserConsumer+Overflow.h: Added. * Source/WebCore/css/parser/CSSPropertyParserCustom.h: * Source/WebCore/rendering/style/RenderStyleConstants.cpp: (WebCore::operator<<): * Source/WebCore/rendering/style/RenderStyleConstants.h: * Source/WebCore/style/StyleBuilderCustom.h: (WebCore::Style::forwardInheritedValue): * Source/WebCore/style/computed/StyleComputedStyleBase.h: * Source/WebCore/style/computed/data/StyleNonInheritedRareData.cpp: (WebCore::Style::NonInheritedRareData::NonInheritedRareData): (WebCore::Style::NonInheritedRareData::operator== const): (WebCore::Style::NonInheritedRareData::dumpDifferences const): * Source/WebCore/style/computed/data/StyleNonInheritedRareData.h: * Source/WebCore/style/values/overflow/StyleOverflowClipMargin.cpp: Added. (WebCore::Style::CSSValueConversion<OverflowClipMargin>::operator): * Source/WebCore/style/values/overflow/StyleOverflowClipMargin.h: Added. (WebCore::Style::OverflowClipMargin::OverflowClipMargin): (WebCore::Style::OverflowClipMargin::switchOn const): Canonical link: https://commits.webkit.org/307956@main
3f38288 to
1e0a89e
Compare
|
Committed 307956@main (1e0a89e): https://commits.webkit.org/307956@main Reviewed commits have been landed. Closing PR #58276 and removing active labels. |
1e0a89e
3f38288
🛠 playstation