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

let ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);

const TAB_STATE_NEEDS_RESTORE = 1;
const TAB_STATE_RESTORING = 2;

let stateBackup = ss.getBrowserState();

let state = {windows:[{tabs:[
  // first group
  {entries:[{url:"http://example.com#1"}],extData:{"tabview-tab":"{\"bounds\":{\"left\":20,\"top\":20,\"width\":20,\"height\":20},\"url\":\"http://example.com#1\",\"groupID\":2}"}},
  {entries:[{url:"http://example.com#2"}],extData:{"tabview-tab":"{\"bounds\":{\"left\":20,\"top\":20,\"width\":20,\"height\":20},\"url\":\"http://example.com#2\",\"groupID\":2}"}},
  {entries:[{url:"http://example.com#3"}],extData:{"tabview-tab":"{\"bounds\":{\"left\":20,\"top\":20,\"width\":20,\"height\":20},\"url\":\"http://example.com#3\",\"groupID\":2}"}},
  {entries:[{url:"http://example.com#4"}],extData:{"tabview-tab":"{\"bounds\":{\"left\":20,\"top\":20,\"width\":20,\"height\":20},\"url\":\"http://example.com#4\",\"groupID\":2}"}},

  // second group
  {entries:[{url:"http://example.com#5"}],hidden:true,extData:{"tabview-tab":"{\"bounds\":{\"left\":20,\"top\":20,\"width\":20,\"height\":20},\"url\":\"http://example.com#5\",\"groupID\":1}"}},
  {entries:[{url:"http://example.com#6"}],hidden:true,extData:{"tabview-tab":"{\"bounds\":{\"left\":20,\"top\":20,\"width\":20,\"height\":20},\"url\":\"http://example.com#6\",\"groupID\":1}"}},
  {entries:[{url:"http://example.com#7"}],hidden:true,extData:{"tabview-tab":"{\"bounds\":{\"left\":20,\"top\":20,\"width\":20,\"height\":20},\"url\":\"http://example.com#7\",\"groupID\":1}"}},
  {entries:[{url:"http://example.com#8"}],hidden:true,extData:{"tabview-tab":"{\"bounds\":{\"left\":20,\"top\":20,\"width\":20,\"height\":20},\"url\":\"http://example.com#8\",\"groupID\":1}"}}
],selected:5,extData:{
  "tabview-groups":"{\"nextID\":8,\"activeGroupId\":1}","tabview-group":"{\"1\":{\"bounds\":{\"left\":15,\"top\":10,\"width\":415,\"height\":367},\"userSize\":{\"x\":415,\"y\":367},\"title\":\"\",\"id\":1},\"2\":{\"bounds\":{\"left\":286,\"top\":488,\"width\":418,\"height\":313},\"title\":\"\",\"id\":2}}",
  "tabview-ui":"{\"pageBounds\":{\"left\":0,\"top\":0,\"width\":940,\"height\":1075}}"
}}]};

function test() {
  waitForExplicitFinish();

  Services.prefs.setBoolPref("browser.sessionstore.restore_hidden_tabs", false);

  TabsProgressListener.init();

  registerCleanupFunction(function () {
    TabsProgressListener.uninit();

    Services.prefs.clearUserPref("browser.sessionstore.restore_hidden_tabs");

    ss.setBrowserState(stateBackup);
  });

  TabView._initFrame(function () {
    executeSoon(testRestoreWithHiddenTabs);
  });
}

function testRestoreWithHiddenTabs() {
  let checked = false;
  let ssReady = false;
  let tabsRestored = false;

  let check = function () {
    if (checked || !ssReady || !tabsRestored)
      return;

    checked = true;

    is(gBrowser.tabs.length, 8, "there are now eight tabs");
    is(gBrowser.visibleTabs.length, 4, "four visible tabs");

    let cw = TabView.getContentWindow();
    is(cw.GroupItems.groupItems.length, 2, "there are now two groupItems");

    testSwitchToInactiveGroup();
  }

  whenSessionStoreReady(function () {
    ssReady = true;
    check();
  });

  TabsProgressListener.setCallback(function (needsRestore, isRestoring) {
    if (4 < needsRestore)
      return;

    TabsProgressListener.unsetCallback();
    is(needsRestore, 4, "4/8 tabs restored");

    tabsRestored = true;
    check();
  });

  ss.setBrowserState(JSON.stringify(state));
}

function testSwitchToInactiveGroup() {
  let firstProgress = true;

  TabsProgressListener.setCallback(function (needsRestore, isRestoring) {
    if (firstProgress) {
      firstProgress = false;
      is(isRestoring, 3, "restoring 3 tabs concurrently");
    } else {
      ok(isRestoring < 4, "restoring max. 3 tabs concurrently");
    }

    if (needsRestore)
      return;

    TabsProgressListener.unsetCallback();

    is(gBrowser.visibleTabs.length, 4, "four visible tabs");
    waitForFocus(finish);
  });

  gBrowser.selectedTab = gBrowser.tabs[4];
}

function whenSessionStoreReady(callback) {
  window.addEventListener("SSWindowStateReady", function onReady() {
    window.removeEventListener("SSWindowStateReady", onReady, false);
    executeSoon(callback);
  }, false);
}

function countTabs() {
  let needsRestore = 0, isRestoring = 0;
  let windowsEnum = Services.wm.getEnumerator("navigator:browser");

  while (windowsEnum.hasMoreElements()) {
    let window = windowsEnum.getNext();
    if (window.closed)
      continue;

    for (let i = 0; i < window.gBrowser.tabs.length; i++) {
      let browser = window.gBrowser.tabs[i].linkedBrowser;
      if (browser.__SS_restoreState == TAB_STATE_RESTORING)
        isRestoring++;
      else if (browser.__SS_restoreState == TAB_STATE_NEEDS_RESTORE)
        needsRestore++;
    }
  }

  return [needsRestore, isRestoring];
}

let TabsProgressListener = {
  init: function () {
    gBrowser.addTabsProgressListener(this);
  },

  uninit: function () {
    this.unsetCallback();
    gBrowser.removeTabsProgressListener(this);
 },

  setCallback: function (callback) {
    this.callback = callback;
  },

  unsetCallback: function () {
    delete this.callback;
  },

  onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
    let isNetwork = aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK;
    let isWindow = aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW;

    if (!(this.callback && isNetwork && isWindow))
      return;

    let self = this;
    let finalize = function () {
      if (wasRestoring)
        delete aBrowser.__wasRestoring;

      self.callback.apply(null, countTabs());
    };

    let isRestoring = aBrowser.__SS_restoreState == TAB_STATE_RESTORING;
    let wasRestoring = !aBrowser.__SS_restoreState && aBrowser.__wasRestoring;
    let hasStopped = aStateFlags & Ci.nsIWebProgressListener.STATE_STOP;

    if (isRestoring && !hasStopped)
      aBrowser.__wasRestoring = true;

    if (hasStopped && (isRestoring || wasRestoring))
      finalize();
  }
}
