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/keyframe_effect_model.h"
32 
33 #include <limits>
34 #include <utility>
35 
36 #include "third_party/blink/renderer/core/animation/animation_effect.h"
37 #include "third_party/blink/renderer/core/animation/compositor_animations.h"
38 #include "third_party/blink/renderer/core/css/css_property_equality.h"
39 #include "third_party/blink/renderer/core/css/property_registry.h"
40 #include "third_party/blink/renderer/core/css/resolver/style_resolver.h"
41 #include "third_party/blink/renderer/core/dom/document.h"
42 #include "third_party/blink/renderer/core/frame/web_feature.h"
43 #include "third_party/blink/renderer/core/style/computed_style.h"
44 #include "third_party/blink/renderer/platform/animation/animation_utilities.h"
45 #include "third_party/blink/renderer/platform/geometry/float_box.h"
46 #include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
47 #include "third_party/blink/renderer/platform/transforms/transformation_matrix.h"
48 #include "third_party/blink/renderer/platform/wtf/text/string_hash.h"
49 
50 namespace blink {
51 
Properties() const52 PropertyHandleSet KeyframeEffectModelBase::Properties() const {
53   PropertyHandleSet result;
54   for (const auto& keyframe : keyframes_) {
55     for (const auto& property : keyframe->Properties())
56       result.insert(property);
57   }
58   return result;
59 }
60 
61 template <class K>
SetFrames(HeapVector<K> & keyframes)62 void KeyframeEffectModelBase::SetFrames(HeapVector<K>& keyframes) {
63   // TODO(samli): Should also notify/invalidate the animation
64   keyframes_.clear();
65   keyframes_.AppendVector(keyframes);
66   ClearCachedData();
67 }
68 
69 template CORE_EXPORT void KeyframeEffectModelBase::SetFrames(
70     HeapVector<Member<Keyframe>>& keyframes);
71 template CORE_EXPORT void KeyframeEffectModelBase::SetFrames(
72     HeapVector<Member<StringKeyframe>>& keyframes);
73 
SetComposite(CompositeOperation composite)74 void KeyframeEffectModelBase::SetComposite(CompositeOperation composite) {
75   composite_ = composite;
76   ClearCachedData();
77 }
78 
Sample(int iteration,double fraction,AnimationTimeDelta iteration_duration,HeapVector<Member<Interpolation>> & result) const79 bool KeyframeEffectModelBase::Sample(
80     int iteration,
81     double fraction,
82     AnimationTimeDelta iteration_duration,
83     HeapVector<Member<Interpolation>>& result) const {
84   DCHECK_GE(iteration, 0);
85   EnsureKeyframeGroups();
86   EnsureInterpolationEffectPopulated();
87 
88   bool changed = iteration != last_iteration_ || fraction != last_fraction_ ||
89                  iteration_duration != last_iteration_duration_;
90   last_iteration_ = iteration;
91   last_fraction_ = fraction;
92   last_iteration_duration_ = iteration_duration;
93   interpolation_effect_->GetActiveInterpolations(fraction, result);
94   return changed;
95 }
96 
97 namespace {
98 
99 static const size_t num_compositable_properties = 7;
100 
CompositableProperties()101 const CSSProperty** CompositableProperties() {
102   static const CSSProperty*
103       kCompositableProperties[num_compositable_properties] = {
104           &GetCSSPropertyOpacity(),       &GetCSSPropertyRotate(),
105           &GetCSSPropertyScale(),         &GetCSSPropertyTransform(),
106           &GetCSSPropertyTranslate(),     &GetCSSPropertyFilter(),
107           &GetCSSPropertyBackdropFilter()};
108   return kCompositableProperties;
109 }
110 
111 }  // namespace
112 
SnapshotNeutralCompositorKeyframes(Element & element,const ComputedStyle & old_style,const ComputedStyle & new_style,const ComputedStyle * parent_style) const113 bool KeyframeEffectModelBase::SnapshotNeutralCompositorKeyframes(
114     Element& element,
115     const ComputedStyle& old_style,
116     const ComputedStyle& new_style,
117     const ComputedStyle* parent_style) const {
118   ShouldSnapshotPropertyCallback should_snapshot_property_callback =
119       [&old_style, &new_style](const PropertyHandle& property) {
120         return !CSSPropertyEquality::PropertiesEqual(property, old_style,
121                                                      new_style);
122       };
123   ShouldSnapshotKeyframeCallback should_snapshot_keyframe_callback =
124       [](const PropertySpecificKeyframe& keyframe) {
125         return keyframe.IsNeutral();
126       };
127 
128   return SnapshotCompositableProperties(element, new_style, parent_style,
129                                         should_snapshot_property_callback,
130                                         should_snapshot_keyframe_callback);
131 }
132 
SnapshotAllCompositorKeyframesIfNecessary(Element & element,const ComputedStyle & base_style,const ComputedStyle * parent_style) const133 bool KeyframeEffectModelBase::SnapshotAllCompositorKeyframesIfNecessary(
134     Element& element,
135     const ComputedStyle& base_style,
136     const ComputedStyle* parent_style) const {
137   if (!needs_compositor_keyframes_snapshot_)
138     return false;
139   needs_compositor_keyframes_snapshot_ = false;
140 
141   bool has_neutral_compositable_keyframe = false;
142   ShouldSnapshotPropertyCallback should_snapshot_property_callback =
143       [](const PropertyHandle& property) { return true; };
144   ShouldSnapshotKeyframeCallback should_snapshot_keyframe_callback =
145       [&has_neutral_compositable_keyframe](
146           const PropertySpecificKeyframe& keyframe) mutable {
147         has_neutral_compositable_keyframe |= keyframe.IsNeutral();
148         return true;
149       };
150 
151   bool updated = SnapshotCompositableProperties(
152       element, base_style, parent_style, should_snapshot_property_callback,
153       should_snapshot_keyframe_callback);
154 
155   if (updated && has_neutral_compositable_keyframe) {
156     UseCounter::Count(element.GetDocument(),
157                       WebFeature::kSyntheticKeyframesInCompositedCSSAnimation);
158   }
159   return updated;
160 }
161 
SnapshotCompositableProperties(Element & element,const ComputedStyle & computed_style,const ComputedStyle * parent_style,ShouldSnapshotPropertyCallback should_snapshot_property_callback,ShouldSnapshotKeyframeCallback should_snapshot_keyframe_callback) const162 bool KeyframeEffectModelBase::SnapshotCompositableProperties(
163     Element& element,
164     const ComputedStyle& computed_style,
165     const ComputedStyle* parent_style,
166     ShouldSnapshotPropertyCallback should_snapshot_property_callback,
167     ShouldSnapshotKeyframeCallback should_snapshot_keyframe_callback) const {
168   EnsureKeyframeGroups();
169   bool updated = false;
170   static const CSSProperty** compositable_properties = CompositableProperties();
171   for (size_t i = 0; i < num_compositable_properties; i++) {
172     updated |= SnapshotCompositorKeyFrames(
173         PropertyHandle(*compositable_properties[i]), element, computed_style,
174         parent_style, should_snapshot_property_callback,
175         should_snapshot_keyframe_callback);
176   }
177 
178   // Custom properties need to be handled separately, since not all values
179   // can be animated.  Need to resolve the value of each custom property to
180   // ensure that it can be animated.
181   const PropertyRegistry* property_registry =
182       element.GetDocument().GetPropertyRegistry();
183   if (!property_registry)
184     return updated;
185 
186   for (const AtomicString& name : computed_style.GetVariableNames()) {
187     if (property_registry->WasReferenced(name)) {
188       // This variable has been referenced as a property value at least once
189       // during style resolution in the document. Animating this property on
190       // the compositor could introduce misalignment in frame synchronization.
191       //
192       // TODO(kevers): For non-inherited properites, check if referenced in
193       // computed style. References elsewhere in the document should not prevent
194       // compositing.
195       continue;
196     }
197     updated |= SnapshotCompositorKeyFrames(
198         PropertyHandle(name), element, computed_style, parent_style,
199         should_snapshot_property_callback, should_snapshot_keyframe_callback);
200   }
201   return updated;
202 }
203 
SnapshotCompositorKeyFrames(const PropertyHandle & property,Element & element,const ComputedStyle & computed_style,const ComputedStyle * parent_style,ShouldSnapshotPropertyCallback should_snapshot_property_callback,ShouldSnapshotKeyframeCallback should_snapshot_keyframe_callback) const204 bool KeyframeEffectModelBase::SnapshotCompositorKeyFrames(
205     const PropertyHandle& property,
206     Element& element,
207     const ComputedStyle& computed_style,
208     const ComputedStyle* parent_style,
209     ShouldSnapshotPropertyCallback should_snapshot_property_callback,
210     ShouldSnapshotKeyframeCallback should_snapshot_keyframe_callback) const {
211   if (!should_snapshot_property_callback(property))
212     return false;
213 
214   PropertySpecificKeyframeGroup* keyframe_group =
215       keyframe_groups_->at(property);
216   if (!keyframe_group)
217     return false;
218 
219   bool updated = false;
220   for (auto& keyframe : keyframe_group->keyframes_) {
221     if (!should_snapshot_keyframe_callback(*keyframe))
222       continue;
223 
224     updated |= keyframe->PopulateCompositorKeyframeValue(
225         property, element, computed_style, parent_style);
226   }
227   return updated;
228 }
229 
230 template <class K>
GetComputedOffsets(const HeapVector<K> & keyframes)231 Vector<double> KeyframeEffectModelBase::GetComputedOffsets(
232     const HeapVector<K>& keyframes) {
233   // To avoid having to create two vectors when converting from the nullable
234   // offsets to the non-nullable computed offsets, we keep the convention in
235   // this function that std::numeric_limits::quiet_NaN() represents null.
236   double last_offset = 0;
237   Vector<double> result;
238   result.ReserveCapacity(keyframes.size());
239 
240   for (const auto& keyframe : keyframes) {
241     base::Optional<double> offset = keyframe->Offset();
242     if (offset) {
243       DCHECK_GE(offset.value(), 0);
244       DCHECK_LE(offset.value(), 1);
245       DCHECK_GE(offset.value(), last_offset);
246       last_offset = offset.value();
247     }
248     result.push_back(offset.value_or(std::numeric_limits<double>::quiet_NaN()));
249   }
250 
251   if (result.IsEmpty())
252     return result;
253 
254   if (std::isnan(result.back()))
255     result.back() = 1;
256 
257   if (result.size() > 1 && std::isnan(result[0])) {
258     result.front() = 0;
259   }
260 
261   wtf_size_t last_index = 0;
262   last_offset = result.front();
263   for (wtf_size_t i = 1; i < result.size(); ++i) {
264     double offset = result[i];
265     if (!std::isnan(offset)) {
266       for (wtf_size_t j = 1; j < i - last_index; ++j) {
267         result[last_index + j] =
268             last_offset + (offset - last_offset) * j / (i - last_index);
269       }
270       last_index = i;
271       last_offset = offset;
272     }
273   }
274 
275   return result;
276 }
277 
278 template CORE_EXPORT Vector<double> KeyframeEffectModelBase::GetComputedOffsets(
279     const HeapVector<Member<Keyframe>>& keyframes);
280 template CORE_EXPORT Vector<double> KeyframeEffectModelBase::GetComputedOffsets(
281     const HeapVector<Member<StringKeyframe>>& keyframes);
282 
IsTransformRelatedEffect() const283 bool KeyframeEffectModelBase::IsTransformRelatedEffect() const {
284   return Affects(PropertyHandle(GetCSSPropertyTransform())) ||
285          Affects(PropertyHandle(GetCSSPropertyRotate())) ||
286          Affects(PropertyHandle(GetCSSPropertyScale())) ||
287          Affects(PropertyHandle(GetCSSPropertyTranslate()));
288 }
289 
SetLogicalPropertyResolutionContext(TextDirection text_direction,WritingMode writing_mode)290 bool KeyframeEffectModelBase::SetLogicalPropertyResolutionContext(
291     TextDirection text_direction,
292     WritingMode writing_mode) {
293   bool changed = false;
294   for (wtf_size_t i = 0; i < keyframes_.size(); i++) {
295     if (auto* string_keyframe = DynamicTo<StringKeyframe>(*keyframes_[i])) {
296       if (string_keyframe->HasLogicalProperty()) {
297         string_keyframe->SetLogicalPropertyResolutionContext(text_direction,
298                                                              writing_mode);
299         changed = true;
300       }
301     }
302   }
303   if (changed)
304     ClearCachedData();
305   return changed;
306 }
307 
Trace(Visitor * visitor) const308 void KeyframeEffectModelBase::Trace(Visitor* visitor) const {
309   visitor->Trace(keyframes_);
310   visitor->Trace(keyframe_groups_);
311   visitor->Trace(interpolation_effect_);
312   EffectModel::Trace(visitor);
313 }
314 
EnsureKeyframeGroups() const315 void KeyframeEffectModelBase::EnsureKeyframeGroups() const {
316   if (keyframe_groups_)
317     return;
318 
319   keyframe_groups_ = MakeGarbageCollected<KeyframeGroupMap>();
320   scoped_refptr<TimingFunction> zero_offset_easing = default_keyframe_easing_;
321   Vector<double> computed_offsets = GetComputedOffsets(keyframes_);
322   DCHECK_EQ(computed_offsets.size(), keyframes_.size());
323   for (wtf_size_t i = 0; i < keyframes_.size(); i++) {
324     double computed_offset = computed_offsets[i];
325     const auto& keyframe = keyframes_[i];
326 
327     if (computed_offset == 0)
328       zero_offset_easing = &keyframe->Easing();
329 
330     for (const PropertyHandle& property : keyframe->Properties()) {
331       Member<PropertySpecificKeyframeGroup>& group =
332           keyframe_groups_->insert(property, nullptr).stored_value->value;
333       if (!group)
334         group = MakeGarbageCollected<PropertySpecificKeyframeGroup>();
335 
336       Keyframe::PropertySpecificKeyframe* property_specific_keyframe =
337           keyframe->CreatePropertySpecificKeyframe(property, composite_,
338                                                    computed_offset);
339       has_revert_ |= property_specific_keyframe->IsRevert();
340       group->AppendKeyframe(property_specific_keyframe);
341     }
342   }
343 
344   // Add synthetic keyframes.
345   has_synthetic_keyframes_ = false;
346   for (const auto& entry : *keyframe_groups_) {
347     if (entry.value->AddSyntheticKeyframeIfRequired(zero_offset_easing))
348       has_synthetic_keyframes_ = true;
349 
350     entry.value->RemoveRedundantKeyframes();
351   }
352 }
353 
HasNonVariableProperty() const354 bool KeyframeEffectModelBase::HasNonVariableProperty() const {
355   for (const auto& keyframe : keyframes_) {
356     for (const auto& property : keyframe->Properties()) {
357       if (!property.IsCSSProperty() ||
358           property.GetCSSProperty().PropertyID() != CSSPropertyID::kVariable)
359         return true;
360     }
361   }
362   return false;
363 }
364 
EnsureInterpolationEffectPopulated() const365 void KeyframeEffectModelBase::EnsureInterpolationEffectPopulated() const {
366   if (interpolation_effect_->IsPopulated())
367     return;
368 
369   for (const auto& entry : *keyframe_groups_) {
370     const PropertySpecificKeyframeVector& keyframes = entry.value->Keyframes();
371     for (wtf_size_t i = 0; i < keyframes.size() - 1; i++) {
372       wtf_size_t start_index = i;
373       wtf_size_t end_index = i + 1;
374       double start_offset = keyframes[start_index]->Offset();
375       double end_offset = keyframes[end_index]->Offset();
376       double apply_from = start_offset;
377       double apply_to = end_offset;
378 
379       if (i == 0) {
380         apply_from = -std::numeric_limits<double>::infinity();
381         DCHECK_EQ(start_offset, 0.0);
382         if (end_offset == 0.0) {
383           DCHECK_NE(keyframes[end_index + 1]->Offset(), 0.0);
384           end_index = start_index;
385         }
386       }
387       if (i == keyframes.size() - 2) {
388         apply_to = std::numeric_limits<double>::infinity();
389         DCHECK_EQ(end_offset, 1.0);
390         if (start_offset == 1.0) {
391           DCHECK_NE(keyframes[start_index - 1]->Offset(), 1.0);
392           start_index = end_index;
393         }
394       }
395 
396       if (apply_from != apply_to) {
397         interpolation_effect_->AddInterpolationsFromKeyframes(
398             entry.key, *keyframes[start_index], *keyframes[end_index],
399             apply_from, apply_to);
400       }
401       // else the interpolation will never be used in sampling
402     }
403   }
404 
405   interpolation_effect_->SetPopulated();
406 }
407 
ClearCachedData()408 void KeyframeEffectModelBase::ClearCachedData() {
409   keyframe_groups_ = nullptr;
410   interpolation_effect_->Clear();
411   last_fraction_ = std::numeric_limits<double>::quiet_NaN();
412   needs_compositor_keyframes_snapshot_ = true;
413 }
414 
IsReplaceOnly() const415 bool KeyframeEffectModelBase::IsReplaceOnly() const {
416   EnsureKeyframeGroups();
417   for (const auto& entry : *keyframe_groups_) {
418     for (const auto& keyframe : entry.value->Keyframes()) {
419       if (keyframe->Composite() != EffectModel::kCompositeReplace)
420         return false;
421     }
422   }
423   return true;
424 }
425 
AppendKeyframe(Keyframe::PropertySpecificKeyframe * keyframe)426 void KeyframeEffectModelBase::PropertySpecificKeyframeGroup::AppendKeyframe(
427     Keyframe::PropertySpecificKeyframe* keyframe) {
428   DCHECK(keyframes_.IsEmpty() ||
429          keyframes_.back()->Offset() <= keyframe->Offset());
430   keyframes_.push_back(std::move(keyframe));
431 }
432 
433 void KeyframeEffectModelBase::PropertySpecificKeyframeGroup::
RemoveRedundantKeyframes()434     RemoveRedundantKeyframes() {
435   // As an optimization, removes interior keyframes that have the same offset
436   // as both their neighbours, as they will never be used by sample().
437   // Note that synthetic keyframes must be added before this method is
438   // called.
439   DCHECK_GE(keyframes_.size(), 2U);
440   for (int i = keyframes_.size() - 2; i > 0; --i) {
441     double offset = keyframes_[i]->Offset();
442     bool has_same_offset_as_previous_neighbor =
443         keyframes_[i - 1]->Offset() == offset;
444     bool has_same_offset_as_next_neighbor =
445         keyframes_[i + 1]->Offset() == offset;
446     if (has_same_offset_as_previous_neighbor &&
447         has_same_offset_as_next_neighbor)
448       keyframes_.EraseAt(i);
449   }
450   DCHECK_GE(keyframes_.size(), 2U);
451 }
452 
453 bool KeyframeEffectModelBase::PropertySpecificKeyframeGroup::
AddSyntheticKeyframeIfRequired(scoped_refptr<TimingFunction> zero_offset_easing)454     AddSyntheticKeyframeIfRequired(
455         scoped_refptr<TimingFunction> zero_offset_easing) {
456   DCHECK(!keyframes_.IsEmpty());
457 
458   bool added_synthetic_keyframe = false;
459 
460   if (keyframes_.front()->Offset() != 0.0) {
461     keyframes_.insert(0, keyframes_.front()->NeutralKeyframe(
462                              0, std::move(zero_offset_easing)));
463     added_synthetic_keyframe = true;
464   }
465   if (keyframes_.back()->Offset() != 1.0) {
466     AppendKeyframe(keyframes_.back()->NeutralKeyframe(1, nullptr));
467     added_synthetic_keyframe = true;
468   }
469 
470   return added_synthetic_keyframe;
471 }
472 
473 }  // namespace blink
474