blob: 79c408054993a1f57ee641d2aefeddbe245a0bc5 [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 Lukaszewicz0ef0f45f2025-02-18 17:59:4211#include "base/test/metrics/histogram_tester.h"
Thomas Lukaszewicz8ada31b2024-09-01 19:18:1612#include "base/test/run_until.h"
shivanigithub93878d02021-06-15 11:37:5313#include "base/test/scoped_feature_list.h"
avib7348942015-12-25 20:57:1014#include "build/build_config.h"
danakjc492bf82020-09-09 20:02:4415#include "content/browser/renderer_host/frame_tree_node.h"
Yao Xiao6e1f7d32022-01-07 03:28:4016#include "content/browser/renderer_host/navigation_request.h"
danakjc492bf82020-09-09 20:02:4417#include "content/browser/renderer_host/render_frame_host_impl.h"
Thomas Lukaszewicz54d8e432024-09-18 20:13:1718#include "content/browser/renderer_host/render_process_host_impl.h"
[email protected]5f96f5a62014-01-10 00:05:1119#include "content/browser/renderer_host/render_view_host_impl.h"
20#include "content/browser/web_contents/web_contents_impl.h"
Thomas Lukaszewicz8ada31b2024-09-01 19:18:1621#include "content/common/content_navigation_policy.h"
shivanigithubf33e469c2021-08-02 13:52:2022#include "content/public/browser/browser_thread.h"
Thomas Lukaszewicz8ada31b2024-09-01 19:18:1623#include "content/public/browser/dedicated_worker_service.h"
Dave Tapuska87696ae2021-11-18 18:48:3124#include "content/public/browser/navigation_handle.h"
Nasko Oskov0401d812021-02-05 22:20:0825#include "content/public/browser/site_isolation_policy.h"
Annie Sullivan35977ad2019-04-30 21:04:2826#include "content/public/common/content_features.h"
nickdb193a12016-09-09 23:09:2327#include "content/public/common/content_switches.h"
Nasko Oskov039121012019-01-11 00:21:3228#include "content/public/common/origin_util.h"
[email protected]5f96f5a62014-01-10 00:05:1129#include "content/public/common/url_constants.h"
Peter Kasting919ce652020-05-07 10:22:3630#include "content/public/test/browser_test.h"
[email protected]5f96f5a62014-01-10 00:05:1131#include "content/public/test/browser_test_utils.h"
[email protected]6e9def12014-03-27 20:23:2832#include "content/public/test/content_browser_test.h"
33#include "content/public/test/content_browser_test_utils.h"
Ari Chivukula5d15efb2023-01-21 04:33:5234#include "content/public/test/content_mock_cert_verifier.h"
John Abd-El-Malek3fea4c42019-01-10 01:15:4235#include "content/public/test/test_frame_navigation_observer.h"
[email protected]5f96f5a62014-01-10 00:05:1136#include "content/public/test/test_navigation_observer.h"
37#include "content/public/test/test_utils.h"
38#include "content/shell/browser/shell.h"
nickdb193a12016-09-09 23:09:2339#include "content/shell/common/shell_switches.h"
[email protected]82307f6b2014-08-07 03:30:1240#include "content/test/content_browser_test_utils_internal.h"
Thomas Lukaszewicz1b672fe2024-09-17 08:35:0341#include "content/test/render_document_feature.h"
David Sandersc9ef3002022-02-02 15:27:0142#include "net/base/features.h"
[email protected]5f96f5a62014-01-10 00:05:1143#include "net/dns/mock_host_resolver.h"
Nan Linea8e9d202022-04-13 18:31:0144#include "net/test/embedded_test_server/controllable_http_response.h"
Dave Tapuska708c2732022-09-23 22:47:4945#include "net/test/embedded_test_server/default_handlers.h"
alexmos478dcbb2014-12-10 21:24:4646#include "net/test/embedded_test_server/embedded_test_server.h"
shivanigithubf33e469c2021-08-02 13:52:2047#include "net/test/embedded_test_server/http_request.h"
Ari Chivukula5d15efb2023-01-21 04:33:5248#include "services/network/public/cpp/network_switches.h"
arthursonzognib93a4472020-04-10 07:38:0049#include "services/network/public/cpp/web_sandbox_flags.h"
50#include "services/network/public/mojom/web_sandbox_flags.mojom-shared.h"
Gyuyoung Kim107c2a02021-04-13 01:49:3051#include "third_party/blink/public/common/chrome_debug_urls.h"
shivanigithub93878d02021-06-15 11:37:5352#include "third_party/blink/public/common/features.h"
Ari Chivukula5d15efb2023-01-21 04:33:5253#include "third_party/blink/public/common/storage_key/storage_key.h"
Thomas Lukaszewicz8ada31b2024-09-01 19:18:1654#include "third_party/blink/public/common/switches.h"
Antonio Gomes4b2c5132020-01-16 11:49:4855#include "third_party/blink/public/mojom/frame/user_activation_update_types.mojom.h"
nick1466c842015-11-25 20:08:0656#include "url/url_constants.h"
[email protected]5f96f5a62014-01-10 00:05:1157
58namespace content {
59
nickdb193a12016-09-09 23:09:2360namespace {
61
Nick Carterb7e71312018-08-03 23:36:1362EvalJsResult GetOriginFromRenderer(FrameTreeNode* node) {
Philip Jägenstedt67302a22018-09-14 09:58:0563 return EvalJs(node, "self.origin");
nickdb193a12016-09-09 23:09:2364}
65
Yuzu Saijo03dbf9b2022-07-22 04:29:4566// Expect that frame_name, id and src match the node's values.
67void ExpectAttributesEq(FrameTreeNode* node,
68 const std::string& frame_name,
Arthur Sonzognic686e8f2024-01-11 08:36:3769 const std::optional<std::string> id,
70 const std::optional<std::string> src) {
Yuzu Saijo03dbf9b2022-07-22 04:29:4571 EXPECT_EQ(frame_name, node->frame_name());
72 EXPECT_EQ(id, node->html_id());
73 EXPECT_EQ(src, node->html_src());
74}
75
nickdb193a12016-09-09 23:09:2376} // namespace
77
[email protected]5f96f5a62014-01-10 00:05:1178class FrameTreeBrowserTest : public ContentBrowserTest {
79 public:
Fergal Dalya1d569972021-03-16 03:24:5380 FrameTreeBrowserTest() = default;
[email protected]5f96f5a62014-01-10 00:05:1181
Peter Boström9b036532021-10-28 23:37:2882 FrameTreeBrowserTest(const FrameTreeBrowserTest&) = delete;
83 FrameTreeBrowserTest& operator=(const FrameTreeBrowserTest&) = delete;
84
alexmos478dcbb2014-12-10 21:24:4685 void SetUpOnMainThread() override {
86 host_resolver()->AddRule("*", "127.0.0.1");
alexmos478dcbb2014-12-10 21:24:4687 SetupCrossSiteRedirector(embedded_test_server());
martijn12d2dad2016-11-11 10:59:2788 ASSERT_TRUE(embedded_test_server()->Start());
alexmos478dcbb2014-12-10 21:24:4689 }
[email protected]5f96f5a62014-01-10 00:05:1190};
91
92// Ensures FrameTree correctly reflects page structure during navigations.
93IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, FrameTreeShape) {
alexmos478dcbb2014-12-10 21:24:4694 GURL base_url = embedded_test_server()->GetURL("A.com", "/site_isolation/");
[email protected]5f96f5a62014-01-10 00:05:1195
96 // Load doc without iframes. Verify FrameTree just has root.
97 // Frame tree:
98 // Site-A Root
Alex Moshchukaa95adf52019-08-13 00:02:0299 EXPECT_TRUE(NavigateToURL(shell(), base_url.Resolve("blank.html")));
Aran Gilman37d11632019-10-08 23:07:15100 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:55101 ->GetPrimaryFrameTree()
102 .root();
[email protected]5f96f5a62014-01-10 00:05:11103 EXPECT_EQ(0U, root->child_count());
104
105 // Add 2 same-site frames. Verify 3 nodes in tree with proper names.
106 // Frame tree:
107 // Site-A Root -- Site-A frame1
108 // \-- Site-A frame2
[email protected]71bc140b2022-03-14 23:45:01109 LoadStopObserver observer1(shell()->web_contents());
Alex Moshchukaa95adf52019-08-13 00:02:02110 EXPECT_TRUE(NavigateToURL(shell(), base_url.Resolve("frames-X-X.html")));
[email protected]5f96f5a62014-01-10 00:05:11111 observer1.Wait();
112 ASSERT_EQ(2U, root->child_count());
113 EXPECT_EQ(0U, root->child_at(0)->child_count());
114 EXPECT_EQ(0U, root->child_at(1)->child_count());
115}
116
117// TODO(ajwong): Talk with nasko and merge this functionality with
118// FrameTreeShape.
119IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, FrameTreeShape2) {
Alex Moshchukaa95adf52019-08-13 00:02:02120 EXPECT_TRUE(NavigateToURL(
121 shell(), embedded_test_server()->GetURL("/frame_tree/top.html")));
[email protected]5f96f5a62014-01-10 00:05:11122
123 WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents());
Carlos Caballero15caeeb2021-10-27 09:57:55124 FrameTreeNode* root = wc->GetPrimaryFrameTree().root();
[email protected]5f96f5a62014-01-10 00:05:11125
[email protected]58faf942014-02-20 21:03:58126 // Check that the root node is properly created.
[email protected]5f96f5a62014-01-10 00:05:11127 ASSERT_EQ(3UL, root->child_count());
Arthur Sonzognic686e8f2024-01-11 08:36:37128 ExpectAttributesEq(root, std::string(), std::nullopt, std::nullopt);
[email protected]5f96f5a62014-01-10 00:05:11129
130 ASSERT_EQ(2UL, root->child_at(0)->child_count());
Yuzu Saijo03dbf9b2022-07-22 04:29:45131 ExpectAttributesEq(root->child_at(0), "1-1-name", "1-1-id", "1-1.html");
[email protected]5f96f5a62014-01-10 00:05:11132
133 // Verify the deepest node exists and has the right name.
134 ASSERT_EQ(2UL, root->child_at(2)->child_count());
135 EXPECT_EQ(1UL, root->child_at(2)->child_at(1)->child_count());
136 EXPECT_EQ(0UL, root->child_at(2)->child_at(1)->child_at(0)->child_count());
Yuzu Saijo03dbf9b2022-07-22 04:29:45137 ExpectAttributesEq(root->child_at(2)->child_at(1)->child_at(0), "3-1-name",
138 "3-1-id", "3-1.html");
[email protected]5f96f5a62014-01-10 00:05:11139
140 // Navigate to about:blank, which should leave only the root node of the frame
141 // tree in the browser process.
Alex Moshchukaa95adf52019-08-13 00:02:02142 EXPECT_TRUE(
143 NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html")));
[email protected]5f96f5a62014-01-10 00:05:11144
Carlos Caballero15caeeb2021-10-27 09:57:55145 root = wc->GetPrimaryFrameTree().root();
[email protected]5f96f5a62014-01-10 00:05:11146 EXPECT_EQ(0UL, root->child_count());
Arthur Sonzognic686e8f2024-01-11 08:36:37147 ExpectAttributesEq(root, std::string(), std::nullopt, std::nullopt);
Yuzu Saijo03dbf9b2022-07-22 04:29:45148}
149
150// Frame attributes of iframe elements are correctly tracked in FrameTree.
151IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, FrameTreeAttributesUpdate) {
152 EXPECT_TRUE(NavigateToURL(
153 shell(), embedded_test_server()->GetURL("/frame_tree/top.html")));
154
155 WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents());
156 FrameTreeNode* root = wc->GetPrimaryFrameTree().root();
157
158 // Check that the root node is properly created.
159 ASSERT_EQ(3UL, root->child_count());
Arthur Sonzognic686e8f2024-01-11 08:36:37160 ExpectAttributesEq(root, std::string(), std::nullopt, std::nullopt);
Yuzu Saijo03dbf9b2022-07-22 04:29:45161
162 ASSERT_EQ(2UL, root->child_at(0)->child_count());
163 ExpectAttributesEq(root->child_at(0), "1-1-name", "1-1-id", "1-1.html");
164
165 // Change id, name and src of the iframe.
166 EXPECT_TRUE(ExecJs(root->current_frame_host(), R"(
167 let iframe = document.getElementById('1-1-id');
168 iframe.id = '1-1-updated-id';
169 iframe.name = '1-1-updated-name';
170 iframe.src = '1-1-updated.html';
171 )"));
172 // |html_name()| gets updated whenever the name attribute gets updated.
173 EXPECT_EQ("1-1-updated-name", root->child_at(0)->html_name());
174 ExpectAttributesEq(root->child_at(0), "1-1-name", "1-1-updated-id",
175 "1-1-updated.html");
176}
177
178// Ensures that frames' name attributes and their updates are tracked in
179// |html_name()| and window.name and its updates are tracked in |frame_name()|.
180IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, FrameNameVSWindowName) {
181 EXPECT_TRUE(NavigateToURL(
182 shell(), embedded_test_server()->GetURL("/frame_tree/top.html")));
183
184 WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents());
185 FrameTreeNode* root = wc->GetPrimaryFrameTree().root();
186
187 // Check that the root node is properly created.
188 ASSERT_EQ(3UL, root->child_count());
Arthur Sonzognic686e8f2024-01-11 08:36:37189 EXPECT_EQ(std::nullopt, root->html_name());
[email protected]5f96f5a62014-01-10 00:05:11190 EXPECT_EQ(std::string(), root->frame_name());
Yuzu Saijo03dbf9b2022-07-22 04:29:45191
192 ASSERT_EQ(2UL, root->child_at(0)->child_count());
193 EXPECT_EQ("1-1-name", root->child_at(0)->html_name());
194 EXPECT_EQ("1-1-name", root->child_at(0)->frame_name());
195
196 // Change the name attribute of the iframe.
197 EXPECT_TRUE(ExecJs(root->current_frame_host(), R"(
198 let iframe = document.getElementById('1-1-id');
199 iframe.name = '1-1-updated-name';
200 )"));
201 // |html_name()| gets updated whenever the name attribute gets updated.
202 EXPECT_EQ("1-1-updated-name", root->child_at(0)->html_name());
203 // |frame_name()| stays the same.
204 EXPECT_EQ("1-1-name", root->child_at(0)->frame_name());
205
206 // Change the window.name of the iframe.
207 EXPECT_TRUE(ExecJs(root->current_frame_host(), R"(
208 let iframe = document.getElementById('1-1-id');
209 iframe.contentWindow.name = '1-1-updated-name-2';
210 )"));
211 // |html_name()| stays the same.
212 EXPECT_EQ("1-1-updated-name", root->child_at(0)->html_name());
213 // |frame_name()| gets updated.
214 EXPECT_EQ("1-1-updated-name-2", root->child_at(0)->frame_name());
215}
216
217// Ensures that long attributes are cut down to the max length.
218IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, LongAttributesCutDown) {
219 EXPECT_TRUE(NavigateToURL(
220 shell(), embedded_test_server()->GetURL("/frame_tree/top.html")));
221
222 WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents());
223 FrameTreeNode* root = wc->GetPrimaryFrameTree().root();
224
225 // Check that the root node is properly created.
226 ASSERT_EQ(3UL, root->child_count());
227 ASSERT_EQ(2UL, root->child_at(0)->child_count());
228 EXPECT_EQ("1-1-name", root->child_at(0)->html_name());
229
230 // Change the name attribute of the iframe.
231 EXPECT_TRUE(ExecJs(root->current_frame_host(), R"(
232 let iframe = document.getElementById('1-1-id');
233 iframe.id += 'a'.repeat(1200);
234 iframe.name += 'b'.repeat(1200);
235 iframe.src += 'c'.repeat(1200) + '.html';
236 )"));
237 // Long attribute is cut down to the maximum length.
Yuzu Saijodc870f92023-01-20 03:39:11238 EXPECT_EQ(1024UL, root->child_at(0)->html_id()->size());
239 EXPECT_EQ(1024UL, root->child_at(0)->html_name()->size());
240 EXPECT_EQ(1024UL, root->child_at(0)->html_src()->size());
Yuzu Saijo03dbf9b2022-07-22 04:29:45241}
242
243// Insert a frame into the frame tree and ensure that the inserted frame's
244// attributes are correctly captured.
245IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, InsertFrameInTree) {
246 EXPECT_TRUE(NavigateToURL(
247 shell(), embedded_test_server()->GetURL("/frame_tree/top.html")));
248
249 WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents());
250 FrameTreeNode* root = wc->GetPrimaryFrameTree().root();
251
252 // Check that the root node is properly created.
253 ASSERT_EQ(3UL, root->child_count());
Arthur Sonzognic686e8f2024-01-11 08:36:37254 ExpectAttributesEq(root, std::string(), std::nullopt, std::nullopt);
Yuzu Saijo03dbf9b2022-07-22 04:29:45255
256 ASSERT_EQ(2UL, root->child_at(0)->child_count());
257 ExpectAttributesEq(root->child_at(0), "1-1-name", "1-1-id", "1-1.html");
258
259 // Insert a child iframe.
260 EXPECT_TRUE(ExecJs(root->current_frame_host(), R"(
261 let new_iframe = document.createElement('iframe');
262 new_iframe.id = '1-1-child-id';
263 new_iframe.src = '1-1-child.html';
264 new_iframe.name = '1-1-child-name';
265
266 document.body.appendChild(new_iframe);
267 )"));
268 // Check that the new iframe is inserted and their attributes are correct.
269 ASSERT_EQ(4UL, root->child_count());
270 ExpectAttributesEq(root->child_at(3), "1-1-child-name", "1-1-child-id",
271 "1-1-child.html");
[email protected]5f96f5a62014-01-10 00:05:11272}
273
274// Test that we can navigate away if the previous renderer doesn't clean up its
275// child frames.
276IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, FrameTreeAfterCrash) {
Alex Moshchukaa95adf52019-08-13 00:02:02277 EXPECT_TRUE(NavigateToURL(
278 shell(), embedded_test_server()->GetURL("/frame_tree/top.html")));
[email protected]5f96f5a62014-01-10 00:05:11279
creise42f2a52014-09-18 18:14:57280 // Ensure the view and frame are live.
Lingqi Chi641aeee2021-09-08 00:32:58281 RenderFrameHostImpl* rfh1 = static_cast<RenderFrameHostImpl*>(
Dave Tapuska327c06c92022-06-13 20:31:51282 shell()->web_contents()->GetPrimaryMainFrame());
Kevin McNeeec162302022-04-14 16:00:28283 RenderViewHostImpl* rvh = rfh1->render_view_host();
creise42f2a52014-09-18 18:14:57284 EXPECT_TRUE(rvh->IsRenderViewLive());
arthursonzognif4c4a4a2019-08-08 18:54:44285 EXPECT_TRUE(rfh1->IsRenderFrameLive());
creise42f2a52014-09-18 18:14:57286
[email protected]5f96f5a62014-01-10 00:05:11287 // Crash the renderer so that it doesn't send any FrameDetached messages.
288 RenderProcessHostWatcher crash_observer(
289 shell()->web_contents(),
290 RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
Wez0abfbf512018-03-03 01:54:45291 ASSERT_TRUE(
Dave Tapuska327c06c92022-06-13 20:31:51292 shell()->web_contents()->GetPrimaryMainFrame()->GetProcess()->Shutdown(
293 0));
[email protected]5f96f5a62014-01-10 00:05:11294 crash_observer.Wait();
295
[email protected]58faf942014-02-20 21:03:58296 // The frame tree should be cleared.
[email protected]5f96f5a62014-01-10 00:05:11297 WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents());
Carlos Caballero15caeeb2021-10-27 09:57:55298 FrameTreeNode* root = wc->GetPrimaryFrameTree().root();
[email protected]5f96f5a62014-01-10 00:05:11299 EXPECT_EQ(0UL, root->child_count());
[email protected]5f96f5a62014-01-10 00:05:11300
creise42f2a52014-09-18 18:14:57301 // Ensure the view and frame aren't live anymore.
302 EXPECT_FALSE(rvh->IsRenderViewLive());
arthursonzognif4c4a4a2019-08-08 18:54:44303 EXPECT_FALSE(rfh1->IsRenderFrameLive());
creise42f2a52014-09-18 18:14:57304
[email protected]5f96f5a62014-01-10 00:05:11305 // Navigate to a new URL.
alexmos478dcbb2014-12-10 21:24:46306 GURL url(embedded_test_server()->GetURL("/title1.html"));
Alex Moshchukaa95adf52019-08-13 00:02:02307 EXPECT_TRUE(NavigateToURL(shell(), url));
[email protected]5f96f5a62014-01-10 00:05:11308 EXPECT_EQ(0UL, root->child_count());
[email protected]58faf942014-02-20 21:03:58309 EXPECT_EQ(url, root->current_url());
creise42f2a52014-09-18 18:14:57310
arthursonzognif4c4a4a2019-08-08 18:54:44311 RenderFrameHostImpl* rfh2 = root->current_frame_host();
creise42f2a52014-09-18 18:14:57312 // Ensure the view and frame are live again.
313 EXPECT_TRUE(rvh->IsRenderViewLive());
arthursonzognif4c4a4a2019-08-08 18:54:44314 EXPECT_TRUE(rfh2->IsRenderFrameLive());
[email protected]5f96f5a62014-01-10 00:05:11315}
316
Thomas Lukaszewicz54d8e432024-09-18 20:13:17317// Tests the frame discard impl, both with and without post-discard process
318// shutdown.
319class FrameTreeBrowserWithDiscardTest
320 : public FrameTreeBrowserTest,
321 public ::testing::WithParamInterface<bool> {
Thomas Lukaszewicz8ada31b2024-09-01 19:18:16322 public:
323 FrameTreeBrowserWithDiscardTest() {
324 scoped_feature_list_.InitAndEnableFeature(features::kWebContentsDiscard);
325 }
326
Thomas Lukaszewicz54d8e432024-09-18 20:13:17327 void DiscardFrameTree(FrameTree& frame_tree) {
328 RenderProcessHostImpl* root_rph = static_cast<RenderProcessHostImpl*>(
329 frame_tree.root()->current_frame_host()->GetProcess());
330 if (KeepAliveDiscardedProcess()) {
331 // Increment the keep alive ref count of the renderer process to keep it
332 // alive post discard, simulating the situation where the process may be
333 // shared by multiple frames.
334 root_rph->IncrementKeepAliveRefCount(0);
335 }
336
337 frame_tree.Discard();
338
339 if (!KeepAliveDiscardedProcess()) {
340 // If not keeping the process alive wait for it to successfully exit.
341 RenderProcessHostWatcher exit_observer(
342 root_rph, content::RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
343 exit_observer.Wait();
344 }
345 }
346
347 bool KeepAliveDiscardedProcess() const { return GetParam(); }
348
Thomas Lukaszewicz8ada31b2024-09-01 19:18:16349 private:
350 base::test::ScopedFeatureList scoped_feature_list_;
351};
352
Thomas Lukaszewicz54d8e432024-09-18 20:13:17353IN_PROC_BROWSER_TEST_P(FrameTreeBrowserWithDiscardTest, DiscardFrameTree) {
Thomas Lukaszewicz0ef0f45f2025-02-18 17:59:42354 base::HistogramTester uma_recorder;
Thomas Lukaszewicz8ada31b2024-09-01 19:18:16355 WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents());
356 FrameTree& frame_tree = wc->GetPrimaryFrameTree();
357 FrameTreeNode* root = frame_tree.root();
358
359 EXPECT_TRUE(NavigateToURL(
360 shell(), embedded_test_server()->GetURL("/frame_tree/top.html")));
361 EXPECT_FALSE(wc->GetController().NeedsReload());
362 EXPECT_EQ(3UL, root->child_count());
363
364 // Ensure the view and frame are live.
365 RenderFrameHostImplWrapper initial_rfh(wc->GetPrimaryMainFrame());
366 RenderViewHostImpl* initial_rvh = initial_rfh->render_view_host();
367 EXPECT_TRUE(initial_rvh->IsRenderViewLive());
368 EXPECT_TRUE(initial_rfh->IsRenderFrameLive());
369
370 const auto get_child_isn = [&](RenderFrameHostImpl* rfh, int child_pos) {
371 return rfh->child_at(child_pos)
372 ->current_frame_host()
373 ->last_committed_frame_entry()
374 ->item_sequence_number();
375 };
376 const int initial_nav_entry_id =
377 wc->GetController().GetLastCommittedEntry()->GetUniqueID();
378 const int64_t initial_isn =
379 initial_rfh->last_committed_frame_entry()->item_sequence_number();
380 const int64_t initial_isn_child1 = get_child_isn(initial_rfh.get(), 0);
381 const int64_t initial_isn_child2 = get_child_isn(initial_rfh.get(), 1);
382 const int64_t initial_isn_child3 = get_child_isn(initial_rfh.get(), 2);
383
384 // Discard the frame tree, wait until all child frames have been cleared away.
385 EXPECT_FALSE(root->was_discarded());
Thomas Lukaszewicz0ef0f45f2025-02-18 17:59:42386 uma_recorder.ExpectUniqueSample("Discarding.DiscardFrameTree", true, 0);
Thomas Lukaszewicz54d8e432024-09-18 20:13:17387 DiscardFrameTree(frame_tree);
Thomas Lukaszewicz8ada31b2024-09-01 19:18:16388 ASSERT_TRUE(
389 base::test::RunUntil([&]() { return 0u == root->child_count(); }));
390
391 // The root rfh and rvh should remain unchanged however the child frames
392 // should have been cleared.
393 EXPECT_EQ(initial_nav_entry_id,
394 wc->GetController().GetLastCommittedEntry()->GetUniqueID());
395 EXPECT_EQ(initial_isn,
396 initial_rfh->last_committed_frame_entry()->item_sequence_number());
397
398 EXPECT_TRUE(root->was_discarded());
399 EXPECT_TRUE(wc->GetController().NeedsReload());
400 EXPECT_EQ(initial_rfh.get(), wc->GetPrimaryMainFrame());
401 EXPECT_EQ(initial_rvh, wc->GetPrimaryMainFrame()->render_view_host());
Thomas Lukaszewicz8ada31b2024-09-01 19:18:16402 EXPECT_EQ(0u, root->child_count());
Thomas Lukaszewicz0ef0f45f2025-02-18 17:59:42403 uma_recorder.ExpectUniqueSample("Discarding.DiscardFrameTree", true, 1);
Thomas Lukaszewicz8ada31b2024-09-01 19:18:16404
Thomas Lukaszewicz54d8e432024-09-18 20:13:17405 if (KeepAliveDiscardedProcess()) {
406 EXPECT_TRUE(initial_rvh->IsRenderViewLive());
407 EXPECT_TRUE(initial_rfh->IsRenderFrameLive());
408 } else {
409 // After the document has been discarded the render process should have been
410 // cleared away.
411 EXPECT_FALSE(initial_rvh->IsRenderViewLive());
412 EXPECT_FALSE(initial_rfh->IsRenderFrameLive());
413 }
414
Thomas Lukaszewicz8ada31b2024-09-01 19:18:16415 // Reload the frame tree. Child frames should be reloaded and the root rfh and
416 // rvh should have changed.
417 wc->GetController().LoadIfNecessary();
418 EXPECT_TRUE(WaitForLoadStop(wc));
419
420 RenderFrameHostImplWrapper final_rfh(wc->GetPrimaryMainFrame());
421 RenderViewHostImpl* final_rvh = final_rfh->render_view_host();
422 EXPECT_EQ(initial_nav_entry_id,
423 wc->GetController().GetLastCommittedEntry()->GetUniqueID());
424 EXPECT_EQ(initial_isn,
425 final_rfh->last_committed_frame_entry()->item_sequence_number());
426 EXPECT_EQ(initial_isn_child1, get_child_isn(final_rfh.get(), 0));
427 EXPECT_EQ(initial_isn_child2, get_child_isn(final_rfh.get(), 1));
428 EXPECT_EQ(initial_isn_child3, get_child_isn(final_rfh.get(), 2));
429
430 EXPECT_FALSE(root->was_discarded());
431 EXPECT_FALSE(wc->GetController().NeedsReload());
432 EXPECT_EQ(3u, root->child_count());
433 EXPECT_NE(initial_rfh.get(), final_rfh.get());
Thomas Lukaszewicz8ada31b2024-09-01 19:18:16434 EXPECT_TRUE(final_rvh->IsRenderViewLive());
435 EXPECT_TRUE(final_rfh->IsRenderFrameLive());
Thomas Lukaszewicz54d8e432024-09-18 20:13:17436
437 if (KeepAliveDiscardedProcess()) {
438 EXPECT_NE(initial_rvh, final_rvh);
439 } else {
440 // TODO(crbug.com/40228869): It should be the case that a new RVH is created
441 // when reloading from a discarded state. This expectation for when the
442 // render process is shutdown should be merged with the one above once
443 // support for terminated processes is landed and all main-frame navigations
444 // use speculative RenderViewHosts.
445 EXPECT_EQ(initial_rvh, final_rvh);
446 }
Thomas Lukaszewicz8ada31b2024-09-01 19:18:16447}
448
449// Regression test for crbug.com/361658816. Ensures that same-document
450// navigations triggered in the document's unload handler are handled without
451// crashing.
Thomas Lukaszewicz54d8e432024-09-18 20:13:17452IN_PROC_BROWSER_TEST_P(FrameTreeBrowserWithDiscardTest,
Thomas Lukaszewicz8ada31b2024-09-01 19:18:16453 DiscardHandlesSameDocumentNavigationsDuringUnload) {
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(NavigateToURL(
459 shell(), embedded_test_server()->GetURL("/frame_tree/top.html")));
460
461 // Setup a same-document navigation in the unload handler.
462 ASSERT_TRUE(ExecJs(shell(), R"(
463 addEventListener('unload', () => {
464 history.pushState({}, '', 'title1.html');
465 });
466 )"));
467
468 // Discard the rfh, the frame and its children should be cleared successfully.
469 EXPECT_FALSE(root->was_discarded());
470 EXPECT_FALSE(wc->GetController().NeedsReload());
471 EXPECT_EQ(3UL, root->child_count());
Thomas Lukaszewicz54d8e432024-09-18 20:13:17472 DiscardFrameTree(frame_tree);
Thomas Lukaszewicz8ada31b2024-09-01 19:18:16473 EXPECT_TRUE(root->was_discarded());
474 EXPECT_TRUE(wc->GetController().NeedsReload());
475
476 ASSERT_TRUE(
477 base::test::RunUntil([&]() { return 0u == root->child_count(); }));
478}
479
Thomas Lukaszewicz1b672fe2024-09-17 08:35:03480// Runs pending navigation discard browsertests with RenderDocument enabled for
481// all frames to ensure a speculative RFH is created during navigation.
482class FrameTreeDiscardPendingNavigationTest
483 : public FrameTreeBrowserWithDiscardTest {
484 public:
485 FrameTreeDiscardPendingNavigationTest() {
486 InitAndEnableRenderDocumentFeature(
487 &feature_list_,
488 GetRenderDocumentLevelName(RenderDocumentLevel::kAllFrames));
489 }
490
491 private:
492 base::test::ScopedFeatureList feature_list_;
493};
494
Thomas Lukaszewicz54d8e432024-09-18 20:13:17495IN_PROC_BROWSER_TEST_P(FrameTreeDiscardPendingNavigationTest,
Thomas Lukaszewicz1b672fe2024-09-17 08:35:03496 DiscardHandlesNavigationWaitingResponse) {
497 WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents());
498 FrameTree& frame_tree = wc->GetPrimaryFrameTree();
499 FrameTreeNode* root = frame_tree.root();
500
501 const GURL original_url =
502 embedded_test_server()->GetURL("/frame_tree/top.html");
503 EXPECT_TRUE(NavigateToURL(shell(), original_url));
504 EXPECT_FALSE(root->was_discarded());
505 EXPECT_FALSE(wc->GetController().NeedsReload());
506 EXPECT_EQ(3UL, root->child_count());
507
508 // Queue a navigation.
509 const GURL new_url = embedded_test_server()->GetURL("/frame_tree/1-1.html");
510 TestNavigationManager manager(shell()->web_contents(), new_url);
511 shell()->LoadURL(new_url);
512
513 // Get to the point where the frame is waiting for the response.
514 EXPECT_TRUE(manager.WaitForRequestStart());
515 manager.ResumeNavigation();
516
517 // Discard while waiting for a response for the previous navigation.
Thomas Lukaszewicz54d8e432024-09-18 20:13:17518 DiscardFrameTree(frame_tree);
Thomas Lukaszewicz1b672fe2024-09-17 08:35:03519 EXPECT_TRUE(WaitForLoadStop(wc));
520 EXPECT_TRUE(root->was_discarded());
521 EXPECT_TRUE(wc->GetController().NeedsReload());
522
523 // Assert the pending navigation finished.
524 ASSERT_TRUE(manager.WaitForNavigationFinished());
525
526 // Wait for the discarded document to be replaced and clear its children.
527 ASSERT_TRUE(
528 base::test::RunUntil([&]() { return 0u == root->child_count(); }));
Thomas Lukaszewicz54d8e432024-09-18 20:13:17529 EXPECT_EQ(original_url, wc->GetLastCommittedURL());
Thomas Lukaszewicz1b672fe2024-09-17 08:35:03530}
531
Thomas Lukaszewicz54d8e432024-09-18 20:13:17532IN_PROC_BROWSER_TEST_P(FrameTreeDiscardPendingNavigationTest,
Thomas Lukaszewicz1b672fe2024-09-17 08:35:03533 DiscardHandlesNavigationPendingCommit) {
534 class ReadyToCommitWaiter : public content::WebContentsObserver {
535 public:
536 explicit ReadyToCommitWaiter(content::WebContents* web_contents)
537 : content::WebContentsObserver(web_contents) {}
538
539 void Wait() { run_loop_.Run(); }
540
541 void ReadyToCommitNavigation(
542 content::NavigationHandle* navigation_handle) override {
543 run_loop_.Quit();
544 }
545
546 private:
547 base::RunLoop run_loop_;
548 };
549
Thomas Lukaszewicz0ef0f45f2025-02-18 17:59:42550 base::HistogramTester uma_recorder;
Thomas Lukaszewicz1b672fe2024-09-17 08:35:03551 WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents());
552 FrameTree& frame_tree = wc->GetPrimaryFrameTree();
553 FrameTreeNode* root = frame_tree.root();
554
555 const GURL original_url =
556 embedded_test_server()->GetURL("/frame_tree/top.html");
557 EXPECT_TRUE(NavigateToURL(shell(), original_url));
558 EXPECT_FALSE(root->was_discarded());
559 EXPECT_FALSE(wc->GetController().NeedsReload());
560 EXPECT_EQ(3UL, root->child_count());
561
562 RenderFrameHostImplWrapper initial_rfh(wc->GetPrimaryMainFrame());
563 EXPECT_TRUE(initial_rfh->IsRenderFrameLive());
564
565 // Queue a navigation and wait until the browser is ready to commit.
566 ReadyToCommitWaiter ready_to_commit_waiter(wc);
567 const GURL new_url = embedded_test_server()->GetURL("/title1.html");
568 shell()->LoadURL(new_url);
569 ready_to_commit_waiter.Wait();
570
571 // Discard while ready to commit the previous navigation.
Thomas Lukaszewicz0ef0f45f2025-02-18 17:59:42572 uma_recorder.ExpectUniqueSample("Discarding.DiscardFrameTree", false, 0);
Thomas Lukaszewicz1b672fe2024-09-17 08:35:03573 frame_tree.Discard();
574 EXPECT_TRUE(WaitForLoadStop(wc));
575
576 // The pending navigation will commit to a new rfh and the tab will settle to
577 // an undiscarded state.
578 EXPECT_FALSE(root->was_discarded());
579 EXPECT_FALSE(wc->GetController().NeedsReload());
Thomas Lukaszewicz0ef0f45f2025-02-18 17:59:42580 uma_recorder.ExpectUniqueSample("Discarding.DiscardFrameTree", false, 1);
Thomas Lukaszewicz1b672fe2024-09-17 08:35:03581
582 RenderFrameHostImplWrapper final_rfh(wc->GetPrimaryMainFrame());
583 EXPECT_NE(initial_rfh.get(), final_rfh.get());
584 EXPECT_EQ(new_url, root->current_url());
585}
586
Thomas Lukaszewicz8067f2c12024-12-13 07:02:18587// Asserts that a process pinned with a keep-alive ref hosting only discarded
588// frames is successfully shutdown after the keep-alive timeout.
589IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest,
590 DiscardedFrameRendererShutdownAfterKeepAliveTimeout) {
591 WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents());
592 FrameTree& frame_tree = wc->GetPrimaryFrameTree();
593
594 EXPECT_TRUE(
595 NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html")));
596
597 // Ensure the view, frame and process are reported alive.
598 RenderFrameHostImpl* rfh =
599 static_cast<RenderFrameHostImpl*>(wc->GetPrimaryMainFrame());
600 RenderViewHostImpl* rvh = rfh->render_view_host();
601 RenderProcessHostImpl* rph =
602 static_cast<RenderProcessHostImpl*>(rfh->GetProcess());
603 EXPECT_TRUE(rvh->IsRenderViewLive());
604 EXPECT_TRUE(rfh->IsRenderFrameLive());
605 EXPECT_TRUE(rph->IsInitializedAndNotDead());
606
607 // Set a keep-alive on the renderer process.
608 rph->IncrementKeepAliveRefCount(0);
609
610 // Discard the frame tree. The process should remain alive.
611 frame_tree.Discard();
612 EXPECT_TRUE(rvh->IsRenderViewLive());
613 EXPECT_TRUE(rfh->IsRenderFrameLive());
614 EXPECT_TRUE(rph->IsInitializedAndNotDead());
615
616 // Simulate a keep-alive timeout, the process should be promptly shutdown.
617 rfh->SimulateDiscardShutdownKeepAliveTimeoutForTesting();
618 RenderProcessHostWatcher process_exit_observer(
619 wc, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
620 process_exit_observer.Wait();
621
622 // Ensure the process has been successfully shutdown.
623 EXPECT_FALSE(rvh->IsRenderViewLive());
624 EXPECT_FALSE(rfh->IsRenderFrameLive());
625 EXPECT_FALSE(rph->IsInitializedAndNotDead());
626}
627
Thomas Lukaszewicz8ada31b2024-09-01 19:18:16628class DedicatedWorkerObserver : public DedicatedWorkerService::Observer {
629 public:
630 explicit DedicatedWorkerObserver(DedicatedWorkerService* worker_service) {
631 scoped_context_observation_.Observe(worker_service);
632 }
633 DedicatedWorkerObserver(const DedicatedWorkerObserver&) = delete;
634 DedicatedWorkerObserver& operator=(const DedicatedWorkerObserver&) = delete;
635 ~DedicatedWorkerObserver() override = default;
636
637 void WaitForCreated() {
638 if (!is_created_) {
639 run_loop_.emplace();
640 run_loop_->Run();
641 run_loop_.reset();
642 }
643 }
644 void WaitForDestroyed() {
645 if (!is_destroyed_) {
646 run_loop_.emplace();
647 run_loop_->Run();
648 run_loop_.reset();
649 }
650 }
651
652 // DedicatedWorkerService::Observer:
653 void OnBeforeWorkerDestroyed(const blink::DedicatedWorkerToken& worker_token,
654 DedicatedWorkerCreator creator) override {
655 is_destroyed_ = true;
656 if (run_loop_.has_value()) {
657 run_loop_->Quit();
658 }
659 }
660 void OnWorkerCreated(const blink::DedicatedWorkerToken& worker_token,
661 int worker_process_id,
662 const url::Origin& security_origin,
663 DedicatedWorkerCreator creator) override {
664 is_created_ = true;
665 if (run_loop_.has_value()) {
666 run_loop_->Quit();
667 }
668 }
669 void OnFinalResponseURLDetermined(
670 const blink::DedicatedWorkerToken& worker_token,
671 const GURL& url) override {}
672
673 private:
674 bool is_created_ = false;
675 bool is_destroyed_ = false;
676 std::optional<base::RunLoop> run_loop_;
677 base::ScopedObservation<DedicatedWorkerService,
678 DedicatedWorkerService::Observer>
679 scoped_context_observation_{this};
680};
681
682class DedicatedWorkerFrameTreeBrowserTest
683 : public FrameTreeBrowserWithDiscardTest {
684 public:
685 void SetUpCommandLine(base::CommandLine* command_line) override {
686 FrameTreeBrowserTest::SetUpCommandLine(command_line);
687 command_line->AppendSwitchASCII(blink::switches::kJavaScriptFlags,
688 "--expose-gc");
689 }
690};
691
Thomas Lukaszewicz54d8e432024-09-18 20:13:17692IN_PROC_BROWSER_TEST_P(DedicatedWorkerFrameTreeBrowserTest,
Thomas Lukaszewicz8ada31b2024-09-01 19:18:16693 DiscardClearsDedicatedWorkers) {
694 WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents());
695 FrameTree& frame_tree = wc->GetPrimaryFrameTree();
696 FrameTreeNode* root = frame_tree.root();
697
698 EXPECT_TRUE(
699 NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html")));
700
701 // Navigate to a page and register a dedicated worker.
702 DedicatedWorkerObserver worker_observer(root->current_frame_host()
703 ->GetStoragePartition()
704 ->GetDedicatedWorkerService());
705 EXPECT_TRUE(EvalJs(shell(), "const worker = new Worker('/workers/empty.js');")
706 .error.empty());
707 worker_observer.WaitForCreated();
708
709 // Discard the rfh, the associated worker should be cleared.
710 EXPECT_FALSE(root->was_discarded());
711 EXPECT_FALSE(wc->GetController().NeedsReload());
Thomas Lukaszewicz54d8e432024-09-18 20:13:17712 DiscardFrameTree(frame_tree);
Thomas Lukaszewicz8ada31b2024-09-01 19:18:16713 EXPECT_TRUE(root->was_discarded());
714 EXPECT_TRUE(wc->GetController().NeedsReload());
Thomas Lukaszewicz54d8e432024-09-18 20:13:17715
716 if (KeepAliveDiscardedProcess()) {
717 // Trigger GC to cleanup the worker in the renderer if persisted.
718 EXPECT_TRUE(EvalJs(shell(), "window.gc();").error.empty());
719 }
720
Thomas Lukaszewicz8ada31b2024-09-01 19:18:16721 worker_observer.WaitForDestroyed();
722}
723
724// TODO(347770670): Consider restricting script access to discarded documents
725// from related documents.
Thomas Lukaszewicz54d8e432024-09-18 20:13:17726IN_PROC_BROWSER_TEST_P(FrameTreeBrowserWithDiscardTest,
Thomas Lukaszewicz8ada31b2024-09-01 19:18:16727 DiscardedFrameAllowsScriptAccess) {
Thomas Lukaszewicz54d8e432024-09-18 20:13:17728 if (!KeepAliveDiscardedProcess()) {
729 GTEST_SKIP() << "Not applicable when destroying process post discard.";
730 }
731
Thomas Lukaszewicz8ada31b2024-09-01 19:18:16732 WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents());
733 FrameTree& frame_tree = wc->GetPrimaryFrameTree();
734
735 const GURL main_url(embedded_test_server()->GetURL("/title1.html"));
736 EXPECT_TRUE(NavigateToURL(shell(), main_url));
737
738 // Open a popup using window.open.
739 const GURL popup_url(embedded_test_server()->GetURL("/title2.html"));
740 Shell* new_shell = OpenPopup(shell(), popup_url, "foo");
741 EXPECT_TRUE(new_shell);
742
743 // Assert the opened window is able to script its opener.
744 EXPECT_EQ("foo", EvalJs(new_shell, "window.name;"));
745 EXPECT_TRUE(EvalJs(new_shell, "window.opener.name = 'bar';").error.empty());
746 EXPECT_EQ("bar", EvalJs(shell(), "window.name;"));
747
748 frame_tree.Discard();
749 EXPECT_TRUE(frame_tree.root()->was_discarded());
750
751 // After a discard operation the opened window should should still be able to
752 // script its opener.
753 EXPECT_TRUE(EvalJs(new_shell, "window.opener.name = 'bar2';").error.empty());
754 EXPECT_EQ("bar2", EvalJs(shell(), "window.name;"));
755
756 // After a reload the opened window should still be able to script its opener.
757 wc->GetController().LoadIfNecessary();
758 EXPECT_TRUE(WaitForLoadStop(wc));
759 EXPECT_TRUE(EvalJs(new_shell, "window.opener.name = 'bar3';").error.empty());
760 EXPECT_EQ("bar3", EvalJs(shell(), "window.name;"));
761}
762
763class FrameTreeBrowserTestWithBFCache : public FrameTreeBrowserTest {
764 public:
765 FrameTreeBrowserTestWithBFCache() {
766 feature_list_.InitWithFeaturesAndParameters(
767 {{features::kBackForwardCache, {}},
768 {kBackForwardCacheNoTimeEviction, {}}},
769 // Allow BackForwardCache for all devices regardless of their memory.
770 {features::kBackForwardCacheMemoryControls});
771 EXPECT_TRUE(IsBackForwardCacheEnabled());
772 }
773
774 private:
775 base::test::ScopedFeatureList feature_list_;
776};
777
778IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTestWithBFCache,
779 FrameTreeBrowserWithDiscardTest) {
780 WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents());
781 FrameTree& frame_tree = wc->GetPrimaryFrameTree();
782 BackForwardCacheImpl& back_forward_cache =
783 wc->GetController().GetBackForwardCache();
784
785 // The BFCache should start empty.
786 EXPECT_TRUE(back_forward_cache.GetEntries().empty());
787
788 const GURL url1(embedded_test_server()->GetURL("a.com", "/title1.html"));
789 const GURL url2(embedded_test_server()->GetURL("b.com", "/title2.html"));
790
791 // Navigate to url1.
792 EXPECT_TRUE(NavigateToURL(shell(), url1));
793 RenderFrameHostImplWrapper rfh_a(wc->GetPrimaryMainFrame());
794 RenderFrameDeletedObserver delete_observer_rfh_a(rfh_a.get());
795
796 // Navigate to url2, the frame hosting url1 should be moved to the BFCache.
797 EXPECT_TRUE(NavigateToURL(shell(), url2));
798 EXPECT_FALSE(delete_observer_rfh_a.deleted());
799 EXPECT_TRUE(rfh_a->IsInBackForwardCache());
800 EXPECT_EQ(1u, back_forward_cache.GetEntries().size());
801
802 // Discard the frame tree, the BFCache should have been cleared.
803 frame_tree.Discard();
804 EXPECT_TRUE(frame_tree.root()->was_discarded());
805 back_forward_cache.PostTaskToDestroyEvictedFrames();
806 ASSERT_TRUE(base::test::RunUntil(
807 [&]() { return back_forward_cache.GetEntries().empty(); }));
808 EXPECT_TRUE(delete_observer_rfh_a.deleted());
809}
810
811IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTestWithBFCache,
812 DiscardedFrameDoesNotEnterBFCache) {
813 WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents());
814 FrameTree& frame_tree = wc->GetPrimaryFrameTree();
815 BackForwardCacheImpl& back_forward_cache =
816 wc->GetController().GetBackForwardCache();
817
818 // The BFCache should start empty.
819 EXPECT_TRUE(back_forward_cache.GetEntries().empty());
820
821 const GURL url1(embedded_test_server()->GetURL("a.com", "/title1.html"));
822 const GURL url2(embedded_test_server()->GetURL("b.com", "/title2.html"));
823
824 // Navigate to url1.
825 EXPECT_TRUE(NavigateToURL(shell(), url1));
826 RenderFrameHostImplWrapper rfh_a(wc->GetPrimaryMainFrame());
827 RenderFrameDeletedObserver delete_observer_rfh_a(rfh_a.get());
828
829 // Discard the frame tree.
830 frame_tree.Discard();
831 EXPECT_TRUE(frame_tree.root()->was_discarded());
832 EXPECT_FALSE(delete_observer_rfh_a.deleted());
833
834 // Navigate to url2, the frame hosting url1 should not be moved to the
835 // BFCache.
836 EXPECT_TRUE(NavigateToURL(shell(), url2));
837 EXPECT_FALSE(frame_tree.root()->was_discarded());
838 EXPECT_TRUE(back_forward_cache.GetEntries().empty());
839 ASSERT_TRUE(
840 base::test::RunUntil([&]() { return delete_observer_rfh_a.deleted(); }));
841}
842
[email protected]5f96f5a62014-01-10 00:05:11843// Test that we can navigate away if the previous renderer doesn't clean up its
844// child frames.
Nasko Oskov31c45ff2019-10-16 01:15:19845IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, NavigateWithLeftoverFrames) {
alexmos478dcbb2014-12-10 21:24:46846 GURL base_url = embedded_test_server()->GetURL("A.com", "/site_isolation/");
[email protected]5f96f5a62014-01-10 00:05:11847
Alex Moshchukaa95adf52019-08-13 00:02:02848 EXPECT_TRUE(NavigateToURL(
849 shell(), embedded_test_server()->GetURL("/frame_tree/top.html")));
[email protected]5f96f5a62014-01-10 00:05:11850
851 // Hang the renderer so that it doesn't send any FrameDetached messages.
852 // (This navigation will never complete, so don't wait for it.)
Gyuyoung Kim107c2a02021-04-13 01:49:30853 shell()->LoadURL(GURL(blink::kChromeUIHangURL));
[email protected]5f96f5a62014-01-10 00:05:11854
855 // Check that the frame tree still has children.
856 WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents());
Carlos Caballero15caeeb2021-10-27 09:57:55857 FrameTreeNode* root = wc->GetPrimaryFrameTree().root();
[email protected]5f96f5a62014-01-10 00:05:11858 ASSERT_EQ(3UL, root->child_count());
859
860 // Navigate to a new URL. We use LoadURL because NavigateToURL will try to
861 // wait for the previous navigation to stop.
862 TestNavigationObserver tab_observer(wc, 1);
863 shell()->LoadURL(base_url.Resolve("blank.html"));
864 tab_observer.Wait();
865
[email protected]58faf942014-02-20 21:03:58866 // The frame tree should now be cleared.
[email protected]5f96f5a62014-01-10 00:05:11867 EXPECT_EQ(0UL, root->child_count());
[email protected]5f96f5a62014-01-10 00:05:11868}
869
creise42f2a52014-09-18 18:14:57870// Ensure that IsRenderFrameLive is true for main frames and same-site iframes.
871IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, IsRenderFrameLive) {
alexmos478dcbb2014-12-10 21:24:46872 GURL main_url(embedded_test_server()->GetURL("/frame_tree/top.html"));
Alex Moshchukaa95adf52019-08-13 00:02:02873 EXPECT_TRUE(NavigateToURL(shell(), main_url));
creise42f2a52014-09-18 18:14:57874
875 // It is safe to obtain the root frame tree node here, as it doesn't change.
876 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:55877 ->GetPrimaryFrameTree()
878 .root();
creise42f2a52014-09-18 18:14:57879
880 // The root and subframe should each have a live RenderFrame.
881 EXPECT_TRUE(
882 root->current_frame_host()->render_view_host()->IsRenderViewLive());
883 EXPECT_TRUE(root->current_frame_host()->IsRenderFrameLive());
884 EXPECT_TRUE(root->child_at(0)->current_frame_host()->IsRenderFrameLive());
885
886 // Load a same-site page into iframe and it should still be live.
alexmos478dcbb2014-12-10 21:24:46887 GURL http_url(embedded_test_server()->GetURL("/title1.html"));
Lukasz Anforowicz69c25dfd2020-11-12 21:50:20888 EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(0), http_url));
creise42f2a52014-09-18 18:14:57889 EXPECT_TRUE(
890 root->current_frame_host()->render_view_host()->IsRenderViewLive());
891 EXPECT_TRUE(root->current_frame_host()->IsRenderFrameLive());
892 EXPECT_TRUE(root->child_at(0)->current_frame_host()->IsRenderFrameLive());
893}
894
alexmosbc7eafa2014-12-06 01:38:09895// Ensure that origins are correctly set on navigations.
896IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, OriginSetOnNavigation) {
nick1466c842015-11-25 20:08:06897 GURL about_blank(url::kAboutBlankURL);
898 GURL main_url(
899 embedded_test_server()->GetURL("a.com", "/frame_tree/top.html"));
alexmosbc7eafa2014-12-06 01:38:09900 EXPECT_TRUE(NavigateToURL(shell(), main_url));
nick1466c842015-11-25 20:08:06901 WebContents* contents = shell()->web_contents();
alexmosbc7eafa2014-12-06 01:38:09902
903 // It is safe to obtain the root frame tree node here, as it doesn't change.
nick1466c842015-11-25 20:08:06904 FrameTreeNode* root =
Carlos Caballero15caeeb2021-10-27 09:57:55905 static_cast<WebContentsImpl*>(contents)->GetPrimaryFrameTree().root();
alexmosbc7eafa2014-12-06 01:38:09906
907 // Extra '/' is added because the replicated origin is serialized in RFC 6454
908 // format, which dictates no trailing '/', whereas GURL::GetOrigin does put a
909 // '/' at the end.
Mike West800532c2021-10-14 09:26:52910 EXPECT_EQ(main_url.DeprecatedGetOriginAsURL().spec(),
nick1466c842015-11-25 20:08:06911 root->current_origin().Serialize() + '/');
912 EXPECT_EQ(
Mike West800532c2021-10-14 09:26:52913 main_url.DeprecatedGetOriginAsURL().spec(),
nick1466c842015-11-25 20:08:06914 root->current_frame_host()->GetLastCommittedOrigin().Serialize() + '/');
alexmosbc7eafa2014-12-06 01:38:09915
nick1466c842015-11-25 20:08:06916 // The iframe is inititially same-origin.
917 EXPECT_TRUE(
918 root->current_frame_host()->GetLastCommittedOrigin().IsSameOriginWith(
919 root->child_at(0)->current_frame_host()->GetLastCommittedOrigin()));
920 EXPECT_EQ(root->current_origin().Serialize(), GetOriginFromRenderer(root));
921 EXPECT_EQ(root->child_at(0)->current_origin().Serialize(),
922 GetOriginFromRenderer(root->child_at(0)));
923
924 // Navigate the iframe cross-origin.
925 GURL frame_url(embedded_test_server()->GetURL("b.com", "/title1.html"));
Lukasz Anforowicz69c25dfd2020-11-12 21:50:20926 EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(0), frame_url));
nick1466c842015-11-25 20:08:06927 EXPECT_EQ(frame_url, root->child_at(0)->current_url());
Mike West800532c2021-10-14 09:26:52928 EXPECT_EQ(frame_url.DeprecatedGetOriginAsURL().spec(),
nick1466c842015-11-25 20:08:06929 root->child_at(0)->current_origin().Serialize() + '/');
930 EXPECT_FALSE(
931 root->current_frame_host()->GetLastCommittedOrigin().IsSameOriginWith(
932 root->child_at(0)->current_frame_host()->GetLastCommittedOrigin()));
933 EXPECT_EQ(root->current_origin().Serialize(), GetOriginFromRenderer(root));
934 EXPECT_EQ(root->child_at(0)->current_origin().Serialize(),
935 GetOriginFromRenderer(root->child_at(0)));
alexmosbc7eafa2014-12-06 01:38:09936
nick1466c842015-11-25 20:08:06937 // Parent-initiated about:blank navigation should inherit the parent's a.com
938 // origin.
939 NavigateIframeToURL(contents, "1-1-id", about_blank);
940 EXPECT_EQ(about_blank, root->child_at(0)->current_url());
Mike West800532c2021-10-14 09:26:52941 EXPECT_EQ(main_url.DeprecatedGetOriginAsURL().spec(),
nick1466c842015-11-25 20:08:06942 root->child_at(0)->current_origin().Serialize() + '/');
943 EXPECT_EQ(root->current_frame_host()->GetLastCommittedOrigin().Serialize(),
944 root->child_at(0)
945 ->current_frame_host()
946 ->GetLastCommittedOrigin()
947 .Serialize());
948 EXPECT_TRUE(
949 root->current_frame_host()->GetLastCommittedOrigin().IsSameOriginWith(
950 root->child_at(0)->current_frame_host()->GetLastCommittedOrigin()));
951 EXPECT_EQ(root->current_origin().Serialize(), GetOriginFromRenderer(root));
952 EXPECT_EQ(root->child_at(0)->current_origin().Serialize(),
953 GetOriginFromRenderer(root->child_at(0)));
alexmosbc7eafa2014-12-06 01:38:09954
955 GURL data_url("data:text/html,foo");
956 EXPECT_TRUE(NavigateToURL(shell(), data_url));
957
958 // Navigating to a data URL should set a unique origin. This is represented
959 // as "null" per RFC 6454.
nick1466c842015-11-25 20:08:06960 EXPECT_EQ("null", root->current_origin().Serialize());
Dave Tapuska327c06c92022-06-13 20:31:51961 EXPECT_TRUE(
962 contents->GetPrimaryMainFrame()->GetLastCommittedOrigin().opaque());
nick1466c842015-11-25 20:08:06963 EXPECT_EQ("null", GetOriginFromRenderer(root));
alexmosbc7eafa2014-12-06 01:38:09964
965 // Re-navigating to a normal URL should update the origin.
966 EXPECT_TRUE(NavigateToURL(shell(), main_url));
Mike West800532c2021-10-14 09:26:52967 EXPECT_EQ(main_url.DeprecatedGetOriginAsURL().spec(),
nick1466c842015-11-25 20:08:06968 root->current_origin().Serialize() + '/');
969 EXPECT_EQ(
Mike West800532c2021-10-14 09:26:52970 main_url.DeprecatedGetOriginAsURL().spec(),
Dave Tapuska327c06c92022-06-13 20:31:51971 contents->GetPrimaryMainFrame()->GetLastCommittedOrigin().Serialize() +
972 '/');
973 EXPECT_FALSE(
974 contents->GetPrimaryMainFrame()->GetLastCommittedOrigin().opaque());
nick1466c842015-11-25 20:08:06975 EXPECT_EQ(root->current_origin().Serialize(), GetOriginFromRenderer(root));
alexmosbc7eafa2014-12-06 01:38:09976}
977
nick9f34e2892016-01-12 21:01:07978// Tests a cross-origin navigation to a blob URL. The main frame initiates this
979// navigation on its grandchild. It should wind up in the main frame's process.
980IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, NavigateGrandchildToBlob) {
981 WebContents* contents = shell()->web_contents();
982 FrameTreeNode* root =
Carlos Caballero15caeeb2021-10-27 09:57:55983 static_cast<WebContentsImpl*>(contents)->GetPrimaryFrameTree().root();
nick9f34e2892016-01-12 21:01:07984
985 // First, snapshot the FrameTree for a normal A(B(A)) case where all frames
986 // are served over http. The blob test should result in the same structure.
987 EXPECT_TRUE(NavigateToURL(
988 shell(), embedded_test_server()->GetURL(
989 "a.com", "/cross_site_iframe_factory.html?a(b(a))")));
Fergal Daly79f44292020-12-01 02:30:48990 std::string reference_tree = DepictFrameTree(*root);
nick9f34e2892016-01-12 21:01:07991
992 GURL main_url(embedded_test_server()->GetURL(
993 "a.com", "/cross_site_iframe_factory.html?a(b(c))"));
994 EXPECT_TRUE(NavigateToURL(shell(), main_url));
995
996 // The root node will initiate the navigation; its grandchild node will be the
997 // target of the navigation.
998 FrameTreeNode* target = root->child_at(0)->child_at(0);
999
creisb2d5f1f32016-11-07 23:25:051000 RenderFrameDeletedObserver deleted_observer(target->current_frame_host());
Nick Carterb7e71312018-08-03 23:36:131001 std::string html =
1002 "<html><body><div>This is blob content.</div>"
1003 "<script>"
Philip Jägenstedt67302a22018-09-14 09:58:051004 "window.parent.parent.postMessage('HI', self.origin);"
Nick Carterb7e71312018-08-03 23:36:131005 "</script></body></html>";
1006 std::string script = JsReplace(
1007 "new Promise((resolve) => {"
1008 " window.addEventListener('message', resolve, false);"
1009 " var blob = new Blob([$1], {type: 'text/html'});"
1010 " var blob_url = URL.createObjectURL(blob);"
1011 " frames[0][0].location.href = blob_url;"
1012 "}).then((event) => {"
nick9f34e2892016-01-12 21:01:071013 " document.body.appendChild(document.createTextNode(event.data));"
Nick Carterb7e71312018-08-03 23:36:131014 " return event.source.location.href;"
1015 "});",
1016 html);
1017 std::string blob_url_string = EvalJs(root, script).ExtractString();
creisb2d5f1f32016-11-07 23:25:051018 // Wait for the RenderFrame to go away, if this will be cross-process.
1019 if (AreAllSitesIsolatedForTesting())
1020 deleted_observer.WaitUntilDeleted();
nick9f34e2892016-01-12 21:01:071021 EXPECT_EQ(GURL(blob_url_string), target->current_url());
1022 EXPECT_EQ(url::kBlobScheme, target->current_url().scheme());
Chris Palmerab5e5b52018-09-28 19:19:301023 EXPECT_FALSE(target->current_origin().opaque());
nick9f34e2892016-01-12 21:01:071024 EXPECT_EQ("a.com", target->current_origin().host());
1025 EXPECT_EQ(url::kHttpScheme, target->current_origin().scheme());
Nick Carterb7e71312018-08-03 23:36:131026 EXPECT_EQ("This is blob content.",
1027 EvalJs(target, "document.body.children[0].innerHTML"));
Fergal Daly79f44292020-12-01 02:30:481028 EXPECT_EQ(reference_tree, DepictFrameTree(*root));
nick9f34e2892016-01-12 21:01:071029}
1030
1031IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, NavigateChildToAboutBlank) {
1032 GURL main_url(embedded_test_server()->GetURL(
1033 "a.com", "/cross_site_iframe_factory.html?a(b(c))"));
1034 EXPECT_TRUE(NavigateToURL(shell(), main_url));
1035 WebContentsImpl* contents =
1036 static_cast<WebContentsImpl*>(shell()->web_contents());
1037
1038 // The leaf node (c.com) will be navigated. Its parent node (b.com) will
1039 // initiate the navigation.
1040 FrameTreeNode* target =
Carlos Caballero15caeeb2021-10-27 09:57:551041 contents->GetPrimaryFrameTree().root()->child_at(0)->child_at(0);
Harkiran Bolaria4eaae3072021-09-21 10:45:481042 RenderFrameHost* initiator_rfh = target->parent();
nick9f34e2892016-01-12 21:01:071043
1044 // Give the target a name.
Nick Carterb7e71312018-08-03 23:36:131045 EXPECT_TRUE(ExecJs(target, "window.name = 'target';"));
nick9f34e2892016-01-12 21:01:071046
1047 // Use window.open(about:blank), then poll the document for access.
Nick Carterb7e71312018-08-03 23:36:131048 EvalJsResult about_blank_origin = EvalJs(
Harkiran Bolaria4eaae3072021-09-21 10:45:481049 initiator_rfh,
Nick Carterb7e71312018-08-03 23:36:131050 "new Promise(resolve => {"
1051 " var didNavigate = false;"
1052 " var intervalID = setInterval(function() {"
1053 " if (!didNavigate) {"
1054 " didNavigate = true;"
1055 " window.open('about:blank', 'target');"
1056 " }"
1057 " // Poll the document until it doesn't throw a SecurityError.\n"
1058 " try {"
1059 " frames[0].document.write('Hi from ' + document.domain);"
1060 " } catch (e) { return; }"
1061 " clearInterval(intervalID);"
Philip Jägenstedt67302a22018-09-14 09:58:051062 " resolve(frames[0].self.origin);"
Nick Carterb7e71312018-08-03 23:36:131063 " }, 16);"
1064 "});");
1065 EXPECT_EQ(target->current_origin(), about_blank_origin);
Nate Chapinab67b042021-05-04 21:00:511066 EXPECT_EQ(GURL(url::kAboutBlankURL), target->current_url());
1067 EXPECT_EQ(url::kAboutScheme, target->current_url().scheme());
Chris Palmerab5e5b52018-09-28 19:19:301068 EXPECT_FALSE(target->current_origin().opaque());
nick9f34e2892016-01-12 21:01:071069 EXPECT_EQ("b.com", target->current_origin().host());
1070 EXPECT_EQ(url::kHttpScheme, target->current_origin().scheme());
nick9f34e2892016-01-12 21:01:071071
Nick Carterb7e71312018-08-03 23:36:131072 EXPECT_EQ("Hi from b.com", EvalJs(target, "document.body.innerHTML"));
nick9f34e2892016-01-12 21:01:071073}
1074
1075// Nested iframes, three origins: A(B(C)). Frame A navigates C to about:blank
1076// (via window.open). This should wind up in A's origin per the spec. Test fails
1077// because of http://crbug.com/564292
1078IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest,
1079 DISABLED_NavigateGrandchildToAboutBlank) {
1080 GURL main_url(embedded_test_server()->GetURL(
1081 "a.com", "/cross_site_iframe_factory.html?a(b(c))"));
1082 EXPECT_TRUE(NavigateToURL(shell(), main_url));
1083 WebContentsImpl* contents =
1084 static_cast<WebContentsImpl*>(shell()->web_contents());
1085
1086 // The leaf node (c.com) will be navigated. Its grandparent node (a.com) will
1087 // initiate the navigation.
1088 FrameTreeNode* target =
Carlos Caballero15caeeb2021-10-27 09:57:551089 contents->GetPrimaryFrameTree().root()->child_at(0)->child_at(0);
Harkiran Bolaria4eaae3072021-09-21 10:45:481090 RenderFrameHost* initiator_rfh = target->parent()->GetParent();
nick9f34e2892016-01-12 21:01:071091
1092 // Give the target a name.
Nick Carterb7e71312018-08-03 23:36:131093 EXPECT_TRUE(ExecJs(target, "window.name = 'target';"));
nick9f34e2892016-01-12 21:01:071094
1095 // Use window.open(about:blank), then poll the document for access.
Nick Carterb7e71312018-08-03 23:36:131096 EvalJsResult about_blank_origin =
Harkiran Bolaria4eaae3072021-09-21 10:45:481097 EvalJs(initiator_rfh,
Nick Carterb7e71312018-08-03 23:36:131098 "new Promise((resolve) => {"
1099 " var didNavigate = false;"
1100 " var intervalID = setInterval(() => {"
1101 " if (!didNavigate) {"
1102 " didNavigate = true;"
1103 " window.open('about:blank', 'target');"
1104 " }"
1105 " // May raise a SecurityError, that's expected.\n"
1106 " try {"
1107 " frames[0][0].document.write('Hi from ' + document.domain);"
1108 " } catch (e) { return; }"
1109 " clearInterval(intervalID);"
Philip Jägenstedt67302a22018-09-14 09:58:051110 " resolve(frames[0][0].self.origin);"
Nick Carterb7e71312018-08-03 23:36:131111 " }, 16);"
1112 "});");
1113 EXPECT_EQ(target->current_origin(), about_blank_origin);
nick9f34e2892016-01-12 21:01:071114 EXPECT_EQ(GURL(url::kAboutBlankURL), target->current_url());
1115 EXPECT_EQ(url::kAboutScheme, target->current_url().scheme());
Chris Palmerab5e5b52018-09-28 19:19:301116 EXPECT_FALSE(target->current_origin().opaque());
nick9f34e2892016-01-12 21:01:071117 EXPECT_EQ("a.com", target->current_origin().host());
1118 EXPECT_EQ(url::kHttpScheme, target->current_origin().scheme());
nick9f34e2892016-01-12 21:01:071119
Nick Carterb7e71312018-08-03 23:36:131120 EXPECT_EQ("Hi from a.com", EvalJs(target, "document.body.innerHTML"));
nick9f34e2892016-01-12 21:01:071121}
1122
Nasko Oskov039121012019-01-11 00:21:321123// Tests a cross-origin navigation to a data: URL. The main frame initiates this
1124// navigation on its grandchild. It should wind up in the main frame's process
1125// and have precursor origin of the main frame origin.
1126IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, NavigateGrandchildToDataUrl) {
1127 GURL main_url(embedded_test_server()->GetURL(
1128 "a.com", "/cross_site_iframe_factory.html?a(b(c))"));
1129 EXPECT_TRUE(NavigateToURL(shell(), main_url));
1130 WebContentsImpl* contents =
1131 static_cast<WebContentsImpl*>(shell()->web_contents());
1132
1133 // The leaf node (c.com) will be navigated. Its grandparent node (a.com) will
1134 // initiate the navigation.
1135 FrameTreeNode* target =
Carlos Caballero15caeeb2021-10-27 09:57:551136 contents->GetPrimaryFrameTree().root()->child_at(0)->child_at(0);
Harkiran Bolaria4eaae3072021-09-21 10:45:481137 RenderFrameHostImpl* initiator_rfh = target->parent()->GetParent();
Nasko Oskov039121012019-01-11 00:21:321138
1139 // Give the target a name.
1140 EXPECT_TRUE(ExecJs(target, "window.name = 'target';"));
1141
1142 // Navigate the target frame through the initiator frame.
1143 {
1144 TestFrameNavigationObserver observer(target);
Harkiran Bolaria4eaae3072021-09-21 10:45:481145 EXPECT_TRUE(ExecJs(initiator_rfh,
1146 "window.open('data:text/html,content', 'target');"));
Nasko Oskov039121012019-01-11 00:21:321147 observer.Wait();
1148 }
1149
1150 url::Origin original_target_origin =
1151 target->current_frame_host()->GetLastCommittedOrigin();
1152 EXPECT_TRUE(original_target_origin.opaque());
1153 EXPECT_EQ(original_target_origin.GetTupleOrPrecursorTupleIfOpaque(),
1154 url::SchemeHostPort(main_url));
1155
1156 // Navigate the grandchild frame again cross-process to foo.com, then
Rakina Zata Amnic7367852022-11-07 17:10:401157 // go back in session history. The frame should commit a new opaque origin,
1158 // but it will still have the same precursor origin (the main frame origin).
Nasko Oskov039121012019-01-11 00:21:321159 {
1160 TestFrameNavigationObserver observer(target);
1161 EXPECT_TRUE(ExecJs(target, JsReplace("window.location = $1",
1162 embedded_test_server()->GetURL(
1163 "foo.com", "/title2.html"))));
1164 observer.Wait();
1165 }
1166 EXPECT_NE(original_target_origin,
1167 target->current_frame_host()->GetLastCommittedOrigin());
1168 {
1169 TestFrameNavigationObserver observer(target);
1170 contents->GetController().GoBack();
1171 observer.Wait();
1172 }
1173
1174 url::Origin target_origin =
1175 target->current_frame_host()->GetLastCommittedOrigin();
Rakina Zata Amnic7367852022-11-07 17:10:401176 EXPECT_NE(target_origin, original_target_origin);
Nasko Oskov039121012019-01-11 00:21:321177 EXPECT_TRUE(target_origin.opaque());
1178 EXPECT_EQ(target_origin.GetTupleOrPrecursorTupleIfOpaque(),
Rakina Zata Amnic7367852022-11-07 17:10:401179 original_target_origin.GetTupleOrPrecursorTupleIfOpaque());
1180 EXPECT_EQ(target_origin.GetTupleOrPrecursorTupleIfOpaque(),
Nasko Oskov039121012019-01-11 00:21:321181 url::SchemeHostPort(main_url));
Nasko Oskov039121012019-01-11 00:21:321182}
1183
naskofaa01fb2016-04-30 01:04:171184// Ensures that iframe with srcdoc is always put in the same origin as its
1185// parent frame.
1186IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, ChildFrameWithSrcdoc) {
1187 GURL main_url(embedded_test_server()->GetURL(
1188 "a.com", "/cross_site_iframe_factory.html?a(b)"));
1189 EXPECT_TRUE(NavigateToURL(shell(), main_url));
1190 WebContentsImpl* contents =
1191 static_cast<WebContentsImpl*>(shell()->web_contents());
Carlos Caballero15caeeb2021-10-27 09:57:551192 FrameTreeNode* root = contents->GetPrimaryFrameTree().root();
naskofaa01fb2016-04-30 01:04:171193 EXPECT_EQ(1U, root->child_count());
1194
1195 FrameTreeNode* child = root->child_at(0);
Philip Jägenstedt67302a22018-09-14 09:58:051196 std::string frame_origin = EvalJs(child, "self.origin;").ExtractString();
naskofaa01fb2016-04-30 01:04:171197 EXPECT_TRUE(
1198 child->current_frame_host()->GetLastCommittedOrigin().IsSameOriginWith(
Daniel Cheng88186bd52017-10-20 08:14:461199 url::Origin::Create(GURL(frame_origin))));
naskofaa01fb2016-04-30 01:04:171200 EXPECT_FALSE(
1201 root->current_frame_host()->GetLastCommittedOrigin().IsSameOriginWith(
Daniel Cheng88186bd52017-10-20 08:14:461202 url::Origin::Create(GURL(frame_origin))));
naskofaa01fb2016-04-30 01:04:171203
1204 // Create a new iframe with srcdoc and add it to the main frame. It should
1205 // be created in the same SiteInstance as the parent.
1206 {
Aran Gilman37d11632019-10-08 23:07:151207 std::string script(
1208 "var f = document.createElement('iframe');"
1209 "f.srcdoc = 'some content';"
1210 "document.body.appendChild(f)");
naskofaa01fb2016-04-30 01:04:171211 TestNavigationObserver observer(shell()->web_contents());
Nick Carterb7e71312018-08-03 23:36:131212 EXPECT_TRUE(ExecJs(root, script));
naskofaa01fb2016-04-30 01:04:171213 EXPECT_EQ(2U, root->child_count());
1214 observer.Wait();
1215
Lukasz Anforowicz42d3d07f2019-06-19 01:06:421216 EXPECT_TRUE(root->child_at(1)->current_url().IsAboutSrcdoc());
Peter Kastingeb8c3ce2021-08-20 04:39:351217 EvalJsResult js_result = EvalJs(root->child_at(1), "self.origin");
Mike West800532c2021-10-14 09:26:521218 EXPECT_EQ(root->current_frame_host()
1219 ->GetLastCommittedURL()
1220 .DeprecatedGetOriginAsURL(),
Peter Kastingeb8c3ce2021-08-20 04:39:351221 GURL(js_result.ExtractString()));
Mike West800532c2021-10-14 09:26:521222 EXPECT_NE(child->current_frame_host()
1223 ->GetLastCommittedURL()
1224 .DeprecatedGetOriginAsURL(),
Peter Kastingeb8c3ce2021-08-20 04:39:351225 GURL(js_result.ExtractString()));
naskofaa01fb2016-04-30 01:04:171226 }
1227
1228 // Set srcdoc on the existing cross-site frame. It should navigate the frame
1229 // back to the origin of the parent.
1230 {
Aran Gilman37d11632019-10-08 23:07:151231 std::string script(
1232 "var f = document.getElementById('child-0');"
1233 "f.srcdoc = 'some content';");
naskofaa01fb2016-04-30 01:04:171234 TestNavigationObserver observer(shell()->web_contents());
Nick Carterb7e71312018-08-03 23:36:131235 EXPECT_TRUE(ExecJs(root, script));
naskofaa01fb2016-04-30 01:04:171236 observer.Wait();
1237
Lukasz Anforowicz42d3d07f2019-06-19 01:06:421238 EXPECT_TRUE(child->current_url().IsAboutSrcdoc());
Avi Drissmana3144c582021-10-14 17:00:261239 EXPECT_EQ(root->current_frame_host()->GetLastCommittedOrigin().Serialize(),
1240 EvalJs(child, "self.origin"));
naskofaa01fb2016-04-30 01:04:171241 }
1242}
1243
Ian Clelland5cbaaf82017-11-27 22:00:031244// Ensure that sandbox flags are correctly set in the main frame when set by
1245// Content-Security-Policy header.
1246IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, SandboxFlagsSetForMainFrame) {
1247 GURL main_url(embedded_test_server()->GetURL("/csp_sandboxed_frame.html"));
1248 EXPECT_TRUE(NavigateToURL(shell(), main_url));
1249
1250 // It is safe to obtain the root frame tree node here, as it doesn't change.
1251 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:551252 ->GetPrimaryFrameTree()
1253 .root();
Ian Clelland5cbaaf82017-11-27 22:00:031254
1255 // Verify that sandbox flags are set properly for the root FrameTreeNode and
1256 // RenderFrameHost. Root frame is sandboxed with "allow-scripts".
arthursonzognib93a4472020-04-10 07:38:001257 EXPECT_EQ(network::mojom::WebSandboxFlags::kNone,
Ian Clelland5cbaaf82017-11-27 22:00:031258 root->effective_frame_policy().sandbox_flags);
arthursonzognib93a4472020-04-10 07:38:001259 EXPECT_EQ(network::mojom::WebSandboxFlags::kAll &
1260 ~network::mojom::WebSandboxFlags::kScripts &
1261 ~network::mojom::WebSandboxFlags::kAutomaticFeatures,
Ian Clelland5cbaaf82017-11-27 22:00:031262 root->active_sandbox_flags());
1263 EXPECT_EQ(root->active_sandbox_flags(),
1264 root->current_frame_host()->active_sandbox_flags());
1265
1266 // Verify that child frames inherit sandbox flags from the root. First frame
1267 // has no explicitly set flags of its own, and should inherit those from the
1268 // root. Second frame is completely sandboxed.
arthursonzognib93a4472020-04-10 07:38:001269 EXPECT_EQ(network::mojom::WebSandboxFlags::kAll &
1270 ~network::mojom::WebSandboxFlags::kScripts &
1271 ~network::mojom::WebSandboxFlags::kAutomaticFeatures,
Ian Clelland5cbaaf82017-11-27 22:00:031272 root->child_at(0)->effective_frame_policy().sandbox_flags);
arthursonzognib93a4472020-04-10 07:38:001273 EXPECT_EQ(network::mojom::WebSandboxFlags::kAll &
1274 ~network::mojom::WebSandboxFlags::kScripts &
1275 ~network::mojom::WebSandboxFlags::kAutomaticFeatures,
Ian Clelland5cbaaf82017-11-27 22:00:031276 root->child_at(0)->active_sandbox_flags());
1277 EXPECT_EQ(root->child_at(0)->active_sandbox_flags(),
1278 root->child_at(0)->current_frame_host()->active_sandbox_flags());
arthursonzognib93a4472020-04-10 07:38:001279 EXPECT_EQ(network::mojom::WebSandboxFlags::kAll,
Ian Clelland5cbaaf82017-11-27 22:00:031280 root->child_at(1)->effective_frame_policy().sandbox_flags);
arthursonzognib93a4472020-04-10 07:38:001281 EXPECT_EQ(network::mojom::WebSandboxFlags::kAll,
Ian Clelland5cbaaf82017-11-27 22:00:031282 root->child_at(1)->active_sandbox_flags());
1283 EXPECT_EQ(root->child_at(1)->active_sandbox_flags(),
1284 root->child_at(1)->current_frame_host()->active_sandbox_flags());
1285
1286 // Navigating the main frame to a different URL should clear sandbox flags.
1287 GURL unsandboxed_url(embedded_test_server()->GetURL("/title1.html"));
Lukasz Anforowicz69c25dfd2020-11-12 21:50:201288 EXPECT_TRUE(NavigateToURLFromRenderer(root, unsandboxed_url));
Ian Clelland5cbaaf82017-11-27 22:00:031289
1290 // Verify that sandbox flags are cleared properly for the root FrameTreeNode
1291 // and RenderFrameHost.
arthursonzognib93a4472020-04-10 07:38:001292 EXPECT_EQ(network::mojom::WebSandboxFlags::kNone,
Ian Clelland5cbaaf82017-11-27 22:00:031293 root->effective_frame_policy().sandbox_flags);
arthursonzognib93a4472020-04-10 07:38:001294 EXPECT_EQ(network::mojom::WebSandboxFlags::kNone,
1295 root->active_sandbox_flags());
1296 EXPECT_EQ(network::mojom::WebSandboxFlags::kNone,
Ian Clelland5cbaaf82017-11-27 22:00:031297 root->current_frame_host()->active_sandbox_flags());
1298}
1299
alexmosf832a2f2015-01-27 22:44:031300// Ensure that sandbox flags are correctly set when child frames are created.
1301IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, SandboxFlagsSetForChildFrames) {
1302 GURL main_url(embedded_test_server()->GetURL("/sandboxed_frames.html"));
1303 EXPECT_TRUE(NavigateToURL(shell(), main_url));
1304
1305 // It is safe to obtain the root frame tree node here, as it doesn't change.
1306 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:551307 ->GetPrimaryFrameTree()
1308 .root();
alexmosf832a2f2015-01-27 22:44:031309
1310 // Verify that sandbox flags are set properly for all FrameTreeNodes.
1311 // First frame is completely sandboxed; second frame uses "allow-scripts",
1312 // which resets both SandboxFlags::Scripts and
1313 // SandboxFlags::AutomaticFeatures bits per blink::parseSandboxPolicy(), and
1314 // third frame has "allow-scripts allow-same-origin".
arthursonzognib93a4472020-04-10 07:38:001315 EXPECT_EQ(network::mojom::WebSandboxFlags::kNone,
Ian Clellandcdc4f312017-10-13 22:24:121316 root->effective_frame_policy().sandbox_flags);
arthursonzognib93a4472020-04-10 07:38:001317 EXPECT_EQ(network::mojom::WebSandboxFlags::kAll,
Ian Clellandcdc4f312017-10-13 22:24:121318 root->child_at(0)->effective_frame_policy().sandbox_flags);
arthursonzognib93a4472020-04-10 07:38:001319 EXPECT_EQ(network::mojom::WebSandboxFlags::kAll &
1320 ~network::mojom::WebSandboxFlags::kScripts &
1321 ~network::mojom::WebSandboxFlags::kAutomaticFeatures,
Ian Clellandcdc4f312017-10-13 22:24:121322 root->child_at(1)->effective_frame_policy().sandbox_flags);
arthursonzognib93a4472020-04-10 07:38:001323 EXPECT_EQ(network::mojom::WebSandboxFlags::kAll &
1324 ~network::mojom::WebSandboxFlags::kScripts &
1325 ~network::mojom::WebSandboxFlags::kAutomaticFeatures &
1326 ~network::mojom::WebSandboxFlags::kOrigin,
Ian Clellandcdc4f312017-10-13 22:24:121327 root->child_at(2)->effective_frame_policy().sandbox_flags);
alexmosf832a2f2015-01-27 22:44:031328
1329 // Sandboxed frames should set a unique origin unless they have the
1330 // "allow-same-origin" directive.
alexmos6e940102016-01-19 22:47:251331 EXPECT_EQ("null", root->child_at(0)->current_origin().Serialize());
1332 EXPECT_EQ("null", root->child_at(1)->current_origin().Serialize());
Mike West800532c2021-10-14 09:26:521333 EXPECT_EQ(main_url.DeprecatedGetOriginAsURL().spec(),
alexmos6e940102016-01-19 22:47:251334 root->child_at(2)->current_origin().Serialize() + "/");
alexmosf832a2f2015-01-27 22:44:031335
1336 // Navigating to a different URL should not clear sandbox flags.
1337 GURL frame_url(embedded_test_server()->GetURL("/title1.html"));
1338 NavigateFrameToURL(root->child_at(0), frame_url);
arthursonzognib93a4472020-04-10 07:38:001339 EXPECT_EQ(network::mojom::WebSandboxFlags::kAll,
Ian Clellandcdc4f312017-10-13 22:24:121340 root->child_at(0)->effective_frame_policy().sandbox_flags);
alexmosf832a2f2015-01-27 22:44:031341}
1342
Ian Clelland5cbaaf82017-11-27 22:00:031343// Ensure that sandbox flags are correctly set in the child frames when set by
1344// Content-Security-Policy header, and in combination with the sandbox iframe
1345// attribute.
1346IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest,
1347 SandboxFlagsSetByCSPForChildFrames) {
1348 GURL main_url(embedded_test_server()->GetURL("/sandboxed_frames_csp.html"));
1349 EXPECT_TRUE(NavigateToURL(shell(), main_url));
1350
1351 // It is safe to obtain the root frame tree node here, as it doesn't change.
1352 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:551353 ->GetPrimaryFrameTree()
1354 .root();
Ian Clelland5cbaaf82017-11-27 22:00:031355
1356 // Verify that sandbox flags are set properly for all FrameTreeNodes.
1357 // First frame has no iframe sandbox flags, but the framed document is served
1358 // with a CSP header which sets "allow-scripts", "allow-popups" and
1359 // "allow-pointer-lock".
1360 // Second frame is sandboxed with "allow-scripts", "allow-pointer-lock" and
1361 // "allow-orientation-lock", and the framed document is also served with a CSP
1362 // header which uses "allow-popups" and "allow-pointer-lock". The resulting
1363 // sandbox for the frame should only have "allow-pointer-lock".
arthursonzognib93a4472020-04-10 07:38:001364 EXPECT_EQ(network::mojom::WebSandboxFlags::kNone,
Ian Clelland5cbaaf82017-11-27 22:00:031365 root->effective_frame_policy().sandbox_flags);
arthursonzognib93a4472020-04-10 07:38:001366 EXPECT_EQ(network::mojom::WebSandboxFlags::kNone,
1367 root->active_sandbox_flags());
Ian Clelland5cbaaf82017-11-27 22:00:031368 EXPECT_EQ(root->active_sandbox_flags(),
1369 root->current_frame_host()->active_sandbox_flags());
arthursonzognib93a4472020-04-10 07:38:001370 EXPECT_EQ(network::mojom::WebSandboxFlags::kNone,
Ian Clelland5cbaaf82017-11-27 22:00:031371 root->child_at(0)->effective_frame_policy().sandbox_flags);
Arthur Sonzogni4ba3104f2022-03-09 09:04:391372 EXPECT_EQ(
1373 network::mojom::WebSandboxFlags::kAll &
1374 ~network::mojom::WebSandboxFlags::kAutomaticFeatures &
1375 ~network::mojom::WebSandboxFlags::kPointerLock &
1376 ~network::mojom::WebSandboxFlags::kPopups &
1377 ~network::mojom::WebSandboxFlags::kScripts &
1378 ~network::mojom::WebSandboxFlags::kTopNavigationToCustomProtocols,
1379 root->child_at(0)->active_sandbox_flags());
Ian Clelland5cbaaf82017-11-27 22:00:031380 EXPECT_EQ(root->child_at(0)->active_sandbox_flags(),
1381 root->child_at(0)->current_frame_host()->active_sandbox_flags());
arthursonzognib93a4472020-04-10 07:38:001382 EXPECT_EQ(network::mojom::WebSandboxFlags::kAll &
1383 ~network::mojom::WebSandboxFlags::kScripts &
1384 ~network::mojom::WebSandboxFlags::kAutomaticFeatures &
1385 ~network::mojom::WebSandboxFlags::kPointerLock &
1386 ~network::mojom::WebSandboxFlags::kOrientationLock,
Ian Clelland5cbaaf82017-11-27 22:00:031387 root->child_at(1)->effective_frame_policy().sandbox_flags);
arthursonzognib93a4472020-04-10 07:38:001388 EXPECT_EQ(network::mojom::WebSandboxFlags::kAll &
1389 ~network::mojom::WebSandboxFlags::kScripts &
1390 ~network::mojom::WebSandboxFlags::kAutomaticFeatures &
1391 ~network::mojom::WebSandboxFlags::kPointerLock,
Ian Clelland5cbaaf82017-11-27 22:00:031392 root->child_at(1)->active_sandbox_flags());
1393 EXPECT_EQ(root->child_at(1)->active_sandbox_flags(),
1394 root->child_at(1)->current_frame_host()->active_sandbox_flags());
1395
1396 // Navigating to a different URL *should* clear CSP-set sandbox flags, but
1397 // should retain those flags set by the frame owner.
1398 GURL frame_url(embedded_test_server()->GetURL("/title1.html"));
1399
Lukasz Anforowicz69c25dfd2020-11-12 21:50:201400 EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(0), frame_url));
arthursonzognib93a4472020-04-10 07:38:001401 EXPECT_EQ(network::mojom::WebSandboxFlags::kNone,
Ian Clelland5cbaaf82017-11-27 22:00:031402 root->child_at(0)->effective_frame_policy().sandbox_flags);
arthursonzognib93a4472020-04-10 07:38:001403 EXPECT_EQ(network::mojom::WebSandboxFlags::kNone,
Ian Clelland5cbaaf82017-11-27 22:00:031404 root->child_at(0)->active_sandbox_flags());
1405 EXPECT_EQ(root->child_at(0)->active_sandbox_flags(),
1406 root->child_at(0)->current_frame_host()->active_sandbox_flags());
1407
Lukasz Anforowicz69c25dfd2020-11-12 21:50:201408 EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(1), frame_url));
arthursonzognib93a4472020-04-10 07:38:001409 EXPECT_EQ(network::mojom::WebSandboxFlags::kAll &
1410 ~network::mojom::WebSandboxFlags::kScripts &
1411 ~network::mojom::WebSandboxFlags::kAutomaticFeatures &
1412 ~network::mojom::WebSandboxFlags::kPointerLock &
1413 ~network::mojom::WebSandboxFlags::kOrientationLock,
Ian Clelland5cbaaf82017-11-27 22:00:031414 root->child_at(1)->effective_frame_policy().sandbox_flags);
arthursonzognib93a4472020-04-10 07:38:001415 EXPECT_EQ(network::mojom::WebSandboxFlags::kAll &
1416 ~network::mojom::WebSandboxFlags::kScripts &
1417 ~network::mojom::WebSandboxFlags::kAutomaticFeatures &
1418 ~network::mojom::WebSandboxFlags::kPointerLock &
1419 ~network::mojom::WebSandboxFlags::kOrientationLock,
Ian Clelland5cbaaf82017-11-27 22:00:031420 root->child_at(1)->active_sandbox_flags());
1421 EXPECT_EQ(root->child_at(1)->active_sandbox_flags(),
1422 root->child_at(1)->current_frame_host()->active_sandbox_flags());
1423}
1424
alexmose201c7cd2015-06-10 17:14:211425// Ensure that a popup opened from a subframe sets its opener to the subframe's
1426// FrameTreeNode, and that the opener is cleared if the subframe is destroyed.
1427IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, SubframeOpenerSetForNewWindow) {
1428 GURL main_url(embedded_test_server()->GetURL("/frame_tree/top.html"));
1429 EXPECT_TRUE(NavigateToURL(shell(), main_url));
1430
1431 // It is safe to obtain the root frame tree node here, as it doesn't change.
1432 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:551433 ->GetPrimaryFrameTree()
1434 .root();
alexmose201c7cd2015-06-10 17:14:211435
1436 // Open a new window from a subframe.
1437 ShellAddedObserver new_shell_observer;
1438 GURL popup_url(embedded_test_server()->GetURL("foo.com", "/title1.html"));
Nick Carterb7e71312018-08-03 23:36:131439 EXPECT_TRUE(
1440 ExecJs(root->child_at(0), JsReplace("window.open($1);", popup_url)));
alexmose201c7cd2015-06-10 17:14:211441 Shell* new_shell = new_shell_observer.GetShell();
1442 WebContents* new_contents = new_shell->web_contents();
Fergal Dalyf0522332020-07-18 06:09:461443 EXPECT_TRUE(WaitForLoadStop(new_contents));
alexmose201c7cd2015-06-10 17:14:211444
1445 // Check that the new window's opener points to the correct subframe on
1446 // original window.
1447 FrameTreeNode* popup_root =
Carlos Caballero15caeeb2021-10-27 09:57:551448 static_cast<WebContentsImpl*>(new_contents)->GetPrimaryFrameTree().root();
alexmose201c7cd2015-06-10 17:14:211449 EXPECT_EQ(root->child_at(0), popup_root->opener());
1450
1451 // Close the original window. This should clear the new window's opener.
1452 shell()->Close();
1453 EXPECT_EQ(nullptr, popup_root->opener());
1454}
1455
Shivani Sharmac4f561582018-11-15 15:58:391456// Tests that the user activation bits get cleared when a same-site document is
1457// installed in the frame.
1458IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest,
1459 ClearUserActivationForNewDocument) {
1460 GURL main_url(embedded_test_server()->GetURL("/frame_tree/top.html"));
1461 EXPECT_TRUE(NavigateToURL(shell(), main_url));
1462
1463 // It is safe to obtain the root frame tree node here, as it doesn't change.
1464 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:551465 ->GetPrimaryFrameTree()
1466 .root();
Shivani Sharmac4f561582018-11-15 15:58:391467
Mustaq Ahmed4baa9a6e82019-12-20 23:43:461468 EXPECT_FALSE(root->HasStickyUserActivation());
Shivani Sharmac4f561582018-11-15 15:58:391469 EXPECT_FALSE(root->HasTransientUserActivation());
1470
1471 // Set the user activation bits.
1472 root->UpdateUserActivationState(
Mustaq Ahmeddc195e5b2020-08-04 18:45:111473 blink::mojom::UserActivationUpdateType::kNotifyActivation,
1474 blink::mojom::UserActivationNotificationType::kTest);
Mustaq Ahmed4baa9a6e82019-12-20 23:43:461475 EXPECT_TRUE(root->HasStickyUserActivation());
Shivani Sharmac4f561582018-11-15 15:58:391476 EXPECT_TRUE(root->HasTransientUserActivation());
1477
1478 // Install a new same-site document to check the clearing of user activation
1479 // bits.
1480 GURL url(embedded_test_server()->GetURL("/title1.html"));
1481 EXPECT_TRUE(NavigateToURL(shell(), url));
1482
Mustaq Ahmed4baa9a6e82019-12-20 23:43:461483 EXPECT_FALSE(root->HasStickyUserActivation());
Shivani Sharmac4f561582018-11-15 15:58:391484 EXPECT_FALSE(root->HasTransientUserActivation());
1485}
1486
[email protected]82307f6b2014-08-07 03:30:121487class CrossProcessFrameTreeBrowserTest : public ContentBrowserTest {
1488 public:
Fergal Dalya1d569972021-03-16 03:24:531489 CrossProcessFrameTreeBrowserTest() = default;
[email protected]82307f6b2014-08-07 03:30:121490
Peter Boström9b036532021-10-28 23:37:281491 CrossProcessFrameTreeBrowserTest(const CrossProcessFrameTreeBrowserTest&) =
1492 delete;
1493 CrossProcessFrameTreeBrowserTest& operator=(
1494 const CrossProcessFrameTreeBrowserTest&) = delete;
1495
dchengc2282aa2014-10-21 12:07:581496 void SetUpCommandLine(base::CommandLine* command_line) override {
nickd30fd962015-07-27 21:51:081497 IsolateAllSitesForTesting(command_line);
[email protected]82307f6b2014-08-07 03:30:121498 }
1499
alexmos478dcbb2014-12-10 21:24:461500 void SetUpOnMainThread() override {
1501 host_resolver()->AddRule("*", "127.0.0.1");
alexmos478dcbb2014-12-10 21:24:461502 SetupCrossSiteRedirector(embedded_test_server());
martijn12d2dad2016-11-11 10:59:271503 ASSERT_TRUE(embedded_test_server()->Start());
alexmos478dcbb2014-12-10 21:24:461504 }
[email protected]82307f6b2014-08-07 03:30:121505};
1506
1507// Ensure that we can complete a cross-process subframe navigation.
[email protected]82307f6b2014-08-07 03:30:121508IN_PROC_BROWSER_TEST_F(CrossProcessFrameTreeBrowserTest,
nasko83fe5dc2015-07-01 16:34:191509 CreateCrossProcessSubframeProxies) {
alexmos478dcbb2014-12-10 21:24:461510 GURL main_url(embedded_test_server()->GetURL("/site_per_process_main.html"));
Alex Moshchukaa95adf52019-08-13 00:02:021511 EXPECT_TRUE(NavigateToURL(shell(), main_url));
[email protected]82307f6b2014-08-07 03:30:121512
1513 // It is safe to obtain the root frame tree node here, as it doesn't change.
1514 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:551515 ->GetPrimaryFrameTree()
1516 .root();
[email protected]82307f6b2014-08-07 03:30:121517
[email protected]14522ce42014-08-08 18:56:161518 // There should not be a proxy for the root's own SiteInstance.
Sharon Yang7424bda2021-11-04 20:27:431519 SiteInstanceImpl* root_instance =
1520 root->current_frame_host()->GetSiteInstance();
Harkiran Bolariad22a1dca2022-02-22 17:01:121521 EXPECT_FALSE(root->current_frame_host()
1522 ->browsing_context_state()
1523 ->GetRenderFrameProxyHost(root_instance->group()));
[email protected]14522ce42014-08-08 18:56:161524
[email protected]82307f6b2014-08-07 03:30:121525 // Load same-site page into iframe.
alexmos478dcbb2014-12-10 21:24:461526 GURL http_url(embedded_test_server()->GetURL("/title1.html"));
Lukasz Anforowicz69c25dfd2020-11-12 21:50:201527 EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(0), http_url));
[email protected]82307f6b2014-08-07 03:30:121528
[email protected]82307f6b2014-08-07 03:30:121529 // Load cross-site page into iframe.
alexmos478dcbb2014-12-10 21:24:461530 GURL cross_site_url(
1531 embedded_test_server()->GetURL("foo.com", "/title2.html"));
Lukasz Anforowicz69c25dfd2020-11-12 21:50:201532 EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(0), cross_site_url));
[email protected]82307f6b2014-08-07 03:30:121533
1534 // Ensure that we have created a new process for the subframe.
naskoe6edde32014-10-17 15:36:481535 ASSERT_EQ(2U, root->child_count());
[email protected]82307f6b2014-08-07 03:30:121536 FrameTreeNode* child = root->child_at(0);
Sharon Yang7424bda2021-11-04 20:27:431537 SiteInstanceImpl* child_instance =
1538 child->current_frame_host()->GetSiteInstance();
[email protected]82307f6b2014-08-07 03:30:121539 RenderViewHost* rvh = child->current_frame_host()->render_view_host();
1540 RenderProcessHost* rph = child->current_frame_host()->GetProcess();
1541
Dave Tapuska327c06c92022-06-13 20:31:511542 EXPECT_NE(shell()->web_contents()->GetPrimaryMainFrame()->GetRenderViewHost(),
1543 rvh);
[email protected]14522ce42014-08-08 18:56:161544 EXPECT_NE(shell()->web_contents()->GetSiteInstance(), child_instance);
Dave Tapuska327c06c92022-06-13 20:31:511545 EXPECT_NE(shell()->web_contents()->GetPrimaryMainFrame()->GetProcess(), rph);
[email protected]82307f6b2014-08-07 03:30:121546
1547 // Ensure that the root node has a proxy for the child node's SiteInstance.
Harkiran Bolariad22a1dca2022-02-22 17:01:121548 EXPECT_TRUE(root->current_frame_host()
1549 ->browsing_context_state()
1550 ->GetRenderFrameProxyHost(child_instance->group()));
[email protected]82307f6b2014-08-07 03:30:121551
1552 // Also ensure that the child has a proxy for the root node's SiteInstance.
Harkiran Bolariad22a1dca2022-02-22 17:01:121553 EXPECT_TRUE(child->current_frame_host()
1554 ->browsing_context_state()
1555 ->GetRenderFrameProxyHost(root_instance->group()));
[email protected]14522ce42014-08-08 18:56:161556
1557 // The nodes should not have proxies for their own SiteInstance.
Harkiran Bolariad22a1dca2022-02-22 17:01:121558 EXPECT_FALSE(root->current_frame_host()
1559 ->browsing_context_state()
1560 ->GetRenderFrameProxyHost(root_instance->group()));
1561 EXPECT_FALSE(child->current_frame_host()
1562 ->browsing_context_state()
1563 ->GetRenderFrameProxyHost(child_instance->group()));
creise42f2a52014-09-18 18:14:571564
1565 // Ensure that the RenderViews and RenderFrames are all live.
1566 EXPECT_TRUE(
1567 root->current_frame_host()->render_view_host()->IsRenderViewLive());
1568 EXPECT_TRUE(
1569 child->current_frame_host()->render_view_host()->IsRenderViewLive());
1570 EXPECT_TRUE(root->current_frame_host()->IsRenderFrameLive());
1571 EXPECT_TRUE(root->child_at(0)->current_frame_host()->IsRenderFrameLive());
[email protected]82307f6b2014-08-07 03:30:121572}
1573
alexmosbc7eafa2014-12-06 01:38:091574IN_PROC_BROWSER_TEST_F(CrossProcessFrameTreeBrowserTest,
Nasko Oskov039121012019-01-11 00:21:321575 OriginSetOnNavigations) {
alexmos478dcbb2014-12-10 21:24:461576 GURL main_url(embedded_test_server()->GetURL("/site_per_process_main.html"));
alexmosbc7eafa2014-12-06 01:38:091577 EXPECT_TRUE(NavigateToURL(shell(), main_url));
1578
1579 // It is safe to obtain the root frame tree node here, as it doesn't change.
1580 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:551581 ->GetPrimaryFrameTree()
1582 .root();
alexmosbc7eafa2014-12-06 01:38:091583
creisabdd2bd62015-11-21 01:14:581584 EXPECT_EQ(root->current_origin().Serialize() + '/',
Mike West800532c2021-10-14 09:26:521585 main_url.DeprecatedGetOriginAsURL().spec());
alexmosbc7eafa2014-12-06 01:38:091586
1587 // First frame is an about:blank frame. Check that its origin is correctly
1588 // inherited from the parent.
creisabdd2bd62015-11-21 01:14:581589 EXPECT_EQ(root->child_at(0)->current_origin().Serialize() + '/',
Mike West800532c2021-10-14 09:26:521590 main_url.DeprecatedGetOriginAsURL().spec());
alexmosbc7eafa2014-12-06 01:38:091591
1592 // Second frame loads a same-site page. Its origin should also be the same
1593 // as the parent.
creisabdd2bd62015-11-21 01:14:581594 EXPECT_EQ(root->child_at(1)->current_origin().Serialize() + '/',
Mike West800532c2021-10-14 09:26:521595 main_url.DeprecatedGetOriginAsURL().spec());
alexmosbc7eafa2014-12-06 01:38:091596
alexmosbc7eafa2014-12-06 01:38:091597 // Load cross-site page into the first frame.
alexmos478dcbb2014-12-10 21:24:461598 GURL cross_site_url(
1599 embedded_test_server()->GetURL("foo.com", "/title2.html"));
Lukasz Anforowicz69c25dfd2020-11-12 21:50:201600 EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(0), cross_site_url));
alexmosbc7eafa2014-12-06 01:38:091601
creisabdd2bd62015-11-21 01:14:581602 EXPECT_EQ(root->child_at(0)->current_origin().Serialize() + '/',
Mike West800532c2021-10-14 09:26:521603 cross_site_url.DeprecatedGetOriginAsURL().spec());
alexmosbc7eafa2014-12-06 01:38:091604
1605 // The root's origin shouldn't have changed.
creisabdd2bd62015-11-21 01:14:581606 EXPECT_EQ(root->current_origin().Serialize() + '/',
Mike West800532c2021-10-14 09:26:521607 main_url.DeprecatedGetOriginAsURL().spec());
alexmosbc7eafa2014-12-06 01:38:091608
Nasko Oskov039121012019-01-11 00:21:321609 {
1610 GURL data_url("data:text/html,foo");
1611 TestNavigationObserver observer(shell()->web_contents());
1612 EXPECT_TRUE(
1613 ExecJs(root->child_at(1), JsReplace("window.location = $1", data_url)));
1614 observer.Wait();
1615 }
alexmosbc7eafa2014-12-06 01:38:091616
1617 // Navigating to a data URL should set a unique origin. This is represented
Nasko Oskov039121012019-01-11 00:21:321618 // as "null" per RFC 6454. A frame navigating itself to a data: URL does not
1619 // require a process transfer, but should retain the original origin
1620 // as its precursor.
creisabdd2bd62015-11-21 01:14:581621 EXPECT_EQ(root->child_at(1)->current_origin().Serialize(), "null");
Nasko Oskov039121012019-01-11 00:21:321622 EXPECT_TRUE(root->child_at(1)->current_origin().opaque());
1623 ASSERT_EQ(
1624 url::SchemeHostPort(main_url),
1625 root->child_at(1)->current_origin().GetTupleOrPrecursorTupleIfOpaque())
1626 << "Expected the precursor origin to be preserved; should be the "
1627 "initiator of a data: navigation.";
1628
1629 // Adding an <iframe sandbox srcdoc=> frame should result in a unique origin
1630 // that is different-origin from its data: URL parent.
1631 {
1632 TestNavigationObserver observer(shell()->web_contents());
1633
1634 ASSERT_EQ(0U, root->child_at(1)->child_count());
1635 EXPECT_TRUE(
1636 ExecJs(root->child_at(1), JsReplace(
1637 R"(
1638 var iframe = document.createElement('iframe');
1639 iframe.setAttribute('sandbox', 'allow-scripts');
1640 iframe.srcdoc = $1;
1641 document.body.appendChild(iframe);
1642 )",
1643 "<html><body>This sandboxed doc should "
1644 "be different-origin.</body></html>")));
1645 observer.Wait();
1646 ASSERT_EQ(1U, root->child_at(1)->child_count());
1647 }
1648
1649 url::Origin root_origin = root->current_origin();
1650 url::Origin child_1 = root->child_at(1)->current_origin();
1651 url::Origin child_1_0 = root->child_at(1)->child_at(0)->current_origin();
1652 EXPECT_FALSE(root_origin.opaque());
1653 EXPECT_TRUE(child_1.opaque());
1654 EXPECT_TRUE(child_1_0.opaque());
1655 EXPECT_NE(child_1, child_1_0);
1656 EXPECT_EQ(url::SchemeHostPort(main_url),
1657 root_origin.GetTupleOrPrecursorTupleIfOpaque());
1658 EXPECT_EQ(url::SchemeHostPort(main_url),
1659 child_1.GetTupleOrPrecursorTupleIfOpaque());
1660 EXPECT_EQ(url::SchemeHostPort(main_url),
1661 child_1_0.GetTupleOrPrecursorTupleIfOpaque());
1662
1663 {
1664 TestNavigationObserver observer(shell()->web_contents());
1665
1666 ASSERT_EQ(1U, root->child_at(1)->child_count());
1667 EXPECT_TRUE(
1668 ExecJs(root->child_at(1), JsReplace(
1669 R"(
1670 var iframe = document.createElement('iframe');
1671 iframe.srcdoc = $1;
1672 document.body.appendChild(iframe);
1673 )",
1674 "<html><body>This srcdoc document should "
1675 "be same-origin.</body></html>")));
1676 observer.Wait();
1677 ASSERT_EQ(2U, root->child_at(1)->child_count());
1678 }
1679 EXPECT_EQ(root_origin, root->current_origin());
1680 EXPECT_EQ(child_1, root->child_at(1)->current_origin());
1681 EXPECT_EQ(child_1_0, root->child_at(1)->child_at(0)->current_origin());
1682 url::Origin child_1_1 = root->child_at(1)->child_at(1)->current_origin();
1683 EXPECT_EQ(child_1, child_1_1);
1684 EXPECT_NE(child_1_0, child_1_1);
1685
1686 {
1687 TestNavigationObserver observer(shell()->web_contents());
1688
1689 ASSERT_EQ(2U, root->child_at(1)->child_count());
1690 EXPECT_TRUE(
1691 ExecJs(root->child_at(1), JsReplace(
1692 R"(
1693 var iframe = document.createElement('iframe');
1694 iframe.src = 'data:text/html;base64,' + btoa($1);
1695 document.body.appendChild(iframe);
1696 )",
1697 "<html><body>This data: doc should be "
1698 "different-origin.</body></html>")));
1699 observer.Wait();
1700 ASSERT_EQ(3U, root->child_at(1)->child_count());
1701 }
1702 EXPECT_EQ(root_origin, root->current_origin());
1703 EXPECT_EQ(child_1, root->child_at(1)->current_origin());
1704 EXPECT_EQ(child_1_0, root->child_at(1)->child_at(0)->current_origin());
1705 EXPECT_EQ(child_1_1, root->child_at(1)->child_at(1)->current_origin());
1706 url::Origin child_1_2 = root->child_at(1)->child_at(2)->current_origin();
1707 EXPECT_NE(child_1, child_1_2);
1708 EXPECT_NE(child_1_0, child_1_2);
1709 EXPECT_NE(child_1_1, child_1_2);
1710 EXPECT_EQ(url::SchemeHostPort(main_url),
1711 child_1_2.GetTupleOrPrecursorTupleIfOpaque());
1712
1713 // If the parent navigates its child to a data URL, it should transfer
1714 // to the parent's process, and the precursor origin should track the
1715 // parent's origin.
1716 {
1717 GURL data_url("data:text/html,foo2");
1718 TestNavigationObserver observer(shell()->web_contents());
1719 EXPECT_TRUE(ExecJs(root, JsReplace("frames[0].location = $1", data_url)));
1720 observer.Wait();
1721 EXPECT_EQ(data_url, root->child_at(0)->current_url());
1722 }
1723
1724 EXPECT_EQ(root->child_at(0)->current_origin().Serialize(), "null");
1725 EXPECT_TRUE(root->child_at(0)->current_origin().opaque());
1726 EXPECT_EQ(
1727 url::SchemeHostPort(main_url),
1728 root->child_at(0)->current_origin().GetTupleOrPrecursorTupleIfOpaque());
1729 EXPECT_EQ(root->current_frame_host()->GetProcess(),
1730 root->child_at(0)->current_frame_host()->GetProcess());
1731}
1732
1733// Test to verify that a blob: URL that is created by a unique opaque origin
1734// will correctly set the origin_to_commit on a session history navigation.
1735IN_PROC_BROWSER_TEST_F(CrossProcessFrameTreeBrowserTest,
1736 OriginForBlobUrlsFromUniqueOpaqueOrigin) {
1737 // Start off with a navigation to data: URL in the main frame. It should
1738 // result in a unique opaque origin without any precursor information.
1739 GURL data_url("data:text/html,foo<iframe id='child' src='" +
1740 embedded_test_server()->GetURL("/title1.html").spec() +
1741 "'></iframe>");
1742 EXPECT_TRUE(NavigateToURL(shell(), data_url));
1743
1744 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:551745 ->GetPrimaryFrameTree()
1746 .root();
Nasko Oskov039121012019-01-11 00:21:321747 EXPECT_TRUE(root->current_origin().opaque());
Nasko Oskov55119382020-01-17 18:22:181748 EXPECT_FALSE(
1749 root->current_origin().GetTupleOrPrecursorTupleIfOpaque().IsValid());
Nasko Oskov039121012019-01-11 00:21:321750 EXPECT_EQ(1UL, root->child_count());
1751 FrameTreeNode* child = root->child_at(0);
1752
1753 // Create a blob: URL and navigate the child frame to it.
1754 std::string html = "<html><body>This is blob content.</body></html>";
1755 std::string script = JsReplace(
1756 "var blob = new Blob([$1], {type: 'text/html'});"
1757 "var blob_url = URL.createObjectURL(blob);"
1758 "document.getElementById('child').src = blob_url;"
1759 "blob_url;",
1760 html);
1761 GURL blob_url;
1762 {
1763 TestFrameNavigationObserver observer(child);
1764 blob_url = GURL(EvalJs(root, script).ExtractString());
1765 observer.Wait();
1766 EXPECT_EQ(blob_url, child->current_frame_host()->GetLastCommittedURL());
1767 }
1768
1769 // We expect the frame to have committed in an opaque origin which contains
1770 // the same precursor information - none.
1771 url::Origin blob_origin = child->current_origin();
1772 EXPECT_TRUE(blob_origin.opaque());
1773 EXPECT_EQ(root->current_origin().GetTupleOrPrecursorTupleIfOpaque(),
1774 blob_origin.GetTupleOrPrecursorTupleIfOpaque());
Rakina Zata Amnic7367852022-11-07 17:10:401775 EXPECT_FALSE(
1776 child->current_origin().GetTupleOrPrecursorTupleIfOpaque().IsValid());
Nasko Oskov039121012019-01-11 00:21:321777
1778 // Navigate the frame away to any web URL.
1779 {
1780 GURL url(embedded_test_server()->GetURL("/title2.html"));
1781 TestFrameNavigationObserver observer(child);
1782 EXPECT_TRUE(ExecJs(child, JsReplace("window.location = $1", url)));
1783 observer.Wait();
1784 EXPECT_EQ(url, child->current_frame_host()->GetLastCommittedURL());
1785 }
1786 EXPECT_FALSE(child->current_origin().opaque());
1787 EXPECT_TRUE(shell()->web_contents()->GetController().CanGoBack());
1788 EXPECT_EQ(3, shell()->web_contents()->GetController().GetEntryCount());
1789 EXPECT_EQ(
1790 2, shell()->web_contents()->GetController().GetLastCommittedEntryIndex());
1791
1792 // Verify the blob URL still exists in the main frame, which keeps it alive
1793 // allowing a session history navigation back to succeed.
1794 EXPECT_EQ(blob_url, GURL(EvalJs(root, "blob_url;").ExtractString()));
1795
1796 // Now navigate back in session history. It should successfully go back to
Rakina Zata Amnic7367852022-11-07 17:10:401797 // the blob: URL. The child frame won't be reusing the exact same origin it
1798 // used before, but it will commit a new opaque origin which will still have
1799 // no precursor information.
Nasko Oskov039121012019-01-11 00:21:321800 {
1801 TestFrameNavigationObserver observer(child);
1802 shell()->web_contents()->GetController().GoBack();
1803 observer.Wait();
1804 }
1805 EXPECT_EQ(blob_url, child->current_frame_host()->GetLastCommittedURL());
1806 EXPECT_TRUE(child->current_origin().opaque());
Rakina Zata Amnic7367852022-11-07 17:10:401807 EXPECT_NE(blob_origin, child->current_origin());
Nasko Oskov039121012019-01-11 00:21:321808 EXPECT_EQ(root->current_origin().GetTupleOrPrecursorTupleIfOpaque(),
1809 child->current_origin().GetTupleOrPrecursorTupleIfOpaque());
Rakina Zata Amnic7367852022-11-07 17:10:401810 EXPECT_FALSE(
1811 child->current_origin().GetTupleOrPrecursorTupleIfOpaque().IsValid());
Nasko Oskov039121012019-01-11 00:21:321812}
1813
1814// Test to verify that about:blank iframe, which is a child of a sandboxed
1815// iframe is not considered same origin, but precursor information is preserved
1816// in its origin.
1817IN_PROC_BROWSER_TEST_F(CrossProcessFrameTreeBrowserTest,
1818 AboutBlankSubframeInSandboxedFrame) {
1819 // Start off by navigating to a page with sandboxed iframe, which allows
1820 // script execution.
1821 GURL main_url(
1822 embedded_test_server()->GetURL("/sandboxed_main_frame_script.html"));
1823 EXPECT_TRUE(NavigateToURL(shell(), main_url));
1824
1825 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:551826 ->GetPrimaryFrameTree()
1827 .root();
Nasko Oskov039121012019-01-11 00:21:321828 EXPECT_EQ(1UL, root->child_count());
1829 FrameTreeNode* child = root->child_at(0);
1830
1831 // Navigate the frame to data: URL to cause it to have an opaque origin that
1832 // is derived from the |main_url| origin.
1833 GURL data_url("data:text/html,<html><body>foo</body></html>");
1834 {
1835 TestFrameNavigationObserver observer(child);
1836 EXPECT_TRUE(ExecJs(root, JsReplace("frames[0].location = $1", data_url)));
1837 observer.Wait();
1838 EXPECT_EQ(data_url, child->current_frame_host()->GetLastCommittedURL());
1839 }
1840
1841 // Add an about:blank iframe to the data: frame, which should not inherit the
1842 // origin, but should preserve the precursor information.
1843 {
1844 EXPECT_TRUE(ExecJs(child,
1845 "var f = document.createElement('iframe');"
1846 "document.body.appendChild(f);"));
1847 }
1848 EXPECT_EQ(1UL, child->child_count());
1849 FrameTreeNode* grandchild = child->child_at(0);
1850
1851 EXPECT_TRUE(grandchild->current_origin().opaque());
1852 EXPECT_EQ(GURL(url::kAboutBlankURL),
1853 grandchild->current_frame_host()->GetLastCommittedURL());
1854
1855 // The origin of the data: document should have precursor information matching
1856 // the main frame origin.
1857 EXPECT_EQ(root->current_origin().GetTupleOrPrecursorTupleIfOpaque(),
1858 child->current_origin().GetTupleOrPrecursorTupleIfOpaque());
1859
1860 // The same should hold also for the about:blank subframe of the data: frame.
1861 EXPECT_EQ(root->current_origin().GetTupleOrPrecursorTupleIfOpaque(),
1862 grandchild->current_origin().GetTupleOrPrecursorTupleIfOpaque());
1863
1864 // The about:blank document should not be able to access its parent, as they
1865 // are considered cross origin due to the sandbox flags on the parent.
1866 EXPECT_FALSE(ExecJs(grandchild, "window.parent.foo = 'bar';"));
1867 EXPECT_NE(child->current_origin(), grandchild->current_origin());
alexmosbc7eafa2014-12-06 01:38:091868}
1869
Ian Clelland5cbaaf82017-11-27 22:00:031870// Ensure that a popup opened from a sandboxed main frame inherits sandbox flags
1871// from its opener.
1872IN_PROC_BROWSER_TEST_F(CrossProcessFrameTreeBrowserTest,
1873 SandboxFlagsSetForNewWindow) {
1874 GURL main_url(
1875 embedded_test_server()->GetURL("/sandboxed_main_frame_script.html"));
1876 EXPECT_TRUE(NavigateToURL(shell(), main_url));
1877
1878 // It is safe to obtain the root frame tree node here, as it doesn't change.
1879 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:551880 ->GetPrimaryFrameTree()
1881 .root();
Ian Clelland5cbaaf82017-11-27 22:00:031882
1883 // Open a new window from the main frame.
1884 GURL popup_url(embedded_test_server()->GetURL("foo.com", "/title1.html"));
1885 Shell* new_shell = OpenPopup(root->current_frame_host(), popup_url, "");
1886 EXPECT_TRUE(new_shell);
1887 WebContents* new_contents = new_shell->web_contents();
1888
1889 // Check that the new window's sandbox flags correctly reflect the opener's
1890 // flags. Main frame sets allow-popups, allow-pointer-lock and allow-scripts.
1891 FrameTreeNode* popup_root =
Carlos Caballero15caeeb2021-10-27 09:57:551892 static_cast<WebContentsImpl*>(new_contents)->GetPrimaryFrameTree().root();
arthursonzognib93a4472020-04-10 07:38:001893 network::mojom::WebSandboxFlags main_frame_sandbox_flags =
Ian Clelland5cbaaf82017-11-27 22:00:031894 root->current_frame_host()->active_sandbox_flags();
Arthur Sonzogni4ba3104f2022-03-09 09:04:391895 EXPECT_EQ(
1896 network::mojom::WebSandboxFlags::kAll &
1897 ~network::mojom::WebSandboxFlags::kAutomaticFeatures &
1898 ~network::mojom::WebSandboxFlags::kPointerLock &
1899 ~network::mojom::WebSandboxFlags::kPopups &
1900 ~network::mojom::WebSandboxFlags::kScripts &
1901 ~network::mojom::WebSandboxFlags::kTopNavigationToCustomProtocols,
1902 main_frame_sandbox_flags);
Ian Clelland5cbaaf82017-11-27 22:00:031903
1904 EXPECT_EQ(main_frame_sandbox_flags,
1905 popup_root->effective_frame_policy().sandbox_flags);
1906 EXPECT_EQ(main_frame_sandbox_flags, popup_root->active_sandbox_flags());
1907 EXPECT_EQ(main_frame_sandbox_flags,
1908 popup_root->current_frame_host()->active_sandbox_flags());
1909}
1910
Shivani Sharmac4f561582018-11-15 15:58:391911// Tests that the user activation bits get cleared when a cross-site document is
1912// installed in the frame.
1913IN_PROC_BROWSER_TEST_F(CrossProcessFrameTreeBrowserTest,
1914 ClearUserActivationForNewDocument) {
1915 GURL main_url(embedded_test_server()->GetURL("/frame_tree/top.html"));
1916 EXPECT_TRUE(NavigateToURL(shell(), main_url));
1917
1918 // It is safe to obtain the root frame tree node here, as it doesn't change.
1919 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:551920 ->GetPrimaryFrameTree()
1921 .root();
Shivani Sharmac4f561582018-11-15 15:58:391922
Mustaq Ahmed4baa9a6e82019-12-20 23:43:461923 EXPECT_FALSE(root->HasStickyUserActivation());
Shivani Sharmac4f561582018-11-15 15:58:391924 EXPECT_FALSE(root->HasTransientUserActivation());
1925
1926 // Set the user activation bits.
1927 root->UpdateUserActivationState(
Mustaq Ahmeddc195e5b2020-08-04 18:45:111928 blink::mojom::UserActivationUpdateType::kNotifyActivation,
1929 blink::mojom::UserActivationNotificationType::kTest);
Mustaq Ahmed4baa9a6e82019-12-20 23:43:461930 EXPECT_TRUE(root->HasStickyUserActivation());
Shivani Sharmac4f561582018-11-15 15:58:391931 EXPECT_TRUE(root->HasTransientUserActivation());
1932
1933 // Install a new cross-site document to check the clearing of user activation
1934 // bits.
1935 GURL cross_site_url(
1936 embedded_test_server()->GetURL("foo.com", "/title2.html"));
1937 EXPECT_TRUE(NavigateToURL(shell(), cross_site_url));
1938
Mustaq Ahmed4baa9a6e82019-12-20 23:43:461939 EXPECT_FALSE(root->HasStickyUserActivation());
Shivani Sharmac4f561582018-11-15 15:58:391940 EXPECT_FALSE(root->HasTransientUserActivation());
1941}
1942
Dave Tapuska708c2732022-09-23 22:47:491943class BrowserContextGroupSwapFrameTreeBrowserTest : public ContentBrowserTest {
1944 public:
1945 BrowserContextGroupSwapFrameTreeBrowserTest()
1946 : https_server_(net::EmbeddedTestServer::TYPE_HTTPS) {}
1947
1948 void SetUpCommandLine(base::CommandLine* command_line) override {
1949 IsolateAllSitesForTesting(command_line);
1950 }
1951
1952 void SetUpOnMainThread() override {
1953 ContentBrowserTest::SetUpOnMainThread();
1954 host_resolver()->AddRule("*", "127.0.0.1");
1955 https_server_.ServeFilesFromSourceDirectory(GetTestDataFilePath());
1956 https_server_.SetSSLConfig(net::EmbeddedTestServer::CERT_TEST_NAMES);
1957 net::test_server::RegisterDefaultHandlers(&https_server_);
1958 ASSERT_TRUE(https_server_.Start());
1959 }
1960
1961 net::EmbeddedTestServer* https_server() { return &https_server_; }
1962
1963 public:
1964 net::EmbeddedTestServer https_server_;
1965};
1966
1967// Force a race between when the RenderViewHostImpl's main frame is running
1968// the unload handlers and when a new navigation occurs that tries to
1969// reuse a RenderViewHostImpl.
1970IN_PROC_BROWSER_TEST_F(BrowserContextGroupSwapFrameTreeBrowserTest,
1971 NavigateAndGoBack) {
1972 GURL main_url(https_server()->GetURL("a.test", "/title1.html"));
1973 EXPECT_TRUE(NavigateToURL(shell(), main_url));
1974
1975 auto* web_contents = static_cast<WebContentsImpl*>(shell()->web_contents());
1976 web_contents->GetPrimaryMainFrame()->DoNotDeleteForTesting();
1977 DisableBFCacheForRFHForTesting(
1978 web_contents->GetPrimaryFrameTree().root()->current_frame_host());
1979
1980 // Load a page with COOP set to force the browsing context group swap
1981 // and clears out old proxies.
1982 GURL new_main_url(https_server()->GetURL(
1983 "b.test", "/set-header?Cross-Origin-Opener-Policy: same-origin"));
1984
1985 EXPECT_TRUE(NavigateToURL(shell(), new_main_url));
1986
1987 TestNavigationObserver back_load_observer(web_contents);
1988 web_contents->GetController().GoBack();
1989 back_load_observer.Wait();
1990}
1991
nickdb193a12016-09-09 23:09:231992// FrameTreeBrowserTest variant where we isolate http://*.is, Iceland's top
naskoabed2a52017-05-03 05:10:171993// level domain. This is an analogue to isolating extensions, which we can use
1994// inside content_browsertests, where extensions don't exist. Iceland, like an
nickdb193a12016-09-09 23:09:231995// extension process, is a special place with magical powers; we want to protect
1996// it from outsiders.
1997class IsolateIcelandFrameTreeBrowserTest : public ContentBrowserTest {
1998 public:
Fergal Dalya1d569972021-03-16 03:24:531999 IsolateIcelandFrameTreeBrowserTest() = default;
nickdb193a12016-09-09 23:09:232000
Peter Boström9b036532021-10-28 23:37:282001 IsolateIcelandFrameTreeBrowserTest(
2002 const IsolateIcelandFrameTreeBrowserTest&) = delete;
2003 IsolateIcelandFrameTreeBrowserTest& operator=(
2004 const IsolateIcelandFrameTreeBrowserTest&) = delete;
2005
nickdb193a12016-09-09 23:09:232006 void SetUpCommandLine(base::CommandLine* command_line) override {
Lukasz Anforowicze7c87d12018-11-03 02:53:342007 // Blink suppresses navigations to blob URLs of origins different from the
alexis.menard83f1b6d2017-05-17 19:37:332008 // frame initiating the navigation. We disable those checks for this test,
2009 // to test what happens in a compromise scenario.
2010 command_line->AppendSwitch(switches::kDisableWebSecurity);
Lukasz Anforowicze7c87d12018-11-03 02:53:342011
2012 // ProcessSwitchForIsolatedBlob test below requires that one of URLs used in
Lukasz Anforowicz25420932018-12-18 20:59:222013 // the test (blob:http://b.is/) belongs to an isolated origin.
2014 command_line->AppendSwitchASCII(switches::kIsolateOrigins, "http://b.is/");
nickdb193a12016-09-09 23:09:232015 }
2016
2017 void SetUpOnMainThread() override {
2018 host_resolver()->AddRule("*", "127.0.0.1");
nickdb193a12016-09-09 23:09:232019 SetupCrossSiteRedirector(embedded_test_server());
martijn12d2dad2016-11-11 10:59:272020 ASSERT_TRUE(embedded_test_server()->Start());
nickdb193a12016-09-09 23:09:232021 }
nickdb193a12016-09-09 23:09:232022};
2023
2024// Regression test for https://crbug.com/644966
2025IN_PROC_BROWSER_TEST_F(IsolateIcelandFrameTreeBrowserTest,
2026 ProcessSwitchForIsolatedBlob) {
nickdb193a12016-09-09 23:09:232027 // Set up an iframe.
2028 WebContents* contents = shell()->web_contents();
2029 FrameTreeNode* root =
Carlos Caballero15caeeb2021-10-27 09:57:552030 static_cast<WebContentsImpl*>(contents)->GetPrimaryFrameTree().root();
nickdb193a12016-09-09 23:09:232031 GURL main_url(embedded_test_server()->GetURL(
nick07fd7e12016-09-12 18:54:062032 "a.com", "/cross_site_iframe_factory.html?a(a)"));
nickdb193a12016-09-09 23:09:232033 EXPECT_TRUE(NavigateToURL(shell(), main_url));
2034
2035 // The navigation targets an invalid blob url; that's intentional to trigger
2036 // an error response. The response should commit in a process dedicated to
Nasko Oskov0401d812021-02-05 22:20:082037 // http://b.is or error pages, depending on policy.
Nick Carterb7e71312018-08-03 23:36:132038 EXPECT_EQ(
2039 "done",
2040 EvalJs(
2041 root,
2042 "new Promise((resolve) => {"
2043 " var iframe_element = document.getElementsByTagName('iframe')[0];"
2044 " iframe_element.onload = () => resolve('done');"
Lukasz Anforowicz25420932018-12-18 20:59:222045 " iframe_element.src = 'blob:http://b.is/';"
Nick Carterb7e71312018-08-03 23:36:132046 "});"));
Fergal Dalyf0522332020-07-18 06:09:462047 EXPECT_TRUE(WaitForLoadStop(contents));
nickdb193a12016-09-09 23:09:232048
2049 // Make sure we did a process transfer back to "b.is".
Aaron Colwelle953e562019-07-24 16:47:362050 const std::string kExpectedSiteURL =
Sharon Yang02279222025-01-15 19:09:192051 AreAllSitesIsolatedForTesting()
2052 ? "http://a.com/"
2053 : SiteInstanceImpl::GetDefaultSiteURL().spec();
Nasko Oskov0401d812021-02-05 22:20:082054 const std::string kExpectedSubframeSiteURL =
2055 SiteIsolationPolicy::IsErrorPageIsolationEnabled(/*in_main_frame*/ false)
2056 ? "chrome-error://chromewebdata/"
2057 : "http://b.is/";
Aaron Colwelle953e562019-07-24 16:47:362058 EXPECT_EQ(base::StringPrintf(" Site A ------------ proxies for B\n"
2059 " +--Site B ------- proxies for A\n"
2060 "Where A = %s\n"
Nasko Oskov0401d812021-02-05 22:20:082061 " B = %s",
2062 kExpectedSiteURL.c_str(),
2063 kExpectedSubframeSiteURL.c_str()),
Fergal Daly79f44292020-12-01 02:30:482064 DepictFrameTree(*root));
nickdb193a12016-09-09 23:09:232065}
2066
Arthur Sonzogni64457592022-11-22 11:08:592067class FrameTreeCredentiallessIframeBrowserTest : public FrameTreeBrowserTest {
Antonio Sartori5abc8de2021-07-13 08:42:472068 public:
Arthur Sonzogni64457592022-11-22 11:08:592069 FrameTreeCredentiallessIframeBrowserTest() = default;
Antonio Sartori5abc8de2021-07-13 08:42:472070
2071 void SetUpCommandLine(base::CommandLine* command_line) override {
Antonio Sartori753cd6d2021-07-23 08:34:552072 command_line->AppendSwitch(switches::kEnableBlinkTestFeatures);
Antonio Sartori5abc8de2021-07-13 08:42:472073 }
2074};
2075
Arthur Sonzogni64457592022-11-22 11:08:592076// Tests the mojo propagation of the 'credentialless' attribute to the browser.
2077IN_PROC_BROWSER_TEST_F(FrameTreeCredentiallessIframeBrowserTest,
Antonio Sartori5abc8de2021-07-13 08:42:472078 AttributeIsPropagatedToBrowser) {
2079 GURL main_url(embedded_test_server()->GetURL("/hello.html"));
2080 EXPECT_TRUE(NavigateToURL(shell(), main_url));
2081
2082 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:552083 ->GetPrimaryFrameTree()
2084 .root();
Antonio Sartori5abc8de2021-07-13 08:42:472085
Arthur Sonzogni64457592022-11-22 11:08:592086 // Not setting the attribute => the iframe is not credentialless.
Antonio Sartori5abc8de2021-07-13 08:42:472087 EXPECT_TRUE(ExecJs(root,
2088 "var f = document.createElement('iframe');"
2089 "document.body.appendChild(f);"));
2090 EXPECT_EQ(1U, root->child_count());
Miyoung Shinc9ff4812023-01-05 08:58:052091 EXPECT_FALSE(root->child_at(0)->Credentialless());
Yifan Luoe1a2e05d2022-01-12 16:47:592092 EXPECT_EQ(false, EvalJs(root->child_at(0)->current_frame_host(),
Arthur Sonzogni64457592022-11-22 11:08:592093 "window.credentialless"));
Antonio Sartori5abc8de2021-07-13 08:42:472094
Arthur Sonzogni64457592022-11-22 11:08:592095 // Setting the attribute on the iframe element makes the iframe
2096 // credentialless.
Antonio Sartori5abc8de2021-07-13 08:42:472097 EXPECT_TRUE(ExecJs(root,
2098 "var d = document.createElement('div');"
Arthur Sonzogni64457592022-11-22 11:08:592099 "d.innerHTML = '<iframe credentialless></iframe>';"
Antonio Sartori5abc8de2021-07-13 08:42:472100 "document.body.appendChild(d);"));
2101 EXPECT_EQ(2U, root->child_count());
Miyoung Shinc9ff4812023-01-05 08:58:052102 EXPECT_TRUE(root->child_at(1)->Credentialless());
Yifan Luo400867472022-05-19 09:34:202103 EXPECT_EQ(true, EvalJs(root->child_at(1)->current_frame_host(),
Arthur Sonzogni64457592022-11-22 11:08:592104 "window.credentialless"));
Antonio Sartori5abc8de2021-07-13 08:42:472105
2106 // Setting the attribute via javascript works.
2107 EXPECT_TRUE(ExecJs(root,
2108 "var g = document.createElement('iframe');"
Arthur Sonzogni64457592022-11-22 11:08:592109 "g.credentialless = true;"
Antonio Sartori5abc8de2021-07-13 08:42:472110 "document.body.appendChild(g);"));
2111 EXPECT_EQ(3U, root->child_count());
Miyoung Shinc9ff4812023-01-05 08:58:052112 EXPECT_TRUE(root->child_at(2)->Credentialless());
Yifan Luo400867472022-05-19 09:34:202113 EXPECT_EQ(true, EvalJs(root->child_at(2)->current_frame_host(),
Arthur Sonzogni64457592022-11-22 11:08:592114 "window.credentialless"));
Antonio Sartori5abc8de2021-07-13 08:42:472115
Arthur Sonzogni64457592022-11-22 11:08:592116 EXPECT_TRUE(ExecJs(root, "g.credentialless = false;"));
Miyoung Shinc9ff4812023-01-05 08:58:052117 EXPECT_FALSE(root->child_at(2)->Credentialless());
Yifan Luo400867472022-05-19 09:34:202118 EXPECT_EQ(true, EvalJs(root->child_at(2)->current_frame_host(),
Arthur Sonzogni64457592022-11-22 11:08:592119 "window.credentialless"));
Antonio Sartori5abc8de2021-07-13 08:42:472120
Arthur Sonzogni64457592022-11-22 11:08:592121 EXPECT_TRUE(ExecJs(root, "g.credentialless = true;"));
Miyoung Shinc9ff4812023-01-05 08:58:052122 EXPECT_TRUE(root->child_at(2)->Credentialless());
Yifan Luo400867472022-05-19 09:34:202123 EXPECT_EQ(true, EvalJs(root->child_at(2)->current_frame_host(),
Arthur Sonzogni64457592022-11-22 11:08:592124 "window.credentialless"));
Antonio Sartori5abc8de2021-07-13 08:42:472125}
2126
Thomas Lukaszewicz54d8e432024-09-18 20:13:172127INSTANTIATE_TEST_SUITE_P(
2128 ,
2129 FrameTreeBrowserWithDiscardTest,
2130 ::testing::Values(false, true),
2131 [](const ::testing::TestParamInfo<
2132 FrameTreeBrowserWithDiscardTest::ParamType>& info) {
2133 return info.param ? "KeepAlive" : "NoKeepAlive";
2134 });
2135
2136INSTANTIATE_TEST_SUITE_P(
2137 ,
2138 FrameTreeDiscardPendingNavigationTest,
2139 ::testing::Values(false, true),
2140 [](const ::testing::TestParamInfo<
2141 FrameTreeDiscardPendingNavigationTest::ParamType>& info) {
2142 return info.param ? "KeepAlive" : "NoKeepAlive";
2143 });
2144
2145INSTANTIATE_TEST_SUITE_P(
2146 ,
2147 DedicatedWorkerFrameTreeBrowserTest,
2148 ::testing::Values(false, true),
2149 [](const ::testing::TestParamInfo<
2150 DedicatedWorkerFrameTreeBrowserTest::ParamType>& info) {
2151 return info.param ? "KeepAlive" : "NoKeepAlive";
2152 });
2153
[email protected]5f96f5a62014-01-10 00:05:112154} // namespace content