Discussion:
[Houdini] Minutes Tokyo F2F 2017-04-18 Part II: Typed OM
(too old to reply)
Dael Jackson
2017-05-15 08:41:20 UTC
Permalink
Raw Message
=================================================
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
--------

- TabAtkins proposed a new approach to numeric values:
https://docs.google.com/presentation/d/1s1LgVKntulttemMacBMbErOAc0UQzLbdol4rQbQf-aw/edit?usp=sharing
- RESOLVED: Adopt Tab & Shane's new proposal for value API as an
improvement over the current proposal; issues around
cases requiring an AST might, however, require further
revision of the design. [Note: This resolution was
overridden by the CSSMathValue one below.]
- RESOLVED: Use iterable (option 2) for Array-like CSS interfaces,
unless/until Proxies become more performant.
See
https://docs.google.com/presentation/d/1pXoJ4vqRfjww7xJ8DYTYk8LBu0kGyGyg0yZm88w8xBw/edit?usp=sharing
- RESOLVED: inline style attribute map is also live
https://github.com/w3c/css-houdini-drafts/issues/149
- RESOLVED: Custom properties that have an initial value show up
in computed style, warn authors about perf issues if
they type everything all the time
- RESOLVED: For inline style, should return the custom properties
that are set on the inline style of the current node
- RESOLVED: For specified style, should return the custom
properties defined in the current rule
- RESOLVED: For computed style, should return all defined custom
properties on the current node (including inheritance)
- RESOLVED: Be consistent about null vs throw in all three
.parse() syntax error cases; follow TAG guidelines
once they have them.
https://github.com/w3c/css-houdini-drafts/issues/305
- RESOLVED: Replace previously-resolved value API with
CSSNumericValue tree.
* CSSNumericValue has two subclasses: CSSMathValue and
CSSUnitValue
* CSSMathValue has two members:
- operator, a string representing a math op like *,
/, min, max, +
- operands, a list of CSSNumericValue.
* CSSNumericValue has a method called toSum which
returns a map of units to numbers representing a
sum of those dimensions, as previously discussed
in Tab & Shane's proposal above. If the
CSSNumericValue cannot be so reduced, it either
returns null or throws, whatever the TAG decides.
- RESOLVED: Using properties is moved to L2
https://github.com/w3c/css-houdini-drafts/issues/310
- RESOLVED: Name style maps accessed via element.attributeStyleMap
and element.computedStyleMap, with attributeStyleMap
maybe renamed later. Pseudo-element styles accessed
through PseudoElement APIs.
https://github.com/w3c/css-houdini-drafts/issues/350
- RESOLVED: Update Typed OM draft on /TR with above edits.

===== FULL MINUTES BELOW ======

Agenda: https://github.com/w3c/css-houdini-drafts/wiki/Tokyo-F2F-April-18-2017

Scribe: fanasai

Typed OM
========

Numeric Values Rethink
----------------------

<TabAtkins> https://docs.google.com/presentation/d/1s1LgVKntulttemMacBMbErOAc0UQzLbdol4rQbQf-aw/edit?usp=sharing
TabAtkins: Why I changed everything about how we did numeric
values.
TabAtkins: Previous number type looked like this: attribute double
value;
TabAtkins: Previous simple length was similar, added readonly
LengthType type;
TabAtkins: calc() was a structlike structure with em, px, etc.
properties.
TabAtkins: Angle values were similar to calc() but looked like a
length. Automatically converts between units.
TabAtkins: This is a problem.
TabAtkins: We have 3 different unit things that have dramatically
different representations.

TabAtkins: Length version is very nice for setting up arbitrary
units. Not bound to unit types we have exposed.
TabAtkins: No updating needed to support new units.
TabAtkins: calc() is nice because it supports sum of values.
TabAtkins: And angle has great auto-conversion.
TabAtkins: But the differences make the system weird.
TabAtkins: ...
TabAtkins: Even then, what we added wasn't good enough.
TabAtkins: We're adding unit algebra into calc() soon.
TabAtkins: px/em needs to remain as px/em, couldn't do that with
old calc interface.
TabAtkins: And also mixing number.
TabAtkins: It also doesn't support custom units.
TabAtkins: Could set that in unit type, but can't do it in calc()
TabAtkins: Can't do that in angles, same problem.
TabAtkins: Bad for various reasons.

TabAtkins: So I rewrote everything into a new structure that
replaces everything.
TabAtkins: Replace it with two classes, plus superclass.
TabAtkins: First one is for dimensions/percentages/numbers
TabAtkins: Other is for calc.

