- From: Dael Jackson <daelcss@gmail.com>
- Date: Sun, 5 Mar 2017 11:00:23 -0500
- To: public-houdini@w3.org
- Cc: www-style@w3.org
=================================================
These are the official Houdini Task Force
minutes. Unless you're correcting the
minutes, please respond by starting
a new thread with an appropriate subject line.
=================================================
Typed OM
--------
- RESOLVED: CSSFooValue objects are mutable
- RESOLVED: Move DOMMatrixReadOnly out of CSSTransformComponent to
CSSMatrixComponent (and make mutable)
- RESOLVED: Add an update() method to styleMap
- TabAtkins will write a mail about if updateAll() is also
necessary.
- RESOLVED: Canonicalize by representing all values, and serialize
to the most-backwards-compatible minimal
representation to be mostly compatible with old CSSOM
(minus some idiosyncrasies), and to gradually bring
old CSSOM in line with new consistent serialization
rules.
Custom Properties
-----------------
- RESOLVED: When you register a property that makes existing value
invalid, we throw out the invalid value.
- RESOLVED: Typed custom properties do not trigger fallback.
- RESOLVED: Registering types for a property therefore does not
force reparse.
- RESOLVED: When typed custom properties fail, they fall back to
their initial value not to invalid.
- RESOLVED: Invalid argument for paint function produces an
invalid image, does not trigger fallback or anything
like that.
- The previous four resolutions need author feedback.
===== FULL MINUTES BELOW ======
Agenda: https://github.com/w3c/css-houdini-drafts/wiki/Seattle-F2F-Jan-10-2017
Scribe: fantasai
Typed OM
========
Mutability
----------
shane: First topic is revisiting mutability.
shane: We resolved in the last meeting that all of the objects in
the typed OM would be immutable.
shane: I've since have implementation feedback from engineers in
Chrome who are building this that that's not great for
performance
shane: Specific example of where it's really bad.
shane: Specific ex is, you've built up a complex transform. Just
want to modify a small part of that frame by frame:
shane writes:
new CSSTransformValue(
[new CSSTranslationComponent(...), ...
new CSSRotationComponent(new CSSAngleValue(5, 'deg')),
...., ]);
fantasai: wow, that is so much writing....
shane: The problem here is that if you do this on every frame,
you're constructing a whole bunch of objects that you then
throw away, repeat, repeat, repeat
shane: Doing a lot of object creation and cleanup on every frame,
which is excessive work.
shane: If this was mutable, though, you could then just modify the
rotation value like: t[3].angle.value +=5;
shane: You just mutate the one piece you're changing.
shane: Given the whole point of this API is high performance, it
seems important that we actually make it high performance =)
Rossen: Why did we make it immutable?
esprehn: For confusion reasons.
esprehn: Today you do element.style.color = ...
esprehn: Concern that people would do
element.style.styleMap.transform and then call a method
on it
esprehn: And then authors would be confused because that's a copy,
not the actual thing
esprehn: But that was maybe preemptive, maybe not really a problem.
shane: We weren't sure coming into the meeting.
shane: We tossed it around, there weren't strong reasons on either
side. We ended up on immutable.
shane: I think this is a strong reason to flip back to mutable.
smfr: Can you write down the mistake that elliot brought up?
<esprehn> ex. element.styleMap.get("transform").rotate(32, 'deg')
<esprehn> nothing happens to the element there
<esprehn> you can alternate mutating the object and setting it
<esprehn> ex. to place objects you could create a transform, and
adjust it on the Y axis as you assign the style of each
item in the list
TabAtkins: The mistake is that you have to set the object back.
smfr: Can replace the whole object, have to get it , modify it,
and then set it?
shane: We want to make an upgrade method to make this easier as
well.
<iank> document.body.styleMap.update('width', w => w.value += 5)
surma: So want to make it mutable but not live.
shane: Right.
plinss: My only concern is that we see all three patterns all over
the platform, and you never know what you're going to get.
Would be nice if we could be consistent with ourselves.
Rossen: Do we have a definition of what we need to be consistent
with?
plinss: No
Rossen: That's another topic for the TAG then.
smfr: Is this proposal to make all the CSSFooValue things mutable?
shane: Yes.
smfr: Would CSSTransformValue have a matrix?
shane: DOMMAtrix is a view of the transform that you've set.
smfr: You'd have to set a whole new matrix if you want to change
it.
smfr: Oh, you're assigning angle there.
smfr: You can set a matrix Component, and then alter that. But
DOMMatrix is the matrix of the whole thing.
smfr: It inherits matrix from the base class?
shane: We'll have DOMMatrix and then... will need to work out.
shane: I think we need to remove the matrix component from the
matrix superclass
shane: I don't think I'm hearing strong disagreement...
Rossen: So first question is whether or not to roll back decision
to make this immutable.
Rossen: Any objections?
RESOLVED: CSSFooValue objects are mutable
Rossen: Next issue is moving the DOMMatrixReadOnly out of
CSSTransformComponent
Rossen: to CSSMatrixComponent.
shane: And making it mutable.
RESOLVED: Move DOMMatrixReadOnly out of CSSTransformComponent to
CSSMatrixComponent (and make mutable)
Rossen: Do we need any other changes?
fremy: Adding update()?
RESOLVED: Add an update() method to styleMap
shane: We might need update() and updateAll()
Rossen: How is set() different from update()?
shane: set() takes a value whereas update() takes a function.
TabAtkins: This is supposed to look like a map. If you're
mysteriously missing standard map methods, that's not
good.
iank: What if you try to update a thing that doesn't exist?
shane: If you call update on a custom property that hasn't been
set, then the input the update function is whatever the
representation of a custom property's undefined value
esprehn: ES map needs update function
esprehn: In C++ you get entry in map and you can do one hash
lookup instead of two. In JS you have to get and set, so
it's two hash lookups.
esprehn: It's supported by underlying platforms everywhere.
slightlyoff: send mail?
ACTION TabAtkins Send mail
Rossen: There's bunch of open issues, but nothing happening.
shane: I have to apologize, I was supposed to get some stuff done
by now, but had some exceptional circumstances in Sydney
shane: This year I started back up on this, been working on
canonicalization.
Rossen: Just wanted to see if we could help with these issues.
shane: They're fairly mechanical afaict.
Canonicalization
----------------
shane: Do want to go over the canonicalization, though.
<nainar> Canonicalization is
https://github.com/w3c/css-houdini-drafts/issues/268
shane: I made a spreadsheet of all the different CSS properties.
shane: Trying to classify them all into different canonicalization
approaches.
shane: We talked about how the properties in the typed OM are
serialized.
shane: Complication of having values not going through string
representation.
shane: We decided we needed a canonical serialization for every
property.
fantasai: Didn't we decide we needed that a long time ago, added a
field to the propdef table?
dbaron: The CSSOM spec refers to canonical order a few times, but
only for shorthands.
shane: Also need to address e.g. colors are canonicalized into rgb.
shane: Going through property by property
shane: Looking at syntaxes and classifying into groups.
shane: For the ones I've done, there's a small set of groups.
shane: Some things are just a choice of keywords, no need to
canonicalize
shane: things which are set of keywords, e.g. flex-flow.
shane: Need to fill in missing productions
shane: e.g. flex-flow, you need a flex direction and a flex wrap.
shane: But if you have a background, you don't want to necessarily
fill in the missing pieces for bits of background that
exist.
shane: Can completely understand what background: red does without
filling in the rest.
shane: Some sets fill in missing pieces, others don't.
fantasai: Do we want to have that distinction? why not follow
shortest-output rule?
shane: The typed OM representation should be able to read both of
these.
fantasai: Wouldn't that be the case for backgrounds as well?
fantasai: There are two types of things here, e.g. flex-flow (like
background), where an omitted value can also be present
-- there is an explicit way to represent a missing value.
fantasai: The there's things like text-decoration, where the a
missing keyword represents a particular state which
cannot be represented explicitly: the presence or
absence of the keyword indicates state, as opposed to
flex-flow where we have two keywords that represent the
two states and one of them is merely the default.
fantasai: Background is the same as flex-flow: you can explicitly
represent the missing values.
fantasai: Why would you treat one different than the other?
fantasai: You're saying that the omitted values aren't
interesting, but that's an opinion, it's not a
structural difference as between text-decoration and
flex-flow.
shane: The background shorthand has two subcomponents, it's got
background-color and then it has stack of images.
shane: If it has no images, then the rest of it isn't interesting.
shane: There's two parts: the color and the stack of images.
dbaron: But that stack has a length of at least one.
dbaron: Because originally there wasn't a stack, there was just
one image.
Scribe: nainar
fantasai: We have had people suggest that we need a color on every
layer - not an assumption you want to make.
TabAtkins: We have that built into the image() function..
fantasai: We don't make a special case where background is ... I'm
not sure. You should be able to read out that
background-image is a null value.
zcorpan: Is it possible to have none? Images in the spec along
with other image.
fantasai: Yes.
fantasai: If you want just the color, read out backgroundColor.
shane: I'm not sure - if background is a complicated thing -
people will default to using color.
Scribe: fantasai
TabAtkins: We're hitting several concepts here.
TabAtkins: One is canonicalization of typed OM stuff.
TabAtkins: The other thing is serialization.
TabAtkins: We do try to have serialization obey the
minimal-representation thing.
TabAtkins: If you only have a background color, you get
'background: red', 'background: repeat none 0 0 red'
etc.
TabAtkins: We need to define serialization.
dbaron: There were a bunch of vague principles.
dbaron: One is minimal representation, other is oldest form (for
most compat) -- usually but not always the same.
dbaron: Shortest in terms of fewest components, not fewest
characters!
zcorpan: CSSOM spec tries to define how serialization should work.
I think the algorithm is broken in some ways.
zcorpan: Tries to omit components that are the initial value, if
it can be serialized that way.
shane: Gets very tricky with e.g. flex shorthand.
TabAtkins: Reason we have to do this, a bunch of properties esp.
shorthands, have values can be easily represented in
typed OM.
TabAtkins: Using string access right now, want compatible way to
expose the string value.
TabAtkins: Without explicit guiding principles, was tasked with
writing out serialization rules for al properties.
shane: Flex is interesting because of 4 special values.
shane: auto | initial | none | <integer>
TabAtkins: It's not weird, it's pretty standard.
TabAtkins: Auto is just a flex basis.
TabAtkins: The expansion rules are a bit special, they don't just
fill in the initial values always.
TabAtkins: We have some keywords that are called out as especially
useful.
shane: If we have a constructed flex object, which matches one of
these keywords that are called out
shane: With flex-grow of 5 and flex-shrink of 1 and flex-basis of
0.
fantasai: That would serialize as 'flex: 5'
TabAtkins: Being the minimal representation.
TabAtkins: This is different from a canonical data structure.
shane: There may be cases where the canonicalization ...
shane: might be cases where canonicalization gets you something
that doesn't serialize out a minimal representation.
TabAtkins: Why can you not produce the same string again?
shane: e.g. top 50% right 50%
shane: When we canonicalize, it's a pair of lengths. When you
re-serialize out...
fantasai: You get 50% 50% -
fantasai: minimal representation.
shane: So we canonicalize by representing all values, and
serialize to the minimal representation.
TabAtkins: Yeah.
shane: Canonicalization fills out all the missing pieces.
gregwhitworth: What is use case where someone asks for shorthand,
then returns a string.
TabAtkins: The use case right now is that typed OM object patterns
don't currently have ability to capture all property
patterns, esp for shorthands which are relatively
complex.
TabAtkins: For those, or for custom properties, we need to expose
the values somehow. And that's a string.
TabAtkins: We need to define what the string is.
TabAtkins: We'd like to get this fixed so everyone is consistent.
TabAtkins: We want typed OM to represent all properties with
objects eventually
TabAtkins: But right now trying to hit 80% case.
gregwhitworth: [...]
gregwhitworth: Should we maybe say V1 we don't handle shorthands?
TabAtkins: We decided at TPAC we need to have shorthands defined.
TabAtkins: That was one of dbaron's conditions.
dbaron: I'm not sure minimal representation is web compatible.
TabAtkins: We have ability to do it new and nice.
<gregwhitworth> I'm primarily saying that most use cases for
shorthands as an author, they'll then parse out to
the longhands. So is it worth spending time on
serializing to strings or could we focus on
building out the shorthand typedOM models for the
shorthands.
fantasai: Have to consider future compat, so will need to have a
"most compatible form" rule going forward.
zcorpan: If you only care about defining serialization for typed
OM, seems like a missed opportunity to achieve interop
for old CSSOM.
zcorpan: Seems like roughly same amount of work to define them to
be the same serialization.
shane: Those incompatible serializations have been shipped already.
zcorpan: But we should try to get interop on those too.
zcorpan: So pick the most web-compatible solution and define that
as the way forward for both.
zcorpan: Instead of defining new rules and leaving old CSSOM stuff.
shane: I think we should have a consistent set of rules, and maybe
we can move CSSOM towards that gradually.
fantasai: Be consistent with the old OM rules even if they are not
the most ideal ones.
fantasai: So that you can keep as much compat with the two systems
as possible.
fantasai: Just drop the really weird idiosyncracies.
fantasai: From the new OM, and then maybe gradually reduce those
in the old OM.
fantasai: Is the rule about most-backwards-compatible minimal
representation in the CSSOM spec?
zcorpan: Minimal representation is, compatible isn't.
fantasai: Probably should put that in the spec then, since that's
the actual principle.
RESOLVED: Canonicalize by representing all values, and serialize
to the most-backwards-compatible minimal representation
to be mostly compatible with old CSSOM (minus some
idiosyncrasies), and to gradually bring old CSSOM in
line with new consistent serialization rules.
break: lunch
Custom Properties
=================
[shane writes styleMap.set('--foo', 'invalid')]
shane: Then register --foo as a color.
shane: That's not a valid color.
shane: If you register it as a color first, you get an exception.
shane: You get a bit of raciness.
TabAtkins: It's not racy unless you do racy things, but it's order
dependent.
TabAtkins: Could make it not order-dependent by making .set
silently.
astearns: First ordering trying to solve, setting something don't
know what grammar applies, then using it in a context
where grammar applies then gets thrown out.
shane: Currently if you have a grammar and you set an invalid
thing, an exception is raised.
shane: If you set something and then register the grammar, don't
get the exception.
esprehn: Do we want it to throw an exception?
shane: Yes.
esprehn: That doesn't match the CSS fallback pattern.
esprehn: Where you set multiple property declaration and the
latest one that is valid wins.
shane: This helps catch mishandling problems.
shane: Much most JS-y
shane: which makes sense because it's a JS API.
esprehn: For unregistered case you want it to hold as a pending
value?
shane: It's a valid value until you register a type.
TabAtkins: The question is, is it OK if this is order dependent?
If not, what do we do about it?
TabAtkins: Could make .set silently fail, probably not great.
TabAtkins: Alternately make the registration throw.
esprehn: That requires synchronously computing registration.
shane: That's bad.
shane: If you read --foo at this point what happens?
TabAtkins: You recalc, and that results in throwing out the
invalid value.
shane: Other option is having explicit invalid value
representation.
TabAtkins: You don't get that if you set 'background' to 'invalid'
zcorpan: What's the use case for setting a property without
setting the grammar?
TabAtkins: That's just a normal custom property.
shane: This is more likely to be a conflict of two libraries than
something intentional.
fremy: What if it works, but did nothing?
fremy: Set the value as an unparsed value
fremy: when browser tries to use the declaration
fremy: like if you did what is in your style sheet, it's still
there in your style sheet but isn't there anymore.
TabAtkins: It's same as 'background: invalid'--effectively thrown
away during parsing. It's in your stylesheet, but not
in the OM.
...
TabAtkins: If you set style attribute, parsing still happens and
we throw out the invalid stuff.
kbabbitt: What happens if I subsequently unregister foo?
shane: We resolved to remove unregistration at TPAC.
shane: Not just this reason, but b/c complicated to support and
didn't have use cases. Fortunately don't need to worry
about that.
fantasai: I think it's fine to not worry about this case, behavior
seems reasonable and expected.
fantasai: Could possibly throw an exception at registration, but
likely to be more annoying than helpful.
shane: It's also very expensive.
RESOLVED: When you register a property that makes existing value
invalid, we throw out the invalid value.
shane: Let's say I have in a style sheet ....
---foo: red;
--foo: invalid;
shane: This is in a style sheet, and foo is registered as a color.
shane: We don't fall back?
TabAtkins: Of course we fall back. we know the grammar.
TabAtkins: Once we know the grammar, can treat it as a property we
know how to parse.
shane: Might be some raciness.
TabAtkins: But when you register you reparse the style sheet.
TabAtkins: That's what needs to happen to make it consistent with
the rest of CSS.
TabAtkins: If we said you don't need to reparse the style sheets,
you get the behavior where that invalid is used,
treated as invalid, becomes
invalid-at-computed-value-time
...
esprehn: You have to reprocess the entire property set.
shane: The behavior with not falling back is consistent with
untyped properties.
TabAtkins: Because it is literally impossible to know what the
value space of a custom property is.
esprehn: But reprocessing the style sheet on registration is very
expensive.
iank: What shane is proposing is that last value always wins, no
fallback.
shane: Even if we know that foo is typed as a color, I don't think
we can deal with this case:
color: red;
color: var(--foo);
shane: We can't fall back in that case.
TabAtkins: At parsing time we don't know the value of --foo.
TabAtkins: Here is where even typed custom properties behave same
as ...
TabAtkins: We could fall back there if we knew that --foo is typed
as other than a color.
shane: ...
TabAtkins: Typed properties have an initial value that matches
their type.
TabAtkins: If --foo is typed as a color, you are guaranteed that
--foo will give you a color.
shane: No, --foo could still be invalid because of loops.
TabAtkins: Loops just need to reset all the involved properties to
something that is no longer referencing anything.
TabAtkins: If you are a typed property, you have an initial value,
we can use that. No reason to use invalid as a value
there.
shane: I think Tim ran into problems when he tried to do that.
TabAtkins: There's no dependency value in falling back to initial.
TabAtkins: Pretty sure we don't need to accept invalid as a bottom
value.
TabAtkins: Thus should be guaranteed that a typed property always
resolves to given type.
shane: So if foo in this case referenced another untyped custom
property?
TabAtkins: Either rando is a color, and parses correctly, or --foo
itself will note that it's not got a color and will
fall back.
shane: Two scenarios on the table:
shane: One is that when we register a custom property, we reparse
the world.
shane: In that scenario we can fall back on typed custom
properties, and on typed vars in typed properties.
shane: In other scenario we don't need to reparse the world, but
never get fallback.
shane: Typed custom properties behave like untyped custom
properties.
kbabbitt: Suppose I have property --foo registered as <length>+
and I say margin: var(--foo);
kbabbitt: Since I don't know the length of the list, so can't tell
if it's valid.
TabAtkins: It's not a valid type match: you'd need <length>{1,4}
<TabAtkins> (The general case for type-matching is actually rather
difficult; a --prop registered as <length>{1,2} should
theoretically work in "margin: 1px var(--prop) 2px;",
but hoo-boy that's making parsing more complicated.)
iank: Once subtle thing:
<iank> https://gist.github.com/bfgeek/460c21569776f75fb707fa8d20419dbe
iank: Arguments for custom paint has similar problem.
iank: Say we specify input items
iank: Initially this property would be invalid:
<style>
.painting { background-image: paint(typed, 10px);
background-image: paint(typed, color);
}
</style>
<script>
registerPaint('typed', class { static inputArguments = ['
<length>']; // etc.
});
iank: What behavior do we want for input arguments?
iank: Here we've registered custom paint argument of type length.
iank: I'd argue that a paint function can never be invalid [unless
it's empty paint()].
TabAtkins: The main reason we have fallback is because we know we
are going to make changes in the future.
TabAtkins: With custom properties, you don't have that problem --
you know what's supported because it's what's in the
library.
TabAtkins: So while consistency with CSS is nice
TabAtkins: fallback is not so critical.
esprehn: The use case is someone publishes a component that is
compatible with two different versions of a library.
TabAtkins: Wouldn't it be better to have two different style
sheets, one per version? Avoids sending over duplicated
data.
TabAtkins: You can't do that with normal CSS.
esprehn: So send different style sheet per UA version?
TabAtkins: You can't predict that and don't have control over it.
gregwhitworth: ...
TabAtkins: Case is API changing out from under you, without your
control. That's essential what browsers do.
TabAtkins: But libraries aren't browsers. You control your
dependencies.
....
esprehn: There's an ancient spec from hixie that introduces a
dependency attribute on scripts and styles and stuff.
esprehn: I think simplifying this make sense. You control your
dependencies. Don't go to /latest
esprehn: If you link to jquery/latest you're probably broken.
zcorpan: Are we saying that we don't want people to use /latest
for libraries in general?
TabAtkins: Should specify version ranges that you know work.
<TabAtkins> (This is already best-practice in Node/etc)
shane: What we're saying is don't reparse stylesheets when
registering custom properties or paints.
TabAtkins: And don't trigger parse-time fallback, in general.
TabAtkins: When they do fail, they should fall back to their
initial value if they're typed. Not to invalid.
fremy: Let's say browser supports new color type.
fremy: And you want your custom property to use the new color type
if available in the browser.
shane: Use @supports
fantasai: I think conclusions here make sense, but should get some
feedback from authors as well.
[Following resolutions need to request author feedback...]
RESOLVED: Typed custom properties do not trigger fallback.
RESOLVED: Registering types for a property therefore does not
force reparse.
RESOLVED: When typed custom properties fail, they fall back to
their initial value not to invalid.
RESOLVED: Invalid argument for paint function produces an invalid
image, does not trigger fallback or anything like that.
<div id="DAB4FAD8-2DD7-40BB-A1B8-4E2AA1F9FDF2"><br />
<table style="border-top: 1px solid #D3D4DE;">
<tr>
<td style="width: 55px; padding-top: 13px;"><a
href="https://www.avast.com/sig-email?utm_medium=email&utm_source=link&utm_campaign=sig-email&utm_content=webmail&utm_term=icon"
target="_blank"><img
src="https://ipmcdn.avast.com/images/icons/icon-envelope-tick-round-orange-animated-no-repeat-v1.gif"
width="46" height="29" style="width: 46px; height: 29px;" /></a></td>
<td style="width: 470px; padding-top: 12px; color: #41424e;
font-size: 13px; font-family: Arial, Helvetica, sans-serif;
line-height: 18px;">Virus-free. <a
href="https://www.avast.com/sig-email?utm_medium=email&utm_source=link&utm_campaign=sig-email&utm_content=webmail&utm_term=link"
target="_blank" style="color: #4453ea;">www.avast.com</a>
</td>
</tr>
</table><a href="#DAB4FAD8-2DD7-40BB-A1B8-4E2AA1F9FDF2" width="1"
height="1"></a></div>
Received on Sunday, 5 March 2017 16:01:31 UTC