blob: f8793aac72c6a43011377ee78b03280c3f51eaaf [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.
#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_ANIMATION_STRING_KEYFRAME_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_ANIMATION_STRING_KEYFRAME_H_
#include "third_party/blink/renderer/core/animation/keyframe.h"
#include "third_party/blink/renderer/core/css/css_property_value_set.h"
#include "third_party/blink/renderer/platform/heap/heap.h"
#include "third_party/blink/renderer/platform/wtf/hash_map.h"
namespace blink {
class StyleSheetContents;
// An implementation of Keyframe used for CSS Animations, web-animations, and
// the HTML <marquee> element.
//
// A StringKeyframe instance supports an arbitrary number of (property, value)
// pairs. The properties can be CSS properties or SVG attributes, mapping to
// CSSValue or plain String values respectively. CSS properties added to a
// StringKeyframe are expanded to shorthand and de-duplicated, with newer
// properties replacing older ones. SVG attributes are similarly de-duplicated.
//
class CORE_EXPORT StringKeyframe : public Keyframe {
public:
StringKeyframe()
: presentation_attribute_map_(
MakeGarbageCollected<MutableCSSPropertyValueSet>(
kHTMLStandardMode)) {}
StringKeyframe(const StringKeyframe& copy_from);
MutableCSSPropertyValueSet::SetResult SetCSSPropertyValue(
const AtomicString& custom_property_name,
const String& value,
SecureContextMode,
StyleSheetContents*);
MutableCSSPropertyValueSet::SetResult SetCSSPropertyValue(
CSSPropertyID,
const String& value,
SecureContextMode,
StyleSheetContents*);
void SetCSSPropertyValue(const CSSProperty&, const CSSValue&);
void RemoveCustomCSSProperty(const PropertyHandle& property);
void SetPresentationAttributeValue(const CSSProperty&,
const String& value,
SecureContextMode,
StyleSheetContents*);
void SetSVGAttributeValue(const QualifiedName&, const String& value);
const CSSValue& CssPropertyValue(const PropertyHandle& property) const {
EnsureCssPropertyMap();
int index = -1;
if (property.IsCSSCustomProperty()) {
index =
css_property_map_->FindPropertyIndex(property.CustomPropertyName());
} else {
DCHECK(!property.GetCSSProperty().IsShorthand());
index = css_property_map_->FindPropertyIndex(
property.GetCSSProperty().PropertyID());
}
CHECK_GE(index, 0);
return css_property_map_->PropertyAt(static_cast<unsigned>(index)).Value();
}
const CSSValue& PresentationAttributeValue(
const CSSProperty& property) const {
int index =
presentation_attribute_map_->FindPropertyIndex(property.PropertyID());
CHECK_GE(index, 0);
return presentation_attribute_map_->PropertyAt(static_cast<unsigned>(index))
.Value();
}
String SvgPropertyValue(const QualifiedName& attribute_name) const {
return svg_attribute_map_.at(&attribute_name);
}
PropertyHandleSet Properties() const override;
bool HasCssProperty() const;
void AddKeyframePropertiesToV8Object(V8ObjectBuilder&,
Element*) const override;
Keyframe* Clone() const override;
bool HasLogicalProperty() { return has_logical_property_; }
bool SetLogicalPropertyResolutionContext(TextDirection text_direction,
WritingMode writing_mode);
void Trace(Visitor*) const override;
class CSSPropertySpecificKeyframe
: public Keyframe::PropertySpecificKeyframe {
public:
CSSPropertySpecificKeyframe(double offset,
scoped_refptr<TimingFunction> easing,
const CSSValue* value,
EffectModel::CompositeOperation composite)
: Keyframe::PropertySpecificKeyframe(offset,
std::move(easing),
composite),
value_(value) {}
const CSSValue* Value() const { return value_.Get(); }
bool PopulateCompositorKeyframeValue(
const PropertyHandle&,
Element&,
const ComputedStyle& base_style,
const ComputedStyle* parent_style) const final;
const CompositorKeyframeValue* GetCompositorKeyframeValue() const final {
return compositor_keyframe_value_cache_;
}
bool IsNeutral() const final { return !value_; }
bool IsRevert() const final;
Keyframe::PropertySpecificKeyframe* NeutralKeyframe(
double offset,
scoped_refptr<TimingFunction> easing) const final;
void Trace(Visitor*) const override;
private:
Keyframe::PropertySpecificKeyframe* CloneWithOffset(
double offset) const override;
bool IsCSSPropertySpecificKeyframe() const override { return true; }
Member<const CSSValue> value_;
mutable Member<CompositorKeyframeValue> compositor_keyframe_value_cache_;
};
class SVGPropertySpecificKeyframe
: public Keyframe::PropertySpecificKeyframe {
public:
SVGPropertySpecificKeyframe(double offset,
scoped_refptr<TimingFunction> easing,
const String& value,
EffectModel::CompositeOperation composite)
: Keyframe::PropertySpecificKeyframe(offset,
std::move(easing),
composite),
value_(value) {}
const String& Value() const { return value_; }
PropertySpecificKeyframe* CloneWithOffset(double offset) const final;
const CompositorKeyframeValue* GetCompositorKeyframeValue() const final {
return nullptr;
}
bool IsNeutral() const final { return value_.IsNull(); }
bool IsRevert() const final { return false; }
PropertySpecificKeyframe* NeutralKeyframe(
double offset,
scoped_refptr<TimingFunction> easing) const final;
private:
bool IsSVGPropertySpecificKeyframe() const override { return true; }
String value_;
};
class PropertyResolver : public GarbageCollected<PropertyResolver> {
public:
// Custom properties must use this version of the constructor.
PropertyResolver(CSSPropertyID property_id, const CSSValue& css_value);
// Shorthand and logical properties must use this version of the
// constructor.
PropertyResolver(const CSSProperty& property,
const MutableCSSPropertyValueSet* property_value_set,
bool is_logical);
static PropertyResolver* CreateCustomVariableResolver(
const CSSValue& css_value);
bool IsValid() const;
const CSSValue* CssValue();
void AppendTo(MutableCSSPropertyValueSet* property_value_set,
TextDirection text_direction,
WritingMode writing_mode);
void SetProperty(MutableCSSPropertyValueSet* property_value_set,
CSSPropertyID property_id,
const CSSValue& value,
TextDirection text_direction,
WritingMode writing_mode);
static bool HasLowerPriority(PropertyResolver* first,
PropertyResolver* second);
// Helper methods for resolving longhand name collisions.
// Longhands take priority over shorthands.
// Physical properties take priority over logical.
// Two shorthands with overlapping longhand properties are sorted based
// on the number of longhand properties in their expansions.
bool IsLogical() { return is_logical_; }
bool IsShorthand() { return css_property_value_set_; }
unsigned ExpansionCount() {
return css_property_value_set_ ? css_property_value_set_->PropertyCount()
: 1;
}
void Trace(Visitor* visitor) const;
private:
CSSPropertyID property_id_ = CSSPropertyID::kInvalid;
Member<const CSSValue> css_value_ = nullptr;
Member<ImmutableCSSPropertyValueSet> css_property_value_set_ = nullptr;
bool is_logical_ = false;
};
private:
Keyframe::PropertySpecificKeyframe* CreatePropertySpecificKeyframe(
const PropertyHandle&,
EffectModel::CompositeOperation effect_composite,
double offset) const override;
void InvalidateCssPropertyMap() { css_property_map_ = nullptr; }
void EnsureCssPropertyMap() const;
bool IsStringKeyframe() const override { return true; }
// Mapping of unresolved properties to a their resolvers. A resolver knows
// how to expand shorthands to their corresponding longhand property names,
// convert logical to physical property names and compare precedence for
// resolving longhand name collisions. The resolver also knows how to
// create serialized text for a shorthand, which is required for getKeyframes
// calls.
// See: https://drafts.csswg.org/web-animations/#keyframes-section
HeapHashMap<PropertyHandle, Member<PropertyResolver>> input_properties_;
// The resolved properties are computed from unresolved ones applying these
// steps:
// 1. Resolve conflicts when multiple properties map to same underlying
// one (e.g., margin, margin-top)
// 2. Expand shorthands to longhands
// 3. Expand logical properties to physical ones
mutable Member<MutableCSSPropertyValueSet> css_property_map_;
Member<MutableCSSPropertyValueSet> presentation_attribute_map_;
HashMap<const QualifiedName*, String> svg_attribute_map_;
// If the keyframes contain one or more logical properties, these need to be
// remapped to physical properties when the writing mode or text direction
// changes.
bool has_logical_property_ = false;
// The following properties are required for mapping logical to physical
// property names. Though the same for all keyframes within the same model,
// we store the value here to facilitate lazy evaluation of the CSS
// properties.
TextDirection text_direction_ = TextDirection::kLtr;
WritingMode writing_mode_ = WritingMode::kHorizontalTb;
};
using CSSPropertySpecificKeyframe = StringKeyframe::CSSPropertySpecificKeyframe;
using SVGPropertySpecificKeyframe = StringKeyframe::SVGPropertySpecificKeyframe;
template <>
struct DowncastTraits<StringKeyframe> {
static bool AllowFrom(const Keyframe& value) {
return value.IsStringKeyframe();
}
};
template <>
struct DowncastTraits<CSSPropertySpecificKeyframe> {
static bool AllowFrom(const Keyframe::PropertySpecificKeyframe& value) {
return value.IsCSSPropertySpecificKeyframe();
}
};
template <>
struct DowncastTraits<SVGPropertySpecificKeyframe> {
static bool AllowFrom(const Keyframe::PropertySpecificKeyframe& value) {
return value.IsSVGPropertySpecificKeyframe();
}
};
} // namespace blink
#endif