forked from w3c/css-houdini-drafts
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathapply-hook-ideas.txt
268 lines (202 loc) · 9.53 KB
/
apply-hook-ideas.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
Introduction {#introduction}
============================
[[css-variables]] defines a new <<var()>> function that can be used to
insert the values of custom properties into other CSS property values. Where
possible, this mechanism should be preferred above the computed value modification
facilities of this specification.
Supported syntax strings {#supported-syntax-strings}
----------------------------------------------------
: "<'[property-name]'>", Where [property-name] is any existing CSS property
name or any string that matches the <<custom-property-name>> production.
:: Any value that parses as a value of the specified property.
The apply hook {#the-apply-hook}
================================
<pre class='idl'>
interface ElementProxy {
readonly attribute StylePropertyMapReadOnly inputStyle;
readonly attribute StylePropertyMap outputStyle;
readonly attribute DOMString? pseudo;
};
dictionary ApplyDescriptor {
sequence<DOMString> inputProperties;
sequence<DOMString> outputProperties;
};
callback VoidFunction = void ();
interface StyleWorklet : WorkletGlobalContext {
void registerApplyHook(DOMString name, VoidFunction applyCtor, ApplyDescriptor config);
void unregisterApplyHook(DOMString name);
};
</pre>
<div class='note'>
The applyCtor is a JavaScript class with the following interface:
<pre class='idl'>
callback interface ApplyClass {
void apply(ElementProxy element);
};
</pre>
</div>
The {{ElementProxy}} interface {#the-elementproxy-interface}
------------------------------------------------------------
{{ElementProxy}} objects represent the partial state of DOM objects that are
available to apply hooks running in <a>worklet global scope</a>s. They provide
the following attributes:
: inputStyle
:: The set of styles that the apply hook has registered a dependency on.
: outputStyle
:: The final result of running this apply hook.
: pseudo
:: The pseudo name of the PseudoElement that this ElementProxy proxies, or
null if this ElementProxy proxies an Element.
Issue(73): Do we need the pseudo attribute on ElementProxy for level 1?
Issue(74): Come up with a better name than ElementProxy. This is more of a computation context.
The {{ApplyDescriptor}} dictionary {#the-applydescriptor-dictionary}
--------------------------------------------------------------------
: <dfn dict-member for=ApplyDescriptor>inputProperties</dfn>
:: The apply function is only called for elements or
pseudoelements on which the listed properties all have non-initial values.
Issue(4): It should it be possible to access properties on the parent.
Issue(2): Should this be available only if explicitly requested in inputProperties?
: <dfn dict-member for=ApplyDescriptor>outputProperties</dfn>
:: This value defines the properties for which the apply function can modify the used
value.
Apply classes {#apply-class-objects}
------------------------------------
<dfn>Apply classes</dfn> provide apply hook behavior. Each <a>apply class</a>
must provide an apply function that will be invoked when apply hooks are
being processed.
The {{StyleWorklet}} interface {#the-styleworklet-interface}
------------------------------------------------------------
<dfn interface>StyleWorklet</dfn> objects provide the context within which apply hooks
are invoked. Each {{StyleWorklet}} contains a <dfn>name map of apply hooks</dfn>,
a <dfn>name map of inputs</dfn>, a <dfn>name map of outputs</dfn>,
and a <dfn>list of affected output properties</dfn>, all of which are
initially empty.
The {{registerApplyHook()}} function {#the-registerapplyhook-function}
----------------------------------------------------------------------
The <dfn method for=StyleWorklet>registerApplyHook(DOMString name, VoidFunction applyCtor, ApplyDescriptor config)</dfn>
function registers a new apply hook for processing computed style.
When {{registerApplyHook(name, applyCtor, config)}} is called, the user agent must run the following steps:
1. If |name| is not a valid <<ident>>, <a>throw</a> a {{NotSupportedError}} and abort
these steps.
1. If |name| is a key in the <a>name map of apply hooks</a>, <a>throw</a> a
{{NotSupportedError}} and abort these steps.
1. Let <var>outputProperties</var> be the value of |config|'s
{{ApplyDescriptor/outputProperties}}.
1. If the |outputProperties| contains a property
that is in the <a>list of affected output properties</a>, <a>throw</a> a
{{NotSupportedError}} and abort these steps.
Issue(49): This is too inflexible. There’s a strong use case around writing to the
same native property for different elements. Maybe throw exception to
window.onError in this case?
1. If the result of <a>IsConstructor</a>(argument=|applyCtor|) is false,
<a>throw</a> a {{NotSupportedError}} and abort these steps.
1. Let <var>prototype</var> be the result of <a>Get</a>(O=|applyCtor|, P="prototype").
1. If the result of <a>IsCallable</a>(argument=<a>Get</a>(O=|prototype|, P="apply"))
is false, <a>throw</a> a {{NotSupportedError}} and abort these steps.
1. Let <var>applyInstance</var> be the result of <a>Construct</a>(|applyCtor|).
1. Add the key-value pair (|name| - |applyInstance|) to the
<a>name map of apply hooks</a> of the {{StyleWorklet}}.
1. Add each property in |outputProperties| to
the <a>list of affected output properties</a> of the {{StyleWorklet}}.
1. Add the key-value pair (|name| - |outputProperties|) to the
<a>name map of outputs</a> of the {{StyleWorklet}}.
1. Let <var>inputProperties</var> be the value of |config|'s
{{ApplyDescriptor/inputProperties}}.
1. Add the key-value pair (|name| - |inputProperties|) to the
<a>name map of inputs</a> of the {{StyleWorklet}}.
Issue: This is one instance per apply hook. Do we want one instance per invocation?
Invoking apply hooks {#invoking-apply-hooks}
--------------------------------------------
Each time style is recomputed for an Element, each registered ApplyDescriptor/applyHook
for which any of the matching {{ApplyDescriptor/inputProperties}} changes as a result of
that recomputation is invoked. This invocation happens after any transitions or animations
registered on the Element have finished applying, in the context of a {{StyleWorklet}}.
Note: apply hooks are called after transitions/animations so that custom properties
can be transitioned and still have their effect apply correctly.
Implementations may memoize the result of apply callbacks relative to the
complete set of inputs provided to apply (that is, the set of attributes on
{{ElementProxy}}).
This invocation takes place by following these steps for each key <var>name</var>
in the <a>name map of apply hooks</a>:
1. Let <var>inputs</var> be the result of looking up <var>name</var> on the
{{StyleWorklet}}'s <a>name map of inputs</a>.
1. Let <var>inputStyleMap</var> be a new {{StylePropertyMapReadOnly}} populated
with only the <a>computed value</a>s for properties listed in |inputs|.
1. Let <var>proxy</var> be a new {{ElementProxy}}.
Issue: Need to fill out the ElementProxy.
1. <a>invoke a method on a class inside a Worklet</a> given "apply" as the
<var>methodPropertyKey</var> and [|proxy|] as the <var>arguments</var> with
the following options:
* To <a>create a worklet global scope</a> the user agent will return a new
{{StyleWorklet}}
* To <a>lookup a class instance on a worklet global scope</a> given a
<var>workletGlobalScope</var> the user agent will return the result of
looking up <var>name</var> on the <var>workletGlobalScope</var>'s
<a>name map of apply hooks</a>.
If an exception is thrown then abort these steps.
Issue: Need to deal with the output.
Note: Apply hooks run in parallel on a given Element. The output of an apply
hook is never used as input to another apply hook on the same Element - instead,
a snapshot of style state is taken, all apply hooks are run with subsets of the
same input snapshot, then the resulting output is written into the used style.
Examples {#examples}
====================
Example 1: Polyfill scale, translate, rotate {#example-1}
---------------------------------------------------------
<pre class='lang-markup'>
<script>
["--scale-x", "--scale-y"].forEach(function(name) {
document.registerProperty({
name: name,
syntax: "<number>",
inherits: false,
initialValue: "1"
});
});
["--translate-x", "--translate-y"].forEach(function(name) {
document.registerProperty({
name: name,
syntax: "<length>",
inherits: false,
initialValue: "0px"
});
});
document.registerProperty({
name: "--rotate",
syntax: "<angle>",
inherits: false,
initialValue: "0deg"
});
</script>
<style>
#myElement {
--translate-x: 5px;
--translate-y: 10px;
--rotate: 10deg;
--scale-x: 25;
--scale-y: 25;
}
.foobar {
--rotate: 20deg;
}
</style>
<script>
this.registerApplyHook("transform-properties", class {
apply(el) {
el.outputStyle.set('transform', new TransformValue(
[
new Translation(el.inputStyle.get('--translate-x'),
el.inputStyle.get('--translate-y')),
new Rotation(el.inputStyle.get('--rotate')),
new Scale(el.inputStyle.get('--scale-x'),
el.inputStyle.get('--scale-y'))
].concat(el.inputStyle.get('transform'))));
}}, {
inputProperties: ["--translate-x", "--translate-y",
"--scale-x", "--scale-y",
"--rotate", "transform"],
outputProperties: ["transform"]
}
});
</script>
</pre>