Skip to content

Implement overflow-clip-margin parsing.#58276

Merged
webkit-commit-queue merged 1 commit into
WebKit:mainfrom
surajt6:eng/Implement-overflow-clip-margin
Feb 21, 2026
Merged

Implement overflow-clip-margin parsing.#58276
webkit-commit-queue merged 1 commit into
WebKit:mainfrom
surajt6:eng/Implement-overflow-clip-margin

Conversation

@surajt6

@surajt6 surajt6 commented Feb 10, 2026

Copy link
Copy Markdown
Contributor

1e0a89e

Implement overflow-clip-margin parsing.

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

Misc iOS, visionOS, tvOS & watchOS macOS Linux Windows
✅ 🧪 style ✅ 🛠 ios ✅ 🛠 mac ✅ 🛠 wpe ✅ 🛠 win
✅ 🧪 bindings ✅ 🛠 ios-sim ✅ 🛠 mac-AS-debug ✅ 🧪 wpe-wk2 ❌ 🧪 win-tests
✅ 🧪 webkitperl ✅ 🧪 ios-wk2 ✅ 🧪 api-mac ✅ 🧪 api-wpe
✅ 🧪 ios-wk2-wpt ✅ 🧪 api-mac-debug ✅ 🛠 gtk3-libwebrtc
✅ 🛠 🧪 jsc ✅ 🧪 api-ios ✅ 🧪 mac-wk1 ✅ 🛠 gtk
✅ 🛠 🧪 jsc-debug-arm64 ⏳ 🛠 ios-safer-cpp ✅ 🧪 mac-wk2 ✅ 🧪 gtk-wk2
✅ 🛠 vision ✅ 🧪 mac-AS-debug-wk2 ✅ 🧪 api-gtk
✅ 🛠 🧪 merge ✅ 🛠 vision-sim ✅ 🧪 mac-wk2-stress 🛠 playstation
✅ 🧪 vision-wk2 ✅ 🧪 mac-intel-wk2 ✅ 🛠 jsc-armv7
✅ 🛠 tv ✅ 🛠 mac-safer-cpp ✅ 🧪 jsc-armv7-tests
✅ 🛠 tv-sim
✅ 🛠 watch
✅ 🛠 watch-sim

@surajt6

surajt6 commented Feb 10, 2026

Copy link
Copy Markdown
Contributor Author

What works?

http://wpt.live/css/css-overflow/overflow-clip-margin-001.html
http://wpt.live/css/css-overflow/overflow-clip-margin-011.html
http://wpt.live/css/css-overflow/overflow-clip-margin-hit-testing.html
http://wpt.live/css/css-overflow/overflow-clip-margin-intersection-observer.html

http://wpt.live/css/css-overflow/overflow-clip-margin-007.html (works sometimes)
http://wpt.live/css/css-overflow/overflow-clip-margin-012.html (works sometimes)

What doesn’t work?

http://wpt.live/css/css-overflow/overflow-clip-margin-002.html
http://wpt.live/css/css-overflow/overflow-clip-margin-004.html
http://wpt.live/css/css-overflow/overflow-clip-margin-005.html
http://wpt.live/css/css-overflow/overflow-clip-margin-010.html
http://wpt.live/css/css-overflow/overflow-clip-margin-border-radius-002.html
http://wpt.live/css/css-overflow/overflow-clip-margin-invalidation.html
http://wpt.live/css/css-overflow/overflow-clip-margin-mul-column-border-box.html
http://wpt.live/css/css-overflow/overflow-clip-margin-mul-column-content-box.html
http://wpt.live/css/css-overflow/overflow-clip-margin-visual-box-and-value-with-border-radius.html
https://wpt.live/css/css-overflow/rounded-overflow-clip-visible.html
http://wpt.live/css/css-overflow/overflow-clip-margin-visual-box.html (partial failure)
http://wpt.live/css/css-overflow/overflow-clip-scroll-size.html (partial failure)
http://wpt.live/css/css-overflow/parsing/overflow-clip-margin-computed.html (6 pass / 8 fail)
http://wpt.live/css/css-overflow/parsing/overflow-clip-margin.html (9 pass / 5 fail)

Feedback requested

For the parsing tests to pass, we most likely need to write a custom parser. For example, 0px content-box should be canonicalized to content-box and AFAIK the auto generated parser doesn’t support that - please confirm if this is true or not

confirm if NoninheritedRareData the right place to store overflow clip margin value

TODOs

  • Fix failing tests
  • Implement custom parser (if needed)
  • Add support for animation
  • Check for paddingBoxRect() regressions. I still didnt get to run layout tests on my webkit build

@Ahmad-S792 Ahmad-S792 added the CSS Cascading Style Sheets implementation label Feb 10, 2026
@Ahmad-S792

Copy link
Copy Markdown
Contributor

@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.

@Ahmad-S792

Copy link
Copy Markdown
Contributor

