Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-animations/KeyframeEffect-getKeyframes.tentative.html (288570 => 288571)
--- trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-animations/KeyframeEffect-getKeyframes.tentative.html 2022-01-25 19:38:36 UTC (rev 288570)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-animations/KeyframeEffect-getKeyframes.tentative.html 2022-01-25 19:41:29 UTC (rev 288571)
@@ -125,6 +125,28 @@
to { margin-top: 20px; margin-right: 20px; margin-bottom: 20px; }
}
+@keyframes anim-merge-offset-and-composite {
+ from { color: rgb(0, 0, 0); animation-composition: add; }
+ to { color: rgb(255, 255, 255); }
+ from { margin-top: 8px; animation-composition: accumulate; }
+ to { margin-top: 16px; }
+ from { font-size: 16px; animation-composition: add; }
+ to { font-size: 32px; }
+ from { padding-left: 2px; animation-composition: accumulate; }
+ to { padding-left: 4px; }
+}
+
+@keyframes anim-merge-offset-easing-and-composite {
+ from { color: rgb(0, 0, 0); animation-composition: add; }
+ to { color: rgb(255, 255, 255); }
+ from { margin-top: 8px; animation-composition: accumulate; }
+ to { margin-top: 16px; }
+ from { font-size: 16px; animation-composition: add; animation-timing-function: linear; }
+ to { font-size: 32px; }
+ from { padding-left: 2px; animation-composition: accumulate; }
+ to { padding-left: 4px; }
+}
+
@keyframes anim-overriding {
from { padding-top: 50px }
50%, from { padding-top: 30px } /* wins: 0% */
@@ -530,6 +552,48 @@
test(t => {
const div = addDiv(t);
+ div.style.animation = 'anim-merge-offset-and-composite 100s';
+
+ const frames = getKeyframes(div);
+
+ const expected = [
+ { offset: 0, computedOffset: 0, easing: "ease", composite: "add",
+ color: "rgb(0, 0, 0)", fontSize: "16px" },
+ { offset: 0, computedOffset: 0, easing: "ease", composite: "accumulate",
+ marginTop: "8px", paddingLeft: "2px" },
+ { offset: 1, computedOffset: 1, easing: "ease", composite: "auto",
+ color: "rgb(255, 255, 255)", fontSize: "32px", marginTop: "16px",
+ paddingLeft: "4px" },
+ ];
+ assert_frame_lists_equal(frames, expected);
+}, 'KeyframeEffect.getKeyframes() returns expected frames for an ' +
+ 'animation with multiple keyframes for the same time and with ' +
+ 'different composite operations');
+
+test(t => {
+ const div = addDiv(t);
+ div.style.animation = 'anim-merge-offset-easing-and-composite 100s';
+
+ const frames = getKeyframes(div);
+
+ const expected = [
+ { offset: 0, computedOffset: 0, easing: "ease", composite: "add",
+ color: "rgb(0, 0, 0)" },
+ { offset: 0, computedOffset: 0, easing: "ease", composite: "accumulate",
+ marginTop: "8px", paddingLeft: "2px" },
+ { offset: 0, computedOffset: 0, easing: "linear", composite: "add",
+ fontSize: "16px" },
+ { offset: 1, computedOffset: 1, easing: "ease", composite: "auto",
+ color: "rgb(255, 255, 255)", fontSize: "32px", marginTop: "16px",
+ paddingLeft: "4px" },
+ ];
+ assert_frame_lists_equal(frames, expected);
+}, 'KeyframeEffect.getKeyframes() returns expected frames for an ' +
+ 'animation with multiple keyframes for the same time and with ' +
+ 'different easing functions and composite operations');
+
+test(t => {
+ const div = addDiv(t);
div.style.animation = 'anim-overriding 100s';
const frames = getKeyframes(div);
Modified: trunk/Source/WebCore/style/StyleResolver.cpp (288570 => 288571)
--- trunk/Source/WebCore/style/StyleResolver.cpp 2022-01-25 19:38:36 UTC (rev 288570)
+++ trunk/Source/WebCore/style/StyleResolver.cpp 2022-01-25 19:41:29 UTC (rev 288571)
@@ -320,6 +320,14 @@
if (it == m_keyframesRuleMap.end())
return { };
+ auto compositeOperationForKeyframe = [](Ref<StyleRuleKeyframe> keyframe) -> CompositeOperation {
+ if (auto compositeOperationCSSValue = keyframe->properties().getPropertyCSSValue(CSSPropertyAnimationComposition)) {
+ if (auto compositeOperation = toCompositeOperation(*compositeOperationCSSValue))
+ return *compositeOperation;
+ }
+ return Animation::initialCompositeOperation();
+ };
+
auto timingFunctionForKeyframe = [](Ref<StyleRuleKeyframe> keyframe) -> RefPtr<const TimingFunction> {
if (auto timingFunctionCSSValue = keyframe->properties().getPropertyCSSValue(CSSPropertyAnimationTimingFunction)) {
if (auto timingFunction = TimingFunction::createFromCSSValue(*timingFunctionCSSValue))
@@ -342,13 +350,14 @@
auto* keyframesRule = it->value.get();
auto* keyframes = &keyframesRule->keyframes();
- using KeyframeUniqueKey = std::pair<double, RefPtr<const TimingFunction>>;
+ using KeyframeUniqueKey = std::tuple<double, RefPtr<const TimingFunction>, CompositeOperation>;
auto hasDuplicateKeys = [&]() -> bool {
HashSet<KeyframeUniqueKey> uniqueKeyframeKeys;
for (auto& keyframe : *keyframes) {
+ auto compositeOperation = compositeOperationForKeyframe(keyframe);
auto timingFunction = uniqueTimingFunctionForKeyframe(keyframe);
for (auto key : keyframe->keys()) {
- if (!uniqueKeyframeKeys.add({ key, timingFunction }))
+ if (!uniqueKeyframeKeys.add({ key, timingFunction, compositeOperation }))
return true;
}
}
@@ -360,19 +369,21 @@
// FIXME: If HashMaps could have Ref<> as value types, we wouldn't need
// to copy the HashMap into a Vector.
+ // Merge keyframes with a similar offset and timing function.
Vector<Ref<StyleRuleKeyframe>> deduplicatedKeyframes;
- // Merge keyframes with a similar offset and timing function.
HashMap<KeyframeUniqueKey, RefPtr<StyleRuleKeyframe>> keyframesMap;
for (auto& originalKeyframe : *keyframes) {
+ auto compositeOperation = compositeOperationForKeyframe(originalKeyframe);
auto timingFunction = uniqueTimingFunctionForKeyframe(originalKeyframe);
for (auto key : originalKeyframe->keys()) {
- if (auto keyframe = keyframesMap.get({ key, timingFunction }))
+ KeyframeUniqueKey uniqueKey { key, timingFunction, compositeOperation };
+ if (auto keyframe = keyframesMap.get(uniqueKey))
keyframe->mutableProperties().mergeAndOverrideOnConflict(originalKeyframe->properties());
else {
auto styleRuleKeyframe = StyleRuleKeyframe::create(MutableStyleProperties::create());
styleRuleKeyframe.ptr()->setKey(key);
styleRuleKeyframe.ptr()->mutableProperties().mergeAndOverrideOnConflict(originalKeyframe->properties());
- keyframesMap.set({ key, timingFunction }, styleRuleKeyframe.ptr());
+ keyframesMap.set(uniqueKey, styleRuleKeyframe.ptr());
deduplicatedKeyframes.append(styleRuleKeyframe);
}
}