TabAtkins:
interface CSSUnitValue: CSSNumericValue {
attribute double value;
attribute DOMString unit;
readonly attribute DOMSTring type;
};
TabAtkins: This handles everything that is a dimension
TabAtkins: it handles custom units, in future
TabAtkins: also handles number and percent.
TabAtkins: It becomes a problem if we create a unit named "number"
or "percent", but that's unlikely.
TabAtkins: Type is used internally to maintain length/angle/number
etc.
TabAtkins: so that can throw if there's a type mismatch, etc.
smfr: What about z-index?
TabAtkins: Those are numbers.
TabAtkins: Integer inputs will clamp numbers
<TabAtkins> calc(5px + 2em) => {"px": 5, "em": 2}

<astearns> https://www.irccloud.com/pastebin/Db0ATwfQ/
TabAtkins: ^ represents calc
TabAtkins: with input validation.
TabAtkins: ...
dbaron: How is this going to extend to calc(3px*2vw/2em)?
TabAtkins: Each such expression can be turned into simple sum of
complex units.
TabAtkins: Those will just be strings.
several people: :/
<shane {"px*vw/em": 3}
TabAtkins: I know of literally no better way to do it.

plinss: How are you storing the operators?
TabAtkins: Every calc can decompose into a sum of united values.
dbaron: Not quite that obvious
dbaron: if you have a sum in the denominator.
dbaron: It's a logical piece of unit algebra
dbaron: We already have a resolution to add unit algebra to L4.
<dbaron> calc(4em / (2em + 2px))

fantasai: What about handling min()/max()?
shane: ...
TabAtkins: Yeah, you can produce expressions that don't reduce
well... would need to produce a tree.

plinss: You're decomposing multiplication into addition?
TabAtkins: No, you distribute
TabAtkins: Unless you have a sum in the denominator, would work
shane: min/max would need a different approach.

shane: Advantage of this interface is that all calc expressions
today, and majority in the future, can be represented here.
shane: Very useful for interrogating through script.
shane: Just says what units are expressed in the calc.
shane: It's a compromise between map and string representations.
shane: Shame we can't express every single calc expression, but
this still captures most of them.
TabAtkins: Will have to change some parts of sub-expressions as an
AST.
<TabAtkins> calc(5px + 2em/(5% + 10px)) => {"px": 5}, + AST for
the second half
shane: It would be a tree representation.
shane: A real pain to deal with in script.
<iank> {type: 'div', numerator: {type: 'value', value: '4em'},
dem: {type: 'plus', value: [{type: 'value', value: '5%'},
{type: 'value', value: '10px'}]}}
<iank> ^ a bad AST representation of dbaron example

jack: So this will be able to represent all?
jack: If some will be represented as complex strings, how is that
better than now?
...
TabAtkins: Every single calc you can express today is decomposable
into sum of simple units
TabAtkins: so simple units as keys.
TabAtkins: Only get arithmetic in the units if doing unit math.
shane: ...
shane: These are like channels, each unit is a channel.
shane: Complex units, except those with sums in the denominator,
are the same concept.

till: What would be representation for 100% - 50px
<TabAtkins> calc(100% - 50px) => {"percent": 100, "px": -50}
shane: percent: 100; px: -50
shane: This works nicely if you add, -25% + 50% you end up with
25%.

SimonSapin: So the way to output is to make a sum?
SimonSapin: That's not obvious, maybe put sum in the name? e.g.
CSSCalcSum
TabAtkins: Not sure that's necessary
fantasai: Seems reasonable to me. We might in future want to
represent calc as the tree that it was.
fantasai: Since this represents a simplification of the calc,
reasonable to express that in the type name.
shane: I'm okay with that.
shane: I also think that we can extend this to represent the
remaining cases that haven't been specced yet by having an
AST member in this that represents the full tree.

fantasai: That sounds great, but my concern is how are we dealing
with min & max?
fantasai: Want to see how that fits into this interface.
TabAtkins: Didn't draw that up yet.
shane: Let's sit down at lunch and figure that out
TabAtkins: Just want to point out that old proposal didn't deal
with this either, this is an improvement in any case.

till: How do vars figure into this?
TabAtkins: It's represented as an unparsed value instead.
shane: That's only for non-computed style uses.

<dbaron> [moved on to slide 10]
TabAtkins: Once the var substitutes, you'll get a proper whatever.
TabAtkins: Superclass has stuff that all values should be able to
do.
interface CSSNumericValue : CSSStyleValue {
CSSNumericValue add(CSSNumericValue value);
CSSNumericValue subtract(CSSNumericValue value);
CSSNumericValue multiply(double value);
CSSNumericValue divide(double value);
bool equals(CSSNumericValue value);
CSSNumericValue to(DOMString unit);
static CSSNumericValue parse(DOMString cssText);
};
TabAtkins: Math is awkward, let's do it for you
TabAtkins: When necessary will pop out a calc value, but in simple
cases won't.
TabAtkins: e.g. adding two px values results in a px value.
TabAtkins: adding px plus percent gets you a calc().

