Bug 1216844 - Implement KeyframeEffect::SetComposite(). r?boris,smaug
MozReview-Commit-ID: C9wHsriHgZ9
--- a/dom/animation/KeyframeEffect.cpp
+++ b/dom/animation/KeyframeEffect.cpp
@@ -150,16 +150,37 @@ KeyframeEffect::SetIterationComposite(
nsNodeUtils::AnimationChanged(mAnimation);
}
mEffectOptions.mIterationComposite = aIterationComposite;
RequestRestyle(EffectCompositor::RestyleType::Layer);
}
void
+KeyframeEffect::SetComposite(const CompositeOperation& aComposite)
+{
+ if (mEffectOptions.mComposite == aComposite) {
+ return;
+ }
+
+ mEffectOptions.mComposite = aComposite;
+
+ if (mAnimation && mAnimation->IsRelevant()) {
+ nsNodeUtils::AnimationChanged(mAnimation);
+ }
+
+ if (mTarget) {
+ RefPtr<nsStyleContext> styleContext = GetTargetStyleContext();
+ if (styleContext) {
+ UpdateProperties(styleContext);
+ }
+ }
+}
+
+void
KeyframeEffect::SetSpacing(JSContext* aCx,
const nsAString& aSpacing,
CallerType aCallerType,
ErrorResult& aRv)
{
SpacingMode spacingMode = SpacingMode::distribute;
nsCSSPropertyID pacedProperty = eCSSProperty_UNKNOWN;
nsAutoString invalidPacedProperty;
--- a/dom/animation/KeyframeEffect.h
+++ b/dom/animation/KeyframeEffect.h
@@ -80,14 +80,15 @@ public:
CallerType aCallerType, ErrorResult& aRv);
IterationCompositeOperation IterationComposite(CallerType aCallerType)
{
return KeyframeEffectReadOnly::IterationComposite();
}
void SetIterationComposite(
const IterationCompositeOperation& aIterationComposite,
CallerType aCallerType);
+ void SetComposite(const CompositeOperation& aComposite);
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_KeyframeEffect_h
--- a/dom/animation/test/chrome/test_observers_for_sync_api.html
+++ b/dom/animation/test/chrome/test_observers_for_sync_api.html
@@ -439,16 +439,39 @@ function createPseudo(test, element, typ
[{ added: [anim], changed: [], removed: [] }],
"records after animation is added");
anim.effect.spacing = "paced(margin-left)";
assert_equals_records(observer.takeRecords(),
[], "no record after setting the same spacing");
}, "set_the_same_spacing");
+ test(t => {
+ var div = addDiv(t);
+ var observer =
+ setupSynchronousObserver(t,
+ aOptions.subtree ? div.parentNode : div,
+ aOptions.subtree);
+
+ var anim = div.animate({ }, 100 * MS_PER_SEC);
+ assert_equals_records(observer.takeRecords(),
+ [{ added: [anim], changed: [], removed: [] }],
+ "records after animation is added");
+
+ anim.effect.composite = "add";
+ assert_equals_records(observer.takeRecords(),
+ [{ added: [], changed: [anim], removed: [] }],
+ "records after composite is changed");
+
+ anim.effect.composite = "add";
+ assert_equals_records(observer.takeRecords(),
+ [], "no record after setting the same composite");
+
+ }, "set_composite");
+
// Test that starting a single animation that is cancelled by calling
// cancel() dispatches an added notification and then a removed
// notification.
test(t => {
var div = addDiv(t, { style: "animation: anim 100s forwards" });
var observer =
setupSynchronousObserver(t,
aOptions.subtree ? div.parentNode : div,
--- a/dom/animation/test/style/file_composite.html
+++ b/dom/animation/test/style/file_composite.html
@@ -1,17 +1,17 @@
<!doctype html>
<meta charset=utf-8>
<script src="../testcommon.js"></script>
<script src="/tests/SimpleTest/paint_listener.js"></script>
<style>
div {
/* Element needs geometry to be eligible for layerization */
- width: 100px;
- height: 100px;
+ width: 20px;
+ height: 20px;
background-color: white;
}
</style>
<body>
<script>
'use strict';
if (!SpecialPowers.DOMWindowUtils.layerManagerRemote ||
@@ -99,11 +99,37 @@ promise_test(t => {
var transform =
SpecialPowers.DOMWindowUtils.getOMTAStyle(div, 'transform');
assert_matrix_equals(transform, 'matrix(1, 0, 0, 1, 250, 0)',
'Transform value at 50%');
});
}, 'Composite specified on a keyframe overrides the composite mode of the ' +
'effect');
+promise_test(t => {
+ useTestRefreshMode(t);
+
+ var div = addDiv(t);
+ div.animate({ transform: [ 'scale(2)', 'scale(2)' ] }, 100 * MS_PER_SEC);
+ var anim = div.animate({ transform: [ 'scale(4)', 'scale(4)' ] },
+ { duration: 100 * MS_PER_SEC, composite: 'add' });
+
+ return waitForPaintsFlushed().then(() => {
+ var transform =
+ SpecialPowers.DOMWindowUtils.getOMTAStyle(div, 'transform');
+ assert_matrix_equals(transform, 'matrix(8, 0, 0, 8, 0, 0)',
+ 'The additive scale value should be scale(8)'); // scale(2) scale(4)
+
+ anim.effect.composite = 'accumulate';
+ return waitForPaintsFlushed();
+ }).then(() => {
+ SpecialPowers.DOMWindowUtils.advanceTimeAndRefresh(1);
+ var transform =
+ SpecialPowers.DOMWindowUtils.getOMTAStyle(div, 'transform');
+ assert_matrix_equals(transform, 'matrix(5, 0, 0, 5, 0, 0)',
+ // (scale(2 - 1) + scale(4 - 1) + scale(1))
+ 'The accumulate scale value should be scale(5)');
+ });
+}, 'Composite operation change');
+
done();
</script>
</body>
--- a/dom/webidl/KeyframeEffect.webidl
+++ b/dom/webidl/KeyframeEffect.webidl
@@ -65,15 +65,14 @@ partial interface KeyframeEffectReadOnly
Constructor ((Element or CSSPseudoElement)? target,
object? keyframes,
optional (unrestricted double or KeyframeEffectOptions) options),
Constructor (KeyframeEffectReadOnly source)]
interface KeyframeEffect : KeyframeEffectReadOnly {
inherit attribute (Element or CSSPseudoElement)? target;
[NeedsCallerType]
inherit attribute IterationCompositeOperation iterationComposite;
- // Bug 1216844 - implement additive animation
- // inherit attribute CompositeOperation composite;
+ inherit attribute CompositeOperation composite;
[SetterThrows, NeedsCallerType]
inherit attribute DOMString spacing;
[Throws]
void setKeyframes (object? keyframes);
};
--- a/testing/web-platform/meta/MANIFEST.json
+++ b/testing/web-platform/meta/MANIFEST.json
@@ -39592,16 +39592,22 @@
}
],
"web-animations/animation-model/combining-effects/effect-composition.html": [
{
"path": "web-animations/animation-model/combining-effects/effect-composition.html",
"url": "/web-animations/animation-model/combining-effects/effect-composition.html"
}
],
+ "web-animations/interfaces/KeyframeEffect/composite.html": [
+ {
+ "path": "web-animations/interfaces/KeyframeEffect/composite.html",
+ "url": "/web-animations/interfaces/KeyframeEffect/composite.html"
+ }
+ ],
"web-animations/interfaces/KeyframeEffect/copy-contructor.html": [
{
"path": "web-animations/interfaces/KeyframeEffect/copy-contructor.html",
"url": "/web-animations/interfaces/KeyframeEffect/copy-contructor.html"
}
],
"web-animations/interfaces/KeyframeEffectReadOnly/copy-contructor.html": [
{
--- a/testing/web-platform/tests/web-animations/animation-model/combining-effects/effect-composition.html
+++ b/testing/web-platform/tests/web-animations/animation-model/combining-effects/effect-composition.html
@@ -60,11 +60,26 @@
{ marginLeft: '20px' }],
{ duration: 100 , composite: composite });
anim.currentTime = 50;
assert_equals(getComputedStyle(div).marginLeft, '20px',
'Animated style at 50%');
}, composite + ' specified on a keyframe overrides the composite mode of ' +
'the effect');
+
+ test(function(t) {
+ var div = createDiv(t);
+ div.style.marginLeft = '10px';
+ var anim =
+ div.animate([{ marginLeft: '10px', composite: 'replace' },
+ { marginLeft: '20px' }],
+ 100);
+
+ anim.effect.composite = composite;
+ anim.currentTime = 50; // (10 + (10 + 20)) * 0.5
+ assert_equals(getComputedStyle(div).marginLeft, '20px',
+ 'Animated style at 50%');
+ }, 'unspecified composite mode on a keyframe is overriden by setting ' +
+ composite + ' of the effect');
});
</script>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/web-animations/interfaces/KeyframeEffect/composite.html
@@ -0,0 +1,47 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>KeyframeEffect.composite tests</title>
+<link rel="help"
+ href="https://w3c.github.io/web-animations/#dom-keyframeeffect-composite">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../../testcommon.js"></script>
+<body>
+<div id="log"></div>
+<script>
+'use strict';
+
+test(function(t) {
+ var anim = createDiv(t).animate(null);
+ assert_equals(anim.effect.composite, 'replace',
+ 'The default value should be replace');
+}, 'Default value');
+
+test(function(t) {
+ var anim = createDiv(t).animate(null);
+ anim.effect.composite = 'add';
+ assert_equals(anim.effect.composite, 'add',
+ 'The effect composite value should be replaced');
+}, 'Change composite value');
+
+test(function(t) {
+ var anim = createDiv(t).animate({ left: '10px' });
+
+ anim.effect.composite = 'add';
+ var keyframes = anim.effect.getKeyframes();
+ assert_equals(keyframes[0].composite, undefined,
+ 'unspecified keyframe composite value should be absent even ' +
+ 'if effect composite is set');
+}, 'Unspecified keyframe composite value when setting effect composite');
+
+test(function(t) {
+ var anim = createDiv(t).animate({ left: '10px', composite: 'replace' });
+
+ anim.effect.composite = 'add';
+ var keyframes = anim.effect.getKeyframes();
+ assert_equals(keyframes[0].composite, 'replace',
+ 'specified keyframe composite value should not be overridden ' +
+ 'by setting effect composite');
+}, 'Specified keyframe composite value when setting effect composite');
+
+</script>