<!DOCTYPE HTML>
<html>
<head>
  <title>Video controls test</title>
  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
  <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<p id="display"></p>

<div id="content">
  <video width="320" height="240" id="video" controls mozNoDynamicControls preload="auto"></video>
</div>

<pre id="test">
<script class="testbody" type="text/javascript">

/*
 * Positions of the  UI elements, relative to the upper-left corner of the
 * <video> box.
 */
const videoWidth = 320;
const videoHeight = 240;
const videoDuration = 3.8329999446868896;

const playButtonWidth = 28;
const playButtonHeight = 28;
const muteButtonWidth = 33;
const muteButtonHeight = 28;
const durationWidth = 34;
const fullscreenButtonWidth = 28;
const fullscreenButtonHeight = 28;
const volumeSliderWidth = 32;
const scrubberWidth = videoWidth - playButtonWidth - durationWidth - muteButtonWidth - volumeSliderWidth - fullscreenButtonWidth;
const scrubberHeight = 28;

// Play button is on the bottom-left
const playButtonCenterX = 0 + Math.round(playButtonWidth / 2);
const playButtonCenterY = videoHeight - Math.round(playButtonHeight / 2);
// Mute button is on the bottom-right before the full screen button and volume slider
const muteButtonCenterX = videoWidth - Math.round(muteButtonWidth / 2) - volumeSliderWidth - fullscreenButtonWidth;
const muteButtonCenterY = videoHeight - Math.round(muteButtonHeight / 2);
// Fullscreen button is on the bottom-right at the far end
const fullscreenButtonCenterX = videoWidth - Math.round(fullscreenButtonWidth / 2);
const fullscreenButtonCenterY = videoHeight - Math.round(fullscreenButtonHeight / 2);
// Scrubber bar is between the play and mute buttons. We don't need it's
// X center, just the offset of its box.
const scrubberOffsetX = 0 + playButtonWidth;
const scrubberCenterY = videoHeight - Math.round(scrubberHeight / 2);

var testnum = 1;
var video = document.getElementById("video");

const domUtil = SpecialPowers.Cc["@mozilla.org/inspector/dom-utils;1"]
                             .getService(SpecialPowers.Ci.inIDOMUtils);

function getButtonByAttribute(aName, aValue) {
  var kids = domUtil.getChildrenForNode(video, true);
  var videocontrols = kids[1];
  return SpecialPowers.wrap(document)
    .getAnonymousElementByAttribute(videocontrols, aName, aValue);
}

function isMuteButtonMuted() {
  var muteButton = getButtonByAttribute('class', 'muteButton');
  return muteButton.getAttribute('muted') === 'true';
}

function isVolumeSliderShowingCorrectVolume(expectedVolume) {
  var volumeButton = getButtonByAttribute('anonid', 'volumeForeground');
  let expectedPaddingRight = (1 - expectedVolume) * volumeSliderWidth + "px";
  is(volumeButton.style.paddingRight, expectedPaddingRight,
     "volume slider should match expected volume");
}

function forceReframe() {
  // Setting display then getting the bounding rect to force a frame
  // reconstruction on the video element.
  video.style.display = "block";
  video.getBoundingClientRect();
  video.style.display = "";
  video.getBoundingClientRect();
}

