Commit aaf10213 authored by Majid Valipour's avatar Majid Valipour Committed by Commit Bot

[web-animations] Make handling of keyframes more spec compliant

WebAnimations spec has two keyframe concepts:

A) keyframes [1]
B) computed keyframes [2]


A key difference is that in A property values remain unresolved while in
B these properties are resolved (e.g., shorthands expand to long hands
etc.)

KeyframeEffect.getKeyframes() is expected to return (A) but our current
implementation only keeps around (B).

This CL introduces the following changes to bring us closer to the
specified behavior:

 1. Introduce a new map <property, value> in StringKeyframe
   representing (A)
 2. CSS property pairs are added to this map as long as they parse
   correctly [3]
 3. Use the new map to produce the result of getKeyframes()


There is some additional special handling required for shorthand
properties because Blink CSS parser does not really produce any
shorthand values. As a shortcut this patch introduces a new type
of css value (`CSSKeyframeShorthandValue`) which represents a
shorthand by encapsulating all its relevant longhand property/value
pairs.

Note that we continue our old behavior for SVG and Presentation attrs
which will be switch over in follow up patch.

[1] https://drafts.csswg.org/web-animations/#keyframes-section
[2] https://drafts.csswg.org/web-animations/#calculating-computed-keyframes
[3] https://drafts.csswg.org/web-animations/#process-a-keyframes-argument step 8

TEST:
 - web-animations/interfaces/{Animatable, Keyframes} => PASS previously failing tests
 - Added two new test cases in above covering animated custom props
 - external/wpt/web-animations/interfaces/KeyframeEffect/getKeyframes.html => New test to cover serialization


