blob: 3727ec5c510a3bea3857bbb5718e91687c8c923c [file] [log] [blame]
// Copyright 2014 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/string_keyframe.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_object_builder.h"
#include "third_party/blink/renderer/core/animation/animation_input_helpers.h"
#include "third_party/blink/renderer/core/animation/css/css_animations.h"
#include "third_party/blink/renderer/core/css/css_custom_property_declaration.h"
#include "third_party/blink/renderer/core/css/css_keyframe_shorthand_value.h"
#include "third_party/blink/renderer/core/css/parser/css_parser.h"
#include "third_party/blink/renderer/core/css/resolver/style_resolver.h"
#include "third_party/blink/renderer/core/style_property_shorthand.h"
#include "third_party/blink/renderer/core/svg/svg_element.h"
#include "third_party/blink/renderer/platform/heap/heap.h"
namespace blink {
namespace {
// Returns handle for the given CSSProperty.
// |value| is required only for custom properties.
PropertyHandle ToPropertyHandle(const CSSProperty& property,
const CSSValue* value) {
if (property.IDEquals(CSSPropertyID::kVariable)) {
return PropertyHandle(To<CSSCustomPropertyDeclaration>(*value).GetName());
} else {
return PropertyHandle(property, false);
}
}
const CSSValue* GetOrCreateCSSValueFrom(
const CSSProperty& property,
const MutableCSSPropertyValueSet& property_value_set) {
DCHECK_NE(property.PropertyID(), CSSPropertyID::kInvalid);
DCHECK_NE(property.PropertyID(), CSSPropertyID::kVariable);
if (!property.IsShorthand())
return property_value_set.GetPropertyCSSValue(property.PropertyID());
// For shorthands create a special wrapper value, |CSSKeyframeShorthandValue|,
// which can be used to correctly serialize it given longhands that are
// present in this set.
return MakeGarbageCollected<CSSKeyframeShorthandValue>(
property.PropertyID(), property_value_set.ImmutableCopyIfNeeded());
}
} // namespace
StringKeyframe::StringKeyframe(const StringKeyframe& copy_from)
: Keyframe(copy_from.offset_, copy_from.composite_, copy_from.easing_),
input_properties_(copy_from.input_properties_),
css_property_map_(copy_from.css_property_map_->MutableCopy()),
presentation_attribute_map_(
copy_from.presentation_attribute_map_->MutableCopy()),
svg_attribute_map_(copy_from.svg_attribute_map_) {}
MutableCSSPropertyValueSet::SetResult StringKeyframe::SetCSSPropertyValue(
const AtomicString& property_name,
const String& value,
SecureContextMode secure_context_mode,
StyleSheetContents* style_sheet_contents) {
bool is_animation_tainted = true;
MutableCSSPropertyValueSet::SetResult result = css_property_map_->SetProperty(
property_name, value, false, secure_context_mode, style_sheet_contents,
is_animation_tainted);
const CSSValue* parsed_value =
css_property_map_->GetPropertyCSSValue(property_name);
if (result.did_parse && parsed_value) {
// Per specification we only keep properties around which are parsable.
input_properties_.Set(PropertyHandle(property_name), *parsed_value);
}
return result;
}
MutableCSSPropertyValueSet::SetResult StringKeyframe::SetCSSPropertyValue(
CSSPropertyID property_id,
const String& value,
SecureContextMode secure_context_mode,
StyleSheetContents* style_sheet_contents) {
DCHECK_NE(property_id, CSSPropertyID::kInvalid);
DCHECK_NE(property_id, CSSPropertyID::kVariable);
const CSSProperty& property = CSSProperty::Get(property_id);
if (CSSAnimations::IsAnimationAffectingProperty(property)) {
bool did_parse = true;
bool did_change = false;
return MutableCSSPropertyValueSet::SetResult{did_parse, did_change};
}
// Use a temporary set for shorthands so that its longhands are stored
// separately and can later be used to construct a special shorthand value.
bool use_temporary_set = property.IsShorthand();
auto* property_value_set =
use_temporary_set ? MakeGarbageCollected<MutableCSSPropertyValueSet>(
css_property_map_->CssParserMode())
: css_property_map_.Get();
MutableCSSPropertyValueSet::SetResult result =
property_value_set->SetProperty(
property_id, value, false, secure_context_mode, style_sheet_contents);
const CSSValue* parsed_value =
GetOrCreateCSSValueFrom(property, *property_value_set);
if (result.did_parse && parsed_value) {
// Per specification we only keep properties around which are parsable.
input_properties_.Set(PropertyHandle(property), parsed_value);
}
if (use_temporary_set)
css_property_map_->MergeAndOverrideOnConflict(property_value_set);
return result;
}
void StringKeyframe::SetCSSPropertyValue(const CSSProperty& property,
const CSSValue& value) {
DCHECK_NE(property.PropertyID(), CSSPropertyID::kInvalid);
DCHECK(!CSSAnimations::IsAnimationAffectingProperty(property));
input_properties_.Set(ToPropertyHandle(property, &value), value);
css_property_map_->SetProperty(property.PropertyID(), value, false);
}
void StringKeyframe::SetPresentationAttributeValue(
const CSSProperty& property,
const String& value,
SecureContextMode secure_context_mode,
StyleSheetContents* style_sheet_contents) {
DCHECK_NE(property.PropertyID(), CSSPropertyID::kInvalid);
if (!CSSAnimations::IsAnimationAffectingProperty(property)) {
presentation_attribute_map_->SetProperty(property.PropertyID(), value,
false, secure_context_mode,
style_sheet_contents);
}
}
void StringKeyframe::SetSVGAttributeValue(const QualifiedName& attribute_name,
const String& value) {
svg_attribute_map_.Set(&attribute_name, value);
}
PropertyHandleSet StringKeyframe::Properties() const {
// This is not used in time-critical code, so we probably don't need to
// worry about caching this result.
PropertyHandleSet properties;
for (unsigned i = 0; i < css_property_map_->PropertyCount(); ++i) {
CSSPropertyValueSet::PropertyReference property_reference =
css_property_map_->PropertyAt(i);
// TODO(crbug.com/980160): Remove access to static Variable instance.
const CSSProperty& property = CSSProperty::Get(property_reference.Id());
DCHECK(!property.IsShorthand())
<< "Web Animations: Encountered unexpanded shorthand CSS property ("
<< static_cast<int>(property.PropertyID()) << ").";
properties.insert(ToPropertyHandle(property, &property_reference.Value()));
}
for (unsigned i = 0; i < presentation_attribute_map_->PropertyCount(); ++i) {
properties.insert(PropertyHandle(
CSSProperty::Get(presentation_attribute_map_->PropertyAt(i).Id()),
true));
}
for (auto* const key : svg_attribute_map_.Keys())
properties.insert(PropertyHandle(*key));
return properties;
}
bool StringKeyframe::HasCssProperty() const {
PropertyHandleSet properties = Properties();
for (const PropertyHandle& property : properties) {
if (property.IsCSSProperty())
return true;
}
return false;
}
void StringKeyframe::AddKeyframePropertiesToV8Object(
V8ObjectBuilder& object_builder,
Element* element) const {
Keyframe::AddKeyframePropertiesToV8Object(object_builder, element);
for (const auto& entry : input_properties_) {
const PropertyHandle& property_handle = entry.key;
const CSSValue* property_value = entry.value;
String property_name =
AnimationInputHelpers::PropertyHandleToKeyframeAttribute(
property_handle);
object_builder.Add(property_name, property_value->CssText());
}
// Legacy code path for SVG and Presentation attributes.
//
// TODO(816956): Move these to input_properties_ and remove this. Note that
// this code path is not well tested given that removing it didn't cause any
// test failures.
for (const PropertyHandle& property : Properties()) {
if (property.IsCSSProperty())
continue;
String property_name =
AnimationInputHelpers::PropertyHandleToKeyframeAttribute(property);
String property_value;
if (property.IsPresentationAttribute()) {
const auto& attribute = property.PresentationAttribute();
property_value = PresentationAttributeValue(attribute).CssText();
} else {
DCHECK(property.IsSVGAttribute());
property_value = SvgPropertyValue(property.SvgAttribute());
}
object_builder.Add(property_name, property_value);
}
}
void StringKeyframe::Trace(Visitor* visitor) const {
visitor->Trace(input_properties_);
visitor->Trace(css_property_map_);
visitor->Trace(presentation_attribute_map_);
Keyframe::Trace(visitor);
}
Keyframe* StringKeyframe::Clone() const {
return MakeGarbageCollected<StringKeyframe>(*this);
}
Keyframe::PropertySpecificKeyframe*
StringKeyframe::CreatePropertySpecificKeyframe(
const PropertyHandle& property,
EffectModel::CompositeOperation effect_composite,
double offset) const {
EffectModel::CompositeOperation composite =
composite_.value_or(effect_composite);
if (property.IsCSSProperty()) {
return MakeGarbageCollected<CSSPropertySpecificKeyframe>(
offset, &Easing(), &CssPropertyValue(property), composite);
}
if (property.IsPresentationAttribute()) {
return MakeGarbageCollected<CSSPropertySpecificKeyframe>(
offset, &Easing(),
&PresentationAttributeValue(property.PresentationAttribute()),
composite);
}
DCHECK(property.IsSVGAttribute());
return MakeGarbageCollected<SVGPropertySpecificKeyframe>(
offset, &Easing(), SvgPropertyValue(property.SvgAttribute()), composite);
}
bool StringKeyframe::CSSPropertySpecificKeyframe::
PopulateCompositorKeyframeValue(const PropertyHandle& property,
Element& element,
const ComputedStyle& base_style,
const ComputedStyle* parent_style) const {
compositor_keyframe_value_cache_ =
StyleResolver::CreateCompositorKeyframeValueSnapshot(
element, base_style, parent_style, property, value_.Get());
return true;
}
bool StringKeyframe::CSSPropertySpecificKeyframe::IsRevert() const {
return value_ && value_->IsRevertValue();
}
Keyframe::PropertySpecificKeyframe*
StringKeyframe::CSSPropertySpecificKeyframe::NeutralKeyframe(
double offset,
scoped_refptr<TimingFunction> easing) const {
return MakeGarbageCollected<CSSPropertySpecificKeyframe>(
offset, std::move(easing), nullptr, EffectModel::kCompositeAdd);
}
void StringKeyframe::CSSPropertySpecificKeyframe::Trace(
Visitor* visitor) const {
visitor->Trace(value_);
visitor->Trace(compositor_keyframe_value_cache_);
Keyframe::PropertySpecificKeyframe::Trace(visitor);
}
Keyframe::PropertySpecificKeyframe*
StringKeyframe::CSSPropertySpecificKeyframe::CloneWithOffset(
double offset) const {
auto* clone = MakeGarbageCollected<CSSPropertySpecificKeyframe>(
offset, easing_, value_.Get(), composite_);
clone->compositor_keyframe_value_cache_ = compositor_keyframe_value_cache_;
return clone;
}
Keyframe::PropertySpecificKeyframe*
SVGPropertySpecificKeyframe::CloneWithOffset(double offset) const {
return MakeGarbageCollected<SVGPropertySpecificKeyframe>(offset, easing_,
value_, composite_);
}
Keyframe::PropertySpecificKeyframe*
SVGPropertySpecificKeyframe::NeutralKeyframe(
double offset,
scoped_refptr<TimingFunction> easing) const {
return MakeGarbageCollected<SVGPropertySpecificKeyframe>(
offset, std::move(easing), String(), EffectModel::kCompositeAdd);
}
} // namespace blink