<!doctype html>
<meta charset=utf-8>
<title>The effect value of a keyframe effect: Forwards-filling animations whose
  values depend on their context (target element)</title>
<link rel="help" href="https://drafts.csswg.org/web-animations/#calculating-computed-keyframes">
<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(t => {
  const div = createDiv(t);
  div.style.fontSize = '10px';
  const animation = div.animate(
    [{ marginLeft: '10em' }, { marginLeft: '20em' }],
    { duration: 1000, fill: 'forwards' }
  );
  animation.finish();
  assert_equals(
    getComputedStyle(div).marginLeft,
    '200px',
    'Effect value before updating font-size'
  );

  div.style.fontSize = '20px';

  assert_equals(
    getComputedStyle(div).marginLeft,
    '400px',
    'Effect value after updating font-size'
  );
}, 'Filling effect values reflect changes to font-size on element');

test(t => {
  const parentDiv = createDiv(t);
  const div = createDiv(t);
  parentDiv.appendChild(div);
  parentDiv.style.fontSize = '10px';

  const animation = div.animate(
    [{ marginLeft: '10em' }, { marginLeft: '20em' }],
    { duration: 1000, fill: 'forwards' }
  );
  animation.finish();
  assert_equals(
    getComputedStyle(div).marginLeft,
    '200px',
    'Effect value before updating font-size on parent element'
  );

  parentDiv.style.fontSize = '20px';

  assert_equals(
    getComputedStyle(div).marginLeft,
    '400px',
    'Effect value after updating font-size on parent element'
  );
}, 'Filling effect values reflect changes to font-size on parent element');

test(t => {
  const div = createDiv(t);
  div.style.setProperty('--target', '100px');
  const animation = div.animate(
    [{ marginLeft: '0px' }, { marginLeft: 'var(--target)' }],
    { duration: 1000, fill: 'forwards' }
  );
  animation.finish();
  assert_equals(
    getComputedStyle(div).marginLeft,
    '100px',
    'Effect value before updating variable'
  );

  div.style.setProperty('--target', '200px');

  assert_equals(
    getComputedStyle(div).marginLeft,
    '200px',
    'Effect value after updating variable'
  );
}, 'Filling effect values reflect changes to variables on element');

test(t => {
  const parentDiv = createDiv(t);
  const div = createDiv(t);
  parentDiv.appendChild(div);

  parentDiv.style.setProperty('--target', '10em');
  parentDiv.style.fontSize = '10px';

  const animation = div.animate(
    [{ marginLeft: '0px' }, { marginLeft: 'calc(var(--target) * 2)' }],
    { duration: 1000, fill: 'forwards' }
  );
  animation.finish();
  assert_equals(
    getComputedStyle(div).marginLeft,
    '200px',
    'Effect value before updating variable'
  );

  parentDiv.style.setProperty('--target', '20em');

  assert_equals(
    getComputedStyle(div).marginLeft,
    '400px',
    'Effect value after updating variable'
  );
}, 'Filling effect values reflect changes to variables on parent element');

test(t => {
  const div = createDiv(t);
  const animation = div.animate(
    [{ marginLeft: '100px' }, { marginLeft: '200px' }],
    { duration: 1000, fill: 'forwards' }
  );
  animation.finish();
  assert_equals(
    getComputedStyle(div).marginLeft,
    '200px',
    'Effect value before updating the animation'
  );

  animation.effect.setKeyframes([
    { marginLeft: '100px' },
    { marginLeft: '300px' },
  ]);

  assert_equals(
    getComputedStyle(div).marginLeft,
    '300px',
    'Effect value after updating the animation'
  );
}, 'Filling effect values reflect changes to the the animation\'s keyframes');

test(t => {
  const div = createDiv(t);
  div.style.marginLeft = '100px';
  const animation = div.animate(
    [{ marginLeft: '100px' }, { marginLeft: '200px' }],
    { duration: 1000, fill: 'forwards' }
  );
  animation.finish();
  assert_equals(
    getComputedStyle(div).marginLeft,
    '200px',
    'Effect value before updating the animation'
  );

  animation.effect.composite = 'add';

  assert_equals(
    getComputedStyle(div).marginLeft,
    '300px',
    'Effect value after updating the composite mode'
  );
}, 'Filling effect values reflect changes to the the animation\'s composite mode');

test(t => {
  const div = createDiv(t);
  const animation = div.animate(
    [{ marginLeft: '0px' }, { marginLeft: '100px' }],
    { duration: 1000, iterations: 2, fill: 'forwards' }
  );
  animation.finish();
  assert_equals(
    getComputedStyle(div).marginLeft,
    '100px',
    'Effect value before updating the animation'
  );

  animation.effect.iterationComposite = 'accumulate';

  assert_equals(
    getComputedStyle(div).marginLeft,
    '200px',
    'Effect value after updating the iteration composite mode'
  );
}, 'Filling effect values reflect changes to the the animation\'s iteration composite mode');

test(t => {
  const div = createDiv(t);
  div.style.marginLeft = '100px';
  const animation = div.animate(
    [{ marginLeft: '100px' }, { marginLeft: '200px' }],
    { duration: 1000, fill: 'forwards', composite: 'add' }
  );
  animation.finish();
  assert_equals(
    getComputedStyle(div).marginLeft,
    '300px',
    'Effect value before updating underlying value'
  );

  div.style.marginLeft = '200px';

  assert_equals(
    getComputedStyle(div).marginLeft,
    '400px',
    'Effect value after updating underlying value'
  );
}, 'Filling effect values reflect changes to the base value when using'
   + ' additive animation');

