[Feature] Support symbols() in list-style-type[symbols] or [countery-styles] depending on what you decide in the other CL
// An anonymous symbols() counter style has no name, so there is no
// corresponding @counter-style at-rule to report.this comment could be simplified to be easier to understand. If its `symbols` then it by definition has no corresponding counter style at rule
// The anonymous counter style of a symbols() function is immutable and
// self-contained, so once built it never becomes stale.we should probably add a CHECK to enforce this, and just return true then.
so something like:
if (IsSymbols())
CHECK(cointer_style_)
return true
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
fixed, thanks!:)
[symbols] or [countery-styles] depending on what you decide in the other CL
Done
// An anonymous symbols() counter style has no name, so there is no
// corresponding @counter-style at-rule to report.this comment could be simplified to be easier to understand. If its `symbols` then it by definition has no corresponding counter style at rule
Done
// The anonymous counter style of a symbols() function is immutable and
// self-contained, so once built it never becomes stale.we should probably add a CHECK to enforce this, and just return true then.
so something like:
if (IsSymbols())
CHECK(cointer_style_)
return true
Done
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
| Code-Review | +1 |
// The anonymous counter style of a symbols() function is immutable and
// self-contained, so once built it never becomes stale.Ananya Anandwe should probably add a CHECK to enforce this, and just return true then.
so something like:
if (IsSymbols())
CHECK(cointer_style_)
return true
Done
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
Member<const cssvalue::CSSSymbolsValue> symbols_value_;Would symbols_funciton_value_ make sense? Same with other namings
// A symbols() function is a nameless counter style; callers must resolve it
// via GetCounterStyle() (which builds the anonymous style) rather than by
// name.nit: this part is ok to remove
bool IsSymbols() const { return symbols_value_ != nullptr; }IsSymbolsFunction
static ListStyleTypeData* CreateSymbols(const cssvalue::CSSSymbolsValue&);CreateSymbolsFunciton
#include "base/memory/values_equivalent.h"I think this should be already included by /css_symbols_value.h and can be removed
Type::kCounterStyle, g_empty_atom, nullptr, &symbols_value);nit: missing /var_name=/ comment for both https://google.github.io/styleguide/cppguide.html#Function_Argument_Comments
// and built on demand by GetCounterStyle(), so its reference never becomesnit: this part can be removed
// and built on demand by GetCounterStyle(), so its reference never becomes
// stale.I'm assuming if we change the list style to a different symbols function we invalidate this. I'm guessing this is just needed for the counter styles global map?
return true;Should we check here that the symbols was properly created?
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
Member<const cssvalue::CSSSymbolsValue> symbols_value_;Would symbols_funciton_value_ make sense? Same with other namings
yeah I think it's good to be more specific
// A symbols() function is a nameless counter style; callers must resolve it
// via GetCounterStyle() (which builds the anonymous style) rather than by
// name.nit: this part is ok to remove
Done
bool IsSymbols() const { return symbols_value_ != nullptr; }Ananya AnandIsSymbolsFunction
Done
static ListStyleTypeData* CreateSymbols(const cssvalue::CSSSymbolsValue&);Ananya AnandCreateSymbolsFunciton
Done
I think this should be already included by /css_symbols_value.h and can be removed
Done
Type::kCounterStyle, g_empty_atom, nullptr, &symbols_value);nit: missing /var_name=/ comment for both https://google.github.io/styleguide/cppguide.html#Function_Argument_Comments
Done
// and built on demand by GetCounterStyle(), so its reference never becomesnit: this part can be removed
Done
// and built on demand by GetCounterStyle(), so its reference never becomes
// stale.I'm assuming if we change the list style to a different symbols function we invalidate this. I'm guessing this is just needed for the counter styles global map?
yep! A different symbols() produces a whole new ListStyleTypeData, so the old value is replaced rather than left stale to detect. This validity check only matters for named counter styles in the global map, so an anonymous symbols() can never go stale and sp I made it return true early.
return true;Should we check here that the symbols was properly created?
I don't think its rlly needed here bc a symbols() value only exists if it passed parsing, and the parser accepts exactly what building the counter style requires (>=1 symbol, and >= 2 for alphabetic/numeric) so CreateAnonymousCounterStyle() can't fail, and the CHECK inside it already backstops that.
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
context.Count(WebFeature::kCSSSymbolsFunction);Might be worth a test to make sure we are firing this
return symbols;What if an author sets this to something like "symbols(...) some-garbage" - where there is garbage listed after the symbols function. Do we return invalid in that case? Might be worth a test if we don't have that one yet
DCHECK(symbols_function_value_);nit: CHECK is preferred instead of DCHECK when the check is trivial
DCHECK(!IsSymbolsFunction());nit: CHECK is preferred instead of DCHECK when the check is trivial
/*tree_scope=*/nullptr, &symbols_function_value);Instead of storing this, could we just create the counterstyle here based on it, rather than doing it lazily. Then we can get rid of symbols_function_value_. Then IsSymbolsFunction would check if IsCounterStyle and the name is anonymous or something to tell if it is a symbols function
return true;Ananya AnandShould we check here that the symbols was properly created?
I don't think its rlly needed here bc a symbols() value only exists if it passed parsing, and the parser accepts exactly what building the counter style requires (>=1 symbol, and >= 2 for alphabetic/numeric) so CreateAnonymousCounterStyle() can't fail, and the CHECK inside it already backstops that.
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
| Commit-Queue | +1 |
Might be worth a test to make sure we are firing this
yeah fs added CSSPropertyParserTest.ListStyleTypeSymbolsFunctionUseCounter in css_property_parser_test.cc
return symbols;What if an author sets this to something like "symbols(...) some-garbage" - where there is garbage listed after the symbols function. Do we return invalid in that case? Might be worth a test if we don't have that one yet
yea its invalid. ListStyleType::ParseSingleValue returns as soon as it matches the symbols func, and the generic property parser then requires the whole value to be consumed. CSSPropertyParser::ParseSingleValue returns null when !stream.AtEnd() so any trailing "junk" after symbols() reject the entire declaration. We didn't have a test for that specific case, so I added two to list-style-type-invalid.html (checking for both cases where symbols() is followed up by a valid keyword and junk).
nit: CHECK is preferred instead of DCHECK when the check is trivial
Done
nit: CHECK is preferred instead of DCHECK when the check is trivial
Done
/*tree_scope=*/nullptr, &symbols_function_value);Instead of storing this, could we just create the counterstyle here based on it, rather than doing it lazily. Then we can get rid of symbols_function_value_. Then IsSymbolsFunction would check if IsCounterStyle and the name is anonymous or something to tell if it is a symbols function
I kept symbols_function_value_ bc it is the computed value of list-style-type and it's what getComputedStyle serializes. A CounterStyle is just the internal object that renders the markers but it isn't a CSSValue, so it can't stand in for that. Building it eagerly would be fine, but dropping the parsed value isn't bc we'd have to rebuild it from the counter style's already-defaulted descriptors, which is messier than just keeping it.
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
return symbols;Ananya AnandWhat if an author sets this to something like "symbols(...) some-garbage" - where there is garbage listed after the symbols function. Do we return invalid in that case? Might be worth a test if we don't have that one yet
yea its invalid. ListStyleType::ParseSingleValue returns as soon as it matches the symbols func, and the generic property parser then requires the whole value to be consumed. CSSPropertyParser::ParseSingleValue returns null when !stream.AtEnd() so any trailing "junk" after symbols() reject the entire declaration. We didn't have a test for that specific case, so I added two to list-style-type-invalid.html (checking for both cases where symbols() is followed up by a valid keyword and junk).
Acknowledged
/*tree_scope=*/nullptr, &symbols_function_value);Ananya AnandInstead of storing this, could we just create the counterstyle here based on it, rather than doing it lazily. Then we can get rid of symbols_function_value_. Then IsSymbolsFunction would check if IsCounterStyle and the name is anonymous or something to tell if it is a symbols function
I kept symbols_function_value_ bc it is the computed value of list-style-type and it's what getComputedStyle serializes. A CounterStyle is just the internal object that renders the markers but it isn't a CSSValue, so it can't stand in for that. Building it eagerly would be fine, but dropping the parsed value isn't bc we'd have to rebuild it from the counter style's already-defaulted descriptors, which is messier than just keeping it.
So there should be precedence to convert the style format to a value format (example: https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc;l=5205?q=CSSGridTemplateAreasValue%20&ss=chromium)
If we don't have enough info in CounterStyle to convert back to the value, then we should consider adding that as opposed to keeping both around
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
added some thoughts! lmk what you think
/*tree_scope=*/nullptr, &symbols_function_value);Ananya AnandInstead of storing this, could we just create the counterstyle here based on it, rather than doing it lazily. Then we can get rid of symbols_function_value_. Then IsSymbolsFunction would check if IsCounterStyle and the name is anonymous or something to tell if it is a symbols function
Alison MaherI kept symbols_function_value_ bc it is the computed value of list-style-type and it's what getComputedStyle serializes. A CounterStyle is just the internal object that renders the markers but it isn't a CSSValue, so it can't stand in for that. Building it eagerly would be fine, but dropping the parsed value isn't bc we'd have to rebuild it from the counter style's already-defaulted descriptors, which is messier than just keeping it.
So there should be precedence to convert the style format to a value format (example: https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc;l=5205?q=CSSGridTemplateAreasValue%20&ss=chromium)
If we don't have enough info in CounterStyle to convert back to the value, then we should consider adding that as opposed to keeping both around
Yeah, that grid pattern works, but symbols() isn't quite the same case. A couple reasons:
1.Grid can reconstruct its value because what it stores is a lightweight, normalized struct (the grid map). But here we're dealing wiht CounterStyle, which is heavyweight and only built lazily (carries a lotta state, it needs GetDecimal(), and fallback resolution). So making it the source of truth would be a step down, not up since we'd have to build it eagerly for every symbols(), even ones only read through getComputedStyle or never rendered.
2.There's precedent for keeping the value. Custom properties store the CSSValue as the computed value and return it directly for getComputedStyle, comparing with base::ValuesEquivalent (same as we do here): https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/core/style/computed_style.cc (see GetVariableValue). That fits symbols() because what you write comes back unchanged when you read it with getComputedStyle so there's nothing to rebuild. Grid is diff bc it already needs that cell map for layout, so keeping the value too would be redundant, which is why it regenerates the value from the map instead. That reasoning only kicks in when you're already holding the resolved form, and we aren't.
3.The named @counter-style path in this same class already stores its reference (the name) and lazily caches the resolved CounterStyle. symbols() is just the inline-value version of that, so keeping the value is the consistent choice.
So I'd lean toward keeping symbols_function_value_, but if you'd still rather reconstruct from the CounterStyle, I'm happy to do that.
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
/*tree_scope=*/nullptr, &symbols_function_value);Ananya AnandInstead of storing this, could we just create the counterstyle here based on it, rather than doing it lazily. Then we can get rid of symbols_function_value_. Then IsSymbolsFunction would check if IsCounterStyle and the name is anonymous or something to tell if it is a symbols function
Alison MaherI kept symbols_function_value_ bc it is the computed value of list-style-type and it's what getComputedStyle serializes. A CounterStyle is just the internal object that renders the markers but it isn't a CSSValue, so it can't stand in for that. Building it eagerly would be fine, but dropping the parsed value isn't bc we'd have to rebuild it from the counter style's already-defaulted descriptors, which is messier than just keeping it.
Ananya AnandSo there should be precedence to convert the style format to a value format (example: https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc;l=5205?q=CSSGridTemplateAreasValue%20&ss=chromium)
If we don't have enough info in CounterStyle to convert back to the value, then we should consider adding that as opposed to keeping both around
Yeah, that grid pattern works, but symbols() isn't quite the same case. A couple reasons:
1.Grid can reconstruct its value because what it stores is a lightweight, normalized struct (the grid map). But here we're dealing wiht CounterStyle, which is heavyweight and only built lazily (carries a lotta state, it needs GetDecimal(), and fallback resolution). So making it the source of truth would be a step down, not up since we'd have to build it eagerly for every symbols(), even ones only read through getComputedStyle or never rendered.
2.There's precedent for keeping the value. Custom properties store the CSSValue as the computed value and return it directly for getComputedStyle, comparing with base::ValuesEquivalent (same as we do here): https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/core/style/computed_style.cc (see GetVariableValue). That fits symbols() because what you write comes back unchanged when you read it with getComputedStyle so there's nothing to rebuild. Grid is diff bc it already needs that cell map for layout, so keeping the value too would be redundant, which is why it regenerates the value from the map instead. That reasoning only kicks in when you're already holding the resolved form, and we aren't.
3.The named @counter-style path in this same class already stores its reference (the name) and lazily caches the resolved CounterStyle. symbols() is just the inline-value version of that, so keeping the value is the consistent choice.
So I'd lean toward keeping symbols_function_value_, but if you'd still rather reconstruct from the CounterStyle, I'm happy to do that.
Ok so I looked into this more. Generally speaking storing both seems un-ideal, especially if you can convert from one to the other. However, I think the piece I was missing was that if you define symbols() it doesn't mean they will actually be used/rendered?
I had imagined normal counter styles were part of a map, and were only used when needed, and that symbols() was inline and always used when defined.
However, maybe it isn't and still has to be applied to actually be used? Is that correct?
If defining symbols() doesn't imply they get rendered, then lazy creating it makes sense. If defining symbols() does imply it will get rendered (unlike other counter styles) then I think we should create it up front and not delay creation to avoid storing both the style and value around when we only need the style value
Do we have any rendering tests that show this being used/rendered? That may help me better understand how this is used in practice (same for the child branch of this)
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
/*tree_scope=*/nullptr, &symbols_function_value);Ananya AnandInstead of storing this, could we just create the counterstyle here based on it, rather than doing it lazily. Then we can get rid of symbols_function_value_. Then IsSymbolsFunction would check if IsCounterStyle and the name is anonymous or something to tell if it is a symbols function
Alison MaherI kept symbols_function_value_ bc it is the computed value of list-style-type and it's what getComputedStyle serializes. A CounterStyle is just the internal object that renders the markers but it isn't a CSSValue, so it can't stand in for that. Building it eagerly would be fine, but dropping the parsed value isn't bc we'd have to rebuild it from the counter style's already-defaulted descriptors, which is messier than just keeping it.
Ananya AnandSo there should be precedence to convert the style format to a value format (example: https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc;l=5205?q=CSSGridTemplateAreasValue%20&ss=chromium)
If we don't have enough info in CounterStyle to convert back to the value, then we should consider adding that as opposed to keeping both around
Alison MaherYeah, that grid pattern works, but symbols() isn't quite the same case. A couple reasons:
1.Grid can reconstruct its value because what it stores is a lightweight, normalized struct (the grid map). But here we're dealing wiht CounterStyle, which is heavyweight and only built lazily (carries a lotta state, it needs GetDecimal(), and fallback resolution). So making it the source of truth would be a step down, not up since we'd have to build it eagerly for every symbols(), even ones only read through getComputedStyle or never rendered.
2.There's precedent for keeping the value. Custom properties store the CSSValue as the computed value and return it directly for getComputedStyle, comparing with base::ValuesEquivalent (same as we do here): https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/core/style/computed_style.cc (see GetVariableValue). That fits symbols() because what you write comes back unchanged when you read it with getComputedStyle so there's nothing to rebuild. Grid is diff bc it already needs that cell map for layout, so keeping the value too would be redundant, which is why it regenerates the value from the map instead. That reasoning only kicks in when you're already holding the resolved form, and we aren't.
3.The named @counter-style path in this same class already stores its reference (the name) and lazily caches the resolved CounterStyle. symbols() is just the inline-value version of that, so keeping the value is the consistent choice.
So I'd lean toward keeping symbols_function_value_, but if you'd still rather reconstruct from the CounterStyle, I'm happy to do that.
Ok so I looked into this more. Generally speaking storing both seems un-ideal, especially if you can convert from one to the other. However, I think the piece I was missing was that if you define symbols() it doesn't mean they will actually be used/rendered?
I had imagined normal counter styles were part of a map, and were only used when needed, and that symbols() was inline and always used when defined.
However, maybe it isn't and still has to be applied to actually be used? Is that correct?
If defining symbols() doesn't imply they get rendered, then lazy creating it makes sense. If defining symbols() does imply it will get rendered (unlike other counter styles) then I think we should create it up front and not delay creation to avoid storing both the style and value around when we only need the style value
Do we have any rendering tests that show this being used/rendered? That may help me better understand how this is used in practice (same for the child branch of this)
Yeah that's definitely a good thing to check! Dug into this further and I think defining symbols() doesn't mean it renders. A few cases of this:
• Not a list item: the element isn't a list item (<li></li>)
• elem uses display: none
• setting a pseudo-elem like this:
li::marker {
content: "→ ";
}
would override any symbols function set on the elem
regarding tests, there are two included: symbols-function.html and symbols-function-dynamic.html
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
How common are these cases? I think the display none case likely isn't worth optimizing for, but in the other cases, do we envision it is likely to be set on a non-list element? Is it likely to be set and then overridden?
In other words, I'm trying to weigh the cost of:
1.) storing both if more often than not, you will end up always creating the style anyways, so it would be cheaper just to store the style up front and not both.
2.) storing just the style and you more often than not don't end up needing it, so it is cheaper to just store the value and create the style only in the rare case it's needed
Any idea on which is more likely to be common?
Given the inline nature, I'm inclined to think that unlike other counter styles, symbols are more likely to end up being used in case where they *are* rendered (but I could be wrong on this assumption)
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |