New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[css-values-4] Switch advanced attr() to being var()-like #4482
Comments
Doesn't seem to diminish what you can do with it. If that increases the chances of it happening, I'm all for it. |
Yeah, it should be absolutely no loss in correct functionality; the only loss is in catching some authoring errors earlier. You actually gain some functionality via the new |
Sounds reasonable to me and would probably better match author expectations, thus improve the overall experience. By the way, Besides being able to parse as a complete
should be changed to use a familiar
|
There is some discussion at the end of https://bugzilla.mozilla.org/show_bug.cgi?id=435426, where I suggested something similar, though I wasn't smart enough to envision the |
I see no value in allowing image or position - I think you're opening a dangerous door once you allow structures like For what it's worth we've implemented attr() as specified, although we're not yet shipping so that shouldn't factor into amy decision to change the definition. Yes, the type checking on parse seemed an unnecessary step, as the value is going to be validated again anywhere when you evaluate the property. I also agree the type is required - the most common use for attr() from where I sit is @Crissov - as we read it, color: rgb(attr(red number), attr(green number), attr(blue number));
width: calc(50px * attr(multiplier number, 4)); But again, I don't think think there's any value in changing the However, as we're looking at attr I'd like to suggest removing the restriction on the fallback value not being an |
Yeah, I agree with the above regarding the simplicity, not so sure about the For what is worth, the other potentially annoying thing from a browser developer perspective is the opportunity for XSS. We right now sanitize away "unsafe" attributes / elements like |
Being able to turn any attribute into an image load URL would indeed bypass the URL attribute sanitizer in Gecko. Does Gecko currently support using |
Loading an image this way doesn't seem to work in Firefox, Chrome or Safari. But it seems to be because the attr() function isn't parsing. |
We currently don't support it, but implementing this feature would allow it. |
Do we have valid use cases of In my intuition, Anyway, it would be a great news for me if I don't need to implement that 😃 |
Any attribute that overrides or partially overrides another is a potential use case.
|
@gibson042 The use cases you gave are totally valid, and in general, using And I was thinking of a different thing though. Sorry for the confusion. What if the referenced attribute itself contains
Or a detour via
So do we have valid use cases like that? If not, we might want to consider making |
I'm generally in favor of the proposal modulo the concerns that Emilio and Henri raised. I don't know if we have valid use cases for variable references inside Is there any reason not to make all I do find it a little odd that the second argument to |
The CSS Working Group just discussed The full IRC log of that discussion<dael> Topic: Switch advanced attr() to being var()-like<dael> github: https://github.com//issues/4482 <dael> TabAtkins: Several years ago we defined the more complicated attr() functionality where it supplies the type. If you say foo=5px we parse as length. <dael> TabAtkins: No one impl. I realized why. <dael> TabAtkins: It ends up being high cost for low value. Type checking eagerly so at parse time we can reject it it means every thing that does grammar checking have to account for possibility of attr() being there <dael> TabAtkins: Lots of fiddly detail work. <dael> TabAtkins: We did it because we don't have valid at parse time but rejected later. We now have that for var(). The var() machinery and building on that gives us a lot of tools that didn't exist earlier which make attr() easier <bradk> 🐈 <dael> TabAtkins: Precise details of grammar aren't laid out, but core is we make attr() act like var(). It makes poperty automatically valid at parse time and we do parse at computed time. We validate at that time. <dael> TabAtkins: Specifying type lets you validate you put the right thing in the attr(). Handling attributes elsewhere tends to allow garbage and ignore. We maintain that and check type and make sure it works. <dael> TabAtkins: If we base on var() it's the same functionality for authors and a significant decrease in implementation complexity. <dael> TabAtkins: I'd like to persue this change and the impl wants to experiment in it <fremy> I strongly support this! <dael> TabAtkins: Is WG ameniable? <dael> emilio: I'm not opposed but concerned about type checking token string and then doing parsing again. When I looked at impl attr() I suggested doing it like variables in bugzilla. <dael> emilio: Complexity of doing attr didn't seem so high either. I'm concerned about parsing, tokenization, and then parsing on performance. <dael> emilio: Other concern is XSS but that happens either way <dael> TabAtkins: Reason why I don't htink first bit is a concern is it ends up being identical to custom values and properties API. Ideal is it works that way but it's inline <dael> emilio: I think that's also a concern with custom properties. I don't want to block on it, it's mostly theoretical <dael> TabAtkins: Never say never but I doubt used in performance sensistive ways <dael> AmeliaBR: My first concern would be how can we make it work logically with the existing use of the attribute function in the content property <dael> AmeliaBR: You've been talking as distinguishing if an explicit type is set. More I'm thinking maybe not necessary. If you don't have an explicit type the type is assumed string and any attribute can be parsed as a string, returned in a string. So maybe not an issue b/c string is always valid in content <dael> AmeliaBR: I'd like to see the exact write up and make sure it makes sense in backwards compat without special behavior <dael> TabAtkins: Not possible w/o any backwards compat b/c assume valid at parse time. Content properties currently invaid but use attr() become valid at parse time. It is a behavior change if we make unspecified type attr() use this. <dael> TabAtkins: Not sure what's best if we split parsing into separate function rather then flag it as attr() here. Puts you in 2 parsing modes based on detail of function grammar. <dael> TabAtkins: If we think it's okay for behavior change in content where you wrote an invalid with a fallback and you're relying on that that seems minor. Otherwise good with your option. <dael> TabAtkins: There's some possibilities there, we can experiment <dael> AmeliaBR: Youre example of something suddenly valid is if something else in content property would be a parse error. Like using slash syntax with alternative text in a browser that doesn't support makes a difference if it's parse itme error <dael> TabAtkins: Exactly. You'd no longer have the fallback <dael> AmeliaBR: I would lean toward having a separate function for the type version and use attr() for how it's curerntly supported. Might be problematic for UA that support attr() more widely <dael> TabAtkins: No idea if various printers support. I know no web browsers do. I'm not sure impl quality of whole thing <dael> TabAtkins: But this is a behavior change. It will be off if there's a current impl. It's a custom thing or breaking change <dael> TabAtkins: If no other questions just want to check for objections for me creating a full write up of changes. I can do that for review next week <dael> Rossen_: Objections? <dael> fantasai: Summary? <dael> TabAtkins: There's a lot of possible ways how, but it's a change in validation to make it more var() like <dael> TabAtkins: I'll have a write up fully next week. What's in the issue is the right jist <dael> TabAtkins: It's switch attr() to var()-type validation rather than strict parse time validation <dael> emilio: THe fallback might be able to be fix for attr(). Unfortunate to add new type of attr() that can't be detected. Nice if forced to a valid type. Worth thinking about <dael> TabAtkins: Yep. <dael> Rossen_: Objections to the approach of switch attr() to var()-type validation rather than strict parse time validation <astearns> +1 to try this out <dael> fantasai: Not sure, but let him write it up <dael> Rossen_: TabAtkins there's no objections. Go ahead and write it up and we'll look when you're ready <dael> cat: meow |
This makes me wonder whether there would be extra complexity to handle cycle involving both
How does the sanitizer work against CSS variable? Or it doesn't matter? |
Yeah, I'd prefer to avoid this if possible.
I don't think it matters. If you're allowing CSS variables means that you're allowing The interesting case here is where you disallow styles, but end up injecting styles anyway due to an |
I don't like this approach; either
While not technically necessary, I think it's useful as a way to trigger the fallback value. This functionality isn't present in var(), but I think that attributes are more likely to accidentally contain trash (most particularly, the empty string or whitespace, instead of being deleted) than custom properties are, and being able to trigger your default value in those cases seems useful.
Yes, I'm adding the same types that Typed OM is aware of: resolution and flex are new additions.
Currently the spec requires the attribute value to be a simple literal; It also keeps the information flow to a minimum; you'll only get precisely the attribute you requested injected into your stylesheet, not an unexpected attribute or a random custom property. (I'm excluding env() because it's an another arbitrary reference, especially once author-defined env() gets nailed down.) (I'll also put a note in the spec about excluding future reference functionality, to remind us to amend this spec if we add more stuff in the future. For example, I think I'll want to exclude custom functions once they exist.)
The threat surface is just that y'all sanitize URLs proactively, yeah? And this would allow URLs to get fed into the system that y'all haven't pre-examined. It doesn't look like this allows any attacks in XSS terms or anything; an attribute can only cause an image load if the author purposely put an attr(foo url) in an image-loading property, which seems pretty explicit about their intent. And with attr() being disallowed from the attribute's value, it can't shift the attack surface to an unexpected attribute either. Is this right? How bad is the sanitization issue?
Yeah, after discussion on the call, this is my plan. It's technically a behavior change for existing attr() uses, but a minor one; if an author is currently writing something like
Can you elaborate on this, @emilio? |
I just meant that the interesting case in terms of the sanitization-bypass that I mentioned is when you're disallowing the style attribute and
I'm not the right person to evaluate that; @hsivonen maybe? |
The CSS Working Group just discussed The full IRC log of that discussion<dael> Topic: [css-values-4] Switch advanced attr() to being var()-like<dael> github: https://github.com//issues/4482 <dael> TabAtkins: I finished the edits to re-write and they're in Values 4 ED. I put attr() in its own section. mostly cribbed from var spec. I have a note to re-write substitution algo. <AmeliaBR> commit for changes: https://github.com/w3c/csswg-drafts/commit/ed7beac806cc4753d8134857ff526150bb2a631c <dael> TabAtkins: Otherwise it works similar to old. You give a type, checkes if the attribute exists, and subsitutes in if it does. Assume valid at parse time. If it fails when you put it in it's invalid at computed value time. <dael> TabAtkins: Our impl thinks it's reasonable. If anyone else wants to look it would be great. <dael> TabAtkins: Only poss controversy is spec what type the value is, doing parsing on it. If you say color it's recognized as a color. Want to keep b/c attributes messier channel. Easier to mess with, scripts sometimes write them. Ensuring a valid thing comes out and then switch to default seems valuable here. <dael> TabAtkins: I'm happy with current. Any further comments or strong opinions please give them here or in issue. <dael> TabAtkins: I'll ask for resolution to approve next week. <dael> Rossen_: Thanks. <dael> Rossen_: Action on everyone who cares about these changes to review so we can discuss on next call. <florian> +1 to the design goals. Will review details |
Reminder to please review the new text at https://drafts.csswg.org/css-values/#attr-notation |
I like it. I think it'd just suggest adding one more attr-type (called That way, you could write stuff like that: <style>
.foo { background: attr(data-light-background auto, white); }
@media (prefers-color-scheme: dark) {
.foo { background: attr(data-dark-background auto, black);}
}
</style>
<div class=foo data-dark-background="center / contain no-repeat url("logo.svg"), #222 35% url("pattern.png");">
…
</div> |
We (the RealObjects PDFreactor development team) had a look at this issue and the new specification text: |
I like |
I ended up not putting |
The latest spec seems very generous on the fallback value:
This is different from the old CSS3 spec that, we no longer verify the fallback against the given type. So I got a few questions:
Note: There's an existing WPT expecting
|
|
There's a compatibility issue found via WPT css/css-variables/variable-generated-content-dynamic-001.html
Before the spec change, we got a Now the resolution of In other words, we got a behavioral change even when the declaration is valid. Besides, could you clarify the substitution value for types
I suppose there is always no substitution value when the attribute is missing? So that the behavior is different from when the attribute value is the empty string. |
Ah, my intention was that a lack of value is different from an empty value (and a lack of value always triggers fallback). I'll clarify the spec. |
The latest draft has the attribute name defined as attr(foo)
attr(m|foo)
attr(|foo)
attr(*|foo) I don't think that last one was intentional, but if it was we need to define what it means with |
Can this be used with @property --foo {
syntax: "<number>";
initial-value: 0;
inherits: true;
} |
@heycam pointed out back in 2019 something I think got lost in the subsequent conversation:
This is a great point. |
Currently, the "advanced" attr() functionality that lets you specify a type to parse as and a fallback to use is specified to require treating the attr() as the specified type during parse time. That is,
width: attr(foo color);
must be rejected as invalid at parse time, becausewidth
doesn't take<color>
arguments.This definition predates the introduction of var() and its specifying of how to handle values that are invalid when substituted in; at the time attr() was written, "invalid at computed-value time" didn't exist, so the only way we knew how to handle this sort of thing was to eagerly typecheck and specify defaults.
One of our Chrome engineers (@xiaochengh) is looking into implementing advanced attr(), and reviewing it with them, it looks like implementing the function as specified will be quite complicated; handling its type-checking will require additions to every manually-parsed grammar in the browser.
However, switching attr() over to acting like var() should be much simpler. All the machinery for handling something substituted at computed value time, and possibly being iacvt, already exists and can be leaned on pretty easily for all properties.
(The type argument does still have value here. For one, it's a switch that dictates whether you take the attribute value as a literal string, or CSS-tokenize it. For two, it advertises to later stylesheet readers what's expected. For three, it helps you guard against garbage in attributes, which is more likely than garbage in variables, and use a fallback instead. For four, the
px
/etc values are useful for parsing plain numeric attributes, which are common in XML languages, into the appropriate CSS unit, without needing calc() shenanigans.)So the proposal, in full:
attr(foo string)
.tokens
type (or maybe spell it*
) that does no parsing, but does still tokenize the value and substitute it accordingly (rather than substituting it as a<string>
), to handle more complicated cases, now that we're free from the tyranny of parse-time type-checking. This type will only trigger fallback if the attribute isn't present at all.Thoughts?
(Note for the Agenda+; I won't be attending the Nov 6th call, so please schedule it for the Nov 13th call instead.)
The text was updated successfully, but these errors were encountered: