// Test for bug 343515 - Need API for tabbrowsers to tell docshells they're visible/hidden

// Globals
var testPath = "http://mochi.test:8888/browser/docshell/test/navigation/";
var ctx = {};

// Helper function to check if a window is active
function isActive(aWindow) {
  var docshell = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
                        .getInterface(Ci.nsIWebNavigation)
                        .QueryInterface(Ci.nsIDocShell);
  return docshell.isActive;
}

function oneShotListener(aElem, aType, aCallback) {
  aElem.addEventListener(aType, function () {
    aElem.removeEventListener(aType, arguments.callee, true);

    // aCallback is executed asynchronously, which is handy because load
    // events fire before mIsDocumentLoaded is actually set to true. :(
    executeSoon(aCallback);
  }, true);
}

// Returns a closure that iteratively (BFS) waits for all
// of the descendant frames of aInitialWindow to finish loading,
// then calls aFinalCallback.
function frameLoadWaiter(aInitialWindow, aFinalCallback) {

  // The window we're currently waiting on
  var curr = aInitialWindow;

  // The windows we need to wait for
  var waitQueue = [];

  // The callback to call when we're all done
  var finalCallback = aFinalCallback;

  function frameLoadCallback() {

    // Push any subframes of what we just got
    for (var i = 0; i < curr.frames.length; ++i)
      waitQueue.push(curr.frames[i]);

    // Handle the next window in the queue
    if (waitQueue.length >= 1) {
      curr = waitQueue.shift();
      if (curr.document.readyState == "complete")
        frameLoadCallback();
      else
        oneShotListener(curr, "load", frameLoadCallback);
      return;
    }

    // Otherwise, we're all done. Call the final callback
    finalCallback();
  }

  return frameLoadCallback;
}

// Entry point from Mochikit
function test() {

  // Lots of callbacks going on here
  waitForExplicitFinish();

  // Begin the test
  step1();
}

function step1() {

  // Get a handle on the initial tab
  ctx.tab0 = gBrowser.selectedTab;
  ctx.tab0Browser = gBrowser.getBrowserForTab(ctx.tab0);
  ctx.tab0Window = ctx.tab0Browser.contentWindow;

  // Our current tab should be active
  ok(isActive(ctx.tab0Window), "Tab 0 should be active at test start");

  // Open a New Tab
  ctx.tab1 = gBrowser.addTab(testPath + "bug343515_pg1.html");
  ctx.tab1Browser = gBrowser.getBrowserForTab(ctx.tab1);
  ctx.tab1Window = ctx.tab1Browser.contentWindow;
  oneShotListener(ctx.tab1Browser, "load", step2);
}

function step2() {
  is(testPath + "bug343515_pg1.html", ctx.tab1Browser.currentURI.spec,
     "Got expected tab 1 url in step 2");

  // Our current tab should still be active
  ok(isActive(ctx.tab0Window), "Tab 0 should still be active");
  ok(!isActive(ctx.tab1Window), "Tab 1 should not be active");

  // Switch to tab 1
  gBrowser.selectedTab = ctx.tab1;

  // Tab 1 should now be active
  ok(!isActive(ctx.tab0Window), "Tab 0 should be inactive");
  ok(isActive(ctx.tab1Window), "Tab 1 should be active");

  // Open another tab
  ctx.tab2 = gBrowser.addTab(testPath + "bug343515_pg2.html");
  ctx.tab2Browser = gBrowser.getBrowserForTab(ctx.tab2);
  ctx.tab2Window = ctx.tab2Browser.contentWindow;
  oneShotListener(ctx.tab2Browser, "load", frameLoadWaiter(ctx.tab2Window, step3));
}

function step3() {
  is(testPath + "bug343515_pg2.html", ctx.tab2Browser.currentURI.spec,
     "Got expected tab 2 url in step 3");

  // Tab 0 should be inactive, Tab 1 should be active
  ok(!isActive(ctx.tab0Window), "Tab 0 should be inactive");
  ok(isActive(ctx.tab1Window), "Tab 1 should be active");

  // Tab 2's window _and_ its iframes should be inactive
  ok(!isActive(ctx.tab2Window), "Tab 2 should be inactive");
  is(ctx.tab2Window.frames.length, 2, "Tab 2 should have 2 iframes");
  for (var i = 0; i < ctx.tab2Window.frames.length; i++)
    info("step 3, frame " + i + " info: " + ctx.tab2Window.frames[i].location);
  ok(!isActive(ctx.tab2Window.frames[0]), "Tab2 iframe 0 should be inactive");
  ok(!isActive(ctx.tab2Window.frames[1]), "Tab2 iframe 1 should be inactive");

  // Navigate tab 2 to a different page
  ctx.tab2Window.location = testPath + "bug343515_pg3.html";
  oneShotListener(ctx.tab2Browser, "load", frameLoadWaiter(ctx.tab2Window, step4));
}

