Fenced frames: Disable preload when network is revoked

Fenced frames(FF) can disable network access by invoking FF API:
window.fence.disableUntrustedNetwork().

After the network access is revoked, no preload requests are
allowed from FF.

There are two triggers for preload requests:

1. Via a response header, for example:
Link: </main.js>; rel="preload"; as="script"

2. From a link element, for example:
<link rel="preload" href="/main.js" as="script"/>

The first case has already been disabled for FF, regardless of FF's
network status. See crrev.com/c/3468236.

The second case is taken care of by the existing network status checks
in CorsURLLoaderFactory. This CL adds tests to verify this.

See: https://github.com/WICG/fenced-frame/blob/master/explainer/fenced_frames_with_local_unpartitioned_data_access.md#revoking-network-access

Bug: 1515599
Change-Id: I19f0d74e682a28bae56d2a5bf12e96b9c5e796e4
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5749322
Commit-Queue: Xiaochen Zhou <xiaochenzh@chromium.org>
Reviewed-by: Garrett Tanzer <gtanzer@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1335078}
diff --git a/content/browser/fenced_frame/fenced_frame_browsertest.cc b/content/browser/fenced_frame/fenced_frame_browsertest.cc
index 80b7d00b..0e95e14 100644
--- a/content/browser/fenced_frame/fenced_frame_browsertest.cc
+++ b/content/browser/fenced_frame/fenced_frame_browsertest.cc
@@ -53,6 +53,7 @@
 #include "content/public/test/test_browser_context.h"
 #include "content/public/test/test_frame_navigation_observer.h"
 #include "content/public/test/test_navigation_observer.h"
+#include "content/public/test/url_loader_monitor.h"
 #include "content/shell/browser/shell.h"
 #include "content/shell/browser/shell_browser_context.h"
 #include "content/test/content_browser_test_utils_internal.h"
@@ -75,6 +76,7 @@
 #include "third_party/blink/public/common/frame/fenced_frame_sandbox_flags.h"
 #include "third_party/blink/public/mojom/fenced_frame/fenced_frame.mojom.h"
 #include "third_party/blink/public/mojom/frame/frame.mojom-test-utils.h"
+#include "third_party/blink/public/mojom/loader/resource_load_info.mojom-shared.h"
 #include "url/gurl.h"
 #include "url/origin.h"
 
@@ -1319,6 +1321,82 @@
   EXPECT_TRUE(iframe.IsRenderFrameDeleted());
 }
 