TabAtkins: This is very strict at the moment.
TabAtkins: Will tell you that 5px and 5px are equal.
TabAtkins: Will not tell you that 96px and 1in are equal.
TabAtkins: Also won't tell you that a unit value of 5px and calc
value of 5px are equal.
ChrisL: Why?
TabAtkins: Question is why you would want to compare things.
TabAtkins: Examples we could tell, probably didn't want to do a
loose equality.
TabAtkins: That said, okay with adding.
ChrisL: Was wondering why. Doesn't seem hard.
TabAtkins: Not hard, but didn't seem to fit with uses.
fantasai: Seems to me you would want to have the ability to do
equivalence matching.
TabAtkins: Wanted to match with what would happen with JS value
equality.
fantasai: That's fine. I think you just probably want both.

TabAtkins: If there's even remotely realistic case, would add
interface that'll do conversion equality.
TabAtkins: We might not need to add that except as a convenience,
though
TabAtkins: because to() function will convert from one type to
another, so long as it's possible.
TabAtkins: Throws if it can't, e.g. not enough info.
TabAtkins: Loose equality would just be a convenience function,
could otherwise convert both things to the same unit.

leaverou: ????
TabAtkins: If you're dealing with computed values, already
resolved away. If dealing with specified values, keep
them as the types that you're looking at and they'll
resolve later on.
shane: Should to() let you cast to combinations of units as well?
TabAtkins: Send it to em+px? Would produce a calc for that?
TabAtkins: That's not the worst idea. Open an issue for me?

TabAtkins: Last function is parse()
TabAtkins: Will turn it into a CSSNumericValue.
leaverou: calc(1px + 2px)?
TabAtkins: You get calc value of 3px. calc(3px)
TabAtkins: Use the math functions if you want ...
leaverou: If calc(3px) is equivalent to 3px, should output 3px.
TabAtkins: Also logical to maintain the calc() that was your
source input.
shane: Calc expression at one end and the other, animating between.
shane: Just because one of them hits zero at some point in the
middle, don't want the calc() wrapper to suddenly disappear.
Rossen: Also want to be able to round-trip it for tooling.

SimonSapin: parse() function takes CSS syntax?
TabAtkins: Yes. Invokes Syntax module algorithm.
SimonSapin: Is there a way to create without parsing?
TabAtkins: Yes, they all have constructors.
TabAtkins: CSSCalcValue has two constructors. Takes a dictionary.
TabAtkins: Other one also should take a map iterator, but
specifying that in WebIDL is really messy right now.
TabAtkins: So put an issue that when WebIDL makes that better,
let's add it.

till: So if calc() is always for sums, why not call it CSSSumValue?
till: Then can have a CSSProductValue.
TabAtkins: Don't think we have a CSSProductValue.
TabAtkins: Also like that it matches up to the functional notation
name.

iank: With the to() function, converting 1em to px, that would
throw?
iank: At some point could add a dictionary to have the conversions
iank: So you could specify what an em is.
fantasai: You would want to have the layout engine and the JS use
different conversion factors?
TabAtkins: You would pass an element, or an element and property,
for reference on how to convert the units.
shane: We have a typed-om-2 label in the issue tracker, so file
them.
SimonSapin: You mentioned resolving value of an element against a
property, including percentages, does that include
percentages resolved during layout?
TabAtkins: Theoretically?
TabAtkins: Definitely L2, can work out details later.
SimonSapin: If you do that probably want it to be asynchronous.
iank: Could provide utility methods for resolving e.g. inline size.
iank: Because we don't want ppl to go through writing modes.
???
iank: ????

TabAtkins: We put it into the ED, if anyone objects, will take it
out :)
TabAtkins: Meade from Chrome is working on it.
astearns: Anyone from Gecko?
dbaron: No idea.
<till> dbaron: based on https://bugzilla.mozilla.org/show_bug.cgi?id=1278697,
I don't think anybody's working on typed OM
TabAtkins: This has been in the spec for ~2months
<astearns> it wouldn't be the worst idea to have a resolution
fantasai: I'm happy to resolve on this, so long as we note that
the max/min investigation might result in further
revision.

SimonSapin: Cases not supported by maplike calc, do we have a plan
for another API?
TabAtkins: Gonna discuss with shane over lunch
TabAtkins: Those parts can hang off in the AST
SimonSapin: So some parts of the calc will be represented in the
sums, but not all?
TabAtkins: Yeah
<shane> I think everything would end up in the maplike, it's just
some of the bits wouldn't map to numbers.
fantasai: That seems really bad.
TabAtkins: Previous approach was worse
fantasai: happy to replace previous approach with this one, but
this one still has issues with calc that we need to fix
shane: could have a key for the extra bits
Rossen: objections?

RESOLVED: Adopt Tab & Shane's new proposal for value API

Rossen: Please file those key issues mentioned so the draft
doesn't progress without them addressed.

Array-like Frustrations
-----------------------