function step4() {
  is(testPath + "bug343515_pg3.html", ctx.tab2Browser.currentURI.spec,
     "Got expected tab 2 url in step 4");

  // Tab 0 should be inactive, Tab 1 should be active
  ok(!isActive(ctx.tab0Window), "Tab 0 should be inactive");
  ok(isActive(ctx.tab1Window), "Tab 1 should be active");

  // Tab2 and all descendants should be inactive
  ok(!isActive(ctx.tab2Window), "Tab 2 should be inactive");
  is(ctx.tab2Window.frames.length, 2, "Tab 2 should have 2 iframes");
  for (var i = 0; i < ctx.tab2Window.frames.length; i++)
    info("step 4, frame " + i + " info: " + ctx.tab2Window.frames[i].location);
  is(ctx.tab2Window.frames[0].frames.length, 1, "Tab 2 iframe 0 should have 1 iframes");
  ok(!isActive(ctx.tab2Window.frames[0]), "Tab2 iframe 0 should be inactive");
  ok(!isActive(ctx.tab2Window.frames[0].frames[0]), "Tab2 iframe 0 subiframe 0 should be inactive");
  ok(!isActive(ctx.tab2Window.frames[1]), "Tab2 iframe 1 should be inactive");

  // Switch to Tab 2
  gBrowser.selectedTab = ctx.tab2;

  // Check everything
  ok(!isActive(ctx.tab0Window), "Tab 0 should be inactive");
  ok(!isActive(ctx.tab1Window), "Tab 1 should be inactive");
  ok(isActive(ctx.tab2Window), "Tab 2 should be active");
  ok(isActive(ctx.tab2Window.frames[0]), "Tab2 iframe 0 should be active");
  ok(isActive(ctx.tab2Window.frames[0].frames[0]), "Tab2 iframe 0 subiframe 0 should be active");
  ok(isActive(ctx.tab2Window.frames[1]), "Tab2 iframe 1 should be active");

  // Go back
  oneShotListener(ctx.tab2Browser, "pageshow", frameLoadWaiter(ctx.tab2Window, step5));
  ctx.tab2Browser.goBack();

}

function step5() {

  // Check everything
  ok(!isActive(ctx.tab0Window), "Tab 0 should be inactive");
  ok(!isActive(ctx.tab1Window), "Tab 1 should be inactive");
  ok(isActive(ctx.tab2Window), "Tab 2 should be active");
  ok(isActive(ctx.tab2Window.frames[0]), "Tab2 iframe 0 should be active");
  ok(isActive(ctx.tab2Window.frames[1]), "Tab2 iframe 1 should be active");

  // Switch to tab 1
  gBrowser.selectedTab = ctx.tab1;

  // Navigate to page 3
  ctx.tab1Window.location = testPath + "bug343515_pg3.html";
  oneShotListener(ctx.tab1Browser, "load", frameLoadWaiter(ctx.tab1Window, step6));
}

function step6() {

  // Check everything
  ok(!isActive(ctx.tab0Window), "Tab 0 should be inactive");
  ok(isActive(ctx.tab1Window), "Tab 1 should be active");
  ok(isActive(ctx.tab1Window.frames[0]), "Tab1 iframe 0 should be active");
  ok(isActive(ctx.tab1Window.frames[0].frames[0]), "Tab1 iframe 0 subiframe 0 should be active");
  ok(isActive(ctx.tab1Window.frames[1]), "Tab1 iframe 1 should be active");
  ok(!isActive(ctx.tab2Window), "Tab 2 should be inactive");
  ok(!isActive(ctx.tab2Window.frames[0]), "Tab2 iframe 0 should be inactive");
  ok(!isActive(ctx.tab2Window.frames[1]), "Tab2 iframe 1 should be inactive");

  // Go forward on tab 2
  oneShotListener(ctx.tab2Browser, "pageshow", frameLoadWaiter(ctx.tab2Window, step7));
  var tab2docshell = ctx.tab2Window.QueryInterface(Ci.nsIInterfaceRequestor)
                                   .getInterface(Ci.nsIWebNavigation);
  tab2docshell.goForward();
}

function step7() {

  ctx.tab2Window = ctx.tab2Browser.contentWindow;

  // Check everything
  ok(!isActive(ctx.tab0Window), "Tab 0 should be inactive");
  ok(isActive(ctx.tab1Window), "Tab 1 should be active");
  ok(isActive(ctx.tab1Window.frames[0]), "Tab1 iframe 0 should be active");
  ok(isActive(ctx.tab1Window.frames[0].frames[0]), "Tab1 iframe 0 subiframe 0 should be active");
  ok(isActive(ctx.tab1Window.frames[1]), "Tab1 iframe 1 should be active");
  ok(!isActive(ctx.tab2Window), "Tab 2 should be inactive");
  ok(!isActive(ctx.tab2Window.frames[0]), "Tab2 iframe 0 should be inactive");
  ok(!isActive(ctx.tab2Window.frames[0].frames[0]), "Tab2 iframe 0 subiframe 0 should be inactive");
  ok(!isActive(ctx.tab2Window.frames[1]), "Tab2 iframe 1 should be inactive");

  // That's probably enough
  allDone();
}

function allDone() {

  // Close the tabs we made
  gBrowser.removeTab(ctx.tab1);
  gBrowser.removeTab(ctx.tab2);

  // Tell the framework we're done
  finish();
}
