1 /*
2  * Copyright (C) 2013 Google 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 are
6  * met:
7  *
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *     * Redistributions in binary form must reproduce the above
11  * copyright notice, this list of conditions and the following disclaimer
12  * in the documentation and/or other materials provided with the
13  * distribution.
14  *     * Neither the name of Google Inc. nor the names of its
15  * contributors may be used to endorse or promote products derived from
16  * this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 #include "third_party/blink/renderer/core/animation/css/css_animations.h"
32 
33 #include <algorithm>
34 #include <bitset>
35 
36 #include "third_party/blink/public/platform/platform.h"
37 #include "third_party/blink/renderer/bindings/core/v8/v8_computed_effect_timing.h"
38 #include "third_party/blink/renderer/core/animation/animation.h"
39 #include "third_party/blink/renderer/core/animation/compositor_animations.h"
40 #include "third_party/blink/renderer/core/animation/css/compositor_keyframe_value_factory.h"
41 #include "third_party/blink/renderer/core/animation/css/css_animation.h"
42 #include "third_party/blink/renderer/core/animation/css/css_keyframe_effect_model.h"
43 #include "third_party/blink/renderer/core/animation/css/css_scroll_timeline.h"
44 #include "third_party/blink/renderer/core/animation/css/css_transition.h"
45 #include "third_party/blink/renderer/core/animation/css_interpolation_types_map.h"
46 #include "third_party/blink/renderer/core/animation/document_animations.h"
47 #include "third_party/blink/renderer/core/animation/document_timeline.h"
48 #include "third_party/blink/renderer/core/animation/element_animations.h"
49 #include "third_party/blink/renderer/core/animation/inert_effect.h"
50 #include "third_party/blink/renderer/core/animation/interpolation.h"
51 #include "third_party/blink/renderer/core/animation/interpolation_environment.h"
52 #include "third_party/blink/renderer/core/animation/interpolation_type.h"
53 #include "third_party/blink/renderer/core/animation/keyframe_effect.h"
54 #include "third_party/blink/renderer/core/animation/keyframe_effect_model.h"
55 #include "third_party/blink/renderer/core/animation/scroll_timeline_offset.h"
56 #include "third_party/blink/renderer/core/animation/timing_calculations.h"
57 #include "third_party/blink/renderer/core/animation/transition_interpolation.h"
58 #include "third_party/blink/renderer/core/animation/worklet_animation_base.h"
59 #include "third_party/blink/renderer/core/css/css_keyframe_rule.h"
60 #include "third_party/blink/renderer/core/css/css_property_equality.h"
61 #include "third_party/blink/renderer/core/css/css_value_list.h"
62 #include "third_party/blink/renderer/core/css/parser/css_variable_parser.h"
63 #include "third_party/blink/renderer/core/css/properties/computed_style_utils.h"
64 #include "third_party/blink/renderer/core/css/properties/css_property.h"
65 #include "third_party/blink/renderer/core/css/property_registry.h"
66 #include "third_party/blink/renderer/core/css/resolver/css_to_style_map.h"
67 #include "third_party/blink/renderer/core/css/resolver/style_resolver.h"
68 #include "third_party/blink/renderer/core/css/style_engine.h"
69 #include "third_party/blink/renderer/core/dom/element.h"
70 #include "third_party/blink/renderer/core/dom/events/event_path.h"
71 #include "third_party/blink/renderer/core/dom/node_computed_style.h"
72 #include "third_party/blink/renderer/core/dom/pseudo_element.h"
73 #include "third_party/blink/renderer/core/dom/shadow_root.h"
74 #include "third_party/blink/renderer/core/events/animation_event.h"
75 #include "third_party/blink/renderer/core/events/transition_event.h"
76 #include "third_party/blink/renderer/core/frame/web_feature.h"
77 #include "third_party/blink/renderer/core/layout/layout_object.h"
78 #include "third_party/blink/renderer/core/paint/paint_layer.h"
79 #include "third_party/blink/renderer/core/style_property_shorthand.h"
80 #include "third_party/blink/renderer/platform/animation/timing_function.h"
81 #include "third_party/blink/renderer/platform/heap/heap.h"
82 #include "third_party/blink/renderer/platform/instrumentation/histogram.h"
83 #include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
84 #include "third_party/blink/renderer/platform/wtf/hash_set.h"
85 
86 namespace blink {
87 
88 using PropertySet = HashSet<const CSSProperty*>;
89 
90 namespace {
91 
92 // Processes keyframe rules, extracting the timing function and properties being
93 // animated for each keyframe. The extraction process is doing more work that
94 // strictly required for the setup to step 5 in the spec
95 // (https://drafts.csswg.org/css-animations-2/#keyframes) as an optimization
96 // to avoid needing to process each rule multiple times to extract different
97 // properties.
ProcessKeyframesRule(const StyleRuleKeyframes * keyframes_rule,const Document & document,const ComputedStyle * parent_style,TimingFunction * default_timing_function,WritingMode writing_mode,TextDirection text_direction)98 StringKeyframeVector ProcessKeyframesRule(
99     const StyleRuleKeyframes* keyframes_rule,
100     const Document& document,
101     const ComputedStyle* parent_style,
102     TimingFunction* default_timing_function,
103     WritingMode writing_mode,
104     TextDirection text_direction) {
105   StringKeyframeVector keyframes;
106   const HeapVector<Member<StyleRuleKeyframe>>& style_keyframes =
107       keyframes_rule->Keyframes();
108 
109   for (wtf_size_t i = 0; i < style_keyframes.size(); ++i) {
110     const StyleRuleKeyframe* style_keyframe = style_keyframes[i].Get();
111     auto* keyframe = MakeGarbageCollected<StringKeyframe>();
112     const Vector<double>& offsets = style_keyframe->Keys();
113     DCHECK(!offsets.IsEmpty());
114     keyframe->SetOffset(offsets[0]);
115     keyframe->SetEasing(default_timing_function);
116     const CSSPropertyValueSet& properties = style_keyframe->Properties();
117     for (unsigned j = 0; j < properties.PropertyCount(); j++) {
118       // TODO(crbug.com/980160): Remove access to static Variable instance.
119       const CSSProperty& property =
120           CSSProperty::Get(properties.PropertyAt(j).Id());
121       if (property.PropertyID() == CSSPropertyID::kAnimationTimingFunction) {
122         const CSSValue& value = properties.PropertyAt(j).Value();
123         scoped_refptr<TimingFunction> timing_function;
124         if (value.IsInheritedValue() && parent_style->Animations()) {
125           timing_function = parent_style->Animations()->TimingFunctionList()[0];
126         } else if (auto* value_list = DynamicTo<CSSValueList>(value)) {
127           timing_function =
128               CSSToStyleMap::MapAnimationTimingFunction(value_list->Item(0));
129         } else {
130           DCHECK(value.IsCSSWideKeyword());
131           timing_function = CSSTimingData::InitialTimingFunction();
132         }
133         keyframe->SetEasing(std::move(timing_function));
134       } else if (!CSSAnimations::IsAnimationAffectingProperty(property)) {
135         // Map Logical to physical property name.
136         const CSSProperty& physical_property =
137             property.ResolveDirectionAwareProperty(text_direction,
138                                                    writing_mode);
139         keyframe->SetCSSPropertyValue(physical_property,
140                                       properties.PropertyAt(j).Value());
141       }
142     }
143     keyframes.push_back(keyframe);
144     // The last keyframe specified at a given offset is used.
145     for (wtf_size_t j = 1; j < offsets.size(); ++j) {
146       keyframes.push_back(
147           To<StringKeyframe>(keyframe->CloneWithOffset(offsets[j])));
148     }
149   }
150 
151   std::stable_sort(keyframes.begin(), keyframes.end(),
152                    [](const Member<Keyframe>& a, const Member<Keyframe>& b) {
153                      return a->CheckedOffset() < b->CheckedOffset();
154                    });
155   return keyframes;
156 }
157 
158 // Finds the index of a keyframe with matching offset and easing.
FindIndexOfMatchingKeyframe(const StringKeyframeVector & keyframes,wtf_size_t start_index,double offset,const TimingFunction & easing)159 base::Optional<int> FindIndexOfMatchingKeyframe(
160     const StringKeyframeVector& keyframes,
161     wtf_size_t start_index,
162     double offset,
163     const TimingFunction& easing) {
164   for (wtf_size_t i = start_index; i < keyframes.size(); i++) {
165     StringKeyframe* keyframe = keyframes[i];
166 
167     // Keyframes are sorted by offset. Search can stop once we hit and offset
168     // that exceeds the target value.
169     if (offset < keyframe->CheckedOffset())
170       break;
171 
172     if (easing.ToString() == keyframe->Easing().ToString())
173       return i;
174   }
175   return base::nullopt;
176 }
177 
178 // Tests conditions for inserting a bounding keyframe, which are outlined in
179 // steps 6 and 7 of the spec for keyframe construction.
180 // https://drafts.csswg.org/css-animations-2/#keyframes
NeedsBoundaryKeyframe(StringKeyframe * candidate,double offset,const PropertySet & animated_properties,const PropertySet & bounding_properties,TimingFunction * default_timing_function)181 bool NeedsBoundaryKeyframe(StringKeyframe* candidate,
182                            double offset,
183                            const PropertySet& animated_properties,
184                            const PropertySet& bounding_properties,
185                            TimingFunction* default_timing_function) {
186   if (!candidate)
187     return true;
188 
189   if (candidate->CheckedOffset() != offset)
190     return true;
191 
192   if (bounding_properties.size() == animated_properties.size())
193     return false;
194 
195   return candidate->Easing().ToString() != default_timing_function->ToString();
196 }
197 
CreateKeyframeEffectModel(StyleResolver * resolver,const Element * animating_element,Element & element,const ComputedStyle * style,const ComputedStyle * parent_style,const AtomicString & name,TimingFunction * default_timing_function,size_t animation_index)198 StringKeyframeEffectModel* CreateKeyframeEffectModel(
199     StyleResolver* resolver,
200     const Element* animating_element,
201     Element& element,
202     const ComputedStyle* style,
203     const ComputedStyle* parent_style,
204     const AtomicString& name,
205     TimingFunction* default_timing_function,
206     size_t animation_index) {
207   // The algorithm for constructing string keyframes for a CSS animation is
208   // covered in the following spec:
209   // https://drafts.csswg.org/css-animations-2/#keyframes
210 
211   // For a given target (pseudo-)element, element, animation name, and
212   // position of the animation in element’s animation-name list, keyframe
213   // objects are generated as follows:
214 
215   // 1. Let default timing function be the timing function at the position
216   //    of the resolved value of the animation-timing-function for element,
217   //    repeating the list as necessary as described in CSS Animations 1 §4.2
218   //    The animation-name property.
219 
220   // 2. Find the last @keyframes at-rule in document order with <keyframes-name>
221   //    matching name.
222   //    If there is no @keyframes at-rule with <keyframes-name> matching name,
223   //    abort this procedure. In this case no animation is generated, and any
224   //    existing animation matching name is canceled.
225 
226   const StyleRuleKeyframes* keyframes_rule =
227       resolver->FindKeyframesRule(&element, name);
228   DCHECK(keyframes_rule);
229 
230   // 3. Let keyframes be an empty sequence of keyframe objects.
231   StringKeyframeVector keyframes;
232 
233   // 4. Let animated properties be an empty set of longhand CSS property names.
234   PropertySet animated_properties;
235 
236   // Start and end properties are also tracked to simplify the process of
237   // determining if the first and last keyframes are missing properties.
238   PropertySet start_properties;
239   PropertySet end_properties;
240 
241   // Properties that have already been processed at the current keyframe.
242   PropertySet current_offset_properties;
243 
244   // 5. Perform a stable sort of the keyframe blocks in the @keyframes rule by
245   //    the offset specified in the keyframe selector, and iterate over the
246   //    result in reverse applying the following steps:
247   keyframes = ProcessKeyframesRule(keyframes_rule, element.GetDocument(),
248                                    parent_style, default_timing_function,
249                                    style->GetWritingMode(), style->Direction());
250 
251   double last_offset = 1;
252   wtf_size_t merged_frame_count = 0;
253   for (wtf_size_t i = keyframes.size(); i > 0; --i) {
254     // 5.1 Let keyframe offset be the value of the keyframe selector converted
255     //     to a value in the range 0 ≤ keyframe offset ≤ 1.
256     int source_index = i - 1;
257     StringKeyframe* rule_keyframe = keyframes[source_index];
258     double keyframe_offset = rule_keyframe->CheckedOffset();
259 
260     // 5.2 Let keyframe timing function be the value of the last valid
261     //     declaration of animation-timing-function specified on the keyframe
262     //     block, or, if there is no such valid declaration, default timing
263     //     function.
264     const TimingFunction& easing = rule_keyframe->Easing();
265 
266     // 5.3 After converting keyframe timing function to its canonical form (e.g.
267     //     such that step-end becomes steps(1, end)) let keyframe refer to the
268     //     existing keyframe in keyframes with matching keyframe offset and
269     //     timing function, if any.
270     //     If there is no such existing keyframe, let keyframe be a new empty
271     //     keyframe with offset, keyframe offset, and timing function, keyframe
272     //     timing function, and prepend it to keyframes.
273 
274     // Prevent stomping a rule override by tracking properties applied at
275     // the current offset.
276     if (last_offset != keyframe_offset) {
277       current_offset_properties.clear();
278       last_offset = keyframe_offset;
279     }
280 
281     // Avoid unnecessary creation of extra keyframes by merging into
282     // existing keyframes.
283     base::Optional<int> existing_keyframe_index = FindIndexOfMatchingKeyframe(
284         keyframes, source_index + merged_frame_count + 1, keyframe_offset,
285         easing);
286     int target_index;
287     if (existing_keyframe_index) {
288       // Merge keyframe propoerties.
289       target_index = existing_keyframe_index.value();
290       merged_frame_count++;
291     } else {
292       target_index = source_index + merged_frame_count;
293       if (target_index != source_index) {
294         // Move keyframe to fill the gap.
295         keyframes[target_index] = keyframes[source_index];
296         source_index = target_index;
297       }
298     }
299 
300     // 5.4 Iterate over all declarations in the keyframe block and add them to
301     //     keyframe such that:
302     //     * All variable references are resolved to their current values.
303     //     * Each shorthand property is expanded to its longhand subproperties.
304     //     * All logical properties are converted to their equivalent physical
305     //       properties.
306     //     * For any expanded physical longhand properties that appear more than
307     //       once, only the last declaration in source order is added.
308     //       Note, since multiple keyframe blocks may specify the same keyframe
309     //       offset, and since this algorithm iterates over these blocks in
310     //       reverse, this implies that if any properties are encountered that
311     //       have already added at this same keyframe offset, they should be
312     //       skipped.
313     //     * All property values are replaced with their computed values.
314     // 5.5 Add each physical longhand property name that was added to keyframe
315     //     to animated properties.
316     StringKeyframe* keyframe = keyframes[target_index];
317     for (const auto& property : rule_keyframe->Properties()) {
318       const CSSProperty& css_property = property.GetCSSProperty();
319 
320       // Since processing keyframes in reverse order, skipping properties that
321       // have already been inserted prevents overwriting a later merged
322       // keyframe.
323       if (current_offset_properties.Contains(&css_property))
324         continue;
325 
326       if (source_index != target_index) {
327         keyframe->SetCSSPropertyValue(
328             css_property, rule_keyframe->CssPropertyValue(property));
329       }
330 
331       current_offset_properties.insert(&css_property);
332       animated_properties.insert(&css_property);
333       if (keyframe_offset == 0)
334         start_properties.insert(&css_property);
335       else if (keyframe_offset == 1)
336         end_properties.insert(&css_property);
337     }
338   }
339 
340   // Compact the vector of keyframes if any keyframes have been merged.
341   keyframes.EraseAt(0, merged_frame_count);
342 
343   // 6.  If there is no keyframe in keyframes with offset 0, or if amongst the
344   //     keyframes in keyframes with offset 0 not all of the properties in
345   //     animated properties are present,
346   //
347   // 6.1 Let initial keyframe be the keyframe in keyframes with offset 0 and
348   //     timing function default timing function.
349   // 6.2 If there is no such keyframe, let initial keyframe be a new empty
350   //     keyframe with offset 0, and timing function default timing function,
351   //     and add it to keyframes after the last keyframe with offset 0.
352   // 6.3 For each property in animated properties that is not present in some
353   //     other keyframe with offset 0, add the computed value of that property
354   //     for element to the keyframe.
355   StringKeyframe* start_keyframe = keyframes.IsEmpty() ? nullptr : keyframes[0];
356   if (NeedsBoundaryKeyframe(start_keyframe, 0, animated_properties,
357                             start_properties, default_timing_function)) {
358     start_keyframe = MakeGarbageCollected<StringKeyframe>();
359     start_keyframe->SetOffset(0);
360     start_keyframe->SetEasing(default_timing_function);
361     keyframes.push_front(start_keyframe);
362   }
363 
364   // 7.  Similarly, if there is no keyframe in keyframes with offset 1, or if
365   //     amongst the keyframes in keyframes with offset 1 not all of the
366   //     properties in animated properties are present,
367   //
368   // 7.1 Let final keyframe be the keyframe in keyframes with offset 1 and
369   //     timing function default timing function.
370   // 7.2 If there is no such keyframe, let final keyframe be a new empty
371   //     keyframe with offset 1, and timing function default timing function,
372   //     and add it to keyframes after the last keyframe with offset 1.
373   // 7.3 For each property in animated properties that is not present in some
374   //     other keyframe with offset 1, add the computed value of that property
375   //     for element to the keyframe.
376   StringKeyframe* end_keyframe = keyframes[keyframes.size() - 1];
377   if (NeedsBoundaryKeyframe(end_keyframe, 1, animated_properties,
378                             end_properties, default_timing_function)) {
379     end_keyframe = MakeGarbageCollected<StringKeyframe>();
380     end_keyframe->SetOffset(1);
381     end_keyframe->SetEasing(default_timing_function);
382     keyframes.push_back(end_keyframe);
383   }
384 
385   DCHECK_GE(keyframes.size(), 2U);
386   DCHECK_EQ(keyframes.front()->CheckedOffset(), 0);
387   DCHECK_EQ(keyframes.back()->CheckedOffset(), 1);
388 
389   auto* model = MakeGarbageCollected<CssKeyframeEffectModel>(
390       keyframes, EffectModel::kCompositeReplace, &start_keyframe->Easing());
391   if (animation_index > 0 && model->HasSyntheticKeyframes()) {
392     UseCounter::Count(element.GetDocument(),
393                       WebFeature::kCSSAnimationsStackedNeutralKeyframe);
394   }
395   return model;
396 }
397 
398 // Returns the start time of an animation given the start delay. A negative
399 // start delay results in the animation starting with non-zero progress.
StartTimeFromDelay(double start_delay)400 AnimationTimeDelta StartTimeFromDelay(double start_delay) {
401   return AnimationTimeDelta::FromSecondsD(start_delay < 0 ? -start_delay : 0);
402 }
403 
404 // Timing functions for computing elapsed time of an event.
405 
IntervalStart(const AnimationEffect & effect)406 AnimationTimeDelta IntervalStart(const AnimationEffect& effect) {
407   const double start_delay = effect.SpecifiedTiming().start_delay;
408   const double active_duration = effect.SpecifiedTiming().ActiveDuration();
409   return AnimationTimeDelta::FromSecondsD(
410       std::fmax(std::fmin(-start_delay, active_duration), 0.0));
411 }
412 
IntervalEnd(const AnimationEffect & effect)413 AnimationTimeDelta IntervalEnd(const AnimationEffect& effect) {
414   const double start_delay = effect.SpecifiedTiming().start_delay;
415   const double end_delay = effect.SpecifiedTiming().end_delay;
416   const double active_duration = effect.SpecifiedTiming().ActiveDuration();
417   const double target_effect_end =
418       std::max(start_delay + active_duration + end_delay, 0.0);
419   return AnimationTimeDelta::FromSecondsD(std::max(
420       std::min(target_effect_end - start_delay, active_duration), 0.0));
421 }
422 
IterationElapsedTime(const AnimationEffect & effect,double previous_iteration)423 AnimationTimeDelta IterationElapsedTime(const AnimationEffect& effect,
424                                         double previous_iteration) {
425   const double current_iteration = effect.CurrentIteration().value();
426   const double iteration_boundary = (previous_iteration > current_iteration)
427                                         ? current_iteration + 1
428                                         : current_iteration;
429   const double iteration_start = effect.SpecifiedTiming().iteration_start;
430   const AnimationTimeDelta iteration_duration =
431       effect.SpecifiedTiming().IterationDuration();
432   return iteration_duration * (iteration_boundary - iteration_start);
433 }
434 
CreateCSSScrollTimeline(Element * element,const CSSScrollTimeline::Options & options)435 CSSScrollTimeline* CreateCSSScrollTimeline(
436     Element* element,
437     const CSSScrollTimeline::Options& options) {
438   if (!options.IsValid())
439     return nullptr;
440   auto* scroll_timeline =
441       MakeGarbageCollected<CSSScrollTimeline>(&element->GetDocument(), options);
442   // It's is not allowed for a style resolve to create timelines that
443   // needs timing updates (i.e. AnimationTimeline::NeedsAnimationTimingUpdate()
444   // must return false). Servicing animations after creation preserves this
445   // invariant by ensuring the last-update time of the timeline is equal to
446   // the current time.
447   scroll_timeline->ServiceAnimations(kTimingUpdateOnDemand);
448   return scroll_timeline;
449 }
450 
FindMatchingCachedTimeline(Document & document,const AtomicString & name,const CSSScrollTimeline::Options & options)451 CSSScrollTimeline* FindMatchingCachedTimeline(
452     Document& document,
453     const AtomicString& name,
454     const CSSScrollTimeline::Options& options) {
455   auto* cached_timeline = DynamicTo<CSSScrollTimeline>(
456       document.GetDocumentAnimations().FindCachedCSSScrollTimeline(name));
457   if (cached_timeline && cached_timeline->Matches(options))
458     return cached_timeline;
459   return nullptr;
460 }
461 
ComputeTimeline(Element * element,const StyleNameOrKeyword & timeline_name,StyleRuleScrollTimeline * rule,AnimationTimeline * existing_timeline)462 AnimationTimeline* ComputeTimeline(Element* element,
463                                    const StyleNameOrKeyword& timeline_name,
464                                    StyleRuleScrollTimeline* rule,
465                                    AnimationTimeline* existing_timeline) {
466   Document& document = element->GetDocument();
467   if (timeline_name.IsKeyword()) {
468     if (timeline_name.GetKeyword() == CSSValueID::kAuto)
469       return &document.Timeline();
470     DCHECK_EQ(timeline_name.GetKeyword(), CSSValueID::kNone);
471     return nullptr;
472   }
473   if (rule) {
474     CSSScrollTimeline::Options options(element, *rule);
475 
476     const AtomicString& name = timeline_name.GetName().GetValue();
477     // When multiple animations refer to the same @scroll-timeline, the same
478     // CSSScrollTimeline instance should be shared.
479     if (auto* timeline = FindMatchingCachedTimeline(document, name, options))
480       return timeline;
481     // When the incoming options match the existing timeline (associated with
482     // an existing animation), we can continue to use the existing timeline,
483     // since creating a new timeline from the options would just yield an
484     // identical timeline.
485     if (auto* timeline = DynamicTo<CSSScrollTimeline>(existing_timeline)) {
486       if (timeline->Matches(options))
487         return existing_timeline;
488     }
489     if (auto* timeline = CreateCSSScrollTimeline(element, options))
490       return timeline;
491   }
492   return nullptr;
493 }
494 
FindScrollTimelineRule(Document & document,const StyleNameOrKeyword & timeline_name)495 StyleRuleScrollTimeline* FindScrollTimelineRule(
496     Document& document,
497     const StyleNameOrKeyword& timeline_name) {
498   if (timeline_name.IsKeyword())
499     return nullptr;
500   return document.GetStyleEngine().FindScrollTimelineRule(
501       timeline_name.GetName().GetValue());
502 }
503 
504 }  // namespace
505 
506 CSSAnimations::CSSAnimations() = default;
507 
508 namespace {
509 
GetKeyframeEffectModelBase(const AnimationEffect * effect)510 const KeyframeEffectModelBase* GetKeyframeEffectModelBase(
511     const AnimationEffect* effect) {
512   if (!effect)
513     return nullptr;
514   const EffectModel* model = nullptr;
515   if (auto* keyframe_effect = DynamicTo<KeyframeEffect>(effect))
516     model = keyframe_effect->Model();
517   else if (auto* inert_effect = DynamicTo<InertEffect>(effect))
518     model = inert_effect->Model();
519   if (!model || !model->IsKeyframeEffectModel())
520     return nullptr;
521   return To<KeyframeEffectModelBase>(model);
522 }
523 
ComputedValuesEqual(const PropertyHandle & property,const ComputedStyle & a,const ComputedStyle & b)524 bool ComputedValuesEqual(const PropertyHandle& property,
525                          const ComputedStyle& a,
526                          const ComputedStyle& b) {
527   // If zoom hasn't changed, compare internal values (stored with zoom applied)
528   // for speed. Custom properties are never zoomed so they are checked here too.
529   if (a.EffectiveZoom() == b.EffectiveZoom() ||
530       property.IsCSSCustomProperty()) {
531     return CSSPropertyEquality::PropertiesEqual(property, a, b);
532   }
533 
534   // If zoom has changed, we must construct and compare the unzoomed
535   // computed values.
536   if (property.GetCSSProperty().PropertyID() == CSSPropertyID::kTransform) {
537     // Transform lists require special handling in this case to deal with
538     // layout-dependent interpolation which does not yet have a CSSValue.
539     return a.Transform().Zoom(1 / a.EffectiveZoom()) ==
540            b.Transform().Zoom(1 / b.EffectiveZoom());
541   } else {
542     const CSSValue* a_val =
543         ComputedStyleUtils::ComputedPropertyValue(property.GetCSSProperty(), a);
544     const CSSValue* b_val =
545         ComputedStyleUtils::ComputedPropertyValue(property.GetCSSProperty(), b);
546     // Computed values can be null if not able to parse.
547     if (a_val && b_val)
548       return *a_val == *b_val;
549     // Fallback to the zoom-unaware comparator if either value could not be
550     // parsed.
551     return CSSPropertyEquality::PropertiesEqual(property, a, b);
552   }
553 }
554 
555 }  // namespace
556 
CalculateCompositorAnimationUpdate(CSSAnimationUpdate & update,const Element * animating_element,Element & element,const ComputedStyle & style,const ComputedStyle * parent_style,bool was_viewport_resized)557 void CSSAnimations::CalculateCompositorAnimationUpdate(
558     CSSAnimationUpdate& update,
559     const Element* animating_element,
560     Element& element,
561     const ComputedStyle& style,
562     const ComputedStyle* parent_style,
563     bool was_viewport_resized) {
564   ElementAnimations* element_animations =
565       animating_element ? animating_element->GetElementAnimations() : nullptr;
566 
567   // If the change in style is only due to the Blink-side animation update, we
568   // do not need to update the compositor-side animations. The compositor is
569   // already changing the same properties and as such this update would provide
570   // no new information.
571   if (!element_animations || element_animations->IsAnimationStyleChange())
572     return;
573 
574   const ComputedStyle* old_style = animating_element->GetComputedStyle();
575   if (!old_style || old_style->IsEnsuredInDisplayNone() ||
576       !old_style->ShouldCompositeForCurrentAnimations()) {
577     return;
578   }
579 
580   bool transform_zoom_changed =
581       old_style->HasCurrentTransformAnimation() &&
582       old_style->EffectiveZoom() != style.EffectiveZoom();
583 
584   const auto& snapshot = [&](AnimationEffect* effect) {
585     const KeyframeEffectModelBase* keyframe_effect =
586         GetKeyframeEffectModelBase(effect);
587     if (!keyframe_effect)
588       return false;
589 
590     if ((transform_zoom_changed || was_viewport_resized) &&
591         (keyframe_effect->Affects(PropertyHandle(GetCSSPropertyTransform())) ||
592          keyframe_effect->Affects(PropertyHandle(GetCSSPropertyTranslate()))))
593       keyframe_effect->InvalidateCompositorKeyframesSnapshot();
594 
595     if (keyframe_effect->SnapshotAllCompositorKeyframesIfNecessary(
596             element, style, parent_style)) {
597       return true;
598     } else if (keyframe_effect->HasSyntheticKeyframes() &&
599                keyframe_effect->SnapshotNeutralCompositorKeyframes(
600                    element, *old_style, style, parent_style)) {
601       return true;
602     }
603     return false;
604   };
605 
606   for (auto& entry : element_animations->Animations()) {
607     Animation& animation = *entry.key;
608     if (snapshot(animation.effect()))
609       update.UpdateCompositorKeyframes(&animation);
610   }
611 
612   for (auto& entry : element_animations->GetWorkletAnimations()) {
613     WorkletAnimationBase& animation = *entry;
614     if (snapshot(animation.GetEffect()))
615       animation.InvalidateCompositingState();
616   }
617 }
618 
CalculateAnimationUpdate(CSSAnimationUpdate & update,const Element * animating_element,Element & element,const ComputedStyle & style,const ComputedStyle * parent_style,StyleResolver * resolver)619 void CSSAnimations::CalculateAnimationUpdate(CSSAnimationUpdate& update,
620                                              const Element* animating_element,
621                                              Element& element,
622                                              const ComputedStyle& style,
623                                              const ComputedStyle* parent_style,
624                                              StyleResolver* resolver) {
625   ElementAnimations* element_animations =
626       animating_element ? animating_element->GetElementAnimations() : nullptr;
627 
628   bool is_animation_style_change =
629       element_animations && element_animations->IsAnimationStyleChange();
630 
631 #if !DCHECK_IS_ON()
632   // If we're in an animation style change, no animations can have started, been
633   // cancelled or changed play state. When DCHECK is enabled, we verify this
634   // optimization.
635   if (is_animation_style_change) {
636     CalculateAnimationActiveInterpolations(update, animating_element);
637     return;
638   }
639 #endif
640 
641   // Rebuild the keyframe model for a CSS animation if it may have been
642   // invalidated by a change to the text direction or writing mode.
643   const ComputedStyle* old_style =
644       animating_element ? animating_element->GetComputedStyle() : nullptr;
645   bool logical_property_mapping_change =
646       !old_style || old_style->Direction() != style.Direction() ||
647       old_style->GetWritingMode() != style.GetWritingMode();
648 
649   if (logical_property_mapping_change && element_animations) {
650     // Update computed keyframes for any running animations that depend on
651     // logical properties.
652     for (auto& entry : element_animations->Animations()) {
653       Animation* animation = entry.key;
654       if (auto* keyframe_effect =
655               DynamicTo<KeyframeEffect>(animation->effect())) {
656         keyframe_effect->SetLogicalPropertyResolutionContext(
657             style.Direction(), style.GetWritingMode());
658         animation->UpdateIfNecessary();
659       }
660     }
661   }
662 
663   const CSSAnimationData* animation_data = style.Animations();
664   const CSSAnimations* css_animations =
665       element_animations ? &element_animations->CssAnimations() : nullptr;
666 
667   Vector<bool> cancel_running_animation_flags(
668       css_animations ? css_animations->running_animations_.size() : 0);
669   for (bool& flag : cancel_running_animation_flags)
670     flag = true;
671 
672   if (animation_data && style.Display() != EDisplay::kNone) {
673     const Vector<AtomicString>& name_list = animation_data->NameList();
674     for (wtf_size_t i = 0; i < name_list.size(); ++i) {
675       AtomicString name = name_list[i];
676       if (name == CSSAnimationData::InitialName())
677         continue;
678 
679       // Find n where this is the nth occurence of this animation name.
680       wtf_size_t name_index = 0;
681       for (wtf_size_t j = 0; j < i; j++) {
682         if (name_list[j] == name)
683           name_index++;
684       }
685 
686       const bool is_paused =
687           CSSTimingData::GetRepeated(animation_data->PlayStateList(), i) ==
688           EAnimPlayState::kPaused;
689 
690       Timing timing = animation_data->ConvertToTiming(i);
691       Timing specified_timing = timing;
692       scoped_refptr<TimingFunction> keyframe_timing_function =
693           timing.timing_function;
694       timing.timing_function = Timing().timing_function;
695 
696       StyleRuleKeyframes* keyframes_rule =
697           resolver->FindKeyframesRule(&element, name);
698       if (!keyframes_rule)
699         continue;  // Cancel the animation if there's no style rule for it.
700 
701       const StyleNameOrKeyword& timeline_name = animation_data->GetTimeline(i);
702 
703       StyleRuleScrollTimeline* scroll_timeline_rule =
704           FindScrollTimelineRule(element.GetDocument(), timeline_name);
705 
706       const RunningAnimation* existing_animation = nullptr;
707       wtf_size_t existing_animation_index = 0;
708 
709       if (css_animations) {
710         for (wtf_size_t j = 0; j < css_animations->running_animations_.size();
711              j++) {
712           const RunningAnimation& running_animation =
713               *css_animations->running_animations_[j];
714           if (running_animation.name == name &&
715               running_animation.name_index == name_index) {
716             existing_animation = &running_animation;
717             existing_animation_index = j;
718             break;
719           }
720         }
721       }
722 
723       if (existing_animation) {
724         cancel_running_animation_flags[existing_animation_index] = false;
725 
726         CSSAnimation* animation =
727             DynamicTo<CSSAnimation>(existing_animation->animation.Get());
728         animation->SetAnimationIndex(i);
729         const bool was_paused =
730             CSSTimingData::GetRepeated(existing_animation->play_state_list,
731                                        i) == EAnimPlayState::kPaused;
732 
733         // Explicit calls to web-animation play controls override changes to
734         // play state via the animation-play-state style. Ensure that the new
735         // play state based on animation-play-state differs from the current
736         // play state and that the change is not blocked by a sticky state.
737         bool toggle_pause_state = false;
738         if (is_paused != was_paused && !animation->getIgnoreCSSPlayState()) {
739           if (animation->Paused() && !is_paused)
740             toggle_pause_state = true;
741           else if (animation->Playing() && is_paused)
742             toggle_pause_state = true;
743         }
744 
745         bool will_be_playing =
746             toggle_pause_state ? animation->Paused() : animation->Playing();
747 
748         AnimationTimeline* timeline = existing_animation->Timeline();
749         if (!is_animation_style_change && !animation->GetIgnoreCSSTimeline()) {
750           timeline = ComputeTimeline(&element, timeline_name,
751                                      scroll_timeline_rule, timeline);
752         }
753 
754         if (keyframes_rule != existing_animation->style_rule ||
755             keyframes_rule->Version() !=
756                 existing_animation->style_rule_version ||
757             existing_animation->specified_timing != specified_timing ||
758             is_paused != was_paused || logical_property_mapping_change ||
759             timeline != existing_animation->Timeline()) {
760           DCHECK(!is_animation_style_change);
761 
762           base::Optional<TimelinePhase> inherited_phase;
763           base::Optional<double> inherited_time;
764 
765           if (timeline) {
766             inherited_phase = base::make_optional(timeline->Phase());
767             inherited_time = animation->UnlimitedCurrentTime();
768 
769             if (will_be_playing &&
770                 ((timeline != existing_animation->Timeline()) ||
771                  animation->ResetsCurrentTimeOnResume())) {
772               if (!timeline->IsMonotonicallyIncreasing())
773                 inherited_time = timeline->CurrentTimeSeconds();
774             }
775           }
776 
777           update.UpdateAnimation(
778               existing_animation_index, animation,
779               *MakeGarbageCollected<InertEffect>(
780                   CreateKeyframeEffectModel(resolver, animating_element,
781                                             element, &style, parent_style, name,
782                                             keyframe_timing_function.get(), i),
783                   timing, is_paused, inherited_time, inherited_phase),
784               specified_timing, keyframes_rule, timeline,
785               animation_data->PlayStateList());
786           if (toggle_pause_state)
787             update.ToggleAnimationIndexPaused(existing_animation_index);
788         }
789       } else {
790         DCHECK(!is_animation_style_change);
791         AnimationTimeline* timeline =
792             ComputeTimeline(&element, timeline_name, scroll_timeline_rule,
793                             nullptr /* existing_timeline */);
794         base::Optional<TimelinePhase> inherited_phase;
795         base::Optional<double> inherited_time;
796         if (timeline) {
797           if (timeline->IsMonotonicallyIncreasing()) {
798             inherited_time = 0;
799           } else {
800             inherited_phase = base::make_optional(timeline->Phase());
801             inherited_time = timeline->CurrentTimeSeconds();
802           }
803         }
804         update.StartAnimation(
805             name, name_index, i,
806             *MakeGarbageCollected<InertEffect>(
807                 CreateKeyframeEffectModel(resolver, animating_element, element,
808                                           &style, parent_style, name,
809                                           keyframe_timing_function.get(), i),
810                 timing, is_paused, inherited_time, inherited_phase),
811             specified_timing, keyframes_rule, timeline,
812             animation_data->PlayStateList());
813       }
814     }
815   }
816 
817   for (wtf_size_t i = 0; i < cancel_running_animation_flags.size(); i++) {
818     if (cancel_running_animation_flags[i]) {
819       DCHECK(css_animations && !is_animation_style_change);
820       update.CancelAnimation(
821           i, *css_animations->running_animations_[i]->animation);
822     }
823   }
824 
825   CalculateAnimationActiveInterpolations(update, animating_element);
826 }
827 
CreateEventDelegate(Element * element,const PropertyHandle & property_handle,const AnimationEffect::EventDelegate * old_event_delegate)828 AnimationEffect::EventDelegate* CSSAnimations::CreateEventDelegate(
829     Element* element,
830     const PropertyHandle& property_handle,
831     const AnimationEffect::EventDelegate* old_event_delegate) {
832   const CSSAnimations::TransitionEventDelegate* old_transition_delegate =
833       DynamicTo<CSSAnimations::TransitionEventDelegate>(old_event_delegate);
834   Timing::Phase previous_phase =
835       old_transition_delegate ? old_transition_delegate->getPreviousPhase()
836                               : Timing::kPhaseNone;
837   return MakeGarbageCollected<TransitionEventDelegate>(element, property_handle,
838                                                        previous_phase);
839 }
840 
CreateEventDelegate(Element * element,const AtomicString & animation_name,const AnimationEffect::EventDelegate * old_event_delegate)841 AnimationEffect::EventDelegate* CSSAnimations::CreateEventDelegate(
842     Element* element,
843     const AtomicString& animation_name,
844     const AnimationEffect::EventDelegate* old_event_delegate) {
845   const CSSAnimations::AnimationEventDelegate* old_animation_delegate =
846       DynamicTo<CSSAnimations::AnimationEventDelegate>(old_event_delegate);
847   Timing::Phase previous_phase =
848       old_animation_delegate ? old_animation_delegate->getPreviousPhase()
849                              : Timing::kPhaseNone;
850   base::Optional<double> previous_iteration =
851       old_animation_delegate ? old_animation_delegate->getPreviousIteration()
852                              : base::nullopt;
853   return MakeGarbageCollected<AnimationEventDelegate>(
854       element, animation_name, previous_phase, previous_iteration);
855 }
856 
SnapshotCompositorKeyframes(Element & element,CSSAnimationUpdate & update,const ComputedStyle & style,const ComputedStyle * parent_style)857 void CSSAnimations::SnapshotCompositorKeyframes(
858     Element& element,
859     CSSAnimationUpdate& update,
860     const ComputedStyle& style,
861     const ComputedStyle* parent_style) {
862   const auto& snapshot = [&element, &style,
863                           parent_style](const AnimationEffect* effect) {
864     const KeyframeEffectModelBase* keyframe_effect =
865         GetKeyframeEffectModelBase(effect);
866     if (keyframe_effect) {
867       keyframe_effect->SnapshotAllCompositorKeyframesIfNecessary(element, style,
868                                                                  parent_style);
869     }
870   };
871 
872   ElementAnimations* element_animations = element.GetElementAnimations();
873   if (element_animations) {
874     for (auto& entry : element_animations->Animations())
875       snapshot(entry.key->effect());
876   }
877 
878   for (const auto& new_animation : update.NewAnimations())
879     snapshot(new_animation.effect.Get());
880 
881   for (const auto& updated_animation : update.AnimationsWithUpdates())
882     snapshot(updated_animation.effect.Get());
883 
884   for (const auto& new_transition : update.NewTransitions())
885     snapshot(new_transition.value->effect.Get());
886 }
887 
MaybeApplyPendingUpdate(Element * element)888 void CSSAnimations::MaybeApplyPendingUpdate(Element* element) {
889   previous_active_interpolations_for_custom_animations_.clear();
890   previous_active_interpolations_for_standard_animations_.clear();
891   if (pending_update_.IsEmpty())
892     return;
893 
894   previous_active_interpolations_for_custom_animations_.swap(
895       pending_update_.ActiveInterpolationsForCustomAnimations());
896   previous_active_interpolations_for_standard_animations_.swap(
897       pending_update_.ActiveInterpolationsForStandardAnimations());
898 
899   for (wtf_size_t paused_index :
900        pending_update_.AnimationIndicesWithPauseToggled()) {
901     CSSAnimation* animation = DynamicTo<CSSAnimation>(
902         running_animations_[paused_index]->animation.Get());
903 
904     if (animation->Paused()) {
905       animation->Unpause();
906       animation->resetIgnoreCSSPlayState();
907     } else {
908       animation->pause();
909       animation->resetIgnoreCSSPlayState();
910     }
911     if (animation->Outdated())
912       animation->Update(kTimingUpdateOnDemand);
913   }
914 
915   for (const auto& animation : pending_update_.UpdatedCompositorKeyframes())
916     animation->SetCompositorPending(true);
917 
918   for (const auto& entry : pending_update_.AnimationsWithUpdates()) {
919     if (entry.animation->effect()) {
920       auto* effect = To<KeyframeEffect>(entry.animation->effect());
921       if (!effect->GetIgnoreCSSKeyframes())
922         effect->SetModel(entry.effect->Model());
923       effect->UpdateSpecifiedTiming(entry.effect->SpecifiedTiming());
924     }
925     if (entry.animation->timeline() != entry.timeline) {
926       entry.animation->setTimeline(entry.timeline);
927       To<CSSAnimation>(*entry.animation).ResetIgnoreCSSTimeline();
928     }
929 
930     running_animations_[entry.index]->Update(entry);
931   }
932 
933   const Vector<wtf_size_t>& cancelled_indices =
934       pending_update_.CancelledAnimationIndices();
935   for (wtf_size_t i = cancelled_indices.size(); i-- > 0;) {
936     DCHECK(i == cancelled_indices.size() - 1 ||
937            cancelled_indices[i] < cancelled_indices[i + 1]);
938     Animation& animation =
939         *running_animations_[cancelled_indices[i]]->animation;
940     animation.ClearOwningElement();
941     if (animation.IsCSSAnimation() &&
942         !DynamicTo<CSSAnimation>(animation)->getIgnoreCSSPlayState())
943       animation.cancel();
944     animation.Update(kTimingUpdateOnDemand);
945     running_animations_.EraseAt(cancelled_indices[i]);
946   }
947 
948   for (const auto& entry : pending_update_.NewAnimations()) {
949     const InertEffect* inert_animation = entry.effect.Get();
950     AnimationEventDelegate* event_delegate =
951         MakeGarbageCollected<AnimationEventDelegate>(element, entry.name);
952     auto* effect = MakeGarbageCollected<KeyframeEffect>(
953         element, inert_animation->Model(), inert_animation->SpecifiedTiming(),
954         KeyframeEffect::kDefaultPriority, event_delegate);
955     auto* animation = MakeGarbageCollected<CSSAnimation>(
956         element->GetExecutionContext(), entry.timeline, effect,
957         entry.position_index, entry.name);
958     animation->play();
959     if (inert_animation->Paused())
960       animation->pause();
961     animation->resetIgnoreCSSPlayState();
962     animation->Update(kTimingUpdateOnDemand);
963 
964     running_animations_.push_back(
965         MakeGarbageCollected<RunningAnimation>(animation, entry));
966   }
967 
968   // Track retargeted transitions that are running on the compositor in order
969   // to update their start times.
970   HashSet<PropertyHandle> retargeted_compositor_transitions;
971   for (const PropertyHandle& property :
972        pending_update_.CancelledTransitions()) {
973     DCHECK(transitions_.Contains(property));
974 
975     Animation* animation = transitions_.Take(property)->animation;
976     auto* effect = To<KeyframeEffect>(animation->effect());
977     if (effect && effect->HasActiveAnimationsOnCompositor(property) &&
978         pending_update_.NewTransitions().find(property) !=
979             pending_update_.NewTransitions().end() &&
980         !animation->Limited()) {
981       retargeted_compositor_transitions.insert(property);
982     }
983     animation->ClearOwningElement();
984     animation->cancel();
985     // After cancellation, transitions must be downgraded or they'll fail
986     // to be considered when retriggering themselves. This can happen if
987     // the transition is captured through getAnimations then played.
988     effect = DynamicTo<KeyframeEffect>(animation->effect());
989     if (effect)
990       effect->DowngradeToNormal();
991     animation->Update(kTimingUpdateOnDemand);
992   }
993 
994   for (const PropertyHandle& property : pending_update_.FinishedTransitions()) {
995     // This transition can also be cancelled and finished at the same time
996     if (transitions_.Contains(property)) {
997       Animation* animation = transitions_.Take(property)->animation;
998       // Transition must be downgraded
999       if (auto* effect = DynamicTo<KeyframeEffect>(animation->effect()))
1000         effect->DowngradeToNormal();
1001     }
1002   }
1003 
1004   if (!pending_update_.NewTransitions().IsEmpty()) {
1005     element->GetDocument()
1006         .GetDocumentAnimations()
1007         .IncrementTrasitionGeneration();
1008   }
1009 
1010   for (const auto& entry : pending_update_.NewTransitions()) {
1011     const CSSAnimationUpdate::NewTransition* new_transition = entry.value;
1012 
1013     RunningTransition* running_transition =
1014         MakeGarbageCollected<RunningTransition>();
1015     running_transition->from = new_transition->from;
1016     running_transition->to = new_transition->to;
1017     running_transition->reversing_adjusted_start_value =
1018         new_transition->reversing_adjusted_start_value;
1019     running_transition->reversing_shortening_factor =
1020         new_transition->reversing_shortening_factor;
1021 
1022     const PropertyHandle& property = new_transition->property;
1023     const InertEffect* inert_animation = new_transition->effect.Get();
1024     TransitionEventDelegate* event_delegate =
1025         MakeGarbageCollected<TransitionEventDelegate>(element, property);
1026 
1027     KeyframeEffectModelBase* model = inert_animation->Model();
1028 
1029     auto* transition_effect = MakeGarbageCollected<KeyframeEffect>(
1030         element, model, inert_animation->SpecifiedTiming(),
1031         KeyframeEffect::kTransitionPriority, event_delegate);
1032     auto* animation = MakeGarbageCollected<CSSTransition>(
1033         element->GetExecutionContext(), &(element->GetDocument().Timeline()),
1034         transition_effect,
1035         element->GetDocument().GetDocumentAnimations().TransitionGeneration(),
1036         property);
1037 
1038     animation->play();
1039 
1040     // Set the current time as the start time for retargeted transitions
1041     if (retargeted_compositor_transitions.Contains(property)) {
1042       animation->setStartTime(
1043           element->GetDocument().Timeline().CurrentTimeMilliseconds());
1044     }
1045     animation->Update(kTimingUpdateOnDemand);
1046     running_transition->animation = animation;
1047     transitions_.Set(property, running_transition);
1048   }
1049   ClearPendingUpdate();
1050 }
1051 
CalculateTransitionUpdateForProperty(TransitionUpdateState & state,const PropertyHandle & property,size_t transition_index)1052 void CSSAnimations::CalculateTransitionUpdateForProperty(
1053     TransitionUpdateState& state,
1054     const PropertyHandle& property,
1055     size_t transition_index) {
1056   state.listed_properties.insert(property);
1057 
1058   // FIXME: We should transition if an !important property changes even when an
1059   // animation is running, but this is a bit hard to do with the current
1060   // applyMatchedProperties system.
1061   if (property.IsCSSCustomProperty()) {
1062     if (state.update.ActiveInterpolationsForCustomAnimations().Contains(
1063             property) ||
1064         (state.animating_element->GetElementAnimations() &&
1065          state.animating_element->GetElementAnimations()
1066              ->CssAnimations()
1067              .previous_active_interpolations_for_custom_animations_.Contains(
1068                  property))) {
1069       return;
1070     }
1071   } else if (state.update.ActiveInterpolationsForStandardAnimations().Contains(
1072                  property) ||
1073              (state.animating_element->GetElementAnimations() &&
1074               state.animating_element->GetElementAnimations()
1075                   ->CssAnimations()
1076                   .previous_active_interpolations_for_standard_animations_
1077                   .Contains(property))) {
1078     return;
1079   }
1080 
1081   const RunningTransition* interrupted_transition = nullptr;
1082   if (state.active_transitions) {
1083     TransitionMap::const_iterator active_transition_iter =
1084         state.active_transitions->find(property);
1085     if (active_transition_iter != state.active_transitions->end()) {
1086       const RunningTransition* running_transition =
1087           active_transition_iter->value;
1088       if (ComputedValuesEqual(property, state.style, *running_transition->to)) {
1089         if (!state.transition_data) {
1090           if (!running_transition->animation->FinishedInternal()) {
1091             UseCounter::Count(
1092                 state.animating_element->GetDocument(),
1093                 WebFeature::kCSSTransitionCancelledByRemovingStyle);
1094           }
1095           // TODO(crbug.com/934700): Add a return to this branch to correctly
1096           // continue transitions under default settings (all 0s) in the absence
1097           // of a change in base computed style.
1098         } else {
1099           return;
1100         }
1101       }
1102       state.update.CancelTransition(property);
1103       DCHECK(!state.animating_element->GetElementAnimations() ||
1104              !state.animating_element->GetElementAnimations()
1105                   ->IsAnimationStyleChange());
1106 
1107       if (ComputedValuesEqual(
1108               property, state.style,
1109               *running_transition->reversing_adjusted_start_value)) {
1110         interrupted_transition = running_transition;
1111       }
1112     }
1113   }
1114 
1115   // In the default configutation (transition: all 0s) we continue and cancel
1116   // transitions but do not start them.
1117   if (!state.transition_data)
1118     return;
1119 
1120   const PropertyRegistry* registry =
1121       state.animating_element->GetDocument().GetPropertyRegistry();
1122   if (property.IsCSSCustomProperty()) {
1123     if (!registry || !registry->Registration(property.CustomPropertyName())) {
1124       return;
1125     }
1126   }
1127 
1128   // Lazy evaluation of the before change style. We only need to update where
1129   // we are transitioning from if the final destination is changing.
1130   if (!state.before_change_style) {
1131     ElementAnimations* element_animations =
1132         state.animating_element->GetElementAnimations();
1133     if (element_animations) {
1134       const ComputedStyle* base_style = element_animations->BaseComputedStyle();
1135       if (base_style) {
1136         state.before_change_style =
1137             CalculateBeforeChangeStyle(state.animating_element, *base_style);
1138       }
1139     }
1140     // Use the style from the previous frame if no base style is found.
1141     // Elements that have not been animated will not have a base style.
1142     // Elements that were previously animated, but where all previously running
1143     // animations have stopped may also be missing a base style. In both cases,
1144     // the old style is equivalent to the base computed style.
1145     if (!state.before_change_style) {
1146       state.before_change_style =
1147           CalculateBeforeChangeStyle(state.animating_element, state.old_style);
1148     }
1149   }
1150 
1151   if (ComputedValuesEqual(property, *state.before_change_style, state.style)) {
1152     return;
1153   }
1154 
1155   CSSInterpolationTypesMap map(registry,
1156                                state.animating_element->GetDocument());
1157   CSSInterpolationEnvironment old_environment(map, *state.before_change_style);
1158   CSSInterpolationEnvironment new_environment(map, state.style);
1159   const InterpolationType* transition_type = nullptr;
1160   InterpolationValue start = nullptr;
1161   InterpolationValue end = nullptr;
1162 
1163   for (const auto& interpolation_type : map.Get(property)) {
1164     start = interpolation_type->MaybeConvertUnderlyingValue(old_environment);
1165     if (!start) {
1166       continue;
1167     }
1168     end = interpolation_type->MaybeConvertUnderlyingValue(new_environment);
1169     if (!end) {
1170       continue;
1171     }
1172     // Merge will only succeed if the two values are considered interpolable.
1173     if (interpolation_type->MaybeMergeSingles(start.Clone(), end.Clone())) {
1174       transition_type = interpolation_type.get();
1175       break;
1176     }
1177   }
1178 
1179   // No smooth interpolation exists between these values so don't start a
1180   // transition.
1181   if (!transition_type) {
1182     return;
1183   }
1184 
1185   // If we have multiple transitions on the same property, we will use the
1186   // last one since we iterate over them in order.
1187 
1188   Timing timing = state.transition_data->ConvertToTiming(transition_index);
1189   // CSS Transitions always have a valid duration (i.e. the value 'auto' is not
1190   // supported), so iteration_duration will always be set.
1191   if (timing.start_delay + timing.iteration_duration->InSecondsF() <= 0) {
1192     // We may have started a transition in a prior CSSTransitionData update,
1193     // this CSSTransitionData update needs to override them.
1194     // TODO(alancutter): Just iterate over the CSSTransitionDatas in reverse and
1195     // skip any properties that have already been visited so we don't need to
1196     // "undo" work like this.
1197     state.update.UnstartTransition(property);
1198     return;
1199   }
1200 
1201   const ComputedStyle* reversing_adjusted_start_value =
1202       state.before_change_style.get();
1203   double reversing_shortening_factor = 1;
1204   if (interrupted_transition) {
1205     AnimationEffect* effect = interrupted_transition->animation->effect();
1206     const base::Optional<double> interrupted_progress =
1207         effect ? effect->Progress() : base::nullopt;
1208     if (interrupted_progress) {
1209       reversing_adjusted_start_value = interrupted_transition->to.get();
1210       reversing_shortening_factor =
1211           clampTo((interrupted_progress.value() *
1212                    interrupted_transition->reversing_shortening_factor) +
1213                       (1 - interrupted_transition->reversing_shortening_factor),
1214                   0.0, 1.0);
1215       timing.iteration_duration.value() *= reversing_shortening_factor;
1216       if (timing.start_delay < 0) {
1217         timing.start_delay *= reversing_shortening_factor;
1218       }
1219     }
1220   }
1221 
1222   TransitionKeyframeVector keyframes;
1223 
1224   TransitionKeyframe* start_keyframe =
1225       MakeGarbageCollected<TransitionKeyframe>(property);
1226   start_keyframe->SetValue(std::make_unique<TypedInterpolationValue>(
1227       *transition_type, start.interpolable_value->Clone(),
1228       start.non_interpolable_value));
1229   start_keyframe->SetOffset(0);
1230   keyframes.push_back(start_keyframe);
1231 
1232   TransitionKeyframe* end_keyframe =
1233       MakeGarbageCollected<TransitionKeyframe>(property);
1234   end_keyframe->SetValue(std::make_unique<TypedInterpolationValue>(
1235       *transition_type, end.interpolable_value->Clone(),
1236       end.non_interpolable_value));
1237   end_keyframe->SetOffset(1);
1238   keyframes.push_back(end_keyframe);
1239 
1240   if (property.GetCSSProperty().IsCompositableProperty()) {
1241     CompositorKeyframeValue* from = CompositorKeyframeValueFactory::Create(
1242         property, *state.before_change_style);
1243     CompositorKeyframeValue* to =
1244         CompositorKeyframeValueFactory::Create(property, state.style);
1245     start_keyframe->SetCompositorValue(from);
1246     end_keyframe->SetCompositorValue(to);
1247   }
1248 
1249   auto* model = MakeGarbageCollected<TransitionKeyframeEffectModel>(keyframes);
1250   if (!state.cloned_style) {
1251     state.cloned_style = ComputedStyle::Clone(state.style);
1252   }
1253   state.update.StartTransition(
1254       property, state.before_change_style, state.cloned_style,
1255       reversing_adjusted_start_value, reversing_shortening_factor,
1256       *MakeGarbageCollected<InertEffect>(model, timing, false, 0,
1257                                          base::nullopt));
1258   DCHECK(!state.animating_element->GetElementAnimations() ||
1259          !state.animating_element->GetElementAnimations()
1260               ->IsAnimationStyleChange());
1261 }
1262 
CalculateTransitionUpdateForCustomProperty(TransitionUpdateState & state,const CSSTransitionData::TransitionProperty & transition_property,size_t transition_index)1263 void CSSAnimations::CalculateTransitionUpdateForCustomProperty(
1264     TransitionUpdateState& state,
1265     const CSSTransitionData::TransitionProperty& transition_property,
1266     size_t transition_index) {
1267   if (transition_property.property_type !=
1268       CSSTransitionData::kTransitionUnknownProperty) {
1269     return;
1270   }
1271   if (!CSSVariableParser::IsValidVariableName(
1272           transition_property.property_string)) {
1273     return;
1274   }
1275   CalculateTransitionUpdateForProperty(
1276       state, PropertyHandle(transition_property.property_string),
1277       transition_index);
1278 }
1279 
CalculateTransitionUpdateForStandardProperty(TransitionUpdateState & state,const CSSTransitionData::TransitionProperty & transition_property,size_t transition_index,const ComputedStyle & style)1280 void CSSAnimations::CalculateTransitionUpdateForStandardProperty(
1281     TransitionUpdateState& state,
1282     const CSSTransitionData::TransitionProperty& transition_property,
1283     size_t transition_index,
1284     const ComputedStyle& style) {
1285   if (transition_property.property_type !=
1286       CSSTransitionData::kTransitionKnownProperty) {
1287     return;
1288   }
1289 
1290   CSSPropertyID resolved_id =
1291       resolveCSSPropertyID(transition_property.unresolved_property);
1292   bool animate_all = resolved_id == CSSPropertyID::kAll;
1293   const StylePropertyShorthand& property_list =
1294       animate_all ? PropertiesForTransitionAll()
1295                   : shorthandForProperty(resolved_id);
1296   // If not a shorthand we only execute one iteration of this loop, and
1297   // refer to the property directly.
1298   for (unsigned i = 0; !i || i < property_list.length(); ++i) {
1299     CSSPropertyID longhand_id =
1300         property_list.length() ? property_list.properties()[i]->PropertyID()
1301                                : resolved_id;
1302     DCHECK_GE(longhand_id, firstCSSProperty);
1303     const CSSProperty& property =
1304         CSSProperty::Get(longhand_id)
1305             .ResolveDirectionAwareProperty(style.Direction(),
1306                                            style.GetWritingMode());
1307     PropertyHandle property_handle = PropertyHandle(property);
1308 
1309     if (!animate_all && !property.IsInterpolable()) {
1310       continue;
1311     }
1312 
1313     CalculateTransitionUpdateForProperty(state, property_handle,
1314                                          transition_index);
1315   }
1316 }
1317 
CalculateTransitionUpdate(CSSAnimationUpdate & update,PropertyPass property_pass,Element * animating_element,const ComputedStyle & style)1318 void CSSAnimations::CalculateTransitionUpdate(CSSAnimationUpdate& update,
1319                                               PropertyPass property_pass,
1320                                               Element* animating_element,
1321                                               const ComputedStyle& style) {
1322   if (!animating_element)
1323     return;
1324 
1325   if (animating_element->GetDocument().FinishingOrIsPrinting())
1326     return;
1327 
1328   ElementAnimations* element_animations =
1329       animating_element->GetElementAnimations();
1330   const TransitionMap* active_transitions =
1331       element_animations ? &element_animations->CssAnimations().transitions_
1332                          : nullptr;
1333   const CSSTransitionData* transition_data = style.Transitions();
1334 
1335   const bool animation_style_recalc =
1336       element_animations && element_animations->IsAnimationStyleChange();
1337 
1338   HashSet<PropertyHandle> listed_properties;
1339   bool any_transition_had_transition_all = false;
1340   const ComputedStyle* old_style = animating_element->GetComputedStyle();
1341   if (!animation_style_recalc && style.Display() != EDisplay::kNone &&
1342       old_style && !old_style->IsEnsuredInDisplayNone()) {
1343     TransitionUpdateState state = {update,
1344                                    animating_element,
1345                                    *old_style,
1346                                    style,
1347                                    /*before_change_style=*/nullptr,
1348                                    /*cloned_style=*/nullptr,
1349                                    active_transitions,
1350                                    listed_properties,
1351                                    transition_data};
1352 
1353     if (transition_data) {
1354       for (wtf_size_t transition_index = 0;
1355            transition_index < transition_data->PropertyList().size();
1356            ++transition_index) {
1357         const CSSTransitionData::TransitionProperty& transition_property =
1358             transition_data->PropertyList()[transition_index];
1359         if (transition_property.unresolved_property == CSSPropertyID::kAll) {
1360           any_transition_had_transition_all = true;
1361         }
1362         if (property_pass == PropertyPass::kCustom) {
1363           CalculateTransitionUpdateForCustomProperty(state, transition_property,
1364                                                      transition_index);
1365         } else {
1366           DCHECK_EQ(property_pass, PropertyPass::kStandard);
1367           CalculateTransitionUpdateForStandardProperty(
1368               state, transition_property, transition_index, style);
1369         }
1370       }
1371     } else if (active_transitions && active_transitions->size()) {
1372       // !transition_data implies transition: all 0s
1373       any_transition_had_transition_all = true;
1374       if (property_pass == PropertyPass::kStandard) {
1375         CSSTransitionData::TransitionProperty default_property(
1376             CSSPropertyID::kAll);
1377         CalculateTransitionUpdateForStandardProperty(state, default_property, 0,
1378                                                      style);
1379       }
1380     }
1381   }
1382 
1383   if (active_transitions) {
1384     for (const auto& entry : *active_transitions) {
1385       const PropertyHandle& property = entry.key;
1386       if (property.IsCSSCustomProperty() !=
1387           (property_pass == PropertyPass::kCustom)) {
1388         continue;
1389       }
1390       if (!any_transition_had_transition_all && !animation_style_recalc &&
1391           !listed_properties.Contains(property)) {
1392         update.CancelTransition(property);
1393       } else if (entry.value->animation->FinishedInternal()) {
1394         update.FinishTransition(property);
1395       }
1396     }
1397   }
1398 
1399   CalculateTransitionActiveInterpolations(update, property_pass,
1400                                           animating_element);
1401 }
1402 
CalculateBeforeChangeStyle(Element * animating_element,const ComputedStyle & base_style)1403 scoped_refptr<const ComputedStyle> CSSAnimations::CalculateBeforeChangeStyle(
1404     Element* animating_element,
1405     const ComputedStyle& base_style) {
1406   ActiveInterpolationsMap interpolations_map;
1407   ElementAnimations* element_animations =
1408       animating_element->GetElementAnimations();
1409   if (element_animations) {
1410     const TransitionMap& transition_map =
1411         element_animations->CssAnimations().transitions_;
1412 
1413     // Assemble list of animations in composite ordering.
1414     // TODO(crbug.com/1082401): Per spec, the before change style should include
1415     // all declarative animations. Currently, only including transitions.
1416     HeapVector<Member<Animation>> animations;
1417     for (const auto& entry : transition_map) {
1418       RunningTransition* transition = entry.value;
1419       Animation* animation = transition->animation;
1420       animations.push_back(animation);
1421     }
1422     std::sort(animations.begin(), animations.end(),
1423               [](Animation* a, Animation* b) {
1424                 return Animation::HasLowerCompositeOrdering(
1425                     a, b, Animation::CompareAnimationsOrdering::kPointerOrder);
1426               });
1427 
1428     // Sample animations and add to the interpolations map.
1429     for (Animation* animation : animations) {
1430       base::Optional<double> current_time = animation->currentTime();
1431       if (!current_time)
1432         continue;
1433 
1434       auto* effect = DynamicTo<KeyframeEffect>(animation->effect());
1435       if (!effect)
1436         continue;
1437 
1438       auto* inert_animation_for_sampling = MakeGarbageCollected<InertEffect>(
1439           effect->Model(), effect->SpecifiedTiming(), false,
1440           current_time.value() / 1000, base::nullopt);
1441 
1442       HeapVector<Member<Interpolation>> sample;
1443       inert_animation_for_sampling->Sample(sample);
1444 
1445       for (const auto& interpolation : sample) {
1446         PropertyHandle handle = interpolation->GetProperty();
1447         auto interpolation_map_entry = interpolations_map.insert(
1448             handle, MakeGarbageCollected<ActiveInterpolations>());
1449         auto& active_interpolations =
1450             *interpolation_map_entry.stored_value->value;
1451         if (!interpolation->DependsOnUnderlyingValue())
1452           active_interpolations.clear();
1453         active_interpolations.push_back(interpolation);
1454       }
1455     }
1456   }
1457 
1458   StyleResolver& resolver = animating_element->GetDocument().GetStyleResolver();
1459   return resolver.BeforeChangeStyleForTransitionUpdate(
1460       *animating_element, base_style, interpolations_map);
1461 }
1462 
Cancel()1463 void CSSAnimations::Cancel() {
1464   for (const auto& running_animation : running_animations_) {
1465     running_animation->animation->cancel();
1466     running_animation->animation->Update(kTimingUpdateOnDemand);
1467   }
1468 
1469   for (const auto& entry : transitions_) {
1470     entry.value->animation->cancel();
1471     entry.value->animation->Update(kTimingUpdateOnDemand);
1472   }
1473 
1474   running_animations_.clear();
1475   transitions_.clear();
1476   ClearPendingUpdate();
1477 }
1478 
1479 namespace {
1480 
IsCustomPropertyHandle(const PropertyHandle & property)1481 bool IsCustomPropertyHandle(const PropertyHandle& property) {
1482   return property.IsCSSCustomProperty();
1483 }
1484 
IsFontAffectingPropertyHandle(const PropertyHandle & property)1485 bool IsFontAffectingPropertyHandle(const PropertyHandle& property) {
1486   if (property.IsCSSCustomProperty() || !property.IsCSSProperty())
1487     return false;
1488   return property.GetCSSProperty().AffectsFont();
1489 }
1490 
1491 // TODO(alancutter): CSS properties and presentation attributes may have
1492 // identical effects. By grouping them in the same set we introduce a bug where
1493 // arbitrary hash iteration will determine the order the apply in and thus which
1494 // one "wins". We should be more deliberate about the order of application in
1495 // the case of effect collisions.
1496 // Example: Both 'color' and 'svg-color' set the color on ComputedStyle but are
1497 // considered distinct properties in the ActiveInterpolationsMap.
IsStandardPropertyHandle(const PropertyHandle & property)1498 bool IsStandardPropertyHandle(const PropertyHandle& property) {
1499   return (property.IsCSSProperty() && !property.IsCSSCustomProperty()) ||
1500          property.IsPresentationAttribute();
1501 }
1502 
AdoptActiveAnimationInterpolations(EffectStack * effect_stack,CSSAnimationUpdate & update,const HeapVector<Member<const InertEffect>> * new_animations,const HeapHashSet<Member<const Animation>> * suppressed_animations)1503 void AdoptActiveAnimationInterpolations(
1504     EffectStack* effect_stack,
1505     CSSAnimationUpdate& update,
1506     const HeapVector<Member<const InertEffect>>* new_animations,
1507     const HeapHashSet<Member<const Animation>>* suppressed_animations) {
1508   ActiveInterpolationsMap custom_interpolations(
1509       EffectStack::ActiveInterpolations(
1510           effect_stack, new_animations, suppressed_animations,
1511           KeyframeEffect::kDefaultPriority, IsCustomPropertyHandle));
1512   update.AdoptActiveInterpolationsForCustomAnimations(custom_interpolations);
1513 
1514   ActiveInterpolationsMap standard_interpolations(
1515       EffectStack::ActiveInterpolations(
1516           effect_stack, new_animations, suppressed_animations,
1517           KeyframeEffect::kDefaultPriority, IsStandardPropertyHandle));
1518   update.AdoptActiveInterpolationsForStandardAnimations(
1519       standard_interpolations);
1520 }
1521 
1522 }  // namespace
1523 
CalculateAnimationActiveInterpolations(CSSAnimationUpdate & update,const Element * animating_element)1524 void CSSAnimations::CalculateAnimationActiveInterpolations(
1525     CSSAnimationUpdate& update,
1526     const Element* animating_element) {
1527   ElementAnimations* element_animations =
1528       animating_element ? animating_element->GetElementAnimations() : nullptr;
1529   EffectStack* effect_stack =
1530       element_animations ? &element_animations->GetEffectStack() : nullptr;
1531 
1532   if (update.NewAnimations().IsEmpty() &&
1533       update.SuppressedAnimations().IsEmpty()) {
1534     AdoptActiveAnimationInterpolations(effect_stack, update, nullptr, nullptr);
1535     return;
1536   }
1537 
1538   HeapVector<Member<const InertEffect>> new_effects;
1539   for (const auto& new_animation : update.NewAnimations())
1540     new_effects.push_back(new_animation.effect);
1541 
1542   // Animations with updates use a temporary InertEffect for the current frame.
1543   for (const auto& updated_animation : update.AnimationsWithUpdates())
1544     new_effects.push_back(updated_animation.effect);
1545 
1546   AdoptActiveAnimationInterpolations(effect_stack, update, &new_effects,
1547                                      &update.SuppressedAnimations());
1548 }
1549 
1550 namespace {
1551 
PropertyFilter(CSSAnimations::PropertyPass property_pass)1552 EffectStack::PropertyHandleFilter PropertyFilter(
1553     CSSAnimations::PropertyPass property_pass) {
1554   if (property_pass == CSSAnimations::PropertyPass::kCustom) {
1555     return IsCustomPropertyHandle;
1556   }
1557   DCHECK_EQ(property_pass, CSSAnimations::PropertyPass::kStandard);
1558   return IsStandardPropertyHandle;
1559 }
1560 
1561 }  // namespace
1562 
CalculateTransitionActiveInterpolations(CSSAnimationUpdate & update,PropertyPass property_pass,const Element * animating_element)1563 void CSSAnimations::CalculateTransitionActiveInterpolations(
1564     CSSAnimationUpdate& update,
1565     PropertyPass property_pass,
1566     const Element* animating_element) {
1567   ElementAnimations* element_animations =
1568       animating_element ? animating_element->GetElementAnimations() : nullptr;
1569   EffectStack* effect_stack =
1570       element_animations ? &element_animations->GetEffectStack() : nullptr;
1571 
1572   ActiveInterpolationsMap active_interpolations_for_transitions;
1573   if (update.NewTransitions().IsEmpty() &&
1574       update.CancelledTransitions().IsEmpty()) {
1575     active_interpolations_for_transitions = EffectStack::ActiveInterpolations(
1576         effect_stack, nullptr, nullptr, KeyframeEffect::kTransitionPriority,
1577         PropertyFilter(property_pass));
1578   } else {
1579     HeapVector<Member<const InertEffect>> new_transitions;
1580     for (const auto& entry : update.NewTransitions())
1581       new_transitions.push_back(entry.value->effect.Get());
1582 
1583     HeapHashSet<Member<const Animation>> cancelled_animations;
1584     if (!update.CancelledTransitions().IsEmpty()) {
1585       DCHECK(element_animations);
1586       const TransitionMap& transition_map =
1587           element_animations->CssAnimations().transitions_;
1588       for (const PropertyHandle& property : update.CancelledTransitions()) {
1589         DCHECK(transition_map.Contains(property));
1590         cancelled_animations.insert(
1591             transition_map.at(property)->animation.Get());
1592       }
1593     }
1594 
1595     active_interpolations_for_transitions = EffectStack::ActiveInterpolations(
1596         effect_stack, &new_transitions, &cancelled_animations,
1597         KeyframeEffect::kTransitionPriority, PropertyFilter(property_pass));
1598   }
1599 
1600   const ActiveInterpolationsMap& animations =
1601       property_pass == PropertyPass::kCustom
1602           ? update.ActiveInterpolationsForCustomAnimations()
1603           : update.ActiveInterpolationsForStandardAnimations();
1604   // Properties being animated by animations don't get values from transitions
1605   // applied.
1606   if (!animations.IsEmpty() &&
1607       !active_interpolations_for_transitions.IsEmpty()) {
1608     for (const auto& entry : animations)
1609       active_interpolations_for_transitions.erase(entry.key);
1610   }
1611 
1612   if (property_pass == PropertyPass::kCustom) {
1613     update.AdoptActiveInterpolationsForCustomTransitions(
1614         active_interpolations_for_transitions);
1615   } else {
1616     DCHECK_EQ(property_pass, PropertyPass::kStandard);
1617     update.AdoptActiveInterpolationsForStandardTransitions(
1618         active_interpolations_for_transitions);
1619   }
1620 }
1621 
GetEventTarget() const1622 EventTarget* CSSAnimations::AnimationEventDelegate::GetEventTarget() const {
1623   return &EventPath::EventTargetRespectingTargetRules(*animation_target_);
1624 }
1625 
MaybeDispatch(Document::ListenerType listener_type,const AtomicString & event_name,const AnimationTimeDelta & elapsed_time)1626 void CSSAnimations::AnimationEventDelegate::MaybeDispatch(
1627     Document::ListenerType listener_type,
1628     const AtomicString& event_name,
1629     const AnimationTimeDelta& elapsed_time) {
1630   if (animation_target_->GetDocument().HasListenerType(listener_type)) {
1631     String pseudo_element_name = PseudoElement::PseudoElementNameForEvents(
1632         animation_target_->GetPseudoId());
1633     AnimationEvent* event = AnimationEvent::Create(
1634         event_name, name_, elapsed_time, pseudo_element_name);
1635     event->SetTarget(GetEventTarget());
1636     GetDocument().EnqueueAnimationFrameEvent(event);
1637   }
1638 }
1639 
RequiresIterationEvents(const AnimationEffect & animation_node)1640 bool CSSAnimations::AnimationEventDelegate::RequiresIterationEvents(
1641     const AnimationEffect& animation_node) {
1642   return GetDocument().HasListenerType(Document::kAnimationIterationListener);
1643 }
1644 
OnEventCondition(const AnimationEffect & animation_node,Timing::Phase current_phase)1645 void CSSAnimations::AnimationEventDelegate::OnEventCondition(
1646     const AnimationEffect& animation_node,
1647     Timing::Phase current_phase) {
1648   const base::Optional<double> current_iteration =
1649       animation_node.CurrentIteration();
1650 
1651   // See http://drafts.csswg.org/css-animations-2/#event-dispatch
1652   // When multiple events are dispatched for a single phase transition,
1653   // the animationstart event is to be dispatched before the animationend
1654   // event.
1655 
1656   // The following phase transitions trigger an animationstart event:
1657   //   idle or before --> active or after
1658   //   after --> active or before
1659   const bool phase_change = previous_phase_ != current_phase;
1660   const bool was_idle_or_before = (previous_phase_ == Timing::kPhaseNone ||
1661                                    previous_phase_ == Timing::kPhaseBefore);
1662   const bool is_active_or_after = (current_phase == Timing::kPhaseActive ||
1663                                    current_phase == Timing::kPhaseAfter);
1664   const bool is_active_or_before = (current_phase == Timing::kPhaseActive ||
1665                                     current_phase == Timing::kPhaseBefore);
1666   const bool was_after = (previous_phase_ == Timing::kPhaseAfter);
1667   if (phase_change && ((was_idle_or_before && is_active_or_after) ||
1668                        (was_after && is_active_or_before))) {
1669     AnimationTimeDelta elapsed_time =
1670         was_after ? IntervalEnd(animation_node) : IntervalStart(animation_node);
1671     MaybeDispatch(Document::kAnimationStartListener,
1672                   event_type_names::kAnimationstart, elapsed_time);
1673   }
1674 
1675   // The following phase transitions trigger an animationend event:
1676   //   idle, before or active--> after
1677   //   active or after--> before
1678   const bool was_active_or_after = (previous_phase_ == Timing::kPhaseActive ||
1679                                     previous_phase_ == Timing::kPhaseAfter);
1680   const bool is_after = (current_phase == Timing::kPhaseAfter);
1681   const bool is_before = (current_phase == Timing::kPhaseBefore);
1682   if (phase_change && (is_after || (was_active_or_after && is_before))) {
1683     AnimationTimeDelta elapsed_time =
1684         is_after ? IntervalEnd(animation_node) : IntervalStart(animation_node);
1685     MaybeDispatch(Document::kAnimationEndListener,
1686                   event_type_names::kAnimationend, elapsed_time);
1687   }
1688 
1689   // The following phase transitions trigger an animationcalcel event:
1690   //   not idle and not after --> idle
1691   if (phase_change && current_phase == Timing::kPhaseNone &&
1692       previous_phase_ != Timing::kPhaseAfter) {
1693     // TODO(crbug.com/1059968): Determine if animation direction or playback
1694     // rate factor into the calculation of the elapsed time.
1695     double cancel_time = animation_node.GetCancelTime();
1696     MaybeDispatch(Document::kAnimationCancelListener,
1697                   event_type_names::kAnimationcancel,
1698                   AnimationTimeDelta::FromSecondsD(cancel_time));
1699   }
1700 
1701   if (!phase_change && current_phase == Timing::kPhaseActive &&
1702       previous_iteration_ != current_iteration) {
1703     // We fire only a single event for all iterations that terminate
1704     // between a single pair of samples. See http://crbug.com/275263. For
1705     // compatibility with the existing implementation, this event uses
1706     // the elapsedTime for the first iteration in question.
1707     DCHECK(previous_iteration_ && current_iteration);
1708     const AnimationTimeDelta elapsed_time =
1709         IterationElapsedTime(animation_node, previous_iteration_.value());
1710     MaybeDispatch(Document::kAnimationIterationListener,
1711                   event_type_names::kAnimationiteration, elapsed_time);
1712   }
1713 
1714   previous_iteration_ = current_iteration;
1715   previous_phase_ = current_phase;
1716 }
1717 
Trace(Visitor * visitor) const1718 void CSSAnimations::AnimationEventDelegate::Trace(Visitor* visitor) const {
1719   visitor->Trace(animation_target_);
1720   AnimationEffect::EventDelegate::Trace(visitor);
1721 }
1722 
GetEventTarget() const1723 EventTarget* CSSAnimations::TransitionEventDelegate::GetEventTarget() const {
1724   return &EventPath::EventTargetRespectingTargetRules(*transition_target_);
1725 }
1726 
OnEventCondition(const AnimationEffect & animation_node,Timing::Phase current_phase)1727 void CSSAnimations::TransitionEventDelegate::OnEventCondition(
1728     const AnimationEffect& animation_node,
1729     Timing::Phase current_phase) {
1730   if (current_phase == previous_phase_)
1731     return;
1732 
1733   if (GetDocument().HasListenerType(Document::kTransitionRunListener)) {
1734     if (previous_phase_ == Timing::kPhaseNone) {
1735       EnqueueEvent(
1736           event_type_names::kTransitionrun,
1737           StartTimeFromDelay(animation_node.SpecifiedTiming().start_delay));
1738     }
1739   }
1740 
1741   if (GetDocument().HasListenerType(Document::kTransitionStartListener)) {
1742     if ((current_phase == Timing::kPhaseActive ||
1743          current_phase == Timing::kPhaseAfter) &&
1744         (previous_phase_ == Timing::kPhaseNone ||
1745          previous_phase_ == Timing::kPhaseBefore)) {
1746       EnqueueEvent(
1747           event_type_names::kTransitionstart,
1748           StartTimeFromDelay(animation_node.SpecifiedTiming().start_delay));
1749     } else if ((current_phase == Timing::kPhaseActive ||
1750                 current_phase == Timing::kPhaseBefore) &&
1751                previous_phase_ == Timing::kPhaseAfter) {
1752       // If the transition is progressing backwards it is considered to have
1753       // started at the end position.
1754       EnqueueEvent(event_type_names::kTransitionstart,
1755                    animation_node.SpecifiedTiming().IterationDuration());
1756     }
1757   }
1758 
1759   if (GetDocument().HasListenerType(Document::kTransitionEndListener)) {
1760     if (current_phase == Timing::kPhaseAfter &&
1761         (previous_phase_ == Timing::kPhaseActive ||
1762          previous_phase_ == Timing::kPhaseBefore ||
1763          previous_phase_ == Timing::kPhaseNone)) {
1764       EnqueueEvent(event_type_names::kTransitionend,
1765                    animation_node.SpecifiedTiming().IterationDuration());
1766     } else if (current_phase == Timing::kPhaseBefore &&
1767                (previous_phase_ == Timing::kPhaseActive ||
1768                 previous_phase_ == Timing::kPhaseAfter)) {
1769       // If the transition is progressing backwards it is considered to have
1770       // ended at the start position.
1771       EnqueueEvent(
1772           event_type_names::kTransitionend,
1773           StartTimeFromDelay(animation_node.SpecifiedTiming().start_delay));
1774     }
1775   }
1776 
1777   if (GetDocument().HasListenerType(Document::kTransitionCancelListener)) {
1778     if (current_phase == Timing::kPhaseNone &&
1779         previous_phase_ != Timing::kPhaseAfter) {
1780       // Per the css-transitions-2 spec, transitioncancel is fired with the
1781       // "active time of the animation at the moment it was cancelled,
1782       // calculated using a fill mode of both".
1783       base::Optional<AnimationTimeDelta> cancel_active_time =
1784           CalculateActiveTime(animation_node.SpecifiedTiming().ActiveDuration(),
1785                               Timing::FillMode::BOTH,
1786                               animation_node.LocalTime(), previous_phase_,
1787                               animation_node.SpecifiedTiming());
1788       // Being the FillMode::BOTH the only possibility to get a null
1789       // cancel_active_time is that previous_phase_ is kPhaseNone. This cannot
1790       // happen because we know that current_phase == kPhaseNone and
1791       // current_phase != previous_phase_ (see early return at the beginning).
1792       DCHECK(cancel_active_time);
1793       EnqueueEvent(event_type_names::kTransitioncancel,
1794                    cancel_active_time.value());
1795     }
1796   }
1797 
1798   previous_phase_ = current_phase;
1799 }
1800 
EnqueueEvent(const WTF::AtomicString & type,const AnimationTimeDelta & elapsed_time)1801 void CSSAnimations::TransitionEventDelegate::EnqueueEvent(
1802     const WTF::AtomicString& type,
1803     const AnimationTimeDelta& elapsed_time) {
1804   String property_name =
1805       property_.IsCSSCustomProperty()
1806           ? property_.CustomPropertyName()
1807           : property_.GetCSSProperty().GetPropertyNameString();
1808   String pseudo_element =
1809       PseudoElement::PseudoElementNameForEvents(GetPseudoId());
1810   TransitionEvent* event = TransitionEvent::Create(
1811       type, property_name, elapsed_time, pseudo_element);
1812   event->SetTarget(GetEventTarget());
1813   GetDocument().EnqueueAnimationFrameEvent(event);
1814 }
1815 
Trace(Visitor * visitor) const1816 void CSSAnimations::TransitionEventDelegate::Trace(Visitor* visitor) const {
1817   visitor->Trace(transition_target_);
1818   AnimationEffect::EventDelegate::Trace(visitor);
1819 }
1820 
PropertiesForTransitionAll()1821 const StylePropertyShorthand& CSSAnimations::PropertiesForTransitionAll() {
1822   DEFINE_STATIC_LOCAL(Vector<const CSSProperty*>, properties, ());
1823   DEFINE_STATIC_LOCAL(StylePropertyShorthand, property_shorthand, ());
1824   if (properties.IsEmpty()) {
1825     for (CSSPropertyID id : CSSPropertyIDList()) {
1826       // Avoid creating overlapping transitions with perspective-origin and
1827       // transition-origin.
1828       if (id == CSSPropertyID::kWebkitPerspectiveOriginX ||
1829           id == CSSPropertyID::kWebkitPerspectiveOriginY ||
1830           id == CSSPropertyID::kWebkitTransformOriginX ||
1831           id == CSSPropertyID::kWebkitTransformOriginY ||
1832           id == CSSPropertyID::kWebkitTransformOriginZ)
1833         continue;
1834       const CSSProperty& property = CSSProperty::Get(id);
1835       if (property.IsInterpolable())
1836         properties.push_back(&property);
1837     }
1838     property_shorthand = StylePropertyShorthand(
1839         CSSPropertyID::kInvalid, properties.begin(), properties.size());
1840   }
1841   return property_shorthand;
1842 }
1843 
1844 // Properties that affect animations are not allowed to be affected by
1845 // animations. https://drafts.csswg.org/web-animations/#not-animatable-section
IsAnimationAffectingProperty(const CSSProperty & property)1846 bool CSSAnimations::IsAnimationAffectingProperty(const CSSProperty& property) {
1847   switch (property.PropertyID()) {
1848     case CSSPropertyID::kAnimation:
1849     case CSSPropertyID::kAnimationDelay:
1850     case CSSPropertyID::kAnimationDirection:
1851     case CSSPropertyID::kAnimationDuration:
1852     case CSSPropertyID::kAnimationFillMode:
1853     case CSSPropertyID::kAnimationIterationCount:
1854     case CSSPropertyID::kAnimationName:
1855     case CSSPropertyID::kAnimationPlayState:
1856     case CSSPropertyID::kAnimationTimeline:
1857     case CSSPropertyID::kAnimationTimingFunction:
1858     case CSSPropertyID::kContentVisibility:
1859     case CSSPropertyID::kContain:
1860     case CSSPropertyID::kDirection:
1861     case CSSPropertyID::kDisplay:
1862     case CSSPropertyID::kTextCombineUpright:
1863     case CSSPropertyID::kTextOrientation:
1864     case CSSPropertyID::kTransition:
1865     case CSSPropertyID::kTransitionDelay:
1866     case CSSPropertyID::kTransitionDuration:
1867     case CSSPropertyID::kTransitionProperty:
1868     case CSSPropertyID::kTransitionTimingFunction:
1869     case CSSPropertyID::kUnicodeBidi:
1870     case CSSPropertyID::kWebkitWritingMode:
1871     case CSSPropertyID::kWillChange:
1872     case CSSPropertyID::kWritingMode:
1873       return true;
1874     default:
1875       return false;
1876   }
1877 }
1878 
IsAffectedByKeyframesFromScope(const Element & element,const TreeScope & tree_scope)1879 bool CSSAnimations::IsAffectedByKeyframesFromScope(
1880     const Element& element,
1881     const TreeScope& tree_scope) {
1882   // Animated elements are affected by @keyframes rules from the same scope
1883   // and from their shadow sub-trees if they are shadow hosts.
1884   if (element.GetTreeScope() == tree_scope)
1885     return true;
1886   if (!IsShadowHost(element))
1887     return false;
1888   if (tree_scope.RootNode() == tree_scope.GetDocument())
1889     return false;
1890   return To<ShadowRoot>(tree_scope.RootNode()).host() == element;
1891 }
1892 
IsAnimatingCustomProperties(const ElementAnimations * element_animations)1893 bool CSSAnimations::IsAnimatingCustomProperties(
1894     const ElementAnimations* element_animations) {
1895   return element_animations &&
1896          element_animations->GetEffectStack().AffectsProperties(
1897              IsCustomPropertyHandle);
1898 }
1899 
IsAnimatingStandardProperties(const ElementAnimations * element_animations,const CSSBitset * bitset,KeyframeEffect::Priority priority)1900 bool CSSAnimations::IsAnimatingStandardProperties(
1901     const ElementAnimations* element_animations,
1902     const CSSBitset* bitset,
1903     KeyframeEffect::Priority priority) {
1904   if (!element_animations || !bitset)
1905     return false;
1906   return element_animations->GetEffectStack().AffectsProperties(*bitset,
1907                                                                 priority);
1908 }
1909 
IsAnimatingFontAffectingProperties(const ElementAnimations * element_animations)1910 bool CSSAnimations::IsAnimatingFontAffectingProperties(
1911     const ElementAnimations* element_animations) {
1912   return element_animations &&
1913          element_animations->GetEffectStack().AffectsProperties(
1914              IsFontAffectingPropertyHandle);
1915 }
1916 
IsAnimatingRevert(const ElementAnimations * element_animations)1917 bool CSSAnimations::IsAnimatingRevert(
1918     const ElementAnimations* element_animations) {
1919   return element_animations && element_animations->GetEffectStack().HasRevert();
1920 }
1921 
Trace(Visitor * visitor) const1922 void CSSAnimations::Trace(Visitor* visitor) const {
1923   visitor->Trace(transitions_);
1924   visitor->Trace(pending_update_);
1925   visitor->Trace(running_animations_);
1926   visitor->Trace(previous_active_interpolations_for_standard_animations_);
1927   visitor->Trace(previous_active_interpolations_for_custom_animations_);
1928 }
1929 
1930 }  // namespace blink
1931