<TabAtkins> GitHub Topic: https://github.com/w3c/css-houdini-drafts/issues/239
<TabAtkins> https://docs.google.com/presentation/d/1pXoJ4vqRfjww7xJ8DYTYk8LBu0kGyGyg0yZm88w8xBw/edit?usp=sharing
TabAtkins: Couple interfaces in typed OM, e.g. CSSUnparsedValue,
CSSTransformValue, that are just sequences of other
values. They have other extra data, e.g. transform has
an equivalent matrix.
TabAtkins: But at their core, they are sequences of values. So
they really want to be arrays.
TabAtkins: Really want to use array notations.
TabAtkins: But making something array-like, only way is to use
Proxies, which are slow and not desired by implementers.
TabAtkins: Annoying because maps and sets are easily faked.
TabAtkins: WebIDL is also broken, can't even do iterables without
invoking Proxies.
TabAtkins: WebIDL is doing something very stupid for bad reasons. (
I'm gonna be really judgmental here.)
TabAtkins: Wanted to go over these issues.
SimonSapin: Can we fix WebIDL?

TabAtkins: First option is suck it up, use LegacyArrayClass.
Invokes Proxy. Not great.
TabAtkins: It also means that any Array methods that return an
array, return a real array, not an instance of our
arraylike.
till: That shouldn't be true anymore with species.
TabAtkins: That's option 3 :)
TabAtkins: I don't want to do Option 1, but it is a possibility.

TabAtkins: Option 2 is Use iterable
<shane> Not an array, but can be turned into one: let arr =
[...cssThing]
<shane> As written today, would still invoke Proxy; it’s written
to require indexed properties. Totally fixable, just
hasn’t been fixed yet in WebIDL.
<shane> (kv-iterator doesn’t act badly like this. It can be fixed!)
<shane> Probably good enough, at least as a first step.
TabAtkins: This lets you iterate in a for loop, or to cast things
into an array.
TabAtkins: It's okay as a first step.
TabAtkins: Fairly easy to get to.
TabAtkins: Problem is WebIDL value iterators, which is what we'd
use here, still requires Proxy.
TabAtkins: ....
TabAtkins: So WebIDL needs to be fixed.
TabAtkins: This would be okay.
TabAtkins: Could also hack around key-value iterator. Doesn't have
the problems. But it's a hack.
TabAtkins: This is a possible solution, particularly in ES6
casting is easy.

TabAtkins: Option 3 is desirable one which is ES spec now has
proper subclassable arrays.
TabAtkins: Similar to previous step, but it's a real Array.
TabAtkins: Will also return the correct subclass from Array
methods.
ChrisL: Which version of ES spec did that come from, and can you
compile this down to ES6?
till: It's ES6 and it is implemented in all engines.
TabAtkins: Not fixed in WebIDL yet.

TabAtkins: Only problem with this is that you still can't
intercept a set operation, like you can with maps.
TabAtkins: So people can put random crap in your interface
TabAtkins: e.g. CSSUnparsedArray is suppoed to only have strings
and ??
TabAtkins: Not sure what to do with that, do we throw when you add
something that's invalid?
TabAtkins: Most of the time you can sanitize at ...
leaverou: If the issue is that people can mess with your types,
that's just how JS works.
TabAtkins: In maplike, in calc(), because it has an impl of set
method, can do type checking and tell you if you're
doing something wrong right away.
TabAtkins: If you're setting an arbitrary object to px key, will
throw, because not a number.
TabAtkins: Doesn't look correct, and then later when you're
setting into a property map, break down.
leaverou: That's their own fault.
TabAtkins: But we still have to handle correctness.
TabAtkins: And we'd have to handle it via checking every set,
iterating over it, even in cases where you haven't done
anything wrong, which is more computation than is
required.
TabAtkins: If you're strict at the start, then we don't have to do
such extra checks.
till: I think you fundamentally want Proxies to implement that.
<TabAtkins> AWB had a proposal to let you hook the [] syntax, but
that's never gone anywhere. :(

shane: At least with proposal 2 the only time we need to check
correctness is on construction, and that is very ...
shane: We wouldn't be in a situation where every time we need to
use contents we typecheck them.
TabAtkins: So I think we should go with option 2 for now, and then
in future, go into Array subclassing.
Rossen: What would prevent us from going to Array subclassing in
the future?
TabAtkins: Only if you're doing exotic checks on whether it's an
array or not.
TabAtkins: If just iterating over it, won't notice the difference.

till: The performance on Proxies might not be that much of a
problem going forward.
till: Engines are doing pretty good optimizations nowadays
till: If all you have is a set hook, then should be optimization.
TabAtkins: I was told that's not the case.
iank: I was told we're not going to be able to make Proxies fast
anytime soon.
till: in the general case, that's true, but this isn't the general
case.
TabAtkins: If good news comes out, ok to change, but for now would
go with option 2.
Rossen: So you're proposing to go with option 2.
Rossen: Any objections?
Rossen: Anyone for option 3 at this point?

RESOLVED: Option 2- Use iterable

Remaining Typed OM Issues
-------------------------

<shane> Issues list:
https://docs.google.com/document/d/1NA020gku-tgEHMGK_8xkB6Ksfpd8ieL4z9jFoGchzbA/edit
shane: 7 issues that need discussion
shane: #239 and #359 just discussed

Issue 149: Describe that StylePropertyMaps are not live objects
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

<shane> Topic: https://github.com/w3c/css-houdini-drafts/issues/149
shane: We resolved in September of last year that the specified
and computed style maps were live.
shane: So if you made changes not through typed OM, would see
those changes reflected in the map.
shane: We did not discuss inline style map.
shane: I assume that would be live as well, anyone disagree?
dbaron: I consider that one case of the specified style.
shane: Same as specified style in the sense that represents
specified style, but has different backing model.
Rossen: If we're making specified style live, inline is just
specified.

RESOLVED: inline style attribute map is also live (Issue 149)

StylePropertyMaps wrt custom properties
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

<shane> Topic: https://github.com/w3c/css-houdini-drafts/issues/276
TabAtkins: We need to clarify how style map works with custom
properties.
TabAtkins: Suggestion from shane is that for style attributes,
custom properties set in style attr should be included.
TabAtkins: For style rule, include those custom properties in that
style rule.
<shane> For inline style, should return the custom properties that
are set on the inline style of the current node
<shane> For specified style, should return the custom properties
defined in the current rule
<shane> For computed style, should return all defined custom
properties on the current node (including inheritance)
TabAtkins: [reads what shane posted]

iank: If you register a custom property, it won't show up
automatically?
TabAtkins: No.
shane: What if you registered it with an initial value?
iank: That's what I meant.
TabAtkins: That's a good question.
iank: ...
dbaron: That has a computed value then, so it should show up.
shane: Just in computed value then, not in specified/inline.
shane: I wonder if we want to allow having an invalid initial
value?
TabAtkins: You can, have an unregistered property with grammar of *
SimonSapin: What happens in old style CSSOM?
TabAtkins: Undefined, but hopefully consistent.
iank: It would make sense that all registered properties appear in
computed style maps.
TabAtkins: [...]

shane: One pattern we've seen with untyped custom properties is
that people use them like macros.
shane: They put a lot of them at the root node
shane: to represent colors that might be used on the page, etc.
shane: Not uncommon to see pages with one or more custom
properties defined.
shane: Relatively easy to keep performant if not referenced much
shane: But could be an issue for typed properties if the same
pattern used, because significantly increases size of
computed value map.
shane: It would be bad if people typed their untyped custom
properties.
shane: At the very least, should provide suggestion that ppl don't
do that.
shane: But might want to look into CSS API for that.
TabAtkins: Currently only JS api for registering, so most of these
cases likely won't do that...
shane: So, should typed custom properties show up in computed
style?

RESOLVED: custom properties that have an initial value show up in
computed style, warn authors about perf issues if they
type everything all the time
RESOLVED: For inline style, should return the custom properties
that are set on the inline style of the current node
RESOLVED: For specified style, should return the custom properties
defined in the current rule
RESOLVED: For computed style, should return all defined custom
properties on the current node (including inheritance)

iank: We also may come back later with impl experience to say this
was a terrible idea, but we'll see.

SSStyleValue.parse(): throw vs return null?
- - - - - - - - - - - - - - - - - - - - - -

<shane> Topic: https://github.com/w3c/css-houdini-drafts/issues/305
TabAtkins: When you call CSSStyleValue.parse()
TabAtkins: parses arbitrary string into typed OM value.
TabAtkins: Takes a property name and a value.
TabAtkins: Several ways to do something wrong, not sure whether to
throw or return null.

TabAtkins: 1st case is, property is not even an identifier.
SimonSapin: If property is a separate arg, not parsed out from
"prop:val"
SimonSapin: Then do you even need to parse it?
SimonSapin: For CSS.supports(), we have API with 2 variations.
SimonSapin: One with colon syntax, uses CSS syntax.
SimonSapin: Other one passes prop and value separately
SimonSapin: In that case you don't parse it,
TabAtkins: ...
SimonSapin: Just assume it's an ident,

TabAtkins: Yes, that avoids the issue entirely.
iank: Might want to throw on the empty string.
iank: We throw a SyntaxError on that.
SimonSapin: Empty string is not valid property name.
iank: It's not a valid function name.
SimonSapin: Are we talking about property names.
iank: First argument in custom paint is an ident, same issue.
shane: What does supports() do with empty string?
SimonSapin: Supports method, you can give it custom property, in
which case name starts with --

shane: There are 3 different error conditions.
shane: 1st, not a valid property name. If that's just empty
string, still.
shane: 2nd, valid property name, but doesn't exist. Can only
happen for non-custom properties.
SimonSapin: Could treat those the same.
shane: 3rd, valid property name, but value's grammar doesn't match.

shane: I thought 2 & 3 were the same, but both of them could be
that you're simply running on a browser that hasn't built
support for that syntax yet.
TabAtkins: Taking the property name makes it not an error.
shane: Could treat them all the same, and throw.
Rossen: It would be easier to handle null from user side.
Rossen: Rather than wrapping everything in try for parsing all
over the place.
Rossen: 1 & 2 are more of an error than 3.
Rossen: 3 is you're parsing a value that you can't parse.
shane: But both 2 & 3 could be that you're trying to parse a
feature that the browser doesn't yet support.

dbaron: It does feel like the more CSS-ish thing to do, not to
throw for unsupported values or properties.
dbaron: In normal CSS, they get silently dropped
dbaron: so null seems more sensible.
shane: Note that we throw if you set a value that doesn't parse
according to your type already.
dbaron: If mistyped things throw, then unknown things should
throw. I would agree with that.

philipwalton: My intuition is, you don't want to both try-catch
and check for null when you're writing code.
philipwalton: I think there are many cases you want try-catch, so
throwing an error is probably best.

<astearns> what about not throwing, but returning a value that
throws an error when you try to read it? Could that be
the worst idea?
<TabAtkins> Worst idea, yes.
<TabAtkins> Value that can validly be used, but represents a
random property/value.

shane: Rossen, you were leaning towards null before. What do you
think now?
Rossen: Not gonna object, but not gonna love it.
iank: What's your hesitation?
Rossen: More of a philosophical discussion.
Rossen: I'm very biased to the APIs we work on on the Windows
platform.
Rossen: We have strict principles on what is an error, how to deal
with errors, how to make API that is ergonomic for
developers.
Rossen: Don't want to sprinkle try-catch all over, half is
error-handling, half is business logic.
Rossen: We have strict rules to guide our APIs. Not gonna impose
those here.
fantasai: Might be worth having that philosophical discussion
fantasai: and codify some principles for our APIs, whether same or
different from Windows API principles

shane: Inconsistent to return null here.
shane: You then set this value, want to throw an error here
[missed exactly the nuance there]
<dbaron> I think Shane was saying that it's not as inconsistent as
he thought it was before.
<shane> yup. When we set values we need to throw an exception on
error because there's no return value
<dbaron> because the other case was setting an object and getting
a type mismatch, whereas this is parsing a string.
<shane> but here we're getting the result as the return value so
we can use null to signal an error

iank: Does TAG have any guidance?
plinss: We have a document discussing principles for designing
APIs, but don't have anything on this topic.
Rossen [offers to review that? send feedback? something?]

Rossen: So going back.
Rossen: Are you reverting your opinion about throwing vs null?
shane: Yeah.
shane: I'm going back from saying we should throw to not having a
strong opinion.
Rossen: Does anyone have a strong opinion?
philipwalton: Likely to do this in environment where you don't
know what you're getting
philipwalton: so maybe throw.
Rossen: Think about it from a mid-layer library, start exposing
some of those.
Rossen: First they're going to write their own wrapper that does a
throw/catch
Rossen: are going to call that so they don't have to deal with
throw/catch all over.
Rossen: If that's the principle we design around, okay.
Rossen: ...

fantasai: Sounds like we should table discussion and have your
philosophical discussion about throw vs null?
plinss: To me, throwing is semantically different than returning
an error.
plinss: If you pass in a property that isn't an ident, e.g.
numbers, can't possibly be a property name. Could see an
error.
plinss: But if passing a name that's unknown to the engine, then
return null.
plinss: Not recommending that's what we do, but there's a
distinction between throwing errors and returning null,
and it's useful to make that distinction.
TabAtkins: If you do construct a value that doesn't make any
sense, and you return null. Whatever you do with that
will throw an error.
TabAtkins: All you can do is check for null.
plinss: Throwing is for exceptional circumstances like out of
memory.
plinss: I've tried using it for everything, and that way lies
madness.
plinss: For a certain class of things, throw exceptions, and be
consistent about it.
plinss: We need some guidance on that, and we don't have it.

iank: I was just looking through a few of the Web apis, like
json.parse
iank: Dae.parse() does not ... seems like more things throw than
silently fail.
iank: HTML throws if not well-formed.
iank: Could file an issue on the TAG.
shane: Let's do that.
Rossen: Let's resolve with following -- in all three cases we will
be consistent in how we handle the condition.
Rossen: And we will follow the TAG guideline on whether to throw
or return null.
<dbaron> The TAG guidance might not be all that prescriptive...
<plinss> https://github.com/w3ctag/design-principles/issues/55

RESOLVED: Be consistent in all three cases; follow TAG guidelines
once they have them.

<br type = lunch>

Numeric Values Rethink Revisited
--------------------------------

<dbaron> Github topic: https://github.com/w3c/css-houdini-drafts/issues/359
shane: We came up with a proposal over lunch.
shane: Getting rid of previous proposal that we resolved on
shane: and replacing it with CSSNumericValue which has two
subclasses: CSSMathValue and CSSUnitValue
shane: CSSMathValue has two members, operator and operands
shane: operator is a string representing a math operator like "*"
shane: *, /, min, max, +
shane: operands is a list of CSSNumericValue.
shane: CSSNumericValue has a method called toSum which returns a
map of units to numbers
shane: representing a sum of those dimensions, as previously.
shane: If the CSSNumericValue cannot be so reduced, it either
returns null or throws, whatever the TAG decides.

SimonSapin: If the initial syntax uses unnecessary parentheses or
calc() inside of calc(), do we want the tree to
preserve that information?
TabAtkins: I would prefer we don't.
<TabAtkins> calc(1px + (2px + 3px)) => CSSMath("+", [1px, 2px,
3px])
fantasai: I agree, because people put parenthesis when they want
to clarify something, not just when they have a
grouping, e.g. calc(1px + (2*2px))

xidorn: Do we want to distinguish between 3px and calc(3px)?
TabAtkins: Yes, and we'll probably default it to the plus operator.

plinss: Going back to unnecessary parens, I think we should
preserve that structure.
plinss: If author wants to reduce, can use toSum()
plinss: E.g. 2*3em, they did that for a reason.
TabAtkins: Don't have a problem with it, but would mean
operator(+,[operator(+, [2px, 3px])])
<TabAtkins> Which would mean calc(1px + (2px + 3px)) =>
CSSMath("+", [1px, CSSMath("+", [2px, 3px])])
shane: Any code dealing with this would have to recurse anyway.
TabAtkins: There's no non-broken code you could write that would
break on extra parens being preserved.
plinss: So let's preserve structure.
SimonSapin: So no distinction between nested calc() and nested
parens?
TabAtkins: We don't even when serializing, so no.

shane: What do you do when animating between calc(5px + (4em +
2%)) and calc(10px + 10em + 10%)
TabAtkins: Animations are computed value anyway, and so they're
necessarily reduced -- they're produced by the engine,
not by the author.
TabAtkins: The beginning point can be unit value, endpoint a
calc(). What's in between? Engine decides.

fantasai: Tab, since you were concerned about typing, could
consider CSSMathValue as a listlike type, and .op as the
operator member.
TabAtkins: No, it's fine.

SimonSapin: Earlier were talking about Proxy for arraylike stuff.
SimonSapin: if it's not used here, where is it used in CSSOM?
TabAtkins: TransformValue and UnparsedValue.
SimonSapin: Why do we want to do something different here?
TabAtkins: Semantically different -- this is an operator, and
arguments to that operator. Those are fundamentally
sequences.
iank: So this proposal is completely replacing the CSSCalcValue
stuff we discussed earlier?
TabAtkins: Yep, throw it away.
iank: So you wouldn't have any of those complex units like em*ch.
iank: Right.

TabAtkins: Still want to handle custom units whenever they exist.
TabAtkins: Also may want to handle case-insensitivity.
fantasai: Custom idents are case-sensitive.
TabAtkins: Note in the spec that we don't have custom units yet,
but plan to.

Rossen: Sounds like ppl are happy with this?
fantasai: Share Tab's earlier concern about operator and operand
being perhaps a bit too long to type, but otherwise
seems okay to me.

[SimonSapin asks how percents represented]
TabAtkins: As "percent". Didn't use "%" so that it wouldn't be
weird to use as a .name

plinss: Unitless zero?
TabAtkins: Custom stuff can't use unitless zero, only in CSS
syntax.
TabAtkins: Also calc() can't have unitless zero.
SimonSapin: I have some bugs to file then.
SimonSapin: Specifically, a "0" value is a <number-token>, which
gets typed by calc() to "integer", and you can't add
"integer" and "length".
TabAtkins: Servo/Stylo works the other way around: you try to
parse <length>, unitless 0 and calc() are both valid
inputs

RESOLVED: Switch to CSSMathValue-based expression tree with
toSum() method for returning dictionary of unit types (
as described above).

Issue 310: Consider using properties in addition to .get/.set
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

<shane> Topic: https://github.com/w3c/css-houdini-drafts/issues/310
shane: I want to either say No or wait until level 2 to consider
this.
Rossen: I'd be fine for that.

RESOLVED: Using properties (issue #310) is moved to L2

Topic 350: Move Houdini APIs to `window.CSS`
- - - - - - - - - - - - - - - - - - - - - -

<shane> Topic: https://github.com/w3c/css-houdini-drafts/issues/350
shane: Have window.getComputedStyleMap()
shane: It should either be CSS.getComputedStyleMap or
CSS.getComputedStyle?
shane: We called it getComputedStyleMap() earlier so it doesn't
collide with getComputedStyle, but now we have an
opportunity to use the other name.
iank: Does it make sense to move that particular one to CSS from
window?

dbaron: Why isn't this element.computedStyleMap?
dbaron: Putting getComputedStyle() on window was a mistake
<shane> 1. Window.getComputedStyleMap()
<shane> 2. CSS.getComputedStyleMap()
<shane> 3. CSS.getComputedStyle()
<shane> 4. Element.computedStyleMap

leaverou: Why not just element.computedStyle
leaverou: Which is what getComputedStyle should have been in the
first place?
dbaron: That's more or less what I said in the first place...
leaverou: I thought you were proposing a function.
dbaron: I think, IIRC, that other things that return style maps
end in StyleMap, and wouldn't want to have just one that
omits the word Map.
dbaron: But should be a getter.
leaverou: Especially if it has Map at the end.
shane: I quite like element.computedStyleMap.

iank: Is this live?
TabAtkins: Yes, it's in the minutes.
<TabAtkins> (Per the minutes, it's live and we're very sad about
it.)
SimonSapin: Style maps are live in general, but computedStyle is
readonly isn't it?
TabAtkins: Yes.
SimonSapin: So it doesn't matter if it's live.
dbaron: Yes it does, cuz then things are changed.
TabAtkins: Nice thing is that values are hidden behind .get call
so can compute them lazily.
iank: One thing that getComputedStyle has is second argument is
optional string for pseudo-element name.
dbaron: We had this discussion before 5 years ago
dbaron: We specced it all in CSSOM
dbaron: But then deleted it because nobody implemented it.
dbaron: But we did come up with API we liked for this
dbaron: We had a pseudo-element class.
birtles: I thought it was defined in two specs, in CSSOM and
css-pseudo.
fantasai: Stuff in css-pseudo is "here is some stuff as ideas,
please comment on it" not ready to impl..
birtles: CSSPseudoElement is used by Web Animations

shane: Let's do what dbaron says.
shane: If we want to have pseudo-element class and have computed
style of them hang off that class, then typed CSSOM might
not let you get pseudo element styles.
shane: Transiently while we put the rest of the mechanism in place.
shane: But I don't think that's an issue given that the typed OM
is incomplete in lots of other ways anyway.
shane: So I think we should do what dbaron says.
Rossen: I think we already established that :)
shane: I'm gonna keep saying it until we resolve on it.
Rossen: Was there some other option?

shane: Yeah, I listed them out:
<shane> 1. Window.getComputedStyleMap()
<shane> 2. CSS.getComputedStyleMap()
<shane> 3. CSS.getComputedStyle()
<shane> 4. Element.computedStyleMap ( + pseudoElement mechanisms)
Rossen: What's + pseudoElement mechanism?
shane: Have an object representing the pseudoElement with a
.computedStyleMap
dino: Why not just .styleMap?
?: Because that's style attr.
dino: But this is the API people actually want.
??: Could call it inlineStyleMap and computedStyleMap to be
explicit about it.
dino/smfr: People already get confused about .style being style
attr and not computed style.
TabAtkins: Don't really want to have .style = inlineStyleMap and
styleMap = getComputedStyle

<shane> 5. 4 + styleMap -> ??bikeshed??StyleMap
<SimonSapin> .styleAttrMap / .computedStyleMap ?
SimonSapin: Don't want to overload inline, already overloaded
<leaverou> 4 (though it depends on the pseudo-element mechanisms)
fantasai: I like .styleAttrMap / .computedStyleMap
Rossen: attrStyleMap
plinss: So that it's always StyleMap
iank: No other DOM api abbreviates attribute.
Rossen: Okay, we can bikeshed that later.

Rossen: So, option #N is adopt element.attrStyleMap and
element.computedStyleMap + pseudoElement mechanisms
iank: I don't want the attribute thing.
Rossen: We'll make it attributeStyleMap.

RESOLVED: element.attributeStyleMap and element.computedStyleMap,
attributeStyleMap to be renamed later, pseudo-element
styles accessed through PseudoElement APIs.

dino: Can someone summarize the pseudoElement stuff?
TabAtkins: element.pseudos with ..
dbaron: was a function pseudo(...) that returns element-like thing
leaverou: What about parameterized pseudo elements?
<dbaron> element.pseudo(":before").computedStyleMap
<TabAtkins> "::before", certainly
<fantasai> https://www.w3.org/TR/2013/WD-cssom-20131205/#extensions-to-the-element-interface
fantasai: There's a reason we publish periodically to /TR with
with dated snapshots :)
<dbaron> https://hg.csswg.org/drafts/rev/26e59827f85c removed it
<dbaron> It is present in
https://www.w3.org/TR/2013/WD-cssom-20131205/#extensions-to-the-element-interface
although there may have been later improvements before
the removal

Typed OM wrapup
---------------

Rossen: we need to publish a new draft pending the changes

RESOLVED: Update typed om draft on /TR with above edits

Loading...