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

/*
 * This test ensures that Session Restore eventually forgets about
 * tabs and windows that have been closed a long time ago.
 */

"use strict";

ChromeUtils.import("resource://gre/modules/Services.jsm", this);
ChromeUtils.import("resource://gre/modules/osfile.jsm", this);

const LONG_TIME_AGO = 1;

const URL_TAB1 =
  "http://example.com/browser_cleaner.js?newtab1=" + Math.random();
const URL_TAB2 =
  "http://example.com/browser_cleaner.js?newtab2=" + Math.random();
const URL_NEWWIN =
  "http://example.com/browser_cleaner.js?newwin=" + Math.random();

function isRecent(stamp) {
  is(typeof stamp, "number", "This is a timestamp");
  return Date.now() - stamp <= 60000;
}

function promiseCleanup() {
  info("Cleaning up browser");

  return promiseBrowserState(getClosedState());
}

function getClosedState() {
  return Cu.cloneInto(CLOSED_STATE, {});
}

var CLOSED_STATE;

add_task(async function init() {
  forgetClosedWindows();
  while (ss.getClosedTabCount(window) > 0) {
    ss.forgetClosedTab(window, 0);
  }
});

add_task(async function test_open_and_close() {
  let newTab1 = BrowserTestUtils.addTab(gBrowser, URL_TAB1);
  await promiseBrowserLoaded(newTab1.linkedBrowser);

  let newTab2 = BrowserTestUtils.addTab(gBrowser, URL_TAB2);
  await promiseBrowserLoaded(newTab2.linkedBrowser);

  let newWin = await promiseNewWindowLoaded();
  let tab = BrowserTestUtils.addTab(newWin.gBrowser, URL_NEWWIN);

  await promiseBrowserLoaded(tab.linkedBrowser);

  await TabStateFlusher.flushWindow(window);
  await TabStateFlusher.flushWindow(newWin);

  info("1. Making sure that before closing, we don't have closedAt");
  // For the moment, no "closedAt"
  let state = JSON.parse(ss.getBrowserState());
  is(
    state.windows[0].closedAt || false,
    false,
    "1. Main window doesn't have closedAt"
  );
  is(
    state.windows[1].closedAt || false,
    false,
    "1. Second window doesn't have closedAt"
  );
  is(
    state.windows[0].tabs[0].closedAt || false,
    false,
    "1. First tab doesn't have closedAt"
  );
  is(
    state.windows[0].tabs[1].closedAt || false,
    false,
    "1. Second tab doesn't have closedAt"
  );

  info("2. Making sure that after closing, we have closedAt");

  // Now close stuff, this should add closeAt
  await BrowserTestUtils.closeWindow(newWin);
  await promiseRemoveTabAndSessionState(newTab1);
  await promiseRemoveTabAndSessionState(newTab2);

  state = CLOSED_STATE = JSON.parse(ss.getBrowserState());

  is(
    state.windows[0].closedAt || false,
    false,
    "2. Main window doesn't have closedAt"
  );
  ok(
    isRecent(state._closedWindows[0].closedAt),
    "2. Second window was closed recently"
  );
  ok(
    isRecent(state.windows[0]._closedTabs[0].closedAt),
    "2. First tab was closed recently"
  );
  ok(
    isRecent(state.windows[0]._closedTabs[1].closedAt),
    "2. Second tab was closed recently"
  );
});

add_task(async function test_restore() {
  info("3. Making sure that after restoring, we don't have closedAt");
  await promiseBrowserState(CLOSED_STATE);

  let newWin = ss.undoCloseWindow(0);
  await promiseDelayedStartupFinished(newWin);

  let newTab2 = ss.undoCloseTab(window, 0);
  await promiseTabRestored(newTab2);

  let newTab1 = ss.undoCloseTab(window, 0);
  await promiseTabRestored(newTab1);

  let state = JSON.parse(ss.getBrowserState());

  is(
    state.windows[0].closedAt || false,
    false,
    "3. Main window doesn't have closedAt"
  );
  is(
    state.windows[1].closedAt || false,
    false,
    "3. Second window doesn't have closedAt"
  );
  is(
    state.windows[0].tabs[0].closedAt || false,
    false,
    "3. First tab doesn't have closedAt"
  );
  is(
    state.windows[0].tabs[1].closedAt || false,
    false,
    "3. Second tab doesn't have closedAt"
  );

  await BrowserTestUtils.closeWindow(newWin);
  gBrowser.removeTab(newTab1);
  gBrowser.removeTab(newTab2);
});

add_task(async function test_old_data() {
  info(
    "4. Removing closedAt from the sessionstore, making sure that it is added upon idle-daily"
  );

  let state = getClosedState();
  delete state._closedWindows[0].closedAt;
  delete state.windows[0]._closedTabs[0].closedAt;
  delete state.windows[0]._closedTabs[1].closedAt;
  await promiseBrowserState(state);

  info("Sending idle-daily");
  Services.obs.notifyObservers(null, "idle-daily");
  info("Sent idle-daily");

  state = JSON.parse(ss.getBrowserState());
  is(
    state.windows[0].closedAt || false,
    false,
    "4. Main window doesn't have closedAt"
  );
  ok(
    isRecent(state._closedWindows[0].closedAt),
    "4. Second window was closed recently"
  );
  ok(
    isRecent(state.windows[0]._closedTabs[0].closedAt),
    "4. First tab was closed recently"
  );
  ok(
    isRecent(state.windows[0]._closedTabs[1].closedAt),
    "4. Second tab was closed recently"
  );
  await promiseCleanup();
});

add_task(async function test_cleanup() {
  info(
    "5. Altering closedAt to an old date, making sure that stuff gets collected, eventually"
  );
  await promiseCleanup();

  let state = getClosedState();
  state._closedWindows[0].closedAt = LONG_TIME_AGO;
  state.windows[0]._closedTabs[0].closedAt = LONG_TIME_AGO;
  state.windows[0]._closedTabs[1].closedAt = Date.now();
  let url = state.windows[0]._closedTabs[1].state.entries[0].url;

  await promiseBrowserState(state);

  info("Sending idle-daily");
  Services.obs.notifyObservers(null, "idle-daily");
  info("Sent idle-daily");

  state = JSON.parse(ss.getBrowserState());
  is(state._closedWindows[0], undefined, "5. Second window was forgotten");

  is(state.windows[0]._closedTabs.length, 1, "5. Only one closed tab left");
  is(
    state.windows[0]._closedTabs[0].state.entries[0].url,
    url,
    "5. The second tab is still here"
  );
  await promiseCleanup();
});
