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