blob: 1cadfdc05b9a78f9063775ca326768797a55dd1a [file] [log] [blame]
[email protected]5f96f5a62014-01-10 00:05:111// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Ken Rockot86bea1c2017-05-16 06:21:355#include "base/command_line.h"
avib7348942015-12-25 20:57:106#include "base/macros.h"
Aaron Colwelle953e562019-07-24 16:47:367#include "base/strings/stringprintf.h"
shivanigithubf33e469c2021-08-02 13:52:208#include "base/synchronization/lock.h"
shivanigithub93878d02021-06-15 11:37:539#include "base/test/scoped_feature_list.h"
avib7348942015-12-25 20:57:1010#include "build/build_config.h"
Dominic Farolino316e7932021-09-03 21:13:5911#include "content/browser/fenced_frame/fenced_frame.h"
shivanigithub93878d02021-06-15 11:37:5312#include "content/browser/fenced_frame/fenced_frame_url_mapping.h"
danakjc492bf82020-09-09 20:02:4413#include "content/browser/renderer_host/frame_tree.h"
14#include "content/browser/renderer_host/frame_tree_node.h"
15#include "content/browser/renderer_host/render_frame_host_impl.h"
[email protected]5f96f5a62014-01-10 00:05:1116#include "content/browser/renderer_host/render_view_host_impl.h"
17#include "content/browser/web_contents/web_contents_impl.h"
shivanigithubf33e469c2021-08-02 13:52:2018#include "content/public/browser/browser_thread.h"
[email protected]5f96f5a62014-01-10 00:05:1119#include "content/public/browser/notification_service.h"
20#include "content/public/browser/notification_types.h"
Nasko Oskov0401d812021-02-05 22:20:0821#include "content/public/browser/site_isolation_policy.h"
Annie Sullivan35977ad2019-04-30 21:04:2822#include "content/public/common/content_features.h"
nickdb193a12016-09-09 23:09:2323#include "content/public/common/content_switches.h"
Nasko Oskov039121012019-01-11 00:21:3224#include "content/public/common/origin_util.h"
[email protected]5f96f5a62014-01-10 00:05:1125#include "content/public/common/url_constants.h"
Peter Kasting919ce652020-05-07 10:22:3626#include "content/public/test/browser_test.h"
[email protected]5f96f5a62014-01-10 00:05:1127#include "content/public/test/browser_test_utils.h"
[email protected]6e9def12014-03-27 20:23:2828#include "content/public/test/content_browser_test.h"
29#include "content/public/test/content_browser_test_utils.h"
John Abd-El-Malek3fea4c42019-01-10 01:15:4230#include "content/public/test/test_frame_navigation_observer.h"
[email protected]5f96f5a62014-01-10 00:05:1131#include "content/public/test/test_navigation_observer.h"
32#include "content/public/test/test_utils.h"
33#include "content/shell/browser/shell.h"
nickdb193a12016-09-09 23:09:2334#include "content/shell/common/shell_switches.h"
[email protected]82307f6b2014-08-07 03:30:1235#include "content/test/content_browser_test_utils_internal.h"
shivanigithubf33e469c2021-08-02 13:52:2036#include "content/test/resource_load_observer.h"
[email protected]5f96f5a62014-01-10 00:05:1137#include "net/dns/mock_host_resolver.h"
alexmos478dcbb2014-12-10 21:24:4638#include "net/test/embedded_test_server/embedded_test_server.h"
shivanigithubf33e469c2021-08-02 13:52:2039#include "net/test/embedded_test_server/http_request.h"
arthursonzognib93a4472020-04-10 07:38:0040#include "services/network/public/cpp/web_sandbox_flags.h"
41#include "services/network/public/mojom/web_sandbox_flags.mojom-shared.h"
Gyuyoung Kim107c2a02021-04-13 01:49:3042#include "third_party/blink/public/common/chrome_debug_urls.h"
shivanigithub93878d02021-06-15 11:37:5343#include "third_party/blink/public/common/features.h"
Antonio Gomes4b2c5132020-01-16 11:49:4844#include "third_party/blink/public/mojom/frame/user_activation_update_types.mojom.h"
nick1466c842015-11-25 20:08:0645#include "url/url_constants.h"
[email protected]5f96f5a62014-01-10 00:05:1146
47namespace content {
48
nickdb193a12016-09-09 23:09:2349namespace {
50
Nick Carterb7e71312018-08-03 23:36:1351EvalJsResult GetOriginFromRenderer(FrameTreeNode* node) {
Philip Jägenstedt67302a22018-09-14 09:58:0552 return EvalJs(node, "self.origin");
nickdb193a12016-09-09 23:09:2353}
54
55} // namespace
56
[email protected]5f96f5a62014-01-10 00:05:1157class FrameTreeBrowserTest : public ContentBrowserTest {
58 public:
Fergal Dalya1d569972021-03-16 03:24:5359 FrameTreeBrowserTest() = default;
[email protected]5f96f5a62014-01-10 00:05:1160
alexmos478dcbb2014-12-10 21:24:4661 void SetUpOnMainThread() override {
62 host_resolver()->AddRule("*", "127.0.0.1");
alexmos478dcbb2014-12-10 21:24:4663 SetupCrossSiteRedirector(embedded_test_server());
martijn12d2dad2016-11-11 10:59:2764 ASSERT_TRUE(embedded_test_server()->Start());
alexmos478dcbb2014-12-10 21:24:4665 }
66
[email protected]5f96f5a62014-01-10 00:05:1167 private:
68 DISALLOW_COPY_AND_ASSIGN(FrameTreeBrowserTest);
69};
70
71// Ensures FrameTree correctly reflects page structure during navigations.
72IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, FrameTreeShape) {
alexmos478dcbb2014-12-10 21:24:4673 GURL base_url = embedded_test_server()->GetURL("A.com", "/site_isolation/");
[email protected]5f96f5a62014-01-10 00:05:1174
75 // Load doc without iframes. Verify FrameTree just has root.
76 // Frame tree:
77 // Site-A Root
Alex Moshchukaa95adf52019-08-13 00:02:0278 EXPECT_TRUE(NavigateToURL(shell(), base_url.Resolve("blank.html")));
Aran Gilman37d11632019-10-08 23:07:1579 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:5580 ->GetPrimaryFrameTree()
81 .root();
[email protected]5f96f5a62014-01-10 00:05:1182 EXPECT_EQ(0U, root->child_count());
83
84 // Add 2 same-site frames. Verify 3 nodes in tree with proper names.
85 // Frame tree:
86 // Site-A Root -- Site-A frame1
87 // \-- Site-A frame2
88 WindowedNotificationObserver observer1(
89 content::NOTIFICATION_LOAD_STOP,
90 content::Source<NavigationController>(
91 &shell()->web_contents()->GetController()));
Alex Moshchukaa95adf52019-08-13 00:02:0292 EXPECT_TRUE(NavigateToURL(shell(), base_url.Resolve("frames-X-X.html")));
[email protected]5f96f5a62014-01-10 00:05:1193 observer1.Wait();
94 ASSERT_EQ(2U, root->child_count());
95 EXPECT_EQ(0U, root->child_at(0)->child_count());
96 EXPECT_EQ(0U, root->child_at(1)->child_count());
97}
98
99// TODO(ajwong): Talk with nasko and merge this functionality with
100// FrameTreeShape.
101IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, FrameTreeShape2) {
Alex Moshchukaa95adf52019-08-13 00:02:02102 EXPECT_TRUE(NavigateToURL(
103 shell(), embedded_test_server()->GetURL("/frame_tree/top.html")));
[email protected]5f96f5a62014-01-10 00:05:11104
105 WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents());
Carlos Caballero15caeeb2021-10-27 09:57:55106 FrameTreeNode* root = wc->GetPrimaryFrameTree().root();
[email protected]5f96f5a62014-01-10 00:05:11107
[email protected]58faf942014-02-20 21:03:58108 // Check that the root node is properly created.
[email protected]5f96f5a62014-01-10 00:05:11109 ASSERT_EQ(3UL, root->child_count());
110 EXPECT_EQ(std::string(), root->frame_name());
[email protected]5f96f5a62014-01-10 00:05:11111
112 ASSERT_EQ(2UL, root->child_at(0)->child_count());
113 EXPECT_STREQ("1-1-name", root->child_at(0)->frame_name().c_str());
114
115 // Verify the deepest node exists and has the right name.
116 ASSERT_EQ(2UL, root->child_at(2)->child_count());
117 EXPECT_EQ(1UL, root->child_at(2)->child_at(1)->child_count());
118 EXPECT_EQ(0UL, root->child_at(2)->child_at(1)->child_at(0)->child_count());
Aran Gilman37d11632019-10-08 23:07:15119 EXPECT_STREQ(
120 "3-1-name",
[email protected]5f96f5a62014-01-10 00:05:11121 root->child_at(2)->child_at(1)->child_at(0)->frame_name().c_str());
122
123 // Navigate to about:blank, which should leave only the root node of the frame
124 // tree in the browser process.
Alex Moshchukaa95adf52019-08-13 00:02:02125 EXPECT_TRUE(
126 NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html")));
[email protected]5f96f5a62014-01-10 00:05:11127
Carlos Caballero15caeeb2021-10-27 09:57:55128 root = wc->GetPrimaryFrameTree().root();
[email protected]5f96f5a62014-01-10 00:05:11129 EXPECT_EQ(0UL, root->child_count());
130 EXPECT_EQ(std::string(), root->frame_name());
[email protected]5f96f5a62014-01-10 00:05:11131}
132
133// Test that we can navigate away if the previous renderer doesn't clean up its
134// child frames.
135IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, FrameTreeAfterCrash) {
Alex Moshchukaa95adf52019-08-13 00:02:02136 EXPECT_TRUE(NavigateToURL(
137 shell(), embedded_test_server()->GetURL("/frame_tree/top.html")));
[email protected]5f96f5a62014-01-10 00:05:11138
creise42f2a52014-09-18 18:14:57139 // Ensure the view and frame are live.
Lingqi Chi641aeee2021-09-08 00:32:58140 RenderFrameHostImpl* rfh1 = static_cast<RenderFrameHostImpl*>(
141 shell()->web_contents()->GetMainFrame());
142 RenderViewHost* rvh = rfh1->GetRenderViewHost();
creise42f2a52014-09-18 18:14:57143 EXPECT_TRUE(rvh->IsRenderViewLive());
arthursonzognif4c4a4a2019-08-08 18:54:44144 EXPECT_TRUE(rfh1->IsRenderFrameLive());
creise42f2a52014-09-18 18:14:57145
[email protected]5f96f5a62014-01-10 00:05:11146 // Crash the renderer so that it doesn't send any FrameDetached messages.
147 RenderProcessHostWatcher crash_observer(
148 shell()->web_contents(),
149 RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
Wez0abfbf512018-03-03 01:54:45150 ASSERT_TRUE(
151 shell()->web_contents()->GetMainFrame()->GetProcess()->Shutdown(0));
[email protected]5f96f5a62014-01-10 00:05:11152 crash_observer.Wait();
153
[email protected]58faf942014-02-20 21:03:58154 // The frame tree should be cleared.
[email protected]5f96f5a62014-01-10 00:05:11155 WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents());
Carlos Caballero15caeeb2021-10-27 09:57:55156 FrameTreeNode* root = wc->GetPrimaryFrameTree().root();
[email protected]5f96f5a62014-01-10 00:05:11157 EXPECT_EQ(0UL, root->child_count());
[email protected]5f96f5a62014-01-10 00:05:11158
creise42f2a52014-09-18 18:14:57159 // Ensure the view and frame aren't live anymore.
160 EXPECT_FALSE(rvh->IsRenderViewLive());
arthursonzognif4c4a4a2019-08-08 18:54:44161 EXPECT_FALSE(rfh1->IsRenderFrameLive());
creise42f2a52014-09-18 18:14:57162
[email protected]5f96f5a62014-01-10 00:05:11163 // Navigate to a new URL.
alexmos478dcbb2014-12-10 21:24:46164 GURL url(embedded_test_server()->GetURL("/title1.html"));
Alex Moshchukaa95adf52019-08-13 00:02:02165 EXPECT_TRUE(NavigateToURL(shell(), url));
[email protected]5f96f5a62014-01-10 00:05:11166 EXPECT_EQ(0UL, root->child_count());
[email protected]58faf942014-02-20 21:03:58167 EXPECT_EQ(url, root->current_url());
creise42f2a52014-09-18 18:14:57168
arthursonzognif4c4a4a2019-08-08 18:54:44169 RenderFrameHostImpl* rfh2 = root->current_frame_host();
creise42f2a52014-09-18 18:14:57170 // Ensure the view and frame are live again.
171 EXPECT_TRUE(rvh->IsRenderViewLive());
arthursonzognif4c4a4a2019-08-08 18:54:44172 EXPECT_TRUE(rfh2->IsRenderFrameLive());
[email protected]5f96f5a62014-01-10 00:05:11173}
174
175// Test that we can navigate away if the previous renderer doesn't clean up its
176// child frames.
Nasko Oskov31c45ff2019-10-16 01:15:19177IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, NavigateWithLeftoverFrames) {
alexmos478dcbb2014-12-10 21:24:46178 GURL base_url = embedded_test_server()->GetURL("A.com", "/site_isolation/");
[email protected]5f96f5a62014-01-10 00:05:11179
Alex Moshchukaa95adf52019-08-13 00:02:02180 EXPECT_TRUE(NavigateToURL(
181 shell(), embedded_test_server()->GetURL("/frame_tree/top.html")));
[email protected]5f96f5a62014-01-10 00:05:11182
183 // Hang the renderer so that it doesn't send any FrameDetached messages.
184 // (This navigation will never complete, so don't wait for it.)
Gyuyoung Kim107c2a02021-04-13 01:49:30185 shell()->LoadURL(GURL(blink::kChromeUIHangURL));
[email protected]5f96f5a62014-01-10 00:05:11186
187 // Check that the frame tree still has children.
188 WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents());
Carlos Caballero15caeeb2021-10-27 09:57:55189 FrameTreeNode* root = wc->GetPrimaryFrameTree().root();
[email protected]5f96f5a62014-01-10 00:05:11190 ASSERT_EQ(3UL, root->child_count());
191
192 // Navigate to a new URL. We use LoadURL because NavigateToURL will try to
193 // wait for the previous navigation to stop.
194 TestNavigationObserver tab_observer(wc, 1);
195 shell()->LoadURL(base_url.Resolve("blank.html"));
196 tab_observer.Wait();
197
[email protected]58faf942014-02-20 21:03:58198 // The frame tree should now be cleared.
[email protected]5f96f5a62014-01-10 00:05:11199 EXPECT_EQ(0UL, root->child_count());
[email protected]5f96f5a62014-01-10 00:05:11200}
201
creise42f2a52014-09-18 18:14:57202// Ensure that IsRenderFrameLive is true for main frames and same-site iframes.
203IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, IsRenderFrameLive) {
alexmos478dcbb2014-12-10 21:24:46204 GURL main_url(embedded_test_server()->GetURL("/frame_tree/top.html"));
Alex Moshchukaa95adf52019-08-13 00:02:02205 EXPECT_TRUE(NavigateToURL(shell(), main_url));
creise42f2a52014-09-18 18:14:57206
207 // It is safe to obtain the root frame tree node here, as it doesn't change.
208 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:55209 ->GetPrimaryFrameTree()
210 .root();
creise42f2a52014-09-18 18:14:57211
212 // The root and subframe should each have a live RenderFrame.
213 EXPECT_TRUE(
214 root->current_frame_host()->render_view_host()->IsRenderViewLive());
215 EXPECT_TRUE(root->current_frame_host()->IsRenderFrameLive());
216 EXPECT_TRUE(root->child_at(0)->current_frame_host()->IsRenderFrameLive());
217
218 // Load a same-site page into iframe and it should still be live.
alexmos478dcbb2014-12-10 21:24:46219 GURL http_url(embedded_test_server()->GetURL("/title1.html"));
Lukasz Anforowicz69c25dfd2020-11-12 21:50:20220 EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(0), http_url));
creise42f2a52014-09-18 18:14:57221 EXPECT_TRUE(
222 root->current_frame_host()->render_view_host()->IsRenderViewLive());
223 EXPECT_TRUE(root->current_frame_host()->IsRenderFrameLive());
224 EXPECT_TRUE(root->child_at(0)->current_frame_host()->IsRenderFrameLive());
225}
226
alexmosbc7eafa2014-12-06 01:38:09227// Ensure that origins are correctly set on navigations.
228IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, OriginSetOnNavigation) {
nick1466c842015-11-25 20:08:06229 GURL about_blank(url::kAboutBlankURL);
230 GURL main_url(
231 embedded_test_server()->GetURL("a.com", "/frame_tree/top.html"));
alexmosbc7eafa2014-12-06 01:38:09232 EXPECT_TRUE(NavigateToURL(shell(), main_url));
nick1466c842015-11-25 20:08:06233 WebContents* contents = shell()->web_contents();
alexmosbc7eafa2014-12-06 01:38:09234
235 // It is safe to obtain the root frame tree node here, as it doesn't change.
nick1466c842015-11-25 20:08:06236 FrameTreeNode* root =
Carlos Caballero15caeeb2021-10-27 09:57:55237 static_cast<WebContentsImpl*>(contents)->GetPrimaryFrameTree().root();
alexmosbc7eafa2014-12-06 01:38:09238
239 // Extra '/' is added because the replicated origin is serialized in RFC 6454
240 // format, which dictates no trailing '/', whereas GURL::GetOrigin does put a
241 // '/' at the end.
Mike West800532c2021-10-14 09:26:52242 EXPECT_EQ(main_url.DeprecatedGetOriginAsURL().spec(),
nick1466c842015-11-25 20:08:06243 root->current_origin().Serialize() + '/');
244 EXPECT_EQ(
Mike West800532c2021-10-14 09:26:52245 main_url.DeprecatedGetOriginAsURL().spec(),
nick1466c842015-11-25 20:08:06246 root->current_frame_host()->GetLastCommittedOrigin().Serialize() + '/');
alexmosbc7eafa2014-12-06 01:38:09247
nick1466c842015-11-25 20:08:06248 // The iframe is inititially same-origin.
249 EXPECT_TRUE(
250 root->current_frame_host()->GetLastCommittedOrigin().IsSameOriginWith(
251 root->child_at(0)->current_frame_host()->GetLastCommittedOrigin()));
252 EXPECT_EQ(root->current_origin().Serialize(), GetOriginFromRenderer(root));
253 EXPECT_EQ(root->child_at(0)->current_origin().Serialize(),
254 GetOriginFromRenderer(root->child_at(0)));
255
256 // Navigate the iframe cross-origin.
257 GURL frame_url(embedded_test_server()->GetURL("b.com", "/title1.html"));
Lukasz Anforowicz69c25dfd2020-11-12 21:50:20258 EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(0), frame_url));
nick1466c842015-11-25 20:08:06259 EXPECT_EQ(frame_url, root->child_at(0)->current_url());
Mike West800532c2021-10-14 09:26:52260 EXPECT_EQ(frame_url.DeprecatedGetOriginAsURL().spec(),
nick1466c842015-11-25 20:08:06261 root->child_at(0)->current_origin().Serialize() + '/');
262 EXPECT_FALSE(
263 root->current_frame_host()->GetLastCommittedOrigin().IsSameOriginWith(
264 root->child_at(0)->current_frame_host()->GetLastCommittedOrigin()));
265 EXPECT_EQ(root->current_origin().Serialize(), GetOriginFromRenderer(root));
266 EXPECT_EQ(root->child_at(0)->current_origin().Serialize(),
267 GetOriginFromRenderer(root->child_at(0)));
alexmosbc7eafa2014-12-06 01:38:09268
nick1466c842015-11-25 20:08:06269 // Parent-initiated about:blank navigation should inherit the parent's a.com
270 // origin.
271 NavigateIframeToURL(contents, "1-1-id", about_blank);
272 EXPECT_EQ(about_blank, root->child_at(0)->current_url());
Mike West800532c2021-10-14 09:26:52273 EXPECT_EQ(main_url.DeprecatedGetOriginAsURL().spec(),
nick1466c842015-11-25 20:08:06274 root->child_at(0)->current_origin().Serialize() + '/');
275 EXPECT_EQ(root->current_frame_host()->GetLastCommittedOrigin().Serialize(),
276 root->child_at(0)
277 ->current_frame_host()
278 ->GetLastCommittedOrigin()
279 .Serialize());
280 EXPECT_TRUE(
281 root->current_frame_host()->GetLastCommittedOrigin().IsSameOriginWith(
282 root->child_at(0)->current_frame_host()->GetLastCommittedOrigin()));
283 EXPECT_EQ(root->current_origin().Serialize(), GetOriginFromRenderer(root));
284 EXPECT_EQ(root->child_at(0)->current_origin().Serialize(),
285 GetOriginFromRenderer(root->child_at(0)));
alexmosbc7eafa2014-12-06 01:38:09286
287 GURL data_url("data:text/html,foo");
288 EXPECT_TRUE(NavigateToURL(shell(), data_url));
289
290 // Navigating to a data URL should set a unique origin. This is represented
291 // as "null" per RFC 6454.
nick1466c842015-11-25 20:08:06292 EXPECT_EQ("null", root->current_origin().Serialize());
Chris Palmerab5e5b52018-09-28 19:19:30293 EXPECT_TRUE(contents->GetMainFrame()->GetLastCommittedOrigin().opaque());
nick1466c842015-11-25 20:08:06294 EXPECT_EQ("null", GetOriginFromRenderer(root));
alexmosbc7eafa2014-12-06 01:38:09295
296 // Re-navigating to a normal URL should update the origin.
297 EXPECT_TRUE(NavigateToURL(shell(), main_url));
Mike West800532c2021-10-14 09:26:52298 EXPECT_EQ(main_url.DeprecatedGetOriginAsURL().spec(),
nick1466c842015-11-25 20:08:06299 root->current_origin().Serialize() + '/');
300 EXPECT_EQ(
Mike West800532c2021-10-14 09:26:52301 main_url.DeprecatedGetOriginAsURL().spec(),
nick1466c842015-11-25 20:08:06302 contents->GetMainFrame()->GetLastCommittedOrigin().Serialize() + '/');
Chris Palmerab5e5b52018-09-28 19:19:30303 EXPECT_FALSE(contents->GetMainFrame()->GetLastCommittedOrigin().opaque());
nick1466c842015-11-25 20:08:06304 EXPECT_EQ(root->current_origin().Serialize(), GetOriginFromRenderer(root));
alexmosbc7eafa2014-12-06 01:38:09305}
306
nick9f34e2892016-01-12 21:01:07307// Tests a cross-origin navigation to a blob URL. The main frame initiates this
308// navigation on its grandchild. It should wind up in the main frame's process.
309IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, NavigateGrandchildToBlob) {
310 WebContents* contents = shell()->web_contents();
311 FrameTreeNode* root =
Carlos Caballero15caeeb2021-10-27 09:57:55312 static_cast<WebContentsImpl*>(contents)->GetPrimaryFrameTree().root();
nick9f34e2892016-01-12 21:01:07313
314 // First, snapshot the FrameTree for a normal A(B(A)) case where all frames
315 // are served over http. The blob test should result in the same structure.
316 EXPECT_TRUE(NavigateToURL(
317 shell(), embedded_test_server()->GetURL(
318 "a.com", "/cross_site_iframe_factory.html?a(b(a))")));
Fergal Daly79f44292020-12-01 02:30:48319 std::string reference_tree = DepictFrameTree(*root);
nick9f34e2892016-01-12 21:01:07320
321 GURL main_url(embedded_test_server()->GetURL(
322 "a.com", "/cross_site_iframe_factory.html?a(b(c))"));
323 EXPECT_TRUE(NavigateToURL(shell(), main_url));
324
325 // The root node will initiate the navigation; its grandchild node will be the
326 // target of the navigation.
327 FrameTreeNode* target = root->child_at(0)->child_at(0);
328
creisb2d5f1f32016-11-07 23:25:05329 RenderFrameDeletedObserver deleted_observer(target->current_frame_host());
Nick Carterb7e71312018-08-03 23:36:13330 std::string html =
331 "<html><body><div>This is blob content.</div>"
332 "<script>"
Philip Jägenstedt67302a22018-09-14 09:58:05333 "window.parent.parent.postMessage('HI', self.origin);"
Nick Carterb7e71312018-08-03 23:36:13334 "</script></body></html>";
335 std::string script = JsReplace(
336 "new Promise((resolve) => {"
337 " window.addEventListener('message', resolve, false);"
338 " var blob = new Blob([$1], {type: 'text/html'});"
339 " var blob_url = URL.createObjectURL(blob);"
340 " frames[0][0].location.href = blob_url;"
341 "}).then((event) => {"
nick9f34e2892016-01-12 21:01:07342 " document.body.appendChild(document.createTextNode(event.data));"
Nick Carterb7e71312018-08-03 23:36:13343 " return event.source.location.href;"
344 "});",
345 html);
346 std::string blob_url_string = EvalJs(root, script).ExtractString();
creisb2d5f1f32016-11-07 23:25:05347 // Wait for the RenderFrame to go away, if this will be cross-process.
348 if (AreAllSitesIsolatedForTesting())
349 deleted_observer.WaitUntilDeleted();
nick9f34e2892016-01-12 21:01:07350 EXPECT_EQ(GURL(blob_url_string), target->current_url());
351 EXPECT_EQ(url::kBlobScheme, target->current_url().scheme());
Chris Palmerab5e5b52018-09-28 19:19:30352 EXPECT_FALSE(target->current_origin().opaque());
nick9f34e2892016-01-12 21:01:07353 EXPECT_EQ("a.com", target->current_origin().host());
354 EXPECT_EQ(url::kHttpScheme, target->current_origin().scheme());
Nick Carterb7e71312018-08-03 23:36:13355 EXPECT_EQ("This is blob content.",
356 EvalJs(target, "document.body.children[0].innerHTML"));
Fergal Daly79f44292020-12-01 02:30:48357 EXPECT_EQ(reference_tree, DepictFrameTree(*root));
nick9f34e2892016-01-12 21:01:07358}
359
360IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, NavigateChildToAboutBlank) {
361 GURL main_url(embedded_test_server()->GetURL(
362 "a.com", "/cross_site_iframe_factory.html?a(b(c))"));
363 EXPECT_TRUE(NavigateToURL(shell(), main_url));
364 WebContentsImpl* contents =
365 static_cast<WebContentsImpl*>(shell()->web_contents());
366
367 // The leaf node (c.com) will be navigated. Its parent node (b.com) will
368 // initiate the navigation.
369 FrameTreeNode* target =
Carlos Caballero15caeeb2021-10-27 09:57:55370 contents->GetPrimaryFrameTree().root()->child_at(0)->child_at(0);
Harkiran Bolaria4eaae3072021-09-21 10:45:48371 RenderFrameHost* initiator_rfh = target->parent();
nick9f34e2892016-01-12 21:01:07372
373 // Give the target a name.
Nick Carterb7e71312018-08-03 23:36:13374 EXPECT_TRUE(ExecJs(target, "window.name = 'target';"));
nick9f34e2892016-01-12 21:01:07375
376 // Use window.open(about:blank), then poll the document for access.
Nick Carterb7e71312018-08-03 23:36:13377 EvalJsResult about_blank_origin = EvalJs(
Harkiran Bolaria4eaae3072021-09-21 10:45:48378 initiator_rfh,
Nick Carterb7e71312018-08-03 23:36:13379 "new Promise(resolve => {"
380 " var didNavigate = false;"
381 " var intervalID = setInterval(function() {"
382 " if (!didNavigate) {"
383 " didNavigate = true;"
384 " window.open('about:blank', 'target');"
385 " }"
386 " // Poll the document until it doesn't throw a SecurityError.\n"
387 " try {"
388 " frames[0].document.write('Hi from ' + document.domain);"
389 " } catch (e) { return; }"
390 " clearInterval(intervalID);"
Philip Jägenstedt67302a22018-09-14 09:58:05391 " resolve(frames[0].self.origin);"
Nick Carterb7e71312018-08-03 23:36:13392 " }, 16);"
393 "});");
Nick Carterb7e71312018-08-03 23:36:13394 EXPECT_EQ(target->current_origin(), about_blank_origin);
Nate Chapinab67b042021-05-04 21:00:51395 EXPECT_EQ(GURL(url::kAboutBlankURL), target->current_url());
396 EXPECT_EQ(url::kAboutScheme, target->current_url().scheme());
Chris Palmerab5e5b52018-09-28 19:19:30397 EXPECT_FALSE(target->current_origin().opaque());
nick9f34e2892016-01-12 21:01:07398 EXPECT_EQ("b.com", target->current_origin().host());
399 EXPECT_EQ(url::kHttpScheme, target->current_origin().scheme());
nick9f34e2892016-01-12 21:01:07400
Nick Carterb7e71312018-08-03 23:36:13401 EXPECT_EQ("Hi from b.com", EvalJs(target, "document.body.innerHTML"));
nick9f34e2892016-01-12 21:01:07402}
403
404// Nested iframes, three origins: A(B(C)). Frame A navigates C to about:blank
405// (via window.open). This should wind up in A's origin per the spec. Test fails
406// because of http://crbug.com/564292
407IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest,
408 DISABLED_NavigateGrandchildToAboutBlank) {
409 GURL main_url(embedded_test_server()->GetURL(
410 "a.com", "/cross_site_iframe_factory.html?a(b(c))"));
411 EXPECT_TRUE(NavigateToURL(shell(), main_url));
412 WebContentsImpl* contents =
413 static_cast<WebContentsImpl*>(shell()->web_contents());
414
415 // The leaf node (c.com) will be navigated. Its grandparent node (a.com) will
416 // initiate the navigation.
417 FrameTreeNode* target =
Carlos Caballero15caeeb2021-10-27 09:57:55418 contents->GetPrimaryFrameTree().root()->child_at(0)->child_at(0);
Harkiran Bolaria4eaae3072021-09-21 10:45:48419 RenderFrameHost* initiator_rfh = target->parent()->GetParent();
nick9f34e2892016-01-12 21:01:07420
421 // Give the target a name.
Nick Carterb7e71312018-08-03 23:36:13422 EXPECT_TRUE(ExecJs(target, "window.name = 'target';"));
nick9f34e2892016-01-12 21:01:07423
424 // Use window.open(about:blank), then poll the document for access.
Nick Carterb7e71312018-08-03 23:36:13425 EvalJsResult about_blank_origin =
Harkiran Bolaria4eaae3072021-09-21 10:45:48426 EvalJs(initiator_rfh,
Nick Carterb7e71312018-08-03 23:36:13427 "new Promise((resolve) => {"
428 " var didNavigate = false;"
429 " var intervalID = setInterval(() => {"
430 " if (!didNavigate) {"
431 " didNavigate = true;"
432 " window.open('about:blank', 'target');"
433 " }"
434 " // May raise a SecurityError, that's expected.\n"
435 " try {"
436 " frames[0][0].document.write('Hi from ' + document.domain);"
437 " } catch (e) { return; }"
438 " clearInterval(intervalID);"
Philip Jägenstedt67302a22018-09-14 09:58:05439 " resolve(frames[0][0].self.origin);"
Nick Carterb7e71312018-08-03 23:36:13440 " }, 16);"
441 "});");
442 EXPECT_EQ(target->current_origin(), about_blank_origin);
nick9f34e2892016-01-12 21:01:07443 EXPECT_EQ(GURL(url::kAboutBlankURL), target->current_url());
444 EXPECT_EQ(url::kAboutScheme, target->current_url().scheme());
Chris Palmerab5e5b52018-09-28 19:19:30445 EXPECT_FALSE(target->current_origin().opaque());
nick9f34e2892016-01-12 21:01:07446 EXPECT_EQ("a.com", target->current_origin().host());
447 EXPECT_EQ(url::kHttpScheme, target->current_origin().scheme());
nick9f34e2892016-01-12 21:01:07448
Nick Carterb7e71312018-08-03 23:36:13449 EXPECT_EQ("Hi from a.com", EvalJs(target, "document.body.innerHTML"));
nick9f34e2892016-01-12 21:01:07450}
451
Nasko Oskov039121012019-01-11 00:21:32452// Tests a cross-origin navigation to a data: URL. The main frame initiates this
453// navigation on its grandchild. It should wind up in the main frame's process
454// and have precursor origin of the main frame origin.
455IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, NavigateGrandchildToDataUrl) {
456 GURL main_url(embedded_test_server()->GetURL(
457 "a.com", "/cross_site_iframe_factory.html?a(b(c))"));
458 EXPECT_TRUE(NavigateToURL(shell(), main_url));
459 WebContentsImpl* contents =
460 static_cast<WebContentsImpl*>(shell()->web_contents());
461
462 // The leaf node (c.com) will be navigated. Its grandparent node (a.com) will
463 // initiate the navigation.
464 FrameTreeNode* target =
Carlos Caballero15caeeb2021-10-27 09:57:55465 contents->GetPrimaryFrameTree().root()->child_at(0)->child_at(0);
Harkiran Bolaria4eaae3072021-09-21 10:45:48466 RenderFrameHostImpl* initiator_rfh = target->parent()->GetParent();
Nasko Oskov039121012019-01-11 00:21:32467
468 // Give the target a name.
469 EXPECT_TRUE(ExecJs(target, "window.name = 'target';"));
470
471 // Navigate the target frame through the initiator frame.
472 {
473 TestFrameNavigationObserver observer(target);
Harkiran Bolaria4eaae3072021-09-21 10:45:48474 EXPECT_TRUE(ExecJs(initiator_rfh,
475 "window.open('data:text/html,content', 'target');"));
Nasko Oskov039121012019-01-11 00:21:32476 observer.Wait();
477 }
478
479 url::Origin original_target_origin =
480 target->current_frame_host()->GetLastCommittedOrigin();
481 EXPECT_TRUE(original_target_origin.opaque());
482 EXPECT_EQ(original_target_origin.GetTupleOrPrecursorTupleIfOpaque(),
483 url::SchemeHostPort(main_url));
484
485 // Navigate the grandchild frame again cross-process to foo.com, then
486 // go back in session history. The origin for the data: URL must be preserved.
487 {
488 TestFrameNavigationObserver observer(target);
489 EXPECT_TRUE(ExecJs(target, JsReplace("window.location = $1",
490 embedded_test_server()->GetURL(
491 "foo.com", "/title2.html"))));
492 observer.Wait();
493 }
494 EXPECT_NE(original_target_origin,
495 target->current_frame_host()->GetLastCommittedOrigin());
496 {
497 TestFrameNavigationObserver observer(target);
498 contents->GetController().GoBack();
499 observer.Wait();
500 }
501
502 url::Origin target_origin =
503 target->current_frame_host()->GetLastCommittedOrigin();
504 EXPECT_TRUE(target_origin.opaque());
505 EXPECT_EQ(target_origin.GetTupleOrPrecursorTupleIfOpaque(),
506 url::SchemeHostPort(main_url));
507 EXPECT_EQ(target_origin, original_target_origin);
508}
509
naskofaa01fb2016-04-30 01:04:17510// Ensures that iframe with srcdoc is always put in the same origin as its
511// parent frame.
512IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, ChildFrameWithSrcdoc) {
513 GURL main_url(embedded_test_server()->GetURL(
514 "a.com", "/cross_site_iframe_factory.html?a(b)"));
515 EXPECT_TRUE(NavigateToURL(shell(), main_url));
516 WebContentsImpl* contents =
517 static_cast<WebContentsImpl*>(shell()->web_contents());
Carlos Caballero15caeeb2021-10-27 09:57:55518 FrameTreeNode* root = contents->GetPrimaryFrameTree().root();
naskofaa01fb2016-04-30 01:04:17519 EXPECT_EQ(1U, root->child_count());
520
521 FrameTreeNode* child = root->child_at(0);
Philip Jägenstedt67302a22018-09-14 09:58:05522 std::string frame_origin = EvalJs(child, "self.origin;").ExtractString();
naskofaa01fb2016-04-30 01:04:17523 EXPECT_TRUE(
524 child->current_frame_host()->GetLastCommittedOrigin().IsSameOriginWith(
Daniel Cheng88186bd52017-10-20 08:14:46525 url::Origin::Create(GURL(frame_origin))));
naskofaa01fb2016-04-30 01:04:17526 EXPECT_FALSE(
527 root->current_frame_host()->GetLastCommittedOrigin().IsSameOriginWith(
Daniel Cheng88186bd52017-10-20 08:14:46528 url::Origin::Create(GURL(frame_origin))));
naskofaa01fb2016-04-30 01:04:17529
530 // Create a new iframe with srcdoc and add it to the main frame. It should
531 // be created in the same SiteInstance as the parent.
532 {
Aran Gilman37d11632019-10-08 23:07:15533 std::string script(
534 "var f = document.createElement('iframe');"
535 "f.srcdoc = 'some content';"
536 "document.body.appendChild(f)");
naskofaa01fb2016-04-30 01:04:17537 TestNavigationObserver observer(shell()->web_contents());
Nick Carterb7e71312018-08-03 23:36:13538 EXPECT_TRUE(ExecJs(root, script));
naskofaa01fb2016-04-30 01:04:17539 EXPECT_EQ(2U, root->child_count());
540 observer.Wait();
541
Lukasz Anforowicz42d3d07f2019-06-19 01:06:42542 EXPECT_TRUE(root->child_at(1)->current_url().IsAboutSrcdoc());
Peter Kastingeb8c3ce2021-08-20 04:39:35543 EvalJsResult js_result = EvalJs(root->child_at(1), "self.origin");
Mike West800532c2021-10-14 09:26:52544 EXPECT_EQ(root->current_frame_host()
545 ->GetLastCommittedURL()
546 .DeprecatedGetOriginAsURL(),
Peter Kastingeb8c3ce2021-08-20 04:39:35547 GURL(js_result.ExtractString()));
Mike West800532c2021-10-14 09:26:52548 EXPECT_NE(child->current_frame_host()
549 ->GetLastCommittedURL()
550 .DeprecatedGetOriginAsURL(),
Peter Kastingeb8c3ce2021-08-20 04:39:35551 GURL(js_result.ExtractString()));
naskofaa01fb2016-04-30 01:04:17552 }
553
554 // Set srcdoc on the existing cross-site frame. It should navigate the frame
555 // back to the origin of the parent.
556 {
Aran Gilman37d11632019-10-08 23:07:15557 std::string script(
558 "var f = document.getElementById('child-0');"
559 "f.srcdoc = 'some content';");
naskofaa01fb2016-04-30 01:04:17560 TestNavigationObserver observer(shell()->web_contents());
Nick Carterb7e71312018-08-03 23:36:13561 EXPECT_TRUE(ExecJs(root, script));
naskofaa01fb2016-04-30 01:04:17562 observer.Wait();
563
Lukasz Anforowicz42d3d07f2019-06-19 01:06:42564 EXPECT_TRUE(child->current_url().IsAboutSrcdoc());
Avi Drissmana3144c582021-10-14 17:00:26565 EXPECT_EQ(root->current_frame_host()->GetLastCommittedOrigin().Serialize(),
566 EvalJs(child, "self.origin"));
naskofaa01fb2016-04-30 01:04:17567 }
568}
569
Ian Clelland5cbaaf82017-11-27 22:00:03570// Ensure that sandbox flags are correctly set in the main frame when set by
571// Content-Security-Policy header.
572IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, SandboxFlagsSetForMainFrame) {
573 GURL main_url(embedded_test_server()->GetURL("/csp_sandboxed_frame.html"));
574 EXPECT_TRUE(NavigateToURL(shell(), main_url));
575
576 // It is safe to obtain the root frame tree node here, as it doesn't change.
577 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:55578 ->GetPrimaryFrameTree()
579 .root();
Ian Clelland5cbaaf82017-11-27 22:00:03580
581 // Verify that sandbox flags are set properly for the root FrameTreeNode and
582 // RenderFrameHost. Root frame is sandboxed with "allow-scripts".
arthursonzognib93a4472020-04-10 07:38:00583 EXPECT_EQ(network::mojom::WebSandboxFlags::kNone,
Ian Clelland5cbaaf82017-11-27 22:00:03584 root->effective_frame_policy().sandbox_flags);
arthursonzognib93a4472020-04-10 07:38:00585 EXPECT_EQ(network::mojom::WebSandboxFlags::kAll &
586 ~network::mojom::WebSandboxFlags::kScripts &
587 ~network::mojom::WebSandboxFlags::kAutomaticFeatures,
Ian Clelland5cbaaf82017-11-27 22:00:03588 root->active_sandbox_flags());
589 EXPECT_EQ(root->active_sandbox_flags(),
590 root->current_frame_host()->active_sandbox_flags());
591
592 // Verify that child frames inherit sandbox flags from the root. First frame
593 // has no explicitly set flags of its own, and should inherit those from the
594 // root. Second frame is completely sandboxed.
arthursonzognib93a4472020-04-10 07:38:00595 EXPECT_EQ(network::mojom::WebSandboxFlags::kAll &
596 ~network::mojom::WebSandboxFlags::kScripts &
597 ~network::mojom::WebSandboxFlags::kAutomaticFeatures,
Ian Clelland5cbaaf82017-11-27 22:00:03598 root->child_at(0)->effective_frame_policy().sandbox_flags);
arthursonzognib93a4472020-04-10 07:38:00599 EXPECT_EQ(network::mojom::WebSandboxFlags::kAll &
600 ~network::mojom::WebSandboxFlags::kScripts &
601 ~network::mojom::WebSandboxFlags::kAutomaticFeatures,
Ian Clelland5cbaaf82017-11-27 22:00:03602 root->child_at(0)->active_sandbox_flags());
603 EXPECT_EQ(root->child_at(0)->active_sandbox_flags(),
604 root->child_at(0)->current_frame_host()->active_sandbox_flags());
arthursonzognib93a4472020-04-10 07:38:00605 EXPECT_EQ(network::mojom::WebSandboxFlags::kAll,
Ian Clelland5cbaaf82017-11-27 22:00:03606 root->child_at(1)->effective_frame_policy().sandbox_flags);
arthursonzognib93a4472020-04-10 07:38:00607 EXPECT_EQ(network::mojom::WebSandboxFlags::kAll,
Ian Clelland5cbaaf82017-11-27 22:00:03608 root->child_at(1)->active_sandbox_flags());
609 EXPECT_EQ(root->child_at(1)->active_sandbox_flags(),
610 root->child_at(1)->current_frame_host()->active_sandbox_flags());
611
612 // Navigating the main frame to a different URL should clear sandbox flags.
613 GURL unsandboxed_url(embedded_test_server()->GetURL("/title1.html"));
Lukasz Anforowicz69c25dfd2020-11-12 21:50:20614 EXPECT_TRUE(NavigateToURLFromRenderer(root, unsandboxed_url));
Ian Clelland5cbaaf82017-11-27 22:00:03615
616 // Verify that sandbox flags are cleared properly for the root FrameTreeNode
617 // and RenderFrameHost.
arthursonzognib93a4472020-04-10 07:38:00618 EXPECT_EQ(network::mojom::WebSandboxFlags::kNone,
Ian Clelland5cbaaf82017-11-27 22:00:03619 root->effective_frame_policy().sandbox_flags);
arthursonzognib93a4472020-04-10 07:38:00620 EXPECT_EQ(network::mojom::WebSandboxFlags::kNone,
621 root->active_sandbox_flags());
622 EXPECT_EQ(network::mojom::WebSandboxFlags::kNone,
Ian Clelland5cbaaf82017-11-27 22:00:03623 root->current_frame_host()->active_sandbox_flags());
624}
625
alexmosf832a2f2015-01-27 22:44:03626// Ensure that sandbox flags are correctly set when child frames are created.
627IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, SandboxFlagsSetForChildFrames) {
628 GURL main_url(embedded_test_server()->GetURL("/sandboxed_frames.html"));
629 EXPECT_TRUE(NavigateToURL(shell(), main_url));
630
631 // It is safe to obtain the root frame tree node here, as it doesn't change.
632 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:55633 ->GetPrimaryFrameTree()
634 .root();
alexmosf832a2f2015-01-27 22:44:03635
636 // Verify that sandbox flags are set properly for all FrameTreeNodes.
637 // First frame is completely sandboxed; second frame uses "allow-scripts",
638 // which resets both SandboxFlags::Scripts and
639 // SandboxFlags::AutomaticFeatures bits per blink::parseSandboxPolicy(), and
640 // third frame has "allow-scripts allow-same-origin".
arthursonzognib93a4472020-04-10 07:38:00641 EXPECT_EQ(network::mojom::WebSandboxFlags::kNone,
Ian Clellandcdc4f312017-10-13 22:24:12642 root->effective_frame_policy().sandbox_flags);
arthursonzognib93a4472020-04-10 07:38:00643 EXPECT_EQ(network::mojom::WebSandboxFlags::kAll,
Ian Clellandcdc4f312017-10-13 22:24:12644 root->child_at(0)->effective_frame_policy().sandbox_flags);
arthursonzognib93a4472020-04-10 07:38:00645 EXPECT_EQ(network::mojom::WebSandboxFlags::kAll &
646 ~network::mojom::WebSandboxFlags::kScripts &
647 ~network::mojom::WebSandboxFlags::kAutomaticFeatures,
Ian Clellandcdc4f312017-10-13 22:24:12648 root->child_at(1)->effective_frame_policy().sandbox_flags);
arthursonzognib93a4472020-04-10 07:38:00649 EXPECT_EQ(network::mojom::WebSandboxFlags::kAll &
650 ~network::mojom::WebSandboxFlags::kScripts &
651 ~network::mojom::WebSandboxFlags::kAutomaticFeatures &
652 ~network::mojom::WebSandboxFlags::kOrigin,
Ian Clellandcdc4f312017-10-13 22:24:12653 root->child_at(2)->effective_frame_policy().sandbox_flags);
alexmosf832a2f2015-01-27 22:44:03654
655 // Sandboxed frames should set a unique origin unless they have the
656 // "allow-same-origin" directive.
alexmos6e940102016-01-19 22:47:25657 EXPECT_EQ("null", root->child_at(0)->current_origin().Serialize());
658 EXPECT_EQ("null", root->child_at(1)->current_origin().Serialize());
Mike West800532c2021-10-14 09:26:52659 EXPECT_EQ(main_url.DeprecatedGetOriginAsURL().spec(),
alexmos6e940102016-01-19 22:47:25660 root->child_at(2)->current_origin().Serialize() + "/");
alexmosf832a2f2015-01-27 22:44:03661
662 // Navigating to a different URL should not clear sandbox flags.
663 GURL frame_url(embedded_test_server()->GetURL("/title1.html"));
664 NavigateFrameToURL(root->child_at(0), frame_url);
arthursonzognib93a4472020-04-10 07:38:00665 EXPECT_EQ(network::mojom::WebSandboxFlags::kAll,
Ian Clellandcdc4f312017-10-13 22:24:12666 root->child_at(0)->effective_frame_policy().sandbox_flags);
alexmosf832a2f2015-01-27 22:44:03667}
668
Ian Clelland5cbaaf82017-11-27 22:00:03669// Ensure that sandbox flags are correctly set in the child frames when set by
670// Content-Security-Policy header, and in combination with the sandbox iframe
671// attribute.
672IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest,
673 SandboxFlagsSetByCSPForChildFrames) {
674 GURL main_url(embedded_test_server()->GetURL("/sandboxed_frames_csp.html"));
675 EXPECT_TRUE(NavigateToURL(shell(), main_url));
676
677 // It is safe to obtain the root frame tree node here, as it doesn't change.
678 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:55679 ->GetPrimaryFrameTree()
680 .root();
Ian Clelland5cbaaf82017-11-27 22:00:03681
682 // Verify that sandbox flags are set properly for all FrameTreeNodes.
683 // First frame has no iframe sandbox flags, but the framed document is served
684 // with a CSP header which sets "allow-scripts", "allow-popups" and
685 // "allow-pointer-lock".
686 // Second frame is sandboxed with "allow-scripts", "allow-pointer-lock" and
687 // "allow-orientation-lock", and the framed document is also served with a CSP
688 // header which uses "allow-popups" and "allow-pointer-lock". The resulting
689 // sandbox for the frame should only have "allow-pointer-lock".
arthursonzognib93a4472020-04-10 07:38:00690 EXPECT_EQ(network::mojom::WebSandboxFlags::kNone,
Ian Clelland5cbaaf82017-11-27 22:00:03691 root->effective_frame_policy().sandbox_flags);
arthursonzognib93a4472020-04-10 07:38:00692 EXPECT_EQ(network::mojom::WebSandboxFlags::kNone,
693 root->active_sandbox_flags());
Ian Clelland5cbaaf82017-11-27 22:00:03694 EXPECT_EQ(root->active_sandbox_flags(),
695 root->current_frame_host()->active_sandbox_flags());
arthursonzognib93a4472020-04-10 07:38:00696 EXPECT_EQ(network::mojom::WebSandboxFlags::kNone,
Ian Clelland5cbaaf82017-11-27 22:00:03697 root->child_at(0)->effective_frame_policy().sandbox_flags);
arthursonzognib93a4472020-04-10 07:38:00698 EXPECT_EQ(network::mojom::WebSandboxFlags::kAll &
699 ~network::mojom::WebSandboxFlags::kScripts &
700 ~network::mojom::WebSandboxFlags::kAutomaticFeatures &
701 ~network::mojom::WebSandboxFlags::kPopups &
702 ~network::mojom::WebSandboxFlags::kPointerLock,
Ian Clelland5cbaaf82017-11-27 22:00:03703 root->child_at(0)->active_sandbox_flags());
704 EXPECT_EQ(root->child_at(0)->active_sandbox_flags(),
705 root->child_at(0)->current_frame_host()->active_sandbox_flags());
arthursonzognib93a4472020-04-10 07:38:00706 EXPECT_EQ(network::mojom::WebSandboxFlags::kAll &
707 ~network::mojom::WebSandboxFlags::kScripts &
708 ~network::mojom::WebSandboxFlags::kAutomaticFeatures &
709 ~network::mojom::WebSandboxFlags::kPointerLock &
710 ~network::mojom::WebSandboxFlags::kOrientationLock,
Ian Clelland5cbaaf82017-11-27 22:00:03711 root->child_at(1)->effective_frame_policy().sandbox_flags);
arthursonzognib93a4472020-04-10 07:38:00712 EXPECT_EQ(network::mojom::WebSandboxFlags::kAll &
713 ~network::mojom::WebSandboxFlags::kScripts &
714 ~network::mojom::WebSandboxFlags::kAutomaticFeatures &
715 ~network::mojom::WebSandboxFlags::kPointerLock,
Ian Clelland5cbaaf82017-11-27 22:00:03716 root->child_at(1)->active_sandbox_flags());
717 EXPECT_EQ(root->child_at(1)->active_sandbox_flags(),
718 root->child_at(1)->current_frame_host()->active_sandbox_flags());
719
720 // Navigating to a different URL *should* clear CSP-set sandbox flags, but
721 // should retain those flags set by the frame owner.
722 GURL frame_url(embedded_test_server()->GetURL("/title1.html"));
723
Lukasz Anforowicz69c25dfd2020-11-12 21:50:20724 EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(0), frame_url));
arthursonzognib93a4472020-04-10 07:38:00725 EXPECT_EQ(network::mojom::WebSandboxFlags::kNone,
Ian Clelland5cbaaf82017-11-27 22:00:03726 root->child_at(0)->effective_frame_policy().sandbox_flags);
arthursonzognib93a4472020-04-10 07:38:00727 EXPECT_EQ(network::mojom::WebSandboxFlags::kNone,
Ian Clelland5cbaaf82017-11-27 22:00:03728 root->child_at(0)->active_sandbox_flags());
729 EXPECT_EQ(root->child_at(0)->active_sandbox_flags(),
730 root->child_at(0)->current_frame_host()->active_sandbox_flags());
731
Lukasz Anforowicz69c25dfd2020-11-12 21:50:20732 EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(1), frame_url));
arthursonzognib93a4472020-04-10 07:38:00733 EXPECT_EQ(network::mojom::WebSandboxFlags::kAll &
734 ~network::mojom::WebSandboxFlags::kScripts &
735 ~network::mojom::WebSandboxFlags::kAutomaticFeatures &
736 ~network::mojom::WebSandboxFlags::kPointerLock &
737 ~network::mojom::WebSandboxFlags::kOrientationLock,
Ian Clelland5cbaaf82017-11-27 22:00:03738 root->child_at(1)->effective_frame_policy().sandbox_flags);
arthursonzognib93a4472020-04-10 07:38:00739 EXPECT_EQ(network::mojom::WebSandboxFlags::kAll &
740 ~network::mojom::WebSandboxFlags::kScripts &
741 ~network::mojom::WebSandboxFlags::kAutomaticFeatures &
742 ~network::mojom::WebSandboxFlags::kPointerLock &
743 ~network::mojom::WebSandboxFlags::kOrientationLock,
Ian Clelland5cbaaf82017-11-27 22:00:03744 root->child_at(1)->active_sandbox_flags());
745 EXPECT_EQ(root->child_at(1)->active_sandbox_flags(),
746 root->child_at(1)->current_frame_host()->active_sandbox_flags());
747}
748
alexmose201c7cd2015-06-10 17:14:21749// Ensure that a popup opened from a subframe sets its opener to the subframe's
750// FrameTreeNode, and that the opener is cleared if the subframe is destroyed.
751IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, SubframeOpenerSetForNewWindow) {
752 GURL main_url(embedded_test_server()->GetURL("/frame_tree/top.html"));
753 EXPECT_TRUE(NavigateToURL(shell(), main_url));
754
755 // It is safe to obtain the root frame tree node here, as it doesn't change.
756 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:55757 ->GetPrimaryFrameTree()
758 .root();
alexmose201c7cd2015-06-10 17:14:21759
760 // Open a new window from a subframe.
761 ShellAddedObserver new_shell_observer;
762 GURL popup_url(embedded_test_server()->GetURL("foo.com", "/title1.html"));
Nick Carterb7e71312018-08-03 23:36:13763 EXPECT_TRUE(
764 ExecJs(root->child_at(0), JsReplace("window.open($1);", popup_url)));
alexmose201c7cd2015-06-10 17:14:21765 Shell* new_shell = new_shell_observer.GetShell();
766 WebContents* new_contents = new_shell->web_contents();
Fergal Dalyf0522332020-07-18 06:09:46767 EXPECT_TRUE(WaitForLoadStop(new_contents));
alexmose201c7cd2015-06-10 17:14:21768
769 // Check that the new window's opener points to the correct subframe on
770 // original window.
771 FrameTreeNode* popup_root =
Carlos Caballero15caeeb2021-10-27 09:57:55772 static_cast<WebContentsImpl*>(new_contents)->GetPrimaryFrameTree().root();
alexmose201c7cd2015-06-10 17:14:21773 EXPECT_EQ(root->child_at(0), popup_root->opener());
774
775 // Close the original window. This should clear the new window's opener.
776 shell()->Close();
777 EXPECT_EQ(nullptr, popup_root->opener());
778}
779
Shivani Sharmac4f561582018-11-15 15:58:39780// Tests that the user activation bits get cleared when a same-site document is
781// installed in the frame.
782IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest,
783 ClearUserActivationForNewDocument) {
784 GURL main_url(embedded_test_server()->GetURL("/frame_tree/top.html"));
785 EXPECT_TRUE(NavigateToURL(shell(), main_url));
786
787 // It is safe to obtain the root frame tree node here, as it doesn't change.
788 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:55789 ->GetPrimaryFrameTree()
790 .root();
Shivani Sharmac4f561582018-11-15 15:58:39791
Mustaq Ahmed4baa9a6e82019-12-20 23:43:46792 EXPECT_FALSE(root->HasStickyUserActivation());
Shivani Sharmac4f561582018-11-15 15:58:39793 EXPECT_FALSE(root->HasTransientUserActivation());
794
795 // Set the user activation bits.
796 root->UpdateUserActivationState(
Mustaq Ahmeddc195e5b2020-08-04 18:45:11797 blink::mojom::UserActivationUpdateType::kNotifyActivation,
798 blink::mojom::UserActivationNotificationType::kTest);
Mustaq Ahmed4baa9a6e82019-12-20 23:43:46799 EXPECT_TRUE(root->HasStickyUserActivation());
Shivani Sharmac4f561582018-11-15 15:58:39800 EXPECT_TRUE(root->HasTransientUserActivation());
801
802 // Install a new same-site document to check the clearing of user activation
803 // bits.
804 GURL url(embedded_test_server()->GetURL("/title1.html"));
805 EXPECT_TRUE(NavigateToURL(shell(), url));
806
Mustaq Ahmed4baa9a6e82019-12-20 23:43:46807 EXPECT_FALSE(root->HasStickyUserActivation());
Shivani Sharmac4f561582018-11-15 15:58:39808 EXPECT_FALSE(root->HasTransientUserActivation());
809}
810
Dominic Farolino316e7932021-09-03 21:13:59811class FencedFrameTreeBrowserTest
812 : public FrameTreeBrowserTest,
813 public ::testing::WithParamInterface<
814 blink::features::FencedFramesImplementationType> {
shivanigithub93878d02021-06-15 11:37:53815 public:
Dominic Farolino316e7932021-09-03 21:13:59816 // Provides meaningful param names instead of /0 and /1.
817 static std::string DescribeParams(
818 const ::testing::TestParamInfo<ParamType>& info) {
819 switch (info.param) {
820 case blink::features::FencedFramesImplementationType::kShadowDOM:
821 return "ShadowDOM";
822 case blink::features::FencedFramesImplementationType::kMPArch:
823 return "MPArch";
824 }
825 }
826
shivanigithub93878d02021-06-15 11:37:53827 FencedFrameTreeBrowserTest()
828 : https_server_(net::EmbeddedTestServer::TYPE_HTTPS) {
shivanigithubf3e92da2021-10-07 15:37:41829 scoped_feature_list_.InitWithFeaturesAndParameters(
830 {{blink::features::kFencedFrames,
831 {{"implementation_type",
832 GetParam() ==
833 blink::features::FencedFramesImplementationType::kShadowDOM
834 ? "shadow_dom"
835 : "mparch"}}},
836 {blink::features::kThirdPartyStoragePartitioning, {}}},
837 {/* disabled_features */});
Dominic Farolino316e7932021-09-03 21:13:59838 }
839
840 // `node` is expected to be the child FrameTreeNode created in response to a
841 // <fencedframe> element being created. This test class is parameterized over
842 // the MPArch and the ShadowDOM implementation of fenced frames, which is why
843 // this method:
844 // - Returns `node` if we're in the ShadowDOM version
845 // - Returns the FrameTreeNode of the fenced frame's inner FrameTree, if
846 // we're in the MPArch version of fenced frames
847 FrameTreeNode* GetFencedFrameRootNode(FrameTreeNode* node) {
848 if (GetParam() ==
849 blink::features::FencedFramesImplementationType::kShadowDOM)
850 return node;
851
852 int inner_node_id =
853 node->current_frame_host()->inner_tree_main_frame_tree_node_id();
854 return FrameTreeNode::GloballyFindByID(inner_node_id);
855 }
856
857 // This is needed because `TestFrameNavigationObserver` doesn't work properly
858 // from within the context of a fenced frame's FrameTree. See the comments
859 // below.
860 void NavigateFrameInsideFencedFrameTreeAndWaitForFinishedLoad(
861 const ToRenderFrameHost& adapter,
862 GURL url,
Nan Linc4aa6f82021-10-14 19:15:24863 const std::string& navigate_script,
864 net::Error expected_net_error_code = net::OK) {
Dominic Farolino316e7932021-09-03 21:13:59865 RenderFrameHostImpl* rfh =
866 static_cast<RenderFrameHostImpl*>(adapter.render_frame_host());
Nan Linc4aa6f82021-10-14 19:15:24867
868 FrameTreeNode* frame_tree_node = rfh->frame_tree_node();
869 EXPECT_TRUE(frame_tree_node->IsInFencedFrameTree());
Dominic Farolino316e7932021-09-03 21:13:59870
871 // For the ShadowDOM version of fenced frames, we can just use a
872 // `TestFrameNavigationObserver` as normal directly on the frame that is
873 // navigating.
874 if (GetParam() ==
875 blink::features::FencedFramesImplementationType::kShadowDOM) {
876 TestFrameNavigationObserver observer(rfh);
877 EXPECT_EQ(url.spec(), EvalJs(rfh->GetParent(), navigate_script));
878 observer.Wait();
Nan Linc4aa6f82021-10-14 19:15:24879 EXPECT_EQ(observer.last_net_error_code(), expected_net_error_code);
Dominic Farolino316e7932021-09-03 21:13:59880 return;
881 }
882
883 // For the MPArch version of fenced frames, `TestFrameNavigationObserver`
884 // does not fully work inside of a fenced frame FrameTree. `WaitForCommit()`
885 // works, but `Wait()` always times out because it expects to hear the
886 // DidFinishedLoad event from the outer WebContents, which is not
887 // communicated by nested FrameTrees.
888 FencedFrame* fenced_frame = GetMatchingFencedFrameInOuterFrameTree(rfh);
889 EXPECT_EQ(url.spec(),
Dave Tapuska5dd5f9822021-09-10 20:57:15890 EvalJs(rfh->GetParentOrOuterDocument(), navigate_script));
Dominic Farolino316e7932021-09-03 21:13:59891 fenced_frame->WaitForDidStopLoadingForTesting();
Nan Linc4aa6f82021-10-14 19:15:24892 // `rfh` might be destroyed and invalid at this point.
893 EXPECT_EQ(frame_tree_node->current_frame_host()->IsErrorDocument(),
894 expected_net_error_code != net::OK);
shivanigithub93878d02021-06-15 11:37:53895 }
896
shivanigithubf33e469c2021-08-02 13:52:20897 void SetUpOnMainThread() override {
898 // Set up the host resolver to allow serving separate sites, so we can
899 // perform cross-process navigation.
900 host_resolver()->AddRule("*", "127.0.0.1");
901
902 // Fenced frames require potentially trustworthy URLs so creating an https
903 // server.
904 https_server_.RegisterRequestMonitor(
905 base::BindRepeating(&FencedFrameTreeBrowserTest::ObserveRequestHeaders,
906 base::Unretained(this)));
907 https_server_.ServeFilesFromSourceDirectory(GetTestDataFilePath());
908 https_server_.SetSSLConfig(net::EmbeddedTestServer::CERT_TEST_NAMES);
909 ASSERT_TRUE(https_server_.Start());
910 }
911
912 // Invoked on "EmbeddedTestServer IO Thread".
913 void ObserveRequestHeaders(const net::test_server::HttpRequest& request) {
914 base::AutoLock auto_lock(requests_lock_);
915 std::string val = request.headers.find("Cookie") != request.headers.end()
916 ? request.headers.at("Cookie").c_str()
917 : "";
918 cookie_headers_map_.insert(std::make_pair(request.GetURL().path(), val));
Nan Lind91c8152021-10-21 16:22:37919
920 val = request.headers.find("Sec-Fetch-Dest") != request.headers.end()
921 ? request.headers.at("Sec-Fetch-Dest").c_str()
922 : "";
923 sec_fetch_dest_headers_map_.insert(
924 std::make_pair(request.GetURL().path(), val));
shivanigithubf33e469c2021-08-02 13:52:20925 }
926
927 // Returns true if the cookie header was present in the last request received
928 // by the server with the same `url.path()`. Also asserts that the cookie
929 // header value matches that given in `expected_value`, if it exists. Also
930 // clears the value that was just checked by the method invocation.
931 bool CheckAndClearCookieHeader(
932 const GURL& url,
Nan Lind91c8152021-10-21 16:22:37933 const std::string& expected_value = "",
934 const base::Location& from_here = base::Location::Current()) {
shivanigithubf33e469c2021-08-02 13:52:20935 base::AutoLock auto_lock(requests_lock_);
936 SCOPED_TRACE(from_here.ToString());
937 DCHECK_CURRENTLY_ON(BrowserThread::UI);
938 std::string file_name = url.path();
939 CHECK(cookie_headers_map_.find(file_name) != cookie_headers_map_.end());
940 std::string header = cookie_headers_map_[file_name];
941 EXPECT_EQ(expected_value, header);
942 cookie_headers_map_.erase(file_name);
943 return !header.empty();
944 }
945
Nan Lind91c8152021-10-21 16:22:37946 bool CheckAndClearSecFetchDestHeader(
947 const GURL& url,
948 const std::string& expected_value = "",
949 const base::Location& from_here = base::Location::Current()) {
950 base::AutoLock auto_lock(requests_lock_);
951 SCOPED_TRACE(from_here.ToString());
952 DCHECK_CURRENTLY_ON(BrowserThread::UI);
953 std::string file_name = url.path();
954 CHECK(sec_fetch_dest_headers_map_.find(file_name) !=
955 sec_fetch_dest_headers_map_.end());
956 std::string header = sec_fetch_dest_headers_map_[file_name];
957 EXPECT_EQ(expected_value, header);
958 sec_fetch_dest_headers_map_.erase(file_name);
959 return !header.empty();
960 }
961
shivanigithub93878d02021-06-15 11:37:53962 net::EmbeddedTestServer* https_server() { return &https_server_; }
963
shivanigithubf33e469c2021-08-02 13:52:20964 ~FencedFrameTreeBrowserTest() override {
965 // Shutdown the server explicitly so that there is no race with the
966 // destruction of cookie_headers_map_ and invocation of RequestMonitor.
967 EXPECT_TRUE(https_server_.ShutdownAndWaitUntilComplete());
968 }
969
shivanigithub93878d02021-06-15 11:37:53970 private:
Dominic Farolino316e7932021-09-03 21:13:59971 // This method takes in a RenderFrameHostImpl that must be inside a fenced
972 // frame FrameTree, and returns the FencedFrame* object that represents this
973 // inner FrameTree from the outer FrameTree.
974 FencedFrame* GetMatchingFencedFrameInOuterFrameTree(
975 RenderFrameHostImpl* rfh) {
976 EXPECT_EQ(GetParam(),
977 blink::features::FencedFramesImplementationType::kMPArch);
978 // `rfh` doesn't always have to be a root frame, since this needs to work
979 // for arbitrary frames within a fenced frame.
980 EXPECT_TRUE(rfh->frame_tree_node()->IsInFencedFrameTree());
981
982 RenderFrameHostImpl* outer_delegate_frame =
Dave Tapuska5dd5f9822021-09-10 20:57:15983 rfh->GetMainFrame()->GetParentOrOuterDocument();
Dominic Farolino316e7932021-09-03 21:13:59984
985 std::vector<FencedFrame*> fenced_frames =
986 outer_delegate_frame->GetFencedFrames();
987 EXPECT_FALSE(fenced_frames.empty());
988
989 for (FencedFrame* fenced_frame : fenced_frames) {
990 if (fenced_frame->GetInnerRoot() == rfh->GetMainFrame()) {
991 return fenced_frame;
992 }
993 }
994
995 NOTREACHED();
996 return nullptr;
997 }
998
shivanigithub93878d02021-06-15 11:37:53999 base::test::ScopedFeatureList scoped_feature_list_;
shivanigithubf33e469c2021-08-02 13:52:201000 base::Lock requests_lock_;
1001 std::map<std::string, std::string> cookie_headers_map_
1002 GUARDED_BY(requests_lock_);
Nan Lind91c8152021-10-21 16:22:371003 std::map<std::string, std::string> sec_fetch_dest_headers_map_
1004 GUARDED_BY(requests_lock_);
shivanigithub93878d02021-06-15 11:37:531005 net::EmbeddedTestServer https_server_;
1006};
1007
1008// Tests that the fenced frame gets navigated to an actual url given a urn:uuid.
Dominic Farolino316e7932021-09-03 21:13:591009IN_PROC_BROWSER_TEST_P(FencedFrameTreeBrowserTest,
shivanigithub93878d02021-06-15 11:37:531010 CheckFencedFrameNavigationWithUUID) {
shivanigithubf33e469c2021-08-02 13:52:201011 GURL main_url = https_server()->GetURL("b.test", "/hello.html");
shivanigithub93878d02021-06-15 11:37:531012 EXPECT_TRUE(NavigateToURL(shell(), main_url));
shivanigithub93878d02021-06-15 11:37:531013 // It is safe to obtain the root frame tree node here, as it doesn't change.
1014 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:551015 ->GetPrimaryFrameTree()
1016 .root();
shivanigithub93878d02021-06-15 11:37:531017
1018 {
1019 EXPECT_TRUE(ExecJs(root,
1020 "var f = document.createElement('fencedframe');"
1021 "document.body.appendChild(f);"));
1022 }
1023 EXPECT_EQ(1U, root->child_count());
Dominic Farolino316e7932021-09-03 21:13:591024 FrameTreeNode* fenced_frame_root_node =
1025 GetFencedFrameRootNode(root->child_at(0));
shivanigithub93878d02021-06-15 11:37:531026
Dominic Farolino316e7932021-09-03 21:13:591027 EXPECT_TRUE(fenced_frame_root_node->IsFencedFrameRoot());
1028 EXPECT_TRUE(fenced_frame_root_node->IsInFencedFrameTree());
shivanigithub93878d02021-06-15 11:37:531029
Nan Linc4aa6f82021-10-14 19:15:241030 GURL https_url(
1031 https_server()->GetURL("a.test", "/fenced_frames/title1.html"));
shivanigithub93878d02021-06-15 11:37:531032 FencedFrameURLMapping& url_mapping =
1033 root->current_frame_host()->GetPage().fenced_frame_urls_map();
1034 GURL urn_uuid = url_mapping.AddFencedFrameURL(https_url);
1035 EXPECT_TRUE(urn_uuid.is_valid());
1036
1037 std::string navigate_urn_script = JsReplace("f.src = $1;", urn_uuid.spec());
1038
1039 {
Dominic Farolino316e7932021-09-03 21:13:591040 TestFrameNavigationObserver observer(fenced_frame_root_node);
shivanigithub93878d02021-06-15 11:37:531041 EXPECT_EQ(urn_uuid.spec(), EvalJs(root, navigate_urn_script));
Dominic Farolino316e7932021-09-03 21:13:591042 observer.WaitForCommit();
shivanigithub93878d02021-06-15 11:37:531043 }
1044
Dominic Farolino316e7932021-09-03 21:13:591045 EXPECT_EQ(
1046 https_url,
1047 fenced_frame_root_node->current_frame_host()->GetLastCommittedURL());
1048 EXPECT_EQ(
1049 url::Origin::Create(https_url),
1050 fenced_frame_root_node->current_frame_host()->GetLastCommittedOrigin());
shivanigithub93878d02021-06-15 11:37:531051
1052 // Parent will still see the src as the urn_uuid and not the mapped url.
1053 EXPECT_EQ(urn_uuid.spec(), EvalJs(root, "f.src"));
1054
1055 // The parent will not be able to access window.frames[0] as fenced frames are
1056 // not visible via frames[].
1057 EXPECT_FALSE(ExecJs(root, "window.frames[0].location"));
1058 EXPECT_EQ(0, EvalJs(root, "window.frames.length"));
1059}
1060
Dominic Farolino316e7932021-09-03 21:13:591061IN_PROC_BROWSER_TEST_P(FencedFrameTreeBrowserTest, CheckFencedFrameNoCookies) {
shivanigithubf33e469c2021-08-02 13:52:201062 // Create an a.test main page and set cookies. Then create a same-origin
1063 // fenced frame. Its request should not carry the cookies that were set.
1064 GURL main_url = https_server()->GetURL("a.test", "/hello.html");
1065 EXPECT_TRUE(NavigateToURL(shell(), main_url));
1066
1067 // It is safe to obtain the root frame tree node here, as it doesn't change.
1068 RenderFrameHostImpl* root_rfh =
1069 static_cast<WebContentsImpl*>(shell()->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:551070 ->GetPrimaryFrameTree()
1071 .root()
shivanigithubf33e469c2021-08-02 13:52:201072 ->current_frame_host();
1073
1074 // Set SameSite=Lax and SameSite=None cookies and retrieve them.
1075 EXPECT_TRUE(ExecJs(root_rfh,
1076 "document.cookie = 'B=2; SameSite=Lax';"
1077 "document.cookie = 'C=2; SameSite=None; Secure';"));
1078 EXPECT_EQ("B=2; C=2", EvalJs(root_rfh, "document.cookie;"));
1079
1080 // Test the fenced frame.
1081 EXPECT_TRUE(ExecJs(root_rfh,
1082 "var f = document.createElement('fencedframe');"
1083 "document.body.appendChild(f);"));
1084 EXPECT_EQ(1U, root_rfh->child_count());
1085
Dominic Farolino316e7932021-09-03 21:13:591086 FrameTreeNode* fenced_frame_root_node =
1087 GetFencedFrameRootNode(root_rfh->child_at(0));
shivanigithubf33e469c2021-08-02 13:52:201088
Dominic Farolino316e7932021-09-03 21:13:591089 EXPECT_TRUE(fenced_frame_root_node->IsFencedFrameRoot());
1090 EXPECT_TRUE(fenced_frame_root_node->IsInFencedFrameTree());
shivanigithubf33e469c2021-08-02 13:52:201091
Nan Linc4aa6f82021-10-14 19:15:241092 GURL https_url(
1093 https_server()->GetURL("a.test", "/fenced_frames/title1.html"));
shivanigithubf33e469c2021-08-02 13:52:201094 FencedFrameURLMapping& url_mapping =
1095 root_rfh->GetPage().fenced_frame_urls_map();
1096 GURL urn_uuid = url_mapping.AddFencedFrameURL(https_url);
1097 EXPECT_TRUE(urn_uuid.is_valid());
1098
1099 std::string navigate_urn_script = JsReplace("f.src = $1;", urn_uuid.spec());
Dominic Farolino316e7932021-09-03 21:13:591100 NavigateFrameInsideFencedFrameTreeAndWaitForFinishedLoad(
1101 fenced_frame_root_node, urn_uuid, navigate_urn_script);
1102 EXPECT_EQ(
1103 https_url,
1104 fenced_frame_root_node->current_frame_host()->GetLastCommittedURL());
1105 EXPECT_EQ(
1106 url::Origin::Create(https_url),
1107 fenced_frame_root_node->current_frame_host()->GetLastCommittedOrigin());
shivanigithubf33e469c2021-08-02 13:52:201108
1109 EXPECT_FALSE(CheckAndClearCookieHeader(https_url));
1110
1111 // Run the same test for an iframe inside the fenced frame. It shouldn't be
1112 // able to send cookies either.
1113 // Add a nested iframe inside the fenced frame.
Dominic Farolino316e7932021-09-03 21:13:591114 EXPECT_EQ(0U, fenced_frame_root_node->child_count());
1115 EXPECT_TRUE(ExecJs(fenced_frame_root_node,
shivanigithubf33e469c2021-08-02 13:52:201116 "var f1 = document.createElement('iframe');"
1117 "document.body.appendChild(f1);"));
Dominic Farolino316e7932021-09-03 21:13:591118 EXPECT_EQ(1U, fenced_frame_root_node->child_count());
1119 EXPECT_FALSE(fenced_frame_root_node->child_at(0)->IsFencedFrameRoot());
1120 EXPECT_TRUE(fenced_frame_root_node->child_at(0)->IsInFencedFrameTree());
shivanigithubf33e469c2021-08-02 13:52:201121 std::string navigate_script = JsReplace("f1.src = $1;", main_url.spec());
1122
Dominic Farolino316e7932021-09-03 21:13:591123 NavigateFrameInsideFencedFrameTreeAndWaitForFinishedLoad(
1124 fenced_frame_root_node->child_at(0), main_url, navigate_script);
1125
1126 EXPECT_EQ(main_url, fenced_frame_root_node->child_at(0)
1127 ->current_frame_host()
1128 ->GetLastCommittedURL());
1129 EXPECT_EQ(url::Origin::Create(main_url), fenced_frame_root_node->child_at(0)
shivanigithubf33e469c2021-08-02 13:52:201130 ->current_frame_host()
1131 ->GetLastCommittedOrigin());
1132 EXPECT_FALSE(CheckAndClearCookieHeader(main_url));
1133
1134 // Check that a subresource request from the main document should have the
1135 // cookies since that is outside the fenced frame tree.
1136 ResourceLoadObserver observer(shell());
1137 GURL image_url = https_server()->GetURL("a.test", "/image.jpg");
1138 EXPECT_TRUE(
1139 ExecJs(root_rfh, JsReplace("var img = document.createElement('img');"
1140 "document.body.appendChild(img);",
1141 image_url)));
1142 std::string load_script = JsReplace("img.src = $1;", image_url.spec());
1143 EXPECT_EQ(image_url.spec(), EvalJs(root_rfh, load_script));
1144 observer.WaitForResourceCompletion(image_url);
1145 EXPECT_TRUE(CheckAndClearCookieHeader(image_url, "B=2; C=2"));
1146}
1147
shivanigithubf3ddff52021-07-03 22:06:301148// Tests when a frame is considered a fenced frame or being inside a fenced
1149// frame tree.
Dominic Farolino316e7932021-09-03 21:13:591150IN_PROC_BROWSER_TEST_P(FencedFrameTreeBrowserTest, CheckIsFencedFrame) {
shivanigithubf33e469c2021-08-02 13:52:201151 GURL main_url(https_server()->GetURL("a.test", "/hello.html"));
shivanigithubf3ddff52021-07-03 22:06:301152 EXPECT_TRUE(NavigateToURL(shell(), main_url));
1153
1154 // It is safe to obtain the root frame tree node here, as it doesn't change.
1155 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:551156 ->GetPrimaryFrameTree()
1157 .root();
shivanigithubf3ddff52021-07-03 22:06:301158
1159 EXPECT_TRUE(ExecJs(root,
Dominic Farolino316e7932021-09-03 21:13:591160 "var fenced_frame = document.createElement('fencedframe');"
1161 "document.body.appendChild(fenced_frame);"));
1162
shivanigithubf3ddff52021-07-03 22:06:301163 EXPECT_EQ(1U, root->child_count());
Dominic Farolino316e7932021-09-03 21:13:591164
1165 FrameTreeNode* fenced_frame_root_node =
1166 GetFencedFrameRootNode(root->child_at(0));
1167 EXPECT_TRUE(fenced_frame_root_node->IsFencedFrameRoot());
1168 EXPECT_TRUE(fenced_frame_root_node->IsInFencedFrameTree());
shivanigithubf3ddff52021-07-03 22:06:301169
1170 // Add an iframe.
1171 EXPECT_TRUE(ExecJs(root,
Dominic Farolino316e7932021-09-03 21:13:591172 "var iframe = document.createElement('iframe');"
1173 "document.body.appendChild(iframe);"));
shivanigithubf3ddff52021-07-03 22:06:301174 EXPECT_EQ(2U, root->child_count());
Dominic Farolino4bc10ee2021-08-31 00:37:361175 EXPECT_FALSE(root->child_at(1)->IsFencedFrameRoot());
shivanigithubf3ddff52021-07-03 22:06:301176 EXPECT_FALSE(root->child_at(1)->IsInFencedFrameTree());
1177
1178 // Add a nested iframe inside the fenced frame.
Dominic Farolino316e7932021-09-03 21:13:591179 // Before we execute script on the fenced frame, we must navigate it once.
1180 // This is because the root of a FrameTree does not call
1181 // RenderFrameHostImpl::RenderFrameCreated() on its owned RFHI, meaning there
1182 // is nothing to execute script in.
1183 {
1184 // Navigate the fenced frame.
Nan Linc4aa6f82021-10-14 19:15:241185 GURL fenced_frame_url(
1186 https_server()->GetURL("a.test", "/fenced_frames/title1.html"));
Dominic Farolino316e7932021-09-03 21:13:591187 std::string navigate_script =
1188 JsReplace("fenced_frame.src = $1;", fenced_frame_url.spec());
1189 NavigateFrameInsideFencedFrameTreeAndWaitForFinishedLoad(
1190 fenced_frame_root_node, fenced_frame_url, navigate_script);
1191 }
1192
1193 EXPECT_TRUE(ExecJs(fenced_frame_root_node,
1194 "var nested_iframe = "
1195 "document.createElement('iframe');document.body."
1196 "appendChild(nested_iframe);"));
1197 EXPECT_EQ(1U, fenced_frame_root_node->child_count());
1198 EXPECT_FALSE(fenced_frame_root_node->child_at(0)->IsFencedFrameRoot());
1199 EXPECT_TRUE(fenced_frame_root_node->child_at(0)->IsInFencedFrameTree());
shivanigithubf3ddff52021-07-03 22:06:301200
1201 // Add a nested fenced frame.
Dominic Farolino316e7932021-09-03 21:13:591202 EXPECT_TRUE(ExecJs(fenced_frame_root_node,
1203 "var nested_fenced_frame = "
1204 "document.createElement('fencedframe');document.body."
1205 "appendChild(nested_fenced_frame);"));
1206 EXPECT_EQ(2U, fenced_frame_root_node->child_count());
1207
1208 FrameTreeNode* nested_fenced_frame_root_node =
1209 GetFencedFrameRootNode(fenced_frame_root_node->child_at(1));
1210 EXPECT_TRUE(nested_fenced_frame_root_node->IsFencedFrameRoot());
1211 EXPECT_TRUE(nested_fenced_frame_root_node->IsInFencedFrameTree());
shivanigithubf3ddff52021-07-03 22:06:301212}
1213
shivanigithub4cd016a2021-09-20 21:10:301214// Tests a nonce is correctly set in the isolation info for a fenced frame tree.
shivanigithubf3e92da2021-10-07 15:37:411215IN_PROC_BROWSER_TEST_P(FencedFrameTreeBrowserTest,
1216 CheckIsolationInfoAndStorageKeyNonce) {
shivanigithub4cd016a2021-09-20 21:10:301217 GURL main_url(https_server()->GetURL("a.test", "/hello.html"));
1218 EXPECT_TRUE(NavigateToURL(shell(), main_url));
1219
1220 // It is safe to obtain the root frame tree node here, as it doesn't change.
1221 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:551222 ->GetPrimaryFrameTree()
1223 .root();
shivanigithub4cd016a2021-09-20 21:10:301224
1225 EXPECT_TRUE(ExecJs(root,
1226 "var f = document.createElement('fencedframe');"
1227 "document.body.appendChild(f);"));
1228 EXPECT_EQ(1U, root->child_count());
1229
1230 auto* fenced_frame = GetFencedFrameRootNode(root->child_at(0));
1231 EXPECT_TRUE(fenced_frame->IsFencedFrameRoot());
1232 EXPECT_TRUE(fenced_frame->IsInFencedFrameTree());
1233
shivanigithubf3e92da2021-10-07 15:37:411234 // Before we check the IsolationInfo/StorageKey on the fenced frame, we must
1235 // navigate it once. This is because the root of a FrameTree does not call
shivanigithub4cd016a2021-09-20 21:10:301236 // RenderFrameHostImpl::RenderFrameCreated() on its owned RFHI.
1237 {
1238 // Navigate the fenced frame.
Nan Linc4aa6f82021-10-14 19:15:241239 GURL fenced_frame_url(
1240 https_server()->GetURL("a.test", "/fenced_frames/title1.html"));
shivanigithub4cd016a2021-09-20 21:10:301241 std::string navigate_script =
1242 JsReplace("f.src = $1;", fenced_frame_url.spec());
1243 NavigateFrameInsideFencedFrameTreeAndWaitForFinishedLoad(
1244 fenced_frame, fenced_frame_url, navigate_script);
1245 }
1246
1247 // There should be a nonce in the IsolationInfo.
1248 const net::IsolationInfo& isolation_info =
1249 fenced_frame->current_frame_host()->GetIsolationInfoForSubresources();
1250 EXPECT_TRUE(isolation_info.nonce().has_value());
1251 absl::optional<base::UnguessableToken> fenced_frame_nonce =
1252 fenced_frame->fenced_frame_nonce();
1253 EXPECT_TRUE(fenced_frame_nonce.has_value());
1254 EXPECT_EQ(fenced_frame_nonce.value(), isolation_info.nonce().value());
1255
shivanigithubf3e92da2021-10-07 15:37:411256 // There should be a nonce in the StorageKey.
1257 EXPECT_TRUE(
1258 fenced_frame->current_frame_host()->storage_key().nonce().has_value());
1259 EXPECT_EQ(fenced_frame_nonce.value(),
1260 fenced_frame->current_frame_host()->storage_key().nonce().value());
1261
1262 // Add an iframe. It should not have a nonce.
shivanigithub4cd016a2021-09-20 21:10:301263 EXPECT_TRUE(ExecJs(root,
1264 "var subframe = document.createElement('iframe');"
1265 "document.body.appendChild(subframe);"));
1266 EXPECT_EQ(2U, root->child_count());
1267 auto* iframe = root->child_at(1);
1268 EXPECT_FALSE(iframe->IsFencedFrameRoot());
1269 EXPECT_FALSE(iframe->IsInFencedFrameTree());
1270 const net::IsolationInfo& iframe_isolation_info =
1271 iframe->current_frame_host()->GetIsolationInfoForSubresources();
1272 EXPECT_FALSE(iframe_isolation_info.nonce().has_value());
shivanigithubf3e92da2021-10-07 15:37:411273 EXPECT_FALSE(iframe->current_frame_host()->storage_key().nonce().has_value());
shivanigithub4cd016a2021-09-20 21:10:301274
1275 // Navigate the iframe. It should still not have a nonce.
1276 EXPECT_TRUE(NavigateToURLFromRenderer(
1277 iframe, https_server()->GetURL("b.test", "/title1.html")));
1278 const net::IsolationInfo& iframe_new_isolation_info =
1279 iframe->current_frame_host()->GetIsolationInfoForSubresources();
shivanigithubf3e92da2021-10-07 15:37:411280
shivanigithub4cd016a2021-09-20 21:10:301281 EXPECT_FALSE(iframe_new_isolation_info.nonce().has_value());
shivanigithubf3e92da2021-10-07 15:37:411282 EXPECT_FALSE(iframe->current_frame_host()->storage_key().nonce().has_value());
shivanigithub4cd016a2021-09-20 21:10:301283
1284 // Add a nested iframe inside the fenced frame.
1285 EXPECT_TRUE(ExecJs(fenced_frame,
1286 "var iframe_within_ff = document.createElement('iframe');"
1287 "document.body.appendChild(iframe_within_ff);"));
1288 EXPECT_EQ(1U, fenced_frame->child_count());
1289 EXPECT_FALSE(fenced_frame->child_at(0)->IsFencedFrameRoot());
1290 EXPECT_TRUE(fenced_frame->child_at(0)->IsInFencedFrameTree());
1291 const net::IsolationInfo& nested_iframe_isolation_info =
1292 fenced_frame->child_at(0)
1293 ->current_frame_host()
1294 ->GetIsolationInfoForSubresources();
1295 EXPECT_TRUE(nested_iframe_isolation_info.nonce().has_value());
1296
1297 // Check that a nested iframe in the fenced frame tree has the same nonce
1298 // value as its parent.
1299 EXPECT_EQ(fenced_frame_nonce.value(),
1300 nested_iframe_isolation_info.nonce().value());
1301 absl::optional<base::UnguessableToken> nested_iframe_nonce =
1302 fenced_frame->child_at(0)->fenced_frame_nonce();
1303 EXPECT_EQ(nested_iframe_isolation_info.nonce().value(),
1304 nested_iframe_nonce.value());
shivanigithubf3e92da2021-10-07 15:37:411305 EXPECT_EQ(fenced_frame_nonce.value(), fenced_frame->child_at(0)
1306 ->current_frame_host()
1307 ->storage_key()
1308 .nonce()
1309 .value());
shivanigithub4cd016a2021-09-20 21:10:301310
1311 // Navigate the iframe. It should still have the same nonce.
1312 {
shivanigithubf3e92da2021-10-07 15:37:411313 GURL https_url(https_server()->GetURL("b.test", "/title1.html"));
shivanigithub4cd016a2021-09-20 21:10:301314 std::string navigate_script =
1315 JsReplace("iframe_within_ff.src = $1;", https_url.spec());
1316 NavigateFrameInsideFencedFrameTreeAndWaitForFinishedLoad(
1317 fenced_frame->child_at(0), https_url, navigate_script);
1318 }
1319 const net::IsolationInfo& nested_iframe_new_isolation_info =
1320 fenced_frame->child_at(0)
1321 ->current_frame_host()
1322 ->GetIsolationInfoForSubresources();
1323 EXPECT_EQ(nested_iframe_new_isolation_info.nonce().value(),
1324 nested_iframe_nonce.value());
shivanigithubf3e92da2021-10-07 15:37:411325 EXPECT_EQ(fenced_frame_nonce.value(), fenced_frame->child_at(0)
1326 ->current_frame_host()
1327 ->storage_key()
1328 .nonce()
1329 .value());
shivanigithub4cd016a2021-09-20 21:10:301330
1331 // Add a nested fenced frame.
1332 EXPECT_TRUE(
1333 ExecJs(fenced_frame,
1334 "var nested_fenced_frame = document.createElement('fencedframe');"
1335 "document.body.appendChild(nested_fenced_frame);"));
1336 EXPECT_EQ(2U, fenced_frame->child_count());
1337 auto* nested_fenced_frame = GetFencedFrameRootNode(fenced_frame->child_at(1));
1338 EXPECT_TRUE(nested_fenced_frame->IsFencedFrameRoot());
1339 EXPECT_TRUE(nested_fenced_frame->IsInFencedFrameTree());
1340 absl::optional<base::UnguessableToken> nested_fframe_nonce =
1341 nested_fenced_frame->fenced_frame_nonce();
1342 EXPECT_TRUE(nested_fframe_nonce.has_value());
1343
1344 // Check that a nested fenced frame has a different value than its parent
1345 // fenced frame.
1346 EXPECT_NE(fenced_frame_nonce.value(), nested_fframe_nonce.value());
1347
1348 // Check that the nonce does not change when there is a cross-document
1349 // navigation.
1350 {
1351 // Navigate the fenced frame.
Nan Linc4aa6f82021-10-14 19:15:241352 GURL fenced_frame_url(
1353 https_server()->GetURL("b.test", "/fenced_frames/title1.html"));
shivanigithub4cd016a2021-09-20 21:10:301354 std::string navigate_script =
1355 JsReplace("f.src = $1;", fenced_frame_url.spec());
1356 NavigateFrameInsideFencedFrameTreeAndWaitForFinishedLoad(
1357 fenced_frame, fenced_frame_url, navigate_script);
1358 }
1359
1360 absl::optional<base::UnguessableToken> new_fenced_frame_nonce =
1361 fenced_frame->fenced_frame_nonce();
1362 EXPECT_NE(absl::nullopt, new_fenced_frame_nonce);
1363 EXPECT_EQ(new_fenced_frame_nonce.value(), fenced_frame_nonce.value());
1364}
1365
shivanigithubf3e92da2021-10-07 15:37:411366// Tests that a fenced frame and a same-origin iframe at the same level do not
1367// share the same storage partition.
1368IN_PROC_BROWSER_TEST_P(FencedFrameTreeBrowserTest, CheckUniqueStorage) {
1369 GURL main_url(https_server()->GetURL("a.test", "/hello.html"));
1370 EXPECT_TRUE(NavigateToURL(shell(), main_url));
1371
1372 // It is safe to obtain the root frame tree node here, as it doesn't change.
1373 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:551374 ->GetPrimaryFrameTree()
1375 .root();
shivanigithubf3e92da2021-10-07 15:37:411376
1377 EXPECT_TRUE(ExecJs(root,
1378 "var f = document.createElement('fencedframe');"
1379 "document.body.appendChild(f);"));
1380 EXPECT_EQ(1U, root->child_count());
1381
1382 auto* fenced_frame = GetFencedFrameRootNode(root->child_at(0));
1383 EXPECT_TRUE(fenced_frame->IsFencedFrameRoot());
1384 EXPECT_TRUE(fenced_frame->IsInFencedFrameTree());
1385
1386 // Before we check the storage key on the fenced frame, we must navigate it
1387 // once. This is because the root of a FrameTree does not call
1388 // RenderFrameHostImpl::RenderFrameCreated() on its owned RFHI.
1389 {
1390 // Navigate the fenced frame.
Nan Linc4aa6f82021-10-14 19:15:241391 GURL fenced_frame_url(
1392 https_server()->GetURL("a.test", "/fenced_frames/title1.html"));
shivanigithubf3e92da2021-10-07 15:37:411393 std::string navigate_script =
1394 JsReplace("f.src = $1;", fenced_frame_url.spec());
1395 NavigateFrameInsideFencedFrameTreeAndWaitForFinishedLoad(
1396 fenced_frame, fenced_frame_url, navigate_script);
1397 }
1398
1399 // There should be a nonce in the StorageKey.
1400 EXPECT_TRUE(
1401 fenced_frame->current_frame_host()->storage_key().nonce().has_value());
1402
1403 absl::optional<base::UnguessableToken> fenced_frame_nonce =
1404 fenced_frame->fenced_frame_nonce();
1405 EXPECT_TRUE(fenced_frame_nonce.has_value());
1406 EXPECT_EQ(fenced_frame_nonce.value(),
1407 fenced_frame->current_frame_host()->storage_key().nonce().value());
1408
1409 // Add an iframe.
1410 EXPECT_TRUE(ExecJs(root,
1411 "var subframe = document.createElement('iframe');"
1412 "document.body.appendChild(subframe);"));
1413 EXPECT_EQ(2U, root->child_count());
1414 auto* iframe = root->child_at(1);
1415 EXPECT_FALSE(iframe->IsFencedFrameRoot());
1416 EXPECT_FALSE(iframe->IsInFencedFrameTree());
1417 EXPECT_FALSE(iframe->current_frame_host()->storage_key().nonce().has_value());
1418
1419 // Navigate the iframe. It should still not have a nonce.
1420 EXPECT_TRUE(NavigateToURLFromRenderer(
1421 iframe, https_server()->GetURL("a.test", "/title1.html")));
1422
1423 EXPECT_FALSE(iframe->current_frame_host()->storage_key().nonce().has_value());
1424
1425 // Set and read a value in the fenced frame's local storage.
1426 EXPECT_TRUE(ExecJs(fenced_frame, "localStorage[\"foo\"] = \"a\""));
1427 EXPECT_EQ("a", EvalJs(fenced_frame, "localStorage[\"foo\"]"));
1428
1429 // Set and read a value in the iframe's local storage.
1430 EXPECT_TRUE(ExecJs(iframe, "localStorage[\"foo\"] = \"b\""));
1431 EXPECT_EQ("b", EvalJs(iframe, "localStorage[\"foo\"]"));
1432
1433 // Set and read a value in the top-frame's local storage.
1434 EXPECT_TRUE(ExecJs(root, "localStorage[\"foo\"] = \"c\""));
1435 EXPECT_EQ("c", EvalJs(root, "localStorage[\"foo\"]"));
1436
1437 // TODO(crbug.com/1199077) This should return "a" once StorageKey starts
1438 // using the nonce for partitioning. Also remove the shadowDOM specific check
1439 // once nonce support is complete (for MPArch, possibly due to a separate
1440 // process and incomplete nonce support, it is returning "a" on some
1441 // platforms).
1442 if (GetParam() ==
1443 blink::features::FencedFramesImplementationType::kShadowDOM) {
1444 EXPECT_EQ("c", EvalJs(fenced_frame, "localStorage[\"foo\"]"));
1445 }
1446}
1447
Nan Linc4aa6f82021-10-14 19:15:241448IN_PROC_BROWSER_TEST_P(FencedFrameTreeBrowserTest,
1449 CheckFencedFrameNotNavigatedWithoutOptIn) {
1450 GURL main_url = https_server()->GetURL("b.test", "/hello.html");
1451 EXPECT_TRUE(NavigateToURL(shell(), main_url));
1452 // It is safe to obtain the root frame tree node here, as it doesn't change.
1453 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:551454 ->GetPrimaryFrameTree()
1455 .root();
Nan Linc4aa6f82021-10-14 19:15:241456
1457 {
1458 EXPECT_TRUE(ExecJs(root,
1459 "var f = document.createElement('fencedframe');"
1460 "document.body.appendChild(f);"));
1461 }
1462 EXPECT_EQ(1U, root->child_count());
1463 FrameTreeNode* fenced_frame_root_node =
1464 GetFencedFrameRootNode(root->child_at(0));
1465
1466 GURL https_url(https_server()->GetURL("a.test", "/title1.html"));
1467 FencedFrameURLMapping& url_mapping =
1468 root->current_frame_host()->GetPage().fenced_frame_urls_map();
1469 GURL urn_uuid = url_mapping.AddFencedFrameURL(https_url);
1470 EXPECT_TRUE(urn_uuid.is_valid());
1471
1472 std::string navigate_urn_script = JsReplace("f.src = $1;", urn_uuid.spec());
1473 NavigateFrameInsideFencedFrameTreeAndWaitForFinishedLoad(
1474 fenced_frame_root_node, urn_uuid, navigate_urn_script,
1475 net::ERR_BLOCKED_BY_RESPONSE);
1476}
1477
Nan Lind91c8152021-10-21 16:22:371478IN_PROC_BROWSER_TEST_P(FencedFrameTreeBrowserTest, CheckSecFetchDestHeader) {
1479 GURL main_url(https_server()->GetURL("a.test", "/hello.html"));
1480 EXPECT_TRUE(NavigateToURL(shell(), main_url));
1481 // It is safe to obtain the root frame tree node here, as it doesn't change.
1482 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
1483 ->GetFrameTree()
1484 ->root();
1485
1486 EXPECT_TRUE(ExecJs(root,
1487 "var fenced_frame = document.createElement('fencedframe');"
1488 "document.body.appendChild(fenced_frame);"));
1489
1490 EXPECT_EQ(1U, root->child_count());
1491
1492 FrameTreeNode* fenced_frame_root_node =
1493 GetFencedFrameRootNode(root->child_at(0));
1494
1495 {
1496 // Navigate the fenced frame.
1497 GURL fenced_frame_url(
1498 https_server()->GetURL("a.test", "/fenced_frames/title1.html"));
1499 std::string navigate_script =
1500 JsReplace("fenced_frame.src = $1;", fenced_frame_url.spec());
1501 NavigateFrameInsideFencedFrameTreeAndWaitForFinishedLoad(
1502 fenced_frame_root_node, fenced_frame_url, navigate_script);
1503 EXPECT_TRUE(
1504 CheckAndClearSecFetchDestHeader(fenced_frame_url, "fencedframe"));
1505 }
1506
1507 // Add a nested iframe inside the fenced frame.
1508 EXPECT_TRUE(ExecJs(fenced_frame_root_node,
1509 "var iframe = document.createElement('iframe');"
1510 "document.body.appendChild(iframe);"));
1511 EXPECT_EQ(1U, fenced_frame_root_node->child_count());
1512
1513 {
1514 // Navigate the iframe.
1515 GURL iframe_url(https_server()->GetURL("a.test", "/title2.html"));
1516 std::string navigate_script =
1517 JsReplace("iframe.src = $1;", iframe_url.spec());
1518 NavigateFrameInsideFencedFrameTreeAndWaitForFinishedLoad(
1519 fenced_frame_root_node->child_at(0), iframe_url, navigate_script);
1520 EXPECT_TRUE(CheckAndClearSecFetchDestHeader(iframe_url, "iframe"));
1521 }
1522}
1523
Dominic Farolino316e7932021-09-03 21:13:591524INSTANTIATE_TEST_SUITE_P(
1525 All,
1526 FencedFrameTreeBrowserTest,
1527 ::testing::Values(
1528 blink::features::FencedFramesImplementationType::kShadowDOM,
1529 blink::features::FencedFramesImplementationType::kMPArch),
1530 &FencedFrameTreeBrowserTest::DescribeParams);
1531
[email protected]82307f6b2014-08-07 03:30:121532class CrossProcessFrameTreeBrowserTest : public ContentBrowserTest {
1533 public:
Fergal Dalya1d569972021-03-16 03:24:531534 CrossProcessFrameTreeBrowserTest() = default;
[email protected]82307f6b2014-08-07 03:30:121535
dchengc2282aa2014-10-21 12:07:581536 void SetUpCommandLine(base::CommandLine* command_line) override {
nickd30fd962015-07-27 21:51:081537 IsolateAllSitesForTesting(command_line);
[email protected]82307f6b2014-08-07 03:30:121538 }
1539
alexmos478dcbb2014-12-10 21:24:461540 void SetUpOnMainThread() override {
1541 host_resolver()->AddRule("*", "127.0.0.1");
alexmos478dcbb2014-12-10 21:24:461542 SetupCrossSiteRedirector(embedded_test_server());
martijn12d2dad2016-11-11 10:59:271543 ASSERT_TRUE(embedded_test_server()->Start());
alexmos478dcbb2014-12-10 21:24:461544 }
1545
[email protected]82307f6b2014-08-07 03:30:121546 private:
1547 DISALLOW_COPY_AND_ASSIGN(CrossProcessFrameTreeBrowserTest);
1548};
1549
1550// Ensure that we can complete a cross-process subframe navigation.
[email protected]82307f6b2014-08-07 03:30:121551IN_PROC_BROWSER_TEST_F(CrossProcessFrameTreeBrowserTest,
nasko83fe5dc2015-07-01 16:34:191552 CreateCrossProcessSubframeProxies) {
alexmos478dcbb2014-12-10 21:24:461553 GURL main_url(embedded_test_server()->GetURL("/site_per_process_main.html"));
Alex Moshchukaa95adf52019-08-13 00:02:021554 EXPECT_TRUE(NavigateToURL(shell(), main_url));
[email protected]82307f6b2014-08-07 03:30:121555
1556 // It is safe to obtain the root frame tree node here, as it doesn't change.
1557 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:551558 ->GetPrimaryFrameTree()
1559 .root();
[email protected]82307f6b2014-08-07 03:30:121560
[email protected]14522ce42014-08-08 18:56:161561 // There should not be a proxy for the root's own SiteInstance.
1562 SiteInstance* root_instance = root->current_frame_host()->GetSiteInstance();
1563 EXPECT_FALSE(root->render_manager()->GetRenderFrameProxyHost(root_instance));
1564
[email protected]82307f6b2014-08-07 03:30:121565 // Load same-site page into iframe.
alexmos478dcbb2014-12-10 21:24:461566 GURL http_url(embedded_test_server()->GetURL("/title1.html"));
Lukasz Anforowicz69c25dfd2020-11-12 21:50:201567 EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(0), http_url));
[email protected]82307f6b2014-08-07 03:30:121568
[email protected]82307f6b2014-08-07 03:30:121569 // Load cross-site page into iframe.
alexmos478dcbb2014-12-10 21:24:461570 GURL cross_site_url(
1571 embedded_test_server()->GetURL("foo.com", "/title2.html"));
Lukasz Anforowicz69c25dfd2020-11-12 21:50:201572 EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(0), cross_site_url));
[email protected]82307f6b2014-08-07 03:30:121573
1574 // Ensure that we have created a new process for the subframe.
naskoe6edde32014-10-17 15:36:481575 ASSERT_EQ(2U, root->child_count());
[email protected]82307f6b2014-08-07 03:30:121576 FrameTreeNode* child = root->child_at(0);
[email protected]14522ce42014-08-08 18:56:161577 SiteInstance* child_instance = child->current_frame_host()->GetSiteInstance();
[email protected]82307f6b2014-08-07 03:30:121578 RenderViewHost* rvh = child->current_frame_host()->render_view_host();
1579 RenderProcessHost* rph = child->current_frame_host()->GetProcess();
1580
Lucas Furukawa Gadanib9e03af2020-10-27 20:30:371581 EXPECT_NE(shell()->web_contents()->GetMainFrame()->GetRenderViewHost(), rvh);
[email protected]14522ce42014-08-08 18:56:161582 EXPECT_NE(shell()->web_contents()->GetSiteInstance(), child_instance);
Lukasz Anforowicz9e0ce4e2017-09-28 19:09:151583 EXPECT_NE(shell()->web_contents()->GetMainFrame()->GetProcess(), rph);
[email protected]82307f6b2014-08-07 03:30:121584
1585 // Ensure that the root node has a proxy for the child node's SiteInstance.
[email protected]14522ce42014-08-08 18:56:161586 EXPECT_TRUE(root->render_manager()->GetRenderFrameProxyHost(child_instance));
[email protected]82307f6b2014-08-07 03:30:121587
1588 // Also ensure that the child has a proxy for the root node's SiteInstance.
[email protected]14522ce42014-08-08 18:56:161589 EXPECT_TRUE(child->render_manager()->GetRenderFrameProxyHost(root_instance));
1590
1591 // The nodes should not have proxies for their own SiteInstance.
1592 EXPECT_FALSE(root->render_manager()->GetRenderFrameProxyHost(root_instance));
1593 EXPECT_FALSE(
1594 child->render_manager()->GetRenderFrameProxyHost(child_instance));
creise42f2a52014-09-18 18:14:571595
1596 // Ensure that the RenderViews and RenderFrames are all live.
1597 EXPECT_TRUE(
1598 root->current_frame_host()->render_view_host()->IsRenderViewLive());
1599 EXPECT_TRUE(
1600 child->current_frame_host()->render_view_host()->IsRenderViewLive());
1601 EXPECT_TRUE(root->current_frame_host()->IsRenderFrameLive());
1602 EXPECT_TRUE(root->child_at(0)->current_frame_host()->IsRenderFrameLive());
[email protected]82307f6b2014-08-07 03:30:121603}
1604
alexmosbc7eafa2014-12-06 01:38:091605IN_PROC_BROWSER_TEST_F(CrossProcessFrameTreeBrowserTest,
Nasko Oskov039121012019-01-11 00:21:321606 OriginSetOnNavigations) {
alexmos478dcbb2014-12-10 21:24:461607 GURL main_url(embedded_test_server()->GetURL("/site_per_process_main.html"));
alexmosbc7eafa2014-12-06 01:38:091608 EXPECT_TRUE(NavigateToURL(shell(), main_url));
1609
1610 // It is safe to obtain the root frame tree node here, as it doesn't change.
1611 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:551612 ->GetPrimaryFrameTree()
1613 .root();
alexmosbc7eafa2014-12-06 01:38:091614
creisabdd2bd62015-11-21 01:14:581615 EXPECT_EQ(root->current_origin().Serialize() + '/',
Mike West800532c2021-10-14 09:26:521616 main_url.DeprecatedGetOriginAsURL().spec());
alexmosbc7eafa2014-12-06 01:38:091617
1618 // First frame is an about:blank frame. Check that its origin is correctly
1619 // inherited from the parent.
creisabdd2bd62015-11-21 01:14:581620 EXPECT_EQ(root->child_at(0)->current_origin().Serialize() + '/',
Mike West800532c2021-10-14 09:26:521621 main_url.DeprecatedGetOriginAsURL().spec());
alexmosbc7eafa2014-12-06 01:38:091622
1623 // Second frame loads a same-site page. Its origin should also be the same
1624 // as the parent.
creisabdd2bd62015-11-21 01:14:581625 EXPECT_EQ(root->child_at(1)->current_origin().Serialize() + '/',
Mike West800532c2021-10-14 09:26:521626 main_url.DeprecatedGetOriginAsURL().spec());
alexmosbc7eafa2014-12-06 01:38:091627
alexmosbc7eafa2014-12-06 01:38:091628 // Load cross-site page into the first frame.
alexmos478dcbb2014-12-10 21:24:461629 GURL cross_site_url(
1630 embedded_test_server()->GetURL("foo.com", "/title2.html"));
Lukasz Anforowicz69c25dfd2020-11-12 21:50:201631 EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(0), cross_site_url));
alexmosbc7eafa2014-12-06 01:38:091632
creisabdd2bd62015-11-21 01:14:581633 EXPECT_EQ(root->child_at(0)->current_origin().Serialize() + '/',
Mike West800532c2021-10-14 09:26:521634 cross_site_url.DeprecatedGetOriginAsURL().spec());
alexmosbc7eafa2014-12-06 01:38:091635
1636 // The root's origin shouldn't have changed.
creisabdd2bd62015-11-21 01:14:581637 EXPECT_EQ(root->current_origin().Serialize() + '/',
Mike West800532c2021-10-14 09:26:521638 main_url.DeprecatedGetOriginAsURL().spec());
alexmosbc7eafa2014-12-06 01:38:091639
Nasko Oskov039121012019-01-11 00:21:321640 {
1641 GURL data_url("data:text/html,foo");
1642 TestNavigationObserver observer(shell()->web_contents());
1643 EXPECT_TRUE(
1644 ExecJs(root->child_at(1), JsReplace("window.location = $1", data_url)));
1645 observer.Wait();
1646 }
alexmosbc7eafa2014-12-06 01:38:091647
1648 // Navigating to a data URL should set a unique origin. This is represented
Nasko Oskov039121012019-01-11 00:21:321649 // as "null" per RFC 6454. A frame navigating itself to a data: URL does not
1650 // require a process transfer, but should retain the original origin
1651 // as its precursor.
creisabdd2bd62015-11-21 01:14:581652 EXPECT_EQ(root->child_at(1)->current_origin().Serialize(), "null");
Nasko Oskov039121012019-01-11 00:21:321653 EXPECT_TRUE(root->child_at(1)->current_origin().opaque());
1654 ASSERT_EQ(
1655 url::SchemeHostPort(main_url),
1656 root->child_at(1)->current_origin().GetTupleOrPrecursorTupleIfOpaque())
1657 << "Expected the precursor origin to be preserved; should be the "
1658 "initiator of a data: navigation.";
1659
1660 // Adding an <iframe sandbox srcdoc=> frame should result in a unique origin
1661 // that is different-origin from its data: URL parent.
1662 {
1663 TestNavigationObserver observer(shell()->web_contents());
1664
1665 ASSERT_EQ(0U, root->child_at(1)->child_count());
1666 EXPECT_TRUE(
1667 ExecJs(root->child_at(1), JsReplace(
1668 R"(
1669 var iframe = document.createElement('iframe');
1670 iframe.setAttribute('sandbox', 'allow-scripts');
1671 iframe.srcdoc = $1;
1672 document.body.appendChild(iframe);
1673 )",
1674 "<html><body>This sandboxed doc should "
1675 "be different-origin.</body></html>")));
1676 observer.Wait();
1677 ASSERT_EQ(1U, root->child_at(1)->child_count());
1678 }
1679
1680 url::Origin root_origin = root->current_origin();
1681 url::Origin child_1 = root->child_at(1)->current_origin();
1682 url::Origin child_1_0 = root->child_at(1)->child_at(0)->current_origin();
1683 EXPECT_FALSE(root_origin.opaque());
1684 EXPECT_TRUE(child_1.opaque());
1685 EXPECT_TRUE(child_1_0.opaque());
1686 EXPECT_NE(child_1, child_1_0);
1687 EXPECT_EQ(url::SchemeHostPort(main_url),
1688 root_origin.GetTupleOrPrecursorTupleIfOpaque());
1689 EXPECT_EQ(url::SchemeHostPort(main_url),
1690 child_1.GetTupleOrPrecursorTupleIfOpaque());
1691 EXPECT_EQ(url::SchemeHostPort(main_url),
1692 child_1_0.GetTupleOrPrecursorTupleIfOpaque());
1693
1694 {
1695 TestNavigationObserver observer(shell()->web_contents());
1696
1697 ASSERT_EQ(1U, root->child_at(1)->child_count());
1698 EXPECT_TRUE(
1699 ExecJs(root->child_at(1), JsReplace(
1700 R"(
1701 var iframe = document.createElement('iframe');
1702 iframe.srcdoc = $1;
1703 document.body.appendChild(iframe);
1704 )",
1705 "<html><body>This srcdoc document should "
1706 "be same-origin.</body></html>")));
1707 observer.Wait();
1708 ASSERT_EQ(2U, root->child_at(1)->child_count());
1709 }
1710 EXPECT_EQ(root_origin, root->current_origin());
1711 EXPECT_EQ(child_1, root->child_at(1)->current_origin());
1712 EXPECT_EQ(child_1_0, root->child_at(1)->child_at(0)->current_origin());
1713 url::Origin child_1_1 = root->child_at(1)->child_at(1)->current_origin();
1714 EXPECT_EQ(child_1, child_1_1);
1715 EXPECT_NE(child_1_0, child_1_1);
1716
1717 {
1718 TestNavigationObserver observer(shell()->web_contents());
1719
1720 ASSERT_EQ(2U, root->child_at(1)->child_count());
1721 EXPECT_TRUE(
1722 ExecJs(root->child_at(1), JsReplace(
1723 R"(
1724 var iframe = document.createElement('iframe');
1725 iframe.src = 'data:text/html;base64,' + btoa($1);
1726 document.body.appendChild(iframe);
1727 )",
1728 "<html><body>This data: doc should be "
1729 "different-origin.</body></html>")));
1730 observer.Wait();
1731 ASSERT_EQ(3U, root->child_at(1)->child_count());
1732 }
1733 EXPECT_EQ(root_origin, root->current_origin());
1734 EXPECT_EQ(child_1, root->child_at(1)->current_origin());
1735 EXPECT_EQ(child_1_0, root->child_at(1)->child_at(0)->current_origin());
1736 EXPECT_EQ(child_1_1, root->child_at(1)->child_at(1)->current_origin());
1737 url::Origin child_1_2 = root->child_at(1)->child_at(2)->current_origin();
1738 EXPECT_NE(child_1, child_1_2);
1739 EXPECT_NE(child_1_0, child_1_2);
1740 EXPECT_NE(child_1_1, child_1_2);
1741 EXPECT_EQ(url::SchemeHostPort(main_url),
1742 child_1_2.GetTupleOrPrecursorTupleIfOpaque());
1743
1744 // If the parent navigates its child to a data URL, it should transfer
1745 // to the parent's process, and the precursor origin should track the
1746 // parent's origin.
1747 {
1748 GURL data_url("data:text/html,foo2");
1749 TestNavigationObserver observer(shell()->web_contents());
1750 EXPECT_TRUE(ExecJs(root, JsReplace("frames[0].location = $1", data_url)));
1751 observer.Wait();
1752 EXPECT_EQ(data_url, root->child_at(0)->current_url());
1753 }
1754
1755 EXPECT_EQ(root->child_at(0)->current_origin().Serialize(), "null");
1756 EXPECT_TRUE(root->child_at(0)->current_origin().opaque());
1757 EXPECT_EQ(
1758 url::SchemeHostPort(main_url),
1759 root->child_at(0)->current_origin().GetTupleOrPrecursorTupleIfOpaque());
1760 EXPECT_EQ(root->current_frame_host()->GetProcess(),
1761 root->child_at(0)->current_frame_host()->GetProcess());
1762}
1763
1764// Test to verify that a blob: URL that is created by a unique opaque origin
1765// will correctly set the origin_to_commit on a session history navigation.
1766IN_PROC_BROWSER_TEST_F(CrossProcessFrameTreeBrowserTest,
1767 OriginForBlobUrlsFromUniqueOpaqueOrigin) {
1768 // Start off with a navigation to data: URL in the main frame. It should
1769 // result in a unique opaque origin without any precursor information.
1770 GURL data_url("data:text/html,foo<iframe id='child' src='" +
1771 embedded_test_server()->GetURL("/title1.html").spec() +
1772 "'></iframe>");
1773 EXPECT_TRUE(NavigateToURL(shell(), data_url));
1774
1775 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:551776 ->GetPrimaryFrameTree()
1777 .root();
Nasko Oskov039121012019-01-11 00:21:321778 EXPECT_TRUE(root->current_origin().opaque());
Nasko Oskov55119382020-01-17 18:22:181779 EXPECT_FALSE(
1780 root->current_origin().GetTupleOrPrecursorTupleIfOpaque().IsValid());
Nasko Oskov039121012019-01-11 00:21:321781 EXPECT_EQ(1UL, root->child_count());
1782 FrameTreeNode* child = root->child_at(0);
1783
1784 // Create a blob: URL and navigate the child frame to it.
1785 std::string html = "<html><body>This is blob content.</body></html>";
1786 std::string script = JsReplace(
1787 "var blob = new Blob([$1], {type: 'text/html'});"
1788 "var blob_url = URL.createObjectURL(blob);"
1789 "document.getElementById('child').src = blob_url;"
1790 "blob_url;",
1791 html);
1792 GURL blob_url;
1793 {
1794 TestFrameNavigationObserver observer(child);
1795 blob_url = GURL(EvalJs(root, script).ExtractString());
1796 observer.Wait();
1797 EXPECT_EQ(blob_url, child->current_frame_host()->GetLastCommittedURL());
1798 }
1799
1800 // We expect the frame to have committed in an opaque origin which contains
1801 // the same precursor information - none.
1802 url::Origin blob_origin = child->current_origin();
1803 EXPECT_TRUE(blob_origin.opaque());
1804 EXPECT_EQ(root->current_origin().GetTupleOrPrecursorTupleIfOpaque(),
1805 blob_origin.GetTupleOrPrecursorTupleIfOpaque());
1806
1807 // Navigate the frame away to any web URL.
1808 {
1809 GURL url(embedded_test_server()->GetURL("/title2.html"));
1810 TestFrameNavigationObserver observer(child);
1811 EXPECT_TRUE(ExecJs(child, JsReplace("window.location = $1", url)));
1812 observer.Wait();
1813 EXPECT_EQ(url, child->current_frame_host()->GetLastCommittedURL());
1814 }
1815 EXPECT_FALSE(child->current_origin().opaque());
1816 EXPECT_TRUE(shell()->web_contents()->GetController().CanGoBack());
1817 EXPECT_EQ(3, shell()->web_contents()->GetController().GetEntryCount());
1818 EXPECT_EQ(
1819 2, shell()->web_contents()->GetController().GetLastCommittedEntryIndex());
1820
1821 // Verify the blob URL still exists in the main frame, which keeps it alive
1822 // allowing a session history navigation back to succeed.
1823 EXPECT_EQ(blob_url, GURL(EvalJs(root, "blob_url;").ExtractString()));
1824
1825 // Now navigate back in session history. It should successfully go back to
1826 // the blob: URL.
1827 {
1828 TestFrameNavigationObserver observer(child);
1829 shell()->web_contents()->GetController().GoBack();
1830 observer.Wait();
1831 }
1832 EXPECT_EQ(blob_url, child->current_frame_host()->GetLastCommittedURL());
1833 EXPECT_TRUE(child->current_origin().opaque());
1834 EXPECT_EQ(blob_origin, child->current_origin());
1835 EXPECT_EQ(root->current_origin().GetTupleOrPrecursorTupleIfOpaque(),
1836 child->current_origin().GetTupleOrPrecursorTupleIfOpaque());
1837}
1838
1839// Test to verify that about:blank iframe, which is a child of a sandboxed
1840// iframe is not considered same origin, but precursor information is preserved
1841// in its origin.
1842IN_PROC_BROWSER_TEST_F(CrossProcessFrameTreeBrowserTest,
1843 AboutBlankSubframeInSandboxedFrame) {
1844 // Start off by navigating to a page with sandboxed iframe, which allows
1845 // script execution.
1846 GURL main_url(
1847 embedded_test_server()->GetURL("/sandboxed_main_frame_script.html"));
1848 EXPECT_TRUE(NavigateToURL(shell(), main_url));
1849
1850 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:551851 ->GetPrimaryFrameTree()
1852 .root();
Nasko Oskov039121012019-01-11 00:21:321853 EXPECT_EQ(1UL, root->child_count());
1854 FrameTreeNode* child = root->child_at(0);
1855
1856 // Navigate the frame to data: URL to cause it to have an opaque origin that
1857 // is derived from the |main_url| origin.
1858 GURL data_url("data:text/html,<html><body>foo</body></html>");
1859 {
1860 TestFrameNavigationObserver observer(child);
1861 EXPECT_TRUE(ExecJs(root, JsReplace("frames[0].location = $1", data_url)));
1862 observer.Wait();
1863 EXPECT_EQ(data_url, child->current_frame_host()->GetLastCommittedURL());
1864 }
1865
1866 // Add an about:blank iframe to the data: frame, which should not inherit the
1867 // origin, but should preserve the precursor information.
1868 {
1869 EXPECT_TRUE(ExecJs(child,
1870 "var f = document.createElement('iframe');"
1871 "document.body.appendChild(f);"));
1872 }
1873 EXPECT_EQ(1UL, child->child_count());
1874 FrameTreeNode* grandchild = child->child_at(0);
1875
1876 EXPECT_TRUE(grandchild->current_origin().opaque());
1877 EXPECT_EQ(GURL(url::kAboutBlankURL),
1878 grandchild->current_frame_host()->GetLastCommittedURL());
1879
1880 // The origin of the data: document should have precursor information matching
1881 // the main frame origin.
1882 EXPECT_EQ(root->current_origin().GetTupleOrPrecursorTupleIfOpaque(),
1883 child->current_origin().GetTupleOrPrecursorTupleIfOpaque());
1884
1885 // The same should hold also for the about:blank subframe of the data: frame.
1886 EXPECT_EQ(root->current_origin().GetTupleOrPrecursorTupleIfOpaque(),
1887 grandchild->current_origin().GetTupleOrPrecursorTupleIfOpaque());
1888
1889 // The about:blank document should not be able to access its parent, as they
1890 // are considered cross origin due to the sandbox flags on the parent.
1891 EXPECT_FALSE(ExecJs(grandchild, "window.parent.foo = 'bar';"));
1892 EXPECT_NE(child->current_origin(), grandchild->current_origin());
alexmosbc7eafa2014-12-06 01:38:091893}
1894
Ian Clelland5cbaaf82017-11-27 22:00:031895// Ensure that a popup opened from a sandboxed main frame inherits sandbox flags
1896// from its opener.
1897IN_PROC_BROWSER_TEST_F(CrossProcessFrameTreeBrowserTest,
1898 SandboxFlagsSetForNewWindow) {
1899 GURL main_url(
1900 embedded_test_server()->GetURL("/sandboxed_main_frame_script.html"));
1901 EXPECT_TRUE(NavigateToURL(shell(), main_url));
1902
1903 // It is safe to obtain the root frame tree node here, as it doesn't change.
1904 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:551905 ->GetPrimaryFrameTree()
1906 .root();
Ian Clelland5cbaaf82017-11-27 22:00:031907
1908 // Open a new window from the main frame.
1909 GURL popup_url(embedded_test_server()->GetURL("foo.com", "/title1.html"));
1910 Shell* new_shell = OpenPopup(root->current_frame_host(), popup_url, "");
1911 EXPECT_TRUE(new_shell);
1912 WebContents* new_contents = new_shell->web_contents();
1913
1914 // Check that the new window's sandbox flags correctly reflect the opener's
1915 // flags. Main frame sets allow-popups, allow-pointer-lock and allow-scripts.
1916 FrameTreeNode* popup_root =
Carlos Caballero15caeeb2021-10-27 09:57:551917 static_cast<WebContentsImpl*>(new_contents)->GetPrimaryFrameTree().root();
arthursonzognib93a4472020-04-10 07:38:001918 network::mojom::WebSandboxFlags main_frame_sandbox_flags =
Ian Clelland5cbaaf82017-11-27 22:00:031919 root->current_frame_host()->active_sandbox_flags();
arthursonzognib93a4472020-04-10 07:38:001920 EXPECT_EQ(network::mojom::WebSandboxFlags::kAll &
1921 ~network::mojom::WebSandboxFlags::kPopups &
1922 ~network::mojom::WebSandboxFlags::kPointerLock &
1923 ~network::mojom::WebSandboxFlags::kScripts &
1924 ~network::mojom::WebSandboxFlags::kAutomaticFeatures,
Ian Clelland5cbaaf82017-11-27 22:00:031925 main_frame_sandbox_flags);
1926
1927 EXPECT_EQ(main_frame_sandbox_flags,
1928 popup_root->effective_frame_policy().sandbox_flags);
1929 EXPECT_EQ(main_frame_sandbox_flags, popup_root->active_sandbox_flags());
1930 EXPECT_EQ(main_frame_sandbox_flags,
1931 popup_root->current_frame_host()->active_sandbox_flags());
1932}
1933
Shivani Sharmac4f561582018-11-15 15:58:391934// Tests that the user activation bits get cleared when a cross-site document is
1935// installed in the frame.
1936IN_PROC_BROWSER_TEST_F(CrossProcessFrameTreeBrowserTest,
1937 ClearUserActivationForNewDocument) {
1938 GURL main_url(embedded_test_server()->GetURL("/frame_tree/top.html"));
1939 EXPECT_TRUE(NavigateToURL(shell(), main_url));
1940
1941 // It is safe to obtain the root frame tree node here, as it doesn't change.
1942 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:551943 ->GetPrimaryFrameTree()
1944 .root();
Shivani Sharmac4f561582018-11-15 15:58:391945
Mustaq Ahmed4baa9a6e82019-12-20 23:43:461946 EXPECT_FALSE(root->HasStickyUserActivation());
Shivani Sharmac4f561582018-11-15 15:58:391947 EXPECT_FALSE(root->HasTransientUserActivation());
1948
1949 // Set the user activation bits.
1950 root->UpdateUserActivationState(
Mustaq Ahmeddc195e5b2020-08-04 18:45:111951 blink::mojom::UserActivationUpdateType::kNotifyActivation,
1952 blink::mojom::UserActivationNotificationType::kTest);
Mustaq Ahmed4baa9a6e82019-12-20 23:43:461953 EXPECT_TRUE(root->HasStickyUserActivation());
Shivani Sharmac4f561582018-11-15 15:58:391954 EXPECT_TRUE(root->HasTransientUserActivation());
1955
1956 // Install a new cross-site document to check the clearing of user activation
1957 // bits.
1958 GURL cross_site_url(
1959 embedded_test_server()->GetURL("foo.com", "/title2.html"));
1960 EXPECT_TRUE(NavigateToURL(shell(), cross_site_url));
1961
Mustaq Ahmed4baa9a6e82019-12-20 23:43:461962 EXPECT_FALSE(root->HasStickyUserActivation());
Shivani Sharmac4f561582018-11-15 15:58:391963 EXPECT_FALSE(root->HasTransientUserActivation());
1964}
1965
nickdb193a12016-09-09 23:09:231966// FrameTreeBrowserTest variant where we isolate http://*.is, Iceland's top
naskoabed2a52017-05-03 05:10:171967// level domain. This is an analogue to isolating extensions, which we can use
1968// inside content_browsertests, where extensions don't exist. Iceland, like an
nickdb193a12016-09-09 23:09:231969// extension process, is a special place with magical powers; we want to protect
1970// it from outsiders.
1971class IsolateIcelandFrameTreeBrowserTest : public ContentBrowserTest {
1972 public:
Fergal Dalya1d569972021-03-16 03:24:531973 IsolateIcelandFrameTreeBrowserTest() = default;
nickdb193a12016-09-09 23:09:231974
1975 void SetUpCommandLine(base::CommandLine* command_line) override {
Lukasz Anforowicze7c87d12018-11-03 02:53:341976 // Blink suppresses navigations to blob URLs of origins different from the
alexis.menard83f1b6d2017-05-17 19:37:331977 // frame initiating the navigation. We disable those checks for this test,
1978 // to test what happens in a compromise scenario.
1979 command_line->AppendSwitch(switches::kDisableWebSecurity);
Lukasz Anforowicze7c87d12018-11-03 02:53:341980
1981 // ProcessSwitchForIsolatedBlob test below requires that one of URLs used in
Lukasz Anforowicz25420932018-12-18 20:59:221982 // the test (blob:http://b.is/) belongs to an isolated origin.
1983 command_line->AppendSwitchASCII(switches::kIsolateOrigins, "http://b.is/");
nickdb193a12016-09-09 23:09:231984 }
1985
1986 void SetUpOnMainThread() override {
1987 host_resolver()->AddRule("*", "127.0.0.1");
nickdb193a12016-09-09 23:09:231988 SetupCrossSiteRedirector(embedded_test_server());
martijn12d2dad2016-11-11 10:59:271989 ASSERT_TRUE(embedded_test_server()->Start());
nickdb193a12016-09-09 23:09:231990 }
1991
1992 private:
1993 DISALLOW_COPY_AND_ASSIGN(IsolateIcelandFrameTreeBrowserTest);
1994};
1995
1996// Regression test for https://crbug.com/644966
1997IN_PROC_BROWSER_TEST_F(IsolateIcelandFrameTreeBrowserTest,
1998 ProcessSwitchForIsolatedBlob) {
nickdb193a12016-09-09 23:09:231999 // Set up an iframe.
2000 WebContents* contents = shell()->web_contents();
2001 FrameTreeNode* root =
Carlos Caballero15caeeb2021-10-27 09:57:552002 static_cast<WebContentsImpl*>(contents)->GetPrimaryFrameTree().root();
nickdb193a12016-09-09 23:09:232003 GURL main_url(embedded_test_server()->GetURL(
nick07fd7e12016-09-12 18:54:062004 "a.com", "/cross_site_iframe_factory.html?a(a)"));
nickdb193a12016-09-09 23:09:232005 EXPECT_TRUE(NavigateToURL(shell(), main_url));
2006
2007 // The navigation targets an invalid blob url; that's intentional to trigger
2008 // an error response. The response should commit in a process dedicated to
Nasko Oskov0401d812021-02-05 22:20:082009 // http://b.is or error pages, depending on policy.
Nick Carterb7e71312018-08-03 23:36:132010 EXPECT_EQ(
2011 "done",
2012 EvalJs(
2013 root,
2014 "new Promise((resolve) => {"
2015 " var iframe_element = document.getElementsByTagName('iframe')[0];"
2016 " iframe_element.onload = () => resolve('done');"
Lukasz Anforowicz25420932018-12-18 20:59:222017 " iframe_element.src = 'blob:http://b.is/';"
Nick Carterb7e71312018-08-03 23:36:132018 "});"));
Fergal Dalyf0522332020-07-18 06:09:462019 EXPECT_TRUE(WaitForLoadStop(contents));
nickdb193a12016-09-09 23:09:232020
2021 // Make sure we did a process transfer back to "b.is".
Aaron Colwelle953e562019-07-24 16:47:362022 const std::string kExpectedSiteURL =
2023 AreDefaultSiteInstancesEnabled()
2024 ? SiteInstanceImpl::GetDefaultSiteURL().spec()
2025 : "http://a.com/";
Nasko Oskov0401d812021-02-05 22:20:082026 const std::string kExpectedSubframeSiteURL =
2027 SiteIsolationPolicy::IsErrorPageIsolationEnabled(/*in_main_frame*/ false)
2028 ? "chrome-error://chromewebdata/"
2029 : "http://b.is/";
Aaron Colwelle953e562019-07-24 16:47:362030 EXPECT_EQ(base::StringPrintf(" Site A ------------ proxies for B\n"
2031 " +--Site B ------- proxies for A\n"
2032 "Where A = %s\n"
Nasko Oskov0401d812021-02-05 22:20:082033 " B = %s",
2034 kExpectedSiteURL.c_str(),
2035 kExpectedSubframeSiteURL.c_str()),
Fergal Daly79f44292020-12-01 02:30:482036 DepictFrameTree(*root));
nickdb193a12016-09-09 23:09:232037}
2038
Antonio Sartori5abc8de2021-07-13 08:42:472039class FrameTreeAnonymousIframeBrowserTest : public FrameTreeBrowserTest {
2040 public:
2041 FrameTreeAnonymousIframeBrowserTest() = default;
2042
2043 void SetUpCommandLine(base::CommandLine* command_line) override {
Antonio Sartori753cd6d2021-07-23 08:34:552044 command_line->AppendSwitch(switches::kEnableBlinkTestFeatures);
Antonio Sartori5abc8de2021-07-13 08:42:472045 }
2046};
2047
2048// Tests the mojo propagation of the 'anonymous' attribute to the browser.
2049IN_PROC_BROWSER_TEST_F(FrameTreeAnonymousIframeBrowserTest,
2050 AttributeIsPropagatedToBrowser) {
2051 GURL main_url(embedded_test_server()->GetURL("/hello.html"));
2052 EXPECT_TRUE(NavigateToURL(shell(), main_url));
2053
2054 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:552055 ->GetPrimaryFrameTree()
2056 .root();
Antonio Sartori5abc8de2021-07-13 08:42:472057
2058 // Not setting the attribute => the iframe is not anonymous.
2059 EXPECT_TRUE(ExecJs(root,
2060 "var f = document.createElement('iframe');"
2061 "document.body.appendChild(f);"));
2062 EXPECT_EQ(1U, root->child_count());
2063 EXPECT_FALSE(root->child_at(0)->anonymous());
2064
2065 // Setting the attribute on the iframe element makes the iframe anonymous.
2066 EXPECT_TRUE(ExecJs(root,
2067 "var d = document.createElement('div');"
2068 "d.innerHTML = '<iframe anonymous></iframe>';"
2069 "document.body.appendChild(d);"));
2070 EXPECT_EQ(2U, root->child_count());
2071 EXPECT_TRUE(root->child_at(1)->anonymous());
2072
2073 // Setting the attribute via javascript works.
2074 EXPECT_TRUE(ExecJs(root,
2075 "var g = document.createElement('iframe');"
2076 "g.anonymous = true;"
2077 "document.body.appendChild(g);"));
2078 EXPECT_EQ(3U, root->child_count());
2079 EXPECT_TRUE(root->child_at(2)->anonymous());
2080
2081 EXPECT_TRUE(ExecJs(root, "g.anonymous = false;"));
2082 EXPECT_FALSE(root->child_at(2)->anonymous());
2083
2084 EXPECT_TRUE(ExecJs(root, "g.anonymous = true;"));
2085 EXPECT_TRUE(root->child_at(2)->anonymous());
2086}
2087
[email protected]5f96f5a62014-01-10 00:05:112088} // namespace content