// META: script=/common/dispatcher/dispatcher.js
// META: script=/common/get-host-info.sub.js
// META: script=/common/utils.js
// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js
// META: script=/html/browsers/browsing-the-web/back-forward-cache/resources/rc-helper.js
// META: script=/fetch/fetch-later/resources/fetch-later-helper.js

'use strict';

// NOTE: Due to the restriction of WPT runner, the following tests are all run
// with BackgroundSync off, which is different from some browsers,
// e.g. Chrome, default behavior, as the testing infra does not support enabling
// it.

parallelPromiseTest(async t => {
  const uuid = token();
  const url = generateSetBeaconURL(uuid);
  // Sets no option to test the default behavior when a document enters BFCache.
  const helper = new RemoteContextHelper();
  // Opens a window with noopener so that BFCache will work.
  const rc1 = await helper.addWindow(
      /*config=*/ null, /*options=*/ {features: 'noopener'});

  // Creates a fetchLater request with default config in remote, which should
  // only be sent on page discarded (not on entering BFCache).
  await rc1.executeScript(url => {
    fetchLater(url);
    // Add a pageshow listener to stash the BFCache event.
    window.addEventListener('pageshow', e => {
      window.pageshowEvent = e;
    });
  }, [url]);
  // Navigates away to let page enter BFCache.
  const rc2 = await rc1.navigateToNew();
  // Navigates back.
  await rc2.historyBack();
  // Verifies the page was BFCached.
  assert_true(await rc1.executeScript(() => {
    return window.pageshowEvent.persisted;
  }));

  // Theoretically, the request should still be pending thus 0 request received.
  // However, 1 request is sent, as by default the WPT test runner, e.g.
  // content_shell in Chromium, does not enable BackgroundSync permission,
  // resulting in forcing request sending on every navigation.
  await expectBeacon(uuid, {count: 1});
}, `fetchLater() sends on page entering BFCache if BackgroundSync is off.`);

parallelPromiseTest(async t => {
  const uuid = token();
  const url = generateSetBeaconURL(uuid);
  const helper = new RemoteContextHelper();
  // Opens a window with noopener so that BFCache will work.
  const rc1 = await helper.addWindow(
      /*config=*/ null, /*options=*/ {features: 'noopener'});

  // When the remote is put into BFCached, creates a fetchLater request w/
  // activateAfter = 0s. It should be sent out immediately.
  await rc1.executeScript(url => {
    window.addEventListener('pagehide', e => {
      if (e.persisted) {
        fetchLater(url, {activateAfter: 0});
      }
    });
    // Add a pageshow listener to stash the BFCache event.
    window.addEventListener('pageshow', e => {
      window.pageshowEvent = e;
    });
  }, [url]);
  // Navigates away to trigger request sending.
  const rc2 = await rc1.navigateToNew();
  // Navigates back.
  await rc2.historyBack();
  // Verifies the page was BFCached.
  assert_true(await rc1.executeScript(() => {
    return window.pageshowEvent.persisted;
  }));

  // NOTE: In this case, it does not matter if BackgroundSync is on or off.
  await expectBeacon(uuid, {count: 1});
}, `Call fetchLater() when BFCached with activateAfter=0 sends immediately.`);

parallelPromiseTest(async t => {
  const uuid = token();
  const url = generateSetBeaconURL(uuid);
  // Sets no option to test the default behavior when a document gets discarded
  // on navigated away.
  const helper = new RemoteContextHelper();
  // Opens a window without BFCache.
  const rc1 = await helper.addWindow();

  // Creates a fetchLater request in remote which should only be sent on
  // navigating away.
  await rc1.executeScript(url => {
    fetchLater(url);
    // Add a pageshow listener to stash the BFCache event.
    window.addEventListener('pageshow', e => {
      window.pageshowEvent = e;
    });
  }, [url]);
  // Navigates away to trigger request sending.
  const rc2 = await rc1.navigateToNew();
  // Navigates back.
  await rc2.historyBack();
  // Verifies the page was NOT BFCached.
  assert_equals(undefined, await rc1.executeScript(() => {
    return window.pageshowEvent;
  }));

  // NOTE: In this case, it does not matter if BackgroundSync is on or off.
  await expectBeacon(uuid, {count: 1});
}, `fetchLater() sends on navigating away a page w/o BFCache.`);

parallelPromiseTest(async t => {
  const uuid = token();
  const url = generateSetBeaconURL(uuid);
  // Sets no option to test the default behavior when a document gets discarded
  // on navigated away.
  const helper = new RemoteContextHelper();
  // Opens a window without BFCache.
  const rc1 = await helper.addWindow();

  // Creates 2 fetchLater requests in remote, and one of them is aborted
  // immediately. The other one should only be sent right on navigating away.
  await rc1.executeScript(url => {
    const controller = new AbortController();
    fetchLater(url, {signal: controller.signal});
    fetchLater(url);
    controller.abort();
    // Add a pageshow listener to stash the BFCache event.
    window.addEventListener('pageshow', e => {
      window.pageshowEvent = e;
    });
  }, [url]);
  // Navigates away to trigger request sending.
  const rc2 = await rc1.navigateToNew();
  // Navigates back.
  await rc2.historyBack();
  // Verifies the page was NOT BFCached.
  assert_equals(undefined, await rc1.executeScript(() => {
    return window.pageshowEvent;
  }));

  // NOTE: In this case, it does not matter if BackgroundSync is on or off.
  await expectBeacon(uuid, {count: 1});
}, `fetchLater() does not send aborted request on navigating away a page w/o BFCache.`);

parallelPromiseTest(async t => {
  const uuid = token();
  const url = generateSetBeaconURL(uuid);
  const options = {activateAfter: 60000};
  const helper = new RemoteContextHelper();
  // Opens a window with noopener so that BFCache will work.
  const rc1 = await helper.addWindow(
      /*config=*/ null, /*options=*/ {features: 'noopener'});

  // Creates a fetchLater request in remote which should only be sent on
  // navigating away.
  await rc1.executeScript((url) => {
    // Sets activateAfter = 1m to indicate the request should NOT be sent out
    // immediately.
    fetchLater(url, {activateAfter: 60000});
    // Adds a pageshow listener to stash the BFCache event.
    window.addEventListener('pageshow', e => {
      window.pageshowEvent = e;
    });
  }, [url]);
  // Navigates away to trigger request sending.
  const rc2 = await rc1.navigateToNew();
  // Navigates back.
  await rc2.historyBack();
  // Verifies the page was BFCached.
  assert_true(await rc1.executeScript(() => {
    return window.pageshowEvent.persisted;
  }));

  // Theoretically, the request should still be pending thus 0 request received.
  // However, 1 request is sent, as by default the WPT test runner, e.g.
  // content_shell in Chromium, does not enable BackgroundSync permission,
  // resulting in forcing request sending on every navigation, even if page is
  // put into BFCache.
  await expectBeacon(uuid, {count: 1});
}, `fetchLater() with activateAfter=1m sends on page entering BFCache if BackgroundSync is off.`);