@weinig & @nt1m - might be able to help you in your feedback.

@webkit-ews-buildbot webkit-ews-buildbot added the merging-blocked Applied to prevent a change from being merged label Feb 10, 2026
private:
OverflowClipMarginType m_type;
Length m_length;
OverflowClipReferenceBox m_clipBox;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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.

Comment thread Source/WebCore/style/values/overflow/StyleOverflowClipMargin.h
PaddingBox
};

enum class OverflowClipMarginType : uint8_t {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Sure, I'll remove it.

@@ -234,6 +234,18 @@ enum class Overflow : uint8_t {
PagedY
};

enum class OverflowClipReferenceBox : uint8_t {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I would consider calling this VisualBox, and adding a spec link to https://drafts.csswg.org/css-box-4/#typedef-visual-box.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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)) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Makes sense. I'll update it.

case OverflowClipReferenceBox::ContentBox:
return { overflowClipReferenceBox };
};
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

After this, you should add:

Suggested change
}
}
state.setCurrentPropertyInvalidAtComputedValueTime();
return 0_css_px;

This will make sure that any other types of primitive values force an invalid style resolution.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Ah, I get it now. Thanks for the clear explanation.

}
}

if (auto list = requiredListDowncast<CSSValueList, CSSValue, 2>(state, value)) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This can be written a bit more succinctly using an early return and a more specific subtype.

Suggested change
if (auto list = requiredListDowncast<CSSValueList, CSSValue, 2>(state, value)) {
auto list = requiredListDowncast<CSSValueList, CSSPrimitiveValue, 2>(state, value);
if (!list)
return 0_css_px;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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()) {

@weinig weinig Feb 10, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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 };

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Got it.

auto overflowClipReferenceBox = fromCSSValue<OverflowClipReferenceBox>(*primitiveValue);
switch (overflowClipReferenceBox) {
case OverflowClipReferenceBox::PaddingBox:
return { 0_css_px };

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yup that makes a lot of sense. I realized later that this code has nothing to do with CSS value canonicalization.

@weinig

weinig commented Feb 10, 2026

Copy link
Copy Markdown
Contributor

For the parsing tests to pass, we most likely need to write a custom parser. For example, 0px content-box should be canonicalized to content-box and AFAIK the auto generated parser doesn’t support that - please confirm if this is true or not

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:

<visual-box>@(default=padding-box) || <length [0,∞]>@(default=0px)

@martadarbinyan martadarbinyan added the skip-ews Applied to prevent a change from being run on EWS label Feb 10, 2026
@surajt6

surajt6 commented Feb 11, 2026

Copy link
Copy Markdown
Contributor Author

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:

<visual-box>@(default=padding-box) || <length [0,∞]>@(default=0px)

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.

<visual-box>@(default=padding-box) - This means that if the term value is padding-box then it wouldn't be serialized into a CSS value and would be ignored by the parser.

Then for this expression - <visual-box>@(default=padding-box) || <length [0,∞]>@(default=0px), when both <visual-box> and <length> are default values, I'm assuming that the parser will return the initial value defined for the property in CSSProperties.json.

Please confirm if I'm understanding this correctly.

@surajt6 surajt6 force-pushed the eng/Implement-overflow-clip-margin branch from 8a90933 to 79d4534 Compare February 11, 2026 09:59
@surajt6

surajt6 commented Feb 11, 2026

Copy link
Copy Markdown
Contributor Author

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
http://wpt.live/css/css-overflow/parsing/overflow-clip-margin-computed.html

TODO

Add parsing behind a feature flag

@weinig

weinig commented Feb 11, 2026

Copy link
Copy Markdown
Contributor

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:

<visual-box>@(default=padding-box) || <length [0,∞]>@(default=0px)

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.

<visual-box>@(default=padding-box) - This means that if the term value is padding-box then it wouldn't be serialized into a CSS value and would be ignored by the parser.

Then for this expression - <visual-box>@(default=padding-box) || <length [0,∞]>@(default=0px), when both <visual-box> and <length> are default values, I'm assuming that the parser will return the initial value defined for the property in CSSProperties.json.

Please confirm if I'm understanding this correctly.

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:

[ <visual-box>@(default=padding-box) || <length [0,∞]>@(default=0px) ]@(default=0px)

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.

@surajt6 surajt6 changed the title Implement overflow-clip-margin Implement overflow-clip-margin parsing. Feb 13, 2026
@surajt6 surajt6 force-pushed the eng/Implement-overflow-clip-margin branch from 79d4534 to c01bdc6 Compare February 13, 2026 07:11
@surajt6 surajt6 marked this pull request as ready for review February 13, 2026 07:14
@surajt6

surajt6 commented Feb 13, 2026

Copy link
Copy Markdown
Contributor Author

@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!

Comment thread Source/WebCore/css/CSSProperties.json
Comment thread Source/WebCore/css/CSSProperties.json Outdated
"animation-type": "not animatable",
"inherited": false,
"initial": "0px",
"values": ["border-box", "content-box", "padding-box"],

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Our convention is put each of these elements in the array on their own line.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I'll update it.

Comment thread Source/WebCore/css/CSSProperties.json
Comment thread Source/WebCore/css/CSSProperties.json Outdated
@@ -14343,6 +14362,14 @@
"url": "https://drafts.fxtf.org/css-masking-1/#typedef-geometry-box"
}
},
"<visual-box>": {
"grammar": "<box>",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Please remove this newline.

return WTF::switchOn(m_value, std::forward<F>(f)...);
}