Bug: 816956
Change-Id: Icc8e0bc4a0ee3019ad7d2c566aacb9d3aee0ffe3
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1634393
Commit-Queue: Majid Valipour <majidvp@chromium.org>
Reviewed-by: default avatarStephen McGruer <smcgruer@chromium.org>
Reviewed-by: default avatarAnders Hartvoll Ruud <andruud@chromium.org>
Cr-Commit-Position: refs/heads/master@{#675665}
parent 665b8df8
......@@ -8,6 +8,8 @@
#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"
......@@ -15,8 +17,39 @@
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_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()),
......@@ -29,30 +62,67 @@ MutableCSSPropertyValueSet::SetResult StringKeyframe::SetCSSPropertyValue(
SecureContextMode secure_context_mode,
StyleSheetContents* style_sheet_contents) {
bool is_animation_tainted = true;
return css_property_map_->SetProperty(
MutableCSSPropertyValueSet::SetResult result = css_property_map_->SetProperty(
property_name, registry, 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,
CSSPropertyID property_id,
const String& value,
SecureContextMode secure_context_mode,
StyleSheetContents* style_sheet_contents) {
DCHECK_NE(property, CSSPropertyID::kInvalid);
if (CSSAnimations::IsAnimationAffectingProperty(CSSProperty::Get(property))) {
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};
}
return css_property_map_->SetProperty(
property, value, false, secure_context_mode, style_sheet_contents);
// 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);
}
......@@ -85,13 +155,7 @@ PropertyHandleSet StringKeyframe::Properties() const {
DCHECK(!property.IsShorthand())
<< "Web Animations: Encountered unexpanded shorthand CSS property ("
<< static_cast<int>(property.PropertyID()) << ").";
if (property.IDEquals(CSSPropertyID::kVariable)) {
properties.insert(PropertyHandle(
To<CSSCustomPropertyDeclaration>(property_reference.Value())
.GetName()));
} else {
properties.insert(PropertyHandle(property, false));
}
properties.insert(ToPropertyHandle(property, &property_reference.Value()));
}
for (unsigned i = 0; i < presentation_attribute_map_->PropertyCount(); ++i) {
......@@ -117,25 +181,41 @@ bool StringKeyframe::HasCssProperty() const {
void StringKeyframe::AddKeyframePropertiesToV8Object(
V8ObjectBuilder& object_builder) const {
Keyframe::AddKeyframePropertiesToV8Object(object_builder);
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 value;
if (property.IsCSSProperty()) {
value = CssPropertyValue(property).CssText();
} else if (property.IsPresentationAttribute()) {
String property_value;
if (property.IsPresentationAttribute()) {
const auto& attribute = property.PresentationAttribute();
value = PresentationAttributeValue(attribute).CssText();
property_value = PresentationAttributeValue(attribute).CssText();
} else {
DCHECK(property.IsSVGAttribute());
value = SvgPropertyValue(property.SvgAttribute());
property_value = SvgPropertyValue(property.SvgAttribute());
}
object_builder.Add(property_name, value);
object_builder.Add(property_name, property_value);
}
}
void StringKeyframe::Trace(Visitor* visitor) {
visitor->Trace(input_properties_);
visitor->Trace(css_property_map_);
visitor->Trace(presentation_attribute_map_);
Keyframe::Trace(visitor);
......
......@@ -165,6 +165,28 @@ class CORE_EXPORT StringKeyframe : public Keyframe {
bool IsStringKeyframe() const override { return true; }
// The unresolved property and their values. This is needed for correct
// implementation of KeyframeEffect.getKeyframes(). We use a single list for
// CSS, SVG properties. The only requirement for a property value to be
// in this list is that it parses correctly.
//
// See: https://drafts.csswg.org/web-animations/#keyframes-section
HeapHashMap<PropertyHandle, Member<const CSSValue>> 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
//
// See:
// https://drafts.csswg.org/web-animations/#calculating-computed-keyframes
//
// TODO(816956): AFAICT we don't do (1) at the moment rather we parse and feed
// values into the MutableCSSPropertyValueSet which keeps replacing values as
// they come in. I am not sure if it leads to the same conflict resolution
// that web-animation expects. This needs more investigation.
Member<MutableCSSPropertyValueSet> css_property_map_;
Member<MutableCSSPropertyValueSet> presentation_attribute_map_;
HashMap<const QualifiedName*, String> svg_attribute_map_;
......
......@@ -100,6 +100,8 @@ blink_core_sources("css") {
"css_invalid_variable_value.h",
"css_keyframe_rule.cc",
"css_keyframe_rule.h",
"css_keyframe_shorthand_value.cc",
"css_keyframe_shorthand_value.h",
"css_keyframes_rule.cc",
"css_keyframes_rule.h",
"css_layout_function_value.cc",
......
// Copyright 2019 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/css/css_keyframe_shorthand_value.h"
namespace blink {
CSSKeyframeShorthandValue::CSSKeyframeShorthandValue(
ImmutableCSSPropertyValueSet* properties)
: CSSValue(kKeyframeShorthandClass), properties_(properties) {}
String CSSKeyframeShorthandValue::CustomCSSText() const {
// All property/value pairs belong to the same shorthand so we grab the id
// from the first one.
CSSPropertyID my_shorthand = properties_->PropertyAt(0).ShorthandID();
#if DCHECK_IS_ON()
for (unsigned i = 0; i < properties_->PropertyCount(); i++) {
DCHECK_EQ(my_shorthand, properties_->PropertyAt(i).ShorthandID())
<< "These are not the longhands you're looking for.";
}
#endif
return properties_->GetPropertyValue(my_shorthand);
}
void CSSKeyframeShorthandValue::TraceAfterDispatch(blink::Visitor* visitor) {
visitor->Trace(properties_);
CSSValue::TraceAfterDispatch(visitor);
}
} // namespace blink
// Copyright 2019 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_CSS_CSS_KEYFRAME_SHORTHAND_VALUE_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_CSS_CSS_KEYFRAME_SHORTHAND_VALUE_H_
#include "third_party/blink/renderer/core/css/css_value.h"
#include "third_party/blink/renderer/platform/wtf/casting.h"
#include "third_party/blink/renderer/core/css/style_property_serializer.h"
namespace blink {
// The special value is used to keep around individual longhand css
// property/values that resulted from parsing a shorthand value. This way we can
// reconstruct the shorthand back from them.
//
// Context:
//
// Web Animation specs requires that we keep around and return a parsed
// shorthand name/value pair if they are present in keyframes. However Blink css
// parser does not keep around shorthands and instead produces longhands.
// Instead of updating the css parser engine to preserve shorthands (which is a
// large undertaking) we are taking a shortcut here that allows us to use
// existing logic that enables serialization of a shorthand given its longhands
// i.e., `StylePropertySerializer`. To this end, this class is be used to wrap
// and store longhands produced by a single shorthand as part of animation
// keyframe logic.
//
// For more information see:
// - `StringKeyframe::SetCSSPropertyValue()`
// - https://drafts.csswg.org/web-animations/#process-a-keyframes-argument
class CSSKeyframeShorthandValue : public CSSValue {
public:
// Assumes that all property/value pairs that are present in the input set are
// longhands for the same shorthand property/value pair.
CSSKeyframeShorthandValue(ImmutableCSSPropertyValueSet*);
String CustomCSSText() const;
bool Equals(const CSSKeyframeShorthandValue& other) const {
return properties_ == other.properties_;
}
void TraceAfterDispatch(blink::Visitor*);
private:
Member<ImmutableCSSPropertyValueSet> properties_;
};
template <>
struct DowncastTraits<CSSKeyframeShorthandValue> {
static bool AllowFrom(const CSSValue& value) {
return value.IsShorthandWrapperValue();
}
};
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_CORE_CSS_CSS_KEYFRAME_SHORTHAND_VALUE_H_
......@@ -53,6 +53,7 @@
#include "third_party/blink/renderer/core/css/css_inherited_value.h"
#include "third_party/blink/renderer/core/css/css_initial_value.h"
#include "third_party/blink/renderer/core/css/css_invalid_variable_value.h"
#include "third_party/blink/renderer/core/css/css_keyframe_shorthand_value.h"
#include "third_party/blink/renderer/core/css/css_layout_function_value.h"
#include "third_party/blink/renderer/core/css/css_math_function_value.h"
#include "third_party/blink/renderer/core/css/css_numeric_literal_value.h"
......@@ -224,6 +225,8 @@ bool CSSValue::operator==(const CSSValue& other) const {
return CompareCSSValues<CSSRayValue>(*this, other);
case kIdentifierClass:
return CompareCSSValues<CSSIdentifierValue>(*this, other);
case kKeyframeShorthandClass:
return CompareCSSValues<CSSKeyframeShorthandValue>(*this, other);
case kQuadClass:
return CompareCSSValues<CSSQuadValue>(*this, other);
case kReflectClass:
......@@ -336,6 +339,8 @@ String CSSValue::CssText() const {
return To<CSSRayValue>(this)->CustomCSSText();
case kIdentifierClass:
return To<CSSIdentifierValue>(this)->CustomCSSText();
case kKeyframeShorthandClass:
return To<CSSKeyframeShorthandValue>(this)->CustomCSSText();
case kQuadClass:
return To<CSSQuadValue>(this)->CustomCSSText();
case kReflectClass:
......@@ -480,6 +485,9 @@ void CSSValue::FinalizeGarbageCollectedObject() {
case kIdentifierClass:
To<CSSIdentifierValue>(this)->~CSSIdentifierValue();
return;
case kKeyframeShorthandClass:
To<CSSKeyframeShorthandValue>(this)->~CSSKeyframeShorthandValue();
return;
case kQuadClass:
To<CSSQuadValue>(this)->~CSSQuadValue();
return;
......@@ -640,6 +648,9 @@ void CSSValue::Trace(blink::Visitor* visitor) {
case kIdentifierClass:
To<CSSIdentifierValue>(this)->TraceAfterDispatch(visitor);
return;
case kKeyframeShorthandClass:
To<CSSKeyframeShorthandValue>(this)->TraceAfterDispatch(visitor);
return;
case kQuadClass:
To<CSSQuadValue>(this)->TraceAfterDispatch(visitor);
return;
......
......@@ -167,6 +167,9 @@ class CORE_EXPORT CSSValue : public GarbageCollectedFinalized<CSSValue> {
return class_type_ == kInvalidVariableValueClass;
}
bool IsAxisValue() const { return class_type_ == kAxisClass; }
bool IsShorthandWrapperValue() const {
return class_type_ == kKeyframeShorthandClass;
}
bool HasFailedOrCanceledSubresources() const;
bool MayContainUrl() const;
......@@ -246,6 +249,8 @@ class CORE_EXPORT CSSValue : public GarbageCollectedFinalized<CSSValue> {
kCSSContentDistributionClass,
kKeyframeShorthandClass,
// List class types must appear after ValueListClass.
kValueListClass,
kFunctionClass,
......
This is a testharness.js-based test.
Found 135 tests; 126 PASS, 9 FAIL, 0 TIMEOUT, 0 NOTRUN.
Found 137 tests; 134 PASS, 3 FAIL, 0 TIMEOUT, 0 NOTRUN.
PASS Element.animate() creates an Animation object
PASS Element.animate() creates an Animation object in the relevant realm of the target element
PASS Element.animate() creates an Animation object with a KeyframeEffect
......@@ -8,18 +8,19 @@ PASS Element.animate() accepts empty keyframe lists (input: [])
PASS Element.animate() accepts empty keyframe lists (input: null)
PASS Element.animate() accepts empty keyframe lists (input: undefined)
PASS Element.animate() accepts a one property two value property-indexed keyframes specification
FAIL Element.animate() accepts a one shorthand property two value property-indexed keyframes specification assert_equals: properties on ComputedKeyframe #0 should match expected "composite,computedOffset,easing,margin,offset" but got "composite,computedOffset,easing,marginBottom,marginLeft,marginRight,marginTop,offset"
FAIL Element.animate() accepts a two property (one shorthand and one of its longhand components) two value property-indexed keyframes specification assert_equals: properties on ComputedKeyframe #0 should match expected "composite,computedOffset,easing,margin,marginTop,offset" but got "composite,computedOffset,easing,marginBottom,marginLeft,marginRight,marginTop,offset"
PASS Element.animate() accepts a one shorthand property two value property-indexed keyframes specification
PASS Element.animate() accepts a two property (one shorthand and one of its longhand components) two value property-indexed keyframes specification
PASS Element.animate() accepts a two property two value property-indexed keyframes specification
PASS Element.animate() accepts a two property property-indexed keyframes specification with different numbers of values
PASS Element.animate() accepts a property-indexed keyframes specification with an invalid value
PASS Element.animate() accepts a one property two value property-indexed keyframes specification that needs to stringify its values
PASS Element.animate() accepts a property-indexed keyframes specification with a CSS variable reference
FAIL Element.animate() accepts a property-indexed keyframes specification with a CSS variable reference in a shorthand property assert_equals: properties on ComputedKeyframe #0 should match expected "composite,computedOffset,easing,margin,offset" but got "composite,computedOffset,easing,marginBottom,marginLeft,marginRight,marginTop,offset"
PASS Element.animate() accepts a property-indexed keyframes specification with a CSS variable reference in a shorthand property
PASS Element.animate() accepts a one property one value property-indexed keyframes specification
PASS Element.animate() accepts a one property one non-array value property-indexed keyframes specification
PASS Element.animate() accepts a one property two value property-indexed keyframes specification where the first value is invalid
PASS Element.animate() accepts a one property two value property-indexed keyframes specification where the second value is invalid
PASS Element.animate() accepts a property-indexed keyframes specification with a CSS variable as the property
PASS Element.animate() accepts a property-indexed keyframe with a single offset
PASS Element.animate() accepts a property-indexed keyframe with an array of offsets
PASS Element.animate() accepts a property-indexed keyframe with an array of offsets that is too short
......@@ -47,13 +48,14 @@ PASS Element.animate() accepts a property-indexed keyframe with a single-element
PASS Element.animate() accepts a one property one keyframe sequence
PASS Element.animate() accepts a one property two keyframe sequence
PASS Element.animate() accepts a two property two keyframe sequence
FAIL Element.animate() accepts a one shorthand property two keyframe sequence assert_equals: properties on ComputedKeyframe #0 should match expected "composite,computedOffset,easing,margin,offset" but got "composite,computedOffset,easing,marginBottom,marginLeft,marginRight,marginTop,offset"
FAIL Element.animate() accepts a two property (a shorthand and one of its component longhands) two keyframe sequence assert_equals: properties on ComputedKeyframe #0 should match expected "composite,computedOffset,easing,margin,marginTop,offset" but got "composite,computedOffset,easing,marginBottom,marginLeft,marginRight,marginTop,offset"
PASS Element.animate() accepts a one shorthand property two keyframe sequence
PASS Element.animate() accepts a two property (a shorthand and one of its component longhands) two keyframe sequence
PASS Element.animate() accepts a two property keyframe sequence where one property is missing from the first keyframe
PASS Element.animate() accepts a two property keyframe sequence where one property is missing from the last keyframe
PASS Element.animate() accepts a one property two keyframe sequence that needs to stringify its values
PASS Element.animate() accepts a keyframe sequence with a CSS variable reference
FAIL Element.animate() accepts a keyframe sequence with a CSS variable reference in a shorthand property assert_equals: properties on ComputedKeyframe #0 should match expected "composite,computedOffset,easing,margin,offset" but got "composite,computedOffset,easing,marginBottom,marginLeft,marginRight,marginTop,offset"
PASS Element.animate() accepts a keyframe sequence with a CSS variable reference in a shorthand property
PASS Element.animate() accepts a keyframe sequence with a CSS variable as its property
PASS Element.animate() accepts a keyframe sequence with duplicate values for a given interior offset
PASS Element.animate() accepts a keyframe sequence with duplicate values for offsets 0 and 1
PASS Element.animate() accepts a two property four keyframe sequence
......
This is a testharness.js-based test.
Found 167 tests; 155 PASS, 12 FAIL, 0 TIMEOUT, 0 NOTRUN.
Found 171 tests; 167 PASS, 4 FAIL, 0 TIMEOUT, 0 NOTRUN.
PASS A KeyframeEffect can be constructed with no frames
PASS easing values are parsed correctly when passed to the KeyframeEffect constructor in KeyframeEffectOptions
PASS Invalid easing values are correctly rejected when passed to the KeyframeEffect constructor in KeyframeEffectOptions
......@@ -8,9 +8,9 @@ FAIL composite values are parsed correctly when passed to the KeyframeEffect con
PASS composite value is auto if the composite operation specified on the keyframe effect is being used
PASS A KeyframeEffect can be constructed with a one property two value property-indexed keyframes specification
PASS A KeyframeEffect constructed with a one property two value property-indexed keyframes specification roundtrips
FAIL A KeyframeEffect can be constructed with a one shorthand property two value property-indexed keyframes specification assert_equals: properties on ComputedKeyframe #0 should match expected "composite,computedOffset,easing,margin,offset" but got "composite,computedOffset,easing,marginBottom,marginLeft,marginRight,marginTop,offset"
PASS A KeyframeEffect can be constructed with a one shorthand property two value property-indexed keyframes specification
PASS A KeyframeEffect constructed with a one shorthand property two value property-indexed keyframes specification roundtrips
FAIL A KeyframeEffect can be constructed with a two property (one shorthand and one of its longhand components) two value property-indexed keyframes specification assert_equals: properties on ComputedKeyframe #0 should match expected "composite,computedOffset,easing,margin,marginTop,offset" but got "composite,computedOffset,easing,marginBottom,marginLeft,marginRight,marginTop,offset"
PASS A KeyframeEffect can be constructed with a two property (one shorthand and one of its longhand components) two value property-indexed keyframes specification
PASS A KeyframeEffect constructed with a two property (one shorthand and one of its longhand components) two value property-indexed keyframes specification roundtrips
PASS A KeyframeEffect can be constructed with a two property two value property-indexed keyframes specification
PASS A KeyframeEffect constructed with a two property two value property-indexed keyframes specification roundtrips
......@@ -22,8 +22,8 @@ PASS A KeyframeEffect can be constructed with a one property two value property-
PASS A KeyframeEffect constructed with a one property two value property-indexed keyframes specification that needs to stringify its values roundtrips
PASS A KeyframeEffect can be constructed with a property-indexed keyframes specification with a CSS variable reference
PASS A KeyframeEffect constructed with a property-indexed keyframes specification with a CSS variable reference roundtrips
FAIL A KeyframeEffect can be constructed with a property-indexed keyframes specification with a CSS variable reference in a shorthand property assert_equals: properties on ComputedKeyframe #0 should match expected "composite,computedOffset,easing,margin,offset" but got "composite,computedOffset,easing,marginBottom,marginLeft,marginRight,marginTop,offset"
FAIL A KeyframeEffect constructed with a property-indexed keyframes specification with a CSS variable reference in a shorthand property roundtrips assert_equals: properties on ComputedKeyframe #0 should match expected "composite,computedOffset,easing,marginBottom,marginLeft,marginRight,marginTop,offset" but got "composite,computedOffset,easing,offset"
PASS A KeyframeEffect can be constructed with a property-indexed keyframes specification with a CSS variable reference in a shorthand property
PASS A KeyframeEffect constructed with a property-indexed keyframes specification with a CSS variable reference in a shorthand property roundtrips
PASS A KeyframeEffect can be constructed with a one property one value property-indexed keyframes specification
PASS A KeyframeEffect constructed with a one property one value property-indexed keyframes specification roundtrips
PASS A KeyframeEffect can be constructed with a one property one non-array value property-indexed keyframes specification
......@@ -32,6 +32,8 @@ PASS A KeyframeEffect can be constructed with a one property two value property-
PASS A KeyframeEffect constructed with a one property two value property-indexed keyframes specification where the first value is invalid roundtrips
PASS A KeyframeEffect can be constructed with a one property two value property-indexed keyframes specification where the second value is invalid
PASS A KeyframeEffect constructed with a one property two value property-indexed keyframes specification where the second value is invalid roundtrips
PASS A KeyframeEffect can be constructed with a property-indexed keyframes specification with a CSS variable as the property
PASS A KeyframeEffect constructed with a property-indexed keyframes specification with a CSS variable as the property roundtrips
PASS A KeyframeEffect can be constructed with a property-indexed keyframe with a single offset
PASS A KeyframeEffect constructed with a property-indexed keyframe with a single offset roundtrips
PASS A KeyframeEffect can be constructed with a property-indexed keyframe with an array of offsets
......@@ -86,9 +88,9 @@ PASS A KeyframeEffect can be constructed with a one property two keyframe sequen
PASS A KeyframeEffect constructed with a one property two keyframe sequence roundtrips
PASS A KeyframeEffect can be constructed with a two property two keyframe sequence
PASS A KeyframeEffect constructed with a two property two keyframe sequence roundtrips
FAIL A KeyframeEffect can be constructed with a one shorthand property two keyframe sequence assert_equals: properties on ComputedKeyframe #0 should match expected "composite,computedOffset,easing,margin,offset" but got "composite,computedOffset,easing,marginBottom,marginLeft,marginRight,marginTop,offset"
PASS A KeyframeEffect can be constructed with a one shorthand property two keyframe sequence
PASS A KeyframeEffect constructed with a one shorthand property two keyframe sequence roundtrips
FAIL A KeyframeEffect can be constructed with a two property (a shorthand and one of its component longhands) two keyframe sequence assert_equals: properties on ComputedKeyframe #0 should match expected "composite,computedOffset,easing,margin,marginTop,offset" but got "composite,computedOffset,easing,marginBottom,marginLeft,marginRight,marginTop,offset"
PASS A KeyframeEffect can be constructed with a two property (a shorthand and one of its component longhands) two keyframe sequence
PASS A KeyframeEffect constructed with a two property (a shorthand and one of its component longhands) two keyframe sequence roundtrips
PASS A KeyframeEffect can be constructed with a two property keyframe sequence where one property is missing from the first keyframe
PASS A KeyframeEffect constructed with a two property keyframe sequence where one property is missing from the first keyframe roundtrips
......@@ -98,8 +100,10 @@ PASS A KeyframeEffect can be constructed with a one property two keyframe sequen
PASS A KeyframeEffect constructed with a one property two keyframe sequence that needs to stringify its values roundtrips
PASS A KeyframeEffect can be constructed with a keyframe sequence with a CSS variable reference
PASS A KeyframeEffect constructed with a keyframe sequence with a CSS variable reference roundtrips
FAIL A KeyframeEffect can be constructed with a keyframe sequence with a CSS variable reference in a shorthand property assert_equals: properties on ComputedKeyframe #0 should match expected "composite,computedOffset,easing,margin,offset" but got "composite,computedOffset,easing,marginBottom,marginLeft,marginRight,marginTop,offset"
FAIL A KeyframeEffect constructed with a keyframe sequence with a CSS variable reference in a shorthand property roundtrips assert_equals: properties on ComputedKeyframe #0 should match expected "composite,computedOffset,easing,marginBottom,marginLeft,marginRight,marginTop,offset" but got "composite,computedOffset,easing,offset"
PASS A KeyframeEffect can be constructed with a keyframe sequence with a CSS variable reference in a shorthand property
PASS A KeyframeEffect constructed with a keyframe sequence with a CSS variable reference in a shorthand property roundtrips
PASS A KeyframeEffect can be constructed with a keyframe sequence with a CSS variable as its property
PASS A KeyframeEffect constructed with a keyframe sequence with a CSS variable as its property roundtrips
PASS A KeyframeEffect can be constructed with a keyframe sequence with duplicate values for a given interior offset
PASS A KeyframeEffect constructed with a keyframe sequence with duplicate values for a given interior offset roundtrips
PASS A KeyframeEffect can be constructed with a keyframe sequence with duplicate values for offsets 0 and 1
......
<!DOCTYPE html>
<meta charset=utf-8>
<title>KeyframeEffect getKeyframes()</title>
<link rel="help"
href="https://drafts.csswg.org/web-animations/#dom-keyframeeffect-getkeyframes">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../../resources/keyframe-utils.js"></script>
<script src="../../resources/keyframe-tests.js"></script>
<body>
<div id="log"></div>
<div id="target"></div>
<script>
'use strict';
const target = document.getElementById('target');
for (const subtest of gKeyframeSerializationTests) {
test(t => {
const effect = new KeyframeEffect(target, subtest.input);
assert_frame_lists_equal(effect.getKeyframes(), subtest.output);
}, `getKeyframes() should serialize its css values with ${subtest.desc}`);
}
</script>
\ No newline at end of file
This is a testharness.js-based test.
Found 76 tests; 69 PASS, 7 FAIL, 0 TIMEOUT, 0 NOTRUN.
Found 78 tests; 77 PASS, 1 FAIL, 0 TIMEOUT, 0 NOTRUN.
PASS Keyframes can be replaced with an empty keyframe
PASS Keyframes can be replaced with a one property two value property-indexed keyframes specification
FAIL Keyframes can be replaced with a one shorthand property two value property-indexed keyframes specification assert_equals: properties on ComputedKeyframe #0 should match expected "composite,computedOffset,easing,margin,offset" but got "composite,computedOffset,easing,marginBottom,marginLeft,marginRight,marginTop,offset"
FAIL Keyframes can be replaced with a two property (one shorthand and one of its longhand components) two value property-indexed keyframes specification assert_equals: properties on ComputedKeyframe #0 should match expected "composite,computedOffset,easing,margin,marginTop,offset" but got "composite,computedOffset,easing,marginBottom,marginLeft,marginRight,marginTop,offset"
PASS Keyframes can be replaced with a one shorthand property two value property-indexed keyframes specification
PASS Keyframes can be replaced with a two property (one shorthand and one of its longhand components) two value property-indexed keyframes specification
PASS Keyframes can be replaced with a two property two value property-indexed keyframes specification
PASS Keyframes can be replaced with a two property property-indexed keyframes specification with different numbers of values
PASS Keyframes can be replaced with a property-indexed keyframes specification with an invalid value
PASS Keyframes can be replaced with a one property two value property-indexed keyframes specification that needs to stringify its values
PASS Keyframes can be replaced with a property-indexed keyframes specification with a CSS variable reference
FAIL Keyframes can be replaced with a property-indexed keyframes specification with a CSS variable reference in a shorthand property assert_equals: properties on ComputedKeyframe #0 should match expected "composite,computedOffset,easing,margin,offset" but got "composite,computedOffset,easing,marginBottom,marginLeft,marginRight,marginTop,offset"
PASS Keyframes can be replaced with a property-indexed keyframes specification with a CSS variable reference in a shorthand property
PASS Keyframes can be replaced with a one property one value property-indexed keyframes specification
PASS Keyframes can be replaced with a one property one non-array value property-indexed keyframes specification
PASS Keyframes can be replaced with a one property two value property-indexed keyframes specification where the first value is invalid
PASS Keyframes can be replaced with a one property two value property-indexed keyframes specification where the second value is invalid
PASS Keyframes can be replaced with a property-indexed keyframes specification with a CSS variable as the property
PASS Keyframes can be replaced with a property-indexed keyframe with a single offset
PASS Keyframes can be replaced with a property-indexed keyframe with an array of offsets
PASS Keyframes can be replaced with a property-indexed keyframe with an array of offsets that is too short
......@@ -41,13 +42,14 @@ PASS Keyframes can be replaced with a property-indexed keyframe with a single-el
PASS Keyframes can be replaced with a one property one keyframe sequence
PASS Keyframes can be replaced with a one property two keyframe sequence
PASS Keyframes can be replaced with a two property two keyframe sequence
FAIL Keyframes can be replaced with a one shorthand property two keyframe sequence assert_equals: properties on ComputedKeyframe #0 should match expected "composite,computedOffset,easing,margin,offset" but got "composite,computedOffset,easing,marginBottom,marginLeft,marginRight,marginTop,offset"
FAIL Keyframes can be replaced with a two property (a shorthand and one of its component longhands) two keyframe sequence assert_equals: properties on ComputedKeyframe #0 should match expected "composite,computedOffset,easing,margin,marginTop,offset" but got "composite,computedOffset,easing,marginBottom,marginLeft,marginRight,marginTop,offset"
PASS Keyframes can be replaced with a one shorthand property two keyframe sequence
PASS Keyframes can be replaced with a two property (a shorthand and one of its component longhands) two keyframe sequence
PASS Keyframes can be replaced with a two property keyframe sequence where one property is missing from the first keyframe
PASS Keyframes can be replaced with a two property keyframe sequence where one property is missing from the last keyframe
PASS Keyframes can be replaced with a one property two keyframe sequence that needs to stringify its values
PASS Keyframes can be replaced with a keyframe sequence with a CSS variable reference
FAIL Keyframes can be replaced with a keyframe sequence with a CSS variable reference in a shorthand property assert_equals: properties on ComputedKeyframe #0 should match expected "composite,computedOffset,easing,margin,offset" but got "composite,computedOffset,easing,marginBottom,marginLeft,marginRight,marginTop,offset"
PASS Keyframes can be replaced with a keyframe sequence with a CSS variable reference in a shorthand property
PASS Keyframes can be replaced with a keyframe sequence with a CSS variable as its property
PASS Keyframes can be replaced with a keyframe sequence with duplicate values for a given interior offset
PASS Keyframes can be replaced with a keyframe sequence with duplicate values for offsets 0 and 1
PASS Keyframes can be replaced with a two property four keyframe sequence
......
......@@ -159,6 +159,13 @@ const gKeyframesTests = [
output: [keyframe(computedOffset(0), { left: '10px' }),
keyframe(computedOffset(1), {})]
},
{
desc: 'a property-indexed keyframes specification with a CSS variable as'
+ ' the property',
input: { '--custom': ['1', '2'] },
output: [keyframe(computedOffset(0), { '--custom': '1' }),
keyframe(computedOffset(1), { '--custom': '2' })]
},
// ----------- Property-indexed keyframes: offset handling -----------
......@@ -453,6 +460,13 @@ const gKeyframesTests = [
keyframe(computedOffset(1),
{ margin: 'calc(var(--dist) + 100px)' })],
},
{
desc: 'a keyframe sequence with a CSS variable as its property',
input: [{ '--custom': 'a' },
{ '--custom': 'b' }],
output: [keyframe(computedOffset(0), { '--custom': 'a' }),
keyframe(computedOffset(1), { '--custom': 'b' })]
},
// ----------- Keyframe sequence: offset handling -----------
......@@ -682,6 +696,18 @@ const gInvalidKeyframesTests = [
},
];
const gKeyframeSerializationTests = [
{
desc: 'a on keyframe sequence which requires value serilaization of its'
+ ' values',
input: [{offset: 0, backgroundColor: 'rgb(1,2,3)' }],
output: [keyframe(offset(0), { backgroundColor: 'rgb(1, 2, 3)' })],
},
];
// ------------------------------
// KeyframeEffectOptions
// ------------------------------
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment