1 // Copyright 2020 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "third_party/blink/renderer/core/animation/css/css_keyframe_effect_model.h"
6
7 #include "third_party/blink/renderer/core/animation/animation_input_helpers.h"
8 #include "third_party/blink/renderer/core/animation/animation_utils.h"
9 #include "third_party/blink/renderer/core/animation/property_handle.h"
10 #include "third_party/blink/renderer/core/animation/string_keyframe.h"
11 #include "third_party/blink/renderer/core/css/properties/computed_style_utils.h"
12 #include "third_party/blink/renderer/core/css/properties/css_property.h"
13 #include "third_party/blink/renderer/core/css/resolver/style_resolver.h"
14 #include "third_party/blink/renderer/core/dom/node_computed_style.h"
15
16 namespace blink {
17
18 namespace {
19
20 using MissingPropertyValueMap = HashMap<String, String>;
21
ResolveUnderlyingPropertyValues(Element & element,const PropertyHandleSet & properties,MissingPropertyValueMap & map)22 void ResolveUnderlyingPropertyValues(Element& element,
23 const PropertyHandleSet& properties,
24 MissingPropertyValueMap& map) {
25 // TODO(crbug.com/1069235): Should sample the underlying animation.
26 ActiveInterpolationsMap empty_interpolations_map;
27 AnimationUtils::ForEachInterpolatedPropertyValue(
28 &element, properties, empty_interpolations_map,
29 WTF::BindRepeating(
30 [](MissingPropertyValueMap* map, PropertyHandle property,
31 const CSSValue* value) {
32 if (property.IsCSSProperty()) {
33 String property_name =
34 AnimationInputHelpers::PropertyHandleToKeyframeAttribute(
35 property);
36 map->Set(property_name, value->CssText());
37 }
38 },
39 WTF::Unretained(&map)));
40 }
41
AddMissingProperties(const MissingPropertyValueMap & property_map,const PropertyHandleSet & all_properties,const PropertyHandleSet & keyframe_properties,StringKeyframe * keyframe)42 void AddMissingProperties(const MissingPropertyValueMap& property_map,
43 const PropertyHandleSet& all_properties,
44 const PropertyHandleSet& keyframe_properties,
45 StringKeyframe* keyframe) {
46 for (const auto& property : all_properties) {
47 // At present, custom properties are to be excluded from the keyframes.
48 // https://github.com/w3c/csswg-drafts/issues/5126.
49 if (property.IsCSSCustomProperty())
50 continue;
51
52 if (keyframe_properties.Contains(property))
53 continue;
54
55 String property_name =
56 AnimationInputHelpers::PropertyHandleToKeyframeAttribute(property);
57 if (property_map.Contains(property_name)) {
58 const String& value = property_map.at(property_name);
59 keyframe->SetCSSPropertyValue(property.GetCSSProperty().PropertyID(),
60 value, SecureContextMode::kInsecureContext,
61 nullptr);
62 }
63 }
64 }
65
ResolveComputedValues(Element * element,StringKeyframe * keyframe)66 void ResolveComputedValues(Element* element, StringKeyframe* keyframe) {
67 DCHECK(element);
68 // Styles are flushed when getKeyframes is called on a CSS animation.
69 DCHECK(element->GetComputedStyle());
70 for (const auto& property : keyframe->Properties()) {
71 if (property.IsCSSCustomProperty()) {
72 // At present, custom properties are to be excluded from the keyframes.
73 // https://github.com/w3c/csswg-drafts/issues/5126.
74 // TODO(csswg/issues/5126): Revisit once issue regarding inclusion of
75 // custom properties is resolved. Perhaps registered should likely be
76 // included since they can be animated in Blink. Pruning unregistered
77 // variables seems justifiable.
78 keyframe->RemoveCustomCSSProperty(property);
79 } else if (property.IsCSSProperty()) {
80 const CSSValue& value = keyframe->CssPropertyValue(property);
81 const CSSPropertyName property_name =
82 property.IsCSSCustomProperty()
83 ? CSSPropertyName(property.CustomPropertyName())
84 : CSSPropertyName(property.GetCSSProperty().PropertyID());
85 const CSSValue* computed_value =
86 StyleResolver::ComputeValue(element, property_name, value);
87 if (computed_value) {
88 keyframe->SetCSSPropertyValue(property.GetCSSProperty(),
89 *computed_value);
90 }
91 }
92 }
93 }
94
95 } // namespace
96
97 KeyframeEffectModelBase::KeyframeVector
GetComputedKeyframes(Element * element)98 CssKeyframeEffectModel::GetComputedKeyframes(Element* element) {
99 const KeyframeEffectModelBase::KeyframeVector& keyframes = GetFrames();
100 if (!element)
101 return keyframes;
102
103 KeyframeEffectModelBase::KeyframeVector computed_keyframes;
104
105 // Lazy resolution of values for missing properties.
106 PropertyHandleSet all_properties = Properties();
107 PropertyHandleSet from_properties;
108 PropertyHandleSet to_properties;
109
110 Vector<double> computed_offsets =
111 KeyframeEffectModelBase::GetComputedOffsets(keyframes);
112 computed_keyframes.ReserveInitialCapacity(keyframes.size());
113 for (wtf_size_t i = 0; i < keyframes.size(); i++) {
114 Keyframe* keyframe = keyframes[i];
115 // TODO(crbug.com/1070627): Use computed values, prune variable references,
116 // and convert logical properties to physical properties.
117 StringKeyframe* computed_keyframe = To<StringKeyframe>(keyframe->Clone());
118 ResolveComputedValues(element, computed_keyframe);
119 computed_keyframes.push_back(computed_keyframe);
120 double offset = computed_offsets[i];
121 if (offset == 0) {
122 for (const auto& property : computed_keyframe->Properties()) {
123 from_properties.insert(property);
124 }
125 } else if (offset == 1) {
126 for (const auto& property : computed_keyframe->Properties()) {
127 to_properties.insert(property);
128 }
129 }
130 }
131
132 // Add missing properties from the bounding keyframes.
133 MissingPropertyValueMap missing_property_value_map;
134 if (from_properties.size() < all_properties.size() ||
135 to_properties.size() < all_properties.size()) {
136 ResolveUnderlyingPropertyValues(*element, all_properties,
137 missing_property_value_map);
138 }
139 if (from_properties.size() < all_properties.size() &&
140 !computed_keyframes.IsEmpty()) {
141 AddMissingProperties(
142 missing_property_value_map, all_properties, from_properties,
143 DynamicTo<StringKeyframe>(computed_keyframes[0].Get()));
144 }
145 if (to_properties.size() < all_properties.size() &&
146 !computed_keyframes.IsEmpty()) {
147 wtf_size_t index = keyframes.size() - 1;
148 AddMissingProperties(
149 missing_property_value_map, all_properties, to_properties,
150 DynamicTo<StringKeyframe>(computed_keyframes[index].Get()));
151 }
152 return computed_keyframes;
153 }
154
155 } // namespace blink
156