/* Any copyright is dedicated to the Public Domain.
 * http://creativecommons.org/publicdomain/zero/1.0/ */

"use strict";

const URL = HTTPROOT + "browser_frametree_sample.html";
const URL_FRAMESET = HTTPROOT + "browser_frametree_sample_frameset.html";
const URL_IFRAMES = HTTPROOT + "browser_frametree_sample_iframes.html";

/**
 * Check that we correctly enumerate non-dynamic child frames.
 */
add_task(async function test_frametree() {
  // Add an empty tab for a start.
  let tab = BrowserTestUtils.addTab(gBrowser, URL);
  let browser = tab.linkedBrowser;
  await promiseBrowserLoaded(browser);

  // The page is a single frame with no children.
  is(await countNonDynamicFrames(browser), 0, "no child frames");

  // Navigate to a frameset.
  browser.loadURI(URL_FRAMESET);
  await promiseBrowserLoaded(browser);

  // The frameset has two frames.
  is(await countNonDynamicFrames(browser), 2, "two non-dynamic child frames");

  // Go back in history.
  let pageShowPromise = ContentTask.spawn(browser, null, async () => {
    return ContentTaskUtils.waitForEvent(this, "pageshow", true);
  });
  browser.goBack();
  await pageShowPromise;

  // We're at page one again.
  is(await countNonDynamicFrames(browser), 0, "no child frames");

  // Append a dynamic frame.
  await ContentTask.spawn(browser, URL, async ([url]) => {
    let frame = content.document.createElement("iframe");
    frame.setAttribute("src", url);
    content.document.body.appendChild(frame);
    return ContentTaskUtils.waitForEvent(frame, "load");
  });

  // The dynamic frame should be ignored.
  is(await countNonDynamicFrames(browser), 0, "we still have a single root frame");

  // Cleanup.
  await promiseRemoveTab(tab);
});

/**
 * Check that we correctly enumerate non-dynamic child frames.
 */
add_task(async function test_frametree_dynamic() {
  // Add an empty tab for a start.
  let tab = BrowserTestUtils.addTab(gBrowser, URL_IFRAMES);
  let browser = tab.linkedBrowser;
  await promiseBrowserLoaded(browser);

  // The page has two iframes.
  is(await countNonDynamicFrames(browser), 2, "two non-dynamic child frames");
  is(await enumerateIndexes(browser), "0,1", "correct indexes 0 and 1");

  // Insert a dynamic frame.
  await ContentTask.spawn(browser, URL, async ([url]) => {
    let frame = content.document.createElement("iframe");
    frame.setAttribute("src", url);
    content.document.body.insertBefore(frame, content.document.getElementsByTagName("iframe")[1]);
    return ContentTaskUtils.waitForEvent(frame, "load");
  });

  // The page still has two iframes.
  is(await countNonDynamicFrames(browser), 2, "two non-dynamic child frames");
  is(await enumerateIndexes(browser), "0,1", "correct indexes 0 and 1");

  // Append a dynamic frame.
  await ContentTask.spawn(browser, URL, async ([url]) => {
    let frame = content.document.createElement("iframe");
    frame.setAttribute("src", url);
    content.document.body.appendChild(frame);
    return ContentTaskUtils.waitForEvent(frame, "load");
  });

  // The page still has two iframes.
  is(await countNonDynamicFrames(browser), 2, "two non-dynamic child frames");
  is(await enumerateIndexes(browser), "0,1", "correct indexes 0 and 1");

  // Remopve a non-dynamic iframe.
  await ContentTask.spawn(browser, URL, async ([url]) => {
    // Remove the first iframe, which should be a non-dynamic iframe.
    content.document.body.removeChild(content.document.getElementsByTagName("iframe")[0]);
  });

  is(await countNonDynamicFrames(browser), 1, "one non-dynamic child frame");
  is(await enumerateIndexes(browser), "1", "correct index 1");

  // Cleanup.
  await promiseRemoveTab(tab);
});

async function countNonDynamicFrames(browser) {
  return ContentTask.spawn(browser, null, async () => {
    const ssu = Cc["@mozilla.org/browser/sessionstore/utils;1"]
                  .getService(Ci.nsISessionStoreUtils);

    let count = 0;
    ssu.forEachNonDynamicChildFrame(content, () => count++);
    return count;
  });
}

async function enumerateIndexes(browser) {
  return ContentTask.spawn(browser, null, async () => {
    const ssu = Cc["@mozilla.org/browser/sessionstore/utils;1"]
                  .getService(Ci.nsISessionStoreUtils);

    let indexes = [];
    ssu.forEachNonDynamicChildFrame(content, (frame, i) => indexes.push(i));
    return indexes.join(",");
  });
}