test(t => {
  const div = createDiv(t);
  div.style.marginLeft = '100px';
  const animation = div.animate(
    [{ marginLeft: '100px' }, { marginLeft: '200px', composite: 'add' }],
    { duration: 1000, fill: 'forwards' }
  );
  animation.finish();
  assert_equals(
    getComputedStyle(div).marginLeft,
    '300px',
    'Effect value before updating underlying value'
  );

  div.style.marginLeft = '200px';

  assert_equals(
    getComputedStyle(div).marginLeft,
    '400px',
    'Effect value after updating underlying value'
  );
}, 'Filling effect values reflect changes to the base value when using'
   + ' additive animation on a single keyframe');

test(t => {
  const div = createDiv(t);
  div.style.marginLeft = '0px';
  const animation = div.animate([{ marginLeft: '100px', offset: 0 }], {
    duration: 1000,
    fill: 'forwards',
  });
  animation.finish();
  assert_equals(
    getComputedStyle(div).marginLeft,
    '0px',
    'Effect value before updating underlying value'
  );

  div.style.marginLeft = '200px';

  assert_equals(
    getComputedStyle(div).marginLeft,
    '200px',
    'Effect value after updating underlying value'
  );
}, 'Filling effect values reflect changes to the base value when using'
   + ' the fill value is an implicit keyframe');

test(t => {
  const parentDiv = createDiv(t);
  const div = createDiv(t);
  parentDiv.appendChild(div);
  parentDiv.style.fontSize = '10px';
  div.style.marginLeft = '10em';
  // Computed underlying margin-left is 100px

  const animation = div.animate(
    [{ marginLeft: '100px' }, { marginLeft: '200px' }],
    { duration: 1000, fill: 'forwards', composite: 'add' }
  );
  animation.finish();
  assert_equals(
    getComputedStyle(div).marginLeft,
    '300px',
    'Effect value before updating font-size on parent'
  );

  parentDiv.style.fontSize = '20px';
  // Computed underlying margin-left is now 200px

  assert_equals(
    getComputedStyle(div).marginLeft,
    '400px',
    'Effect value after updating font-size on parent'
  );
}, 'Filling effect values reflect changes to the base value via a'
   + ' parent element');

test(t => {
  const div = createDiv(t);
  const animationA = div.animate(
    [{ marginLeft: '0px' }, { marginLeft: '100px' }],
    { duration: 2000, fill: 'forwards', easing: 'step-end' }
  );
  const animationB = div.animate(
    [{ marginLeft: '100px' }, { marginLeft: '200px' }],
    { duration: 1000, fill: 'forwards', composite: 'add' }
  );
  animationB.finish();
  assert_equals(
    getComputedStyle(div).marginLeft,
    '200px',
    'Effect value before updating underyling animation'
  );

  // Go to end of the underlying animation so that it jumps to 100px
  animationA.finish();

  assert_equals(
    getComputedStyle(div).marginLeft,
    '300px',
    'Effect value after updating underlying animation'
  );
}, 'Filling effect values reflect changes to underlying animations');

test(t => {
  const parentDiv = createDiv(t);
  const div = createDiv(t);
  parentDiv.appendChild(div);

  parentDiv.style.fontSize = '10px';

  const animationA = div.animate(
    [{ marginLeft: '0px' }, { marginLeft: '10em' }],
    { duration: 2000, fill: 'forwards', easing: 'step-start' }
  );
  const animationB = div.animate(
    [{ marginLeft: '100px' }, { marginLeft: '200px' }],
    { duration: 1000, fill: 'forwards', composite: 'add' }
  );
  animationB.finish();
  assert_equals(
    getComputedStyle(div).marginLeft,
    '300px',
    'Effect value before updating parent font-size'
  );

  parentDiv.style.fontSize = '20px';
  // At this point the underlying animation's output should jump to 200px.

  assert_equals(
    getComputedStyle(div).marginLeft,
    '400px',
    'Effect value after updating parent font-size'
  );
}, 'Filling effect values reflect changes to underlying animations via a'
   + ' a parent element');

test(t => {
  const div = createDiv(t);
  const animationA = div.animate(
    [{ marginLeft: '0px' }, { marginLeft: '0px' }],
    { duration: 2000, fill: 'forwards' }
  );
  const animationB = div.animate(
    [{ marginLeft: '100px' }, { marginLeft: '200px' }],
    { duration: 1000, fill: 'forwards', composite: 'add' }
  );
  animationB.finish();
  assert_equals(
    getComputedStyle(div).marginLeft,
    '200px',
    'Effect value before updating underyling animation'
  );

  animationA.effect.setKeyframes([
    { marginLeft: '100px' },
    { marginLeft: '100px' },
  ]);

  assert_equals(
    getComputedStyle(div).marginLeft,
    '300px',
    'Effect value after updating underlying animation'
  );
}, 'Filling effect values reflect changes to underlying animations made by'
   + ' directly changing the keyframes');

</script>
</body>
