<!doctype html>
<title>Verify history.back() on a persisted page resumes timers</title>
<meta name="timeout" content="long">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script type="text/javascript">

function make_post_back_url(name) {
  return new URL('resources/post_name_on_load.html?name=' + name,
                 window.location).href;
}

function wait_for_message(name) {
  return new Promise(resolve => {
    addEventListener('message', function onMsg(evt) {
      if (evt.data !== name) {
        return;
      }
      removeEventListener('message', onMsg);
      resolve();
    });
  });
}

function with_window_by_name(name) {
  let win = window.open(make_post_back_url(name));
  return wait_for_message(name).then(_ => {
    return win;
  });
}

function with_nested_frame(win, url) {
  return new Promise(resolve => {
    let frame = win.document.createElement('iframe');
    frame.addEventListener('load', function onLoad(evt) {
      removeEventListener('load', onLoad);
      resolve(frame);
    });
    frame.src = url;
    win.document.body.appendChild(frame);
  });
}

function delay(win, delay) {
  return new Promise(resolve => {
    win.setTimeout(_ => {
      resolve(win);
    }, delay);
  });
}

function navigate_by_name(win, name) {
  win.location = make_post_back_url(name);
  return wait_for_message(name).then(_ => {
    return win;
  });
}

function go_back(win) {
  return new Promise(resolve => {
    win.onpagehide = e => resolve(win);
    win.history.back();
  });
}

let DELAY = 500;

promise_test(t => {
  // Create a new window so we can navigate it later.
  return with_window_by_name('foo').then(win => {
    // Schedule a timer within the new window.  Our intent is
    // to navigate the window before the timer fires.
    let delayFired = false;
    let innerDelay = delay(win, DELAY);
    innerDelay.then(_ => {
      delayFired = true;
    });

    return navigate_by_name(win, 'bar').then(_ => {
      // Since the window has navigated the timer should not
      // fire.  We set a timer on our current test window
      // to verify the other timer is not received.
      assert_false(delayFired);
      return delay(window, DELAY * 2);
    }).then(_ => {
      // The navigated window's timer should not have fired.
      assert_false(delayFired);
      // Now go back to the document that set the timer.
      return go_back(win);
    }).then(_ => {
      // We wait for one of two conditions here.  For browsers
      // with a bfcache the original suspended timer will fire.
      // Alternatively, if the browser reloads the page the original
      // message will be sent again.  Wait for either of these
      // two events.
      return Promise.race([wait_for_message('foo'), innerDelay]);
    }).then(_ => {
      win.close();
    });
  });
}, 'history.back() handles top level page timer correctly');

promise_test(t => {
  let win;
  // Create a new window so we can navigate it later.
  return with_window_by_name('foo').then(w => {
    win = w;

    // Create a nested frame so we check if navigation and history.back()
    // properly handle child window state.
    return with_nested_frame(win, 'about:blank');

  }).then(frame => {
    // Schedule a timer within the nested frame contained by the new window.
    // Our intent is to navigate the window before the timer fires.
    let delayFired = false;
    let innerDelay = delay(frame.contentWindow, DELAY);
    innerDelay.then(_ => {
      delayFired = true;
    });

    return navigate_by_name(win, 'bar').then(_ => {
      // Since the window has navigated the timer should not
      // fire.  We set a timer on our current test window
      // to verify the other timer is not received.
      assert_false(delayFired);
      return delay(window, DELAY * 2);
    }).then(_ => {
      // The navigated window's timer should not have fired.
      assert_false(delayFired);
      // Now go back to the document containing the frame that set the timer.
      return go_back(win);
    }).then(_ => {
      // We wait for one of two conditions here.  For browsers
      // with a bfcache the original suspended timer will fire.
      // Alternatively, if the browser reloads the page the original
      // message will be sent again.  Wait for either of these
      // two events.
      return Promise.race([wait_for_message('foo'), innerDelay]);
    }).then(_ => {
      win.close();
    });
  });
}, 'history.back() handles nested iframe timer correctly');

</script>
