/**
 * Test whether the autoplay permission can be transfer well through different
 * frame hierarchy. When the target frame has been interacted with the user
 * gesture, it would has the autoplay permission, then the permission would be
 * propagated to its parent and child frames.
 *
 * In this test, I use A/B/C to indicate different domain frames, and the number
 * after the name is which layer the frame is in.
 * Ex. A1 -> B2 -> A3,
 * Top frame and grandchild frame is in the domain A, and child frame is in the
 * domain B.
 *
 * Once any document in a tab is activated, all other documents in that tab
 * should be considered activated, even if they're cross origin from the
 * originally activated document.
 */
const PAGE_A1_A2 = "https://example.com/browser/toolkit/content/tests/browser/file_autoplay_two_layers_frame1.html";
const PAGE_A1_B2 = "https://example.com/browser/toolkit/content/tests/browser/file_autoplay_two_layers_frame2.html";
const PAGE_A1_B2_C3 = "https://test1.example.org/browser/toolkit/content/tests/browser/file_autoplay_three_layers_frame1.html";
const PAGE_A1_B2_A3 = "https://example.org/browser/toolkit/content/tests/browser/file_autoplay_three_layers_frame1.html";
const PAGE_A1_B2_B3 = "https://example.org/browser/toolkit/content/tests/browser/file_autoplay_three_layers_frame2.html";
const PAGE_A1_A2_A3 = "https://example.com/browser/toolkit/content/tests/browser/file_autoplay_three_layers_frame2.html";
const PAGE_A1_A2_B3 = "https://example.com/browser/toolkit/content/tests/browser/file_autoplay_three_layers_frame1.html";

function setup_test_preference() {
  return SpecialPowers.pushPrefEnv({"set": [
    ["media.autoplay.default", SpecialPowers.Ci.nsIAutoplay.BLOCKED],
    ["media.autoplay.enabled.user-gestures-needed", true],
  ]});
}

var frameTestArray = [
  { name: "A1_A2",    layersNum: 2, src: PAGE_A1_A2 },
  { name: "A1_B2",    layersNum: 2, src: PAGE_A1_B2 },
  { name: "A1_B2_C3", layersNum: 3, src: PAGE_A1_B2_C3 },
  { name: "A1_B2_A3", layersNum: 3, src: PAGE_A1_B2_A3 },
  { name: "A1_B2_B3", layersNum: 3, src: PAGE_A1_B2_B3 },
  { name: "A1_A2_A3", layersNum: 3, src: PAGE_A1_A2_A3 },
  { name: "A1_A2_B3", layersNum: 3, src: PAGE_A1_A2_B3 },
];

async function test_permission_propagation(testName, testSrc, layersNum) {
  info(`- start test for ${testName} -`);
  for (let layerIdx = 1; layerIdx <= layersNum; layerIdx++) {
    info("- open new tab -");
    let tab = await BrowserTestUtils.openNewForegroundTab(window.gBrowser,
                                                          "about:blank");
    BrowserTestUtils.loadURI(tab.linkedBrowser, testSrc);
    await BrowserTestUtils.browserLoaded(tab.linkedBrowser);

    // If the frame isn't activated, the video play will fail.
    async function playing_video_should_fail(layersNum) {
      for (let layerIdx = 1; layerIdx <= layersNum; layerIdx++) {
        let doc;
        if (layerIdx == 1) {
          doc = content.document;
        } else {
          doc = layerIdx == 2 ? content.frames[0].document :
                                content.frames[0].frames[0].document;
        }
        await doc.getElementById("v").play().catch(function() {
          ok(true, `video in layer ${layerIdx} can't start play without user input.`);
        });
      }
    }
    await ContentTask.spawn(tab.linkedBrowser, layersNum,
                            playing_video_should_fail);

    function activate_frame(testInfo) {
      let layerIdx = testInfo[0];
      info(`- activate frame in layer ${layerIdx} (${testInfo[1]}) -`);
      let doc;
      if (layerIdx == 1) {
        doc = content.document;
      } else {
        doc = layerIdx == 2 ? content.frames[0].document :
                              content.frames[0].frames[0].document;
      }
      doc.notifyUserGestureActivation();
    }
    await ContentTask.spawn(tab.linkedBrowser, [layerIdx, testName],
                            activate_frame);

    // If frame is activated, the video play will succeed, as interaction
    // anywhere in the frame activates the entire doctree, irrespective
    // of whether the documents are cross origin or not.
    async function playing_video_may_success(testInfo) {
      let layersNum = testInfo[2];
      for (let layerIdx = 1; layerIdx <= layersNum; layerIdx++) {
        let doc;
        if (layerIdx == 1) {
          doc = content.document;
        } else {
          doc = layerIdx == 2 ? content.frames[0].document :
                                content.frames[0].frames[0].document;
        }
        let video = doc.getElementById("v");
        try {
          await video.play();
          ok(true, `video in layer ${layerIdx} starts playing.`);
        } catch (e) {
          ok(false, `video in layer ${layerIdx} fails to start.`);
        }
      }
    }
    await ContentTask.spawn(tab.linkedBrowser,
                            [layerIdx, testName, layersNum],
                            playing_video_may_success);

    info("- remove tab -");
    BrowserTestUtils.removeTab(tab);
  }
}

add_task(async function start_test() {
  info("- setup test preference -");
  await setup_test_preference();
  requestLongerTimeout(2);

  info("- test permission propagation in different frame hierarchy -");
  for (let testIdx = 0; testIdx < frameTestArray.length; testIdx++) {
    let testInfo = frameTestArray[testIdx];
    let testName = testInfo.name;
    let testSrc = testInfo.src;
    let layersNum = testInfo.layersNum;
    if (layersNum > 3) {
      ok(false, "Not support more than 3 layers frame yet.");
    } else {
      await test_permission_propagation(testName, testSrc, layersNum);
    }
  }
});
