blob: 65ac324a3895b1a350fe7cfa9049b736eff21b4c [file] [log] [blame]
Avi Drissman4e1b7bc32022-09-15 14:03:501// Copyright 2014 The Chromium Authors
[email protected]5f96f5a62014-01-10 00:05:112// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Thomas Lukaszewicz00d39272024-08-21 23:54:105#include "content/browser/renderer_host/frame_tree.h"
6
Ken Rockot86bea1c2017-05-16 06:21:357#include "base/command_line.h"
Ari Chivukula5d15efb2023-01-21 04:33:528#include "base/strings/strcat.h"
Aaron Colwelle953e562019-07-24 16:47:369#include "base/strings/stringprintf.h"
shivanigithubf33e469c2021-08-02 13:52:2010#include "base/synchronization/lock.h"
Thomas Lukaszewicz00d39272024-08-21 23:54:1011#include "base/test/run_until.h"
shivanigithub93878d02021-06-15 11:37:5312#include "base/test/scoped_feature_list.h"
avib7348942015-12-25 20:57:1013#include "build/build_config.h"
danakjc492bf82020-09-09 20:02:4414#include "content/browser/renderer_host/frame_tree_node.h"
Yao Xiao6e1f7d32022-01-07 03:28:4015#include "content/browser/renderer_host/navigation_request.h"
danakjc492bf82020-09-09 20:02:4416#include "content/browser/renderer_host/render_frame_host_impl.h"
[email protected]5f96f5a62014-01-10 00:05:1117#include "content/browser/renderer_host/render_view_host_impl.h"
18#include "content/browser/web_contents/web_contents_impl.h"
Thomas Lukaszewicz00d39272024-08-21 23:54:1019#include "content/common/content_navigation_policy.h"
shivanigithubf33e469c2021-08-02 13:52:2020#include "content/public/browser/browser_thread.h"
Thomas Lukaszewicz00d39272024-08-21 23:54:1021#include "content/public/browser/dedicated_worker_service.h"
Dave Tapuska87696ae2021-11-18 18:48:3122#include "content/public/browser/navigation_handle.h"
Nasko Oskov0401d812021-02-05 22:20:0823#include "content/public/browser/site_isolation_policy.h"
Annie Sullivan35977ad2019-04-30 21:04:2824#include "content/public/common/content_features.h"
nickdb193a12016-09-09 23:09:2325#include "content/public/common/content_switches.h"
Nasko Oskov039121012019-01-11 00:21:3226#include "content/public/common/origin_util.h"
[email protected]5f96f5a62014-01-10 00:05:1127#include "content/public/common/url_constants.h"
Peter Kasting919ce652020-05-07 10:22:3628#include "content/public/test/browser_test.h"
[email protected]5f96f5a62014-01-10 00:05:1129#include "content/public/test/browser_test_utils.h"
[email protected]6e9def12014-03-27 20:23:2830#include "content/public/test/content_browser_test.h"
31#include "content/public/test/content_browser_test_utils.h"
Ari Chivukula5d15efb2023-01-21 04:33:5232#include "content/public/test/content_mock_cert_verifier.h"
John Abd-El-Malek3fea4c42019-01-10 01:15:4233#include "content/public/test/test_frame_navigation_observer.h"
[email protected]5f96f5a62014-01-10 00:05:1134#include "content/public/test/test_navigation_observer.h"
35#include "content/public/test/test_utils.h"
36#include "content/shell/browser/shell.h"
nickdb193a12016-09-09 23:09:2337#include "content/shell/common/shell_switches.h"
[email protected]82307f6b2014-08-07 03:30:1238#include "content/test/content_browser_test_utils_internal.h"
David Sandersc9ef3002022-02-02 15:27:0139#include "net/base/features.h"
[email protected]5f96f5a62014-01-10 00:05:1140#include "net/dns/mock_host_resolver.h"
Nan Linea8e9d202022-04-13 18:31:0141#include "net/test/embedded_test_server/controllable_http_response.h"
Dave Tapuska708c2732022-09-23 22:47:4942#include "net/test/embedded_test_server/default_handlers.h"
alexmos478dcbb2014-12-10 21:24:4643#include "net/test/embedded_test_server/embedded_test_server.h"
shivanigithubf33e469c2021-08-02 13:52:2044#include "net/test/embedded_test_server/http_request.h"
Ari Chivukula5d15efb2023-01-21 04:33:5245#include "services/network/public/cpp/network_switches.h"
arthursonzognib93a4472020-04-10 07:38:0046#include "services/network/public/cpp/web_sandbox_flags.h"
47#include "services/network/public/mojom/web_sandbox_flags.mojom-shared.h"
Gyuyoung Kim107c2a02021-04-13 01:49:3048#include "third_party/blink/public/common/chrome_debug_urls.h"
shivanigithub93878d02021-06-15 11:37:5349#include "third_party/blink/public/common/features.h"
Ari Chivukula5d15efb2023-01-21 04:33:5250#include "third_party/blink/public/common/storage_key/storage_key.h"
Thomas Lukaszewicz00d39272024-08-21 23:54:1051#include "third_party/blink/public/common/switches.h"
Antonio Gomes4b2c5132020-01-16 11:49:4852#include "third_party/blink/public/mojom/frame/user_activation_update_types.mojom.h"
nick1466c842015-11-25 20:08:0653#include "url/url_constants.h"
[email protected]5f96f5a62014-01-10 00:05:1154
55namespace content {
56
nickdb193a12016-09-09 23:09:2357namespace {
58
Nick Carterb7e71312018-08-03 23:36:1359EvalJsResult GetOriginFromRenderer(FrameTreeNode* node) {
Philip Jägenstedt67302a22018-09-14 09:58:0560 return EvalJs(node, "self.origin");
nickdb193a12016-09-09 23:09:2361}
62
Yuzu Saijo03dbf9b2022-07-22 04:29:4563// Expect that frame_name, id and src match the node's values.
64void ExpectAttributesEq(FrameTreeNode* node,
65 const std::string& frame_name,
Arthur Sonzognic686e8f2024-01-11 08:36:3766 const std::optional<std::string> id,
67 const std::optional<std::string> src) {
Yuzu Saijo03dbf9b2022-07-22 04:29:4568 EXPECT_EQ(frame_name, node->frame_name());
69 EXPECT_EQ(id, node->html_id());
70 EXPECT_EQ(src, node->html_src());
71}
72
nickdb193a12016-09-09 23:09:2373} // namespace
74
[email protected]5f96f5a62014-01-10 00:05:1175class FrameTreeBrowserTest : public ContentBrowserTest {
76 public:
Fergal Dalya1d569972021-03-16 03:24:5377 FrameTreeBrowserTest() = default;
[email protected]5f96f5a62014-01-10 00:05:1178
Peter Boström9b036532021-10-28 23:37:2879 FrameTreeBrowserTest(const FrameTreeBrowserTest&) = delete;
80 FrameTreeBrowserTest& operator=(const FrameTreeBrowserTest&) = delete;
81
alexmos478dcbb2014-12-10 21:24:4682 void SetUpOnMainThread() override {
83 host_resolver()->AddRule("*", "127.0.0.1");
alexmos478dcbb2014-12-10 21:24:4684 SetupCrossSiteRedirector(embedded_test_server());
martijn12d2dad2016-11-11 10:59:2785 ASSERT_TRUE(embedded_test_server()->Start());
alexmos478dcbb2014-12-10 21:24:4686 }
[email protected]5f96f5a62014-01-10 00:05:1187};
88
89// Ensures FrameTree correctly reflects page structure during navigations.
90IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, FrameTreeShape) {
alexmos478dcbb2014-12-10 21:24:4691 GURL base_url = embedded_test_server()->GetURL("A.com", "/site_isolation/");
[email protected]5f96f5a62014-01-10 00:05:1192
93 // Load doc without iframes. Verify FrameTree just has root.
94 // Frame tree:
95 // Site-A Root
Alex Moshchukaa95adf52019-08-13 00:02:0296 EXPECT_TRUE(NavigateToURL(shell(), base_url.Resolve("blank.html")));
Aran Gilman37d11632019-10-08 23:07:1597 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:5598 ->GetPrimaryFrameTree()
99 .root();
[email protected]5f96f5a62014-01-10 00:05:11100 EXPECT_EQ(0U, root->child_count());
101
102 // Add 2 same-site frames. Verify 3 nodes in tree with proper names.
103 // Frame tree:
104 // Site-A Root -- Site-A frame1
105 // \-- Site-A frame2
[email protected]71bc140b2022-03-14 23:45:01106 LoadStopObserver observer1(shell()->web_contents());
Alex Moshchukaa95adf52019-08-13 00:02:02107 EXPECT_TRUE(NavigateToURL(shell(), base_url.Resolve("frames-X-X.html")));
[email protected]5f96f5a62014-01-10 00:05:11108 observer1.Wait();
109 ASSERT_EQ(2U, root->child_count());
110 EXPECT_EQ(0U, root->child_at(0)->child_count());
111 EXPECT_EQ(0U, root->child_at(1)->child_count());
112}
113
114// TODO(ajwong): Talk with nasko and merge this functionality with
115// FrameTreeShape.
116IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, FrameTreeShape2) {
Alex Moshchukaa95adf52019-08-13 00:02:02117 EXPECT_TRUE(NavigateToURL(
118 shell(), embedded_test_server()->GetURL("/frame_tree/top.html")));
[email protected]5f96f5a62014-01-10 00:05:11119
120 WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents());
Carlos Caballero15caeeb2021-10-27 09:57:55121 FrameTreeNode* root = wc->GetPrimaryFrameTree().root();
[email protected]5f96f5a62014-01-10 00:05:11122
[email protected]58faf942014-02-20 21:03:58123 // Check that the root node is properly created.
[email protected]5f96f5a62014-01-10 00:05:11124 ASSERT_EQ(3UL, root->child_count());
Arthur Sonzognic686e8f2024-01-11 08:36:37125 ExpectAttributesEq(root, std::string(), std::nullopt, std::nullopt);
[email protected]5f96f5a62014-01-10 00:05:11126
127 ASSERT_EQ(2UL, root->child_at(0)->child_count());
Yuzu Saijo03dbf9b2022-07-22 04:29:45128 ExpectAttributesEq(root->child_at(0), "1-1-name", "1-1-id", "1-1.html");
[email protected]5f96f5a62014-01-10 00:05:11129
130 // Verify the deepest node exists and has the right name.
131 ASSERT_EQ(2UL, root->child_at(2)->child_count());
132 EXPECT_EQ(1UL, root->child_at(2)->child_at(1)->child_count());
133 EXPECT_EQ(0UL, root->child_at(2)->child_at(1)->child_at(0)->child_count());
Yuzu Saijo03dbf9b2022-07-22 04:29:45134 ExpectAttributesEq(root->child_at(2)->child_at(1)->child_at(0), "3-1-name",
135 "3-1-id", "3-1.html");
[email protected]5f96f5a62014-01-10 00:05:11136
137 // Navigate to about:blank, which should leave only the root node of the frame
138 // tree in the browser process.
Alex Moshchukaa95adf52019-08-13 00:02:02139 EXPECT_TRUE(
140 NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html")));
[email protected]5f96f5a62014-01-10 00:05:11141
Carlos Caballero15caeeb2021-10-27 09:57:55142 root = wc->GetPrimaryFrameTree().root();
[email protected]5f96f5a62014-01-10 00:05:11143 EXPECT_EQ(0UL, root->child_count());
Arthur Sonzognic686e8f2024-01-11 08:36:37144 ExpectAttributesEq(root, std::string(), std::nullopt, std::nullopt);
Yuzu Saijo03dbf9b2022-07-22 04:29:45145}
146
147// Frame attributes of iframe elements are correctly tracked in FrameTree.
148IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, FrameTreeAttributesUpdate) {
149 EXPECT_TRUE(NavigateToURL(
150 shell(), embedded_test_server()->GetURL("/frame_tree/top.html")));
151
152 WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents());
153 FrameTreeNode* root = wc->GetPrimaryFrameTree().root();
154
155 // Check that the root node is properly created.
156 ASSERT_EQ(3UL, root->child_count());
Arthur Sonzognic686e8f2024-01-11 08:36:37157 ExpectAttributesEq(root, std::string(), std::nullopt, std::nullopt);
Yuzu Saijo03dbf9b2022-07-22 04:29:45158
159 ASSERT_EQ(2UL, root->child_at(0)->child_count());
160 ExpectAttributesEq(root->child_at(0), "1-1-name", "1-1-id", "1-1.html");
161
162 // Change id, name and src of the iframe.
163 EXPECT_TRUE(ExecJs(root->current_frame_host(), R"(
164 let iframe = document.getElementById('1-1-id');
165 iframe.id = '1-1-updated-id';
166 iframe.name = '1-1-updated-name';
167 iframe.src = '1-1-updated.html';
168 )"));
169 // |html_name()| gets updated whenever the name attribute gets updated.
170 EXPECT_EQ("1-1-updated-name", root->child_at(0)->html_name());
171 ExpectAttributesEq(root->child_at(0), "1-1-name", "1-1-updated-id",
172 "1-1-updated.html");
173}
174
175// Ensures that frames' name attributes and their updates are tracked in
176// |html_name()| and window.name and its updates are tracked in |frame_name()|.
177IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, FrameNameVSWindowName) {
178 EXPECT_TRUE(NavigateToURL(
179 shell(), embedded_test_server()->GetURL("/frame_tree/top.html")));
180
181 WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents());
182 FrameTreeNode* root = wc->GetPrimaryFrameTree().root();
183
184 // Check that the root node is properly created.
185 ASSERT_EQ(3UL, root->child_count());
Arthur Sonzognic686e8f2024-01-11 08:36:37186 EXPECT_EQ(std::nullopt, root->html_name());
[email protected]5f96f5a62014-01-10 00:05:11187 EXPECT_EQ(std::string(), root->frame_name());
Yuzu Saijo03dbf9b2022-07-22 04:29:45188
189 ASSERT_EQ(2UL, root->child_at(0)->child_count());
190 EXPECT_EQ("1-1-name", root->child_at(0)->html_name());
191 EXPECT_EQ("1-1-name", root->child_at(0)->frame_name());
192
193 // Change the name attribute of the iframe.
194 EXPECT_TRUE(ExecJs(root->current_frame_host(), R"(
195 let iframe = document.getElementById('1-1-id');
196 iframe.name = '1-1-updated-name';
197 )"));
198 // |html_name()| gets updated whenever the name attribute gets updated.
199 EXPECT_EQ("1-1-updated-name", root->child_at(0)->html_name());
200 // |frame_name()| stays the same.
201 EXPECT_EQ("1-1-name", root->child_at(0)->frame_name());
202
203 // Change the window.name of the iframe.
204 EXPECT_TRUE(ExecJs(root->current_frame_host(), R"(
205 let iframe = document.getElementById('1-1-id');
206 iframe.contentWindow.name = '1-1-updated-name-2';
207 )"));
208 // |html_name()| stays the same.
209 EXPECT_EQ("1-1-updated-name", root->child_at(0)->html_name());
210 // |frame_name()| gets updated.
211 EXPECT_EQ("1-1-updated-name-2", root->child_at(0)->frame_name());
212}
213
214// Ensures that long attributes are cut down to the max length.
215IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, LongAttributesCutDown) {
216 EXPECT_TRUE(NavigateToURL(
217 shell(), embedded_test_server()->GetURL("/frame_tree/top.html")));
218
219 WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents());
220 FrameTreeNode* root = wc->GetPrimaryFrameTree().root();
221
222 // Check that the root node is properly created.
223 ASSERT_EQ(3UL, root->child_count());
224 ASSERT_EQ(2UL, root->child_at(0)->child_count());
225 EXPECT_EQ("1-1-name", root->child_at(0)->html_name());
226
227 // Change the name attribute of the iframe.
228 EXPECT_TRUE(ExecJs(root->current_frame_host(), R"(
229 let iframe = document.getElementById('1-1-id');
230 iframe.id += 'a'.repeat(1200);
231 iframe.name += 'b'.repeat(1200);
232 iframe.src += 'c'.repeat(1200) + '.html';
233 )"));
234 // Long attribute is cut down to the maximum length.
Yuzu Saijodc870f92023-01-20 03:39:11235 EXPECT_EQ(1024UL, root->child_at(0)->html_id()->size());
236 EXPECT_EQ(1024UL, root->child_at(0)->html_name()->size());
237 EXPECT_EQ(1024UL, root->child_at(0)->html_src()->size());
Yuzu Saijo03dbf9b2022-07-22 04:29:45238}
239
240// Insert a frame into the frame tree and ensure that the inserted frame's
241// attributes are correctly captured.
242IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, InsertFrameInTree) {
243 EXPECT_TRUE(NavigateToURL(
244 shell(), embedded_test_server()->GetURL("/frame_tree/top.html")));
245
246 WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents());
247 FrameTreeNode* root = wc->GetPrimaryFrameTree().root();
248
249 // Check that the root node is properly created.
250 ASSERT_EQ(3UL, root->child_count());
Arthur Sonzognic686e8f2024-01-11 08:36:37251 ExpectAttributesEq(root, std::string(), std::nullopt, std::nullopt);
Yuzu Saijo03dbf9b2022-07-22 04:29:45252
253 ASSERT_EQ(2UL, root->child_at(0)->child_count());
254 ExpectAttributesEq(root->child_at(0), "1-1-name", "1-1-id", "1-1.html");
255
256 // Insert a child iframe.
257 EXPECT_TRUE(ExecJs(root->current_frame_host(), R"(
258 let new_iframe = document.createElement('iframe');
259 new_iframe.id = '1-1-child-id';
260 new_iframe.src = '1-1-child.html';
261 new_iframe.name = '1-1-child-name';
262
263 document.body.appendChild(new_iframe);
264 )"));
265 // Check that the new iframe is inserted and their attributes are correct.
266 ASSERT_EQ(4UL, root->child_count());
267 ExpectAttributesEq(root->child_at(3), "1-1-child-name", "1-1-child-id",
268 "1-1-child.html");
[email protected]5f96f5a62014-01-10 00:05:11269}
270
271// Test that we can navigate away if the previous renderer doesn't clean up its
272// child frames.
273IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, FrameTreeAfterCrash) {
Alex Moshchukaa95adf52019-08-13 00:02:02274 EXPECT_TRUE(NavigateToURL(
275 shell(), embedded_test_server()->GetURL("/frame_tree/top.html")));
[email protected]5f96f5a62014-01-10 00:05:11276
creise42f2a52014-09-18 18:14:57277 // Ensure the view and frame are live.
Lingqi Chi641aeee2021-09-08 00:32:58278 RenderFrameHostImpl* rfh1 = static_cast<RenderFrameHostImpl*>(
Dave Tapuska327c06c92022-06-13 20:31:51279 shell()->web_contents()->GetPrimaryMainFrame());
Kevin McNeeec162302022-04-14 16:00:28280 RenderViewHostImpl* rvh = rfh1->render_view_host();
creise42f2a52014-09-18 18:14:57281 EXPECT_TRUE(rvh->IsRenderViewLive());
arthursonzognif4c4a4a2019-08-08 18:54:44282 EXPECT_TRUE(rfh1->IsRenderFrameLive());
creise42f2a52014-09-18 18:14:57283
[email protected]5f96f5a62014-01-10 00:05:11284 // Crash the renderer so that it doesn't send any FrameDetached messages.
285 RenderProcessHostWatcher crash_observer(
286 shell()->web_contents(),
287 RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
Wez0abfbf512018-03-03 01:54:45288 ASSERT_TRUE(
Dave Tapuska327c06c92022-06-13 20:31:51289 shell()->web_contents()->GetPrimaryMainFrame()->GetProcess()->Shutdown(
290 0));
[email protected]5f96f5a62014-01-10 00:05:11291 crash_observer.Wait();
292
[email protected]58faf942014-02-20 21:03:58293 // The frame tree should be cleared.
[email protected]5f96f5a62014-01-10 00:05:11294 WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents());
Carlos Caballero15caeeb2021-10-27 09:57:55295 FrameTreeNode* root = wc->GetPrimaryFrameTree().root();
[email protected]5f96f5a62014-01-10 00:05:11296 EXPECT_EQ(0UL, root->child_count());
[email protected]5f96f5a62014-01-10 00:05:11297
creise42f2a52014-09-18 18:14:57298 // Ensure the view and frame aren't live anymore.
299 EXPECT_FALSE(rvh->IsRenderViewLive());
arthursonzognif4c4a4a2019-08-08 18:54:44300 EXPECT_FALSE(rfh1->IsRenderFrameLive());
creise42f2a52014-09-18 18:14:57301
[email protected]5f96f5a62014-01-10 00:05:11302 // Navigate to a new URL.
alexmos478dcbb2014-12-10 21:24:46303 GURL url(embedded_test_server()->GetURL("/title1.html"));
Alex Moshchukaa95adf52019-08-13 00:02:02304 EXPECT_TRUE(NavigateToURL(shell(), url));
[email protected]5f96f5a62014-01-10 00:05:11305 EXPECT_EQ(0UL, root->child_count());
[email protected]58faf942014-02-20 21:03:58306 EXPECT_EQ(url, root->current_url());
creise42f2a52014-09-18 18:14:57307
arthursonzognif4c4a4a2019-08-08 18:54:44308 RenderFrameHostImpl* rfh2 = root->current_frame_host();
creise42f2a52014-09-18 18:14:57309 // Ensure the view and frame are live again.
310 EXPECT_TRUE(rvh->IsRenderViewLive());
arthursonzognif4c4a4a2019-08-08 18:54:44311 EXPECT_TRUE(rfh2->IsRenderFrameLive());
[email protected]5f96f5a62014-01-10 00:05:11312}
313
Thomas Lukaszewicz00d39272024-08-21 23:54:10314IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, DiscardFrameTree) {
315 WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents());
316 FrameTree& frame_tree = wc->GetPrimaryFrameTree();
317 FrameTreeNode* root = frame_tree.root();
318
319 EXPECT_TRUE(NavigateToURL(
320 shell(), embedded_test_server()->GetURL("/frame_tree/top.html")));
321 EXPECT_FALSE(wc->GetController().NeedsReload());
322 EXPECT_EQ(3UL, root->child_count());
323
324 // Ensure the view and frame are live.
325 RenderFrameHostImplWrapper initial_rfh(wc->GetPrimaryMainFrame());
326 RenderViewHostImpl* initial_rvh = initial_rfh->render_view_host();
327 EXPECT_TRUE(initial_rvh->IsRenderViewLive());
328 EXPECT_TRUE(initial_rfh->IsRenderFrameLive());
329
330 const auto get_child_isn = [&](RenderFrameHostImpl* rfh, int child_pos) {
331 return rfh->child_at(child_pos)
332 ->current_frame_host()
333 ->last_committed_frame_entry()
334 ->item_sequence_number();
335 };
336 const int initial_nav_entry_id =
337 wc->GetController().GetLastCommittedEntry()->GetUniqueID();
338 const int64_t initial_isn =
339 initial_rfh->last_committed_frame_entry()->item_sequence_number();
340 const int64_t initial_isn_child1 = get_child_isn(initial_rfh.get(), 0);
341 const int64_t initial_isn_child2 = get_child_isn(initial_rfh.get(), 1);
342 const int64_t initial_isn_child3 = get_child_isn(initial_rfh.get(), 2);
343
344 // Discard the frame tree, wait until all child frames have been cleared away.
345 EXPECT_FALSE(root->was_discarded());
346 frame_tree.Discard();
347 ASSERT_TRUE(
348 base::test::RunUntil([&]() { return 0u == root->child_count(); }));
349
350 // The root rfh and rvh should remain unchanged however the child frames
351 // should have been cleared.
352 EXPECT_EQ(initial_nav_entry_id,
353 wc->GetController().GetLastCommittedEntry()->GetUniqueID());
354 EXPECT_EQ(initial_isn,
355 initial_rfh->last_committed_frame_entry()->item_sequence_number());
356
357 EXPECT_TRUE(root->was_discarded());
358 EXPECT_TRUE(wc->GetController().NeedsReload());
359 EXPECT_EQ(initial_rfh.get(), wc->GetPrimaryMainFrame());
360 EXPECT_EQ(initial_rvh, wc->GetPrimaryMainFrame()->render_view_host());
361 EXPECT_TRUE(initial_rvh->IsRenderViewLive());
362 EXPECT_TRUE(initial_rfh->IsRenderFrameLive());
363 EXPECT_EQ(0u, root->child_count());
364
365 // Reload the frame tree. Child frames should be reloaded and the root rfh and
366 // rvh should have changed.
367 wc->GetController().LoadIfNecessary();
368 EXPECT_TRUE(WaitForLoadStop(wc));
369
370 RenderFrameHostImplWrapper final_rfh(wc->GetPrimaryMainFrame());
371 RenderViewHostImpl* final_rvh = final_rfh->render_view_host();
372 EXPECT_EQ(initial_nav_entry_id,
373 wc->GetController().GetLastCommittedEntry()->GetUniqueID());
374 EXPECT_EQ(initial_isn,
375 final_rfh->last_committed_frame_entry()->item_sequence_number());
376 EXPECT_EQ(initial_isn_child1, get_child_isn(final_rfh.get(), 0));
377 EXPECT_EQ(initial_isn_child2, get_child_isn(final_rfh.get(), 1));
378 EXPECT_EQ(initial_isn_child3, get_child_isn(final_rfh.get(), 2));
379
380 EXPECT_FALSE(root->was_discarded());
381 EXPECT_FALSE(wc->GetController().NeedsReload());
382 EXPECT_EQ(3u, root->child_count());
383 EXPECT_NE(initial_rfh.get(), final_rfh.get());
384 EXPECT_NE(initial_rvh, final_rvh);
385 EXPECT_TRUE(final_rvh->IsRenderViewLive());
386 EXPECT_TRUE(final_rfh->IsRenderFrameLive());
387}
388
389class DedicatedWorkerObserver : public DedicatedWorkerService::Observer {
390 public:
391 explicit DedicatedWorkerObserver(DedicatedWorkerService* worker_service) {
392 scoped_context_observation_.Observe(worker_service);
393 }
394 DedicatedWorkerObserver(const DedicatedWorkerObserver&) = delete;
395 DedicatedWorkerObserver& operator=(const DedicatedWorkerObserver&) = delete;
396 ~DedicatedWorkerObserver() override = default;
397
398 void WaitForCreated() {
399 if (!is_created_) {
400 run_loop_.emplace();
401 run_loop_->Run();
402 run_loop_.reset();
403 }
404 }
405 void WaitForDestroyed() {
406 if (!is_destroyed_) {
407 run_loop_.emplace();
408 run_loop_->Run();
409 run_loop_.reset();
410 }
411 }
412
413 // DedicatedWorkerService::Observer:
414 void OnBeforeWorkerDestroyed(const blink::DedicatedWorkerToken& worker_token,
415 DedicatedWorkerCreator creator) override {
416 is_destroyed_ = true;
417 if (run_loop_.has_value()) {
418 run_loop_->Quit();
419 }
420 }
421 void OnWorkerCreated(const blink::DedicatedWorkerToken& worker_token,
422 int worker_process_id,
423 const url::Origin& security_origin,
424 DedicatedWorkerCreator creator) override {
425 is_created_ = true;
426 if (run_loop_.has_value()) {
427 run_loop_->Quit();
428 }
429 }
430 void OnFinalResponseURLDetermined(
431 const blink::DedicatedWorkerToken& worker_token,
432 const GURL& url) override {}
433
434 private:
435 bool is_created_ = false;
436 bool is_destroyed_ = false;
437 std::optional<base::RunLoop> run_loop_;
438 base::ScopedObservation<DedicatedWorkerService,
439 DedicatedWorkerService::Observer>
440 scoped_context_observation_{this};
441};
442
443class DedicatedWorkerFrameTreeBrowserTest : public FrameTreeBrowserTest {
444 public:
445 void SetUpCommandLine(base::CommandLine* command_line) override {
446 FrameTreeBrowserTest::SetUpCommandLine(command_line);
447 command_line->AppendSwitchASCII(blink::switches::kJavaScriptFlags,
448 "--expose-gc");
449 }
450};
451
452IN_PROC_BROWSER_TEST_F(DedicatedWorkerFrameTreeBrowserTest,
453 DiscardClearsDedicatedWorkers) {
454 WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents());
455 FrameTree& frame_tree = wc->GetPrimaryFrameTree();
456 FrameTreeNode* root = frame_tree.root();
457
458 EXPECT_TRUE(
459 NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html")));
460
461 // Navigate to a page and register a dedicated worker.
462 DedicatedWorkerObserver worker_observer(root->current_frame_host()
463 ->GetStoragePartition()
464 ->GetDedicatedWorkerService());
465 EXPECT_TRUE(EvalJs(shell(), "const worker = new Worker('/workers/empty.js');")
466 .error.empty());
467 worker_observer.WaitForCreated();
468
469 // Discard the rfh, the associated worker should be cleared.
470 EXPECT_FALSE(root->was_discarded());
471 EXPECT_FALSE(wc->GetController().NeedsReload());
472 frame_tree.Discard();
473 EXPECT_TRUE(root->was_discarded());
474 EXPECT_TRUE(wc->GetController().NeedsReload());
475 // Trigger GC to cleanup the worker in the renderer.
476 EXPECT_TRUE(EvalJs(shell(), "window.gc();").error.empty());
477 worker_observer.WaitForDestroyed();
478}
479
480// TODO(347770670): Consider restricting script access to discarded documents
481// from related documents.
482IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, DiscardedFrameAllowsScriptAccess) {
483 WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents());
484 FrameTree& frame_tree = wc->GetPrimaryFrameTree();
485
486 const GURL main_url(embedded_test_server()->GetURL("/title1.html"));
487 EXPECT_TRUE(NavigateToURL(shell(), main_url));
488
489 // Open a popup using window.open.
490 const GURL popup_url(embedded_test_server()->GetURL("/title2.html"));
491 Shell* new_shell = OpenPopup(shell(), popup_url, "foo");
492 EXPECT_TRUE(new_shell);
493
494 // Assert the opened window is able to script its opener.
495 EXPECT_EQ("foo", EvalJs(new_shell, "window.name;"));
496 EXPECT_TRUE(EvalJs(new_shell, "window.opener.name = 'bar';").error.empty());
497 EXPECT_EQ("bar", EvalJs(shell(), "window.name;"));
498
499 frame_tree.Discard();
500 EXPECT_TRUE(frame_tree.root()->was_discarded());
501
502 // After a discard operation the opened window should should still be able to
503 // script its opener.
504 EXPECT_TRUE(EvalJs(new_shell, "window.opener.name = 'bar2';").error.empty());
505 EXPECT_EQ("bar2", EvalJs(shell(), "window.name;"));
506
507 // After a reload the opened window should still be able to script its opener.
508 wc->GetController().LoadIfNecessary();
509 EXPECT_TRUE(WaitForLoadStop(wc));
510 EXPECT_TRUE(EvalJs(new_shell, "window.opener.name = 'bar3';").error.empty());
511 EXPECT_EQ("bar3", EvalJs(shell(), "window.name;"));
512}
513
514class FrameTreeBrowserTestWithBFCache : public FrameTreeBrowserTest {
515 public:
516 FrameTreeBrowserTestWithBFCache() {
517 feature_list_.InitWithFeaturesAndParameters(
518 {{features::kBackForwardCache, {}},
519 {kBackForwardCacheNoTimeEviction, {}}},
520 // Allow BackForwardCache for all devices regardless of their memory.
521 {features::kBackForwardCacheMemoryControls});
522 EXPECT_TRUE(IsBackForwardCacheEnabled());
523 }
524
525 private:
526 base::test::ScopedFeatureList feature_list_;
527};
528
529IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTestWithBFCache, DiscardClearsBFCache) {
530 WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents());
531 FrameTree& frame_tree = wc->GetPrimaryFrameTree();
532 BackForwardCacheImpl& back_forward_cache =
533 wc->GetController().GetBackForwardCache();
534
535 // The BFCache should start empty.
536 EXPECT_TRUE(back_forward_cache.GetEntries().empty());
537
538 const GURL url1(embedded_test_server()->GetURL("a.com", "/title1.html"));
539 const GURL url2(embedded_test_server()->GetURL("b.com", "/title2.html"));
540
541 // Navigate to url1.
542 EXPECT_TRUE(NavigateToURL(shell(), url1));
543 RenderFrameHostImplWrapper rfh_a(wc->GetPrimaryMainFrame());
544 RenderFrameDeletedObserver delete_observer_rfh_a(rfh_a.get());
545
546 // Navigate to url2, the frame hosting url1 should be moved to the BFCache.
547 EXPECT_TRUE(NavigateToURL(shell(), url2));
548 EXPECT_FALSE(delete_observer_rfh_a.deleted());
549 EXPECT_TRUE(rfh_a->IsInBackForwardCache());
550 EXPECT_EQ(1u, back_forward_cache.GetEntries().size());
551
552 // Discard the frame tree, the BFCache should have been cleared.
553 frame_tree.Discard();
554 EXPECT_TRUE(frame_tree.root()->was_discarded());
555 back_forward_cache.PostTaskToDestroyEvictedFrames();
556 ASSERT_TRUE(base::test::RunUntil(
557 [&]() { return back_forward_cache.GetEntries().empty(); }));
558 EXPECT_TRUE(delete_observer_rfh_a.deleted());
559}
560
561IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTestWithBFCache,
562 DiscardedFrameDoesNotEnterBFCache) {
563 WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents());
564 FrameTree& frame_tree = wc->GetPrimaryFrameTree();
565 BackForwardCacheImpl& back_forward_cache =
566 wc->GetController().GetBackForwardCache();
567
568 // The BFCache should start empty.
569 EXPECT_TRUE(back_forward_cache.GetEntries().empty());
570
571 const GURL url1(embedded_test_server()->GetURL("a.com", "/title1.html"));
572 const GURL url2(embedded_test_server()->GetURL("b.com", "/title2.html"));
573
574 // Navigate to url1.
575 EXPECT_TRUE(NavigateToURL(shell(), url1));
576 RenderFrameHostImplWrapper rfh_a(wc->GetPrimaryMainFrame());
577 RenderFrameDeletedObserver delete_observer_rfh_a(rfh_a.get());
578
579 // Discard the frame tree.
580 frame_tree.Discard();
581 EXPECT_TRUE(frame_tree.root()->was_discarded());
582 EXPECT_FALSE(delete_observer_rfh_a.deleted());
583
584 // Navigate to url2, the frame hosting url1 should not be moved to the
585 // BFCache.
586 EXPECT_TRUE(NavigateToURL(shell(), url2));
587 EXPECT_FALSE(frame_tree.root()->was_discarded());
588 EXPECT_TRUE(back_forward_cache.GetEntries().empty());
589 ASSERT_TRUE(
590 base::test::RunUntil([&]() { return delete_observer_rfh_a.deleted(); }));
591}
592
[email protected]5f96f5a62014-01-10 00:05:11593// Test that we can navigate away if the previous renderer doesn't clean up its
594// child frames.
Nasko Oskov31c45ff2019-10-16 01:15:19595IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, NavigateWithLeftoverFrames) {
alexmos478dcbb2014-12-10 21:24:46596 GURL base_url = embedded_test_server()->GetURL("A.com", "/site_isolation/");
[email protected]5f96f5a62014-01-10 00:05:11597
Alex Moshchukaa95adf52019-08-13 00:02:02598 EXPECT_TRUE(NavigateToURL(
599 shell(), embedded_test_server()->GetURL("/frame_tree/top.html")));
[email protected]5f96f5a62014-01-10 00:05:11600
601 // Hang the renderer so that it doesn't send any FrameDetached messages.
602 // (This navigation will never complete, so don't wait for it.)
Gyuyoung Kim107c2a02021-04-13 01:49:30603 shell()->LoadURL(GURL(blink::kChromeUIHangURL));
[email protected]5f96f5a62014-01-10 00:05:11604
605 // Check that the frame tree still has children.
606 WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents());
Carlos Caballero15caeeb2021-10-27 09:57:55607 FrameTreeNode* root = wc->GetPrimaryFrameTree().root();
[email protected]5f96f5a62014-01-10 00:05:11608 ASSERT_EQ(3UL, root->child_count());
609
610 // Navigate to a new URL. We use LoadURL because NavigateToURL will try to
611 // wait for the previous navigation to stop.
612 TestNavigationObserver tab_observer(wc, 1);
613 shell()->LoadURL(base_url.Resolve("blank.html"));
614 tab_observer.Wait();
615
[email protected]58faf942014-02-20 21:03:58616 // The frame tree should now be cleared.
[email protected]5f96f5a62014-01-10 00:05:11617 EXPECT_EQ(0UL, root->child_count());
[email protected]5f96f5a62014-01-10 00:05:11618}
619
creise42f2a52014-09-18 18:14:57620// Ensure that IsRenderFrameLive is true for main frames and same-site iframes.
621IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, IsRenderFrameLive) {
alexmos478dcbb2014-12-10 21:24:46622 GURL main_url(embedded_test_server()->GetURL("/frame_tree/top.html"));
Alex Moshchukaa95adf52019-08-13 00:02:02623 EXPECT_TRUE(NavigateToURL(shell(), main_url));
creise42f2a52014-09-18 18:14:57624
625 // It is safe to obtain the root frame tree node here, as it doesn't change.
626 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:55627 ->GetPrimaryFrameTree()
628 .root();
creise42f2a52014-09-18 18:14:57629
630 // The root and subframe should each have a live RenderFrame.
631 EXPECT_TRUE(
632 root->current_frame_host()->render_view_host()->IsRenderViewLive());
633 EXPECT_TRUE(root->current_frame_host()->IsRenderFrameLive());
634 EXPECT_TRUE(root->child_at(0)->current_frame_host()->IsRenderFrameLive());
635
636 // Load a same-site page into iframe and it should still be live.
alexmos478dcbb2014-12-10 21:24:46637 GURL http_url(embedded_test_server()->GetURL("/title1.html"));
Lukasz Anforowicz69c25dfd2020-11-12 21:50:20638 EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(0), http_url));
creise42f2a52014-09-18 18:14:57639 EXPECT_TRUE(
640 root->current_frame_host()->render_view_host()->IsRenderViewLive());
641 EXPECT_TRUE(root->current_frame_host()->IsRenderFrameLive());
642 EXPECT_TRUE(root->child_at(0)->current_frame_host()->IsRenderFrameLive());
643}
644
alexmosbc7eafa2014-12-06 01:38:09645// Ensure that origins are correctly set on navigations.
646IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, OriginSetOnNavigation) {
nick1466c842015-11-25 20:08:06647 GURL about_blank(url::kAboutBlankURL);
648 GURL main_url(
649 embedded_test_server()->GetURL("a.com", "/frame_tree/top.html"));
alexmosbc7eafa2014-12-06 01:38:09650 EXPECT_TRUE(NavigateToURL(shell(), main_url));
nick1466c842015-11-25 20:08:06651 WebContents* contents = shell()->web_contents();
alexmosbc7eafa2014-12-06 01:38:09652
653 // It is safe to obtain the root frame tree node here, as it doesn't change.
nick1466c842015-11-25 20:08:06654 FrameTreeNode* root =
Carlos Caballero15caeeb2021-10-27 09:57:55655 static_cast<WebContentsImpl*>(contents)->GetPrimaryFrameTree().root();
alexmosbc7eafa2014-12-06 01:38:09656
657 // Extra '/' is added because the replicated origin is serialized in RFC 6454
658 // format, which dictates no trailing '/', whereas GURL::GetOrigin does put a
659 // '/' at the end.
Mike West800532c2021-10-14 09:26:52660 EXPECT_EQ(main_url.DeprecatedGetOriginAsURL().spec(),
nick1466c842015-11-25 20:08:06661 root->current_origin().Serialize() + '/');
662 EXPECT_EQ(
Mike West800532c2021-10-14 09:26:52663 main_url.DeprecatedGetOriginAsURL().spec(),
nick1466c842015-11-25 20:08:06664 root->current_frame_host()->GetLastCommittedOrigin().Serialize() + '/');
alexmosbc7eafa2014-12-06 01:38:09665
nick1466c842015-11-25 20:08:06666 // The iframe is inititially same-origin.
667 EXPECT_TRUE(
668 root->current_frame_host()->GetLastCommittedOrigin().IsSameOriginWith(
669 root->child_at(0)->current_frame_host()->GetLastCommittedOrigin()));
670 EXPECT_EQ(root->current_origin().Serialize(), GetOriginFromRenderer(root));
671 EXPECT_EQ(root->child_at(0)->current_origin().Serialize(),
672 GetOriginFromRenderer(root->child_at(0)));
673
674 // Navigate the iframe cross-origin.
675 GURL frame_url(embedded_test_server()->GetURL("b.com", "/title1.html"));
Lukasz Anforowicz69c25dfd2020-11-12 21:50:20676 EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(0), frame_url));
nick1466c842015-11-25 20:08:06677 EXPECT_EQ(frame_url, root->child_at(0)->current_url());
Mike West800532c2021-10-14 09:26:52678 EXPECT_EQ(frame_url.DeprecatedGetOriginAsURL().spec(),
nick1466c842015-11-25 20:08:06679 root->child_at(0)->current_origin().Serialize() + '/');
680 EXPECT_FALSE(
681 root->current_frame_host()->GetLastCommittedOrigin().IsSameOriginWith(
682 root->child_at(0)->current_frame_host()->GetLastCommittedOrigin()));
683 EXPECT_EQ(root->current_origin().Serialize(), GetOriginFromRenderer(root));
684 EXPECT_EQ(root->child_at(0)->current_origin().Serialize(),
685 GetOriginFromRenderer(root->child_at(0)));
alexmosbc7eafa2014-12-06 01:38:09686
nick1466c842015-11-25 20:08:06687 // Parent-initiated about:blank navigation should inherit the parent's a.com
688 // origin.
689 NavigateIframeToURL(contents, "1-1-id", about_blank);
690 EXPECT_EQ(about_blank, root->child_at(0)->current_url());
Mike West800532c2021-10-14 09:26:52691 EXPECT_EQ(main_url.DeprecatedGetOriginAsURL().spec(),
nick1466c842015-11-25 20:08:06692 root->child_at(0)->current_origin().Serialize() + '/');
693 EXPECT_EQ(root->current_frame_host()->GetLastCommittedOrigin().Serialize(),
694 root->child_at(0)
695 ->current_frame_host()
696 ->GetLastCommittedOrigin()
697 .Serialize());
698 EXPECT_TRUE(
699 root->current_frame_host()->GetLastCommittedOrigin().IsSameOriginWith(
700 root->child_at(0)->current_frame_host()->GetLastCommittedOrigin()));
701 EXPECT_EQ(root->current_origin().Serialize(), GetOriginFromRenderer(root));
702 EXPECT_EQ(root->child_at(0)->current_origin().Serialize(),
703 GetOriginFromRenderer(root->child_at(0)));
alexmosbc7eafa2014-12-06 01:38:09704
705 GURL data_url("data:text/html,foo");
706 EXPECT_TRUE(NavigateToURL(shell(), data_url));
707
708 // Navigating to a data URL should set a unique origin. This is represented
709 // as "null" per RFC 6454.
nick1466c842015-11-25 20:08:06710 EXPECT_EQ("null", root->current_origin().Serialize());
Dave Tapuska327c06c92022-06-13 20:31:51711 EXPECT_TRUE(
712 contents->GetPrimaryMainFrame()->GetLastCommittedOrigin().opaque());
nick1466c842015-11-25 20:08:06713 EXPECT_EQ("null", GetOriginFromRenderer(root));
alexmosbc7eafa2014-12-06 01:38:09714
715 // Re-navigating to a normal URL should update the origin.
716 EXPECT_TRUE(NavigateToURL(shell(), main_url));
Mike West800532c2021-10-14 09:26:52717 EXPECT_EQ(main_url.DeprecatedGetOriginAsURL().spec(),
nick1466c842015-11-25 20:08:06718 root->current_origin().Serialize() + '/');
719 EXPECT_EQ(
Mike West800532c2021-10-14 09:26:52720 main_url.DeprecatedGetOriginAsURL().spec(),
Dave Tapuska327c06c92022-06-13 20:31:51721 contents->GetPrimaryMainFrame()->GetLastCommittedOrigin().Serialize() +
722 '/');
723 EXPECT_FALSE(
724 contents->GetPrimaryMainFrame()->GetLastCommittedOrigin().opaque());
nick1466c842015-11-25 20:08:06725 EXPECT_EQ(root->current_origin().Serialize(), GetOriginFromRenderer(root));
alexmosbc7eafa2014-12-06 01:38:09726}
727
nick9f34e2892016-01-12 21:01:07728// Tests a cross-origin navigation to a blob URL. The main frame initiates this
729// navigation on its grandchild. It should wind up in the main frame's process.
730IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, NavigateGrandchildToBlob) {
731 WebContents* contents = shell()->web_contents();
732 FrameTreeNode* root =
Carlos Caballero15caeeb2021-10-27 09:57:55733 static_cast<WebContentsImpl*>(contents)->GetPrimaryFrameTree().root();
nick9f34e2892016-01-12 21:01:07734
735 // First, snapshot the FrameTree for a normal A(B(A)) case where all frames
736 // are served over http. The blob test should result in the same structure.
737 EXPECT_TRUE(NavigateToURL(
738 shell(), embedded_test_server()->GetURL(
739 "a.com", "/cross_site_iframe_factory.html?a(b(a))")));
Fergal Daly79f44292020-12-01 02:30:48740 std::string reference_tree = DepictFrameTree(*root);
nick9f34e2892016-01-12 21:01:07741
742 GURL main_url(embedded_test_server()->GetURL(
743 "a.com", "/cross_site_iframe_factory.html?a(b(c))"));
744 EXPECT_TRUE(NavigateToURL(shell(), main_url));
745
746 // The root node will initiate the navigation; its grandchild node will be the
747 // target of the navigation.
748 FrameTreeNode* target = root->child_at(0)->child_at(0);
749
creisb2d5f1f32016-11-07 23:25:05750 RenderFrameDeletedObserver deleted_observer(target->current_frame_host());
Nick Carterb7e71312018-08-03 23:36:13751 std::string html =
752 "<html><body><div>This is blob content.</div>"
753 "<script>"
Philip Jägenstedt67302a22018-09-14 09:58:05754 "window.parent.parent.postMessage('HI', self.origin);"
Nick Carterb7e71312018-08-03 23:36:13755 "</script></body></html>";
756 std::string script = JsReplace(
757 "new Promise((resolve) => {"
758 " window.addEventListener('message', resolve, false);"
759 " var blob = new Blob([$1], {type: 'text/html'});"
760 " var blob_url = URL.createObjectURL(blob);"
761 " frames[0][0].location.href = blob_url;"
762 "}).then((event) => {"
nick9f34e2892016-01-12 21:01:07763 " document.body.appendChild(document.createTextNode(event.data));"
Nick Carterb7e71312018-08-03 23:36:13764 " return event.source.location.href;"
765 "});",
766 html);
767 std::string blob_url_string = EvalJs(root, script).ExtractString();
creisb2d5f1f32016-11-07 23:25:05768 // Wait for the RenderFrame to go away, if this will be cross-process.
769 if (AreAllSitesIsolatedForTesting())
770 deleted_observer.WaitUntilDeleted();
nick9f34e2892016-01-12 21:01:07771 EXPECT_EQ(GURL(blob_url_string), target->current_url());
772 EXPECT_EQ(url::kBlobScheme, target->current_url().scheme());
Chris Palmerab5e5b52018-09-28 19:19:30773 EXPECT_FALSE(target->current_origin().opaque());
nick9f34e2892016-01-12 21:01:07774 EXPECT_EQ("a.com", target->current_origin().host());
775 EXPECT_EQ(url::kHttpScheme, target->current_origin().scheme());
Nick Carterb7e71312018-08-03 23:36:13776 EXPECT_EQ("This is blob content.",
777 EvalJs(target, "document.body.children[0].innerHTML"));
Fergal Daly79f44292020-12-01 02:30:48778 EXPECT_EQ(reference_tree, DepictFrameTree(*root));
nick9f34e2892016-01-12 21:01:07779}
780
781IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, NavigateChildToAboutBlank) {
782 GURL main_url(embedded_test_server()->GetURL(
783 "a.com", "/cross_site_iframe_factory.html?a(b(c))"));
784 EXPECT_TRUE(NavigateToURL(shell(), main_url));
785 WebContentsImpl* contents =
786 static_cast<WebContentsImpl*>(shell()->web_contents());
787
788 // The leaf node (c.com) will be navigated. Its parent node (b.com) will
789 // initiate the navigation.
790 FrameTreeNode* target =
Carlos Caballero15caeeb2021-10-27 09:57:55791 contents->GetPrimaryFrameTree().root()->child_at(0)->child_at(0);
Harkiran Bolaria4eaae3072021-09-21 10:45:48792 RenderFrameHost* initiator_rfh = target->parent();
nick9f34e2892016-01-12 21:01:07793
794 // Give the target a name.
Nick Carterb7e71312018-08-03 23:36:13795 EXPECT_TRUE(ExecJs(target, "window.name = 'target';"));
nick9f34e2892016-01-12 21:01:07796
797 // Use window.open(about:blank), then poll the document for access.
Nick Carterb7e71312018-08-03 23:36:13798 EvalJsResult about_blank_origin = EvalJs(
Harkiran Bolaria4eaae3072021-09-21 10:45:48799 initiator_rfh,
Nick Carterb7e71312018-08-03 23:36:13800 "new Promise(resolve => {"
801 " var didNavigate = false;"
802 " var intervalID = setInterval(function() {"
803 " if (!didNavigate) {"
804 " didNavigate = true;"
805 " window.open('about:blank', 'target');"
806 " }"
807 " // Poll the document until it doesn't throw a SecurityError.\n"
808 " try {"
809 " frames[0].document.write('Hi from ' + document.domain);"
810 " } catch (e) { return; }"
811 " clearInterval(intervalID);"
Philip Jägenstedt67302a22018-09-14 09:58:05812 " resolve(frames[0].self.origin);"
Nick Carterb7e71312018-08-03 23:36:13813 " }, 16);"
814 "});");
815 EXPECT_EQ(target->current_origin(), about_blank_origin);
Nate Chapinab67b042021-05-04 21:00:51816 EXPECT_EQ(GURL(url::kAboutBlankURL), target->current_url());
817 EXPECT_EQ(url::kAboutScheme, target->current_url().scheme());
Chris Palmerab5e5b52018-09-28 19:19:30818 EXPECT_FALSE(target->current_origin().opaque());
nick9f34e2892016-01-12 21:01:07819 EXPECT_EQ("b.com", target->current_origin().host());
820 EXPECT_EQ(url::kHttpScheme, target->current_origin().scheme());
nick9f34e2892016-01-12 21:01:07821
Nick Carterb7e71312018-08-03 23:36:13822 EXPECT_EQ("Hi from b.com", EvalJs(target, "document.body.innerHTML"));
nick9f34e2892016-01-12 21:01:07823}
824
825// Nested iframes, three origins: A(B(C)). Frame A navigates C to about:blank
826// (via window.open). This should wind up in A's origin per the spec. Test fails
827// because of http://crbug.com/564292
828IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest,
829 DISABLED_NavigateGrandchildToAboutBlank) {
830 GURL main_url(embedded_test_server()->GetURL(
831 "a.com", "/cross_site_iframe_factory.html?a(b(c))"));
832 EXPECT_TRUE(NavigateToURL(shell(), main_url));
833 WebContentsImpl* contents =
834 static_cast<WebContentsImpl*>(shell()->web_contents());
835
836 // The leaf node (c.com) will be navigated. Its grandparent node (a.com) will
837 // initiate the navigation.
838 FrameTreeNode* target =
Carlos Caballero15caeeb2021-10-27 09:57:55839 contents->GetPrimaryFrameTree().root()->child_at(0)->child_at(0);
Harkiran Bolaria4eaae3072021-09-21 10:45:48840 RenderFrameHost* initiator_rfh = target->parent()->GetParent();
nick9f34e2892016-01-12 21:01:07841
842 // Give the target a name.
Nick Carterb7e71312018-08-03 23:36:13843 EXPECT_TRUE(ExecJs(target, "window.name = 'target';"));
nick9f34e2892016-01-12 21:01:07844
845 // Use window.open(about:blank), then poll the document for access.
Nick Carterb7e71312018-08-03 23:36:13846 EvalJsResult about_blank_origin =
Harkiran Bolaria4eaae3072021-09-21 10:45:48847 EvalJs(initiator_rfh,
Nick Carterb7e71312018-08-03 23:36:13848 "new Promise((resolve) => {"
849 " var didNavigate = false;"
850 " var intervalID = setInterval(() => {"
851 " if (!didNavigate) {"
852 " didNavigate = true;"
853 " window.open('about:blank', 'target');"
854 " }"
855 " // May raise a SecurityError, that's expected.\n"
856 " try {"
857 " frames[0][0].document.write('Hi from ' + document.domain);"
858 " } catch (e) { return; }"
859 " clearInterval(intervalID);"
Philip Jägenstedt67302a22018-09-14 09:58:05860 " resolve(frames[0][0].self.origin);"
Nick Carterb7e71312018-08-03 23:36:13861 " }, 16);"
862 "});");
863 EXPECT_EQ(target->current_origin(), about_blank_origin);
nick9f34e2892016-01-12 21:01:07864 EXPECT_EQ(GURL(url::kAboutBlankURL), target->current_url());
865 EXPECT_EQ(url::kAboutScheme, target->current_url().scheme());
Chris Palmerab5e5b52018-09-28 19:19:30866 EXPECT_FALSE(target->current_origin().opaque());
nick9f34e2892016-01-12 21:01:07867 EXPECT_EQ("a.com", target->current_origin().host());
868 EXPECT_EQ(url::kHttpScheme, target->current_origin().scheme());
nick9f34e2892016-01-12 21:01:07869
Nick Carterb7e71312018-08-03 23:36:13870 EXPECT_EQ("Hi from a.com", EvalJs(target, "document.body.innerHTML"));
nick9f34e2892016-01-12 21:01:07871}
872
Nasko Oskov039121012019-01-11 00:21:32873// Tests a cross-origin navigation to a data: URL. The main frame initiates this
874// navigation on its grandchild. It should wind up in the main frame's process
875// and have precursor origin of the main frame origin.
876IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, NavigateGrandchildToDataUrl) {
877 GURL main_url(embedded_test_server()->GetURL(
878 "a.com", "/cross_site_iframe_factory.html?a(b(c))"));
879 EXPECT_TRUE(NavigateToURL(shell(), main_url));
880 WebContentsImpl* contents =
881 static_cast<WebContentsImpl*>(shell()->web_contents());
882
883 // The leaf node (c.com) will be navigated. Its grandparent node (a.com) will
884 // initiate the navigation.
885 FrameTreeNode* target =
Carlos Caballero15caeeb2021-10-27 09:57:55886 contents->GetPrimaryFrameTree().root()->child_at(0)->child_at(0);
Harkiran Bolaria4eaae3072021-09-21 10:45:48887 RenderFrameHostImpl* initiator_rfh = target->parent()->GetParent();
Nasko Oskov039121012019-01-11 00:21:32888
889 // Give the target a name.
890 EXPECT_TRUE(ExecJs(target, "window.name = 'target';"));
891
892 // Navigate the target frame through the initiator frame.
893 {
894 TestFrameNavigationObserver observer(target);
Harkiran Bolaria4eaae3072021-09-21 10:45:48895 EXPECT_TRUE(ExecJs(initiator_rfh,
896 "window.open('data:text/html,content', 'target');"));
Nasko Oskov039121012019-01-11 00:21:32897 observer.Wait();
898 }
899
900 url::Origin original_target_origin =
901 target->current_frame_host()->GetLastCommittedOrigin();
902 EXPECT_TRUE(original_target_origin.opaque());
903 EXPECT_EQ(original_target_origin.GetTupleOrPrecursorTupleIfOpaque(),
904 url::SchemeHostPort(main_url));
905
906 // Navigate the grandchild frame again cross-process to foo.com, then
Rakina Zata Amnic7367852022-11-07 17:10:40907 // go back in session history. The frame should commit a new opaque origin,
908 // but it will still have the same precursor origin (the main frame origin).
Nasko Oskov039121012019-01-11 00:21:32909 {
910 TestFrameNavigationObserver observer(target);
911 EXPECT_TRUE(ExecJs(target, JsReplace("window.location = $1",
912 embedded_test_server()->GetURL(
913 "foo.com", "/title2.html"))));
914 observer.Wait();
915 }
916 EXPECT_NE(original_target_origin,
917 target->current_frame_host()->GetLastCommittedOrigin());
918 {
919 TestFrameNavigationObserver observer(target);
920 contents->GetController().GoBack();
921 observer.Wait();
922 }
923
924 url::Origin target_origin =
925 target->current_frame_host()->GetLastCommittedOrigin();
Rakina Zata Amnic7367852022-11-07 17:10:40926 EXPECT_NE(target_origin, original_target_origin);
Nasko Oskov039121012019-01-11 00:21:32927 EXPECT_TRUE(target_origin.opaque());
928 EXPECT_EQ(target_origin.GetTupleOrPrecursorTupleIfOpaque(),
Rakina Zata Amnic7367852022-11-07 17:10:40929 original_target_origin.GetTupleOrPrecursorTupleIfOpaque());
930 EXPECT_EQ(target_origin.GetTupleOrPrecursorTupleIfOpaque(),
Nasko Oskov039121012019-01-11 00:21:32931 url::SchemeHostPort(main_url));
Nasko Oskov039121012019-01-11 00:21:32932}
933
naskofaa01fb2016-04-30 01:04:17934// Ensures that iframe with srcdoc is always put in the same origin as its
935// parent frame.
936IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, ChildFrameWithSrcdoc) {
937 GURL main_url(embedded_test_server()->GetURL(
938 "a.com", "/cross_site_iframe_factory.html?a(b)"));
939 EXPECT_TRUE(NavigateToURL(shell(), main_url));
940 WebContentsImpl* contents =
941 static_cast<WebContentsImpl*>(shell()->web_contents());
Carlos Caballero15caeeb2021-10-27 09:57:55942 FrameTreeNode* root = contents->GetPrimaryFrameTree().root();
naskofaa01fb2016-04-30 01:04:17943 EXPECT_EQ(1U, root->child_count());
944
945 FrameTreeNode* child = root->child_at(0);
Philip Jägenstedt67302a22018-09-14 09:58:05946 std::string frame_origin = EvalJs(child, "self.origin;").ExtractString();
naskofaa01fb2016-04-30 01:04:17947 EXPECT_TRUE(
948 child->current_frame_host()->GetLastCommittedOrigin().IsSameOriginWith(
Daniel Cheng88186bd52017-10-20 08:14:46949 url::Origin::Create(GURL(frame_origin))));
naskofaa01fb2016-04-30 01:04:17950 EXPECT_FALSE(
951 root->current_frame_host()->GetLastCommittedOrigin().IsSameOriginWith(
Daniel Cheng88186bd52017-10-20 08:14:46952 url::Origin::Create(GURL(frame_origin))));
naskofaa01fb2016-04-30 01:04:17953
954 // Create a new iframe with srcdoc and add it to the main frame. It should
955 // be created in the same SiteInstance as the parent.
956 {
Aran Gilman37d11632019-10-08 23:07:15957 std::string script(
958 "var f = document.createElement('iframe');"
959 "f.srcdoc = 'some content';"
960 "document.body.appendChild(f)");
naskofaa01fb2016-04-30 01:04:17961 TestNavigationObserver observer(shell()->web_contents());
Nick Carterb7e71312018-08-03 23:36:13962 EXPECT_TRUE(ExecJs(root, script));
naskofaa01fb2016-04-30 01:04:17963 EXPECT_EQ(2U, root->child_count());
964 observer.Wait();
965
Lukasz Anforowicz42d3d07f2019-06-19 01:06:42966 EXPECT_TRUE(root->child_at(1)->current_url().IsAboutSrcdoc());
Peter Kastingeb8c3ce2021-08-20 04:39:35967 EvalJsResult js_result = EvalJs(root->child_at(1), "self.origin");
Mike West800532c2021-10-14 09:26:52968 EXPECT_EQ(root->current_frame_host()
969 ->GetLastCommittedURL()
970 .DeprecatedGetOriginAsURL(),
Peter Kastingeb8c3ce2021-08-20 04:39:35971 GURL(js_result.ExtractString()));
Mike West800532c2021-10-14 09:26:52972 EXPECT_NE(child->current_frame_host()
973 ->GetLastCommittedURL()
974 .DeprecatedGetOriginAsURL(),
Peter Kastingeb8c3ce2021-08-20 04:39:35975 GURL(js_result.ExtractString()));
naskofaa01fb2016-04-30 01:04:17976 }
977
978 // Set srcdoc on the existing cross-site frame. It should navigate the frame
979 // back to the origin of the parent.
980 {
Aran Gilman37d11632019-10-08 23:07:15981 std::string script(
982 "var f = document.getElementById('child-0');"
983 "f.srcdoc = 'some content';");
naskofaa01fb2016-04-30 01:04:17984 TestNavigationObserver observer(shell()->web_contents());
Nick Carterb7e71312018-08-03 23:36:13985 EXPECT_TRUE(ExecJs(root, script));
naskofaa01fb2016-04-30 01:04:17986 observer.Wait();
987
Lukasz Anforowicz42d3d07f2019-06-19 01:06:42988 EXPECT_TRUE(child->current_url().IsAboutSrcdoc());
Avi Drissmana3144c582021-10-14 17:00:26989 EXPECT_EQ(root->current_frame_host()->GetLastCommittedOrigin().Serialize(),
990 EvalJs(child, "self.origin"));
naskofaa01fb2016-04-30 01:04:17991 }
992}
993
Ian Clelland5cbaaf82017-11-27 22:00:03994// Ensure that sandbox flags are correctly set in the main frame when set by
995// Content-Security-Policy header.
996IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, SandboxFlagsSetForMainFrame) {
997 GURL main_url(embedded_test_server()->GetURL("/csp_sandboxed_frame.html"));
998 EXPECT_TRUE(NavigateToURL(shell(), main_url));
999
1000 // It is safe to obtain the root frame tree node here, as it doesn't change.
1001 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:551002 ->GetPrimaryFrameTree()
1003 .root();
Ian Clelland5cbaaf82017-11-27 22:00:031004
1005 // Verify that sandbox flags are set properly for the root FrameTreeNode and
1006 // RenderFrameHost. Root frame is sandboxed with "allow-scripts".
arthursonzognib93a4472020-04-10 07:38:001007 EXPECT_EQ(network::mojom::WebSandboxFlags::kNone,
Ian Clelland5cbaaf82017-11-27 22:00:031008 root->effective_frame_policy().sandbox_flags);
arthursonzognib93a4472020-04-10 07:38:001009 EXPECT_EQ(network::mojom::WebSandboxFlags::kAll &
1010 ~network::mojom::WebSandboxFlags::kScripts &
1011 ~network::mojom::WebSandboxFlags::kAutomaticFeatures,
Ian Clelland5cbaaf82017-11-27 22:00:031012 root->active_sandbox_flags());
1013 EXPECT_EQ(root->active_sandbox_flags(),
1014 root->current_frame_host()->active_sandbox_flags());
1015
1016 // Verify that child frames inherit sandbox flags from the root. First frame
1017 // has no explicitly set flags of its own, and should inherit those from the
1018 // root. Second frame is completely sandboxed.
arthursonzognib93a4472020-04-10 07:38:001019 EXPECT_EQ(network::mojom::WebSandboxFlags::kAll &
1020 ~network::mojom::WebSandboxFlags::kScripts &
1021 ~network::mojom::WebSandboxFlags::kAutomaticFeatures,
Ian Clelland5cbaaf82017-11-27 22:00:031022 root->child_at(0)->effective_frame_policy().sandbox_flags);
arthursonzognib93a4472020-04-10 07:38:001023 EXPECT_EQ(network::mojom::WebSandboxFlags::kAll &
1024 ~network::mojom::WebSandboxFlags::kScripts &
1025 ~network::mojom::WebSandboxFlags::kAutomaticFeatures,
Ian Clelland5cbaaf82017-11-27 22:00:031026 root->child_at(0)->active_sandbox_flags());
1027 EXPECT_EQ(root->child_at(0)->active_sandbox_flags(),
1028 root->child_at(0)->current_frame_host()->active_sandbox_flags());
arthursonzognib93a4472020-04-10 07:38:001029 EXPECT_EQ(network::mojom::WebSandboxFlags::kAll,
Ian Clelland5cbaaf82017-11-27 22:00:031030 root->child_at(1)->effective_frame_policy().sandbox_flags);
arthursonzognib93a4472020-04-10 07:38:001031 EXPECT_EQ(network::mojom::WebSandboxFlags::kAll,
Ian Clelland5cbaaf82017-11-27 22:00:031032 root->child_at(1)->active_sandbox_flags());
1033 EXPECT_EQ(root->child_at(1)->active_sandbox_flags(),
1034 root->child_at(1)->current_frame_host()->active_sandbox_flags());
1035
1036 // Navigating the main frame to a different URL should clear sandbox flags.
1037 GURL unsandboxed_url(embedded_test_server()->GetURL("/title1.html"));
Lukasz Anforowicz69c25dfd2020-11-12 21:50:201038 EXPECT_TRUE(NavigateToURLFromRenderer(root, unsandboxed_url));
Ian Clelland5cbaaf82017-11-27 22:00:031039
1040 // Verify that sandbox flags are cleared properly for the root FrameTreeNode
1041 // and RenderFrameHost.
arthursonzognib93a4472020-04-10 07:38:001042 EXPECT_EQ(network::mojom::WebSandboxFlags::kNone,
Ian Clelland5cbaaf82017-11-27 22:00:031043 root->effective_frame_policy().sandbox_flags);
arthursonzognib93a4472020-04-10 07:38:001044 EXPECT_EQ(network::mojom::WebSandboxFlags::kNone,
1045 root->active_sandbox_flags());
1046 EXPECT_EQ(network::mojom::WebSandboxFlags::kNone,
Ian Clelland5cbaaf82017-11-27 22:00:031047 root->current_frame_host()->active_sandbox_flags());
1048}
1049
alexmosf832a2f2015-01-27 22:44:031050// Ensure that sandbox flags are correctly set when child frames are created.
1051IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, SandboxFlagsSetForChildFrames) {
1052 GURL main_url(embedded_test_server()->GetURL("/sandboxed_frames.html"));
1053 EXPECT_TRUE(NavigateToURL(shell(), main_url));
1054
1055 // It is safe to obtain the root frame tree node here, as it doesn't change.
1056 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:551057 ->GetPrimaryFrameTree()
1058 .root();
alexmosf832a2f2015-01-27 22:44:031059
1060 // Verify that sandbox flags are set properly for all FrameTreeNodes.
1061 // First frame is completely sandboxed; second frame uses "allow-scripts",
1062 // which resets both SandboxFlags::Scripts and
1063 // SandboxFlags::AutomaticFeatures bits per blink::parseSandboxPolicy(), and
1064 // third frame has "allow-scripts allow-same-origin".
arthursonzognib93a4472020-04-10 07:38:001065 EXPECT_EQ(network::mojom::WebSandboxFlags::kNone,
Ian Clellandcdc4f312017-10-13 22:24:121066 root->effective_frame_policy().sandbox_flags);
arthursonzognib93a4472020-04-10 07:38:001067 EXPECT_EQ(network::mojom::WebSandboxFlags::kAll,
Ian Clellandcdc4f312017-10-13 22:24:121068 root->child_at(0)->effective_frame_policy().sandbox_flags);
arthursonzognib93a4472020-04-10 07:38:001069 EXPECT_EQ(network::mojom::WebSandboxFlags::kAll &
1070 ~network::mojom::WebSandboxFlags::kScripts &
1071 ~network::mojom::WebSandboxFlags::kAutomaticFeatures,
Ian Clellandcdc4f312017-10-13 22:24:121072 root->child_at(1)->effective_frame_policy().sandbox_flags);
arthursonzognib93a4472020-04-10 07:38:001073 EXPECT_EQ(network::mojom::WebSandboxFlags::kAll &
1074 ~network::mojom::WebSandboxFlags::kScripts &
1075 ~network::mojom::WebSandboxFlags::kAutomaticFeatures &
1076 ~network::mojom::WebSandboxFlags::kOrigin,
Ian Clellandcdc4f312017-10-13 22:24:121077 root->child_at(2)->effective_frame_policy().sandbox_flags);
alexmosf832a2f2015-01-27 22:44:031078
1079 // Sandboxed frames should set a unique origin unless they have the
1080 // "allow-same-origin" directive.
alexmos6e940102016-01-19 22:47:251081 EXPECT_EQ("null", root->child_at(0)->current_origin().Serialize());
1082 EXPECT_EQ("null", root->child_at(1)->current_origin().Serialize());
Mike West800532c2021-10-14 09:26:521083 EXPECT_EQ(main_url.DeprecatedGetOriginAsURL().spec(),
alexmos6e940102016-01-19 22:47:251084 root->child_at(2)->current_origin().Serialize() + "/");
alexmosf832a2f2015-01-27 22:44:031085
1086 // Navigating to a different URL should not clear sandbox flags.
1087 GURL frame_url(embedded_test_server()->GetURL("/title1.html"));
1088 NavigateFrameToURL(root->child_at(0), frame_url);
arthursonzognib93a4472020-04-10 07:38:001089 EXPECT_EQ(network::mojom::WebSandboxFlags::kAll,
Ian Clellandcdc4f312017-10-13 22:24:121090 root->child_at(0)->effective_frame_policy().sandbox_flags);
alexmosf832a2f2015-01-27 22:44:031091}
1092
Ian Clelland5cbaaf82017-11-27 22:00:031093// Ensure that sandbox flags are correctly set in the child frames when set by
1094// Content-Security-Policy header, and in combination with the sandbox iframe
1095// attribute.
1096IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest,
1097 SandboxFlagsSetByCSPForChildFrames) {
1098 GURL main_url(embedded_test_server()->GetURL("/sandboxed_frames_csp.html"));
1099 EXPECT_TRUE(NavigateToURL(shell(), main_url));
1100
1101 // It is safe to obtain the root frame tree node here, as it doesn't change.
1102 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:551103 ->GetPrimaryFrameTree()
1104 .root();
Ian Clelland5cbaaf82017-11-27 22:00:031105
1106 // Verify that sandbox flags are set properly for all FrameTreeNodes.
1107 // First frame has no iframe sandbox flags, but the framed document is served
1108 // with a CSP header which sets "allow-scripts", "allow-popups" and
1109 // "allow-pointer-lock".
1110 // Second frame is sandboxed with "allow-scripts", "allow-pointer-lock" and
1111 // "allow-orientation-lock", and the framed document is also served with a CSP
1112 // header which uses "allow-popups" and "allow-pointer-lock". The resulting
1113 // sandbox for the frame should only have "allow-pointer-lock".
arthursonzognib93a4472020-04-10 07:38:001114 EXPECT_EQ(network::mojom::WebSandboxFlags::kNone,
Ian Clelland5cbaaf82017-11-27 22:00:031115 root->effective_frame_policy().sandbox_flags);
arthursonzognib93a4472020-04-10 07:38:001116 EXPECT_EQ(network::mojom::WebSandboxFlags::kNone,
1117 root->active_sandbox_flags());
Ian Clelland5cbaaf82017-11-27 22:00:031118 EXPECT_EQ(root->active_sandbox_flags(),
1119 root->current_frame_host()->active_sandbox_flags());
arthursonzognib93a4472020-04-10 07:38:001120 EXPECT_EQ(network::mojom::WebSandboxFlags::kNone,
Ian Clelland5cbaaf82017-11-27 22:00:031121 root->child_at(0)->effective_frame_policy().sandbox_flags);
Arthur Sonzogni4ba3104f2022-03-09 09:04:391122 EXPECT_EQ(
1123 network::mojom::WebSandboxFlags::kAll &
1124 ~network::mojom::WebSandboxFlags::kAutomaticFeatures &
1125 ~network::mojom::WebSandboxFlags::kPointerLock &
1126 ~network::mojom::WebSandboxFlags::kPopups &
1127 ~network::mojom::WebSandboxFlags::kScripts &
1128 ~network::mojom::WebSandboxFlags::kTopNavigationToCustomProtocols,
1129 root->child_at(0)->active_sandbox_flags());
Ian Clelland5cbaaf82017-11-27 22:00:031130 EXPECT_EQ(root->child_at(0)->active_sandbox_flags(),
1131 root->child_at(0)->current_frame_host()->active_sandbox_flags());
arthursonzognib93a4472020-04-10 07:38:001132 EXPECT_EQ(network::mojom::WebSandboxFlags::kAll &
1133 ~network::mojom::WebSandboxFlags::kScripts &
1134 ~network::mojom::WebSandboxFlags::kAutomaticFeatures &
1135 ~network::mojom::WebSandboxFlags::kPointerLock &
1136 ~network::mojom::WebSandboxFlags::kOrientationLock,
Ian Clelland5cbaaf82017-11-27 22:00:031137 root->child_at(1)->effective_frame_policy().sandbox_flags);
arthursonzognib93a4472020-04-10 07:38:001138 EXPECT_EQ(network::mojom::WebSandboxFlags::kAll &
1139 ~network::mojom::WebSandboxFlags::kScripts &
1140 ~network::mojom::WebSandboxFlags::kAutomaticFeatures &
1141 ~network::mojom::WebSandboxFlags::kPointerLock,
Ian Clelland5cbaaf82017-11-27 22:00:031142 root->child_at(1)->active_sandbox_flags());
1143 EXPECT_EQ(root->child_at(1)->active_sandbox_flags(),
1144 root->child_at(1)->current_frame_host()->active_sandbox_flags());
1145
1146 // Navigating to a different URL *should* clear CSP-set sandbox flags, but
1147 // should retain those flags set by the frame owner.
1148 GURL frame_url(embedded_test_server()->GetURL("/title1.html"));
1149
Lukasz Anforowicz69c25dfd2020-11-12 21:50:201150 EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(0), frame_url));
arthursonzognib93a4472020-04-10 07:38:001151 EXPECT_EQ(network::mojom::WebSandboxFlags::kNone,
Ian Clelland5cbaaf82017-11-27 22:00:031152 root->child_at(0)->effective_frame_policy().sandbox_flags);
arthursonzognib93a4472020-04-10 07:38:001153 EXPECT_EQ(network::mojom::WebSandboxFlags::kNone,
Ian Clelland5cbaaf82017-11-27 22:00:031154 root->child_at(0)->active_sandbox_flags());
1155 EXPECT_EQ(root->child_at(0)->active_sandbox_flags(),
1156 root->child_at(0)->current_frame_host()->active_sandbox_flags());
1157
Lukasz Anforowicz69c25dfd2020-11-12 21:50:201158 EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(1), frame_url));
arthursonzognib93a4472020-04-10 07:38:001159 EXPECT_EQ(network::mojom::WebSandboxFlags::kAll &
1160 ~network::mojom::WebSandboxFlags::kScripts &
1161 ~network::mojom::WebSandboxFlags::kAutomaticFeatures &
1162 ~network::mojom::WebSandboxFlags::kPointerLock &
1163 ~network::mojom::WebSandboxFlags::kOrientationLock,
Ian Clelland5cbaaf82017-11-27 22:00:031164 root->child_at(1)->effective_frame_policy().sandbox_flags);
arthursonzognib93a4472020-04-10 07:38:001165 EXPECT_EQ(network::mojom::WebSandboxFlags::kAll &
1166 ~network::mojom::WebSandboxFlags::kScripts &
1167 ~network::mojom::WebSandboxFlags::kAutomaticFeatures &
1168 ~network::mojom::WebSandboxFlags::kPointerLock &
1169 ~network::mojom::WebSandboxFlags::kOrientationLock,
Ian Clelland5cbaaf82017-11-27 22:00:031170 root->child_at(1)->active_sandbox_flags());
1171 EXPECT_EQ(root->child_at(1)->active_sandbox_flags(),
1172 root->child_at(1)->current_frame_host()->active_sandbox_flags());
1173}
1174
alexmose201c7cd2015-06-10 17:14:211175// Ensure that a popup opened from a subframe sets its opener to the subframe's
1176// FrameTreeNode, and that the opener is cleared if the subframe is destroyed.
1177IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, SubframeOpenerSetForNewWindow) {
1178 GURL main_url(embedded_test_server()->GetURL("/frame_tree/top.html"));
1179 EXPECT_TRUE(NavigateToURL(shell(), main_url));
1180
1181 // It is safe to obtain the root frame tree node here, as it doesn't change.
1182 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:551183 ->GetPrimaryFrameTree()
1184 .root();
alexmose201c7cd2015-06-10 17:14:211185
1186 // Open a new window from a subframe.
1187 ShellAddedObserver new_shell_observer;
1188 GURL popup_url(embedded_test_server()->GetURL("foo.com", "/title1.html"));
Nick Carterb7e71312018-08-03 23:36:131189 EXPECT_TRUE(
1190 ExecJs(root->child_at(0), JsReplace("window.open($1);", popup_url)));
alexmose201c7cd2015-06-10 17:14:211191 Shell* new_shell = new_shell_observer.GetShell();
1192 WebContents* new_contents = new_shell->web_contents();
Fergal Dalyf0522332020-07-18 06:09:461193 EXPECT_TRUE(WaitForLoadStop(new_contents));
alexmose201c7cd2015-06-10 17:14:211194
1195 // Check that the new window's opener points to the correct subframe on
1196 // original window.
1197 FrameTreeNode* popup_root =
Carlos Caballero15caeeb2021-10-27 09:57:551198 static_cast<WebContentsImpl*>(new_contents)->GetPrimaryFrameTree().root();
alexmose201c7cd2015-06-10 17:14:211199 EXPECT_EQ(root->child_at(0), popup_root->opener());
1200
1201 // Close the original window. This should clear the new window's opener.
1202 shell()->Close();
1203 EXPECT_EQ(nullptr, popup_root->opener());
1204}
1205
Shivani Sharmac4f561582018-11-15 15:58:391206// Tests that the user activation bits get cleared when a same-site document is
1207// installed in the frame.
1208IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest,
1209 ClearUserActivationForNewDocument) {
1210 GURL main_url(embedded_test_server()->GetURL("/frame_tree/top.html"));
1211 EXPECT_TRUE(NavigateToURL(shell(), main_url));
1212
1213 // It is safe to obtain the root frame tree node here, as it doesn't change.
1214 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:551215 ->GetPrimaryFrameTree()
1216 .root();
Shivani Sharmac4f561582018-11-15 15:58:391217
Mustaq Ahmed4baa9a6e82019-12-20 23:43:461218 EXPECT_FALSE(root->HasStickyUserActivation());
Shivani Sharmac4f561582018-11-15 15:58:391219 EXPECT_FALSE(root->HasTransientUserActivation());
1220
1221 // Set the user activation bits.
1222 root->UpdateUserActivationState(
Mustaq Ahmeddc195e5b2020-08-04 18:45:111223 blink::mojom::UserActivationUpdateType::kNotifyActivation,
1224 blink::mojom::UserActivationNotificationType::kTest);
Mustaq Ahmed4baa9a6e82019-12-20 23:43:461225 EXPECT_TRUE(root->HasStickyUserActivation());
Shivani Sharmac4f561582018-11-15 15:58:391226 EXPECT_TRUE(root->HasTransientUserActivation());
1227
1228 // Install a new same-site document to check the clearing of user activation
1229 // bits.
1230 GURL url(embedded_test_server()->GetURL("/title1.html"));
1231 EXPECT_TRUE(NavigateToURL(shell(), url));
1232
Mustaq Ahmed4baa9a6e82019-12-20 23:43:461233 EXPECT_FALSE(root->HasStickyUserActivation());
Shivani Sharmac4f561582018-11-15 15:58:391234 EXPECT_FALSE(root->HasTransientUserActivation());
1235}
1236
[email protected]82307f6b2014-08-07 03:30:121237class CrossProcessFrameTreeBrowserTest : public ContentBrowserTest {
1238 public:
Fergal Dalya1d569972021-03-16 03:24:531239 CrossProcessFrameTreeBrowserTest() = default;
[email protected]82307f6b2014-08-07 03:30:121240
Peter Boström9b036532021-10-28 23:37:281241 CrossProcessFrameTreeBrowserTest(const CrossProcessFrameTreeBrowserTest&) =
1242 delete;
1243 CrossProcessFrameTreeBrowserTest& operator=(
1244 const CrossProcessFrameTreeBrowserTest&) = delete;
1245
dchengc2282aa2014-10-21 12:07:581246 void SetUpCommandLine(base::CommandLine* command_line) override {
nickd30fd962015-07-27 21:51:081247 IsolateAllSitesForTesting(command_line);
[email protected]82307f6b2014-08-07 03:30:121248 }
1249
alexmos478dcbb2014-12-10 21:24:461250 void SetUpOnMainThread() override {
1251 host_resolver()->AddRule("*", "127.0.0.1");
alexmos478dcbb2014-12-10 21:24:461252 SetupCrossSiteRedirector(embedded_test_server());
martijn12d2dad2016-11-11 10:59:271253 ASSERT_TRUE(embedded_test_server()->Start());
alexmos478dcbb2014-12-10 21:24:461254 }
[email protected]82307f6b2014-08-07 03:30:121255};
1256
1257// Ensure that we can complete a cross-process subframe navigation.
[email protected]82307f6b2014-08-07 03:30:121258IN_PROC_BROWSER_TEST_F(CrossProcessFrameTreeBrowserTest,
nasko83fe5dc2015-07-01 16:34:191259 CreateCrossProcessSubframeProxies) {
alexmos478dcbb2014-12-10 21:24:461260 GURL main_url(embedded_test_server()->GetURL("/site_per_process_main.html"));
Alex Moshchukaa95adf52019-08-13 00:02:021261 EXPECT_TRUE(NavigateToURL(shell(), main_url));
[email protected]82307f6b2014-08-07 03:30:121262
1263 // It is safe to obtain the root frame tree node here, as it doesn't change.
1264 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:551265 ->GetPrimaryFrameTree()
1266 .root();
[email protected]82307f6b2014-08-07 03:30:121267
[email protected]14522ce42014-08-08 18:56:161268 // There should not be a proxy for the root's own SiteInstance.
Sharon Yang7424bda2021-11-04 20:27:431269 SiteInstanceImpl* root_instance =
1270 root->current_frame_host()->GetSiteInstance();
Harkiran Bolariad22a1dca2022-02-22 17:01:121271 EXPECT_FALSE(root->current_frame_host()
1272 ->browsing_context_state()
1273 ->GetRenderFrameProxyHost(root_instance->group()));
[email protected]14522ce42014-08-08 18:56:161274
[email protected]82307f6b2014-08-07 03:30:121275 // Load same-site page into iframe.
alexmos478dcbb2014-12-10 21:24:461276 GURL http_url(embedded_test_server()->GetURL("/title1.html"));
Lukasz Anforowicz69c25dfd2020-11-12 21:50:201277 EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(0), http_url));
[email protected]82307f6b2014-08-07 03:30:121278
[email protected]82307f6b2014-08-07 03:30:121279 // Load cross-site page into iframe.
alexmos478dcbb2014-12-10 21:24:461280 GURL cross_site_url(
1281 embedded_test_server()->GetURL("foo.com", "/title2.html"));
Lukasz Anforowicz69c25dfd2020-11-12 21:50:201282 EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(0), cross_site_url));
[email protected]82307f6b2014-08-07 03:30:121283
1284 // Ensure that we have created a new process for the subframe.
naskoe6edde32014-10-17 15:36:481285 ASSERT_EQ(2U, root->child_count());
[email protected]82307f6b2014-08-07 03:30:121286 FrameTreeNode* child = root->child_at(0);
Sharon Yang7424bda2021-11-04 20:27:431287 SiteInstanceImpl* child_instance =
1288 child->current_frame_host()->GetSiteInstance();
[email protected]82307f6b2014-08-07 03:30:121289 RenderViewHost* rvh = child->current_frame_host()->render_view_host();
1290 RenderProcessHost* rph = child->current_frame_host()->GetProcess();
1291
Dave Tapuska327c06c92022-06-13 20:31:511292 EXPECT_NE(shell()->web_contents()->GetPrimaryMainFrame()->GetRenderViewHost(),
1293 rvh);
[email protected]14522ce42014-08-08 18:56:161294 EXPECT_NE(shell()->web_contents()->GetSiteInstance(), child_instance);
Dave Tapuska327c06c92022-06-13 20:31:511295 EXPECT_NE(shell()->web_contents()->GetPrimaryMainFrame()->GetProcess(), rph);
[email protected]82307f6b2014-08-07 03:30:121296
1297 // Ensure that the root node has a proxy for the child node's SiteInstance.
Harkiran Bolariad22a1dca2022-02-22 17:01:121298 EXPECT_TRUE(root->current_frame_host()
1299 ->browsing_context_state()
1300 ->GetRenderFrameProxyHost(child_instance->group()));
[email protected]82307f6b2014-08-07 03:30:121301
1302 // Also ensure that the child has a proxy for the root node's SiteInstance.
Harkiran Bolariad22a1dca2022-02-22 17:01:121303 EXPECT_TRUE(child->current_frame_host()
1304 ->browsing_context_state()
1305 ->GetRenderFrameProxyHost(root_instance->group()));
[email protected]14522ce42014-08-08 18:56:161306
1307 // The nodes should not have proxies for their own SiteInstance.
Harkiran Bolariad22a1dca2022-02-22 17:01:121308 EXPECT_FALSE(root->current_frame_host()
1309 ->browsing_context_state()
1310 ->GetRenderFrameProxyHost(root_instance->group()));
1311 EXPECT_FALSE(child->current_frame_host()
1312 ->browsing_context_state()
1313 ->GetRenderFrameProxyHost(child_instance->group()));
creise42f2a52014-09-18 18:14:571314
1315 // Ensure that the RenderViews and RenderFrames are all live.
1316 EXPECT_TRUE(
1317 root->current_frame_host()->render_view_host()->IsRenderViewLive());
1318 EXPECT_TRUE(
1319 child->current_frame_host()->render_view_host()->IsRenderViewLive());
1320 EXPECT_TRUE(root->current_frame_host()->IsRenderFrameLive());
1321 EXPECT_TRUE(root->child_at(0)->current_frame_host()->IsRenderFrameLive());
[email protected]82307f6b2014-08-07 03:30:121322}
1323
alexmosbc7eafa2014-12-06 01:38:091324IN_PROC_BROWSER_TEST_F(CrossProcessFrameTreeBrowserTest,
Nasko Oskov039121012019-01-11 00:21:321325 OriginSetOnNavigations) {
alexmos478dcbb2014-12-10 21:24:461326 GURL main_url(embedded_test_server()->GetURL("/site_per_process_main.html"));
alexmosbc7eafa2014-12-06 01:38:091327 EXPECT_TRUE(NavigateToURL(shell(), main_url));
1328
1329 // It is safe to obtain the root frame tree node here, as it doesn't change.
1330 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:551331 ->GetPrimaryFrameTree()
1332 .root();
alexmosbc7eafa2014-12-06 01:38:091333
creisabdd2bd62015-11-21 01:14:581334 EXPECT_EQ(root->current_origin().Serialize() + '/',
Mike West800532c2021-10-14 09:26:521335 main_url.DeprecatedGetOriginAsURL().spec());
alexmosbc7eafa2014-12-06 01:38:091336
1337 // First frame is an about:blank frame. Check that its origin is correctly
1338 // inherited from the parent.
creisabdd2bd62015-11-21 01:14:581339 EXPECT_EQ(root->child_at(0)->current_origin().Serialize() + '/',
Mike West800532c2021-10-14 09:26:521340 main_url.DeprecatedGetOriginAsURL().spec());
alexmosbc7eafa2014-12-06 01:38:091341
1342 // Second frame loads a same-site page. Its origin should also be the same
1343 // as the parent.
creisabdd2bd62015-11-21 01:14:581344 EXPECT_EQ(root->child_at(1)->current_origin().Serialize() + '/',
Mike West800532c2021-10-14 09:26:521345 main_url.DeprecatedGetOriginAsURL().spec());
alexmosbc7eafa2014-12-06 01:38:091346
alexmosbc7eafa2014-12-06 01:38:091347 // Load cross-site page into the first frame.
alexmos478dcbb2014-12-10 21:24:461348 GURL cross_site_url(
1349 embedded_test_server()->GetURL("foo.com", "/title2.html"));
Lukasz Anforowicz69c25dfd2020-11-12 21:50:201350 EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(0), cross_site_url));
alexmosbc7eafa2014-12-06 01:38:091351
creisabdd2bd62015-11-21 01:14:581352 EXPECT_EQ(root->child_at(0)->current_origin().Serialize() + '/',
Mike West800532c2021-10-14 09:26:521353 cross_site_url.DeprecatedGetOriginAsURL().spec());
alexmosbc7eafa2014-12-06 01:38:091354
1355 // The root's origin shouldn't have changed.
creisabdd2bd62015-11-21 01:14:581356 EXPECT_EQ(root->current_origin().Serialize() + '/',
Mike West800532c2021-10-14 09:26:521357 main_url.DeprecatedGetOriginAsURL().spec());
alexmosbc7eafa2014-12-06 01:38:091358
Nasko Oskov039121012019-01-11 00:21:321359 {
1360 GURL data_url("data:text/html,foo");
1361 TestNavigationObserver observer(shell()->web_contents());
1362 EXPECT_TRUE(
1363 ExecJs(root->child_at(1), JsReplace("window.location = $1", data_url)));
1364 observer.Wait();
1365 }
alexmosbc7eafa2014-12-06 01:38:091366
1367 // Navigating to a data URL should set a unique origin. This is represented
Nasko Oskov039121012019-01-11 00:21:321368 // as "null" per RFC 6454. A frame navigating itself to a data: URL does not
1369 // require a process transfer, but should retain the original origin
1370 // as its precursor.
creisabdd2bd62015-11-21 01:14:581371 EXPECT_EQ(root->child_at(1)->current_origin().Serialize(), "null");
Nasko Oskov039121012019-01-11 00:21:321372 EXPECT_TRUE(root->child_at(1)->current_origin().opaque());
1373 ASSERT_EQ(
1374 url::SchemeHostPort(main_url),
1375 root->child_at(1)->current_origin().GetTupleOrPrecursorTupleIfOpaque())
1376 << "Expected the precursor origin to be preserved; should be the "
1377 "initiator of a data: navigation.";
1378
1379 // Adding an <iframe sandbox srcdoc=> frame should result in a unique origin
1380 // that is different-origin from its data: URL parent.
1381 {
1382 TestNavigationObserver observer(shell()->web_contents());
1383
1384 ASSERT_EQ(0U, root->child_at(1)->child_count());
1385 EXPECT_TRUE(
1386 ExecJs(root->child_at(1), JsReplace(
1387 R"(
1388 var iframe = document.createElement('iframe');
1389 iframe.setAttribute('sandbox', 'allow-scripts');
1390 iframe.srcdoc = $1;
1391 document.body.appendChild(iframe);
1392 )",
1393 "<html><body>This sandboxed doc should "
1394 "be different-origin.</body></html>")));
1395 observer.Wait();
1396 ASSERT_EQ(1U, root->child_at(1)->child_count());
1397 }
1398
1399 url::Origin root_origin = root->current_origin();
1400 url::Origin child_1 = root->child_at(1)->current_origin();
1401 url::Origin child_1_0 = root->child_at(1)->child_at(0)->current_origin();
1402 EXPECT_FALSE(root_origin.opaque());
1403 EXPECT_TRUE(child_1.opaque());
1404 EXPECT_TRUE(child_1_0.opaque());
1405 EXPECT_NE(child_1, child_1_0);
1406 EXPECT_EQ(url::SchemeHostPort(main_url),
1407 root_origin.GetTupleOrPrecursorTupleIfOpaque());
1408 EXPECT_EQ(url::SchemeHostPort(main_url),
1409 child_1.GetTupleOrPrecursorTupleIfOpaque());
1410 EXPECT_EQ(url::SchemeHostPort(main_url),
1411 child_1_0.GetTupleOrPrecursorTupleIfOpaque());
1412
1413 {
1414 TestNavigationObserver observer(shell()->web_contents());
1415
1416 ASSERT_EQ(1U, root->child_at(1)->child_count());
1417 EXPECT_TRUE(
1418 ExecJs(root->child_at(1), JsReplace(
1419 R"(
1420 var iframe = document.createElement('iframe');
1421 iframe.srcdoc = $1;
1422 document.body.appendChild(iframe);
1423 )",
1424 "<html><body>This srcdoc document should "
1425 "be same-origin.</body></html>")));
1426 observer.Wait();
1427 ASSERT_EQ(2U, root->child_at(1)->child_count());
1428 }
1429 EXPECT_EQ(root_origin, root->current_origin());
1430 EXPECT_EQ(child_1, root->child_at(1)->current_origin());
1431 EXPECT_EQ(child_1_0, root->child_at(1)->child_at(0)->current_origin());
1432 url::Origin child_1_1 = root->child_at(1)->child_at(1)->current_origin();
1433 EXPECT_EQ(child_1, child_1_1);
1434 EXPECT_NE(child_1_0, child_1_1);
1435
1436 {
1437 TestNavigationObserver observer(shell()->web_contents());
1438
1439 ASSERT_EQ(2U, root->child_at(1)->child_count());
1440 EXPECT_TRUE(
1441 ExecJs(root->child_at(1), JsReplace(
1442 R"(
1443 var iframe = document.createElement('iframe');
1444 iframe.src = 'data:text/html;base64,' + btoa($1);
1445 document.body.appendChild(iframe);
1446 )",
1447 "<html><body>This data: doc should be "
1448 "different-origin.</body></html>")));
1449 observer.Wait();
1450 ASSERT_EQ(3U, root->child_at(1)->child_count());
1451 }
1452 EXPECT_EQ(root_origin, root->current_origin());
1453 EXPECT_EQ(child_1, root->child_at(1)->current_origin());
1454 EXPECT_EQ(child_1_0, root->child_at(1)->child_at(0)->current_origin());
1455 EXPECT_EQ(child_1_1, root->child_at(1)->child_at(1)->current_origin());
1456 url::Origin child_1_2 = root->child_at(1)->child_at(2)->current_origin();
1457 EXPECT_NE(child_1, child_1_2);
1458 EXPECT_NE(child_1_0, child_1_2);
1459 EXPECT_NE(child_1_1, child_1_2);
1460 EXPECT_EQ(url::SchemeHostPort(main_url),
1461 child_1_2.GetTupleOrPrecursorTupleIfOpaque());
1462
1463 // If the parent navigates its child to a data URL, it should transfer
1464 // to the parent's process, and the precursor origin should track the
1465 // parent's origin.
1466 {
1467 GURL data_url("data:text/html,foo2");
1468 TestNavigationObserver observer(shell()->web_contents());
1469 EXPECT_TRUE(ExecJs(root, JsReplace("frames[0].location = $1", data_url)));
1470 observer.Wait();
1471 EXPECT_EQ(data_url, root->child_at(0)->current_url());
1472 }
1473
1474 EXPECT_EQ(root->child_at(0)->current_origin().Serialize(), "null");
1475 EXPECT_TRUE(root->child_at(0)->current_origin().opaque());
1476 EXPECT_EQ(
1477 url::SchemeHostPort(main_url),
1478 root->child_at(0)->current_origin().GetTupleOrPrecursorTupleIfOpaque());
1479 EXPECT_EQ(root->current_frame_host()->GetProcess(),
1480 root->child_at(0)->current_frame_host()->GetProcess());
1481}
1482
1483// Test to verify that a blob: URL that is created by a unique opaque origin
1484// will correctly set the origin_to_commit on a session history navigation.
1485IN_PROC_BROWSER_TEST_F(CrossProcessFrameTreeBrowserTest,
1486 OriginForBlobUrlsFromUniqueOpaqueOrigin) {
1487 // Start off with a navigation to data: URL in the main frame. It should
1488 // result in a unique opaque origin without any precursor information.
1489 GURL data_url("data:text/html,foo<iframe id='child' src='" +
1490 embedded_test_server()->GetURL("/title1.html").spec() +
1491 "'></iframe>");
1492 EXPECT_TRUE(NavigateToURL(shell(), data_url));
1493
1494 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:551495 ->GetPrimaryFrameTree()
1496 .root();
Nasko Oskov039121012019-01-11 00:21:321497 EXPECT_TRUE(root->current_origin().opaque());
Nasko Oskov55119382020-01-17 18:22:181498 EXPECT_FALSE(
1499 root->current_origin().GetTupleOrPrecursorTupleIfOpaque().IsValid());
Nasko Oskov039121012019-01-11 00:21:321500 EXPECT_EQ(1UL, root->child_count());
1501 FrameTreeNode* child = root->child_at(0);
1502
1503 // Create a blob: URL and navigate the child frame to it.
1504 std::string html = "<html><body>This is blob content.</body></html>";
1505 std::string script = JsReplace(
1506 "var blob = new Blob([$1], {type: 'text/html'});"
1507 "var blob_url = URL.createObjectURL(blob);"
1508 "document.getElementById('child').src = blob_url;"
1509 "blob_url;",
1510 html);
1511 GURL blob_url;
1512 {
1513 TestFrameNavigationObserver observer(child);
1514 blob_url = GURL(EvalJs(root, script).ExtractString());
1515 observer.Wait();
1516 EXPECT_EQ(blob_url, child->current_frame_host()->GetLastCommittedURL());
1517 }
1518
1519 // We expect the frame to have committed in an opaque origin which contains
1520 // the same precursor information - none.
1521 url::Origin blob_origin = child->current_origin();
1522 EXPECT_TRUE(blob_origin.opaque());
1523 EXPECT_EQ(root->current_origin().GetTupleOrPrecursorTupleIfOpaque(),
1524 blob_origin.GetTupleOrPrecursorTupleIfOpaque());
Rakina Zata Amnic7367852022-11-07 17:10:401525 EXPECT_FALSE(
1526 child->current_origin().GetTupleOrPrecursorTupleIfOpaque().IsValid());
Nasko Oskov039121012019-01-11 00:21:321527
1528 // Navigate the frame away to any web URL.
1529 {
1530 GURL url(embedded_test_server()->GetURL("/title2.html"));
1531 TestFrameNavigationObserver observer(child);
1532 EXPECT_TRUE(ExecJs(child, JsReplace("window.location = $1", url)));
1533 observer.Wait();
1534 EXPECT_EQ(url, child->current_frame_host()->GetLastCommittedURL());
1535 }
1536 EXPECT_FALSE(child->current_origin().opaque());
1537 EXPECT_TRUE(shell()->web_contents()->GetController().CanGoBack());
1538 EXPECT_EQ(3, shell()->web_contents()->GetController().GetEntryCount());
1539 EXPECT_EQ(
1540 2, shell()->web_contents()->GetController().GetLastCommittedEntryIndex());
1541
1542 // Verify the blob URL still exists in the main frame, which keeps it alive
1543 // allowing a session history navigation back to succeed.
1544 EXPECT_EQ(blob_url, GURL(EvalJs(root, "blob_url;").ExtractString()));
1545
1546 // Now navigate back in session history. It should successfully go back to
Rakina Zata Amnic7367852022-11-07 17:10:401547 // the blob: URL. The child frame won't be reusing the exact same origin it
1548 // used before, but it will commit a new opaque origin which will still have
1549 // no precursor information.
Nasko Oskov039121012019-01-11 00:21:321550 {
1551 TestFrameNavigationObserver observer(child);
1552 shell()->web_contents()->GetController().GoBack();
1553 observer.Wait();
1554 }
1555 EXPECT_EQ(blob_url, child->current_frame_host()->GetLastCommittedURL());
1556 EXPECT_TRUE(child->current_origin().opaque());
Rakina Zata Amnic7367852022-11-07 17:10:401557 EXPECT_NE(blob_origin, child->current_origin());
Nasko Oskov039121012019-01-11 00:21:321558 EXPECT_EQ(root->current_origin().GetTupleOrPrecursorTupleIfOpaque(),
1559 child->current_origin().GetTupleOrPrecursorTupleIfOpaque());
Rakina Zata Amnic7367852022-11-07 17:10:401560 EXPECT_FALSE(
1561 child->current_origin().GetTupleOrPrecursorTupleIfOpaque().IsValid());
Nasko Oskov039121012019-01-11 00:21:321562}
1563
1564// Test to verify that about:blank iframe, which is a child of a sandboxed
1565// iframe is not considered same origin, but precursor information is preserved
1566// in its origin.
1567IN_PROC_BROWSER_TEST_F(CrossProcessFrameTreeBrowserTest,
1568 AboutBlankSubframeInSandboxedFrame) {
1569 // Start off by navigating to a page with sandboxed iframe, which allows
1570 // script execution.
1571 GURL main_url(
1572 embedded_test_server()->GetURL("/sandboxed_main_frame_script.html"));
1573 EXPECT_TRUE(NavigateToURL(shell(), main_url));
1574
1575 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:551576 ->GetPrimaryFrameTree()
1577 .root();
Nasko Oskov039121012019-01-11 00:21:321578 EXPECT_EQ(1UL, root->child_count());
1579 FrameTreeNode* child = root->child_at(0);
1580
1581 // Navigate the frame to data: URL to cause it to have an opaque origin that
1582 // is derived from the |main_url| origin.
1583 GURL data_url("data:text/html,<html><body>foo</body></html>");
1584 {
1585 TestFrameNavigationObserver observer(child);
1586 EXPECT_TRUE(ExecJs(root, JsReplace("frames[0].location = $1", data_url)));
1587 observer.Wait();
1588 EXPECT_EQ(data_url, child->current_frame_host()->GetLastCommittedURL());
1589 }
1590
1591 // Add an about:blank iframe to the data: frame, which should not inherit the
1592 // origin, but should preserve the precursor information.
1593 {
1594 EXPECT_TRUE(ExecJs(child,
1595 "var f = document.createElement('iframe');"
1596 "document.body.appendChild(f);"));
1597 }
1598 EXPECT_EQ(1UL, child->child_count());
1599 FrameTreeNode* grandchild = child->child_at(0);
1600
1601 EXPECT_TRUE(grandchild->current_origin().opaque());
1602 EXPECT_EQ(GURL(url::kAboutBlankURL),
1603 grandchild->current_frame_host()->GetLastCommittedURL());
1604
1605 // The origin of the data: document should have precursor information matching
1606 // the main frame origin.
1607 EXPECT_EQ(root->current_origin().GetTupleOrPrecursorTupleIfOpaque(),
1608 child->current_origin().GetTupleOrPrecursorTupleIfOpaque());
1609
1610 // The same should hold also for the about:blank subframe of the data: frame.
1611 EXPECT_EQ(root->current_origin().GetTupleOrPrecursorTupleIfOpaque(),
1612 grandchild->current_origin().GetTupleOrPrecursorTupleIfOpaque());
1613
1614 // The about:blank document should not be able to access its parent, as they
1615 // are considered cross origin due to the sandbox flags on the parent.
1616 EXPECT_FALSE(ExecJs(grandchild, "window.parent.foo = 'bar';"));
1617 EXPECT_NE(child->current_origin(), grandchild->current_origin());
alexmosbc7eafa2014-12-06 01:38:091618}
1619
Ian Clelland5cbaaf82017-11-27 22:00:031620// Ensure that a popup opened from a sandboxed main frame inherits sandbox flags
1621// from its opener.
1622IN_PROC_BROWSER_TEST_F(CrossProcessFrameTreeBrowserTest,
1623 SandboxFlagsSetForNewWindow) {
1624 GURL main_url(
1625 embedded_test_server()->GetURL("/sandboxed_main_frame_script.html"));
1626 EXPECT_TRUE(NavigateToURL(shell(), main_url));
1627
1628 // It is safe to obtain the root frame tree node here, as it doesn't change.
1629 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:551630 ->GetPrimaryFrameTree()
1631 .root();
Ian Clelland5cbaaf82017-11-27 22:00:031632
1633 // Open a new window from the main frame.
1634 GURL popup_url(embedded_test_server()->GetURL("foo.com", "/title1.html"));
1635 Shell* new_shell = OpenPopup(root->current_frame_host(), popup_url, "");
1636 EXPECT_TRUE(new_shell);
1637 WebContents* new_contents = new_shell->web_contents();
1638
1639 // Check that the new window's sandbox flags correctly reflect the opener's
1640 // flags. Main frame sets allow-popups, allow-pointer-lock and allow-scripts.
1641 FrameTreeNode* popup_root =
Carlos Caballero15caeeb2021-10-27 09:57:551642 static_cast<WebContentsImpl*>(new_contents)->GetPrimaryFrameTree().root();
arthursonzognib93a4472020-04-10 07:38:001643 network::mojom::WebSandboxFlags main_frame_sandbox_flags =
Ian Clelland5cbaaf82017-11-27 22:00:031644 root->current_frame_host()->active_sandbox_flags();
Arthur Sonzogni4ba3104f2022-03-09 09:04:391645 EXPECT_EQ(
1646 network::mojom::WebSandboxFlags::kAll &
1647 ~network::mojom::WebSandboxFlags::kAutomaticFeatures &
1648 ~network::mojom::WebSandboxFlags::kPointerLock &
1649 ~network::mojom::WebSandboxFlags::kPopups &
1650 ~network::mojom::WebSandboxFlags::kScripts &
1651 ~network::mojom::WebSandboxFlags::kTopNavigationToCustomProtocols,
1652 main_frame_sandbox_flags);
Ian Clelland5cbaaf82017-11-27 22:00:031653
1654 EXPECT_EQ(main_frame_sandbox_flags,
1655 popup_root->effective_frame_policy().sandbox_flags);
1656 EXPECT_EQ(main_frame_sandbox_flags, popup_root->active_sandbox_flags());
1657 EXPECT_EQ(main_frame_sandbox_flags,
1658 popup_root->current_frame_host()->active_sandbox_flags());
1659}
1660
Shivani Sharmac4f561582018-11-15 15:58:391661// Tests that the user activation bits get cleared when a cross-site document is
1662// installed in the frame.
1663IN_PROC_BROWSER_TEST_F(CrossProcessFrameTreeBrowserTest,
1664 ClearUserActivationForNewDocument) {
1665 GURL main_url(embedded_test_server()->GetURL("/frame_tree/top.html"));
1666 EXPECT_TRUE(NavigateToURL(shell(), main_url));
1667
1668 // It is safe to obtain the root frame tree node here, as it doesn't change.
1669 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:551670 ->GetPrimaryFrameTree()
1671 .root();
Shivani Sharmac4f561582018-11-15 15:58:391672
Mustaq Ahmed4baa9a6e82019-12-20 23:43:461673 EXPECT_FALSE(root->HasStickyUserActivation());
Shivani Sharmac4f561582018-11-15 15:58:391674 EXPECT_FALSE(root->HasTransientUserActivation());
1675
1676 // Set the user activation bits.
1677 root->UpdateUserActivationState(
Mustaq Ahmeddc195e5b2020-08-04 18:45:111678 blink::mojom::UserActivationUpdateType::kNotifyActivation,
1679 blink::mojom::UserActivationNotificationType::kTest);
Mustaq Ahmed4baa9a6e82019-12-20 23:43:461680 EXPECT_TRUE(root->HasStickyUserActivation());
Shivani Sharmac4f561582018-11-15 15:58:391681 EXPECT_TRUE(root->HasTransientUserActivation());
1682
1683 // Install a new cross-site document to check the clearing of user activation
1684 // bits.
1685 GURL cross_site_url(
1686 embedded_test_server()->GetURL("foo.com", "/title2.html"));
1687 EXPECT_TRUE(NavigateToURL(shell(), cross_site_url));
1688
Mustaq Ahmed4baa9a6e82019-12-20 23:43:461689 EXPECT_FALSE(root->HasStickyUserActivation());
Shivani Sharmac4f561582018-11-15 15:58:391690 EXPECT_FALSE(root->HasTransientUserActivation());
1691}
1692
Dave Tapuska708c2732022-09-23 22:47:491693class BrowserContextGroupSwapFrameTreeBrowserTest : public ContentBrowserTest {
1694 public:
1695 BrowserContextGroupSwapFrameTreeBrowserTest()
1696 : https_server_(net::EmbeddedTestServer::TYPE_HTTPS) {}
1697
1698 void SetUpCommandLine(base::CommandLine* command_line) override {
1699 IsolateAllSitesForTesting(command_line);
1700 }
1701
1702 void SetUpOnMainThread() override {
1703 ContentBrowserTest::SetUpOnMainThread();
1704 host_resolver()->AddRule("*", "127.0.0.1");
1705 https_server_.ServeFilesFromSourceDirectory(GetTestDataFilePath());
1706 https_server_.SetSSLConfig(net::EmbeddedTestServer::CERT_TEST_NAMES);
1707 net::test_server::RegisterDefaultHandlers(&https_server_);
1708 ASSERT_TRUE(https_server_.Start());
1709 }
1710
1711 net::EmbeddedTestServer* https_server() { return &https_server_; }
1712
1713 public:
1714 net::EmbeddedTestServer https_server_;
1715};
1716
1717// Force a race between when the RenderViewHostImpl's main frame is running
1718// the unload handlers and when a new navigation occurs that tries to
1719// reuse a RenderViewHostImpl.
1720IN_PROC_BROWSER_TEST_F(BrowserContextGroupSwapFrameTreeBrowserTest,
1721 NavigateAndGoBack) {
1722 GURL main_url(https_server()->GetURL("a.test", "/title1.html"));
1723 EXPECT_TRUE(NavigateToURL(shell(), main_url));
1724
1725 auto* web_contents = static_cast<WebContentsImpl*>(shell()->web_contents());
1726 web_contents->GetPrimaryMainFrame()->DoNotDeleteForTesting();
1727 DisableBFCacheForRFHForTesting(
1728 web_contents->GetPrimaryFrameTree().root()->current_frame_host());
1729
1730 // Load a page with COOP set to force the browsing context group swap
1731 // and clears out old proxies.
1732 GURL new_main_url(https_server()->GetURL(
1733 "b.test", "/set-header?Cross-Origin-Opener-Policy: same-origin"));
1734
1735 EXPECT_TRUE(NavigateToURL(shell(), new_main_url));
1736
1737 TestNavigationObserver back_load_observer(web_contents);
1738 web_contents->GetController().GoBack();
1739 back_load_observer.Wait();
1740}
1741
nickdb193a12016-09-09 23:09:231742// FrameTreeBrowserTest variant where we isolate http://*.is, Iceland's top
naskoabed2a52017-05-03 05:10:171743// level domain. This is an analogue to isolating extensions, which we can use
1744// inside content_browsertests, where extensions don't exist. Iceland, like an
nickdb193a12016-09-09 23:09:231745// extension process, is a special place with magical powers; we want to protect
1746// it from outsiders.
1747class IsolateIcelandFrameTreeBrowserTest : public ContentBrowserTest {
1748 public:
Fergal Dalya1d569972021-03-16 03:24:531749 IsolateIcelandFrameTreeBrowserTest() = default;
nickdb193a12016-09-09 23:09:231750
Peter Boström9b036532021-10-28 23:37:281751 IsolateIcelandFrameTreeBrowserTest(
1752 const IsolateIcelandFrameTreeBrowserTest&) = delete;
1753 IsolateIcelandFrameTreeBrowserTest& operator=(
1754 const IsolateIcelandFrameTreeBrowserTest&) = delete;
1755
nickdb193a12016-09-09 23:09:231756 void SetUpCommandLine(base::CommandLine* command_line) override {
Lukasz Anforowicze7c87d12018-11-03 02:53:341757 // Blink suppresses navigations to blob URLs of origins different from the
alexis.menard83f1b6d2017-05-17 19:37:331758 // frame initiating the navigation. We disable those checks for this test,
1759 // to test what happens in a compromise scenario.
1760 command_line->AppendSwitch(switches::kDisableWebSecurity);
Lukasz Anforowicze7c87d12018-11-03 02:53:341761
1762 // ProcessSwitchForIsolatedBlob test below requires that one of URLs used in
Lukasz Anforowicz25420932018-12-18 20:59:221763 // the test (blob:http://b.is/) belongs to an isolated origin.
1764 command_line->AppendSwitchASCII(switches::kIsolateOrigins, "http://b.is/");
nickdb193a12016-09-09 23:09:231765 }
1766
1767 void SetUpOnMainThread() override {
1768 host_resolver()->AddRule("*", "127.0.0.1");
nickdb193a12016-09-09 23:09:231769 SetupCrossSiteRedirector(embedded_test_server());
martijn12d2dad2016-11-11 10:59:271770 ASSERT_TRUE(embedded_test_server()->Start());
nickdb193a12016-09-09 23:09:231771 }
nickdb193a12016-09-09 23:09:231772};
1773
1774// Regression test for https://crbug.com/644966
1775IN_PROC_BROWSER_TEST_F(IsolateIcelandFrameTreeBrowserTest,
1776 ProcessSwitchForIsolatedBlob) {
nickdb193a12016-09-09 23:09:231777 // Set up an iframe.
1778 WebContents* contents = shell()->web_contents();
1779 FrameTreeNode* root =
Carlos Caballero15caeeb2021-10-27 09:57:551780 static_cast<WebContentsImpl*>(contents)->GetPrimaryFrameTree().root();
nickdb193a12016-09-09 23:09:231781 GURL main_url(embedded_test_server()->GetURL(
nick07fd7e12016-09-12 18:54:061782 "a.com", "/cross_site_iframe_factory.html?a(a)"));
nickdb193a12016-09-09 23:09:231783 EXPECT_TRUE(NavigateToURL(shell(), main_url));
1784
1785 // The navigation targets an invalid blob url; that's intentional to trigger
1786 // an error response. The response should commit in a process dedicated to
Nasko Oskov0401d812021-02-05 22:20:081787 // http://b.is or error pages, depending on policy.
Nick Carterb7e71312018-08-03 23:36:131788 EXPECT_EQ(
1789 "done",
1790 EvalJs(
1791 root,
1792 "new Promise((resolve) => {"
1793 " var iframe_element = document.getElementsByTagName('iframe')[0];"
1794 " iframe_element.onload = () => resolve('done');"
Lukasz Anforowicz25420932018-12-18 20:59:221795 " iframe_element.src = 'blob:http://b.is/';"
Nick Carterb7e71312018-08-03 23:36:131796 "});"));
Fergal Dalyf0522332020-07-18 06:09:461797 EXPECT_TRUE(WaitForLoadStop(contents));
nickdb193a12016-09-09 23:09:231798
1799 // Make sure we did a process transfer back to "b.is".
Aaron Colwelle953e562019-07-24 16:47:361800 const std::string kExpectedSiteURL =
1801 AreDefaultSiteInstancesEnabled()
1802 ? SiteInstanceImpl::GetDefaultSiteURL().spec()
1803 : "http://a.com/";
Nasko Oskov0401d812021-02-05 22:20:081804 const std::string kExpectedSubframeSiteURL =
1805 SiteIsolationPolicy::IsErrorPageIsolationEnabled(/*in_main_frame*/ false)
1806 ? "chrome-error://chromewebdata/"
1807 : "http://b.is/";
Aaron Colwelle953e562019-07-24 16:47:361808 EXPECT_EQ(base::StringPrintf(" Site A ------------ proxies for B\n"
1809 " +--Site B ------- proxies for A\n"
1810 "Where A = %s\n"
Nasko Oskov0401d812021-02-05 22:20:081811 " B = %s",
1812 kExpectedSiteURL.c_str(),
1813 kExpectedSubframeSiteURL.c_str()),
Fergal Daly79f44292020-12-01 02:30:481814 DepictFrameTree(*root));
nickdb193a12016-09-09 23:09:231815}
1816
Arthur Sonzogni64457592022-11-22 11:08:591817class FrameTreeCredentiallessIframeBrowserTest : public FrameTreeBrowserTest {
Antonio Sartori5abc8de2021-07-13 08:42:471818 public:
Arthur Sonzogni64457592022-11-22 11:08:591819 FrameTreeCredentiallessIframeBrowserTest() = default;
Antonio Sartori5abc8de2021-07-13 08:42:471820
1821 void SetUpCommandLine(base::CommandLine* command_line) override {
Antonio Sartori753cd6d2021-07-23 08:34:551822 command_line->AppendSwitch(switches::kEnableBlinkTestFeatures);
Antonio Sartori5abc8de2021-07-13 08:42:471823 }
1824};
1825
Arthur Sonzogni64457592022-11-22 11:08:591826// Tests the mojo propagation of the 'credentialless' attribute to the browser.
1827IN_PROC_BROWSER_TEST_F(FrameTreeCredentiallessIframeBrowserTest,
Antonio Sartori5abc8de2021-07-13 08:42:471828 AttributeIsPropagatedToBrowser) {
1829 GURL main_url(embedded_test_server()->GetURL("/hello.html"));
1830 EXPECT_TRUE(NavigateToURL(shell(), main_url));
1831
1832 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:551833 ->GetPrimaryFrameTree()
1834 .root();
Antonio Sartori5abc8de2021-07-13 08:42:471835
Arthur Sonzogni64457592022-11-22 11:08:591836 // Not setting the attribute => the iframe is not credentialless.
Antonio Sartori5abc8de2021-07-13 08:42:471837 EXPECT_TRUE(ExecJs(root,
1838 "var f = document.createElement('iframe');"
1839 "document.body.appendChild(f);"));
1840 EXPECT_EQ(1U, root->child_count());
Miyoung Shinc9ff4812023-01-05 08:58:051841 EXPECT_FALSE(root->child_at(0)->Credentialless());
Yifan Luoe1a2e05d2022-01-12 16:47:591842 EXPECT_EQ(false, EvalJs(root->child_at(0)->current_frame_host(),
Arthur Sonzogni64457592022-11-22 11:08:591843 "window.credentialless"));
Antonio Sartori5abc8de2021-07-13 08:42:471844
Arthur Sonzogni64457592022-11-22 11:08:591845 // Setting the attribute on the iframe element makes the iframe
1846 // credentialless.
Antonio Sartori5abc8de2021-07-13 08:42:471847 EXPECT_TRUE(ExecJs(root,
1848 "var d = document.createElement('div');"
Arthur Sonzogni64457592022-11-22 11:08:591849 "d.innerHTML = '<iframe credentialless></iframe>';"
Antonio Sartori5abc8de2021-07-13 08:42:471850 "document.body.appendChild(d);"));
1851 EXPECT_EQ(2U, root->child_count());
Miyoung Shinc9ff4812023-01-05 08:58:051852 EXPECT_TRUE(root->child_at(1)->Credentialless());
Yifan Luo400867472022-05-19 09:34:201853 EXPECT_EQ(true, EvalJs(root->child_at(1)->current_frame_host(),
Arthur Sonzogni64457592022-11-22 11:08:591854 "window.credentialless"));
Antonio Sartori5abc8de2021-07-13 08:42:471855
1856 // Setting the attribute via javascript works.
1857 EXPECT_TRUE(ExecJs(root,
1858 "var g = document.createElement('iframe');"
Arthur Sonzogni64457592022-11-22 11:08:591859 "g.credentialless = true;"
Antonio Sartori5abc8de2021-07-13 08:42:471860 "document.body.appendChild(g);"));
1861 EXPECT_EQ(3U, root->child_count());
Miyoung Shinc9ff4812023-01-05 08:58:051862 EXPECT_TRUE(root->child_at(2)->Credentialless());
Yifan Luo400867472022-05-19 09:34:201863 EXPECT_EQ(true, EvalJs(root->child_at(2)->current_frame_host(),
Arthur Sonzogni64457592022-11-22 11:08:591864 "window.credentialless"));
Antonio Sartori5abc8de2021-07-13 08:42:471865
Arthur Sonzogni64457592022-11-22 11:08:591866 EXPECT_TRUE(ExecJs(root, "g.credentialless = false;"));
Miyoung Shinc9ff4812023-01-05 08:58:051867 EXPECT_FALSE(root->child_at(2)->Credentialless());
Yifan Luo400867472022-05-19 09:34:201868 EXPECT_EQ(true, EvalJs(root->child_at(2)->current_frame_host(),
Arthur Sonzogni64457592022-11-22 11:08:591869 "window.credentialless"));
Antonio Sartori5abc8de2021-07-13 08:42:471870
Arthur Sonzogni64457592022-11-22 11:08:591871 EXPECT_TRUE(ExecJs(root, "g.credentialless = true;"));
Miyoung Shinc9ff4812023-01-05 08:58:051872 EXPECT_TRUE(root->child_at(2)->Credentialless());
Yifan Luo400867472022-05-19 09:34:201873 EXPECT_EQ(true, EvalJs(root->child_at(2)->current_frame_host(),
Arthur Sonzogni64457592022-11-22 11:08:591874 "window.credentialless"));
Antonio Sartori5abc8de2021-07-13 08:42:471875}
1876
[email protected]5f96f5a62014-01-10 00:05:111877} // namespace content