bool operator==(const OverflowClipMargin&) const = default;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

CSSBoxType is really too broad for this. I would stick with adding a new VisualBox type.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Sure.

using Length = Style::Length<CSS::Nonnegative, float>;
using VisualBox = CSSBoxType;

OverflowClipMargin(CSS::ValueLiteral<CSS::LengthUnit::Px> length)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

You are going to need more constructors. If you want to leave that for the next change, that is fine.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I'll leave that for the next change.

namespace WebCore {
namespace Style {

using namespace CSS::Literals;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I would move this to be in the body of CSSValueConversion::operator() to constrain it.

Comment thread Source/WebCore/css/parser/CSSPropertyParserCustom.h
#include "CSSPrimitiveValue.h"
#include "CSSPropertyParserConsumer+CSSPrimitiveValueResolver.h"
#include "CSSPropertyParserConsumer+Primitives.h"
#include "CSSPropertyParserConsumer+Shapes.h"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Is this include needed?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

These are not needed. I'll remove them.

#include "CSSPropertyParserConsumer+CSSPrimitiveValueResolver.h"
#include "CSSPropertyParserConsumer+Primitives.h"
#include "CSSPropertyParserConsumer+Shapes.h"
#include "CSSPropertyParserConsumer+URL.h"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Is this include needed?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Nope. I'll remove them.

continue;
break;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

You need to check that at least one of the values got parsed here.

if (!visualBox && !length)
    return nullptr;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why is value_or(true) right here?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

(Note, isZero() will return nullopt when it can't be determined if the value is zero, like when calc() is used).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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.

@weinig weinig removed the skip-ews Applied to prevent a change from being run on EWS label Feb 13, 2026
@weinig

weinig commented Feb 13, 2026

Copy link
Copy Markdown
Contributor

Looks like there aren't any parsing tests that utilize calc(). Please add some.

Comment on lines +1307 to +1308
humanReadableName: "CSS Overflow Clip Margin"
humanReadableDescription: "Enable CSS Overflow Clip Margin"

@nt1m nt1m Feb 17, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
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

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Sure, I'll update the text.

@surajt6

surajt6 commented Feb 17, 2026

Copy link
Copy Markdown
Contributor Author

Looks like there aren't any parsing tests that utilize calc(). Please add some.

Sure, I'll add some calc() tests.

@surajt6 surajt6 force-pushed the eng/Implement-overflow-clip-margin branch from c01bdc6 to c63c7ee Compare February 17, 2026 07:14
Comment on lines +30 to +39
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%)');

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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; });

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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.

@surajt6 surajt6 force-pushed the eng/Implement-overflow-clip-margin branch from c63c7ee to 4f46041 Compare February 17, 2026 07:44
@surajt6

surajt6 commented Feb 17, 2026

Copy link
Copy Markdown
Contributor Author

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.

@surajt6 surajt6 force-pushed the eng/Implement-overflow-clip-margin branch from 4f46041 to c5655d2 Compare February 18, 2026 17:18
@@ -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"

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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 });

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This can be simplified down to

            return CSSPrimitiveValueResolver<CSS::Length<CSS::Nonnegative>>::consumeAndResolve(range, state);

unitlessZeroLength = UnitlessZeroQuirk::Allow is the default.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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.

@surajt6 surajt6 force-pushed the eng/Implement-overflow-clip-margin branch from c5655d2 to 3f38288 Compare February 19, 2026 16:55
@fujii fujii added merge-queue Applied to send a pull request to merge-queue and removed merging-blocked Applied to prevent a change from being merged labels Feb 21, 2026
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
@webkit-commit-queue webkit-commit-queue force-pushed the eng/Implement-overflow-clip-margin branch from 3f38288 to 1e0a89e Compare February 21, 2026 03:41
@webkit-commit-queue

Copy link
Copy Markdown
Collaborator

Committed 307956@main (1e0a89e): https://commits.webkit.org/307956@main

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

@webkit-commit-queue webkit-commit-queue merged commit 1e0a89e into WebKit:main Feb 21, 2026
@webkit-commit-queue webkit-commit-queue removed the merge-queue Applied to send a pull request to merge-queue label Feb 21, 2026
@surajt6 surajt6 deleted the eng/Implement-overflow-clip-margin branch February 21, 2026 17:24
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

Development

Successfully merging this pull request may close these issues.

9 participants