dom/base/test/file_domwindowutils_animation.html
author Hiroyuki Ikezoe <hikezoe@mozilla.com>
Wed, 22 Nov 2017 09:57:31 +0900
changeset 701865 51ab3c3c9935e8cd332a26ab2facfd35c7ecb8b7
parent 701343 696edf3997a4e70e0502fa7d9a6b8bc2ab0bb22b
child 763029 750ce6bc769be8227ccc3d716eb9deafa46ef7d6
permissions -rw-r--r--
Bug 1418867 - getUnanimatedComputedStyle throws an exception for non-existent pseudo element. r=birtles This is a prerequisite change for passing pseudo element to Servo_StyleSet_GetBaseComputedValuesForElement which will be done in the next commit. MozReview-Commit-ID: HEGF2wjBGEP

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>DOMWindowUtils test with animation</title>
</head>
<body>
<script type="application/javascript">

const SimpleTest = window.opener.SimpleTest;
const utils = SpecialPowers.getDOMWindowUtils(window);
const next = window.opener.next;
const is = window.opener.is;
const ok = window.opener.ok;

function addStyle(rules) {
  const extraStyle = document.createElement("style");
  document.head.appendChild(extraStyle);
  rules.forEach(rule => {
    extraStyle.sheet.insertRule(rule, extraStyle.sheet.cssRules.length);
  });
}

function deleteStyle() {
  document.head.querySelector("style").remove();
}


function test_getUnanimatedComputedStyle() {
  [
    {
      property: "opacity",
      keyframes: [1, 0],
      expectedInitialStyle: "1",
      expectedDuringTransitionStyle: "0",
      isDiscrete: false,
    },
    {
      property: "clear",
      keyframes: ["left", "inline-end"],
      expectedInitialStyle: "none",
      expectedDuringTransitionStyle: "inline-end",
      isDiscrete: true,
    },
  ].forEach(testcase => {
    const { property, keyframes, expectedInitialStyle,
            expectedDuringTransitionStyle, isDiscrete } = testcase;

    [null, "unset", "initial", "inherit"].forEach(initialStyle => {
      const scriptAnimation = target => {
        return target.animate({ [property]: keyframes }, 1000);
      }
      checkUnanimatedComputedStyle(property, initialStyle, null,
                                   expectedInitialStyle, expectedInitialStyle,
                                   scriptAnimation, "script animation");

      const cssAnimationStyle = `@keyframes cssanimation {`
                                + ` from { ${property}: ${ keyframes[0] }; }`
                                + ` to { ${property}: ${ keyframes[1] }; } }`;
      addStyle([cssAnimationStyle]);
      const cssAnimation = target => {
        target.style.animation = "cssanimation 1s";
        return target.getAnimations()[0];
      }
      checkUnanimatedComputedStyle(property, initialStyle, null,
                                   expectedInitialStyle, expectedInitialStyle,
                                   cssAnimation, "CSS Animations");
      deleteStyle();

      // We don't support discrete animations for CSS Transitions yet.
      // (bug 1320854)
      if (!isDiscrete) {
        const cssTransition = target => {
          target.style[property] = keyframes[0];
          target.style.transition =
            `${ property } 1s`;
          window.getComputedStyle(target)[property];
          target.style[property] = keyframes[1];
          return target.getAnimations()[0];
        }
        checkUnanimatedComputedStyle(property, initialStyle, null,
                                     expectedInitialStyle,
                                     expectedDuringTransitionStyle,
                                     cssTransition, "CSS Transitions");
      }

      addStyle([cssAnimationStyle,
                ".pseudo::before { content: '' }",
                ".animation::before { animation: cssanimation 1s }"]);
      const pseudoAnimation = target => {
        target.classList.add("animation");
        return target.getAnimations({ subtree: true })[0];
      }
      checkUnanimatedComputedStyle(property, initialStyle, "::before",
                                   expectedInitialStyle, expectedInitialStyle,
                                   pseudoAnimation, "Animation at pseudo");
      deleteStyle();
    });
  });

  const div = document.createElement("div");
  document.body.appendChild(div);

  SimpleTest.doesThrow(
    () => utils.getUnanimatedComputedStyle(div, null, "background", utils.FLUSH_NONE),
    "NS_ERROR_INVALID_ARG",
    "Shorthand property should throw");

  SimpleTest.doesThrow(
    () => utils.getUnanimatedComputedStyle(div, null, "invalid", utils.FLUSH_NONE),
    "NS_ERROR_INVALID_ARG",
    "Invalid property should throw");

  SimpleTest.doesThrow(
    () => utils.getUnanimatedComputedStyle(null, null, "opacity", utils.FLUSH_NONE),
    "NS_ERROR_INVALID_ARG",
    "Null element should throw");

  SimpleTest.doesThrow(
    () => utils.getUnanimatedComputedStyle(div, null, "opacity", utils.FLUSH_LAYOUT),
    "NS_ERROR_INVALID_ARG",
    "FLUSH_LAYOUT option should throw");

  SimpleTest.doesThrow(
    () => utils.getUnanimatedComputedStyle(div, null, "opacity", utils.FLUSH_DISPLAY),
    "NS_ERROR_INVALID_ARG",
    "FLUSH_DISPLAY option should throw");

  if (utils.isStyledByServo) {
    SimpleTest.doesThrow(
      () => utils.getUnanimatedComputedStyle(div, "::before", "opacity", utils.FLUSH_NONE),
      "NS_ERROR_FAILURE",
      "Non-existent pseudo should throw");

    // Flush styles since getUnanimatedComputedStyle flushes pending styles even
    // with FLUSH_NONE option if the element hasn't yet styled.
    getComputedStyle(div).opacity;

    div.style.opacity = "0";
    is(utils.getUnanimatedComputedStyle(div, null, "opacity", utils.FLUSH_NONE),
       "1",
       "getUnanimatedComputedStyle with FLUSH_NONE should not flush pending styles");

    is(utils.getUnanimatedComputedStyle(div, null, "opacity", utils.FLUSH_STYLE),
       "0",
       "getUnanimatedComputedStyle with FLUSH_STYLE should flush pending styles");
  }

  div.remove();

  next();
  window.close();
}

function checkUnanimatedComputedStyle(property, initialStyle, pseudoType,
                                      expectedBeforeAnimation,
                                      expectedDuringAnimation,
                                      animate, animationType) {
  const div = document.createElement("div");
  document.body.appendChild(div);

  if (initialStyle) {
    div.style[property] = initialStyle;
  }
  if (pseudoType) {
    div.classList.add("pseudo");
  }

  is(utils.getUnanimatedComputedStyle(div, pseudoType, property, utils.FLUSH_STYLE),
     expectedBeforeAnimation,
     `'${ property }' property with '${ initialStyle }' style `
     + `should be '${ expectedBeforeAnimation }' `
     + `before animating by ${ animationType }`);

  const animation = animate(div);
  animation.currentTime = 500;
  is(utils.getUnanimatedComputedStyle(div, pseudoType, property, utils.FLUSH_STYLE),
     expectedDuringAnimation,
     `'${ property }' property with '${ initialStyle }' style `
     + `should be '${ expectedDuringAnimation }' `
     + `even while animating by ${ animationType }`);

  div.remove();
}

window.addEventListener("load", test_getUnanimatedComputedStyle);

</script>
</body>
</html>