[fetch-later] Add `FetchLaterParams` into network::mojom::URLRequest.

Sequence Diagram: https://docs.google.com/document/d/1U8XSnICPY3j-fjzG35UVm6zjwL6LvX6ETU3T8WrzLyQ/edit#heading=h.y51nt9atd97v

- Replace `URLRequest::is_fetch_later_api` with
  `URLRequest::fetch_later_params`.
- Define `FetchLaterPrams`, which includes a pending_remote of
  FetchLaterClient.
- Define `FetchLaterClient`, which supports `SetFetchLaterHost()` and
  `Clone()`. The former should be implemented in blink to support
  Renderer->Browser communication.
- Define `FetchLaterHost`, which is implemented by every
  KeepAliveURLLoader. It supports a one-time method `SendNow()` and
  `Abort()`.
- Aborting a FetchLater request right after calling fetchLater(), and
  then disposing a page, can introduce race condition, such that the
  browser still send out the request. In KeepAliveURLLoader, it tries
  to mitigate this by looking into the custom disconnection reason
  from the remote of FetchLaterClient. But the race can still happen.
  OT info will include this problem.

See the "Renderer->Browser Communication" section [1] for the details
and other considered alternatives.

Subsequent CL https://crrev.com/c/4803283 will use this interface to
implement fetchLater()'s backgroundTimeout feature.

[1]: https://docs.google.com/document/d/1U8XSnICPY3j-fjzG35UVm6zjwL6LvX6ETU3T8WrzLyQ/edit#heading=h.15fe7ufeo4uv

Bug: 1465781
Change-Id: Ib5067de1a10478cd0bb16ff6c4d981872bf5c1a6
diff --git a/fetch/fetch-later/send-on-deactivate.tentative.https.window.js b/fetch/fetch-later/send-on-deactivate.tentative.https.window.js
new file mode 100644
index 0000000..dc87265
--- /dev/null
+++ b/fetch/fetch-later/send-on-deactivate.tentative.https.window.js
@@ -0,0 +1,78 @@
+// META: script=/resources/testharness.js
+// META: script=/resources/testharnessreport.js
+// 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=/pending-beacon/resources/pending_beacon-helper.js
+
+'use strict';
+
+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();
+  // Navigate back.
+  await rc2.historyBack();
+  // Verify that the page was BFCached.
+  assert_true(await rc1.executeScript(() => {
+    return window.pageshowEvent.persisted;
+  }));
+
+  await expectBeacon(uuid, {count: 0});
+}, `fetchLater() does not send on page entering 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);
+    // Current implementation does not guarantee the execution of abort() in the
+    // same task. Hence, put in anonymous task.
+    (() => {
+      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();
+  // Navigate back.
+  await rc2.historyBack();
+  // Verify that the page was NOT BFCached.
+  assert_equals(undefined, await rc1.executeScript(() => {
+    return window.pageshowEvent;
+  }));
+
+  // TODO(crbug.com/1465781): Fix this in https://crrev.com/c/4803283.
+  await expectBeacon(uuid, {count: 1});
+}, `fetchLater() does not send aborted request on navigating away a page w/o BFCache.`);
diff --git a/fetch/fetch-later/send-on-discard.tentative.https.window.js b/fetch/fetch-later/send-on-discard.tentative.https.window.js
index 4fde4e4..d9d5e34 100644
--- a/fetch/fetch-later/send-on-discard.tentative.https.window.js
+++ b/fetch/fetch-later/send-on-discard.tentative.https.window.js
@@ -36,7 +36,9 @@
     const controller = new AbortController();
     fetchLater(url, {signal: controller.signal});
     fetchLater(url, {method: 'POST'});
-    controller.abort();
+    // Current implementation does not guarantee the execution of abort() in the
+    // same task. Hence, put in anonymous task.
+    (() => { controller.abort(); })();
   `);
   // Delete the iframe to trigger deferred request sending.
   document.body.removeChild(iframe);