+// Verify preload from a link element works in fenced frame.
+IN_PROC_BROWSER_TEST_F(FencedFrameMPArchBrowserTest, LinkPreload) {
+  ASSERT_TRUE(https_server()->Start());
+
+  // Navigate to a page that contains a fenced frame.
+  const GURL main_url = https_server()->GetURL(
+      "a.test", "/cross_site_iframe_factory.html?a.test(a.test{fenced})");
+  EXPECT_TRUE(NavigateToURL(shell(), main_url));
+
+  // Get fenced frame render frame host.
+  RenderFrameHostImpl* fenced_frame_rfh =
+      primary_main_frame_host()->GetFencedFrames().at(0)->GetInnerRoot();
+
+  // Set up URLLoaderMonitor.
+  std::string relative_url = "/title1.html";
+  const GURL preload_url = https_server()->GetURL("a.test", relative_url);
+  URLLoaderMonitor monitor({preload_url});
+
+  // Navigate fenced frame to a page with a link element that does a preload.
+  TestFrameNavigationObserver observer(fenced_frame_rfh);
+  EXPECT_TRUE(
+      ExecJs(primary_main_frame_host(),
+             JsReplace(
+                 R"(document.querySelector('fencedframe').config
+                            = new FencedFrameConfig($1);)",
+                 https_server()->GetURL(
+                     "a.test", "/fenced_frames/link_rel_preload.html"))));
+  observer.WaitForCommit();
+
+  // The preload request is received. It has script resource type.
+  monitor.WaitForUrl(preload_url);
+  std::optional<network::ResourceRequest> request =
+      monitor.GetRequestInfo(preload_url);
+  EXPECT_EQ(request->resource_type,
+            static_cast<int>(blink::mojom::ResourceType::kScript));
+}
+
+// Verify preload from a link element is disabled after fenced frame network
+// cutoff.
+IN_PROC_BROWSER_TEST_F(FencedFrameMPArchBrowserTest,
+                       NetworkCutoffDisablesLinkPreload) {
+  ASSERT_TRUE(https_server()->Start());
+
+  // Navigate to a page that contains a fenced frame.
+  const GURL main_url = https_server()->GetURL(
+      "a.test", "/cross_site_iframe_factory.html?a.test(a.test{fenced})");
+  EXPECT_TRUE(NavigateToURL(shell(), main_url));
+
+  // Get fenced frame render frame host.
+  RenderFrameHostImpl* fenced_frame_rfh =
+      primary_main_frame_host()->GetFencedFrames().at(0)->GetInnerRoot();
+
+  // Set up URLLoaderMonitor.
+  std::string relative_url = "/title1.html";
+  const GURL preload_url = https_server()->GetURL("a.test", relative_url);
+  URLLoaderMonitor monitor({preload_url});
+
+  // Navigate fenced frame to a page that disables network access, then adds a
+  // link element that does a preload.
+  TestFrameNavigationObserver observer(fenced_frame_rfh);
+  EXPECT_TRUE(
+      ExecJs(primary_main_frame_host(),
+             JsReplace(
+                 R"(document.querySelector('fencedframe').config
+                            = new FencedFrameConfig($1);)",
+                 https_server()->GetURL(
+                     "a.test",
+                     "/fenced_frames/link_rel_preload_disable_network.html"))));
+  observer.WaitForCommit();
+
+  // The preload request is blocked with code `ERR_NETWORK_ACCESS_REVOKED`.
+  monitor.WaitForUrl(preload_url);
+  EXPECT_EQ(monitor.WaitForRequestCompletion(preload_url).error_code,
+            net::ERR_NETWORK_ACCESS_REVOKED);
+}
+
 class FencedFrameWithSiteIsolationDisabledBrowserTest
     : public FencedFrameMPArchBrowserTest,
       public testing::WithParamInterface<std::tuple<bool, bool>> {
diff --git a/content/test/content_test_bundle_data.filelist b/content/test/content_test_bundle_data.filelist
index 4ac2195..1195dc1 100644
--- a/content/test/content_test_bundle_data.filelist
+++ b/content/test/content_test_bundle_data.filelist
@@ -6277,6 +6277,10 @@
 data/fenced_frames/find_in_page.html.mock-http-headers
 data/fenced_frames/in_iframe.html
 data/fenced_frames/in_iframe.html.mock-http-headers
+data/fenced_frames/link_rel_preload.html
+data/fenced_frames/link_rel_preload.html.mock-http-headers
+data/fenced_frames/link_rel_preload_disable_network.html
+data/fenced_frames/link_rel_preload_disable_network.html.mock-http-headers
 data/fenced_frames/nested.html
 data/fenced_frames/nested.html.mock-http-headers
 data/fenced_frames/outer_inner_frame_as_param.html
diff --git a/content/test/data/fenced_frames/link_rel_preload.html b/content/test/data/fenced_frames/link_rel_preload.html
new file mode 100644
index 0000000..134e8c7
--- /dev/null
+++ b/content/test/data/fenced_frames/link_rel_preload.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<html>
+<body>
+  <link rel="preload" href="/title1.html" as="script"/>
+</body>
+</html>
diff --git a/content/test/data/fenced_frames/link_rel_preload.html.mock-http-headers b/content/test/data/fenced_frames/link_rel_preload.html.mock-http-headers
new file mode 100644
index 0000000..263e89c4
--- /dev/null
+++ b/content/test/data/fenced_frames/link_rel_preload.html.mock-http-headers
@@ -0,0 +1,2 @@
+HTTP/1.1 200 OK
+Supports-Loading-Mode: fenced-frame
\ No newline at end of file
diff --git a/content/test/data/fenced_frames/link_rel_preload_disable_network.html b/content/test/data/fenced_frames/link_rel_preload_disable_network.html
new file mode 100644
index 0000000..d593b85
--- /dev/null
+++ b/content/test/data/fenced_frames/link_rel_preload_disable_network.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+<body>
+  <script>
+    (async () => {
+      await window.fence.disableUntrustedNetwork().then(
+        () => {
+          var link_element = document.createElement('link');
+          link_element.href = '/title1.html';
+          link_element.rel = 'preload';
+          link_element.as = 'script';
+          document.body.appendChild(link_element);
+        }
+      );
+    })();
+  </script>
+</body>
+</html>
diff --git a/content/test/data/fenced_frames/link_rel_preload_disable_network.html.mock-http-headers b/content/test/data/fenced_frames/link_rel_preload_disable_network.html.mock-http-headers
new file mode 100644
index 0000000..263e89c4
--- /dev/null
+++ b/content/test/data/fenced_frames/link_rel_preload_disable_network.html.mock-http-headers
@@ -0,0 +1,2 @@
+HTTP/1.1 200 OK
+Supports-Loading-Mode: fenced-frame
\ No newline at end of file