blob: 5729b43432a530d3695ad2356762d6d0ef27f462 [file] [log] [blame]
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/core/animation/css/css_keyframe_effect_model.h"
#include "third_party/blink/renderer/core/animation/animation_input_helpers.h"
#include "third_party/blink/renderer/core/animation/animation_utils.h"
#include "third_party/blink/renderer/core/animation/property_handle.h"
#include "third_party/blink/renderer/core/animation/string_keyframe.h"
#include "third_party/blink/renderer/core/css/properties/computed_style_utils.h"
#include "third_party/blink/renderer/core/css/properties/css_property.h"
#include "third_party/blink/renderer/core/css/resolver/style_resolver.h"
#include "third_party/blink/renderer/core/dom/node_computed_style.h"
namespace blink {
namespace {
using MissingPropertyValueMap = HashMap<String, String>;
void ResolveUnderlyingPropertyValues(Element& element,
const PropertyHandleSet& properties,
MissingPropertyValueMap& map) {
// TODO(crbug.com/1069235): Should sample the underlying animation.
ActiveInterpolationsMap empty_interpolations_map;
AnimationUtils::ForEachInterpolatedPropertyValue(
&element, properties, empty_interpolations_map,
WTF::BindRepeating(
[](MissingPropertyValueMap* map, PropertyHandle property,
const CSSValue* value) {
if (property.IsCSSProperty()) {
String property_name =
AnimationInputHelpers::PropertyHandleToKeyframeAttribute(
property);
map->Set(property_name, value->CssText());
}
},
WTF::Unretained(&map)));
}
void AddMissingProperties(const MissingPropertyValueMap& property_map,
const PropertyHandleSet& all_properties,
const PropertyHandleSet& keyframe_properties,
StringKeyframe* keyframe) {
for (const auto& property : all_properties) {
// At present, custom properties are to be excluded from the keyframes.
// https://github.com/w3c/csswg-drafts/issues/5126.
if (property.IsCSSCustomProperty())
continue;
if (keyframe_properties.Contains(property))
continue;
String property_name =
AnimationInputHelpers::PropertyHandleToKeyframeAttribute(property);
if (property_map.Contains(property_name)) {
const String& value = property_map.at(property_name);
keyframe->SetCSSPropertyValue(property.GetCSSProperty().PropertyID(),
value, SecureContextMode::kInsecureContext,
nullptr);
}
}
}
void ResolveComputedValues(Element* element, StringKeyframe* keyframe) {
DCHECK(element);
// Styles are flushed when getKeyframes is called on a CSS animation.
DCHECK(element->GetComputedStyle());
for (const auto& property : keyframe->Properties()) {
if (property.IsCSSCustomProperty()) {
// At present, custom properties are to be excluded from the keyframes.
// https://github.com/w3c/csswg-drafts/issues/5126.
// TODO(csswg/issues/5126): Revisit once issue regarding inclusion of
// custom properties is resolved. Perhaps registered should likely be
// included since they can be animated in Blink. Pruning unregistered
// variables seems justifiable.
keyframe->RemoveCustomCSSProperty(property);
} else if (property.IsCSSProperty()) {
const CSSValue& value = keyframe->CssPropertyValue(property);
const CSSPropertyName property_name =
property.IsCSSCustomProperty()
? CSSPropertyName(property.CustomPropertyName())
: CSSPropertyName(property.GetCSSProperty().PropertyID());
const CSSValue* computed_value =
StyleResolver::ComputeValue(element, property_name, value);
if (computed_value) {
keyframe->SetCSSPropertyValue(property.GetCSSProperty(),
*computed_value);
}
}
}
}
} // namespace
KeyframeEffectModelBase::KeyframeVector
CssKeyframeEffectModel::GetComputedKeyframes(Element* element) {
const KeyframeEffectModelBase::KeyframeVector& keyframes = GetFrames();
if (!element)
return keyframes;
KeyframeEffectModelBase::KeyframeVector computed_keyframes;
// Lazy resolution of values for missing properties.
PropertyHandleSet all_properties = Properties();
PropertyHandleSet from_properties;
PropertyHandleSet to_properties;
Vector<double> computed_offsets =
KeyframeEffectModelBase::GetComputedOffsets(keyframes);
computed_keyframes.ReserveInitialCapacity(keyframes.size());
for (wtf_size_t i = 0; i < keyframes.size(); i++) {
Keyframe* keyframe = keyframes[i];
// TODO(crbug.com/1070627): Use computed values, prune variable references,
// and convert logical properties to physical properties.
StringKeyframe* computed_keyframe = To<StringKeyframe>(keyframe->Clone());
ResolveComputedValues(element, computed_keyframe);
computed_keyframes.push_back(computed_keyframe);
double offset = computed_offsets[i];
if (offset == 0) {
for (const auto& property : computed_keyframe->Properties()) {
from_properties.insert(property);
}
} else if (offset == 1) {
for (const auto& property : computed_keyframe->Properties()) {
to_properties.insert(property);
}
}
}
// Add missing properties from the bounding keyframes.
MissingPropertyValueMap missing_property_value_map;
if (from_properties.size() < all_properties.size() ||
to_properties.size() < all_properties.size()) {
ResolveUnderlyingPropertyValues(*element, all_properties,
missing_property_value_map);
}
if (from_properties.size() < all_properties.size() &&
!computed_keyframes.IsEmpty()) {
AddMissingProperties(
missing_property_value_map, all_properties, from_properties,
DynamicTo<StringKeyframe>(computed_keyframes[0].Get()));
}
if (to_properties.size() < all_properties.size() &&
!computed_keyframes.IsEmpty()) {
wtf_size_t index = keyframes.size() - 1;
AddMissingProperties(
missing_property_value_map, all_properties, to_properties,
DynamicTo<StringKeyframe>(computed_keyframes[index].Get()));
}
return computed_keyframes;
}
} // namespace blink