[Web Animations] DocumentTimeline::updateAnimations() is called endlessly
[WebKit-https.git] / Source / WebCore / animation / KeyframeEffectReadOnly.cpp
1 /*
2  * Copyright (C) 2017 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "KeyframeEffectReadOnly.h"
28
29 #include "Animation.h"
30 #include "AnimationEffectTimingReadOnly.h"
31 #include "CSSAnimation.h"
32 #include "CSSComputedStyleDeclaration.h"
33 #include "CSSKeyframeRule.h"
34 #include "CSSPropertyAnimation.h"
35 #include "CSSPropertyNames.h"
36 #include "CSSStyleDeclaration.h"
37 #include "CSSTimingFunctionValue.h"
38 #include "CSSTransition.h"
39 #include "Element.h"
40 #include "FontCascade.h"
41 #include "FrameView.h"
42 #include "GeometryUtilities.h"
43 #include "JSCompositeOperation.h"
44 #include "JSKeyframeEffectReadOnly.h"
45 #include "RenderBox.h"
46 #include "RenderBoxModelObject.h"
47 #include "RenderElement.h"
48 #include "RenderStyle.h"
49 #include "StylePendingResources.h"
50 #include "StyleResolver.h"
51 #include "TimingFunction.h"
52 #include "TranslateTransformOperation.h"
53 #include "WillChangeData.h"
54 #include <JavaScriptCore/Exception.h>
55 #include <wtf/UUID.h>
56
57 namespace WebCore {
58 using namespace JSC;
59
60 static inline void invalidateElement(Element* element)
61 {
62     if (element)
63         element->invalidateStyleAndLayerComposition();
64 }
65
66 static inline String CSSPropertyIDToIDLAttributeName(CSSPropertyID cssPropertyId)
67 {
68     // https://drafts.csswg.org/web-animations-1/#animation-property-name-to-idl-attribute-name
69     // 1. If property follows the <custom-property-name> production, return property.
70     // FIXME: We don't handle custom properties yet.
71
72     // 2. If property refers to the CSS float property, return the string "cssFloat".
73     if (cssPropertyId == CSSPropertyFloat)
74         return "cssFloat";
75
76     // 3. If property refers to the CSS offset property, return the string "cssOffset".
77     // FIXME: we don't support the CSS "offset" property
78
79     // 4. Otherwise, return the result of applying the CSS property to IDL attribute algorithm [CSSOM] to property.
80     return getJSPropertyName(cssPropertyId);
81 }
82
83 static inline CSSPropertyID IDLAttributeNameToAnimationPropertyName(const String& idlAttributeName)
84 {
85     // https://drafts.csswg.org/web-animations-1/#idl-attribute-name-to-animation-property-name
86     // 1. If attribute conforms to the <custom-property-name> production, return attribute.
87     // FIXME: We don't handle custom properties yet.
88
89     // 2. If attribute is the string "cssFloat", then return an animation property representing the CSS float property.
90     if (idlAttributeName == "cssFloat")
91         return CSSPropertyFloat;
92
93     // 3. If attribute is the string "cssOffset", then return an animation property representing the CSS offset property.
94     // FIXME: We don't support the CSS "offset" property.
95
96     // 4. Otherwise, return the result of applying the IDL attribute to CSS property algorithm [CSSOM] to attribute.
97     auto cssPropertyId = CSSStyleDeclaration::getCSSPropertyIDFromJavaScriptPropertyName(idlAttributeName);
98
99     // We need to check that converting the property back to IDL form yields the same result such that a property passed
100     // in non-IDL form is rejected, for instance "font-size".
101     if (idlAttributeName != CSSPropertyIDToIDLAttributeName(cssPropertyId))
102         return CSSPropertyInvalid;
103
104     return cssPropertyId;
105 }
106
107 static inline void computeMissingKeyframeOffsets(Vector<KeyframeEffectReadOnly::ParsedKeyframe>& keyframes)
108 {
109     // https://drafts.csswg.org/web-animations-1/#compute-missing-keyframe-offsets
110
111     if (keyframes.isEmpty())
112         return;
113
114     // 1. For each keyframe, in keyframes, let the computed keyframe offset of the keyframe be equal to its keyframe offset value.
115     // In our implementation, we only set non-null values to avoid making computedOffset std::optional<double>. Instead, we'll know
116     // that a keyframe hasn't had a computed offset by checking if it has a null offset and a 0 computedOffset, since the first
117     // keyframe will already have a 0 computedOffset.
118     for (auto& keyframe : keyframes)
119         keyframe.computedOffset = keyframe.offset.value_or(0);
120
121     // 2. If keyframes contains more than one keyframe and the computed keyframe offset of the first keyframe in keyframes is null,
122     //    set the computed keyframe offset of the first keyframe to 0.
123     if (keyframes.size() > 1 && !keyframes[0].offset)
124         keyframes[0].computedOffset = 0;
125
126     // 3. If the computed keyframe offset of the last keyframe in keyframes is null, set its computed keyframe offset to 1.
127     if (!keyframes.last().offset)
128         keyframes.last().computedOffset = 1;
129
130     // 4. For each pair of keyframes A and B where:
131     //    - A appears before B in keyframes, and
132     //    - A and B have a computed keyframe offset that is not null, and
133     //    - all keyframes between A and B have a null computed keyframe offset,
134     //    calculate the computed keyframe offset of each keyframe between A and B as follows:
135     //    1. Let offsetk be the computed keyframe offset of a keyframe k.
136     //    2. Let n be the number of keyframes between and including A and B minus 1.
137     //    3. Let index refer to the position of keyframe in the sequence of keyframes between A and B such that the first keyframe after A has an index of 1.
138     //    4. Set the computed keyframe offset of keyframe to offsetA + (offsetB − offsetA) × index / n.
139     size_t indexOfLastKeyframeWithNonNullOffset = 0;
140     for (size_t i = 1; i < keyframes.size(); ++i) {
141         auto& keyframe = keyframes[i];
142         // Keyframes with a null offset that don't yet have a non-zero computed offset are keyframes
143         // with an offset that needs to be computed.
144         if (!keyframe.offset && !keyframe.computedOffset)
145             continue;
146         if (indexOfLastKeyframeWithNonNullOffset != i - 1) {
147             double lastNonNullOffset = keyframes[indexOfLastKeyframeWithNonNullOffset].computedOffset;
148             double offsetDelta = keyframe.computedOffset - lastNonNullOffset;
149             double offsetIncrement = offsetDelta / (i - indexOfLastKeyframeWithNonNullOffset);
150             size_t indexOfFirstKeyframeWithNullOffset = indexOfLastKeyframeWithNonNullOffset + 1;
151             for (size_t j = indexOfFirstKeyframeWithNullOffset; j < i; ++j)
152                 keyframes[j].computedOffset = lastNonNullOffset + (j - indexOfLastKeyframeWithNonNullOffset) * offsetIncrement;
153         }
154         indexOfLastKeyframeWithNonNullOffset = i;
155     }
156 }
157
158 static inline ExceptionOr<KeyframeEffectReadOnly::KeyframeLikeObject> processKeyframeLikeObject(ExecState& state, Strong<JSObject>&& keyframesInput, bool allowLists)
159 {
160     // https://drafts.csswg.org/web-animations-1/#process-a-keyframe-like-object
161
162     VM& vm = state.vm();
163     auto scope = DECLARE_THROW_SCOPE(vm);
164
165     // 1. Run the procedure to convert an ECMAScript value to a dictionary type [WEBIDL] with keyframe input as the ECMAScript value as follows:
166     // 
167     //    If allow lists is true, use the following dictionary type:
168     //
169     //    dictionary BasePropertyIndexedKeyframe {
170     //        (double? or sequence<double?>)                         offset = [];
171     //        (DOMString or sequence<DOMString>)                     easing = [];
172     //        (CompositeOperation? or sequence<CompositeOperation?>) composite = [];
173     //    };
174     //
175     //    Otherwise, use the following dictionary type:
176     //
177     //    dictionary BaseKeyframe {
178     //        double?             offset = null;
179     //        DOMString           easing = "linear";
180     //        CompositeOperation? composite = null;
181     //    };
182     //
183     //    Store the result of this procedure as keyframe output.
184     KeyframeEffect::BasePropertyIndexedKeyframe baseProperties;
185     if (allowLists)
186         baseProperties = convert<IDLDictionary<KeyframeEffectReadOnly::BasePropertyIndexedKeyframe>>(state, keyframesInput.get());
187     else {
188         auto baseKeyframe = convert<IDLDictionary<KeyframeEffectReadOnly::BaseKeyframe>>(state, keyframesInput.get());
189         if (baseKeyframe.offset)
190             baseProperties.offset = baseKeyframe.offset.value();
191         else
192             baseProperties.offset = nullptr;
193         baseProperties.easing = baseKeyframe.easing;
194         if (baseKeyframe.composite)
195             baseProperties.composite = baseKeyframe.composite.value();
196         else
197             baseProperties.composite = nullptr;
198     }
199     RETURN_IF_EXCEPTION(scope, Exception { TypeError });
200
201     KeyframeEffectReadOnly::KeyframeLikeObject keyframeOuput;
202     keyframeOuput.baseProperties = baseProperties;
203
204     // 2. Build up a list of animatable properties as follows:
205     //
206     //    1. Let animatable properties be a list of property names (including shorthand properties that have longhand sub-properties
207     //       that are animatable) that can be animated by the implementation.
208     //    2. Convert each property name in animatable properties to the equivalent IDL attribute by applying the animation property
209     //       name to IDL attribute name algorithm.
210
211     // 3. Let input properties be the result of calling the EnumerableOwnNames operation with keyframe input as the object.
212     PropertyNameArray inputProperties(&vm, PropertyNameMode::Strings, PrivateSymbolMode::Exclude);
213     JSObject::getOwnPropertyNames(keyframesInput.get(), &state, inputProperties, EnumerationMode());
214
215     // 4. Make up a new list animation properties that consists of all of the properties that are in both input properties and animatable
216     //    properties, or which are in input properties and conform to the <custom-property-name> production.
217     Vector<JSC::Identifier> animationProperties;
218     size_t numberOfProperties = inputProperties.size();
219     for (size_t i = 0; i < numberOfProperties; ++i) {
220         if (CSSPropertyAnimation::isPropertyAnimatable(IDLAttributeNameToAnimationPropertyName(inputProperties[i].string())))
221             animationProperties.append(inputProperties[i]);
222     }
223
224     // 5. Sort animation properties in ascending order by the Unicode codepoints that define each property name.
225     std::sort(animationProperties.begin(), animationProperties.end(), [](auto& lhs, auto& rhs) {
226         return lhs.string().utf8() < rhs.string().utf8();
227     });
228
229     // 6. For each property name in animation properties,
230     size_t numberOfAnimationProperties = animationProperties.size();
231     for (size_t i = 0; i < numberOfAnimationProperties; ++i) {
232         // 1. Let raw value be the result of calling the [[Get]] internal method on keyframe input, with property name as the property
233         //    key and keyframe input as the receiver.
234         auto rawValue = keyframesInput->get(&state, animationProperties[i]);
235
236         // 2. Check the completion record of raw value.
237         RETURN_IF_EXCEPTION(scope, Exception { TypeError });
238
239         // 3. Convert raw value to a DOMString or sequence of DOMStrings property values as follows:
240         Vector<String> propertyValues;
241         if (allowLists) {
242             // If allow lists is true,
243             // Let property values be the result of converting raw value to IDL type (DOMString or sequence<DOMString>)
244             // using the procedures defined for converting an ECMAScript value to an IDL value [WEBIDL].
245             // If property values is a single DOMString, replace property values with a sequence of DOMStrings with the original value of property
246             // Values as the only element.
247             if (rawValue.isString())
248                 propertyValues = { rawValue.toWTFString(&state) };
249             else if (rawValue.isObject())
250                 propertyValues = convert<IDLSequence<IDLDOMString>>(state, rawValue);
251         } else {
252             // Otherwise,
253             // Let property values be the result of converting raw value to a DOMString using the procedure for converting an ECMAScript value to a DOMString.
254             propertyValues = { convert<IDLDOMString>(state, rawValue) };
255         }
256         RETURN_IF_EXCEPTION(scope, Exception { TypeError });
257
258         // 4. Calculate the normalized property name as the result of applying the IDL attribute name to animation property name algorithm to property name.
259         auto cssPropertyID = IDLAttributeNameToAnimationPropertyName(animationProperties[i].string());
260
261         // 5. Add a property to to keyframe output with normalized property name as the property name, and property values as the property value.
262         keyframeOuput.propertiesAndValues.append({ cssPropertyID, propertyValues });
263     }
264
265     // 7. Return keyframe output.
266     return { WTFMove(keyframeOuput) };
267 }
268
269 static inline ExceptionOr<void> processIterableKeyframes(ExecState& state, Strong<JSObject>&& keyframesInput, JSValue method, Vector<KeyframeEffectReadOnly::ParsedKeyframe>& parsedKeyframes)
270 {
271     // 1. Let iter be GetIterator(object, method).
272     forEachInIterable(state, keyframesInput.get(), method, [&parsedKeyframes](VM& vm, ExecState& state, JSValue nextValue) -> ExceptionOr<void> {
273         // Steps 2 through 6 are already implemented by forEachInIterable().
274         auto scope = DECLARE_THROW_SCOPE(vm);
275         if (!nextValue || !nextValue.isObject()) {
276             throwException(&state, scope, JSC::Exception::create(vm, createTypeError(&state)));
277             return { };
278         }
279
280         // 7. Append to processed keyframes the result of running the procedure to process a keyframe-like object passing nextItem
281         // as the keyframe input and with the allow lists flag set to false.
282         auto processKeyframeLikeObjectResult = processKeyframeLikeObject(state, Strong<JSObject>(vm, nextValue.toObject(&state)), false);
283         if (processKeyframeLikeObjectResult.hasException())
284             return processKeyframeLikeObjectResult.releaseException();
285         auto keyframeLikeObject = processKeyframeLikeObjectResult.returnValue();
286
287         KeyframeEffectReadOnly::ParsedKeyframe keyframeOutput;
288
289         // When calling processKeyframeLikeObject() with the "allow lists" flag set to false, the only offset
290         // alternatives we should expect are double and nullptr.
291         if (WTF::holds_alternative<double>(keyframeLikeObject.baseProperties.offset))
292             keyframeOutput.offset = WTF::get<double>(keyframeLikeObject.baseProperties.offset);
293         else
294             ASSERT(WTF::holds_alternative<std::nullptr_t>(keyframeLikeObject.baseProperties.offset));
295
296         // When calling processKeyframeLikeObject() with the "allow lists" flag set to false, the only easing
297         // alternative we should expect is String.
298         ASSERT(WTF::holds_alternative<String>(keyframeLikeObject.baseProperties.easing));
299         keyframeOutput.easing = WTF::get<String>(keyframeLikeObject.baseProperties.easing);
300
301         // When calling processKeyframeLikeObject() with the "allow lists" flag set to false, the only composite
302         // alternatives we should expect are CompositeOperation and nullptr.
303         if (WTF::holds_alternative<CompositeOperation>(keyframeLikeObject.baseProperties.composite))
304             keyframeOutput.composite = WTF::get<CompositeOperation>(keyframeLikeObject.baseProperties.composite);
305         else
306             ASSERT(WTF::holds_alternative<std::nullptr_t>(keyframeLikeObject.baseProperties.composite));
307
308         for (auto& propertyAndValue : keyframeLikeObject.propertiesAndValues) {
309             auto cssPropertyId = propertyAndValue.property;
310             // When calling processKeyframeLikeObject() with the "allow lists" flag set to false,
311             // there should only ever be a single value for a given property.
312             ASSERT(propertyAndValue.values.size() == 1);
313             auto stringValue = propertyAndValue.values[0];
314             if (keyframeOutput.style->setProperty(cssPropertyId, stringValue))
315                 keyframeOutput.unparsedStyle.set(cssPropertyId, stringValue);
316         }
317
318         parsedKeyframes.append(WTFMove(keyframeOutput));
319
320         return { };
321     });
322
323     return { };
324 }
325
326 static inline ExceptionOr<void> processPropertyIndexedKeyframes(ExecState& state, Strong<JSObject>&& keyframesInput, Vector<KeyframeEffectReadOnly::ParsedKeyframe>& parsedKeyframes, Vector<String>& unusedEasings)
327 {
328     // 1. Let property-indexed keyframe be the result of running the procedure to process a keyframe-like object passing object as the keyframe input.
329     auto processKeyframeLikeObjectResult = processKeyframeLikeObject(state, WTFMove(keyframesInput), true);
330     if (processKeyframeLikeObjectResult.hasException())
331         return processKeyframeLikeObjectResult.releaseException();
332     auto propertyIndexedKeyframe = processKeyframeLikeObjectResult.returnValue();
333
334     // 2. For each member, m, in property-indexed keyframe, perform the following steps:
335     for (auto& m : propertyIndexedKeyframe.propertiesAndValues) {
336         // 1. Let property name be the key for m.
337         auto propertyName = m.property;
338         // 2. If property name is “composite”, or “easing”, or “offset”, skip the remaining steps in this loop and continue from the next member in property-indexed
339         //    keyframe after m.
340         //    We skip this test since we split those properties and the actual CSS properties that we're currently iterating over.
341         // 3. Let property values be the value for m.
342         auto propertyValues = m.values;
343         // 4. Let property keyframes be an empty sequence of keyframes.
344         Vector<KeyframeEffectReadOnly::ParsedKeyframe> propertyKeyframes;
345         // 5. For each value, v, in property values perform the following steps:
346         for (auto& v : propertyValues) {
347             // 1. Let k be a new keyframe with a null keyframe offset.
348             KeyframeEffectReadOnly::ParsedKeyframe k;
349             // 2. Add the property-value pair, property name → v, to k.
350             if (k.style->setProperty(propertyName, v))
351                 k.unparsedStyle.set(propertyName, v);
352             // 3. Append k to property keyframes.
353             propertyKeyframes.append(WTFMove(k));
354         }
355         // 6. Apply the procedure to compute missing keyframe offsets to property keyframes.
356         computeMissingKeyframeOffsets(propertyKeyframes);
357
358         // 7. Add keyframes in property keyframes to processed keyframes.
359         for (auto& keyframe : propertyKeyframes)
360             parsedKeyframes.append(WTFMove(keyframe));
361     }
362
363     // 3. Sort processed keyframes by the computed keyframe offset of each keyframe in increasing order.
364     std::sort(parsedKeyframes.begin(), parsedKeyframes.end(), [](auto& lhs, auto& rhs) {
365         return lhs.computedOffset < rhs.computedOffset;
366     });
367
368     // 4. Merge adjacent keyframes in processed keyframes when they have equal computed keyframe offsets.
369     size_t i = 1;
370     while (i < parsedKeyframes.size()) {
371         auto& keyframe = parsedKeyframes[i];
372         auto& previousKeyframe = parsedKeyframes[i - 1];
373         // If the offsets of this keyframe and the previous keyframe are different,
374         // this means that the two keyframes should not be merged and we can move
375         // on to the next keyframe.
376         if (keyframe.computedOffset != previousKeyframe.computedOffset) {
377             i++;
378             continue;
379         }
380         // Otherwise, both this keyframe and the previous keyframe should be merged.
381         // Unprocessed keyframes in parsedKeyframes at this stage have at most a single
382         // property in cssPropertiesAndValues, so just set this on the previous keyframe.
383         // In case an invalid or null value was originally provided, then the property
384         // was not set and the property count is 0, in which case there is nothing to merge.
385         if (keyframe.style->propertyCount()) {
386             auto property = keyframe.style->propertyAt(0);
387             previousKeyframe.style->setProperty(property.id(), property.value());
388             previousKeyframe.unparsedStyle.set(property.id(), keyframe.unparsedStyle.get(property.id()));
389         }
390         // Since we've processed this keyframe, we can remove it and keep i the same
391         // so that we process the next keyframe in the next loop iteration.
392         parsedKeyframes.remove(i);
393     }
394
395     // 5. Let offsets be a sequence of nullable double values assigned based on the type of the “offset” member of the property-indexed keyframe as follows:
396     //    - sequence<double?>, the value of “offset” as-is.
397     //    - double?, a sequence of length one with the value of “offset” as its single item, i.e. « offset »,
398     Vector<std::optional<double>> offsets;
399     if (WTF::holds_alternative<Vector<std::optional<double>>>(propertyIndexedKeyframe.baseProperties.offset))
400         offsets = WTF::get<Vector<std::optional<double>>>(propertyIndexedKeyframe.baseProperties.offset);
401     else if (WTF::holds_alternative<double>(propertyIndexedKeyframe.baseProperties.offset))
402         offsets.append(WTF::get<double>(propertyIndexedKeyframe.baseProperties.offset));
403     else if (WTF::holds_alternative<std::nullptr_t>(propertyIndexedKeyframe.baseProperties.offset))
404         offsets.append(std::nullopt);
405
406     // 6. Assign each value in offsets to the keyframe offset of the keyframe with corresponding position in property keyframes until the end of either sequence is reached.
407     for (size_t i = 0; i < offsets.size() && i < parsedKeyframes.size(); ++i)
408         parsedKeyframes[i].offset = offsets[i];
409
410     // 7. Let easings be a sequence of DOMString values assigned based on the type of the “easing” member of the property-indexed keyframe as follows:
411     //    - sequence<DOMString>, the value of “easing” as-is.
412     //    - DOMString, a sequence of length one with the value of “easing” as its single item, i.e. « easing »,
413     Vector<String> easings;
414     if (WTF::holds_alternative<Vector<String>>(propertyIndexedKeyframe.baseProperties.easing))
415         easings = WTF::get<Vector<String>>(propertyIndexedKeyframe.baseProperties.easing);
416     else if (WTF::holds_alternative<String>(propertyIndexedKeyframe.baseProperties.easing))
417         easings.append(WTF::get<String>(propertyIndexedKeyframe.baseProperties.easing));
418
419     // 8. If easings is an empty sequence, let it be a sequence of length one containing the single value “linear”, i.e. « "linear" ».
420     if (easings.isEmpty())
421         easings.append("linear");
422
423     // 9. If easings has fewer items than property keyframes, repeat the elements in easings successively starting from the beginning of the list until easings has as many
424     //    items as property keyframes.
425     if (easings.size() < parsedKeyframes.size()) {
426         size_t initialNumberOfEasings = easings.size();
427         for (i = initialNumberOfEasings; i < parsedKeyframes.size(); ++i)
428             easings.append(easings[i % initialNumberOfEasings]);
429     }
430
431     // 10. If easings has more items than property keyframes, store the excess items as unused easings.
432     while (easings.size() > parsedKeyframes.size())
433         unusedEasings.append(easings.takeLast());
434
435     // 11. Assign each value in easings to a property named “easing” on the keyframe with the corresponding position in property keyframes until the end of property keyframes
436     //     is reached.
437     for (size_t i = 0; i < parsedKeyframes.size(); ++i)
438         parsedKeyframes[i].easing = easings[i];
439
440     // 12. If the “composite” member of the property-indexed keyframe is not an empty sequence:
441     Vector<std::optional<CompositeOperation>> compositeModes;
442     if (WTF::holds_alternative<Vector<std::optional<CompositeOperation>>>(propertyIndexedKeyframe.baseProperties.composite))
443         compositeModes = WTF::get<Vector<std::optional<CompositeOperation>>>(propertyIndexedKeyframe.baseProperties.composite);
444     else if (WTF::holds_alternative<CompositeOperation>(propertyIndexedKeyframe.baseProperties.composite))
445         compositeModes.append(WTF::get<CompositeOperation>(propertyIndexedKeyframe.baseProperties.composite));
446     else if (WTF::holds_alternative<std::nullptr_t>(propertyIndexedKeyframe.baseProperties.composite))
447         compositeModes.append(std::nullopt);
448     if (!compositeModes.isEmpty()) {
449         // 1. Let composite modes be a sequence of composite operations assigned from the “composite” member of property-indexed keyframe. If that member is a single composite
450         //    operation, let composite modes be a sequence of length one, with the value of the “composite” as its single item.
451         // 2. As with easings, if composite modes has fewer items than property keyframes, repeat the elements in composite modes successively starting from the beginning of
452         //    the list until composite modes has as many items as property keyframes.
453         if (compositeModes.size() < parsedKeyframes.size()) {
454             size_t initialNumberOfCompositeModes = compositeModes.size();
455             for (i = initialNumberOfCompositeModes; i < parsedKeyframes.size(); ++i)
456                 compositeModes.append(compositeModes[i % initialNumberOfCompositeModes]);
457         }
458         // 3. Assign each value in composite modes to the keyframe-specific composite operation on the keyframe with the corresponding position in property keyframes until
459         //    the end of property keyframes is reached.
460         for (size_t i = 0; i < compositeModes.size() && i < parsedKeyframes.size(); ++i)
461             parsedKeyframes[i].composite = compositeModes[i];
462     }
463
464     return { };
465 }
466
467 ExceptionOr<Ref<KeyframeEffectReadOnly>> KeyframeEffectReadOnly::create(ExecState& state, Element* target, Strong<JSObject>&& keyframes, std::optional<Variant<double, KeyframeEffectOptions>>&& options)
468 {
469     auto keyframeEffect = adoptRef(*new KeyframeEffectReadOnly(KeyframeEffectReadOnlyClass, AnimationEffectTimingReadOnly::create(), target));
470
471     auto setPropertiesResult = keyframeEffect->timing()->setProperties(WTFMove(options));
472     if (setPropertiesResult.hasException())
473         return setPropertiesResult.releaseException();
474
475     auto processKeyframesResult = keyframeEffect->processKeyframes(state, WTFMove(keyframes));
476     if (processKeyframesResult.hasException())
477         return processKeyframesResult.releaseException();
478
479     return WTFMove(keyframeEffect);
480 }
481
482 ExceptionOr<Ref<KeyframeEffectReadOnly>> KeyframeEffectReadOnly::create(JSC::ExecState&, Ref<KeyframeEffectReadOnly>&& source)
483 {
484     auto keyframeEffect = adoptRef(*new KeyframeEffectReadOnly(KeyframeEffectReadOnlyClass, AnimationEffectTimingReadOnly::create(), nullptr));
485     keyframeEffect->copyPropertiesFromSource(WTFMove(source));
486     return WTFMove(keyframeEffect);
487 }
488
489 Ref<KeyframeEffectReadOnly> KeyframeEffectReadOnly::create(const Element& target)
490 {
491     return adoptRef(*new KeyframeEffectReadOnly(KeyframeEffectReadOnlyClass, AnimationEffectTimingReadOnly::create(), const_cast<Element*>(&target)));
492 }
493
494 KeyframeEffectReadOnly::KeyframeEffectReadOnly(ClassType classType, Ref<AnimationEffectTimingReadOnly>&& timing, Element* target)
495     : AnimationEffectReadOnly(classType, WTFMove(timing))
496     , m_target(target)
497     , m_blendingKeyframes(emptyString())
498 {
499 }
500
501 void KeyframeEffectReadOnly::copyPropertiesFromSource(Ref<KeyframeEffectReadOnly>&& source)
502 {
503     m_target = source->m_target;
504     m_compositeOperation = source->m_compositeOperation;
505     m_iterationCompositeOperation = source->m_iterationCompositeOperation;
506
507     Vector<ParsedKeyframe> parsedKeyframes;
508     for (auto& sourceParsedKeyframe : source->m_parsedKeyframes) {
509         ParsedKeyframe parsedKeyframe;
510         parsedKeyframe.easing = sourceParsedKeyframe.easing;
511         parsedKeyframe.offset = sourceParsedKeyframe.offset;
512         parsedKeyframe.composite = sourceParsedKeyframe.composite;
513         parsedKeyframe.unparsedStyle = sourceParsedKeyframe.unparsedStyle;
514         parsedKeyframe.computedOffset = sourceParsedKeyframe.computedOffset;
515         parsedKeyframe.timingFunction = sourceParsedKeyframe.timingFunction;
516         parsedKeyframe.style = sourceParsedKeyframe.style->mutableCopy();
517         parsedKeyframes.append(WTFMove(parsedKeyframe));
518     }
519     m_parsedKeyframes = WTFMove(parsedKeyframes);
520
521     timing()->copyPropertiesFromSource(source->timing());
522
523     KeyframeList keyframeList("keyframe-effect-" + createCanonicalUUIDString());
524     for (auto& keyframe : source->m_blendingKeyframes.keyframes()) {
525         KeyframeValue keyframeValue(keyframe.key(), RenderStyle::clonePtr(*keyframe.style()));
526         for (auto propertyId : keyframe.properties())
527             keyframeValue.addProperty(propertyId);
528         keyframeList.insert(WTFMove(keyframeValue));
529     }
530     setBlendingKeyframes(keyframeList);
531 }
532
533 Vector<Strong<JSObject>> KeyframeEffectReadOnly::getKeyframes(ExecState& state)
534 {
535     // https://drafts.csswg.org/web-animations-1/#dom-keyframeeffectreadonly-getkeyframes
536
537     auto lock = JSLockHolder { &state };
538
539     // Since keyframes are represented by a partially open-ended dictionary type that is not currently able to be expressed with WebIDL,
540     // the procedure used to prepare the result of this method is defined in prose below:
541     //
542     // 1. Let result be an empty sequence of objects.
543     Vector<Strong<JSObject>> result;
544
545     // 2. Let keyframes be the result of applying the procedure to compute missing keyframe offsets to the keyframes for this keyframe effect.
546
547     // 3. For each keyframe in keyframes perform the following steps:
548     if (is<DeclarativeAnimation>(animation())) {
549         auto computedStyleExtractor = ComputedStyleExtractor(m_target.get());
550         for (size_t i = 0; i < m_blendingKeyframes.size(); ++i) {
551             // 1. Initialize a dictionary object, output keyframe, using the following definition:
552             //
553             // dictionary BaseComputedKeyframe {
554             //      double?             offset = null;
555             //      double              computedOffset;
556             //      DOMString           easing = "linear";
557             //      CompositeOperation? composite = null;
558             // };
559
560             auto& keyframe = m_blendingKeyframes[i];
561
562             // 2. Set offset, computedOffset, easing members of output keyframe to the respective values keyframe offset, computed keyframe offset,
563             // and keyframe-specific timing function of keyframe.
564             BaseComputedKeyframe computedKeyframe;
565             computedKeyframe.offset = keyframe.key();
566             computedKeyframe.computedOffset = keyframe.key();
567             // For CSS transitions, there are only two keyframes and the second keyframe should always report "linear". In practice, this value
568             // has no bearing since, as the last keyframe, its value will never be used.
569             computedKeyframe.easing = is<CSSTransition>(animation()) && i == 1 ? "linear" : timingFunctionForKeyframeAtIndex(0)->cssText();
570
571             auto outputKeyframe = convertDictionaryToJS(state, *jsCast<JSDOMGlobalObject*>(state.lexicalGlobalObject()), computedKeyframe);
572
573             // 3. For each animation property-value pair specified on keyframe, declaration, perform the following steps:
574             auto& style = *keyframe.style();
575             for (auto cssPropertyId : keyframe.properties()) {
576                 if (cssPropertyId == CSSPropertyCustom)
577                     continue;
578                 // 1. Let property name be the result of applying the animation property name to IDL attribute name algorithm to the property name of declaration.
579                 auto propertyName = CSSPropertyIDToIDLAttributeName(cssPropertyId);
580                 // 2. Let IDL value be the result of serializing the property value of declaration by passing declaration to the algorithm to serialize a CSS value.
581                 String idlValue = "";
582                 if (auto cssValue = computedStyleExtractor.valueForPropertyinStyle(style, cssPropertyId))
583                     idlValue = cssValue->cssText();
584                 // 3. Let value be the result of converting IDL value to an ECMAScript String value.
585                 auto value = toJS<IDLDOMString>(state, idlValue);
586                 // 4. Call the [[DefineOwnProperty]] internal method on output keyframe with property name property name,
587                 //    Property Descriptor { [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true, [[Value]]: value } and Boolean flag false.
588                 JSObject::defineOwnProperty(outputKeyframe, &state, AtomicString(propertyName).impl(), PropertyDescriptor(value, 0), false);
589             }
590
591             // 5. Append output keyframe to result.
592             result.append(JSC::Strong<JSC::JSObject> { state.vm(), outputKeyframe });
593         }
594     } else {
595         for (size_t i = 0; i < m_parsedKeyframes.size(); ++i) {
596             // 1. Initialize a dictionary object, output keyframe, using the following definition:
597             //
598             // dictionary BaseComputedKeyframe {
599             //      double?             offset = null;
600             //      double              computedOffset;
601             //      DOMString           easing = "linear";
602             //      CompositeOperation? composite = null;
603             // };
604
605             auto& parsedKeyframe = m_parsedKeyframes[i];
606
607             // 2. Set offset, computedOffset, easing, composite members of output keyframe to the respective values keyframe offset, computed keyframe
608             // offset, keyframe-specific timing function and keyframe-specific composite operation of keyframe.
609             BaseComputedKeyframe computedKeyframe;
610             computedKeyframe.offset = parsedKeyframe.offset;
611             computedKeyframe.computedOffset = parsedKeyframe.computedOffset;
612             computedKeyframe.easing = timingFunctionForKeyframeAtIndex(i)->cssText();
613             computedKeyframe.composite = parsedKeyframe.composite;
614
615             auto outputKeyframe = convertDictionaryToJS(state, *jsCast<JSDOMGlobalObject*>(state.lexicalGlobalObject()), computedKeyframe);
616
617             // 3. For each animation property-value pair specified on keyframe, declaration, perform the following steps:
618             for (auto it = parsedKeyframe.unparsedStyle.begin(), end = parsedKeyframe.unparsedStyle.end(); it != end; ++it) {
619                 // 1. Let property name be the result of applying the animation property name to IDL attribute name algorithm to the property name of declaration.
620                 auto propertyName = CSSPropertyIDToIDLAttributeName(it->key);
621                 // 2. Let IDL value be the result of serializing the property value of declaration by passing declaration to the algorithm to serialize a CSS value.
622                 // 3. Let value be the result of converting IDL value to an ECMAScript String value.
623                 auto value = toJS<IDLDOMString>(state, it->value);
624                 // 4. Call the [[DefineOwnProperty]] internal method on output keyframe with property name property name,
625                 //    Property Descriptor { [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true, [[Value]]: value } and Boolean flag false.
626                 JSObject::defineOwnProperty(outputKeyframe, &state, AtomicString(propertyName).impl(), PropertyDescriptor(value, 0), false);
627             }
628
629             // 4. Append output keyframe to result.
630             result.append(JSC::Strong<JSC::JSObject> { state.vm(), outputKeyframe });
631         }
632     }
633
634     // 4. Return result.
635     return result;
636 }
637
638 ExceptionOr<void> KeyframeEffectReadOnly::processKeyframes(ExecState& state, Strong<JSObject>&& keyframesInput)
639 {
640     // 1. If object is null, return an empty sequence of keyframes.
641     if (!keyframesInput.get())
642         return { };
643
644     VM& vm = state.vm();
645     auto scope = DECLARE_THROW_SCOPE(vm);
646
647     // 2. Let processed keyframes be an empty sequence of keyframes.
648     Vector<ParsedKeyframe> parsedKeyframes;
649
650     // 3. Let method be the result of GetMethod(object, @@iterator).
651     auto method = keyframesInput.get()->get(&state, vm.propertyNames->iteratorSymbol);
652
653     // 4. Check the completion record of method.
654     RETURN_IF_EXCEPTION(scope, Exception { TypeError });
655
656     // 5. Perform the steps corresponding to the first matching condition from below,
657     Vector<String> unusedEasings;
658     if (!method.isUndefined())
659         processIterableKeyframes(state, WTFMove(keyframesInput), WTFMove(method), parsedKeyframes);
660     else
661         processPropertyIndexedKeyframes(state, WTFMove(keyframesInput), parsedKeyframes, unusedEasings);
662
663     // 6. If processed keyframes is not loosely sorted by offset, throw a TypeError and abort these steps.
664     // 7. If there exist any keyframe in processed keyframes whose keyframe offset is non-null and less than
665     //    zero or greater than one, throw a TypeError and abort these steps.
666     double lastNonNullOffset = -1;
667     for (auto& keyframe : parsedKeyframes) {
668         if (!keyframe.offset)
669             continue;
670         auto offset = keyframe.offset.value();
671         if (offset < lastNonNullOffset || offset < 0 || offset > 1)
672             return Exception { TypeError };
673         lastNonNullOffset = offset;
674     }
675
676     // We take a slight detour from the spec text and compute the missing keyframe offsets right away
677     // since they can be computed up-front.
678     computeMissingKeyframeOffsets(parsedKeyframes);
679
680     // 8. For each frame in processed keyframes, perform the following steps:
681     for (auto& keyframe : parsedKeyframes) {
682         // Let the timing function of frame be the result of parsing the “easing” property on frame using the CSS syntax
683         // defined for the easing property of the AnimationEffectTimingReadOnly interface.
684         // If parsing the “easing” property fails, throw a TypeError and abort this procedure.
685         auto timingFunctionResult = TimingFunction::createFromCSSText(keyframe.easing);
686         if (timingFunctionResult.hasException())
687             return timingFunctionResult.releaseException();
688         keyframe.timingFunction = timingFunctionResult.returnValue();
689     }
690
691     // 9. Parse each of the values in unused easings using the CSS syntax defined for easing property of the
692     //    AnimationEffectTimingReadOnly interface, and if any of the values fail to parse, throw a TypeError
693     //    and abort this procedure.
694     for (auto& easing : unusedEasings) {
695         auto timingFunctionResult = TimingFunction::createFromCSSText(easing);
696         if (timingFunctionResult.hasException())
697             return timingFunctionResult.releaseException();
698     }
699
700     m_parsedKeyframes = WTFMove(parsedKeyframes);
701
702     m_blendingKeyframes.clear();
703
704     return { };
705 }
706
707 void KeyframeEffectReadOnly::updateBlendingKeyframes(RenderStyle& elementStyle)
708 {
709     if (!m_blendingKeyframes.isEmpty() || !m_target)
710         return;
711
712     KeyframeList keyframeList("keyframe-effect-" + createCanonicalUUIDString());
713     StyleResolver& styleResolver = m_target->styleResolver();
714
715     for (auto& keyframe : m_parsedKeyframes) {
716         styleResolver.setNewStateWithElement(*m_target);
717         KeyframeValue keyframeValue(keyframe.computedOffset, nullptr);
718
719         auto styleProperties = keyframe.style->immutableCopyIfNeeded();
720         for (unsigned i = 0; i < styleProperties->propertyCount(); ++i)
721             keyframeList.addProperty(styleProperties->propertyAt(i).id());
722
723         auto keyframeRule = StyleRuleKeyframe::create(WTFMove(styleProperties));
724         keyframeValue.setStyle(styleResolver.styleForKeyframe(&elementStyle, keyframeRule.ptr(), keyframeValue));
725         keyframeList.insert(WTFMove(keyframeValue));
726     }
727
728     setBlendingKeyframes(keyframeList);
729 }
730
731 bool KeyframeEffectReadOnly::forceLayoutIfNeeded()
732 {
733     if (!m_needsForcedLayout || !m_target)
734         return false;
735
736     auto* renderer = m_target->renderer();
737     if (!renderer || !renderer->parent())
738         return false;
739
740     auto* frameView = m_target->document().view();
741     if (!frameView)
742         return false;
743
744     frameView->forceLayout();
745     return true;
746 }
747
748 void KeyframeEffectReadOnly::setBlendingKeyframes(KeyframeList& blendingKeyframes)
749 {
750     m_blendingKeyframes = WTFMove(blendingKeyframes);
751
752     computedNeedsForcedLayout();
753     computeStackingContextImpact();
754     computeShouldRunAccelerated();
755
756     checkForMatchingTransformFunctionLists();
757     checkForMatchingFilterFunctionLists();
758 #if ENABLE(FILTERS_LEVEL_2)
759     checkForMatchingBackdropFilterFunctionLists();
760 #endif
761     checkForMatchingColorFilterFunctionLists();
762 }
763
764 void KeyframeEffectReadOnly::checkForMatchingTransformFunctionLists()
765 {
766     m_transformFunctionListsMatch = false;
767
768     if (m_blendingKeyframes.size() < 2 || !m_blendingKeyframes.containsProperty(CSSPropertyTransform))
769         return;
770
771     // Empty transforms match anything, so find the first non-empty entry as the reference.
772     size_t numKeyframes = m_blendingKeyframes.size();
773     size_t firstNonEmptyTransformKeyframeIndex = numKeyframes;
774
775     for (size_t i = 0; i < numKeyframes; ++i) {
776         const KeyframeValue& currentKeyframe = m_blendingKeyframes[i];
777         if (currentKeyframe.style()->transform().operations().size()) {
778             firstNonEmptyTransformKeyframeIndex = i;
779             break;
780         }
781     }
782
783     if (firstNonEmptyTransformKeyframeIndex == numKeyframes)
784         return;
785
786     const TransformOperations* firstVal = &m_blendingKeyframes[firstNonEmptyTransformKeyframeIndex].style()->transform();
787     for (size_t i = firstNonEmptyTransformKeyframeIndex + 1; i < numKeyframes; ++i) {
788         const KeyframeValue& currentKeyframe = m_blendingKeyframes[i];
789         const TransformOperations* val = &currentKeyframe.style()->transform();
790
791         // An empty transform list matches anything.
792         if (val->operations().isEmpty())
793             continue;
794
795         if (!firstVal->operationsMatch(*val))
796             return;
797     }
798
799     m_transformFunctionListsMatch = true;
800 }
801
802 bool KeyframeEffectReadOnly::checkForMatchingFilterFunctionLists(CSSPropertyID propertyID, const std::function<const FilterOperations& (const RenderStyle&)>& filtersGetter) const
803 {
804     if (m_blendingKeyframes.size() < 2 || !m_blendingKeyframes.containsProperty(propertyID))
805         return false;
806
807     // Empty filters match anything, so find the first non-empty entry as the reference.
808     size_t numKeyframes = m_blendingKeyframes.size();
809     size_t firstNonEmptyKeyframeIndex = numKeyframes;
810
811     for (size_t i = 0; i < numKeyframes; ++i) {
812         if (filtersGetter(*m_blendingKeyframes[i].style()).operations().size()) {
813             firstNonEmptyKeyframeIndex = i;
814             break;
815         }
816     }
817
818     if (firstNonEmptyKeyframeIndex == numKeyframes)
819         return false;
820
821     auto& firstVal = filtersGetter(*m_blendingKeyframes[firstNonEmptyKeyframeIndex].style());
822     for (size_t i = firstNonEmptyKeyframeIndex + 1; i < numKeyframes; ++i) {
823         auto& value = filtersGetter(*m_blendingKeyframes[i].style());
824
825         // An empty filter list matches anything.
826         if (value.operations().isEmpty())
827             continue;
828
829         if (!firstVal.operationsMatch(value))
830             return false;
831     }
832
833     return true;
834 }
835
836 void KeyframeEffectReadOnly::checkForMatchingFilterFunctionLists()
837 {
838     m_filterFunctionListsMatch = checkForMatchingFilterFunctionLists(CSSPropertyFilter, [] (const RenderStyle& style) -> const FilterOperations& {
839         return style.filter();
840     });
841 }
842
843 #if ENABLE(FILTERS_LEVEL_2)
844 void KeyframeEffectReadOnly::checkForMatchingBackdropFilterFunctionLists()
845 {
846     m_backdropFilterFunctionListsMatch = checkForMatchingFilterFunctionLists(CSSPropertyWebkitBackdropFilter, [] (const RenderStyle& style) -> const FilterOperations& {
847         return style.backdropFilter();
848     });
849 }
850 #endif
851
852 void KeyframeEffectReadOnly::checkForMatchingColorFilterFunctionLists()
853 {
854     m_colorFilterFunctionListsMatch = checkForMatchingFilterFunctionLists(CSSPropertyAppleColorFilter, [] (const RenderStyle& style) -> const FilterOperations& {
855         return style.appleColorFilter();
856     });
857 }
858
859 void KeyframeEffectReadOnly::computeDeclarativeAnimationBlendingKeyframes(const RenderStyle* oldStyle, const RenderStyle& newStyle)
860 {
861     ASSERT(is<DeclarativeAnimation>(animation()));
862     if (is<CSSAnimation>(animation()))
863         computeCSSAnimationBlendingKeyframes();
864     else if (is<CSSTransition>(animation()))
865         computeCSSTransitionBlendingKeyframes(oldStyle, newStyle);
866 }
867
868 void KeyframeEffectReadOnly::computeCSSAnimationBlendingKeyframes()
869 {
870     ASSERT(is<CSSAnimation>(animation()));
871
872     auto cssAnimation = downcast<CSSAnimation>(animation());
873     auto& backingAnimation = cssAnimation->backingAnimation();
874
875     KeyframeList keyframeList(backingAnimation.name());
876     if (auto* styleScope = Style::Scope::forOrdinal(*m_target, backingAnimation.nameStyleScopeOrdinal()))
877         styleScope->resolver().keyframeStylesForAnimation(*m_target, &cssAnimation->unanimatedStyle(), keyframeList);
878
879     // Ensure resource loads for all the frames.
880     for (auto& keyframe : keyframeList.keyframes()) {
881         if (auto* style = const_cast<RenderStyle*>(keyframe.style()))
882             Style::loadPendingResources(*style, m_target->document(), m_target.get());
883     }
884
885     setBlendingKeyframes(keyframeList);
886 }
887
888 void KeyframeEffectReadOnly::computeCSSTransitionBlendingKeyframes(const RenderStyle* oldStyle, const RenderStyle& newStyle)
889 {
890     ASSERT(is<CSSTransition>(animation()));
891
892     if (!oldStyle || m_blendingKeyframes.size())
893         return;
894
895     auto property = downcast<CSSTransition>(animation())->property();
896
897     auto toStyle = RenderStyle::clonePtr(newStyle);
898     if (m_target)
899         Style::loadPendingResources(*toStyle, m_target->document(), m_target.get());
900
901     KeyframeList keyframeList("keyframe-effect-" + createCanonicalUUIDString());
902     keyframeList.addProperty(property);
903
904     KeyframeValue fromKeyframeValue(0, RenderStyle::clonePtr(*oldStyle));
905     fromKeyframeValue.addProperty(property);
906     keyframeList.insert(WTFMove(fromKeyframeValue));
907
908     KeyframeValue toKeyframeValue(1, WTFMove(toStyle));
909     toKeyframeValue.addProperty(property);
910     keyframeList.insert(WTFMove(toKeyframeValue));
911
912     setBlendingKeyframes(keyframeList);
913 }
914
915 void KeyframeEffectReadOnly::computedNeedsForcedLayout()
916 {
917     m_needsForcedLayout = false;
918     if (is<CSSTransition>(animation()) || !m_blendingKeyframes.containsProperty(CSSPropertyTransform))
919         return;
920
921     size_t numberOfKeyframes = m_blendingKeyframes.size();
922     for (size_t i = 0; i < numberOfKeyframes; i++) {
923         auto* keyframeStyle = m_blendingKeyframes[i].style();
924         if (!keyframeStyle) {
925             ASSERT_NOT_REACHED();
926             continue;
927         }
928         if (keyframeStyle->hasTransform()) {
929             auto& transformOperations = keyframeStyle->transform();
930             for (auto operation : transformOperations.operations()) {
931                 if (operation->isTranslateTransformOperationType()) {
932                     auto translation = downcast<TranslateTransformOperation>(operation.get());
933                     if (translation->x().isPercent() || translation->y().isPercent()) {
934                         m_needsForcedLayout = true;
935                         return;
936                     }
937                 }
938             }
939         }
940     }
941 }
942
943 void KeyframeEffectReadOnly::computeStackingContextImpact()
944 {
945     m_triggersStackingContext = false;
946     for (auto cssPropertyId : m_blendingKeyframes.properties()) {
947         if (WillChangeData::propertyCreatesStackingContext(cssPropertyId)) {
948             m_triggersStackingContext = true;
949             break;
950         }
951     }
952 }
953
954 void KeyframeEffectReadOnly::setTarget(RefPtr<Element>&& newTarget)
955 {
956     if (m_target == newTarget)
957         return;
958
959     auto previousTarget = std::exchange(m_target, WTFMove(newTarget));
960
961     if (auto* effectAnimation = animation())
962         effectAnimation->effectTargetDidChange(previousTarget.get(), m_target.get());
963
964     m_blendingKeyframes.clear();
965
966     // We need to invalidate the effect now that the target has changed
967     // to ensure the effect's styles are applied to the new target right away.
968     invalidate();
969
970     // Likewise, we need to invalidate styles on the previous target so that
971     // any animated styles are removed immediately.
972     invalidateElement(previousTarget.get());
973 }
974
975 void KeyframeEffectReadOnly::apply(RenderStyle& targetStyle)
976 {
977     if (!m_target)
978         return;
979
980     updateBlendingKeyframes(targetStyle);
981
982     updateAcceleratedAnimationState();
983
984     auto progress = iterationProgress();
985     if (!progress)
986         return;
987
988     setAnimatedPropertiesInStyle(targetStyle, progress.value());
989
990     // https://w3c.github.io/web-animations/#side-effects-section
991     // For every property targeted by at least one animation effect that is current or in effect, the user agent
992     // must act as if the will-change property ([css-will-change-1]) on the target element includes the property.
993     if (m_triggersStackingContext && targetStyle.hasAutoZIndex())
994         targetStyle.setZIndex(0);
995 }
996
997 void KeyframeEffectReadOnly::invalidate()
998 {
999     invalidateElement(m_target.get());
1000 }
1001
1002 void KeyframeEffectReadOnly::computeShouldRunAccelerated()
1003 {
1004     m_shouldRunAccelerated = hasBlendingKeyframes();
1005     for (auto cssPropertyId : m_blendingKeyframes.properties()) {
1006         if (!CSSPropertyAnimation::animationOfPropertyIsAccelerated(cssPropertyId)) {
1007             m_shouldRunAccelerated = false;
1008             return;
1009         }
1010     }
1011 }
1012
1013 void KeyframeEffectReadOnly::getAnimatedStyle(std::unique_ptr<RenderStyle>& animatedStyle)
1014 {
1015     if (!m_target || !animation())
1016         return;
1017
1018     auto progress = iterationProgress();
1019     if (!progress)
1020         return;
1021
1022     if (!animatedStyle)
1023         animatedStyle = RenderStyle::clonePtr(renderer()->style());
1024
1025     setAnimatedPropertiesInStyle(*animatedStyle.get(), progress.value());
1026 }
1027
1028 void KeyframeEffectReadOnly::setAnimatedPropertiesInStyle(RenderStyle& targetStyle, double iterationProgress)
1029 {
1030     // 4.4.3. The effect value of a keyframe effect
1031     // https://drafts.csswg.org/web-animations-1/#the-effect-value-of-a-keyframe-animation-effect
1032     //
1033     // The effect value of a single property referenced by a keyframe effect as one of its target properties,
1034     // for a given iteration progress, current iteration and underlying value is calculated as follows.
1035
1036     updateBlendingKeyframes(targetStyle);
1037     if (m_blendingKeyframes.isEmpty())
1038         return;
1039
1040     bool isCSSAnimation = is<CSSAnimation>(animation());
1041
1042     for (auto cssPropertyId : m_blendingKeyframes.properties()) {
1043         // 1. If iteration progress is unresolved abort this procedure.
1044         // 2. Let target property be the longhand property for which the effect value is to be calculated.
1045         // 3. If animation type of the target property is not animatable abort this procedure since the effect cannot be applied.
1046         // 4. Define the neutral value for composition as a value which, when combined with an underlying value using the add composite operation,
1047         //    produces the underlying value.
1048
1049         // 5. Let property-specific keyframes be the result of getting the set of computed keyframes for this keyframe effect.
1050         // 6. Remove any keyframes from property-specific keyframes that do not have a property value for target property.
1051         unsigned numberOfKeyframesWithZeroOffset = 0;
1052         unsigned numberOfKeyframesWithOneOffset = 0;
1053         Vector<std::optional<size_t>> propertySpecificKeyframes;
1054         for (size_t i = 0; i < m_blendingKeyframes.size(); ++i) {
1055             auto& keyframe = m_blendingKeyframes[i];
1056             auto offset = keyframe.key();
1057             if (!keyframe.containsProperty(cssPropertyId)) {
1058                 // If we're dealing with a CSS animation, we consider the first and last keyframes to always have the property listed
1059                 // since the underlying style was provided and should be captured.
1060                 if (!isCSSAnimation || (offset && offset < 1))
1061                     continue;
1062             }
1063             if (!offset)
1064                 numberOfKeyframesWithZeroOffset++;
1065             if (offset == 1)
1066                 numberOfKeyframesWithOneOffset++;
1067             propertySpecificKeyframes.append(i);
1068         }
1069
1070         // 7. If property-specific keyframes is empty, return underlying value.
1071         if (propertySpecificKeyframes.isEmpty())
1072             continue;
1073
1074         // 8. If there is no keyframe in property-specific keyframes with a computed keyframe offset of 0, create a new keyframe with a computed keyframe
1075         //    offset of 0, a property value set to the neutral value for composition, and a composite operation of add, and prepend it to the beginning of
1076         //    property-specific keyframes.
1077         if (!numberOfKeyframesWithZeroOffset) {
1078             propertySpecificKeyframes.insert(0, std::nullopt);
1079             numberOfKeyframesWithZeroOffset = 1;
1080         }
1081
1082         // 9. Similarly, if there is no keyframe in property-specific keyframes with a computed keyframe offset of 1, create a new keyframe with a computed
1083         //    keyframe offset of 1, a property value set to the neutral value for composition, and a composite operation of add, and append it to the end of
1084         //    property-specific keyframes.
1085         if (!numberOfKeyframesWithOneOffset) {
1086             propertySpecificKeyframes.append(std::nullopt);
1087             numberOfKeyframesWithOneOffset = 1;
1088         }
1089
1090         // 10. Let interval endpoints be an empty sequence of keyframes.
1091         Vector<std::optional<size_t>> intervalEndpoints;
1092
1093         // 11. Populate interval endpoints by following the steps from the first matching condition from below:
1094         if (iterationProgress < 0 && numberOfKeyframesWithZeroOffset > 1) {
1095             // If iteration progress < 0 and there is more than one keyframe in property-specific keyframes with a computed keyframe offset of 0,
1096             // Add the first keyframe in property-specific keyframes to interval endpoints.
1097             intervalEndpoints.append(propertySpecificKeyframes.first());
1098         } else if (iterationProgress >= 1 && numberOfKeyframesWithOneOffset > 1) {
1099             // If iteration progress ≥ 1 and there is more than one keyframe in property-specific keyframes with a computed keyframe offset of 1,
1100             // Add the last keyframe in property-specific keyframes to interval endpoints.
1101             intervalEndpoints.append(propertySpecificKeyframes.last());
1102         } else {
1103             // Otherwise,
1104             // 1. Append to interval endpoints the last keyframe in property-specific keyframes whose computed keyframe offset is less than or equal
1105             //    to iteration progress and less than 1. If there is no such keyframe (because, for example, the iteration progress is negative),
1106             //    add the last keyframe whose computed keyframe offset is 0.
1107             // 2. Append to interval endpoints the next keyframe in property-specific keyframes after the one added in the previous step.
1108             size_t indexOfLastKeyframeWithZeroOffset = 0;
1109             int indexOfFirstKeyframeToAddToIntervalEndpoints = -1;
1110             for (size_t i = 0; i < propertySpecificKeyframes.size(); ++i) {
1111                 auto keyframeIndex = propertySpecificKeyframes[i];
1112                 auto offset = [&] () -> double {
1113                     if (!keyframeIndex)
1114                         return i ? 1 : 0;
1115                     return m_blendingKeyframes[keyframeIndex.value()].key();
1116                 }();
1117                 if (!offset)
1118                     indexOfLastKeyframeWithZeroOffset = i;
1119                 if (offset <= iterationProgress && offset < 1)
1120                     indexOfFirstKeyframeToAddToIntervalEndpoints = i;
1121                 else
1122                     break;
1123             }
1124
1125             if (indexOfFirstKeyframeToAddToIntervalEndpoints >= 0) {
1126                 intervalEndpoints.append(propertySpecificKeyframes[indexOfFirstKeyframeToAddToIntervalEndpoints]);
1127                 intervalEndpoints.append(propertySpecificKeyframes[indexOfFirstKeyframeToAddToIntervalEndpoints + 1]);
1128             } else {
1129                 ASSERT(indexOfLastKeyframeWithZeroOffset < propertySpecificKeyframes.size() - 1);
1130                 intervalEndpoints.append(propertySpecificKeyframes[indexOfLastKeyframeWithZeroOffset]);
1131                 intervalEndpoints.append(propertySpecificKeyframes[indexOfLastKeyframeWithZeroOffset + 1]);
1132             }
1133         }
1134
1135         // 12. For each keyframe in interval endpoints…
1136         // FIXME: we don't support this step yet since we don't deal with any composite operation other than "replace".
1137
1138         // 13. If there is only one keyframe in interval endpoints return the property value of target property on that keyframe.
1139         if (intervalEndpoints.size() == 1) {
1140             auto keyframeIndex = intervalEndpoints[0];
1141             auto keyframeStyle = !keyframeIndex ? &targetStyle : m_blendingKeyframes[keyframeIndex.value()].style();
1142             CSSPropertyAnimation::blendProperties(this, cssPropertyId, &targetStyle, keyframeStyle, keyframeStyle, 0);
1143             continue;
1144         }
1145
1146         // 14. Let start offset be the computed keyframe offset of the first keyframe in interval endpoints.
1147         auto startKeyframeIndex = intervalEndpoints.first();
1148         auto startOffset = !startKeyframeIndex ? 0 : m_blendingKeyframes[startKeyframeIndex.value()].key();
1149
1150         // 15. Let end offset be the computed keyframe offset of last keyframe in interval endpoints.
1151         auto endKeyframeIndex = intervalEndpoints.last();
1152         auto endOffset = !endKeyframeIndex ? 1 : m_blendingKeyframes[endKeyframeIndex.value()].key();
1153
1154         // 16. Let interval distance be the result of evaluating (iteration progress - start offset) / (end offset - start offset).
1155         auto intervalDistance = (iterationProgress - startOffset) / (endOffset - startOffset);
1156
1157         // 17. Let transformed distance be the result of evaluating the timing function associated with the first keyframe in interval endpoints
1158         //     passing interval distance as the input progress.
1159         auto transformedDistance = intervalDistance;
1160         if (startKeyframeIndex) {
1161             if (auto iterationDuration = timing()->iterationDuration()) {
1162                 auto rangeDuration = (endOffset - startOffset) * iterationDuration.seconds();
1163                 if (auto* timingFunction = timingFunctionForKeyframeAtIndex(startKeyframeIndex.value()))
1164                     transformedDistance = timingFunction->transformTime(intervalDistance, rangeDuration);
1165             }
1166         }
1167
1168         // 18. Return the result of applying the interpolation procedure defined by the animation type of the target property, to the values of the target
1169         //     property specified on the two keyframes in interval endpoints taking the first such value as Vstart and the second as Vend and using transformed
1170         //     distance as the interpolation parameter p.
1171         auto startStyle = !startKeyframeIndex ? &targetStyle : m_blendingKeyframes[startKeyframeIndex.value()].style();
1172         auto endStyle = !endKeyframeIndex ? &targetStyle : m_blendingKeyframes[endKeyframeIndex.value()].style();
1173         CSSPropertyAnimation::blendProperties(this, cssPropertyId, &targetStyle, startStyle, endStyle, transformedDistance);
1174     }
1175 }
1176
1177 TimingFunction* KeyframeEffectReadOnly::timingFunctionForKeyframeAtIndex(size_t index)
1178 {
1179     if (!m_parsedKeyframes.isEmpty())
1180         return m_parsedKeyframes[index].timingFunction.get();
1181
1182     auto effectAnimation = animation();
1183     if (is<DeclarativeAnimation>(effectAnimation)) {
1184         // If we're dealing with a CSS Animation, the timing function is specified either on the keyframe itself.
1185         if (is<CSSAnimation>(effectAnimation)) {
1186             if (auto* timingFunction = m_blendingKeyframes[index].timingFunction())
1187                 return timingFunction;
1188         }
1189
1190         // Failing that, or for a CSS Transition, the timing function is inherited from the backing Animation object.
1191         return downcast<DeclarativeAnimation>(effectAnimation)->backingAnimation().timingFunction();
1192     }
1193
1194     return nullptr;
1195 }
1196
1197 void KeyframeEffectReadOnly::updateAcceleratedAnimationState()
1198 {
1199     if (!m_shouldRunAccelerated)
1200         return;
1201
1202     if (!renderer()) {
1203         if (isRunningAccelerated())
1204             addPendingAcceleratedAction(AcceleratedAction::Stop);
1205         return;
1206     }
1207
1208     auto localTime = animation()->currentTime();
1209
1210     // If we don't have a localTime or localTime < 0, we either don't have a start time or we're before the startTime
1211     // so we shouldn't be running.
1212     if (!localTime || localTime.value() < 0_s) {
1213         if (isRunningAccelerated())
1214             addPendingAcceleratedAction(AcceleratedAction::Stop);
1215         return;
1216     }
1217
1218     auto playState = animation()->playState();
1219     if (playState == WebAnimation::PlayState::Paused) {
1220         if (m_lastRecordedAcceleratedAction != AcceleratedAction::Pause) {
1221             if (m_lastRecordedAcceleratedAction == AcceleratedAction::Stop)
1222                 addPendingAcceleratedAction(AcceleratedAction::Play);
1223             addPendingAcceleratedAction(AcceleratedAction::Pause);
1224         }
1225         return;
1226     }
1227
1228     if (playState == WebAnimation::PlayState::Finished) {
1229         if (isRunningAccelerated())
1230             addPendingAcceleratedAction(AcceleratedAction::Stop);
1231         else {
1232             m_lastRecordedAcceleratedAction = AcceleratedAction::Stop;
1233             m_pendingAcceleratedActions.clear();
1234         }
1235         return;
1236     }
1237
1238     if (playState == WebAnimation::PlayState::Running && localTime >= 0_s) {
1239         if (m_lastRecordedAcceleratedAction != AcceleratedAction::Play)
1240             addPendingAcceleratedAction(AcceleratedAction::Play);
1241         return;
1242     }
1243 }
1244
1245 void KeyframeEffectReadOnly::addPendingAcceleratedAction(AcceleratedAction action)
1246 {
1247     if (action == AcceleratedAction::Stop)
1248         m_pendingAcceleratedActions.clear();
1249     m_pendingAcceleratedActions.append(action);
1250     if (action != AcceleratedAction::Seek)
1251         m_lastRecordedAcceleratedAction = action;
1252     animation()->acceleratedStateDidChange();
1253 }
1254
1255 void KeyframeEffectReadOnly::animationDidSeek()
1256 {
1257     // There is no need to seek if we're not playing an animation already. If seeking
1258     // means we're moving into an active state, we'll pick this up in apply().
1259     if (m_shouldRunAccelerated && isRunningAccelerated())
1260         addPendingAcceleratedAction(AcceleratedAction::Seek);
1261 }
1262
1263 void KeyframeEffectReadOnly::animationSuspensionStateDidChange(bool animationIsSuspended)
1264 {
1265     if (m_shouldRunAccelerated)
1266         addPendingAcceleratedAction(animationIsSuspended ? AcceleratedAction::Pause : AcceleratedAction::Play);
1267 }
1268
1269 void KeyframeEffectReadOnly::applyPendingAcceleratedActions()
1270 {
1271     // Once an accelerated animation has been committed, we no longer want to force a layout.
1272     // This should have been performed by a call to forceLayoutIfNeeded() prior to applying
1273     // pending accelerated actions.
1274     m_needsForcedLayout = false;
1275
1276     if (m_pendingAcceleratedActions.isEmpty())
1277         return;
1278
1279     auto* renderer = this->renderer();
1280     if (!renderer || !renderer->isComposited()) {
1281         if (m_lastRecordedAcceleratedAction != AcceleratedAction::Stop || m_pendingAcceleratedActions.last() != AcceleratedAction::Stop)
1282             animation()->acceleratedStateDidChange();
1283         return;
1284     }
1285
1286     auto pendingAcceleratedActions = m_pendingAcceleratedActions;
1287     m_pendingAcceleratedActions.clear();
1288
1289     auto* compositedRenderer = downcast<RenderBoxModelObject>(renderer);
1290
1291     // To simplify the code we use a default of 0s for an unresolved current time since for a Stop action that is acceptable.
1292     auto timeOffset = animation()->currentTime().value_or(0_s).seconds() - timing()->delay().seconds();
1293
1294     for (const auto& action : pendingAcceleratedActions) {
1295         switch (action) {
1296         case AcceleratedAction::Play:
1297             if (!compositedRenderer->startAnimation(timeOffset, backingAnimationForCompositedRenderer().ptr(), m_blendingKeyframes)) {
1298                 m_shouldRunAccelerated = false;
1299                 m_lastRecordedAcceleratedAction = AcceleratedAction::Stop;
1300                 return;
1301             }
1302             break;
1303         case AcceleratedAction::Pause:
1304             compositedRenderer->animationPaused(timeOffset, m_blendingKeyframes.animationName());
1305             break;
1306         case AcceleratedAction::Seek:
1307             compositedRenderer->animationSeeked(timeOffset, m_blendingKeyframes.animationName());
1308             break;
1309         case AcceleratedAction::Stop:
1310             compositedRenderer->animationFinished(m_blendingKeyframes.animationName());
1311             if (!m_target->document().renderTreeBeingDestroyed())
1312                 m_target->invalidateStyleAndLayerComposition();
1313             break;
1314         }
1315     }
1316 }
1317
1318 Ref<const Animation> KeyframeEffectReadOnly::backingAnimationForCompositedRenderer() const
1319 {
1320     auto effectAnimation = animation();
1321     if (is<DeclarativeAnimation>(effectAnimation))
1322         return downcast<DeclarativeAnimation>(effectAnimation)->backingAnimation();
1323
1324     // FIXME: The iterationStart and endDelay AnimationEffectTimingReadOnly properties do not have
1325     // corresponding Animation properties.
1326     auto effectTiming = timing();
1327     auto animation = Animation::create();
1328     animation->setDuration(effectTiming->iterationDuration().seconds());
1329     animation->setDelay(effectTiming->delay().seconds());
1330     animation->setIterationCount(effectTiming->iterations());
1331     animation->setTimingFunction(effectTiming->timingFunction()->clone());
1332
1333     switch (effectTiming->fill()) {
1334     case FillMode::None:
1335     case FillMode::Auto:
1336         animation->setFillMode(AnimationFillMode::None);
1337         break;
1338     case FillMode::Backwards:
1339         animation->setFillMode(AnimationFillMode::Backwards);
1340         break;
1341     case FillMode::Forwards:
1342         animation->setFillMode(AnimationFillMode::Forwards);
1343         break;
1344     case FillMode::Both:
1345         animation->setFillMode(AnimationFillMode::Both);
1346         break;
1347     }
1348
1349     switch (effectTiming->direction()) {
1350     case PlaybackDirection::Normal:
1351         animation->setDirection(Animation::AnimationDirectionNormal);
1352         break;
1353     case PlaybackDirection::Alternate:
1354         animation->setDirection(Animation::AnimationDirectionAlternate);
1355         break;
1356     case PlaybackDirection::Reverse:
1357         animation->setDirection(Animation::AnimationDirectionReverse);
1358         break;
1359     case PlaybackDirection::AlternateReverse:
1360         animation->setDirection(Animation::AnimationDirectionAlternateReverse);
1361         break;
1362     }
1363
1364     return WTFMove(animation);
1365 }
1366
1367 RenderElement* KeyframeEffectReadOnly::renderer() const
1368 {
1369     return m_target ? m_target->renderer() : nullptr;
1370 }
1371
1372 const RenderStyle& KeyframeEffectReadOnly::currentStyle() const
1373 {
1374     if (auto* renderer = this->renderer())
1375         return renderer->style();
1376     return RenderStyle::defaultStyle();
1377 }
1378
1379 bool KeyframeEffectReadOnly::computeExtentOfTransformAnimation(LayoutRect& bounds) const
1380 {
1381     ASSERT(m_blendingKeyframes.containsProperty(CSSPropertyTransform));
1382
1383     if (!is<RenderBox>(renderer()))
1384         return true; // Non-boxes don't get transformed;
1385
1386     RenderBox& box = downcast<RenderBox>(*renderer());
1387     FloatRect rendererBox = snapRectToDevicePixels(box.borderBoxRect(), box.document().deviceScaleFactor());
1388
1389     FloatRect cumulativeBounds = bounds;
1390
1391     for (const auto& keyframe : m_blendingKeyframes.keyframes()) {
1392         // FIXME: maybe for declarative animations we always say it's true for the first and last keyframe.
1393         if (!keyframe.containsProperty(CSSPropertyTransform))
1394             continue;
1395
1396         LayoutRect keyframeBounds = bounds;
1397
1398         bool canCompute;
1399         if (transformFunctionListsMatch())
1400             canCompute = computeTransformedExtentViaTransformList(rendererBox, *keyframe.style(), keyframeBounds);
1401         else
1402             canCompute = computeTransformedExtentViaMatrix(rendererBox, *keyframe.style(), keyframeBounds);
1403
1404         if (!canCompute)
1405             return false;
1406
1407         cumulativeBounds.unite(keyframeBounds);
1408     }
1409
1410     bounds = LayoutRect(cumulativeBounds);
1411     return true;
1412 }
1413
1414 static bool containsRotation(const Vector<RefPtr<TransformOperation>>& operations)
1415 {
1416     for (const auto& operation : operations) {
1417         if (operation->type() == TransformOperation::ROTATE)
1418             return true;
1419     }
1420     return false;
1421 }
1422
1423 bool KeyframeEffectReadOnly::computeTransformedExtentViaTransformList(const FloatRect& rendererBox, const RenderStyle& style, LayoutRect& bounds) const
1424 {
1425     FloatRect floatBounds = bounds;
1426     FloatPoint transformOrigin;
1427
1428     bool applyTransformOrigin = containsRotation(style.transform().operations()) || style.transform().affectedByTransformOrigin();
1429     if (applyTransformOrigin) {
1430         transformOrigin.setX(rendererBox.x() + floatValueForLength(style.transformOriginX(), rendererBox.width()));
1431         transformOrigin.setY(rendererBox.y() + floatValueForLength(style.transformOriginY(), rendererBox.height()));
1432         // Ignore transformOriginZ because we'll bail if we encounter any 3D transforms.
1433
1434         floatBounds.moveBy(-transformOrigin);
1435     }
1436
1437     for (const auto& operation : style.transform().operations()) {
1438         if (operation->type() == TransformOperation::ROTATE) {
1439             // For now, just treat this as a full rotation. This could take angle into account to reduce inflation.
1440             floatBounds = boundsOfRotatingRect(floatBounds);
1441         } else {
1442             TransformationMatrix transform;
1443             operation->apply(transform, rendererBox.size());
1444             if (!transform.isAffine())
1445                 return false;
1446
1447             if (operation->type() == TransformOperation::MATRIX || operation->type() == TransformOperation::MATRIX_3D) {
1448                 TransformationMatrix::Decomposed2Type toDecomp;
1449                 transform.decompose2(toDecomp);
1450                 // Any rotation prevents us from using a simple start/end rect union.
1451                 if (toDecomp.angle)
1452                     return false;
1453             }
1454
1455             floatBounds = transform.mapRect(floatBounds);
1456         }
1457     }
1458
1459     if (applyTransformOrigin)
1460         floatBounds.moveBy(transformOrigin);
1461
1462     bounds = LayoutRect(floatBounds);
1463     return true;
1464 }
1465
1466 bool KeyframeEffectReadOnly::computeTransformedExtentViaMatrix(const FloatRect& rendererBox, const RenderStyle& style, LayoutRect& bounds) const
1467 {
1468     TransformationMatrix transform;
1469     style.applyTransform(transform, rendererBox, RenderStyle::IncludeTransformOrigin);
1470     if (!transform.isAffine())
1471         return false;
1472
1473     TransformationMatrix::Decomposed2Type fromDecomp;
1474     transform.decompose2(fromDecomp);
1475     // Any rotation prevents us from using a simple start/end rect union.
1476     if (fromDecomp.angle)
1477         return false;
1478
1479     bounds = LayoutRect(transform.mapRect(bounds));
1480     return true;
1481 }
1482
1483 } // namespace WebCore