blob: 1356bb9808b45cee98b68df8513f9c88e7d665c5 [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 Oskov03912102019-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()) {
Nan Lin359184e2025-04-03 19:45:22331 // Increment the worker ref count of the renderer process to keep it
Thomas Lukaszewicz54d8e432024-09-18 20:13:17332 // alive post discard, simulating the situation where the process may be
333 // shared by multiple frames.
Nan Lin359184e2025-04-03 19:45:22334 root_rph->IncrementWorkerRefCount();
Thomas Lukaszewicz54d8e432024-09-18 20:13:17335 }
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 Lukaszewiczbbd83a202025-03-22 07:16:54480IN_PROC_BROWSER_TEST_P(FrameTreeBrowserWithDiscardTest,
481 DiscardClearsServiceWorkers) {
482 WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents());
483 FrameTree& frame_tree = wc->GetPrimaryFrameTree();
484 FrameTreeNode* root = frame_tree.root();
485
486 // Load a new page, register a service worker and wait for it to become ready.
487 EXPECT_TRUE(NavigateToURL(shell(), embedded_test_server()->GetURL(
488 "/register_service_worker.html")));
489 EXPECT_EQ("DONE", EvalJs(shell(), "register('/fetch_event_passthrough.js')"));
490 RenderFrameHostImplWrapper rfh(wc->GetPrimaryMainFrame());
491 EXPECT_EQ(1u, rfh->service_worker_clients_for_testing().size());
492
493 // Discard the frame tree.
494 EXPECT_FALSE(root->was_discarded());
495 EXPECT_FALSE(wc->GetController().NeedsReload());
496 DiscardFrameTree(frame_tree);
497 EXPECT_TRUE(root->was_discarded());
498 EXPECT_TRUE(wc->GetController().NeedsReload());
499
500 // Assert the service worker has been de-registered post discard.
501 ASSERT_TRUE(base::test::RunUntil(
502 [&]() { return rfh->service_worker_clients_for_testing().size() == 0; }));
503}
504
Thomas Lukaszewicz1b672fe2024-09-17 08:35:03505// Runs pending navigation discard browsertests with RenderDocument enabled for
506// all frames to ensure a speculative RFH is created during navigation.
507class FrameTreeDiscardPendingNavigationTest
508 : public FrameTreeBrowserWithDiscardTest {
509 public:
510 FrameTreeDiscardPendingNavigationTest() {
511 InitAndEnableRenderDocumentFeature(
512 &feature_list_,
513 GetRenderDocumentLevelName(RenderDocumentLevel::kAllFrames));
514 }
515
516 private:
517 base::test::ScopedFeatureList feature_list_;
518};
519
Thomas Lukaszewicz54d8e432024-09-18 20:13:17520IN_PROC_BROWSER_TEST_P(FrameTreeDiscardPendingNavigationTest,
Thomas Lukaszewicz1b672fe2024-09-17 08:35:03521 DiscardHandlesNavigationWaitingResponse) {
522 WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents());
523 FrameTree& frame_tree = wc->GetPrimaryFrameTree();
524 FrameTreeNode* root = frame_tree.root();
525
526 const GURL original_url =
527 embedded_test_server()->GetURL("/frame_tree/top.html");
528 EXPECT_TRUE(NavigateToURL(shell(), original_url));
529 EXPECT_FALSE(root->was_discarded());
530 EXPECT_FALSE(wc->GetController().NeedsReload());
531 EXPECT_EQ(3UL, root->child_count());
532
533 // Queue a navigation.
534 const GURL new_url = embedded_test_server()->GetURL("/frame_tree/1-1.html");
535 TestNavigationManager manager(shell()->web_contents(), new_url);
536 shell()->LoadURL(new_url);
537
538 // Get to the point where the frame is waiting for the response.
539 EXPECT_TRUE(manager.WaitForRequestStart());
540 manager.ResumeNavigation();
541
542 // Discard while waiting for a response for the previous navigation.
Thomas Lukaszewicz54d8e432024-09-18 20:13:17543 DiscardFrameTree(frame_tree);
Thomas Lukaszewicz1b672fe2024-09-17 08:35:03544 EXPECT_TRUE(WaitForLoadStop(wc));
545 EXPECT_TRUE(root->was_discarded());
546 EXPECT_TRUE(wc->GetController().NeedsReload());
547
548 // Assert the pending navigation finished.
549 ASSERT_TRUE(manager.WaitForNavigationFinished());
550
551 // Wait for the discarded document to be replaced and clear its children.
552 ASSERT_TRUE(
553 base::test::RunUntil([&]() { return 0u == root->child_count(); }));
Thomas Lukaszewicz54d8e432024-09-18 20:13:17554 EXPECT_EQ(original_url, wc->GetLastCommittedURL());
Thomas Lukaszewicz1b672fe2024-09-17 08:35:03555}
556
Thomas Lukaszewicz54d8e432024-09-18 20:13:17557IN_PROC_BROWSER_TEST_P(FrameTreeDiscardPendingNavigationTest,
Thomas Lukaszewicz1b672fe2024-09-17 08:35:03558 DiscardHandlesNavigationPendingCommit) {
559 class ReadyToCommitWaiter : public content::WebContentsObserver {
560 public:
561 explicit ReadyToCommitWaiter(content::WebContents* web_contents)
562 : content::WebContentsObserver(web_contents) {}
563
564 void Wait() { run_loop_.Run(); }
565
566 void ReadyToCommitNavigation(
567 content::NavigationHandle* navigation_handle) override {
568 run_loop_.Quit();
569 }
570
571 private:
572 base::RunLoop run_loop_;
573 };
574
Thomas Lukaszewicz0ef0f45f2025-02-18 17:59:42575 base::HistogramTester uma_recorder;
Thomas Lukaszewicz1b672fe2024-09-17 08:35:03576 WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents());
577 FrameTree& frame_tree = wc->GetPrimaryFrameTree();
578 FrameTreeNode* root = frame_tree.root();
579
580 const GURL original_url =
581 embedded_test_server()->GetURL("/frame_tree/top.html");
582 EXPECT_TRUE(NavigateToURL(shell(), original_url));
583 EXPECT_FALSE(root->was_discarded());
584 EXPECT_FALSE(wc->GetController().NeedsReload());
585 EXPECT_EQ(3UL, root->child_count());
586
587 RenderFrameHostImplWrapper initial_rfh(wc->GetPrimaryMainFrame());
588 EXPECT_TRUE(initial_rfh->IsRenderFrameLive());
589
590 // Queue a navigation and wait until the browser is ready to commit.
591 ReadyToCommitWaiter ready_to_commit_waiter(wc);
592 const GURL new_url = embedded_test_server()->GetURL("/title1.html");
593 shell()->LoadURL(new_url);
594 ready_to_commit_waiter.Wait();
595
596 // Discard while ready to commit the previous navigation.
Thomas Lukaszewicz0ef0f45f2025-02-18 17:59:42597 uma_recorder.ExpectUniqueSample("Discarding.DiscardFrameTree", false, 0);
Thomas Lukaszewicz1b672fe2024-09-17 08:35:03598 frame_tree.Discard();
599 EXPECT_TRUE(WaitForLoadStop(wc));
600
601 // The pending navigation will commit to a new rfh and the tab will settle to
602 // an undiscarded state.
603 EXPECT_FALSE(root->was_discarded());
604 EXPECT_FALSE(wc->GetController().NeedsReload());
Thomas Lukaszewicz0ef0f45f2025-02-18 17:59:42605 uma_recorder.ExpectUniqueSample("Discarding.DiscardFrameTree", false, 1);
Thomas Lukaszewicz1b672fe2024-09-17 08:35:03606
607 RenderFrameHostImplWrapper final_rfh(wc->GetPrimaryMainFrame());
608 EXPECT_NE(initial_rfh.get(), final_rfh.get());
609 EXPECT_EQ(new_url, root->current_url());
610}
611
Thomas Lukaszewicz8067f2c12024-12-13 07:02:18612// Asserts that a process pinned with a keep-alive ref hosting only discarded
613// frames is successfully shutdown after the keep-alive timeout.
614IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest,
615 DiscardedFrameRendererShutdownAfterKeepAliveTimeout) {
616 WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents());
617 FrameTree& frame_tree = wc->GetPrimaryFrameTree();
618
619 EXPECT_TRUE(
620 NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html")));
621
622 // Ensure the view, frame and process are reported alive.
623 RenderFrameHostImpl* rfh =
624 static_cast<RenderFrameHostImpl*>(wc->GetPrimaryMainFrame());
625 RenderViewHostImpl* rvh = rfh->render_view_host();
626 RenderProcessHostImpl* rph =
627 static_cast<RenderProcessHostImpl*>(rfh->GetProcess());
628 EXPECT_TRUE(rvh->IsRenderViewLive());
629 EXPECT_TRUE(rfh->IsRenderFrameLive());
630 EXPECT_TRUE(rph->IsInitializedAndNotDead());
631
Nan Lin359184e2025-04-03 19:45:22632 // Set a worker on the renderer process.
633 rph->IncrementWorkerRefCount();
Thomas Lukaszewicz8067f2c12024-12-13 07:02:18634
635 // Discard the frame tree. The process should remain alive.
636 frame_tree.Discard();
637 EXPECT_TRUE(rvh->IsRenderViewLive());
638 EXPECT_TRUE(rfh->IsRenderFrameLive());
639 EXPECT_TRUE(rph->IsInitializedAndNotDead());
640
641 // Simulate a keep-alive timeout, the process should be promptly shutdown.
642 rfh->SimulateDiscardShutdownKeepAliveTimeoutForTesting();
643 RenderProcessHostWatcher process_exit_observer(
644 wc, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
645 process_exit_observer.Wait();
646
647 // Ensure the process has been successfully shutdown.
648 EXPECT_FALSE(rvh->IsRenderViewLive());
649 EXPECT_FALSE(rfh->IsRenderFrameLive());
650 EXPECT_FALSE(rph->IsInitializedAndNotDead());
651}
652
Thomas Lukaszewicz8ada31b2024-09-01 19:18:16653class DedicatedWorkerObserver : public DedicatedWorkerService::Observer {
654 public:
655 explicit DedicatedWorkerObserver(DedicatedWorkerService* worker_service) {
656 scoped_context_observation_.Observe(worker_service);
657 }
658 DedicatedWorkerObserver(const DedicatedWorkerObserver&) = delete;
659 DedicatedWorkerObserver& operator=(const DedicatedWorkerObserver&) = delete;
660 ~DedicatedWorkerObserver() override = default;
661
662 void WaitForCreated() {
663 if (!is_created_) {
664 run_loop_.emplace();
665 run_loop_->Run();
666 run_loop_.reset();
667 }
668 }
669 void WaitForDestroyed() {
670 if (!is_destroyed_) {
671 run_loop_.emplace();
672 run_loop_->Run();
673 run_loop_.reset();
674 }
675 }
676
677 // DedicatedWorkerService::Observer:
678 void OnBeforeWorkerDestroyed(const blink::DedicatedWorkerToken& worker_token,
679 DedicatedWorkerCreator creator) override {
680 is_destroyed_ = true;
681 if (run_loop_.has_value()) {
682 run_loop_->Quit();
683 }
684 }
685 void OnWorkerCreated(const blink::DedicatedWorkerToken& worker_token,
686 int worker_process_id,
687 const url::Origin& security_origin,
688 DedicatedWorkerCreator creator) override {
689 is_created_ = true;
690 if (run_loop_.has_value()) {
691 run_loop_->Quit();
692 }
693 }
694 void OnFinalResponseURLDetermined(
695 const blink::DedicatedWorkerToken& worker_token,
696 const GURL& url) override {}
697
698 private:
699 bool is_created_ = false;
700 bool is_destroyed_ = false;
701 std::optional<base::RunLoop> run_loop_;
702 base::ScopedObservation<DedicatedWorkerService,
703 DedicatedWorkerService::Observer>
704 scoped_context_observation_{this};
705};
706
707class DedicatedWorkerFrameTreeBrowserTest
708 : public FrameTreeBrowserWithDiscardTest {
709 public:
710 void SetUpCommandLine(base::CommandLine* command_line) override {
711 FrameTreeBrowserTest::SetUpCommandLine(command_line);
712 command_line->AppendSwitchASCII(blink::switches::kJavaScriptFlags,
713 "--expose-gc");
714 }
715};
716
Thomas Lukaszewicz54d8e432024-09-18 20:13:17717IN_PROC_BROWSER_TEST_P(DedicatedWorkerFrameTreeBrowserTest,
Thomas Lukaszewicz8ada31b2024-09-01 19:18:16718 DiscardClearsDedicatedWorkers) {
719 WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents());
720 FrameTree& frame_tree = wc->GetPrimaryFrameTree();
721 FrameTreeNode* root = frame_tree.root();
722
723 EXPECT_TRUE(
724 NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html")));
725
726 // Navigate to a page and register a dedicated worker.
727 DedicatedWorkerObserver worker_observer(root->current_frame_host()
728 ->GetStoragePartition()
729 ->GetDedicatedWorkerService());
730 EXPECT_TRUE(EvalJs(shell(), "const worker = new Worker('/workers/empty.js');")
731 .error.empty());
732 worker_observer.WaitForCreated();
733
734 // Discard the rfh, the associated worker should be cleared.
735 EXPECT_FALSE(root->was_discarded());
736 EXPECT_FALSE(wc->GetController().NeedsReload());
Thomas Lukaszewicz54d8e432024-09-18 20:13:17737 DiscardFrameTree(frame_tree);
Thomas Lukaszewicz8ada31b2024-09-01 19:18:16738 EXPECT_TRUE(root->was_discarded());
739 EXPECT_TRUE(wc->GetController().NeedsReload());
Thomas Lukaszewicz54d8e432024-09-18 20:13:17740
741 if (KeepAliveDiscardedProcess()) {
742 // Trigger GC to cleanup the worker in the renderer if persisted.
743 EXPECT_TRUE(EvalJs(shell(), "window.gc();").error.empty());
744 }
745
Thomas Lukaszewicz8ada31b2024-09-01 19:18:16746 worker_observer.WaitForDestroyed();
747}
748
749// TODO(347770670): Consider restricting script access to discarded documents
750// from related documents.
Thomas Lukaszewicz54d8e432024-09-18 20:13:17751IN_PROC_BROWSER_TEST_P(FrameTreeBrowserWithDiscardTest,
Thomas Lukaszewicz8ada31b2024-09-01 19:18:16752 DiscardedFrameAllowsScriptAccess) {
Thomas Lukaszewicz54d8e432024-09-18 20:13:17753 if (!KeepAliveDiscardedProcess()) {
754 GTEST_SKIP() << "Not applicable when destroying process post discard.";
755 }
756
Thomas Lukaszewicz8ada31b2024-09-01 19:18:16757 WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents());
758 FrameTree& frame_tree = wc->GetPrimaryFrameTree();
759
760 const GURL main_url(embedded_test_server()->GetURL("/title1.html"));
761 EXPECT_TRUE(NavigateToURL(shell(), main_url));
762
763 // Open a popup using window.open.
764 const GURL popup_url(embedded_test_server()->GetURL("/title2.html"));
765 Shell* new_shell = OpenPopup(shell(), popup_url, "foo");
766 EXPECT_TRUE(new_shell);
767
768 // Assert the opened window is able to script its opener.
769 EXPECT_EQ("foo", EvalJs(new_shell, "window.name;"));
770 EXPECT_TRUE(EvalJs(new_shell, "window.opener.name = 'bar';").error.empty());
771 EXPECT_EQ("bar", EvalJs(shell(), "window.name;"));
772
773 frame_tree.Discard();
774 EXPECT_TRUE(frame_tree.root()->was_discarded());
775
776 // After a discard operation the opened window should should still be able to
777 // script its opener.
778 EXPECT_TRUE(EvalJs(new_shell, "window.opener.name = 'bar2';").error.empty());
779 EXPECT_EQ("bar2", EvalJs(shell(), "window.name;"));
780
781 // After a reload the opened window should still be able to script its opener.
782 wc->GetController().LoadIfNecessary();
783 EXPECT_TRUE(WaitForLoadStop(wc));
784 EXPECT_TRUE(EvalJs(new_shell, "window.opener.name = 'bar3';").error.empty());
785 EXPECT_EQ("bar3", EvalJs(shell(), "window.name;"));
786}
787
788class FrameTreeBrowserTestWithBFCache : public FrameTreeBrowserTest {
789 public:
790 FrameTreeBrowserTestWithBFCache() {
791 feature_list_.InitWithFeaturesAndParameters(
792 {{features::kBackForwardCache, {}},
793 {kBackForwardCacheNoTimeEviction, {}}},
794 // Allow BackForwardCache for all devices regardless of their memory.
795 {features::kBackForwardCacheMemoryControls});
796 EXPECT_TRUE(IsBackForwardCacheEnabled());
797 }
798
799 private:
800 base::test::ScopedFeatureList feature_list_;
801};
802
803IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTestWithBFCache,
804 FrameTreeBrowserWithDiscardTest) {
805 WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents());
806 FrameTree& frame_tree = wc->GetPrimaryFrameTree();
807 BackForwardCacheImpl& back_forward_cache =
808 wc->GetController().GetBackForwardCache();
809
810 // The BFCache should start empty.
811 EXPECT_TRUE(back_forward_cache.GetEntries().empty());
812
813 const GURL url1(embedded_test_server()->GetURL("a.com", "/title1.html"));
814 const GURL url2(embedded_test_server()->GetURL("b.com", "/title2.html"));
815
816 // Navigate to url1.
817 EXPECT_TRUE(NavigateToURL(shell(), url1));
818 RenderFrameHostImplWrapper rfh_a(wc->GetPrimaryMainFrame());
819 RenderFrameDeletedObserver delete_observer_rfh_a(rfh_a.get());
820
821 // Navigate to url2, the frame hosting url1 should be moved to the BFCache.
822 EXPECT_TRUE(NavigateToURL(shell(), url2));
823 EXPECT_FALSE(delete_observer_rfh_a.deleted());
824 EXPECT_TRUE(rfh_a->IsInBackForwardCache());
825 EXPECT_EQ(1u, back_forward_cache.GetEntries().size());
826
827 // Discard the frame tree, the BFCache should have been cleared.
828 frame_tree.Discard();
829 EXPECT_TRUE(frame_tree.root()->was_discarded());
830 back_forward_cache.PostTaskToDestroyEvictedFrames();
831 ASSERT_TRUE(base::test::RunUntil(
832 [&]() { return back_forward_cache.GetEntries().empty(); }));
833 EXPECT_TRUE(delete_observer_rfh_a.deleted());
834}
835
836IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTestWithBFCache,
837 DiscardedFrameDoesNotEnterBFCache) {
838 WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents());
839 FrameTree& frame_tree = wc->GetPrimaryFrameTree();
840 BackForwardCacheImpl& back_forward_cache =
841 wc->GetController().GetBackForwardCache();
842
843 // The BFCache should start empty.
844 EXPECT_TRUE(back_forward_cache.GetEntries().empty());
845
846 const GURL url1(embedded_test_server()->GetURL("a.com", "/title1.html"));
847 const GURL url2(embedded_test_server()->GetURL("b.com", "/title2.html"));
848
849 // Navigate to url1.
850 EXPECT_TRUE(NavigateToURL(shell(), url1));
851 RenderFrameHostImplWrapper rfh_a(wc->GetPrimaryMainFrame());
852 RenderFrameDeletedObserver delete_observer_rfh_a(rfh_a.get());
853
854 // Discard the frame tree.
855 frame_tree.Discard();
856 EXPECT_TRUE(frame_tree.root()->was_discarded());
857 EXPECT_FALSE(delete_observer_rfh_a.deleted());
858
859 // Navigate to url2, the frame hosting url1 should not be moved to the
860 // BFCache.
861 EXPECT_TRUE(NavigateToURL(shell(), url2));
862 EXPECT_FALSE(frame_tree.root()->was_discarded());
863 EXPECT_TRUE(back_forward_cache.GetEntries().empty());
864 ASSERT_TRUE(
865 base::test::RunUntil([&]() { return delete_observer_rfh_a.deleted(); }));
866}
867
[email protected]5f96f5a62014-01-10 00:05:11868// Test that we can navigate away if the previous renderer doesn't clean up its
869// child frames.
Nasko Oskov31c45ff2019-10-16 01:15:19870IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, NavigateWithLeftoverFrames) {
alexmos478dcbb2014-12-10 21:24:46871 GURL base_url = embedded_test_server()->GetURL("A.com", "/site_isolation/");
[email protected]5f96f5a62014-01-10 00:05:11872
Alex Moshchukaa95adf52019-08-13 00:02:02873 EXPECT_TRUE(NavigateToURL(
874 shell(), embedded_test_server()->GetURL("/frame_tree/top.html")));
[email protected]5f96f5a62014-01-10 00:05:11875
876 // Hang the renderer so that it doesn't send any FrameDetached messages.
877 // (This navigation will never complete, so don't wait for it.)
Gyuyoung Kim107c2a02021-04-13 01:49:30878 shell()->LoadURL(GURL(blink::kChromeUIHangURL));
[email protected]5f96f5a62014-01-10 00:05:11879
880 // Check that the frame tree still has children.
881 WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents());
Carlos Caballero15caeeb2021-10-27 09:57:55882 FrameTreeNode* root = wc->GetPrimaryFrameTree().root();
[email protected]5f96f5a62014-01-10 00:05:11883 ASSERT_EQ(3UL, root->child_count());
884
885 // Navigate to a new URL. We use LoadURL because NavigateToURL will try to
886 // wait for the previous navigation to stop.
887 TestNavigationObserver tab_observer(wc, 1);
888 shell()->LoadURL(base_url.Resolve("blank.html"));
889 tab_observer.Wait();
890
[email protected]58faf942014-02-20 21:03:58891 // The frame tree should now be cleared.
[email protected]5f96f5a62014-01-10 00:05:11892 EXPECT_EQ(0UL, root->child_count());
[email protected]5f96f5a62014-01-10 00:05:11893}
894
creise42f2a52014-09-18 18:14:57895// Ensure that IsRenderFrameLive is true for main frames and same-site iframes.
896IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, IsRenderFrameLive) {
alexmos478dcbb2014-12-10 21:24:46897 GURL main_url(embedded_test_server()->GetURL("/frame_tree/top.html"));
Alex Moshchukaa95adf52019-08-13 00:02:02898 EXPECT_TRUE(NavigateToURL(shell(), main_url));
creise42f2a52014-09-18 18:14:57899
900 // It is safe to obtain the root frame tree node here, as it doesn't change.
901 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:55902 ->GetPrimaryFrameTree()
903 .root();
creise42f2a52014-09-18 18:14:57904
905 // The root and subframe should each have a live RenderFrame.
906 EXPECT_TRUE(
907 root->current_frame_host()->render_view_host()->IsRenderViewLive());
908 EXPECT_TRUE(root->current_frame_host()->IsRenderFrameLive());
909 EXPECT_TRUE(root->child_at(0)->current_frame_host()->IsRenderFrameLive());
910
911 // Load a same-site page into iframe and it should still be live.
alexmos478dcbb2014-12-10 21:24:46912 GURL http_url(embedded_test_server()->GetURL("/title1.html"));
Lukasz Anforowicz69c25dfd2020-11-12 21:50:20913 EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(0), http_url));
creise42f2a52014-09-18 18:14:57914 EXPECT_TRUE(
915 root->current_frame_host()->render_view_host()->IsRenderViewLive());
916 EXPECT_TRUE(root->current_frame_host()->IsRenderFrameLive());
917 EXPECT_TRUE(root->child_at(0)->current_frame_host()->IsRenderFrameLive());
918}
919
alexmosbc7eafa2014-12-06 01:38:09920// Ensure that origins are correctly set on navigations.
921IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, OriginSetOnNavigation) {
nick1466c842015-11-25 20:08:06922 GURL about_blank(url::kAboutBlankURL);
923 GURL main_url(
924 embedded_test_server()->GetURL("a.com", "/frame_tree/top.html"));
alexmosbc7eafa2014-12-06 01:38:09925 EXPECT_TRUE(NavigateToURL(shell(), main_url));
nick1466c842015-11-25 20:08:06926 WebContents* contents = shell()->web_contents();
alexmosbc7eafa2014-12-06 01:38:09927
928 // It is safe to obtain the root frame tree node here, as it doesn't change.
nick1466c842015-11-25 20:08:06929 FrameTreeNode* root =
Carlos Caballero15caeeb2021-10-27 09:57:55930 static_cast<WebContentsImpl*>(contents)->GetPrimaryFrameTree().root();
alexmosbc7eafa2014-12-06 01:38:09931
932 // Extra '/' is added because the replicated origin is serialized in RFC 6454
933 // format, which dictates no trailing '/', whereas GURL::GetOrigin does put a
934 // '/' at the end.
Mike West800532c2021-10-14 09:26:52935 EXPECT_EQ(main_url.DeprecatedGetOriginAsURL().spec(),
nick1466c842015-11-25 20:08:06936 root->current_origin().Serialize() + '/');
937 EXPECT_EQ(
Mike West800532c2021-10-14 09:26:52938 main_url.DeprecatedGetOriginAsURL().spec(),
nick1466c842015-11-25 20:08:06939 root->current_frame_host()->GetLastCommittedOrigin().Serialize() + '/');
alexmosbc7eafa2014-12-06 01:38:09940
nick1466c842015-11-25 20:08:06941 // The iframe is inititially same-origin.
942 EXPECT_TRUE(
943 root->current_frame_host()->GetLastCommittedOrigin().IsSameOriginWith(
944 root->child_at(0)->current_frame_host()->GetLastCommittedOrigin()));
945 EXPECT_EQ(root->current_origin().Serialize(), GetOriginFromRenderer(root));
946 EXPECT_EQ(root->child_at(0)->current_origin().Serialize(),
947 GetOriginFromRenderer(root->child_at(0)));
948
949 // Navigate the iframe cross-origin.
950 GURL frame_url(embedded_test_server()->GetURL("b.com", "/title1.html"));
Lukasz Anforowicz69c25dfd2020-11-12 21:50:20951 EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(0), frame_url));
nick1466c842015-11-25 20:08:06952 EXPECT_EQ(frame_url, root->child_at(0)->current_url());
Mike West800532c2021-10-14 09:26:52953 EXPECT_EQ(frame_url.DeprecatedGetOriginAsURL().spec(),
nick1466c842015-11-25 20:08:06954 root->child_at(0)->current_origin().Serialize() + '/');
955 EXPECT_FALSE(
956 root->current_frame_host()->GetLastCommittedOrigin().IsSameOriginWith(
957 root->child_at(0)->current_frame_host()->GetLastCommittedOrigin()));
958 EXPECT_EQ(root->current_origin().Serialize(), GetOriginFromRenderer(root));
959 EXPECT_EQ(root->child_at(0)->current_origin().Serialize(),
960 GetOriginFromRenderer(root->child_at(0)));
alexmosbc7eafa2014-12-06 01:38:09961
nick1466c842015-11-25 20:08:06962 // Parent-initiated about:blank navigation should inherit the parent's a.com
963 // origin.
964 NavigateIframeToURL(contents, "1-1-id", about_blank);
965 EXPECT_EQ(about_blank, root->child_at(0)->current_url());
Mike West800532c2021-10-14 09:26:52966 EXPECT_EQ(main_url.DeprecatedGetOriginAsURL().spec(),
nick1466c842015-11-25 20:08:06967 root->child_at(0)->current_origin().Serialize() + '/');
968 EXPECT_EQ(root->current_frame_host()->GetLastCommittedOrigin().Serialize(),
969 root->child_at(0)
970 ->current_frame_host()
971 ->GetLastCommittedOrigin()
972 .Serialize());
973 EXPECT_TRUE(
974 root->current_frame_host()->GetLastCommittedOrigin().IsSameOriginWith(
975 root->child_at(0)->current_frame_host()->GetLastCommittedOrigin()));
976 EXPECT_EQ(root->current_origin().Serialize(), GetOriginFromRenderer(root));
977 EXPECT_EQ(root->child_at(0)->current_origin().Serialize(),
978 GetOriginFromRenderer(root->child_at(0)));
alexmosbc7eafa2014-12-06 01:38:09979
980 GURL data_url("data:text/html,foo");
981 EXPECT_TRUE(NavigateToURL(shell(), data_url));
982
983 // Navigating to a data URL should set a unique origin. This is represented
984 // as "null" per RFC 6454.
nick1466c842015-11-25 20:08:06985 EXPECT_EQ("null", root->current_origin().Serialize());
Dave Tapuska327c06c92022-06-13 20:31:51986 EXPECT_TRUE(
987 contents->GetPrimaryMainFrame()->GetLastCommittedOrigin().opaque());
nick1466c842015-11-25 20:08:06988 EXPECT_EQ("null", GetOriginFromRenderer(root));
alexmosbc7eafa2014-12-06 01:38:09989
990 // Re-navigating to a normal URL should update the origin.
991 EXPECT_TRUE(NavigateToURL(shell(), main_url));
Mike West800532c2021-10-14 09:26:52992 EXPECT_EQ(main_url.DeprecatedGetOriginAsURL().spec(),
nick1466c842015-11-25 20:08:06993 root->current_origin().Serialize() + '/');
994 EXPECT_EQ(
Mike West800532c2021-10-14 09:26:52995 main_url.DeprecatedGetOriginAsURL().spec(),
Dave Tapuska327c06c92022-06-13 20:31:51996 contents->GetPrimaryMainFrame()->GetLastCommittedOrigin().Serialize() +
997 '/');
998 EXPECT_FALSE(
999 contents->GetPrimaryMainFrame()->GetLastCommittedOrigin().opaque());
nick1466c842015-11-25 20:08:061000 EXPECT_EQ(root->current_origin().Serialize(), GetOriginFromRenderer(root));
alexmosbc7eafa2014-12-06 01:38:091001}
1002
nick9f34e2892016-01-12 21:01:071003// Tests a cross-origin navigation to a blob URL. The main frame initiates this
1004// navigation on its grandchild. It should wind up in the main frame's process.
1005IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, NavigateGrandchildToBlob) {
1006 WebContents* contents = shell()->web_contents();
1007 FrameTreeNode* root =
Carlos Caballero15caeeb2021-10-27 09:57:551008 static_cast<WebContentsImpl*>(contents)->GetPrimaryFrameTree().root();
nick9f34e2892016-01-12 21:01:071009
1010 // First, snapshot the FrameTree for a normal A(B(A)) case where all frames
1011 // are served over http. The blob test should result in the same structure.
1012 EXPECT_TRUE(NavigateToURL(
1013 shell(), embedded_test_server()->GetURL(
1014 "a.com", "/cross_site_iframe_factory.html?a(b(a))")));
Fergal Daly79f44292020-12-01 02:30:481015 std::string reference_tree = DepictFrameTree(*root);
nick9f34e2892016-01-12 21:01:071016
1017 GURL main_url(embedded_test_server()->GetURL(
1018 "a.com", "/cross_site_iframe_factory.html?a(b(c))"));
1019 EXPECT_TRUE(NavigateToURL(shell(), main_url));
1020
1021 // The root node will initiate the navigation; its grandchild node will be the
1022 // target of the navigation.
1023 FrameTreeNode* target = root->child_at(0)->child_at(0);
1024
creisb2d5f1f32016-11-07 23:25:051025 RenderFrameDeletedObserver deleted_observer(target->current_frame_host());
Nick Carterb7e71312018-08-03 23:36:131026 std::string html =
1027 "<html><body><div>This is blob content.</div>"
1028 "<script>"
Philip Jägenstedt67302a22018-09-14 09:58:051029 "window.parent.parent.postMessage('HI', self.origin);"
Nick Carterb7e71312018-08-03 23:36:131030 "</script></body></html>";
1031 std::string script = JsReplace(
1032 "new Promise((resolve) => {"
1033 " window.addEventListener('message', resolve, false);"
1034 " var blob = new Blob([$1], {type: 'text/html'});"
1035 " var blob_url = URL.createObjectURL(blob);"
1036 " frames[0][0].location.href = blob_url;"
1037 "}).then((event) => {"
nick9f34e2892016-01-12 21:01:071038 " document.body.appendChild(document.createTextNode(event.data));"
Nick Carterb7e71312018-08-03 23:36:131039 " return event.source.location.href;"
1040 "});",
1041 html);
1042 std::string blob_url_string = EvalJs(root, script).ExtractString();
creisb2d5f1f32016-11-07 23:25:051043 // Wait for the RenderFrame to go away, if this will be cross-process.
1044 if (AreAllSitesIsolatedForTesting())
1045 deleted_observer.WaitUntilDeleted();
nick9f34e2892016-01-12 21:01:071046 EXPECT_EQ(GURL(blob_url_string), target->current_url());
1047 EXPECT_EQ(url::kBlobScheme, target->current_url().scheme());
Chris Palmerab5e5b52018-09-28 19:19:301048 EXPECT_FALSE(target->current_origin().opaque());
nick9f34e2892016-01-12 21:01:071049 EXPECT_EQ("a.com", target->current_origin().host());
1050 EXPECT_EQ(url::kHttpScheme, target->current_origin().scheme());
Nick Carterb7e71312018-08-03 23:36:131051 EXPECT_EQ("This is blob content.",
1052 EvalJs(target, "document.body.children[0].innerHTML"));
Fergal Daly79f44292020-12-01 02:30:481053 EXPECT_EQ(reference_tree, DepictFrameTree(*root));
nick9f34e2892016-01-12 21:01:071054}
1055
1056IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, NavigateChildToAboutBlank) {
1057 GURL main_url(embedded_test_server()->GetURL(
1058 "a.com", "/cross_site_iframe_factory.html?a(b(c))"));
1059 EXPECT_TRUE(NavigateToURL(shell(), main_url));
1060 WebContentsImpl* contents =
1061 static_cast<WebContentsImpl*>(shell()->web_contents());
1062
1063 // The leaf node (c.com) will be navigated. Its parent node (b.com) will
1064 // initiate the navigation.
1065 FrameTreeNode* target =
Carlos Caballero15caeeb2021-10-27 09:57:551066 contents->GetPrimaryFrameTree().root()->child_at(0)->child_at(0);
Harkiran Bolaria4eaae3072021-09-21 10:45:481067 RenderFrameHost* initiator_rfh = target->parent();
nick9f34e2892016-01-12 21:01:071068
1069 // Give the target a name.
Nick Carterb7e71312018-08-03 23:36:131070 EXPECT_TRUE(ExecJs(target, "window.name = 'target';"));
nick9f34e2892016-01-12 21:01:071071
1072 // Use window.open(about:blank), then poll the document for access.
Nick Carterb7e71312018-08-03 23:36:131073 EvalJsResult about_blank_origin = EvalJs(
Harkiran Bolaria4eaae3072021-09-21 10:45:481074 initiator_rfh,
Nick Carterb7e71312018-08-03 23:36:131075 "new Promise(resolve => {"
1076 " var didNavigate = false;"
1077 " var intervalID = setInterval(function() {"
1078 " if (!didNavigate) {"
1079 " didNavigate = true;"
1080 " window.open('about:blank', 'target');"
1081 " }"
1082 " // Poll the document until it doesn't throw a SecurityError.\n"
1083 " try {"
1084 " frames[0].document.write('Hi from ' + document.domain);"
1085 " } catch (e) { return; }"
1086 " clearInterval(intervalID);"
Philip Jägenstedt67302a22018-09-14 09:58:051087 " resolve(frames[0].self.origin);"
Nick Carterb7e71312018-08-03 23:36:131088 " }, 16);"
1089 "});");
1090 EXPECT_EQ(target->current_origin(), about_blank_origin);
Nate Chapinab67b042021-05-04 21:00:511091 EXPECT_EQ(GURL(url::kAboutBlankURL), target->current_url());
1092 EXPECT_EQ(url::kAboutScheme, target->current_url().scheme());
Chris Palmerab5e5b52018-09-28 19:19:301093 EXPECT_FALSE(target->current_origin().opaque());
nick9f34e2892016-01-12 21:01:071094 EXPECT_EQ("b.com", target->current_origin().host());
1095 EXPECT_EQ(url::kHttpScheme, target->current_origin().scheme());
nick9f34e2892016-01-12 21:01:071096
Nick Carterb7e71312018-08-03 23:36:131097 EXPECT_EQ("Hi from b.com", EvalJs(target, "document.body.innerHTML"));
nick9f34e2892016-01-12 21:01:071098}
1099
1100// Nested iframes, three origins: A(B(C)). Frame A navigates C to about:blank
1101// (via window.open). This should wind up in A's origin per the spec. Test fails
1102// because of http://crbug.com/564292
1103IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest,
1104 DISABLED_NavigateGrandchildToAboutBlank) {
1105 GURL main_url(embedded_test_server()->GetURL(
1106 "a.com", "/cross_site_iframe_factory.html?a(b(c))"));
1107 EXPECT_TRUE(NavigateToURL(shell(), main_url));
1108 WebContentsImpl* contents =
1109 static_cast<WebContentsImpl*>(shell()->web_contents());
1110
1111 // The leaf node (c.com) will be navigated. Its grandparent node (a.com) will
1112 // initiate the navigation.
1113 FrameTreeNode* target =
Carlos Caballero15caeeb2021-10-27 09:57:551114 contents->GetPrimaryFrameTree().root()->child_at(0)->child_at(0);
Harkiran Bolaria4eaae3072021-09-21 10:45:481115 RenderFrameHost* initiator_rfh = target->parent()->GetParent();
nick9f34e2892016-01-12 21:01:071116
1117 // Give the target a name.
Nick Carterb7e71312018-08-03 23:36:131118 EXPECT_TRUE(ExecJs(target, "window.name = 'target';"));
nick9f34e2892016-01-12 21:01:071119
1120 // Use window.open(about:blank), then poll the document for access.
Nick Carterb7e71312018-08-03 23:36:131121 EvalJsResult about_blank_origin =
Harkiran Bolaria4eaae3072021-09-21 10:45:481122 EvalJs(initiator_rfh,
Nick Carterb7e71312018-08-03 23:36:131123 "new Promise((resolve) => {"
1124 " var didNavigate = false;"
1125 " var intervalID = setInterval(() => {"
1126 " if (!didNavigate) {"
1127 " didNavigate = true;"
1128 " window.open('about:blank', 'target');"
1129 " }"
1130 " // May raise a SecurityError, that's expected.\n"
1131 " try {"
1132 " frames[0][0].document.write('Hi from ' + document.domain);"
1133 " } catch (e) { return; }"
1134 " clearInterval(intervalID);"
Philip Jägenstedt67302a22018-09-14 09:58:051135 " resolve(frames[0][0].self.origin);"
Nick Carterb7e71312018-08-03 23:36:131136 " }, 16);"
1137 "});");
1138 EXPECT_EQ(target->current_origin(), about_blank_origin);
nick9f34e2892016-01-12 21:01:071139 EXPECT_EQ(GURL(url::kAboutBlankURL), target->current_url());
1140 EXPECT_EQ(url::kAboutScheme, target->current_url().scheme());
Chris Palmerab5e5b52018-09-28 19:19:301141 EXPECT_FALSE(target->current_origin().opaque());
nick9f34e2892016-01-12 21:01:071142 EXPECT_EQ("a.com", target->current_origin().host());
1143 EXPECT_EQ(url::kHttpScheme, target->current_origin().scheme());
nick9f34e2892016-01-12 21:01:071144
Nick Carterb7e71312018-08-03 23:36:131145 EXPECT_EQ("Hi from a.com", EvalJs(target, "document.body.innerHTML"));
nick9f34e2892016-01-12 21:01:071146}
1147
Nasko Oskov03912102019-01-11 00:21:321148// Tests a cross-origin navigation to a data: URL. The main frame initiates this
1149// navigation on its grandchild. It should wind up in the main frame's process
1150// and have precursor origin of the main frame origin.
1151IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, NavigateGrandchildToDataUrl) {
1152 GURL main_url(embedded_test_server()->GetURL(
1153 "a.com", "/cross_site_iframe_factory.html?a(b(c))"));
1154 EXPECT_TRUE(NavigateToURL(shell(), main_url));
1155 WebContentsImpl* contents =
1156 static_cast<WebContentsImpl*>(shell()->web_contents());
1157
1158 // The leaf node (c.com) will be navigated. Its grandparent node (a.com) will
1159 // initiate the navigation.
1160 FrameTreeNode* target =
Carlos Caballero15caeeb2021-10-27 09:57:551161 contents->GetPrimaryFrameTree().root()->child_at(0)->child_at(0);
Harkiran Bolaria4eaae3072021-09-21 10:45:481162 RenderFrameHostImpl* initiator_rfh = target->parent()->GetParent();
Nasko Oskov03912102019-01-11 00:21:321163
1164 // Give the target a name.
1165 EXPECT_TRUE(ExecJs(target, "window.name = 'target';"));
1166
1167 // Navigate the target frame through the initiator frame.
1168 {
1169 TestFrameNavigationObserver observer(target);
Harkiran Bolaria4eaae3072021-09-21 10:45:481170 EXPECT_TRUE(ExecJs(initiator_rfh,
1171 "window.open('data:text/html,content', 'target');"));
Nasko Oskov03912102019-01-11 00:21:321172 observer.Wait();
1173 }
1174
1175 url::Origin original_target_origin =
1176 target->current_frame_host()->GetLastCommittedOrigin();
1177 EXPECT_TRUE(original_target_origin.opaque());
1178 EXPECT_EQ(original_target_origin.GetTupleOrPrecursorTupleIfOpaque(),
1179 url::SchemeHostPort(main_url));
1180
1181 // Navigate the grandchild frame again cross-process to foo.com, then
Rakina Zata Amnic7367852022-11-07 17:10:401182 // go back in session history. The frame should commit a new opaque origin,
1183 // but it will still have the same precursor origin (the main frame origin).
Nasko Oskov03912102019-01-11 00:21:321184 {
1185 TestFrameNavigationObserver observer(target);
1186 EXPECT_TRUE(ExecJs(target, JsReplace("window.location = $1",
1187 embedded_test_server()->GetURL(
1188 "foo.com", "/title2.html"))));
1189 observer.Wait();
1190 }
1191 EXPECT_NE(original_target_origin,
1192 target->current_frame_host()->GetLastCommittedOrigin());
1193 {
1194 TestFrameNavigationObserver observer(target);
1195 contents->GetController().GoBack();
1196 observer.Wait();
1197 }
1198
1199 url::Origin target_origin =
1200 target->current_frame_host()->GetLastCommittedOrigin();
Rakina Zata Amnic7367852022-11-07 17:10:401201 EXPECT_NE(target_origin, original_target_origin);
Nasko Oskov03912102019-01-11 00:21:321202 EXPECT_TRUE(target_origin.opaque());
1203 EXPECT_EQ(target_origin.GetTupleOrPrecursorTupleIfOpaque(),
Rakina Zata Amnic7367852022-11-07 17:10:401204 original_target_origin.GetTupleOrPrecursorTupleIfOpaque());
1205 EXPECT_EQ(target_origin.GetTupleOrPrecursorTupleIfOpaque(),
Nasko Oskov03912102019-01-11 00:21:321206 url::SchemeHostPort(main_url));
Nasko Oskov03912102019-01-11 00:21:321207}
1208
naskofaa01fb2016-04-30 01:04:171209// Ensures that iframe with srcdoc is always put in the same origin as its
1210// parent frame.
1211IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, ChildFrameWithSrcdoc) {
1212 GURL main_url(embedded_test_server()->GetURL(
1213 "a.com", "/cross_site_iframe_factory.html?a(b)"));
1214 EXPECT_TRUE(NavigateToURL(shell(), main_url));
1215 WebContentsImpl* contents =
1216 static_cast<WebContentsImpl*>(shell()->web_contents());
Carlos Caballero15caeeb2021-10-27 09:57:551217 FrameTreeNode* root = contents->GetPrimaryFrameTree().root();
naskofaa01fb2016-04-30 01:04:171218 EXPECT_EQ(1U, root->child_count());
1219
1220 FrameTreeNode* child = root->child_at(0);
Philip Jägenstedt67302a22018-09-14 09:58:051221 std::string frame_origin = EvalJs(child, "self.origin;").ExtractString();
naskofaa01fb2016-04-30 01:04:171222 EXPECT_TRUE(
1223 child->current_frame_host()->GetLastCommittedOrigin().IsSameOriginWith(
Daniel Cheng88186bd52017-10-20 08:14:461224 url::Origin::Create(GURL(frame_origin))));
naskofaa01fb2016-04-30 01:04:171225 EXPECT_FALSE(
1226 root->current_frame_host()->GetLastCommittedOrigin().IsSameOriginWith(
Daniel Cheng88186bd52017-10-20 08:14:461227 url::Origin::Create(GURL(frame_origin))));
naskofaa01fb2016-04-30 01:04:171228
1229 // Create a new iframe with srcdoc and add it to the main frame. It should
1230 // be created in the same SiteInstance as the parent.
1231 {
Aran Gilman37d11632019-10-08 23:07:151232 std::string script(
1233 "var f = document.createElement('iframe');"
1234 "f.srcdoc = 'some content';"
1235 "document.body.appendChild(f)");
naskofaa01fb2016-04-30 01:04:171236 TestNavigationObserver observer(shell()->web_contents());
Nick Carterb7e71312018-08-03 23:36:131237 EXPECT_TRUE(ExecJs(root, script));
naskofaa01fb2016-04-30 01:04:171238 EXPECT_EQ(2U, root->child_count());
1239 observer.Wait();
1240
Lukasz Anforowicz42d3d07f2019-06-19 01:06:421241 EXPECT_TRUE(root->child_at(1)->current_url().IsAboutSrcdoc());
Peter Kastingeb8c3ce2021-08-20 04:39:351242 EvalJsResult js_result = EvalJs(root->child_at(1), "self.origin");
Mike West800532c2021-10-14 09:26:521243 EXPECT_EQ(root->current_frame_host()
1244 ->GetLastCommittedURL()
1245 .DeprecatedGetOriginAsURL(),
Peter Kastingeb8c3ce2021-08-20 04:39:351246 GURL(js_result.ExtractString()));
Mike West800532c2021-10-14 09:26:521247 EXPECT_NE(child->current_frame_host()
1248 ->GetLastCommittedURL()
1249 .DeprecatedGetOriginAsURL(),
Peter Kastingeb8c3ce2021-08-20 04:39:351250 GURL(js_result.ExtractString()));
naskofaa01fb2016-04-30 01:04:171251 }
1252
1253 // Set srcdoc on the existing cross-site frame. It should navigate the frame
1254 // back to the origin of the parent.
1255 {
Aran Gilman37d11632019-10-08 23:07:151256 std::string script(
1257 "var f = document.getElementById('child-0');"
1258 "f.srcdoc = 'some content';");
naskofaa01fb2016-04-30 01:04:171259 TestNavigationObserver observer(shell()->web_contents());
Nick Carterb7e71312018-08-03 23:36:131260 EXPECT_TRUE(ExecJs(root, script));
naskofaa01fb2016-04-30 01:04:171261 observer.Wait();
1262
Lukasz Anforowicz42d3d07f2019-06-19 01:06:421263 EXPECT_TRUE(child->current_url().IsAboutSrcdoc());
Avi Drissmana3144c582021-10-14 17:00:261264 EXPECT_EQ(root->current_frame_host()->GetLastCommittedOrigin().Serialize(),
1265 EvalJs(child, "self.origin"));
naskofaa01fb2016-04-30 01:04:171266 }
1267}
1268
Ian Clelland5cbaaf82017-11-27 22:00:031269// Ensure that sandbox flags are correctly set in the main frame when set by
1270// Content-Security-Policy header.
1271IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, SandboxFlagsSetForMainFrame) {
1272 GURL main_url(embedded_test_server()->GetURL("/csp_sandboxed_frame.html"));
1273 EXPECT_TRUE(NavigateToURL(shell(), main_url));
1274
1275 // It is safe to obtain the root frame tree node here, as it doesn't change.
1276 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:551277 ->GetPrimaryFrameTree()
1278 .root();
Ian Clelland5cbaaf82017-11-27 22:00:031279
1280 // Verify that sandbox flags are set properly for the root FrameTreeNode and
1281 // RenderFrameHost. Root frame is sandboxed with "allow-scripts".
arthursonzognib93a4472020-04-10 07:38:001282 EXPECT_EQ(network::mojom::WebSandboxFlags::kNone,
Ian Clelland5cbaaf82017-11-27 22:00:031283 root->effective_frame_policy().sandbox_flags);
arthursonzognib93a4472020-04-10 07:38:001284 EXPECT_EQ(network::mojom::WebSandboxFlags::kAll &
1285 ~network::mojom::WebSandboxFlags::kScripts &
1286 ~network::mojom::WebSandboxFlags::kAutomaticFeatures,
Ian Clelland5cbaaf82017-11-27 22:00:031287 root->active_sandbox_flags());
1288 EXPECT_EQ(root->active_sandbox_flags(),
1289 root->current_frame_host()->active_sandbox_flags());
1290
1291 // Verify that child frames inherit sandbox flags from the root. First frame
1292 // has no explicitly set flags of its own, and should inherit those from the
1293 // root. Second frame is completely sandboxed.
arthursonzognib93a4472020-04-10 07:38:001294 EXPECT_EQ(network::mojom::WebSandboxFlags::kAll &
1295 ~network::mojom::WebSandboxFlags::kScripts &
1296 ~network::mojom::WebSandboxFlags::kAutomaticFeatures,
Ian Clelland5cbaaf82017-11-27 22:00:031297 root->child_at(0)->effective_frame_policy().sandbox_flags);
arthursonzognib93a4472020-04-10 07:38:001298 EXPECT_EQ(network::mojom::WebSandboxFlags::kAll &
1299 ~network::mojom::WebSandboxFlags::kScripts &
1300 ~network::mojom::WebSandboxFlags::kAutomaticFeatures,
Ian Clelland5cbaaf82017-11-27 22:00:031301 root->child_at(0)->active_sandbox_flags());
1302 EXPECT_EQ(root->child_at(0)->active_sandbox_flags(),
1303 root->child_at(0)->current_frame_host()->active_sandbox_flags());
arthursonzognib93a4472020-04-10 07:38:001304 EXPECT_EQ(network::mojom::WebSandboxFlags::kAll,
Ian Clelland5cbaaf82017-11-27 22:00:031305 root->child_at(1)->effective_frame_policy().sandbox_flags);
arthursonzognib93a4472020-04-10 07:38:001306 EXPECT_EQ(network::mojom::WebSandboxFlags::kAll,
Ian Clelland5cbaaf82017-11-27 22:00:031307 root->child_at(1)->active_sandbox_flags());
1308 EXPECT_EQ(root->child_at(1)->active_sandbox_flags(),
1309 root->child_at(1)->current_frame_host()->active_sandbox_flags());
1310
1311 // Navigating the main frame to a different URL should clear sandbox flags.
1312 GURL unsandboxed_url(embedded_test_server()->GetURL("/title1.html"));
Lukasz Anforowicz69c25dfd2020-11-12 21:50:201313 EXPECT_TRUE(NavigateToURLFromRenderer(root, unsandboxed_url));
Ian Clelland5cbaaf82017-11-27 22:00:031314
1315 // Verify that sandbox flags are cleared properly for the root FrameTreeNode
1316 // and RenderFrameHost.
arthursonzognib93a4472020-04-10 07:38:001317 EXPECT_EQ(network::mojom::WebSandboxFlags::kNone,
Ian Clelland5cbaaf82017-11-27 22:00:031318 root->effective_frame_policy().sandbox_flags);
arthursonzognib93a4472020-04-10 07:38:001319 EXPECT_EQ(network::mojom::WebSandboxFlags::kNone,
1320 root->active_sandbox_flags());
1321 EXPECT_EQ(network::mojom::WebSandboxFlags::kNone,
Ian Clelland5cbaaf82017-11-27 22:00:031322 root->current_frame_host()->active_sandbox_flags());
1323}
1324
alexmosf832a2f2015-01-27 22:44:031325// Ensure that sandbox flags are correctly set when child frames are created.
1326IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, SandboxFlagsSetForChildFrames) {
1327 GURL main_url(embedded_test_server()->GetURL("/sandboxed_frames.html"));
1328 EXPECT_TRUE(NavigateToURL(shell(), main_url));
1329
1330 // It is safe to obtain the root frame tree node here, as it doesn't change.
1331 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:551332 ->GetPrimaryFrameTree()
1333 .root();
alexmosf832a2f2015-01-27 22:44:031334
1335 // Verify that sandbox flags are set properly for all FrameTreeNodes.
1336 // First frame is completely sandboxed; second frame uses "allow-scripts",
1337 // which resets both SandboxFlags::Scripts and
1338 // SandboxFlags::AutomaticFeatures bits per blink::parseSandboxPolicy(), and
1339 // third frame has "allow-scripts allow-same-origin".
arthursonzognib93a4472020-04-10 07:38:001340 EXPECT_EQ(network::mojom::WebSandboxFlags::kNone,
Ian Clellandcdc4f312017-10-13 22:24:121341 root->effective_frame_policy().sandbox_flags);
arthursonzognib93a4472020-04-10 07:38:001342 EXPECT_EQ(network::mojom::WebSandboxFlags::kAll,
Ian Clellandcdc4f312017-10-13 22:24:121343 root->child_at(0)->effective_frame_policy().sandbox_flags);
arthursonzognib93a4472020-04-10 07:38:001344 EXPECT_EQ(network::mojom::WebSandboxFlags::kAll &
1345 ~network::mojom::WebSandboxFlags::kScripts &
1346 ~network::mojom::WebSandboxFlags::kAutomaticFeatures,
Ian Clellandcdc4f312017-10-13 22:24:121347 root->child_at(1)->effective_frame_policy().sandbox_flags);
arthursonzognib93a4472020-04-10 07:38:001348 EXPECT_EQ(network::mojom::WebSandboxFlags::kAll &
1349 ~network::mojom::WebSandboxFlags::kScripts &
1350 ~network::mojom::WebSandboxFlags::kAutomaticFeatures &
1351 ~network::mojom::WebSandboxFlags::kOrigin,
Ian Clellandcdc4f312017-10-13 22:24:121352 root->child_at(2)->effective_frame_policy().sandbox_flags);
alexmosf832a2f2015-01-27 22:44:031353
1354 // Sandboxed frames should set a unique origin unless they have the
1355 // "allow-same-origin" directive.
alexmos6e940102016-01-19 22:47:251356 EXPECT_EQ("null", root->child_at(0)->current_origin().Serialize());
1357 EXPECT_EQ("null", root->child_at(1)->current_origin().Serialize());
Mike West800532c2021-10-14 09:26:521358 EXPECT_EQ(main_url.DeprecatedGetOriginAsURL().spec(),
alexmos6e940102016-01-19 22:47:251359 root->child_at(2)->current_origin().Serialize() + "/");
alexmosf832a2f2015-01-27 22:44:031360
1361 // Navigating to a different URL should not clear sandbox flags.
1362 GURL frame_url(embedded_test_server()->GetURL("/title1.html"));
1363 NavigateFrameToURL(root->child_at(0), frame_url);
arthursonzognib93a4472020-04-10 07:38:001364 EXPECT_EQ(network::mojom::WebSandboxFlags::kAll,
Ian Clellandcdc4f312017-10-13 22:24:121365 root->child_at(0)->effective_frame_policy().sandbox_flags);
alexmosf832a2f2015-01-27 22:44:031366}
1367
Ian Clelland5cbaaf82017-11-27 22:00:031368// Ensure that sandbox flags are correctly set in the child frames when set by
1369// Content-Security-Policy header, and in combination with the sandbox iframe
1370// attribute.
1371IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest,
1372 SandboxFlagsSetByCSPForChildFrames) {
1373 GURL main_url(embedded_test_server()->GetURL("/sandboxed_frames_csp.html"));
1374 EXPECT_TRUE(NavigateToURL(shell(), main_url));
1375
1376 // It is safe to obtain the root frame tree node here, as it doesn't change.
1377 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:551378 ->GetPrimaryFrameTree()
1379 .root();
Ian Clelland5cbaaf82017-11-27 22:00:031380
1381 // Verify that sandbox flags are set properly for all FrameTreeNodes.
1382 // First frame has no iframe sandbox flags, but the framed document is served
1383 // with a CSP header which sets "allow-scripts", "allow-popups" and
1384 // "allow-pointer-lock".
1385 // Second frame is sandboxed with "allow-scripts", "allow-pointer-lock" and
1386 // "allow-orientation-lock", and the framed document is also served with a CSP
1387 // header which uses "allow-popups" and "allow-pointer-lock". The resulting
1388 // sandbox for the frame should only have "allow-pointer-lock".
arthursonzognib93a4472020-04-10 07:38:001389 EXPECT_EQ(network::mojom::WebSandboxFlags::kNone,
Ian Clelland5cbaaf82017-11-27 22:00:031390 root->effective_frame_policy().sandbox_flags);
arthursonzognib93a4472020-04-10 07:38:001391 EXPECT_EQ(network::mojom::WebSandboxFlags::kNone,
1392 root->active_sandbox_flags());
Ian Clelland5cbaaf82017-11-27 22:00:031393 EXPECT_EQ(root->active_sandbox_flags(),
1394 root->current_frame_host()->active_sandbox_flags());
arthursonzognib93a4472020-04-10 07:38:001395 EXPECT_EQ(network::mojom::WebSandboxFlags::kNone,
Ian Clelland5cbaaf82017-11-27 22:00:031396 root->child_at(0)->effective_frame_policy().sandbox_flags);
Arthur Sonzogni4ba3104f2022-03-09 09:04:391397 EXPECT_EQ(
1398 network::mojom::WebSandboxFlags::kAll &
1399 ~network::mojom::WebSandboxFlags::kAutomaticFeatures &
1400 ~network::mojom::WebSandboxFlags::kPointerLock &
1401 ~network::mojom::WebSandboxFlags::kPopups &
1402 ~network::mojom::WebSandboxFlags::kScripts &
1403 ~network::mojom::WebSandboxFlags::kTopNavigationToCustomProtocols,
1404 root->child_at(0)->active_sandbox_flags());
Ian Clelland5cbaaf82017-11-27 22:00:031405 EXPECT_EQ(root->child_at(0)->active_sandbox_flags(),
1406 root->child_at(0)->current_frame_host()->active_sandbox_flags());
arthursonzognib93a4472020-04-10 07:38:001407 EXPECT_EQ(network::mojom::WebSandboxFlags::kAll &
1408 ~network::mojom::WebSandboxFlags::kScripts &
1409 ~network::mojom::WebSandboxFlags::kAutomaticFeatures &
1410 ~network::mojom::WebSandboxFlags::kPointerLock &
1411 ~network::mojom::WebSandboxFlags::kOrientationLock,
Ian Clelland5cbaaf82017-11-27 22:00:031412 root->child_at(1)->effective_frame_policy().sandbox_flags);
arthursonzognib93a4472020-04-10 07:38:001413 EXPECT_EQ(network::mojom::WebSandboxFlags::kAll &
1414 ~network::mojom::WebSandboxFlags::kScripts &
1415 ~network::mojom::WebSandboxFlags::kAutomaticFeatures &
1416 ~network::mojom::WebSandboxFlags::kPointerLock,
Ian Clelland5cbaaf82017-11-27 22:00:031417 root->child_at(1)->active_sandbox_flags());
1418 EXPECT_EQ(root->child_at(1)->active_sandbox_flags(),
1419 root->child_at(1)->current_frame_host()->active_sandbox_flags());
1420
1421 // Navigating to a different URL *should* clear CSP-set sandbox flags, but
1422 // should retain those flags set by the frame owner.
1423 GURL frame_url(embedded_test_server()->GetURL("/title1.html"));
1424
Lukasz Anforowicz69c25dfd2020-11-12 21:50:201425 EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(0), frame_url));
arthursonzognib93a4472020-04-10 07:38:001426 EXPECT_EQ(network::mojom::WebSandboxFlags::kNone,
Ian Clelland5cbaaf82017-11-27 22:00:031427 root->child_at(0)->effective_frame_policy().sandbox_flags);
arthursonzognib93a4472020-04-10 07:38:001428 EXPECT_EQ(network::mojom::WebSandboxFlags::kNone,
Ian Clelland5cbaaf82017-11-27 22:00:031429 root->child_at(0)->active_sandbox_flags());
1430 EXPECT_EQ(root->child_at(0)->active_sandbox_flags(),
1431 root->child_at(0)->current_frame_host()->active_sandbox_flags());
1432
Lukasz Anforowicz69c25dfd2020-11-12 21:50:201433 EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(1), frame_url));
arthursonzognib93a4472020-04-10 07:38:001434 EXPECT_EQ(network::mojom::WebSandboxFlags::kAll &
1435 ~network::mojom::WebSandboxFlags::kScripts &
1436 ~network::mojom::WebSandboxFlags::kAutomaticFeatures &
1437 ~network::mojom::WebSandboxFlags::kPointerLock &
1438 ~network::mojom::WebSandboxFlags::kOrientationLock,
Ian Clelland5cbaaf82017-11-27 22:00:031439 root->child_at(1)->effective_frame_policy().sandbox_flags);
arthursonzognib93a4472020-04-10 07:38:001440 EXPECT_EQ(network::mojom::WebSandboxFlags::kAll &
1441 ~network::mojom::WebSandboxFlags::kScripts &
1442 ~network::mojom::WebSandboxFlags::kAutomaticFeatures &
1443 ~network::mojom::WebSandboxFlags::kPointerLock &
1444 ~network::mojom::WebSandboxFlags::kOrientationLock,
Ian Clelland5cbaaf82017-11-27 22:00:031445 root->child_at(1)->active_sandbox_flags());
1446 EXPECT_EQ(root->child_at(1)->active_sandbox_flags(),
1447 root->child_at(1)->current_frame_host()->active_sandbox_flags());
1448}
1449
alexmose201c7cd2015-06-10 17:14:211450// Ensure that a popup opened from a subframe sets its opener to the subframe's
1451// FrameTreeNode, and that the opener is cleared if the subframe is destroyed.
1452IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, SubframeOpenerSetForNewWindow) {
1453 GURL main_url(embedded_test_server()->GetURL("/frame_tree/top.html"));
1454 EXPECT_TRUE(NavigateToURL(shell(), main_url));
1455
1456 // It is safe to obtain the root frame tree node here, as it doesn't change.
1457 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:551458 ->GetPrimaryFrameTree()
1459 .root();
alexmose201c7cd2015-06-10 17:14:211460
1461 // Open a new window from a subframe.
1462 ShellAddedObserver new_shell_observer;
1463 GURL popup_url(embedded_test_server()->GetURL("foo.com", "/title1.html"));
Nick Carterb7e71312018-08-03 23:36:131464 EXPECT_TRUE(
1465 ExecJs(root->child_at(0), JsReplace("window.open($1);", popup_url)));
alexmose201c7cd2015-06-10 17:14:211466 Shell* new_shell = new_shell_observer.GetShell();
1467 WebContents* new_contents = new_shell->web_contents();
Fergal Dalyf0522332020-07-18 06:09:461468 EXPECT_TRUE(WaitForLoadStop(new_contents));
alexmose201c7cd2015-06-10 17:14:211469
1470 // Check that the new window's opener points to the correct subframe on
1471 // original window.
1472 FrameTreeNode* popup_root =
Carlos Caballero15caeeb2021-10-27 09:57:551473 static_cast<WebContentsImpl*>(new_contents)->GetPrimaryFrameTree().root();
alexmose201c7cd2015-06-10 17:14:211474 EXPECT_EQ(root->child_at(0), popup_root->opener());
1475
1476 // Close the original window. This should clear the new window's opener.
1477 shell()->Close();
1478 EXPECT_EQ(nullptr, popup_root->opener());
1479}
1480
Shivani Sharmac4f561582018-11-15 15:58:391481// Tests that the user activation bits get cleared when a same-site document is
1482// installed in the frame.
1483IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest,
1484 ClearUserActivationForNewDocument) {
1485 GURL main_url(embedded_test_server()->GetURL("/frame_tree/top.html"));
1486 EXPECT_TRUE(NavigateToURL(shell(), main_url));
1487
1488 // It is safe to obtain the root frame tree node here, as it doesn't change.
1489 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:551490 ->GetPrimaryFrameTree()
1491 .root();
Shivani Sharmac4f561582018-11-15 15:58:391492
Mustaq Ahmed4baa9a6e82019-12-20 23:43:461493 EXPECT_FALSE(root->HasStickyUserActivation());
Shivani Sharmac4f561582018-11-15 15:58:391494 EXPECT_FALSE(root->HasTransientUserActivation());
1495
1496 // Set the user activation bits.
1497 root->UpdateUserActivationState(
Mustaq Ahmeddc195e5b2020-08-04 18:45:111498 blink::mojom::UserActivationUpdateType::kNotifyActivation,
1499 blink::mojom::UserActivationNotificationType::kTest);
Mustaq Ahmed4baa9a6e82019-12-20 23:43:461500 EXPECT_TRUE(root->HasStickyUserActivation());
Shivani Sharmac4f561582018-11-15 15:58:391501 EXPECT_TRUE(root->HasTransientUserActivation());
1502
1503 // Install a new same-site document to check the clearing of user activation
1504 // bits.
1505 GURL url(embedded_test_server()->GetURL("/title1.html"));
1506 EXPECT_TRUE(NavigateToURL(shell(), url));
1507
Mustaq Ahmed4baa9a6e82019-12-20 23:43:461508 EXPECT_FALSE(root->HasStickyUserActivation());
Shivani Sharmac4f561582018-11-15 15:58:391509 EXPECT_FALSE(root->HasTransientUserActivation());
1510}
1511
[email protected]82307f6b2014-08-07 03:30:121512class CrossProcessFrameTreeBrowserTest : public ContentBrowserTest {
1513 public:
Fergal Dalya1d569972021-03-16 03:24:531514 CrossProcessFrameTreeBrowserTest() = default;
[email protected]82307f6b2014-08-07 03:30:121515
Peter Boström9b036532021-10-28 23:37:281516 CrossProcessFrameTreeBrowserTest(const CrossProcessFrameTreeBrowserTest&) =
1517 delete;
1518 CrossProcessFrameTreeBrowserTest& operator=(
1519 const CrossProcessFrameTreeBrowserTest&) = delete;
1520
dchengc2282aa2014-10-21 12:07:581521 void SetUpCommandLine(base::CommandLine* command_line) override {
nickd30fd962015-07-27 21:51:081522 IsolateAllSitesForTesting(command_line);
[email protected]82307f6b2014-08-07 03:30:121523 }
1524
alexmos478dcbb2014-12-10 21:24:461525 void SetUpOnMainThread() override {
1526 host_resolver()->AddRule("*", "127.0.0.1");
alexmos478dcbb2014-12-10 21:24:461527 SetupCrossSiteRedirector(embedded_test_server());
martijn12d2dad2016-11-11 10:59:271528 ASSERT_TRUE(embedded_test_server()->Start());
alexmos478dcbb2014-12-10 21:24:461529 }
[email protected]82307f6b2014-08-07 03:30:121530};
1531
1532// Ensure that we can complete a cross-process subframe navigation.
[email protected]82307f6b2014-08-07 03:30:121533IN_PROC_BROWSER_TEST_F(CrossProcessFrameTreeBrowserTest,
nasko83fe5dc2015-07-01 16:34:191534 CreateCrossProcessSubframeProxies) {
alexmos478dcbb2014-12-10 21:24:461535 GURL main_url(embedded_test_server()->GetURL("/site_per_process_main.html"));
Alex Moshchukaa95adf52019-08-13 00:02:021536 EXPECT_TRUE(NavigateToURL(shell(), main_url));
[email protected]82307f6b2014-08-07 03:30:121537
1538 // It is safe to obtain the root frame tree node here, as it doesn't change.
1539 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:551540 ->GetPrimaryFrameTree()
1541 .root();
[email protected]82307f6b2014-08-07 03:30:121542
[email protected]14522ce42014-08-08 18:56:161543 // There should not be a proxy for the root's own SiteInstance.
Sharon Yang7424bda2021-11-04 20:27:431544 SiteInstanceImpl* root_instance =
1545 root->current_frame_host()->GetSiteInstance();
Harkiran Bolariad22a1dca2022-02-22 17:01:121546 EXPECT_FALSE(root->current_frame_host()
1547 ->browsing_context_state()
1548 ->GetRenderFrameProxyHost(root_instance->group()));
[email protected]14522ce42014-08-08 18:56:161549
[email protected]82307f6b2014-08-07 03:30:121550 // Load same-site page into iframe.
alexmos478dcbb2014-12-10 21:24:461551 GURL http_url(embedded_test_server()->GetURL("/title1.html"));
Lukasz Anforowicz69c25dfd2020-11-12 21:50:201552 EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(0), http_url));
[email protected]82307f6b2014-08-07 03:30:121553
[email protected]82307f6b2014-08-07 03:30:121554 // Load cross-site page into iframe.
alexmos478dcbb2014-12-10 21:24:461555 GURL cross_site_url(
1556 embedded_test_server()->GetURL("foo.com", "/title2.html"));
Lukasz Anforowicz69c25dfd2020-11-12 21:50:201557 EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(0), cross_site_url));
[email protected]82307f6b2014-08-07 03:30:121558
1559 // Ensure that we have created a new process for the subframe.
naskoe6edde32014-10-17 15:36:481560 ASSERT_EQ(2U, root->child_count());
[email protected]82307f6b2014-08-07 03:30:121561 FrameTreeNode* child = root->child_at(0);
Sharon Yang7424bda2021-11-04 20:27:431562 SiteInstanceImpl* child_instance =
1563 child->current_frame_host()->GetSiteInstance();
[email protected]82307f6b2014-08-07 03:30:121564 RenderViewHost* rvh = child->current_frame_host()->render_view_host();
1565 RenderProcessHost* rph = child->current_frame_host()->GetProcess();
1566
Dave Tapuska327c06c92022-06-13 20:31:511567 EXPECT_NE(shell()->web_contents()->GetPrimaryMainFrame()->GetRenderViewHost(),
1568 rvh);
[email protected]14522ce42014-08-08 18:56:161569 EXPECT_NE(shell()->web_contents()->GetSiteInstance(), child_instance);
Dave Tapuska327c06c92022-06-13 20:31:511570 EXPECT_NE(shell()->web_contents()->GetPrimaryMainFrame()->GetProcess(), rph);
[email protected]82307f6b2014-08-07 03:30:121571
1572 // Ensure that the root node has a proxy for the child node's SiteInstance.
Harkiran Bolariad22a1dca2022-02-22 17:01:121573 EXPECT_TRUE(root->current_frame_host()
1574 ->browsing_context_state()
1575 ->GetRenderFrameProxyHost(child_instance->group()));
[email protected]82307f6b2014-08-07 03:30:121576
1577 // Also ensure that the child has a proxy for the root node's SiteInstance.
Harkiran Bolariad22a1dca2022-02-22 17:01:121578 EXPECT_TRUE(child->current_frame_host()
1579 ->browsing_context_state()
1580 ->GetRenderFrameProxyHost(root_instance->group()));
[email protected]14522ce42014-08-08 18:56:161581
1582 // The nodes should not have proxies for their own SiteInstance.
Harkiran Bolariad22a1dca2022-02-22 17:01:121583 EXPECT_FALSE(root->current_frame_host()
1584 ->browsing_context_state()
1585 ->GetRenderFrameProxyHost(root_instance->group()));
1586 EXPECT_FALSE(child->current_frame_host()
1587 ->browsing_context_state()
1588 ->GetRenderFrameProxyHost(child_instance->group()));
creise42f2a52014-09-18 18:14:571589
1590 // Ensure that the RenderViews and RenderFrames are all live.
1591 EXPECT_TRUE(
1592 root->current_frame_host()->render_view_host()->IsRenderViewLive());
1593 EXPECT_TRUE(
1594 child->current_frame_host()->render_view_host()->IsRenderViewLive());
1595 EXPECT_TRUE(root->current_frame_host()->IsRenderFrameLive());
1596 EXPECT_TRUE(root->child_at(0)->current_frame_host()->IsRenderFrameLive());
[email protected]82307f6b2014-08-07 03:30:121597}
1598
alexmosbc7eafa2014-12-06 01:38:091599IN_PROC_BROWSER_TEST_F(CrossProcessFrameTreeBrowserTest,
Nasko Oskov03912102019-01-11 00:21:321600 OriginSetOnNavigations) {
alexmos478dcbb2014-12-10 21:24:461601 GURL main_url(embedded_test_server()->GetURL("/site_per_process_main.html"));
alexmosbc7eafa2014-12-06 01:38:091602 EXPECT_TRUE(NavigateToURL(shell(), main_url));
1603
1604 // It is safe to obtain the root frame tree node here, as it doesn't change.
1605 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:551606 ->GetPrimaryFrameTree()
1607 .root();
alexmosbc7eafa2014-12-06 01:38:091608
creisabdd2bd62015-11-21 01:14:581609 EXPECT_EQ(root->current_origin().Serialize() + '/',
Mike West800532c2021-10-14 09:26:521610 main_url.DeprecatedGetOriginAsURL().spec());
alexmosbc7eafa2014-12-06 01:38:091611
1612 // First frame is an about:blank frame. Check that its origin is correctly
1613 // inherited from the parent.
creisabdd2bd62015-11-21 01:14:581614 EXPECT_EQ(root->child_at(0)->current_origin().Serialize() + '/',
Mike West800532c2021-10-14 09:26:521615 main_url.DeprecatedGetOriginAsURL().spec());
alexmosbc7eafa2014-12-06 01:38:091616
1617 // Second frame loads a same-site page. Its origin should also be the same
1618 // as the parent.
creisabdd2bd62015-11-21 01:14:581619 EXPECT_EQ(root->child_at(1)->current_origin().Serialize() + '/',
Mike West800532c2021-10-14 09:26:521620 main_url.DeprecatedGetOriginAsURL().spec());
alexmosbc7eafa2014-12-06 01:38:091621
alexmosbc7eafa2014-12-06 01:38:091622 // Load cross-site page into the first frame.
alexmos478dcbb2014-12-10 21:24:461623 GURL cross_site_url(
1624 embedded_test_server()->GetURL("foo.com", "/title2.html"));
Lukasz Anforowicz69c25dfd2020-11-12 21:50:201625 EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(0), cross_site_url));
alexmosbc7eafa2014-12-06 01:38:091626
creisabdd2bd62015-11-21 01:14:581627 EXPECT_EQ(root->child_at(0)->current_origin().Serialize() + '/',
Mike West800532c2021-10-14 09:26:521628 cross_site_url.DeprecatedGetOriginAsURL().spec());
alexmosbc7eafa2014-12-06 01:38:091629
1630 // The root's origin shouldn't have changed.
creisabdd2bd62015-11-21 01:14:581631 EXPECT_EQ(root->current_origin().Serialize() + '/',
Mike West800532c2021-10-14 09:26:521632 main_url.DeprecatedGetOriginAsURL().spec());
alexmosbc7eafa2014-12-06 01:38:091633
Nasko Oskov03912102019-01-11 00:21:321634 {
1635 GURL data_url("data:text/html,foo");
1636 TestNavigationObserver observer(shell()->web_contents());
1637 EXPECT_TRUE(
1638 ExecJs(root->child_at(1), JsReplace("window.location = $1", data_url)));
1639 observer.Wait();
1640 }
alexmosbc7eafa2014-12-06 01:38:091641
1642 // Navigating to a data URL should set a unique origin. This is represented
Nasko Oskov03912102019-01-11 00:21:321643 // as "null" per RFC 6454. A frame navigating itself to a data: URL does not
1644 // require a process transfer, but should retain the original origin
1645 // as its precursor.
creisabdd2bd62015-11-21 01:14:581646 EXPECT_EQ(root->child_at(1)->current_origin().Serialize(), "null");
Nasko Oskov03912102019-01-11 00:21:321647 EXPECT_TRUE(root->child_at(1)->current_origin().opaque());
1648 ASSERT_EQ(
1649 url::SchemeHostPort(main_url),
1650 root->child_at(1)->current_origin().GetTupleOrPrecursorTupleIfOpaque())
1651 << "Expected the precursor origin to be preserved; should be the "
1652 "initiator of a data: navigation.";
1653
1654 // Adding an <iframe sandbox srcdoc=> frame should result in a unique origin
1655 // that is different-origin from its data: URL parent.
1656 {
1657 TestNavigationObserver observer(shell()->web_contents());
1658
1659 ASSERT_EQ(0U, root->child_at(1)->child_count());
1660 EXPECT_TRUE(
1661 ExecJs(root->child_at(1), JsReplace(
1662 R"(
1663 var iframe = document.createElement('iframe');
1664 iframe.setAttribute('sandbox', 'allow-scripts');
1665 iframe.srcdoc = $1;
1666 document.body.appendChild(iframe);
1667 )",
1668 "<html><body>This sandboxed doc should "
1669 "be different-origin.</body></html>")));
1670 observer.Wait();
1671 ASSERT_EQ(1U, root->child_at(1)->child_count());
1672 }
1673
1674 url::Origin root_origin = root->current_origin();
1675 url::Origin child_1 = root->child_at(1)->current_origin();
1676 url::Origin child_1_0 = root->child_at(1)->child_at(0)->current_origin();
1677 EXPECT_FALSE(root_origin.opaque());
1678 EXPECT_TRUE(child_1.opaque());
1679 EXPECT_TRUE(child_1_0.opaque());
1680 EXPECT_NE(child_1, child_1_0);
1681 EXPECT_EQ(url::SchemeHostPort(main_url),
1682 root_origin.GetTupleOrPrecursorTupleIfOpaque());
1683 EXPECT_EQ(url::SchemeHostPort(main_url),
1684 child_1.GetTupleOrPrecursorTupleIfOpaque());
1685 EXPECT_EQ(url::SchemeHostPort(main_url),
1686 child_1_0.GetTupleOrPrecursorTupleIfOpaque());
1687
1688 {
1689 TestNavigationObserver observer(shell()->web_contents());
1690
1691 ASSERT_EQ(1U, root->child_at(1)->child_count());
1692 EXPECT_TRUE(
1693 ExecJs(root->child_at(1), JsReplace(
1694 R"(
1695 var iframe = document.createElement('iframe');
1696 iframe.srcdoc = $1;
1697 document.body.appendChild(iframe);
1698 )",
1699 "<html><body>This srcdoc document should "
1700 "be same-origin.</body></html>")));
1701 observer.Wait();
1702 ASSERT_EQ(2U, root->child_at(1)->child_count());
1703 }
1704 EXPECT_EQ(root_origin, root->current_origin());
1705 EXPECT_EQ(child_1, root->child_at(1)->current_origin());
1706 EXPECT_EQ(child_1_0, root->child_at(1)->child_at(0)->current_origin());
1707 url::Origin child_1_1 = root->child_at(1)->child_at(1)->current_origin();
1708 EXPECT_EQ(child_1, child_1_1);
1709 EXPECT_NE(child_1_0, child_1_1);
1710
1711 {
1712 TestNavigationObserver observer(shell()->web_contents());
1713
1714 ASSERT_EQ(2U, root->child_at(1)->child_count());
1715 EXPECT_TRUE(
1716 ExecJs(root->child_at(1), JsReplace(
1717 R"(
1718 var iframe = document.createElement('iframe');
1719 iframe.src = 'data:text/html;base64,' + btoa($1);
1720 document.body.appendChild(iframe);
1721 )",
1722 "<html><body>This data: doc should be "
1723 "different-origin.</body></html>")));
1724 observer.Wait();
1725 ASSERT_EQ(3U, root->child_at(1)->child_count());
1726 }
1727 EXPECT_EQ(root_origin, root->current_origin());
1728 EXPECT_EQ(child_1, root->child_at(1)->current_origin());
1729 EXPECT_EQ(child_1_0, root->child_at(1)->child_at(0)->current_origin());
1730 EXPECT_EQ(child_1_1, root->child_at(1)->child_at(1)->current_origin());
1731 url::Origin child_1_2 = root->child_at(1)->child_at(2)->current_origin();
1732 EXPECT_NE(child_1, child_1_2);
1733 EXPECT_NE(child_1_0, child_1_2);
1734 EXPECT_NE(child_1_1, child_1_2);
1735 EXPECT_EQ(url::SchemeHostPort(main_url),
1736 child_1_2.GetTupleOrPrecursorTupleIfOpaque());
1737
1738 // If the parent navigates its child to a data URL, it should transfer
1739 // to the parent's process, and the precursor origin should track the
1740 // parent's origin.
1741 {
1742 GURL data_url("data:text/html,foo2");
1743 TestNavigationObserver observer(shell()->web_contents());
1744 EXPECT_TRUE(ExecJs(root, JsReplace("frames[0].location = $1", data_url)));
1745 observer.Wait();
1746 EXPECT_EQ(data_url, root->child_at(0)->current_url());
1747 }
1748
1749 EXPECT_EQ(root->child_at(0)->current_origin().Serialize(), "null");
1750 EXPECT_TRUE(root->child_at(0)->current_origin().opaque());
1751 EXPECT_EQ(
1752 url::SchemeHostPort(main_url),
1753 root->child_at(0)->current_origin().GetTupleOrPrecursorTupleIfOpaque());
1754 EXPECT_EQ(root->current_frame_host()->GetProcess(),
1755 root->child_at(0)->current_frame_host()->GetProcess());
1756}
1757
1758// Test to verify that a blob: URL that is created by a unique opaque origin
1759// will correctly set the origin_to_commit on a session history navigation.
1760IN_PROC_BROWSER_TEST_F(CrossProcessFrameTreeBrowserTest,
1761 OriginForBlobUrlsFromUniqueOpaqueOrigin) {
1762 // Start off with a navigation to data: URL in the main frame. It should
1763 // result in a unique opaque origin without any precursor information.
1764 GURL data_url("data:text/html,foo<iframe id='child' src='" +
1765 embedded_test_server()->GetURL("/title1.html").spec() +
1766 "'></iframe>");
1767 EXPECT_TRUE(NavigateToURL(shell(), data_url));
1768
1769 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:551770 ->GetPrimaryFrameTree()
1771 .root();
Nasko Oskov03912102019-01-11 00:21:321772 EXPECT_TRUE(root->current_origin().opaque());
Nasko Oskov55119382020-01-17 18:22:181773 EXPECT_FALSE(
1774 root->current_origin().GetTupleOrPrecursorTupleIfOpaque().IsValid());
Nasko Oskov03912102019-01-11 00:21:321775 EXPECT_EQ(1UL, root->child_count());
1776 FrameTreeNode* child = root->child_at(0);
1777
1778 // Create a blob: URL and navigate the child frame to it.
1779 std::string html = "<html><body>This is blob content.</body></html>";
1780 std::string script = JsReplace(
1781 "var blob = new Blob([$1], {type: 'text/html'});"
1782 "var blob_url = URL.createObjectURL(blob);"
1783 "document.getElementById('child').src = blob_url;"
1784 "blob_url;",
1785 html);
1786 GURL blob_url;
1787 {
1788 TestFrameNavigationObserver observer(child);
1789 blob_url = GURL(EvalJs(root, script).ExtractString());
1790 observer.Wait();
1791 EXPECT_EQ(blob_url, child->current_frame_host()->GetLastCommittedURL());
1792 }
1793
1794 // We expect the frame to have committed in an opaque origin which contains
1795 // the same precursor information - none.
1796 url::Origin blob_origin = child->current_origin();
1797 EXPECT_TRUE(blob_origin.opaque());
1798 EXPECT_EQ(root->current_origin().GetTupleOrPrecursorTupleIfOpaque(),
1799 blob_origin.GetTupleOrPrecursorTupleIfOpaque());
Rakina Zata Amnic7367852022-11-07 17:10:401800 EXPECT_FALSE(
1801 child->current_origin().GetTupleOrPrecursorTupleIfOpaque().IsValid());
Nasko Oskov03912102019-01-11 00:21:321802
1803 // Navigate the frame away to any web URL.
1804 {
1805 GURL url(embedded_test_server()->GetURL("/title2.html"));
1806 TestFrameNavigationObserver observer(child);
1807 EXPECT_TRUE(ExecJs(child, JsReplace("window.location = $1", url)));
1808 observer.Wait();
1809 EXPECT_EQ(url, child->current_frame_host()->GetLastCommittedURL());
1810 }
1811 EXPECT_FALSE(child->current_origin().opaque());
1812 EXPECT_TRUE(shell()->web_contents()->GetController().CanGoBack());
1813 EXPECT_EQ(3, shell()->web_contents()->GetController().GetEntryCount());
1814 EXPECT_EQ(
1815 2, shell()->web_contents()->GetController().GetLastCommittedEntryIndex());
1816
1817 // Verify the blob URL still exists in the main frame, which keeps it alive
1818 // allowing a session history navigation back to succeed.
1819 EXPECT_EQ(blob_url, GURL(EvalJs(root, "blob_url;").ExtractString()));
1820
1821 // Now navigate back in session history. It should successfully go back to
Rakina Zata Amnic7367852022-11-07 17:10:401822 // the blob: URL. The child frame won't be reusing the exact same origin it
1823 // used before, but it will commit a new opaque origin which will still have
1824 // no precursor information.
Nasko Oskov03912102019-01-11 00:21:321825 {
1826 TestFrameNavigationObserver observer(child);
1827 shell()->web_contents()->GetController().GoBack();
1828 observer.Wait();
1829 }
1830 EXPECT_EQ(blob_url, child->current_frame_host()->GetLastCommittedURL());
1831 EXPECT_TRUE(child->current_origin().opaque());
Rakina Zata Amnic7367852022-11-07 17:10:401832 EXPECT_NE(blob_origin, child->current_origin());
Nasko Oskov03912102019-01-11 00:21:321833 EXPECT_EQ(root->current_origin().GetTupleOrPrecursorTupleIfOpaque(),
1834 child->current_origin().GetTupleOrPrecursorTupleIfOpaque());
Rakina Zata Amnic7367852022-11-07 17:10:401835 EXPECT_FALSE(
1836 child->current_origin().GetTupleOrPrecursorTupleIfOpaque().IsValid());
Nasko Oskov03912102019-01-11 00:21:321837}
1838
1839// Test to verify that about:blank iframe, which is a child of a sandboxed
1840// iframe is not considered same origin, but precursor information is preserved
1841// in its origin.
1842IN_PROC_BROWSER_TEST_F(CrossProcessFrameTreeBrowserTest,
1843 AboutBlankSubframeInSandboxedFrame) {
1844 // Start off by navigating to a page with sandboxed iframe, which allows
1845 // script execution.
1846 GURL main_url(
1847 embedded_test_server()->GetURL("/sandboxed_main_frame_script.html"));
1848 EXPECT_TRUE(NavigateToURL(shell(), main_url));
1849
1850 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:551851 ->GetPrimaryFrameTree()
1852 .root();
Nasko Oskov03912102019-01-11 00:21:321853 EXPECT_EQ(1UL, root->child_count());
1854 FrameTreeNode* child = root->child_at(0);
1855
1856 // Navigate the frame to data: URL to cause it to have an opaque origin that
1857 // is derived from the |main_url| origin.
1858 GURL data_url("data:text/html,<html><body>foo</body></html>");
1859 {
1860 TestFrameNavigationObserver observer(child);
1861 EXPECT_TRUE(ExecJs(root, JsReplace("frames[0].location = $1", data_url)));
1862 observer.Wait();
1863 EXPECT_EQ(data_url, child->current_frame_host()->GetLastCommittedURL());
1864 }
1865
1866 // Add an about:blank iframe to the data: frame, which should not inherit the
1867 // origin, but should preserve the precursor information.
1868 {
1869 EXPECT_TRUE(ExecJs(child,
1870 "var f = document.createElement('iframe');"
1871 "document.body.appendChild(f);"));
1872 }
1873 EXPECT_EQ(1UL, child->child_count());
1874 FrameTreeNode* grandchild = child->child_at(0);
1875
1876 EXPECT_TRUE(grandchild->current_origin().opaque());
1877 EXPECT_EQ(GURL(url::kAboutBlankURL),
1878 grandchild->current_frame_host()->GetLastCommittedURL());
1879
1880 // The origin of the data: document should have precursor information matching
1881 // the main frame origin.
1882 EXPECT_EQ(root->current_origin().GetTupleOrPrecursorTupleIfOpaque(),
1883 child->current_origin().GetTupleOrPrecursorTupleIfOpaque());
1884
1885 // The same should hold also for the about:blank subframe of the data: frame.
1886 EXPECT_EQ(root->current_origin().GetTupleOrPrecursorTupleIfOpaque(),
1887 grandchild->current_origin().GetTupleOrPrecursorTupleIfOpaque());
1888
1889 // The about:blank document should not be able to access its parent, as they
1890 // are considered cross origin due to the sandbox flags on the parent.
1891 EXPECT_FALSE(ExecJs(grandchild, "window.parent.foo = 'bar';"));
1892 EXPECT_NE(child->current_origin(), grandchild->current_origin());
alexmosbc7eafa2014-12-06 01:38:091893}
1894
Ian Clelland5cbaaf82017-11-27 22:00:031895// Ensure that a popup opened from a sandboxed main frame inherits sandbox flags
1896// from its opener.
1897IN_PROC_BROWSER_TEST_F(CrossProcessFrameTreeBrowserTest,
1898 SandboxFlagsSetForNewWindow) {
1899 GURL main_url(
1900 embedded_test_server()->GetURL("/sandboxed_main_frame_script.html"));
1901 EXPECT_TRUE(NavigateToURL(shell(), main_url));
1902
1903 // It is safe to obtain the root frame tree node here, as it doesn't change.
1904 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:551905 ->GetPrimaryFrameTree()
1906 .root();
Ian Clelland5cbaaf82017-11-27 22:00:031907
1908 // Open a new window from the main frame.
1909 GURL popup_url(embedded_test_server()->GetURL("foo.com", "/title1.html"));
1910 Shell* new_shell = OpenPopup(root->current_frame_host(), popup_url, "");
1911 EXPECT_TRUE(new_shell);
1912 WebContents* new_contents = new_shell->web_contents();
1913
1914 // Check that the new window's sandbox flags correctly reflect the opener's
1915 // flags. Main frame sets allow-popups, allow-pointer-lock and allow-scripts.
1916 FrameTreeNode* popup_root =
Carlos Caballero15caeeb2021-10-27 09:57:551917 static_cast<WebContentsImpl*>(new_contents)->GetPrimaryFrameTree().root();
arthursonzognib93a4472020-04-10 07:38:001918 network::mojom::WebSandboxFlags main_frame_sandbox_flags =
Ian Clelland5cbaaf82017-11-27 22:00:031919 root->current_frame_host()->active_sandbox_flags();
Arthur Sonzogni4ba3104f2022-03-09 09:04:391920 EXPECT_EQ(
1921 network::mojom::WebSandboxFlags::kAll &
1922 ~network::mojom::WebSandboxFlags::kAutomaticFeatures &
1923 ~network::mojom::WebSandboxFlags::kPointerLock &
1924 ~network::mojom::WebSandboxFlags::kPopups &
1925 ~network::mojom::WebSandboxFlags::kScripts &
1926 ~network::mojom::WebSandboxFlags::kTopNavigationToCustomProtocols,
1927 main_frame_sandbox_flags);
Ian Clelland5cbaaf82017-11-27 22:00:031928
1929 EXPECT_EQ(main_frame_sandbox_flags,
1930 popup_root->effective_frame_policy().sandbox_flags);
1931 EXPECT_EQ(main_frame_sandbox_flags, popup_root->active_sandbox_flags());
1932 EXPECT_EQ(main_frame_sandbox_flags,
1933 popup_root->current_frame_host()->active_sandbox_flags());
1934}
1935
Shivani Sharmac4f561582018-11-15 15:58:391936// Tests that the user activation bits get cleared when a cross-site document is
1937// installed in the frame.
1938IN_PROC_BROWSER_TEST_F(CrossProcessFrameTreeBrowserTest,
1939 ClearUserActivationForNewDocument) {
1940 GURL main_url(embedded_test_server()->GetURL("/frame_tree/top.html"));
1941 EXPECT_TRUE(NavigateToURL(shell(), main_url));
1942
1943 // It is safe to obtain the root frame tree node here, as it doesn't change.
1944 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:551945 ->GetPrimaryFrameTree()
1946 .root();
Shivani Sharmac4f561582018-11-15 15:58:391947
Mustaq Ahmed4baa9a6e82019-12-20 23:43:461948 EXPECT_FALSE(root->HasStickyUserActivation());
Shivani Sharmac4f561582018-11-15 15:58:391949 EXPECT_FALSE(root->HasTransientUserActivation());
1950
1951 // Set the user activation bits.
1952 root->UpdateUserActivationState(
Mustaq Ahmeddc195e5b2020-08-04 18:45:111953 blink::mojom::UserActivationUpdateType::kNotifyActivation,
1954 blink::mojom::UserActivationNotificationType::kTest);
Mustaq Ahmed4baa9a6e82019-12-20 23:43:461955 EXPECT_TRUE(root->HasStickyUserActivation());
Shivani Sharmac4f561582018-11-15 15:58:391956 EXPECT_TRUE(root->HasTransientUserActivation());
1957
1958 // Install a new cross-site document to check the clearing of user activation
1959 // bits.
1960 GURL cross_site_url(
1961 embedded_test_server()->GetURL("foo.com", "/title2.html"));
1962 EXPECT_TRUE(NavigateToURL(shell(), cross_site_url));
1963
Mustaq Ahmed4baa9a6e82019-12-20 23:43:461964 EXPECT_FALSE(root->HasStickyUserActivation());
Shivani Sharmac4f561582018-11-15 15:58:391965 EXPECT_FALSE(root->HasTransientUserActivation());
1966}
1967
Dave Tapuska708c2732022-09-23 22:47:491968class BrowserContextGroupSwapFrameTreeBrowserTest : public ContentBrowserTest {
1969 public:
1970 BrowserContextGroupSwapFrameTreeBrowserTest()
1971 : https_server_(net::EmbeddedTestServer::TYPE_HTTPS) {}
1972
1973 void SetUpCommandLine(base::CommandLine* command_line) override {
1974 IsolateAllSitesForTesting(command_line);
1975 }
1976
1977 void SetUpOnMainThread() override {
1978 ContentBrowserTest::SetUpOnMainThread();
1979 host_resolver()->AddRule("*", "127.0.0.1");
1980 https_server_.ServeFilesFromSourceDirectory(GetTestDataFilePath());
1981 https_server_.SetSSLConfig(net::EmbeddedTestServer::CERT_TEST_NAMES);
1982 net::test_server::RegisterDefaultHandlers(&https_server_);
1983 ASSERT_TRUE(https_server_.Start());
1984 }
1985
1986 net::EmbeddedTestServer* https_server() { return &https_server_; }
1987
1988 public:
1989 net::EmbeddedTestServer https_server_;
1990};
1991
1992// Force a race between when the RenderViewHostImpl's main frame is running
1993// the unload handlers and when a new navigation occurs that tries to
1994// reuse a RenderViewHostImpl.
1995IN_PROC_BROWSER_TEST_F(BrowserContextGroupSwapFrameTreeBrowserTest,
1996 NavigateAndGoBack) {
1997 GURL main_url(https_server()->GetURL("a.test", "/title1.html"));
1998 EXPECT_TRUE(NavigateToURL(shell(), main_url));
1999
2000 auto* web_contents = static_cast<WebContentsImpl*>(shell()->web_contents());
2001 web_contents->GetPrimaryMainFrame()->DoNotDeleteForTesting();
2002 DisableBFCacheForRFHForTesting(
2003 web_contents->GetPrimaryFrameTree().root()->current_frame_host());
2004
2005 // Load a page with COOP set to force the browsing context group swap
2006 // and clears out old proxies.
2007 GURL new_main_url(https_server()->GetURL(
2008 "b.test", "/set-header?Cross-Origin-Opener-Policy: same-origin"));
2009
2010 EXPECT_TRUE(NavigateToURL(shell(), new_main_url));
2011
2012 TestNavigationObserver back_load_observer(web_contents);
2013 web_contents->GetController().GoBack();
2014 back_load_observer.Wait();
2015}
2016
nickdb193a12016-09-09 23:09:232017// FrameTreeBrowserTest variant where we isolate http://*.is, Iceland's top
naskoabed2a52017-05-03 05:10:172018// level domain. This is an analogue to isolating extensions, which we can use
2019// inside content_browsertests, where extensions don't exist. Iceland, like an
nickdb193a12016-09-09 23:09:232020// extension process, is a special place with magical powers; we want to protect
2021// it from outsiders.
2022class IsolateIcelandFrameTreeBrowserTest : public ContentBrowserTest {
2023 public:
Fergal Dalya1d569972021-03-16 03:24:532024 IsolateIcelandFrameTreeBrowserTest() = default;
nickdb193a12016-09-09 23:09:232025
Peter Boström9b036532021-10-28 23:37:282026 IsolateIcelandFrameTreeBrowserTest(
2027 const IsolateIcelandFrameTreeBrowserTest&) = delete;
2028 IsolateIcelandFrameTreeBrowserTest& operator=(
2029 const IsolateIcelandFrameTreeBrowserTest&) = delete;
2030
nickdb193a12016-09-09 23:09:232031 void SetUpCommandLine(base::CommandLine* command_line) override {
Lukasz Anforowicze7c87d12018-11-03 02:53:342032 // Blink suppresses navigations to blob URLs of origins different from the
alexis.menard83f1b6d2017-05-17 19:37:332033 // frame initiating the navigation. We disable those checks for this test,
2034 // to test what happens in a compromise scenario.
2035 command_line->AppendSwitch(switches::kDisableWebSecurity);
Lukasz Anforowicze7c87d12018-11-03 02:53:342036
2037 // ProcessSwitchForIsolatedBlob test below requires that one of URLs used in
Lukasz Anforowicz25420932018-12-18 20:59:222038 // the test (blob:http://b.is/) belongs to an isolated origin.
2039 command_line->AppendSwitchASCII(switches::kIsolateOrigins, "http://b.is/");
nickdb193a12016-09-09 23:09:232040 }
2041
2042 void SetUpOnMainThread() override {
2043 host_resolver()->AddRule("*", "127.0.0.1");
nickdb193a12016-09-09 23:09:232044 SetupCrossSiteRedirector(embedded_test_server());
martijn12d2dad2016-11-11 10:59:272045 ASSERT_TRUE(embedded_test_server()->Start());
nickdb193a12016-09-09 23:09:232046 }
nickdb193a12016-09-09 23:09:232047};
2048
2049// Regression test for https://crbug.com/644966
2050IN_PROC_BROWSER_TEST_F(IsolateIcelandFrameTreeBrowserTest,
2051 ProcessSwitchForIsolatedBlob) {
nickdb193a12016-09-09 23:09:232052 // Set up an iframe.
2053 WebContents* contents = shell()->web_contents();
2054 FrameTreeNode* root =
Carlos Caballero15caeeb2021-10-27 09:57:552055 static_cast<WebContentsImpl*>(contents)->GetPrimaryFrameTree().root();
nickdb193a12016-09-09 23:09:232056 GURL main_url(embedded_test_server()->GetURL(
nick07fd7e12016-09-12 18:54:062057 "a.com", "/cross_site_iframe_factory.html?a(a)"));
nickdb193a12016-09-09 23:09:232058 EXPECT_TRUE(NavigateToURL(shell(), main_url));
2059
2060 // The navigation targets an invalid blob url; that's intentional to trigger
2061 // an error response. The response should commit in a process dedicated to
Nasko Oskov0401d812021-02-05 22:20:082062 // http://b.is or error pages, depending on policy.
Nick Carterb7e71312018-08-03 23:36:132063 EXPECT_EQ(
2064 "done",
2065 EvalJs(
2066 root,
2067 "new Promise((resolve) => {"
2068 " var iframe_element = document.getElementsByTagName('iframe')[0];"
2069 " iframe_element.onload = () => resolve('done');"
Lukasz Anforowicz25420932018-12-18 20:59:222070 " iframe_element.src = 'blob:http://b.is/';"
Nick Carterb7e71312018-08-03 23:36:132071 "});"));
Fergal Dalyf0522332020-07-18 06:09:462072 EXPECT_TRUE(WaitForLoadStop(contents));
nickdb193a12016-09-09 23:09:232073
2074 // Make sure we did a process transfer back to "b.is".
Aaron Colwelle953e562019-07-24 16:47:362075 const std::string kExpectedSiteURL =
Sharon Yang47453bb2025-04-25 14:48:092076 AreStrictSiteInstancesEnabled()
Sharon Yang02279222025-01-15 19:09:192077 ? "http://a.com/"
2078 : SiteInstanceImpl::GetDefaultSiteURL().spec();
Nasko Oskov0401d812021-02-05 22:20:082079 const std::string kExpectedSubframeSiteURL =
2080 SiteIsolationPolicy::IsErrorPageIsolationEnabled(/*in_main_frame*/ false)
2081 ? "chrome-error://chromewebdata/"
2082 : "http://b.is/";
Aaron Colwelle953e562019-07-24 16:47:362083 EXPECT_EQ(base::StringPrintf(" Site A ------------ proxies for B\n"
2084 " +--Site B ------- proxies for A\n"
2085 "Where A = %s\n"
Nasko Oskov0401d812021-02-05 22:20:082086 " B = %s",
2087 kExpectedSiteURL.c_str(),
2088 kExpectedSubframeSiteURL.c_str()),
Fergal Daly79f44292020-12-01 02:30:482089 DepictFrameTree(*root));
nickdb193a12016-09-09 23:09:232090}
2091
Arthur Sonzogni64457592022-11-22 11:08:592092class FrameTreeCredentiallessIframeBrowserTest : public FrameTreeBrowserTest {
Antonio Sartori5abc8de2021-07-13 08:42:472093 public:
Arthur Sonzogni64457592022-11-22 11:08:592094 FrameTreeCredentiallessIframeBrowserTest() = default;
Antonio Sartori5abc8de2021-07-13 08:42:472095
2096 void SetUpCommandLine(base::CommandLine* command_line) override {
Antonio Sartori753cd6d2021-07-23 08:34:552097 command_line->AppendSwitch(switches::kEnableBlinkTestFeatures);
Antonio Sartori5abc8de2021-07-13 08:42:472098 }
2099};
2100
Arthur Sonzogni64457592022-11-22 11:08:592101// Tests the mojo propagation of the 'credentialless' attribute to the browser.
2102IN_PROC_BROWSER_TEST_F(FrameTreeCredentiallessIframeBrowserTest,
Antonio Sartori5abc8de2021-07-13 08:42:472103 AttributeIsPropagatedToBrowser) {
2104 GURL main_url(embedded_test_server()->GetURL("/hello.html"));
2105 EXPECT_TRUE(NavigateToURL(shell(), main_url));
2106
2107 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:552108 ->GetPrimaryFrameTree()
2109 .root();
Antonio Sartori5abc8de2021-07-13 08:42:472110
Arthur Sonzogni64457592022-11-22 11:08:592111 // Not setting the attribute => the iframe is not credentialless.
Antonio Sartori5abc8de2021-07-13 08:42:472112 EXPECT_TRUE(ExecJs(root,
2113 "var f = document.createElement('iframe');"
2114 "document.body.appendChild(f);"));
2115 EXPECT_EQ(1U, root->child_count());
Miyoung Shinc9ff4812023-01-05 08:58:052116 EXPECT_FALSE(root->child_at(0)->Credentialless());
Yifan Luoe1a2e05d2022-01-12 16:47:592117 EXPECT_EQ(false, EvalJs(root->child_at(0)->current_frame_host(),
Arthur Sonzogni64457592022-11-22 11:08:592118 "window.credentialless"));
Antonio Sartori5abc8de2021-07-13 08:42:472119
Arthur Sonzogni64457592022-11-22 11:08:592120 // Setting the attribute on the iframe element makes the iframe
2121 // credentialless.
Antonio Sartori5abc8de2021-07-13 08:42:472122 EXPECT_TRUE(ExecJs(root,
2123 "var d = document.createElement('div');"
Arthur Sonzogni64457592022-11-22 11:08:592124 "d.innerHTML = '<iframe credentialless></iframe>';"
Antonio Sartori5abc8de2021-07-13 08:42:472125 "document.body.appendChild(d);"));
2126 EXPECT_EQ(2U, root->child_count());
Miyoung Shinc9ff4812023-01-05 08:58:052127 EXPECT_TRUE(root->child_at(1)->Credentialless());
Yifan Luo400867472022-05-19 09:34:202128 EXPECT_EQ(true, EvalJs(root->child_at(1)->current_frame_host(),
Arthur Sonzogni64457592022-11-22 11:08:592129 "window.credentialless"));
Antonio Sartori5abc8de2021-07-13 08:42:472130
2131 // Setting the attribute via javascript works.
2132 EXPECT_TRUE(ExecJs(root,
2133 "var g = document.createElement('iframe');"
Arthur Sonzogni64457592022-11-22 11:08:592134 "g.credentialless = true;"
Antonio Sartori5abc8de2021-07-13 08:42:472135 "document.body.appendChild(g);"));
2136 EXPECT_EQ(3U, root->child_count());
Miyoung Shinc9ff4812023-01-05 08:58:052137 EXPECT_TRUE(root->child_at(2)->Credentialless());
Yifan Luo400867472022-05-19 09:34:202138 EXPECT_EQ(true, EvalJs(root->child_at(2)->current_frame_host(),
Arthur Sonzogni64457592022-11-22 11:08:592139 "window.credentialless"));
Antonio Sartori5abc8de2021-07-13 08:42:472140
Arthur Sonzogni64457592022-11-22 11:08:592141 EXPECT_TRUE(ExecJs(root, "g.credentialless = false;"));
Miyoung Shinc9ff4812023-01-05 08:58:052142 EXPECT_FALSE(root->child_at(2)->Credentialless());
Yifan Luo400867472022-05-19 09:34:202143 EXPECT_EQ(true, EvalJs(root->child_at(2)->current_frame_host(),
Arthur Sonzogni64457592022-11-22 11:08:592144 "window.credentialless"));
Antonio Sartori5abc8de2021-07-13 08:42:472145
Arthur Sonzogni64457592022-11-22 11:08:592146 EXPECT_TRUE(ExecJs(root, "g.credentialless = true;"));
Miyoung Shinc9ff4812023-01-05 08:58:052147 EXPECT_TRUE(root->child_at(2)->Credentialless());
Yifan Luo400867472022-05-19 09:34:202148 EXPECT_EQ(true, EvalJs(root->child_at(2)->current_frame_host(),
Arthur Sonzogni64457592022-11-22 11:08:592149 "window.credentialless"));
Antonio Sartori5abc8de2021-07-13 08:42:472150}
2151
Andrew Verge7233e662025-05-13 13:09:232152class FrameTreeLastSuccessfulOriginBrowserTest : public FrameTreeBrowserTest {
2153 public:
2154 FrameTreeLastSuccessfulOriginBrowserTest() = default;
2155};
2156
2157IN_PROC_BROWSER_TEST_F(FrameTreeLastSuccessfulOriginBrowserTest,
2158 SuccessfulNavigation) {
2159 GURL main_url(embedded_test_server()->GetURL("a.test", "/hello.html"));
2160 EXPECT_TRUE(NavigateToURL(shell(), main_url));
2161
2162 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
2163 ->GetPrimaryFrameTree()
2164 .root();
2165
2166 // After a successful navigation, the "last committed origin" and the
2167 // "last successfully committed origin" should be the same.
2168 EXPECT_FALSE(root->current_origin().opaque());
2169 EXPECT_EQ(root->current_origin().GetTupleOrPrecursorTupleIfOpaque().host(),
2170 "a.test");
2171 EXPECT_EQ(root->last_successful_origin(), root->current_origin());
2172}
2173
2174IN_PROC_BROWSER_TEST_F(FrameTreeLastSuccessfulOriginBrowserTest,
2175 FailedNavigationAfterSuccessfulNavigation) {
2176 // First, perform a successful navigation, so that the root FrameTreeNode has
2177 // a non-opaque `last_successful_origin()`.
2178 GURL main_url(embedded_test_server()->GetURL("a.test", "/hello.html"));
2179 EXPECT_TRUE(NavigateToURL(shell(), main_url));
2180
2181 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
2182 ->GetPrimaryFrameTree()
2183 .root();
2184 EXPECT_EQ(root->last_successful_origin(), root->current_origin());
2185 url::Origin initial_successful_origin = root->last_successful_origin();
2186
2187 // Now, navigate to a malformed URL to force an error page.
2188 TestFrameNavigationObserver navigation_observer(root);
2189 EXPECT_TRUE(ExecJs(root, R"(location.href = 'https://hello';)"));
2190 navigation_observer.Wait();
2191
2192 EXPECT_FALSE(navigation_observer.last_navigation_succeeded());
2193 EXPECT_TRUE(root->current_frame_host()->IsErrorDocument());
2194
2195 // The new error document should have an opaque origin, but the frame's
2196 // `last_successful_origin()` should remain the same as the initial origin
2197 // from the first successful navigation.
2198 EXPECT_TRUE(root->current_origin().opaque());
2199 EXPECT_NE(root->last_successful_origin(), root->current_origin());
2200 EXPECT_EQ(root->last_successful_origin(), initial_successful_origin);
2201}
2202
2203IN_PROC_BROWSER_TEST_F(FrameTreeLastSuccessfulOriginBrowserTest,
2204 CorrectStateForNewMainFrame) {
2205 // Don't navigate the root frame to anything. It should have an empty URL with
2206 // an opaque origin.
2207 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
2208 ->GetPrimaryFrameTree()
2209 .root();
2210 EXPECT_TRUE(root->current_frame_host()->GetLastCommittedURL().is_empty());
2211 EXPECT_TRUE(root->current_origin().opaque());
2212 EXPECT_EQ(root->last_successful_origin(), root->current_origin());
2213}
2214
2215IN_PROC_BROWSER_TEST_F(FrameTreeLastSuccessfulOriginBrowserTest,
2216 CorrectStateForNewSubframe) {
2217 GURL main_url(embedded_test_server()->GetURL("a.test", "/hello.html"));
2218 EXPECT_TRUE(NavigateToURL(shell(), main_url));
2219
2220 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
2221 ->GetPrimaryFrameTree()
2222 .root();
2223
2224 EXPECT_TRUE(ExecJs(root,
2225 "let frame = document.createElement('iframe');"
2226 "document.body.appendChild(frame);"));
2227 EXPECT_EQ(1U, root->child_count());
2228 FrameTreeNode* new_frame = root->child_at(0);
2229
2230 // Our new subframe hasn't been navigated yet, so its current URL is
2231 // about:blank. It has also inherited the origin of its creator, and the
2232 // last successful origin should be the same.
2233 EXPECT_EQ(new_frame->current_url(), url::kAboutBlankURL);
2234 EXPECT_EQ(new_frame->current_origin().host(), "a.test");
2235 EXPECT_EQ(new_frame->last_successful_origin(), new_frame->current_origin());
2236}
2237
Thomas Lukaszewicz54d8e432024-09-18 20:13:172238INSTANTIATE_TEST_SUITE_P(
2239 ,
2240 FrameTreeBrowserWithDiscardTest,
2241 ::testing::Values(false, true),
2242 [](const ::testing::TestParamInfo<
2243 FrameTreeBrowserWithDiscardTest::ParamType>& info) {
2244 return info.param ? "KeepAlive" : "NoKeepAlive";
2245 });
2246
2247INSTANTIATE_TEST_SUITE_P(
2248 ,
2249 FrameTreeDiscardPendingNavigationTest,
2250 ::testing::Values(false, true),
2251 [](const ::testing::TestParamInfo<
2252 FrameTreeDiscardPendingNavigationTest::ParamType>& info) {
2253 return info.param ? "KeepAlive" : "NoKeepAlive";
2254 });
2255
2256INSTANTIATE_TEST_SUITE_P(
2257 ,
2258 DedicatedWorkerFrameTreeBrowserTest,
2259 ::testing::Values(false, true),
2260 [](const ::testing::TestParamInfo<
2261 DedicatedWorkerFrameTreeBrowserTest::ParamType>& info) {
2262 return info.param ? "KeepAlive" : "NoKeepAlive";
2263 });
2264
[email protected]5f96f5a62014-01-10 00:05:112265} // namespace content