function runTest(event) {
  ok(true, "----- test #" + testnum + " -----");

  switch (testnum) {
    /*
     * Check operation of play/pause/mute/unmute buttons.
     */
    case 1:
      // Check initial state upon load
      is(event.type, "canplaythrough", "checking event type");
      is(video.paused, true, "checking video play state");
      is(video.muted, false, "checking video mute state");

      // Click the play button
      SimpleTest.executeSoon(() => {
        synthesizeMouse(video, playButtonCenterX, playButtonCenterY, { });
      });
      break;

    case 2:
      is(event.type, "play",  "checking event type");
      is(video.paused, false, "checking video play state");
      is(video.muted, false,  "checking video mute state");

      // Click the pause button
      SimpleTest.executeSoon(() => {
        synthesizeMouse(video, playButtonCenterX, playButtonCenterY, { });
      });
      break;

    case 3:
      is(event.type, "pause", "checking event type");
      is(video.paused, true,  "checking video play state");
      is(video.muted, false,  "checking video mute state");

      SimpleTest.executeSoon(() => {
        synthesizeMouse(video, muteButtonCenterX, muteButtonCenterY, { }); // Mute.
      });
      break;

    case 4:
      is(event.type, "volumechange", "checking event type");
      is(video.paused, true,  "checking video play state");
      is(video.muted,  true,  "checking video mute state");

      SimpleTest.executeSoon(() => {
        synthesizeMouse(video, muteButtonCenterX, muteButtonCenterY, { }); // Unmute.
      });
      break;

    /*
     * Bug 470596: Make sure that having CSS border or padding doesn't
     * break the controls (though it should move them)
     */
    case 5:
      is(event.type, "volumechange", "checking event type");
      is(video.paused, true,  "checking video play state");
      is(video.muted,  false, "checking video mute state");

      video.style.border = "medium solid purple";
      video.style.borderWidth = "30px 40px 50px 60px";
      video.style.padding = "10px 20px 30px 40px";
      // totals: top: 40px, right: 60px, bottom: 80px, left: 100px

      // Click the play button
      SimpleTest.executeSoon(() => {
        synthesizeMouse(video, 100 + playButtonCenterX, 40 + playButtonCenterY, { });
      });
      break;

    case 6:
      is(event.type, "play",  "checking event type");
      is(video.paused, false, "checking video play state");
      is(video.muted, false,  "checking video mute state");
      video.pause();
      break;

    case 7:
      is(event.type, "pause",  "checking event type");
      is(video.paused, true, "checking video play state");
      is(video.muted, false,  "checking video mute state");

      // Click the mute button
      SimpleTest.executeSoon(() => {
        synthesizeMouse(video, 100 + muteButtonCenterX, 40 + muteButtonCenterY, { });
      });
      break;

    case 8:
      is(event.type, "volumechange", "checking event type");
      is(video.paused, true,  "checking video play state");
      is(video.muted,  true,  "checking video mute state");
      // Clear the style set in test 5.
      video.style.border = "";
      video.style.borderWidth = "";
      video.style.padding = "";

      video.muted = false;
      break;

    /*
     * Previous tests have moved playback postion, reset it to 0.
     */
    case 9:
      is(event.type, "volumechange", "checking event type");
      is(video.paused, true,  "checking video play state");
      is(video.muted,  false,  "checking video mute state");
      ok(true, "video position is at " + video.currentTime);
      video.currentTime = 0.0;
      break;

    case 10:
      is(event.type, "seeking", "checking event type");
      ok(true, "video position is at " + video.currentTime);
      break;

    /*
     * Drag the slider's thumb to the halfway point with the mouse.
     */
    case 11:
      is(event.type, "seeked", "checking event type");
      ok(true, "video position is at " + video.currentTime);
      // Bug 477434 -- sometimes we get 0.098999 here instead of 0!
      // is(video.currentTime, 0.0, "checking playback position");

      SimpleTest.executeSoon(() => {
        var beginDragX = scrubberOffsetX;
        var endDragX = scrubberOffsetX + (scrubberWidth / 2);
        synthesizeMouse(video, beginDragX, scrubberCenterY, { type: "mousedown", button: 0 });
        synthesizeMouse(video, endDragX,   scrubberCenterY, { type: "mousemove", button: 0 });
        synthesizeMouse(video, endDragX,   scrubberCenterY, { type: "mouseup",   button: 0 });
      });
      break;

    case 12:
      is(event.type, "seeking", "checking event type");
      ok(true, "video position is at " + video.currentTime);
      break;

    /*
     * Click the slider at the 1/4 point with the mouse (jump backwards)
     */
    case 13:
      is(event.type, "seeked", "checking event type");
      ok(true, "video position is at " + video.currentTime);
      var expectedTime = videoDuration / 2;
      ok(Math.abs(video.currentTime - expectedTime) < 0.1, "checking expected playback position");

      SimpleTest.executeSoon(() => {
        synthesizeMouse(video, scrubberOffsetX + (scrubberWidth / 4), scrubberCenterY, { });
      });
      break;

    case 14:
      is(event.type, "seeking", "checking event type");
      ok(true, "video position is at " + video.currentTime);
      break;

    case 15:
      is(event.type, "seeked", "checking event type");
      ok(true, "video position is at " + video.currentTime);
      // The scrubber currently just jumps towards the nearest pageIncrement point, not
      // precisely to the point clicked. So, expectedTime isn't (videoDuration / 4).
      // We should end up at 1.733, but sometimes we end up at 1.498. I guess
      // it's timing depending if the <scale> things it's click-and-hold, or a
      // single click. So, just make sure we end up less that the previous
      // position.
      lastPosition = (videoDuration / 2) - 0.1;
      ok(video.currentTime < lastPosition, "checking expected playback position");

      // Set volume to 0.1 so one down arrow hit will decrease it to 0.
      video.volume = 0.1;
      break;

    // See bug 694696.
    case 16:
      is(event.type, "volumechange", "checking event type");
      is(video.volume, 0.1, "Volume should be set.");
      ok(!video.muted, "Video is not muted.");

      video.focus();
      SimpleTest.executeSoon(() => synthesizeKey("VK_DOWN", {}));
      break;

    case 17:
      is(event.type, "volumechange", "checking event type");
      is(video.volume, 0, "Volume should be 0.");
      ok(!video.muted, "Video is not muted.");

      SimpleTest.executeSoon(() => {
        ok(isMuteButtonMuted(), "Mute button says it's muted");
        synthesizeKey("VK_UP", {});
      });
      break;

    case 18:
      is(event.type, "volumechange", "checking event type");
      is(video.volume, 0.1, "Volume is increased.");
      ok(!video.muted, "Video is not muted.");

      SimpleTest.executeSoon(() => {
        ok(!isMuteButtonMuted(), "Mute button says it's not muted");
        synthesizeKey("VK_DOWN", {});
      });
      break;

    case 19:
      is(event.type, "volumechange", "checking event type");
      is(video.volume, 0, "Volume should be 0.");
      ok(!video.muted, "Video is not muted.");

      SimpleTest.executeSoon(() => {
        ok(isMuteButtonMuted(), "Mute button says it's muted");
        synthesizeMouse(video, muteButtonCenterX, muteButtonCenterY, { });
      });
      break;

    case 20:
      is(event.type, "volumechange", "checking event type");
      is(video.volume, 0.5, "Volume should be 0.5.");
      ok(!video.muted, "Video is not muted.");

      SimpleTest.executeSoon(() => synthesizeKey("VK_UP", {}));
      break;

    case 21:
      is(event.type, "volumechange", "checking event type");
      is(video.volume, 0.6, "Volume should be 0.6.");
      ok(!video.muted, "Video is not muted.");

      SimpleTest.executeSoon(() => {
        synthesizeMouse(video, muteButtonCenterX, muteButtonCenterY, { });
      });
      break;

    case 22:
      is(event.type, "volumechange", "checking event type");
      is(video.volume, 0.6, "Volume should be 0.6.");
      ok(video.muted, "Video is muted.");

      SimpleTest.executeSoon(() => {
        ok(isMuteButtonMuted(), "Mute button says it's muted");
        synthesizeMouse(video, muteButtonCenterX, muteButtonCenterY, { });
      });
      break;

    case 23:
      is(event.type, "volumechange", "checking event type");
      is(video.volume, 0.6, "Volume should be 0.6.");
      ok(!video.muted, "Video is not muted.");

      SimpleTest.executeSoon(() => {
        ok(!isMuteButtonMuted(), "Mute button says it's not muted");
        synthesizeMouse(video, fullscreenButtonCenterX, fullscreenButtonCenterY, { });
      });
      break;

    case 24:
      is(event.type, "mozfullscreenchange", "checking event type");
      is(video.volume, 0.6, "Volume should still be 0.6");
      SimpleTest.executeSoon(function() {
        isVolumeSliderShowingCorrectVolume(video.volume);
        synthesizeKey("VK_ESCAPE", {});
      });
      break;

    case 25:
      is(event.type, "mozfullscreenchange", "checking event type");
      is(video.volume, 0.6, "Volume should still be 0.6");
      SimpleTest.executeSoon(function() {
        isVolumeSliderShowingCorrectVolume(video.volume);
        forceReframe();
        video.focus();
        synthesizeKey("VK_DOWN", {});
      });
      break;

    case 26:
      is(event.type, "volumechange", "checking event type");
      is(video.volume, 0.5, "Volume should be decreased by 0.1");
      SimpleTest.executeSoon(function() {
        isVolumeSliderShowingCorrectVolume(video.volume);
        SimpleTest.finish();
      });
      break;

    default:
      throw "unexpected test #" + testnum + " w/ event " + event.type;
  }

  testnum++;
}



function canplaythroughevent(event) {
  video.removeEventListener("canplaythrough",  canplaythroughevent, false);
  // Other events expected by the test.
  video.addEventListener("play",  runTest, false);
  video.addEventListener("pause", runTest, false);
  video.addEventListener("volumechange", runTest, false);
  video.addEventListener("seeking", runTest, false);
  video.addEventListener("seeked", runTest, false);
  document.addEventListener("mozfullscreenchange", runTest, false);
  // Begin the test.
  runTest(event);
}

function startMediaLoad() {
  // Kick off test once video has loaded, in its canplaythrough event handler.
  video.src = "seek_with_sound.ogg";
  video.addEventListener("canplaythrough", canplaythroughevent, false);
}

function loadevent(event) {
  SpecialPowers.pushPrefEnv({"set": [["media.cache_size", 40000]]}, startMediaLoad);
}

window.addEventListener("load",  loadevent, false);

SimpleTest.waitForExplicitFinish();

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