Avi Drissman | 4e1b7bc3 | 2022-09-15 14:03:50 | [diff] [blame] | 1 | // Copyright 2014 The Chromium Authors |
[email protected] | 5f96f5a6 | 2014-01-10 00:05:11 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
Thomas Lukaszewicz | 00d3927 | 2024-08-21 23:54:10 | [diff] [blame] | 5 | #include "content/browser/renderer_host/frame_tree.h" |
| 6 | |
Ken Rockot | 86bea1c | 2017-05-16 06:21:35 | [diff] [blame] | 7 | #include "base/command_line.h" |
Ari Chivukula | 5d15efb | 2023-01-21 04:33:52 | [diff] [blame] | 8 | #include "base/strings/strcat.h" |
Aaron Colwell | e953e56 | 2019-07-24 16:47:36 | [diff] [blame] | 9 | #include "base/strings/stringprintf.h" |
shivanigithub | f33e469c | 2021-08-02 13:52:20 | [diff] [blame] | 10 | #include "base/synchronization/lock.h" |
Thomas Lukaszewicz | 0ef0f45f | 2025-02-18 17:59:42 | [diff] [blame] | 11 | #include "base/test/metrics/histogram_tester.h" |
Thomas Lukaszewicz | 8ada31b | 2024-09-01 19:18:16 | [diff] [blame] | 12 | #include "base/test/run_until.h" |
shivanigithub | 93878d0 | 2021-06-15 11:37:53 | [diff] [blame] | 13 | #include "base/test/scoped_feature_list.h" |
avi | b734894 | 2015-12-25 20:57:10 | [diff] [blame] | 14 | #include "build/build_config.h" |
danakj | c492bf8 | 2020-09-09 20:02:44 | [diff] [blame] | 15 | #include "content/browser/renderer_host/frame_tree_node.h" |
Yao Xiao | 6e1f7d3 | 2022-01-07 03:28:40 | [diff] [blame] | 16 | #include "content/browser/renderer_host/navigation_request.h" |
danakj | c492bf8 | 2020-09-09 20:02:44 | [diff] [blame] | 17 | #include "content/browser/renderer_host/render_frame_host_impl.h" |
Thomas Lukaszewicz | 54d8e43 | 2024-09-18 20:13:17 | [diff] [blame] | 18 | #include "content/browser/renderer_host/render_process_host_impl.h" |
[email protected] | 5f96f5a6 | 2014-01-10 00:05:11 | [diff] [blame] | 19 | #include "content/browser/renderer_host/render_view_host_impl.h" |
| 20 | #include "content/browser/web_contents/web_contents_impl.h" |
Thomas Lukaszewicz | 8ada31b | 2024-09-01 19:18:16 | [diff] [blame] | 21 | #include "content/common/content_navigation_policy.h" |
shivanigithub | f33e469c | 2021-08-02 13:52:20 | [diff] [blame] | 22 | #include "content/public/browser/browser_thread.h" |
Thomas Lukaszewicz | 8ada31b | 2024-09-01 19:18:16 | [diff] [blame] | 23 | #include "content/public/browser/dedicated_worker_service.h" |
Dave Tapuska | 87696ae | 2021-11-18 18:48:31 | [diff] [blame] | 24 | #include "content/public/browser/navigation_handle.h" |
Nasko Oskov | 0401d81 | 2021-02-05 22:20:08 | [diff] [blame] | 25 | #include "content/public/browser/site_isolation_policy.h" |
Annie Sullivan | 35977ad | 2019-04-30 21:04:28 | [diff] [blame] | 26 | #include "content/public/common/content_features.h" |
nick | db193a1 | 2016-09-09 23:09:23 | [diff] [blame] | 27 | #include "content/public/common/content_switches.h" |
Nasko Oskov | 0391210 | 2019-01-11 00:21:32 | [diff] [blame] | 28 | #include "content/public/common/origin_util.h" |
[email protected] | 5f96f5a6 | 2014-01-10 00:05:11 | [diff] [blame] | 29 | #include "content/public/common/url_constants.h" |
Peter Kasting | 919ce65 | 2020-05-07 10:22:36 | [diff] [blame] | 30 | #include "content/public/test/browser_test.h" |
[email protected] | 5f96f5a6 | 2014-01-10 00:05:11 | [diff] [blame] | 31 | #include "content/public/test/browser_test_utils.h" |
[email protected] | 6e9def1 | 2014-03-27 20:23:28 | [diff] [blame] | 32 | #include "content/public/test/content_browser_test.h" |
| 33 | #include "content/public/test/content_browser_test_utils.h" |
Ari Chivukula | 5d15efb | 2023-01-21 04:33:52 | [diff] [blame] | 34 | #include "content/public/test/content_mock_cert_verifier.h" |
John Abd-El-Malek | 3fea4c4 | 2019-01-10 01:15:42 | [diff] [blame] | 35 | #include "content/public/test/test_frame_navigation_observer.h" |
[email protected] | 5f96f5a6 | 2014-01-10 00:05:11 | [diff] [blame] | 36 | #include "content/public/test/test_navigation_observer.h" |
| 37 | #include "content/public/test/test_utils.h" |
| 38 | #include "content/shell/browser/shell.h" |
nick | db193a1 | 2016-09-09 23:09:23 | [diff] [blame] | 39 | #include "content/shell/common/shell_switches.h" |
[email protected] | 82307f6b | 2014-08-07 03:30:12 | [diff] [blame] | 40 | #include "content/test/content_browser_test_utils_internal.h" |
Thomas Lukaszewicz | 1b672fe | 2024-09-17 08:35:03 | [diff] [blame] | 41 | #include "content/test/render_document_feature.h" |
David Sanders | c9ef300 | 2022-02-02 15:27:01 | [diff] [blame] | 42 | #include "net/base/features.h" |
[email protected] | 5f96f5a6 | 2014-01-10 00:05:11 | [diff] [blame] | 43 | #include "net/dns/mock_host_resolver.h" |
Nan Lin | ea8e9d20 | 2022-04-13 18:31:01 | [diff] [blame] | 44 | #include "net/test/embedded_test_server/controllable_http_response.h" |
Dave Tapuska | 708c273 | 2022-09-23 22:47:49 | [diff] [blame] | 45 | #include "net/test/embedded_test_server/default_handlers.h" |
alexmos | 478dcbb | 2014-12-10 21:24:46 | [diff] [blame] | 46 | #include "net/test/embedded_test_server/embedded_test_server.h" |
shivanigithub | f33e469c | 2021-08-02 13:52:20 | [diff] [blame] | 47 | #include "net/test/embedded_test_server/http_request.h" |
Ari Chivukula | 5d15efb | 2023-01-21 04:33:52 | [diff] [blame] | 48 | #include "services/network/public/cpp/network_switches.h" |
arthursonzogni | b93a447 | 2020-04-10 07:38:00 | [diff] [blame] | 49 | #include "services/network/public/cpp/web_sandbox_flags.h" |
| 50 | #include "services/network/public/mojom/web_sandbox_flags.mojom-shared.h" |
Gyuyoung Kim | 107c2a0 | 2021-04-13 01:49:30 | [diff] [blame] | 51 | #include "third_party/blink/public/common/chrome_debug_urls.h" |
shivanigithub | 93878d0 | 2021-06-15 11:37:53 | [diff] [blame] | 52 | #include "third_party/blink/public/common/features.h" |
Ari Chivukula | 5d15efb | 2023-01-21 04:33:52 | [diff] [blame] | 53 | #include "third_party/blink/public/common/storage_key/storage_key.h" |
Thomas Lukaszewicz | 8ada31b | 2024-09-01 19:18:16 | [diff] [blame] | 54 | #include "third_party/blink/public/common/switches.h" |
Antonio Gomes | 4b2c513 | 2020-01-16 11:49:48 | [diff] [blame] | 55 | #include "third_party/blink/public/mojom/frame/user_activation_update_types.mojom.h" |
nick | 1466c84 | 2015-11-25 20:08:06 | [diff] [blame] | 56 | #include "url/url_constants.h" |
[email protected] | 5f96f5a6 | 2014-01-10 00:05:11 | [diff] [blame] | 57 | |
| 58 | namespace content { |
| 59 | |
nick | db193a1 | 2016-09-09 23:09:23 | [diff] [blame] | 60 | namespace { |
| 61 | |
Nick Carter | b7e7131 | 2018-08-03 23:36:13 | [diff] [blame] | 62 | EvalJsResult GetOriginFromRenderer(FrameTreeNode* node) { |
Philip Jägenstedt | 67302a2 | 2018-09-14 09:58:05 | [diff] [blame] | 63 | return EvalJs(node, "self.origin"); |
nick | db193a1 | 2016-09-09 23:09:23 | [diff] [blame] | 64 | } |
| 65 | |
Yuzu Saijo | 03dbf9b | 2022-07-22 04:29:45 | [diff] [blame] | 66 | // Expect that frame_name, id and src match the node's values. |
| 67 | void ExpectAttributesEq(FrameTreeNode* node, |
| 68 | const std::string& frame_name, |
Arthur Sonzogni | c686e8f | 2024-01-11 08:36:37 | [diff] [blame] | 69 | const std::optional<std::string> id, |
| 70 | const std::optional<std::string> src) { |
Yuzu Saijo | 03dbf9b | 2022-07-22 04:29:45 | [diff] [blame] | 71 | EXPECT_EQ(frame_name, node->frame_name()); |
| 72 | EXPECT_EQ(id, node->html_id()); |
| 73 | EXPECT_EQ(src, node->html_src()); |
| 74 | } |
| 75 | |
nick | db193a1 | 2016-09-09 23:09:23 | [diff] [blame] | 76 | } // namespace |
| 77 | |
[email protected] | 5f96f5a6 | 2014-01-10 00:05:11 | [diff] [blame] | 78 | class FrameTreeBrowserTest : public ContentBrowserTest { |
| 79 | public: |
Fergal Daly | a1d56997 | 2021-03-16 03:24:53 | [diff] [blame] | 80 | FrameTreeBrowserTest() = default; |
[email protected] | 5f96f5a6 | 2014-01-10 00:05:11 | [diff] [blame] | 81 | |
Peter Boström | 9b03653 | 2021-10-28 23:37:28 | [diff] [blame] | 82 | FrameTreeBrowserTest(const FrameTreeBrowserTest&) = delete; |
| 83 | FrameTreeBrowserTest& operator=(const FrameTreeBrowserTest&) = delete; |
| 84 | |
alexmos | 478dcbb | 2014-12-10 21:24:46 | [diff] [blame] | 85 | void SetUpOnMainThread() override { |
| 86 | host_resolver()->AddRule("*", "127.0.0.1"); |
alexmos | 478dcbb | 2014-12-10 21:24:46 | [diff] [blame] | 87 | SetupCrossSiteRedirector(embedded_test_server()); |
martijn | 12d2dad | 2016-11-11 10:59:27 | [diff] [blame] | 88 | ASSERT_TRUE(embedded_test_server()->Start()); |
alexmos | 478dcbb | 2014-12-10 21:24:46 | [diff] [blame] | 89 | } |
[email protected] | 5f96f5a6 | 2014-01-10 00:05:11 | [diff] [blame] | 90 | }; |
| 91 | |
| 92 | // Ensures FrameTree correctly reflects page structure during navigations. |
| 93 | IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, FrameTreeShape) { |
alexmos | 478dcbb | 2014-12-10 21:24:46 | [diff] [blame] | 94 | GURL base_url = embedded_test_server()->GetURL("A.com", "/site_isolation/"); |
[email protected] | 5f96f5a6 | 2014-01-10 00:05:11 | [diff] [blame] | 95 | |
| 96 | // Load doc without iframes. Verify FrameTree just has root. |
| 97 | // Frame tree: |
| 98 | // Site-A Root |
Alex Moshchuk | aa95adf5 | 2019-08-13 00:02:02 | [diff] [blame] | 99 | EXPECT_TRUE(NavigateToURL(shell(), base_url.Resolve("blank.html"))); |
Aran Gilman | 37d1163 | 2019-10-08 23:07:15 | [diff] [blame] | 100 | FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents()) |
Carlos Caballero | 15caeeb | 2021-10-27 09:57:55 | [diff] [blame] | 101 | ->GetPrimaryFrameTree() |
| 102 | .root(); |
[email protected] | 5f96f5a6 | 2014-01-10 00:05:11 | [diff] [blame] | 103 | 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] | 71bc140b | 2022-03-14 23:45:01 | [diff] [blame] | 109 | LoadStopObserver observer1(shell()->web_contents()); |
Alex Moshchuk | aa95adf5 | 2019-08-13 00:02:02 | [diff] [blame] | 110 | EXPECT_TRUE(NavigateToURL(shell(), base_url.Resolve("frames-X-X.html"))); |
[email protected] | 5f96f5a6 | 2014-01-10 00:05:11 | [diff] [blame] | 111 | 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. |
| 119 | IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, FrameTreeShape2) { |
Alex Moshchuk | aa95adf5 | 2019-08-13 00:02:02 | [diff] [blame] | 120 | EXPECT_TRUE(NavigateToURL( |
| 121 | shell(), embedded_test_server()->GetURL("/frame_tree/top.html"))); |
[email protected] | 5f96f5a6 | 2014-01-10 00:05:11 | [diff] [blame] | 122 | |
| 123 | WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents()); |
Carlos Caballero | 15caeeb | 2021-10-27 09:57:55 | [diff] [blame] | 124 | FrameTreeNode* root = wc->GetPrimaryFrameTree().root(); |
[email protected] | 5f96f5a6 | 2014-01-10 00:05:11 | [diff] [blame] | 125 | |
[email protected] | 58faf94 | 2014-02-20 21:03:58 | [diff] [blame] | 126 | // Check that the root node is properly created. |
[email protected] | 5f96f5a6 | 2014-01-10 00:05:11 | [diff] [blame] | 127 | ASSERT_EQ(3UL, root->child_count()); |
Arthur Sonzogni | c686e8f | 2024-01-11 08:36:37 | [diff] [blame] | 128 | ExpectAttributesEq(root, std::string(), std::nullopt, std::nullopt); |
[email protected] | 5f96f5a6 | 2014-01-10 00:05:11 | [diff] [blame] | 129 | |
| 130 | ASSERT_EQ(2UL, root->child_at(0)->child_count()); |
Yuzu Saijo | 03dbf9b | 2022-07-22 04:29:45 | [diff] [blame] | 131 | ExpectAttributesEq(root->child_at(0), "1-1-name", "1-1-id", "1-1.html"); |
[email protected] | 5f96f5a6 | 2014-01-10 00:05:11 | [diff] [blame] | 132 | |
| 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 Saijo | 03dbf9b | 2022-07-22 04:29:45 | [diff] [blame] | 137 | ExpectAttributesEq(root->child_at(2)->child_at(1)->child_at(0), "3-1-name", |
| 138 | "3-1-id", "3-1.html"); |
[email protected] | 5f96f5a6 | 2014-01-10 00:05:11 | [diff] [blame] | 139 | |
| 140 | // Navigate to about:blank, which should leave only the root node of the frame |
| 141 | // tree in the browser process. |
Alex Moshchuk | aa95adf5 | 2019-08-13 00:02:02 | [diff] [blame] | 142 | EXPECT_TRUE( |
| 143 | NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html"))); |
[email protected] | 5f96f5a6 | 2014-01-10 00:05:11 | [diff] [blame] | 144 | |
Carlos Caballero | 15caeeb | 2021-10-27 09:57:55 | [diff] [blame] | 145 | root = wc->GetPrimaryFrameTree().root(); |
[email protected] | 5f96f5a6 | 2014-01-10 00:05:11 | [diff] [blame] | 146 | EXPECT_EQ(0UL, root->child_count()); |
Arthur Sonzogni | c686e8f | 2024-01-11 08:36:37 | [diff] [blame] | 147 | ExpectAttributesEq(root, std::string(), std::nullopt, std::nullopt); |
Yuzu Saijo | 03dbf9b | 2022-07-22 04:29:45 | [diff] [blame] | 148 | } |
| 149 | |
| 150 | // Frame attributes of iframe elements are correctly tracked in FrameTree. |
| 151 | IN_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 Sonzogni | c686e8f | 2024-01-11 08:36:37 | [diff] [blame] | 160 | ExpectAttributesEq(root, std::string(), std::nullopt, std::nullopt); |
Yuzu Saijo | 03dbf9b | 2022-07-22 04:29:45 | [diff] [blame] | 161 | |
| 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()|. |
| 180 | IN_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 Sonzogni | c686e8f | 2024-01-11 08:36:37 | [diff] [blame] | 189 | EXPECT_EQ(std::nullopt, root->html_name()); |
[email protected] | 5f96f5a6 | 2014-01-10 00:05:11 | [diff] [blame] | 190 | EXPECT_EQ(std::string(), root->frame_name()); |
Yuzu Saijo | 03dbf9b | 2022-07-22 04:29:45 | [diff] [blame] | 191 | |
| 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. |
| 218 | IN_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 Saijo | dc870f9 | 2023-01-20 03:39:11 | [diff] [blame] | 238 | 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 Saijo | 03dbf9b | 2022-07-22 04:29:45 | [diff] [blame] | 241 | } |
| 242 | |
| 243 | // Insert a frame into the frame tree and ensure that the inserted frame's |
| 244 | // attributes are correctly captured. |
| 245 | IN_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 Sonzogni | c686e8f | 2024-01-11 08:36:37 | [diff] [blame] | 254 | ExpectAttributesEq(root, std::string(), std::nullopt, std::nullopt); |
Yuzu Saijo | 03dbf9b | 2022-07-22 04:29:45 | [diff] [blame] | 255 | |
| 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] | 5f96f5a6 | 2014-01-10 00:05:11 | [diff] [blame] | 272 | } |
| 273 | |
| 274 | // Test that we can navigate away if the previous renderer doesn't clean up its |
| 275 | // child frames. |
| 276 | IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, FrameTreeAfterCrash) { |
Alex Moshchuk | aa95adf5 | 2019-08-13 00:02:02 | [diff] [blame] | 277 | EXPECT_TRUE(NavigateToURL( |
| 278 | shell(), embedded_test_server()->GetURL("/frame_tree/top.html"))); |
[email protected] | 5f96f5a6 | 2014-01-10 00:05:11 | [diff] [blame] | 279 | |
creis | e42f2a5 | 2014-09-18 18:14:57 | [diff] [blame] | 280 | // Ensure the view and frame are live. |
Lingqi Chi | 641aeee | 2021-09-08 00:32:58 | [diff] [blame] | 281 | RenderFrameHostImpl* rfh1 = static_cast<RenderFrameHostImpl*>( |
Dave Tapuska | 327c06c9 | 2022-06-13 20:31:51 | [diff] [blame] | 282 | shell()->web_contents()->GetPrimaryMainFrame()); |
Kevin McNee | ec16230 | 2022-04-14 16:00:28 | [diff] [blame] | 283 | RenderViewHostImpl* rvh = rfh1->render_view_host(); |
creis | e42f2a5 | 2014-09-18 18:14:57 | [diff] [blame] | 284 | EXPECT_TRUE(rvh->IsRenderViewLive()); |
arthursonzogni | f4c4a4a | 2019-08-08 18:54:44 | [diff] [blame] | 285 | EXPECT_TRUE(rfh1->IsRenderFrameLive()); |
creis | e42f2a5 | 2014-09-18 18:14:57 | [diff] [blame] | 286 | |
[email protected] | 5f96f5a6 | 2014-01-10 00:05:11 | [diff] [blame] | 287 | // 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); |
Wez | 0abfbf51 | 2018-03-03 01:54:45 | [diff] [blame] | 291 | ASSERT_TRUE( |
Dave Tapuska | 327c06c9 | 2022-06-13 20:31:51 | [diff] [blame] | 292 | shell()->web_contents()->GetPrimaryMainFrame()->GetProcess()->Shutdown( |
| 293 | 0)); |
[email protected] | 5f96f5a6 | 2014-01-10 00:05:11 | [diff] [blame] | 294 | crash_observer.Wait(); |
| 295 | |
[email protected] | 58faf94 | 2014-02-20 21:03:58 | [diff] [blame] | 296 | // The frame tree should be cleared. |
[email protected] | 5f96f5a6 | 2014-01-10 00:05:11 | [diff] [blame] | 297 | WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents()); |
Carlos Caballero | 15caeeb | 2021-10-27 09:57:55 | [diff] [blame] | 298 | FrameTreeNode* root = wc->GetPrimaryFrameTree().root(); |
[email protected] | 5f96f5a6 | 2014-01-10 00:05:11 | [diff] [blame] | 299 | EXPECT_EQ(0UL, root->child_count()); |
[email protected] | 5f96f5a6 | 2014-01-10 00:05:11 | [diff] [blame] | 300 | |
creis | e42f2a5 | 2014-09-18 18:14:57 | [diff] [blame] | 301 | // Ensure the view and frame aren't live anymore. |
| 302 | EXPECT_FALSE(rvh->IsRenderViewLive()); |
arthursonzogni | f4c4a4a | 2019-08-08 18:54:44 | [diff] [blame] | 303 | EXPECT_FALSE(rfh1->IsRenderFrameLive()); |
creis | e42f2a5 | 2014-09-18 18:14:57 | [diff] [blame] | 304 | |
[email protected] | 5f96f5a6 | 2014-01-10 00:05:11 | [diff] [blame] | 305 | // Navigate to a new URL. |
alexmos | 478dcbb | 2014-12-10 21:24:46 | [diff] [blame] | 306 | GURL url(embedded_test_server()->GetURL("/title1.html")); |
Alex Moshchuk | aa95adf5 | 2019-08-13 00:02:02 | [diff] [blame] | 307 | EXPECT_TRUE(NavigateToURL(shell(), url)); |
[email protected] | 5f96f5a6 | 2014-01-10 00:05:11 | [diff] [blame] | 308 | EXPECT_EQ(0UL, root->child_count()); |
[email protected] | 58faf94 | 2014-02-20 21:03:58 | [diff] [blame] | 309 | EXPECT_EQ(url, root->current_url()); |
creis | e42f2a5 | 2014-09-18 18:14:57 | [diff] [blame] | 310 | |
arthursonzogni | f4c4a4a | 2019-08-08 18:54:44 | [diff] [blame] | 311 | RenderFrameHostImpl* rfh2 = root->current_frame_host(); |
creis | e42f2a5 | 2014-09-18 18:14:57 | [diff] [blame] | 312 | // Ensure the view and frame are live again. |
| 313 | EXPECT_TRUE(rvh->IsRenderViewLive()); |
arthursonzogni | f4c4a4a | 2019-08-08 18:54:44 | [diff] [blame] | 314 | EXPECT_TRUE(rfh2->IsRenderFrameLive()); |
[email protected] | 5f96f5a6 | 2014-01-10 00:05:11 | [diff] [blame] | 315 | } |
| 316 | |
Thomas Lukaszewicz | 54d8e43 | 2024-09-18 20:13:17 | [diff] [blame] | 317 | // Tests the frame discard impl, both with and without post-discard process |
| 318 | // shutdown. |
| 319 | class FrameTreeBrowserWithDiscardTest |
| 320 | : public FrameTreeBrowserTest, |
| 321 | public ::testing::WithParamInterface<bool> { |
Thomas Lukaszewicz | 8ada31b | 2024-09-01 19:18:16 | [diff] [blame] | 322 | public: |
| 323 | FrameTreeBrowserWithDiscardTest() { |
| 324 | scoped_feature_list_.InitAndEnableFeature(features::kWebContentsDiscard); |
| 325 | } |
| 326 | |
Thomas Lukaszewicz | 54d8e43 | 2024-09-18 20:13:17 | [diff] [blame] | 327 | void DiscardFrameTree(FrameTree& frame_tree) { |
| 328 | RenderProcessHostImpl* root_rph = static_cast<RenderProcessHostImpl*>( |
| 329 | frame_tree.root()->current_frame_host()->GetProcess()); |
| 330 | if (KeepAliveDiscardedProcess()) { |
Nan Lin | 359184e | 2025-04-03 19:45:22 | [diff] [blame] | 331 | // Increment the worker ref count of the renderer process to keep it |
Thomas Lukaszewicz | 54d8e43 | 2024-09-18 20:13:17 | [diff] [blame] | 332 | // alive post discard, simulating the situation where the process may be |
| 333 | // shared by multiple frames. |
Nan Lin | 359184e | 2025-04-03 19:45:22 | [diff] [blame] | 334 | root_rph->IncrementWorkerRefCount(); |
Thomas Lukaszewicz | 54d8e43 | 2024-09-18 20:13:17 | [diff] [blame] | 335 | } |
| 336 | |
| 337 | frame_tree.Discard(); |
| 338 | |
| 339 | if (!KeepAliveDiscardedProcess()) { |
| 340 | // If not keeping the process alive wait for it to successfully exit. |
| 341 | RenderProcessHostWatcher exit_observer( |
| 342 | root_rph, content::RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT); |
| 343 | exit_observer.Wait(); |
| 344 | } |
| 345 | } |
| 346 | |
| 347 | bool KeepAliveDiscardedProcess() const { return GetParam(); } |
| 348 | |
Thomas Lukaszewicz | 8ada31b | 2024-09-01 19:18:16 | [diff] [blame] | 349 | private: |
| 350 | base::test::ScopedFeatureList scoped_feature_list_; |
| 351 | }; |
| 352 | |
Thomas Lukaszewicz | 54d8e43 | 2024-09-18 20:13:17 | [diff] [blame] | 353 | IN_PROC_BROWSER_TEST_P(FrameTreeBrowserWithDiscardTest, DiscardFrameTree) { |
Thomas Lukaszewicz | 0ef0f45f | 2025-02-18 17:59:42 | [diff] [blame] | 354 | base::HistogramTester uma_recorder; |
Thomas Lukaszewicz | 8ada31b | 2024-09-01 19:18:16 | [diff] [blame] | 355 | 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 Lukaszewicz | 0ef0f45f | 2025-02-18 17:59:42 | [diff] [blame] | 386 | uma_recorder.ExpectUniqueSample("Discarding.DiscardFrameTree", true, 0); |
Thomas Lukaszewicz | 54d8e43 | 2024-09-18 20:13:17 | [diff] [blame] | 387 | DiscardFrameTree(frame_tree); |
Thomas Lukaszewicz | 8ada31b | 2024-09-01 19:18:16 | [diff] [blame] | 388 | 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 Lukaszewicz | 8ada31b | 2024-09-01 19:18:16 | [diff] [blame] | 402 | EXPECT_EQ(0u, root->child_count()); |
Thomas Lukaszewicz | 0ef0f45f | 2025-02-18 17:59:42 | [diff] [blame] | 403 | uma_recorder.ExpectUniqueSample("Discarding.DiscardFrameTree", true, 1); |
Thomas Lukaszewicz | 8ada31b | 2024-09-01 19:18:16 | [diff] [blame] | 404 | |
Thomas Lukaszewicz | 54d8e43 | 2024-09-18 20:13:17 | [diff] [blame] | 405 | 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 Lukaszewicz | 8ada31b | 2024-09-01 19:18:16 | [diff] [blame] | 415 | // 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 Lukaszewicz | 8ada31b | 2024-09-01 19:18:16 | [diff] [blame] | 434 | EXPECT_TRUE(final_rvh->IsRenderViewLive()); |
| 435 | EXPECT_TRUE(final_rfh->IsRenderFrameLive()); |
Thomas Lukaszewicz | 54d8e43 | 2024-09-18 20:13:17 | [diff] [blame] | 436 | |
| 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 Lukaszewicz | 8ada31b | 2024-09-01 19:18:16 | [diff] [blame] | 447 | } |
| 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 Lukaszewicz | 54d8e43 | 2024-09-18 20:13:17 | [diff] [blame] | 452 | IN_PROC_BROWSER_TEST_P(FrameTreeBrowserWithDiscardTest, |
Thomas Lukaszewicz | 8ada31b | 2024-09-01 19:18:16 | [diff] [blame] | 453 | 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 Lukaszewicz | 54d8e43 | 2024-09-18 20:13:17 | [diff] [blame] | 472 | DiscardFrameTree(frame_tree); |
Thomas Lukaszewicz | 8ada31b | 2024-09-01 19:18:16 | [diff] [blame] | 473 | 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 Lukaszewicz | bbd83a20 | 2025-03-22 07:16:54 | [diff] [blame] | 480 | IN_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 Lukaszewicz | 1b672fe | 2024-09-17 08:35:03 | [diff] [blame] | 505 | // Runs pending navigation discard browsertests with RenderDocument enabled for |
| 506 | // all frames to ensure a speculative RFH is created during navigation. |
| 507 | class 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 Lukaszewicz | 54d8e43 | 2024-09-18 20:13:17 | [diff] [blame] | 520 | IN_PROC_BROWSER_TEST_P(FrameTreeDiscardPendingNavigationTest, |
Thomas Lukaszewicz | 1b672fe | 2024-09-17 08:35:03 | [diff] [blame] | 521 | 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 Lukaszewicz | 54d8e43 | 2024-09-18 20:13:17 | [diff] [blame] | 543 | DiscardFrameTree(frame_tree); |
Thomas Lukaszewicz | 1b672fe | 2024-09-17 08:35:03 | [diff] [blame] | 544 | 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 Lukaszewicz | 54d8e43 | 2024-09-18 20:13:17 | [diff] [blame] | 554 | EXPECT_EQ(original_url, wc->GetLastCommittedURL()); |
Thomas Lukaszewicz | 1b672fe | 2024-09-17 08:35:03 | [diff] [blame] | 555 | } |
| 556 | |
Thomas Lukaszewicz | 54d8e43 | 2024-09-18 20:13:17 | [diff] [blame] | 557 | IN_PROC_BROWSER_TEST_P(FrameTreeDiscardPendingNavigationTest, |
Thomas Lukaszewicz | 1b672fe | 2024-09-17 08:35:03 | [diff] [blame] | 558 | 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 Lukaszewicz | 0ef0f45f | 2025-02-18 17:59:42 | [diff] [blame] | 575 | base::HistogramTester uma_recorder; |
Thomas Lukaszewicz | 1b672fe | 2024-09-17 08:35:03 | [diff] [blame] | 576 | 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 Lukaszewicz | 0ef0f45f | 2025-02-18 17:59:42 | [diff] [blame] | 597 | uma_recorder.ExpectUniqueSample("Discarding.DiscardFrameTree", false, 0); |
Thomas Lukaszewicz | 1b672fe | 2024-09-17 08:35:03 | [diff] [blame] | 598 | 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 Lukaszewicz | 0ef0f45f | 2025-02-18 17:59:42 | [diff] [blame] | 605 | uma_recorder.ExpectUniqueSample("Discarding.DiscardFrameTree", false, 1); |
Thomas Lukaszewicz | 1b672fe | 2024-09-17 08:35:03 | [diff] [blame] | 606 | |
| 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 Lukaszewicz | 8067f2c1 | 2024-12-13 07:02:18 | [diff] [blame] | 612 | // Asserts that a process pinned with a keep-alive ref hosting only discarded |
| 613 | // frames is successfully shutdown after the keep-alive timeout. |
| 614 | IN_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 Lin | 359184e | 2025-04-03 19:45:22 | [diff] [blame] | 632 | // Set a worker on the renderer process. |
| 633 | rph->IncrementWorkerRefCount(); |
Thomas Lukaszewicz | 8067f2c1 | 2024-12-13 07:02:18 | [diff] [blame] | 634 | |
| 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 Lukaszewicz | 8ada31b | 2024-09-01 19:18:16 | [diff] [blame] | 653 | class 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 | |
| 707 | class 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 Lukaszewicz | 54d8e43 | 2024-09-18 20:13:17 | [diff] [blame] | 717 | IN_PROC_BROWSER_TEST_P(DedicatedWorkerFrameTreeBrowserTest, |
Thomas Lukaszewicz | 8ada31b | 2024-09-01 19:18:16 | [diff] [blame] | 718 | 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 Lukaszewicz | 54d8e43 | 2024-09-18 20:13:17 | [diff] [blame] | 737 | DiscardFrameTree(frame_tree); |
Thomas Lukaszewicz | 8ada31b | 2024-09-01 19:18:16 | [diff] [blame] | 738 | EXPECT_TRUE(root->was_discarded()); |
| 739 | EXPECT_TRUE(wc->GetController().NeedsReload()); |
Thomas Lukaszewicz | 54d8e43 | 2024-09-18 20:13:17 | [diff] [blame] | 740 | |
| 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 Lukaszewicz | 8ada31b | 2024-09-01 19:18:16 | [diff] [blame] | 746 | worker_observer.WaitForDestroyed(); |
| 747 | } |
| 748 | |
| 749 | // TODO(347770670): Consider restricting script access to discarded documents |
| 750 | // from related documents. |
Thomas Lukaszewicz | 54d8e43 | 2024-09-18 20:13:17 | [diff] [blame] | 751 | IN_PROC_BROWSER_TEST_P(FrameTreeBrowserWithDiscardTest, |
Thomas Lukaszewicz | 8ada31b | 2024-09-01 19:18:16 | [diff] [blame] | 752 | DiscardedFrameAllowsScriptAccess) { |
Thomas Lukaszewicz | 54d8e43 | 2024-09-18 20:13:17 | [diff] [blame] | 753 | if (!KeepAliveDiscardedProcess()) { |
| 754 | GTEST_SKIP() << "Not applicable when destroying process post discard."; |
| 755 | } |
| 756 | |
Thomas Lukaszewicz | 8ada31b | 2024-09-01 19:18:16 | [diff] [blame] | 757 | 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 | |
| 788 | class 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 | |
| 803 | IN_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 | |
| 836 | IN_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] | 5f96f5a6 | 2014-01-10 00:05:11 | [diff] [blame] | 868 | // Test that we can navigate away if the previous renderer doesn't clean up its |
| 869 | // child frames. |
Nasko Oskov | 31c45ff | 2019-10-16 01:15:19 | [diff] [blame] | 870 | IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, NavigateWithLeftoverFrames) { |
alexmos | 478dcbb | 2014-12-10 21:24:46 | [diff] [blame] | 871 | GURL base_url = embedded_test_server()->GetURL("A.com", "/site_isolation/"); |
[email protected] | 5f96f5a6 | 2014-01-10 00:05:11 | [diff] [blame] | 872 | |
Alex Moshchuk | aa95adf5 | 2019-08-13 00:02:02 | [diff] [blame] | 873 | EXPECT_TRUE(NavigateToURL( |
| 874 | shell(), embedded_test_server()->GetURL("/frame_tree/top.html"))); |
[email protected] | 5f96f5a6 | 2014-01-10 00:05:11 | [diff] [blame] | 875 | |
| 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 Kim | 107c2a0 | 2021-04-13 01:49:30 | [diff] [blame] | 878 | shell()->LoadURL(GURL(blink::kChromeUIHangURL)); |
[email protected] | 5f96f5a6 | 2014-01-10 00:05:11 | [diff] [blame] | 879 | |
| 880 | // Check that the frame tree still has children. |
| 881 | WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents()); |
Carlos Caballero | 15caeeb | 2021-10-27 09:57:55 | [diff] [blame] | 882 | FrameTreeNode* root = wc->GetPrimaryFrameTree().root(); |
[email protected] | 5f96f5a6 | 2014-01-10 00:05:11 | [diff] [blame] | 883 | 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] | 58faf94 | 2014-02-20 21:03:58 | [diff] [blame] | 891 | // The frame tree should now be cleared. |
[email protected] | 5f96f5a6 | 2014-01-10 00:05:11 | [diff] [blame] | 892 | EXPECT_EQ(0UL, root->child_count()); |
[email protected] | 5f96f5a6 | 2014-01-10 00:05:11 | [diff] [blame] | 893 | } |
| 894 | |
creis | e42f2a5 | 2014-09-18 18:14:57 | [diff] [blame] | 895 | // Ensure that IsRenderFrameLive is true for main frames and same-site iframes. |
| 896 | IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, IsRenderFrameLive) { |
alexmos | 478dcbb | 2014-12-10 21:24:46 | [diff] [blame] | 897 | GURL main_url(embedded_test_server()->GetURL("/frame_tree/top.html")); |
Alex Moshchuk | aa95adf5 | 2019-08-13 00:02:02 | [diff] [blame] | 898 | EXPECT_TRUE(NavigateToURL(shell(), main_url)); |
creis | e42f2a5 | 2014-09-18 18:14:57 | [diff] [blame] | 899 | |
| 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 Caballero | 15caeeb | 2021-10-27 09:57:55 | [diff] [blame] | 902 | ->GetPrimaryFrameTree() |
| 903 | .root(); |
creis | e42f2a5 | 2014-09-18 18:14:57 | [diff] [blame] | 904 | |
| 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. |
alexmos | 478dcbb | 2014-12-10 21:24:46 | [diff] [blame] | 912 | GURL http_url(embedded_test_server()->GetURL("/title1.html")); |
Lukasz Anforowicz | 69c25dfd | 2020-11-12 21:50:20 | [diff] [blame] | 913 | EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(0), http_url)); |
creis | e42f2a5 | 2014-09-18 18:14:57 | [diff] [blame] | 914 | 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 | |
alexmos | bc7eafa | 2014-12-06 01:38:09 | [diff] [blame] | 920 | // Ensure that origins are correctly set on navigations. |
| 921 | IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, OriginSetOnNavigation) { |
nick | 1466c84 | 2015-11-25 20:08:06 | [diff] [blame] | 922 | GURL about_blank(url::kAboutBlankURL); |
| 923 | GURL main_url( |
| 924 | embedded_test_server()->GetURL("a.com", "/frame_tree/top.html")); |
alexmos | bc7eafa | 2014-12-06 01:38:09 | [diff] [blame] | 925 | EXPECT_TRUE(NavigateToURL(shell(), main_url)); |
nick | 1466c84 | 2015-11-25 20:08:06 | [diff] [blame] | 926 | WebContents* contents = shell()->web_contents(); |
alexmos | bc7eafa | 2014-12-06 01:38:09 | [diff] [blame] | 927 | |
| 928 | // It is safe to obtain the root frame tree node here, as it doesn't change. |
nick | 1466c84 | 2015-11-25 20:08:06 | [diff] [blame] | 929 | FrameTreeNode* root = |
Carlos Caballero | 15caeeb | 2021-10-27 09:57:55 | [diff] [blame] | 930 | static_cast<WebContentsImpl*>(contents)->GetPrimaryFrameTree().root(); |
alexmos | bc7eafa | 2014-12-06 01:38:09 | [diff] [blame] | 931 | |
| 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 West | 800532c | 2021-10-14 09:26:52 | [diff] [blame] | 935 | EXPECT_EQ(main_url.DeprecatedGetOriginAsURL().spec(), |
nick | 1466c84 | 2015-11-25 20:08:06 | [diff] [blame] | 936 | root->current_origin().Serialize() + '/'); |
| 937 | EXPECT_EQ( |
Mike West | 800532c | 2021-10-14 09:26:52 | [diff] [blame] | 938 | main_url.DeprecatedGetOriginAsURL().spec(), |
nick | 1466c84 | 2015-11-25 20:08:06 | [diff] [blame] | 939 | root->current_frame_host()->GetLastCommittedOrigin().Serialize() + '/'); |
alexmos | bc7eafa | 2014-12-06 01:38:09 | [diff] [blame] | 940 | |
nick | 1466c84 | 2015-11-25 20:08:06 | [diff] [blame] | 941 | // 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 Anforowicz | 69c25dfd | 2020-11-12 21:50:20 | [diff] [blame] | 951 | EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(0), frame_url)); |
nick | 1466c84 | 2015-11-25 20:08:06 | [diff] [blame] | 952 | EXPECT_EQ(frame_url, root->child_at(0)->current_url()); |
Mike West | 800532c | 2021-10-14 09:26:52 | [diff] [blame] | 953 | EXPECT_EQ(frame_url.DeprecatedGetOriginAsURL().spec(), |
nick | 1466c84 | 2015-11-25 20:08:06 | [diff] [blame] | 954 | 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))); |
alexmos | bc7eafa | 2014-12-06 01:38:09 | [diff] [blame] | 961 | |
nick | 1466c84 | 2015-11-25 20:08:06 | [diff] [blame] | 962 | // 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 West | 800532c | 2021-10-14 09:26:52 | [diff] [blame] | 966 | EXPECT_EQ(main_url.DeprecatedGetOriginAsURL().spec(), |
nick | 1466c84 | 2015-11-25 20:08:06 | [diff] [blame] | 967 | 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))); |
alexmos | bc7eafa | 2014-12-06 01:38:09 | [diff] [blame] | 979 | |
| 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. |
nick | 1466c84 | 2015-11-25 20:08:06 | [diff] [blame] | 985 | EXPECT_EQ("null", root->current_origin().Serialize()); |
Dave Tapuska | 327c06c9 | 2022-06-13 20:31:51 | [diff] [blame] | 986 | EXPECT_TRUE( |
| 987 | contents->GetPrimaryMainFrame()->GetLastCommittedOrigin().opaque()); |
nick | 1466c84 | 2015-11-25 20:08:06 | [diff] [blame] | 988 | EXPECT_EQ("null", GetOriginFromRenderer(root)); |
alexmos | bc7eafa | 2014-12-06 01:38:09 | [diff] [blame] | 989 | |
| 990 | // Re-navigating to a normal URL should update the origin. |
| 991 | EXPECT_TRUE(NavigateToURL(shell(), main_url)); |
Mike West | 800532c | 2021-10-14 09:26:52 | [diff] [blame] | 992 | EXPECT_EQ(main_url.DeprecatedGetOriginAsURL().spec(), |
nick | 1466c84 | 2015-11-25 20:08:06 | [diff] [blame] | 993 | root->current_origin().Serialize() + '/'); |
| 994 | EXPECT_EQ( |
Mike West | 800532c | 2021-10-14 09:26:52 | [diff] [blame] | 995 | main_url.DeprecatedGetOriginAsURL().spec(), |
Dave Tapuska | 327c06c9 | 2022-06-13 20:31:51 | [diff] [blame] | 996 | contents->GetPrimaryMainFrame()->GetLastCommittedOrigin().Serialize() + |
| 997 | '/'); |
| 998 | EXPECT_FALSE( |
| 999 | contents->GetPrimaryMainFrame()->GetLastCommittedOrigin().opaque()); |
nick | 1466c84 | 2015-11-25 20:08:06 | [diff] [blame] | 1000 | EXPECT_EQ(root->current_origin().Serialize(), GetOriginFromRenderer(root)); |
alexmos | bc7eafa | 2014-12-06 01:38:09 | [diff] [blame] | 1001 | } |
| 1002 | |
nick | 9f34e289 | 2016-01-12 21:01:07 | [diff] [blame] | 1003 | // 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. |
| 1005 | IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, NavigateGrandchildToBlob) { |
| 1006 | WebContents* contents = shell()->web_contents(); |
| 1007 | FrameTreeNode* root = |
Carlos Caballero | 15caeeb | 2021-10-27 09:57:55 | [diff] [blame] | 1008 | static_cast<WebContentsImpl*>(contents)->GetPrimaryFrameTree().root(); |
nick | 9f34e289 | 2016-01-12 21:01:07 | [diff] [blame] | 1009 | |
| 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 Daly | 79f4429 | 2020-12-01 02:30:48 | [diff] [blame] | 1015 | std::string reference_tree = DepictFrameTree(*root); |
nick | 9f34e289 | 2016-01-12 21:01:07 | [diff] [blame] | 1016 | |
| 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 | |
creis | b2d5f1f3 | 2016-11-07 23:25:05 | [diff] [blame] | 1025 | RenderFrameDeletedObserver deleted_observer(target->current_frame_host()); |
Nick Carter | b7e7131 | 2018-08-03 23:36:13 | [diff] [blame] | 1026 | std::string html = |
| 1027 | "<html><body><div>This is blob content.</div>" |
| 1028 | "<script>" |
Philip Jägenstedt | 67302a2 | 2018-09-14 09:58:05 | [diff] [blame] | 1029 | "window.parent.parent.postMessage('HI', self.origin);" |
Nick Carter | b7e7131 | 2018-08-03 23:36:13 | [diff] [blame] | 1030 | "</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) => {" |
nick | 9f34e289 | 2016-01-12 21:01:07 | [diff] [blame] | 1038 | " document.body.appendChild(document.createTextNode(event.data));" |
Nick Carter | b7e7131 | 2018-08-03 23:36:13 | [diff] [blame] | 1039 | " return event.source.location.href;" |
| 1040 | "});", |
| 1041 | html); |
| 1042 | std::string blob_url_string = EvalJs(root, script).ExtractString(); |
creis | b2d5f1f3 | 2016-11-07 23:25:05 | [diff] [blame] | 1043 | // Wait for the RenderFrame to go away, if this will be cross-process. |
| 1044 | if (AreAllSitesIsolatedForTesting()) |
| 1045 | deleted_observer.WaitUntilDeleted(); |
nick | 9f34e289 | 2016-01-12 21:01:07 | [diff] [blame] | 1046 | EXPECT_EQ(GURL(blob_url_string), target->current_url()); |
| 1047 | EXPECT_EQ(url::kBlobScheme, target->current_url().scheme()); |
Chris Palmer | ab5e5b5 | 2018-09-28 19:19:30 | [diff] [blame] | 1048 | EXPECT_FALSE(target->current_origin().opaque()); |
nick | 9f34e289 | 2016-01-12 21:01:07 | [diff] [blame] | 1049 | EXPECT_EQ("a.com", target->current_origin().host()); |
| 1050 | EXPECT_EQ(url::kHttpScheme, target->current_origin().scheme()); |
Nick Carter | b7e7131 | 2018-08-03 23:36:13 | [diff] [blame] | 1051 | EXPECT_EQ("This is blob content.", |
| 1052 | EvalJs(target, "document.body.children[0].innerHTML")); |
Fergal Daly | 79f4429 | 2020-12-01 02:30:48 | [diff] [blame] | 1053 | EXPECT_EQ(reference_tree, DepictFrameTree(*root)); |
nick | 9f34e289 | 2016-01-12 21:01:07 | [diff] [blame] | 1054 | } |
| 1055 | |
| 1056 | IN_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 Caballero | 15caeeb | 2021-10-27 09:57:55 | [diff] [blame] | 1066 | contents->GetPrimaryFrameTree().root()->child_at(0)->child_at(0); |
Harkiran Bolaria | 4eaae307 | 2021-09-21 10:45:48 | [diff] [blame] | 1067 | RenderFrameHost* initiator_rfh = target->parent(); |
nick | 9f34e289 | 2016-01-12 21:01:07 | [diff] [blame] | 1068 | |
| 1069 | // Give the target a name. |
Nick Carter | b7e7131 | 2018-08-03 23:36:13 | [diff] [blame] | 1070 | EXPECT_TRUE(ExecJs(target, "window.name = 'target';")); |
nick | 9f34e289 | 2016-01-12 21:01:07 | [diff] [blame] | 1071 | |
| 1072 | // Use window.open(about:blank), then poll the document for access. |
Nick Carter | b7e7131 | 2018-08-03 23:36:13 | [diff] [blame] | 1073 | EvalJsResult about_blank_origin = EvalJs( |
Harkiran Bolaria | 4eaae307 | 2021-09-21 10:45:48 | [diff] [blame] | 1074 | initiator_rfh, |
Nick Carter | b7e7131 | 2018-08-03 23:36:13 | [diff] [blame] | 1075 | "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ägenstedt | 67302a2 | 2018-09-14 09:58:05 | [diff] [blame] | 1087 | " resolve(frames[0].self.origin);" |
Nick Carter | b7e7131 | 2018-08-03 23:36:13 | [diff] [blame] | 1088 | " }, 16);" |
| 1089 | "});"); |
| 1090 | EXPECT_EQ(target->current_origin(), about_blank_origin); |
Nate Chapin | ab67b04 | 2021-05-04 21:00:51 | [diff] [blame] | 1091 | EXPECT_EQ(GURL(url::kAboutBlankURL), target->current_url()); |
| 1092 | EXPECT_EQ(url::kAboutScheme, target->current_url().scheme()); |
Chris Palmer | ab5e5b5 | 2018-09-28 19:19:30 | [diff] [blame] | 1093 | EXPECT_FALSE(target->current_origin().opaque()); |
nick | 9f34e289 | 2016-01-12 21:01:07 | [diff] [blame] | 1094 | EXPECT_EQ("b.com", target->current_origin().host()); |
| 1095 | EXPECT_EQ(url::kHttpScheme, target->current_origin().scheme()); |
nick | 9f34e289 | 2016-01-12 21:01:07 | [diff] [blame] | 1096 | |
Nick Carter | b7e7131 | 2018-08-03 23:36:13 | [diff] [blame] | 1097 | EXPECT_EQ("Hi from b.com", EvalJs(target, "document.body.innerHTML")); |
nick | 9f34e289 | 2016-01-12 21:01:07 | [diff] [blame] | 1098 | } |
| 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 |
| 1103 | IN_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 Caballero | 15caeeb | 2021-10-27 09:57:55 | [diff] [blame] | 1114 | contents->GetPrimaryFrameTree().root()->child_at(0)->child_at(0); |
Harkiran Bolaria | 4eaae307 | 2021-09-21 10:45:48 | [diff] [blame] | 1115 | RenderFrameHost* initiator_rfh = target->parent()->GetParent(); |
nick | 9f34e289 | 2016-01-12 21:01:07 | [diff] [blame] | 1116 | |
| 1117 | // Give the target a name. |
Nick Carter | b7e7131 | 2018-08-03 23:36:13 | [diff] [blame] | 1118 | EXPECT_TRUE(ExecJs(target, "window.name = 'target';")); |
nick | 9f34e289 | 2016-01-12 21:01:07 | [diff] [blame] | 1119 | |
| 1120 | // Use window.open(about:blank), then poll the document for access. |
Nick Carter | b7e7131 | 2018-08-03 23:36:13 | [diff] [blame] | 1121 | EvalJsResult about_blank_origin = |
Harkiran Bolaria | 4eaae307 | 2021-09-21 10:45:48 | [diff] [blame] | 1122 | EvalJs(initiator_rfh, |
Nick Carter | b7e7131 | 2018-08-03 23:36:13 | [diff] [blame] | 1123 | "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ägenstedt | 67302a2 | 2018-09-14 09:58:05 | [diff] [blame] | 1135 | " resolve(frames[0][0].self.origin);" |
Nick Carter | b7e7131 | 2018-08-03 23:36:13 | [diff] [blame] | 1136 | " }, 16);" |
| 1137 | "});"); |
| 1138 | EXPECT_EQ(target->current_origin(), about_blank_origin); |
nick | 9f34e289 | 2016-01-12 21:01:07 | [diff] [blame] | 1139 | EXPECT_EQ(GURL(url::kAboutBlankURL), target->current_url()); |
| 1140 | EXPECT_EQ(url::kAboutScheme, target->current_url().scheme()); |
Chris Palmer | ab5e5b5 | 2018-09-28 19:19:30 | [diff] [blame] | 1141 | EXPECT_FALSE(target->current_origin().opaque()); |
nick | 9f34e289 | 2016-01-12 21:01:07 | [diff] [blame] | 1142 | EXPECT_EQ("a.com", target->current_origin().host()); |
| 1143 | EXPECT_EQ(url::kHttpScheme, target->current_origin().scheme()); |
nick | 9f34e289 | 2016-01-12 21:01:07 | [diff] [blame] | 1144 | |
Nick Carter | b7e7131 | 2018-08-03 23:36:13 | [diff] [blame] | 1145 | EXPECT_EQ("Hi from a.com", EvalJs(target, "document.body.innerHTML")); |
nick | 9f34e289 | 2016-01-12 21:01:07 | [diff] [blame] | 1146 | } |
| 1147 | |
Nasko Oskov | 0391210 | 2019-01-11 00:21:32 | [diff] [blame] | 1148 | // 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. |
| 1151 | IN_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 Caballero | 15caeeb | 2021-10-27 09:57:55 | [diff] [blame] | 1161 | contents->GetPrimaryFrameTree().root()->child_at(0)->child_at(0); |
Harkiran Bolaria | 4eaae307 | 2021-09-21 10:45:48 | [diff] [blame] | 1162 | RenderFrameHostImpl* initiator_rfh = target->parent()->GetParent(); |
Nasko Oskov | 0391210 | 2019-01-11 00:21:32 | [diff] [blame] | 1163 | |
| 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 Bolaria | 4eaae307 | 2021-09-21 10:45:48 | [diff] [blame] | 1170 | EXPECT_TRUE(ExecJs(initiator_rfh, |
| 1171 | "window.open('data:text/html,content', 'target');")); |
Nasko Oskov | 0391210 | 2019-01-11 00:21:32 | [diff] [blame] | 1172 | 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 Amni | c736785 | 2022-11-07 17:10:40 | [diff] [blame] | 1182 | // 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 Oskov | 0391210 | 2019-01-11 00:21:32 | [diff] [blame] | 1184 | { |
| 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 Amni | c736785 | 2022-11-07 17:10:40 | [diff] [blame] | 1201 | EXPECT_NE(target_origin, original_target_origin); |
Nasko Oskov | 0391210 | 2019-01-11 00:21:32 | [diff] [blame] | 1202 | EXPECT_TRUE(target_origin.opaque()); |
| 1203 | EXPECT_EQ(target_origin.GetTupleOrPrecursorTupleIfOpaque(), |
Rakina Zata Amni | c736785 | 2022-11-07 17:10:40 | [diff] [blame] | 1204 | original_target_origin.GetTupleOrPrecursorTupleIfOpaque()); |
| 1205 | EXPECT_EQ(target_origin.GetTupleOrPrecursorTupleIfOpaque(), |
Nasko Oskov | 0391210 | 2019-01-11 00:21:32 | [diff] [blame] | 1206 | url::SchemeHostPort(main_url)); |
Nasko Oskov | 0391210 | 2019-01-11 00:21:32 | [diff] [blame] | 1207 | } |
| 1208 | |
nasko | faa01fb | 2016-04-30 01:04:17 | [diff] [blame] | 1209 | // Ensures that iframe with srcdoc is always put in the same origin as its |
| 1210 | // parent frame. |
| 1211 | IN_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 Caballero | 15caeeb | 2021-10-27 09:57:55 | [diff] [blame] | 1217 | FrameTreeNode* root = contents->GetPrimaryFrameTree().root(); |
nasko | faa01fb | 2016-04-30 01:04:17 | [diff] [blame] | 1218 | EXPECT_EQ(1U, root->child_count()); |
| 1219 | |
| 1220 | FrameTreeNode* child = root->child_at(0); |
Philip Jägenstedt | 67302a2 | 2018-09-14 09:58:05 | [diff] [blame] | 1221 | std::string frame_origin = EvalJs(child, "self.origin;").ExtractString(); |
nasko | faa01fb | 2016-04-30 01:04:17 | [diff] [blame] | 1222 | EXPECT_TRUE( |
| 1223 | child->current_frame_host()->GetLastCommittedOrigin().IsSameOriginWith( |
Daniel Cheng | 88186bd5 | 2017-10-20 08:14:46 | [diff] [blame] | 1224 | url::Origin::Create(GURL(frame_origin)))); |
nasko | faa01fb | 2016-04-30 01:04:17 | [diff] [blame] | 1225 | EXPECT_FALSE( |
| 1226 | root->current_frame_host()->GetLastCommittedOrigin().IsSameOriginWith( |
Daniel Cheng | 88186bd5 | 2017-10-20 08:14:46 | [diff] [blame] | 1227 | url::Origin::Create(GURL(frame_origin)))); |
nasko | faa01fb | 2016-04-30 01:04:17 | [diff] [blame] | 1228 | |
| 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 Gilman | 37d1163 | 2019-10-08 23:07:15 | [diff] [blame] | 1232 | std::string script( |
| 1233 | "var f = document.createElement('iframe');" |
| 1234 | "f.srcdoc = 'some content';" |
| 1235 | "document.body.appendChild(f)"); |
nasko | faa01fb | 2016-04-30 01:04:17 | [diff] [blame] | 1236 | TestNavigationObserver observer(shell()->web_contents()); |
Nick Carter | b7e7131 | 2018-08-03 23:36:13 | [diff] [blame] | 1237 | EXPECT_TRUE(ExecJs(root, script)); |
nasko | faa01fb | 2016-04-30 01:04:17 | [diff] [blame] | 1238 | EXPECT_EQ(2U, root->child_count()); |
| 1239 | observer.Wait(); |
| 1240 | |
Lukasz Anforowicz | 42d3d07f | 2019-06-19 01:06:42 | [diff] [blame] | 1241 | EXPECT_TRUE(root->child_at(1)->current_url().IsAboutSrcdoc()); |
Peter Kasting | eb8c3ce | 2021-08-20 04:39:35 | [diff] [blame] | 1242 | EvalJsResult js_result = EvalJs(root->child_at(1), "self.origin"); |
Mike West | 800532c | 2021-10-14 09:26:52 | [diff] [blame] | 1243 | EXPECT_EQ(root->current_frame_host() |
| 1244 | ->GetLastCommittedURL() |
| 1245 | .DeprecatedGetOriginAsURL(), |
Peter Kasting | eb8c3ce | 2021-08-20 04:39:35 | [diff] [blame] | 1246 | GURL(js_result.ExtractString())); |
Mike West | 800532c | 2021-10-14 09:26:52 | [diff] [blame] | 1247 | EXPECT_NE(child->current_frame_host() |
| 1248 | ->GetLastCommittedURL() |
| 1249 | .DeprecatedGetOriginAsURL(), |
Peter Kasting | eb8c3ce | 2021-08-20 04:39:35 | [diff] [blame] | 1250 | GURL(js_result.ExtractString())); |
nasko | faa01fb | 2016-04-30 01:04:17 | [diff] [blame] | 1251 | } |
| 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 Gilman | 37d1163 | 2019-10-08 23:07:15 | [diff] [blame] | 1256 | std::string script( |
| 1257 | "var f = document.getElementById('child-0');" |
| 1258 | "f.srcdoc = 'some content';"); |
nasko | faa01fb | 2016-04-30 01:04:17 | [diff] [blame] | 1259 | TestNavigationObserver observer(shell()->web_contents()); |
Nick Carter | b7e7131 | 2018-08-03 23:36:13 | [diff] [blame] | 1260 | EXPECT_TRUE(ExecJs(root, script)); |
nasko | faa01fb | 2016-04-30 01:04:17 | [diff] [blame] | 1261 | observer.Wait(); |
| 1262 | |
Lukasz Anforowicz | 42d3d07f | 2019-06-19 01:06:42 | [diff] [blame] | 1263 | EXPECT_TRUE(child->current_url().IsAboutSrcdoc()); |
Avi Drissman | a3144c58 | 2021-10-14 17:00:26 | [diff] [blame] | 1264 | EXPECT_EQ(root->current_frame_host()->GetLastCommittedOrigin().Serialize(), |
| 1265 | EvalJs(child, "self.origin")); |
nasko | faa01fb | 2016-04-30 01:04:17 | [diff] [blame] | 1266 | } |
| 1267 | } |
| 1268 | |
Ian Clelland | 5cbaaf8 | 2017-11-27 22:00:03 | [diff] [blame] | 1269 | // Ensure that sandbox flags are correctly set in the main frame when set by |
| 1270 | // Content-Security-Policy header. |
| 1271 | IN_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 Caballero | 15caeeb | 2021-10-27 09:57:55 | [diff] [blame] | 1277 | ->GetPrimaryFrameTree() |
| 1278 | .root(); |
Ian Clelland | 5cbaaf8 | 2017-11-27 22:00:03 | [diff] [blame] | 1279 | |
| 1280 | // Verify that sandbox flags are set properly for the root FrameTreeNode and |
| 1281 | // RenderFrameHost. Root frame is sandboxed with "allow-scripts". |
arthursonzogni | b93a447 | 2020-04-10 07:38:00 | [diff] [blame] | 1282 | EXPECT_EQ(network::mojom::WebSandboxFlags::kNone, |
Ian Clelland | 5cbaaf8 | 2017-11-27 22:00:03 | [diff] [blame] | 1283 | root->effective_frame_policy().sandbox_flags); |
arthursonzogni | b93a447 | 2020-04-10 07:38:00 | [diff] [blame] | 1284 | EXPECT_EQ(network::mojom::WebSandboxFlags::kAll & |
| 1285 | ~network::mojom::WebSandboxFlags::kScripts & |
| 1286 | ~network::mojom::WebSandboxFlags::kAutomaticFeatures, |
Ian Clelland | 5cbaaf8 | 2017-11-27 22:00:03 | [diff] [blame] | 1287 | 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. |
arthursonzogni | b93a447 | 2020-04-10 07:38:00 | [diff] [blame] | 1294 | EXPECT_EQ(network::mojom::WebSandboxFlags::kAll & |
| 1295 | ~network::mojom::WebSandboxFlags::kScripts & |
| 1296 | ~network::mojom::WebSandboxFlags::kAutomaticFeatures, |
Ian Clelland | 5cbaaf8 | 2017-11-27 22:00:03 | [diff] [blame] | 1297 | root->child_at(0)->effective_frame_policy().sandbox_flags); |
arthursonzogni | b93a447 | 2020-04-10 07:38:00 | [diff] [blame] | 1298 | EXPECT_EQ(network::mojom::WebSandboxFlags::kAll & |
| 1299 | ~network::mojom::WebSandboxFlags::kScripts & |
| 1300 | ~network::mojom::WebSandboxFlags::kAutomaticFeatures, |
Ian Clelland | 5cbaaf8 | 2017-11-27 22:00:03 | [diff] [blame] | 1301 | 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()); |
arthursonzogni | b93a447 | 2020-04-10 07:38:00 | [diff] [blame] | 1304 | EXPECT_EQ(network::mojom::WebSandboxFlags::kAll, |
Ian Clelland | 5cbaaf8 | 2017-11-27 22:00:03 | [diff] [blame] | 1305 | root->child_at(1)->effective_frame_policy().sandbox_flags); |
arthursonzogni | b93a447 | 2020-04-10 07:38:00 | [diff] [blame] | 1306 | EXPECT_EQ(network::mojom::WebSandboxFlags::kAll, |
Ian Clelland | 5cbaaf8 | 2017-11-27 22:00:03 | [diff] [blame] | 1307 | 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 Anforowicz | 69c25dfd | 2020-11-12 21:50:20 | [diff] [blame] | 1313 | EXPECT_TRUE(NavigateToURLFromRenderer(root, unsandboxed_url)); |
Ian Clelland | 5cbaaf8 | 2017-11-27 22:00:03 | [diff] [blame] | 1314 | |
| 1315 | // Verify that sandbox flags are cleared properly for the root FrameTreeNode |
| 1316 | // and RenderFrameHost. |
arthursonzogni | b93a447 | 2020-04-10 07:38:00 | [diff] [blame] | 1317 | EXPECT_EQ(network::mojom::WebSandboxFlags::kNone, |
Ian Clelland | 5cbaaf8 | 2017-11-27 22:00:03 | [diff] [blame] | 1318 | root->effective_frame_policy().sandbox_flags); |
arthursonzogni | b93a447 | 2020-04-10 07:38:00 | [diff] [blame] | 1319 | EXPECT_EQ(network::mojom::WebSandboxFlags::kNone, |
| 1320 | root->active_sandbox_flags()); |
| 1321 | EXPECT_EQ(network::mojom::WebSandboxFlags::kNone, |
Ian Clelland | 5cbaaf8 | 2017-11-27 22:00:03 | [diff] [blame] | 1322 | root->current_frame_host()->active_sandbox_flags()); |
| 1323 | } |
| 1324 | |
alexmos | f832a2f | 2015-01-27 22:44:03 | [diff] [blame] | 1325 | // Ensure that sandbox flags are correctly set when child frames are created. |
| 1326 | IN_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 Caballero | 15caeeb | 2021-10-27 09:57:55 | [diff] [blame] | 1332 | ->GetPrimaryFrameTree() |
| 1333 | .root(); |
alexmos | f832a2f | 2015-01-27 22:44:03 | [diff] [blame] | 1334 | |
| 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". |
arthursonzogni | b93a447 | 2020-04-10 07:38:00 | [diff] [blame] | 1340 | EXPECT_EQ(network::mojom::WebSandboxFlags::kNone, |
Ian Clelland | cdc4f31 | 2017-10-13 22:24:12 | [diff] [blame] | 1341 | root->effective_frame_policy().sandbox_flags); |
arthursonzogni | b93a447 | 2020-04-10 07:38:00 | [diff] [blame] | 1342 | EXPECT_EQ(network::mojom::WebSandboxFlags::kAll, |
Ian Clelland | cdc4f31 | 2017-10-13 22:24:12 | [diff] [blame] | 1343 | root->child_at(0)->effective_frame_policy().sandbox_flags); |
arthursonzogni | b93a447 | 2020-04-10 07:38:00 | [diff] [blame] | 1344 | EXPECT_EQ(network::mojom::WebSandboxFlags::kAll & |
| 1345 | ~network::mojom::WebSandboxFlags::kScripts & |
| 1346 | ~network::mojom::WebSandboxFlags::kAutomaticFeatures, |
Ian Clelland | cdc4f31 | 2017-10-13 22:24:12 | [diff] [blame] | 1347 | root->child_at(1)->effective_frame_policy().sandbox_flags); |
arthursonzogni | b93a447 | 2020-04-10 07:38:00 | [diff] [blame] | 1348 | EXPECT_EQ(network::mojom::WebSandboxFlags::kAll & |
| 1349 | ~network::mojom::WebSandboxFlags::kScripts & |
| 1350 | ~network::mojom::WebSandboxFlags::kAutomaticFeatures & |
| 1351 | ~network::mojom::WebSandboxFlags::kOrigin, |
Ian Clelland | cdc4f31 | 2017-10-13 22:24:12 | [diff] [blame] | 1352 | root->child_at(2)->effective_frame_policy().sandbox_flags); |
alexmos | f832a2f | 2015-01-27 22:44:03 | [diff] [blame] | 1353 | |
| 1354 | // Sandboxed frames should set a unique origin unless they have the |
| 1355 | // "allow-same-origin" directive. |
alexmos | 6e94010 | 2016-01-19 22:47:25 | [diff] [blame] | 1356 | EXPECT_EQ("null", root->child_at(0)->current_origin().Serialize()); |
| 1357 | EXPECT_EQ("null", root->child_at(1)->current_origin().Serialize()); |
Mike West | 800532c | 2021-10-14 09:26:52 | [diff] [blame] | 1358 | EXPECT_EQ(main_url.DeprecatedGetOriginAsURL().spec(), |
alexmos | 6e94010 | 2016-01-19 22:47:25 | [diff] [blame] | 1359 | root->child_at(2)->current_origin().Serialize() + "/"); |
alexmos | f832a2f | 2015-01-27 22:44:03 | [diff] [blame] | 1360 | |
| 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); |
arthursonzogni | b93a447 | 2020-04-10 07:38:00 | [diff] [blame] | 1364 | EXPECT_EQ(network::mojom::WebSandboxFlags::kAll, |
Ian Clelland | cdc4f31 | 2017-10-13 22:24:12 | [diff] [blame] | 1365 | root->child_at(0)->effective_frame_policy().sandbox_flags); |
alexmos | f832a2f | 2015-01-27 22:44:03 | [diff] [blame] | 1366 | } |
| 1367 | |
Ian Clelland | 5cbaaf8 | 2017-11-27 22:00:03 | [diff] [blame] | 1368 | // 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. |
| 1371 | IN_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 Caballero | 15caeeb | 2021-10-27 09:57:55 | [diff] [blame] | 1378 | ->GetPrimaryFrameTree() |
| 1379 | .root(); |
Ian Clelland | 5cbaaf8 | 2017-11-27 22:00:03 | [diff] [blame] | 1380 | |
| 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". |
arthursonzogni | b93a447 | 2020-04-10 07:38:00 | [diff] [blame] | 1389 | EXPECT_EQ(network::mojom::WebSandboxFlags::kNone, |
Ian Clelland | 5cbaaf8 | 2017-11-27 22:00:03 | [diff] [blame] | 1390 | root->effective_frame_policy().sandbox_flags); |
arthursonzogni | b93a447 | 2020-04-10 07:38:00 | [diff] [blame] | 1391 | EXPECT_EQ(network::mojom::WebSandboxFlags::kNone, |
| 1392 | root->active_sandbox_flags()); |
Ian Clelland | 5cbaaf8 | 2017-11-27 22:00:03 | [diff] [blame] | 1393 | EXPECT_EQ(root->active_sandbox_flags(), |
| 1394 | root->current_frame_host()->active_sandbox_flags()); |
arthursonzogni | b93a447 | 2020-04-10 07:38:00 | [diff] [blame] | 1395 | EXPECT_EQ(network::mojom::WebSandboxFlags::kNone, |
Ian Clelland | 5cbaaf8 | 2017-11-27 22:00:03 | [diff] [blame] | 1396 | root->child_at(0)->effective_frame_policy().sandbox_flags); |
Arthur Sonzogni | 4ba3104f | 2022-03-09 09:04:39 | [diff] [blame] | 1397 | 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 Clelland | 5cbaaf8 | 2017-11-27 22:00:03 | [diff] [blame] | 1405 | EXPECT_EQ(root->child_at(0)->active_sandbox_flags(), |
| 1406 | root->child_at(0)->current_frame_host()->active_sandbox_flags()); |
arthursonzogni | b93a447 | 2020-04-10 07:38:00 | [diff] [blame] | 1407 | 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 Clelland | 5cbaaf8 | 2017-11-27 22:00:03 | [diff] [blame] | 1412 | root->child_at(1)->effective_frame_policy().sandbox_flags); |
arthursonzogni | b93a447 | 2020-04-10 07:38:00 | [diff] [blame] | 1413 | EXPECT_EQ(network::mojom::WebSandboxFlags::kAll & |
| 1414 | ~network::mojom::WebSandboxFlags::kScripts & |
| 1415 | ~network::mojom::WebSandboxFlags::kAutomaticFeatures & |
| 1416 | ~network::mojom::WebSandboxFlags::kPointerLock, |
Ian Clelland | 5cbaaf8 | 2017-11-27 22:00:03 | [diff] [blame] | 1417 | 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 Anforowicz | 69c25dfd | 2020-11-12 21:50:20 | [diff] [blame] | 1425 | EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(0), frame_url)); |
arthursonzogni | b93a447 | 2020-04-10 07:38:00 | [diff] [blame] | 1426 | EXPECT_EQ(network::mojom::WebSandboxFlags::kNone, |
Ian Clelland | 5cbaaf8 | 2017-11-27 22:00:03 | [diff] [blame] | 1427 | root->child_at(0)->effective_frame_policy().sandbox_flags); |
arthursonzogni | b93a447 | 2020-04-10 07:38:00 | [diff] [blame] | 1428 | EXPECT_EQ(network::mojom::WebSandboxFlags::kNone, |
Ian Clelland | 5cbaaf8 | 2017-11-27 22:00:03 | [diff] [blame] | 1429 | 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 Anforowicz | 69c25dfd | 2020-11-12 21:50:20 | [diff] [blame] | 1433 | EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(1), frame_url)); |
arthursonzogni | b93a447 | 2020-04-10 07:38:00 | [diff] [blame] | 1434 | 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 Clelland | 5cbaaf8 | 2017-11-27 22:00:03 | [diff] [blame] | 1439 | root->child_at(1)->effective_frame_policy().sandbox_flags); |
arthursonzogni | b93a447 | 2020-04-10 07:38:00 | [diff] [blame] | 1440 | 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 Clelland | 5cbaaf8 | 2017-11-27 22:00:03 | [diff] [blame] | 1445 | 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 | |
alexmos | e201c7cd | 2015-06-10 17:14:21 | [diff] [blame] | 1450 | // 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. |
| 1452 | IN_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 Caballero | 15caeeb | 2021-10-27 09:57:55 | [diff] [blame] | 1458 | ->GetPrimaryFrameTree() |
| 1459 | .root(); |
alexmos | e201c7cd | 2015-06-10 17:14:21 | [diff] [blame] | 1460 | |
| 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 Carter | b7e7131 | 2018-08-03 23:36:13 | [diff] [blame] | 1464 | EXPECT_TRUE( |
| 1465 | ExecJs(root->child_at(0), JsReplace("window.open($1);", popup_url))); |
alexmos | e201c7cd | 2015-06-10 17:14:21 | [diff] [blame] | 1466 | Shell* new_shell = new_shell_observer.GetShell(); |
| 1467 | WebContents* new_contents = new_shell->web_contents(); |
Fergal Daly | f052233 | 2020-07-18 06:09:46 | [diff] [blame] | 1468 | EXPECT_TRUE(WaitForLoadStop(new_contents)); |
alexmos | e201c7cd | 2015-06-10 17:14:21 | [diff] [blame] | 1469 | |
| 1470 | // Check that the new window's opener points to the correct subframe on |
| 1471 | // original window. |
| 1472 | FrameTreeNode* popup_root = |
Carlos Caballero | 15caeeb | 2021-10-27 09:57:55 | [diff] [blame] | 1473 | static_cast<WebContentsImpl*>(new_contents)->GetPrimaryFrameTree().root(); |
alexmos | e201c7cd | 2015-06-10 17:14:21 | [diff] [blame] | 1474 | 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 Sharma | c4f56158 | 2018-11-15 15:58:39 | [diff] [blame] | 1481 | // Tests that the user activation bits get cleared when a same-site document is |
| 1482 | // installed in the frame. |
| 1483 | IN_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 Caballero | 15caeeb | 2021-10-27 09:57:55 | [diff] [blame] | 1490 | ->GetPrimaryFrameTree() |
| 1491 | .root(); |
Shivani Sharma | c4f56158 | 2018-11-15 15:58:39 | [diff] [blame] | 1492 | |
Mustaq Ahmed | 4baa9a6e8 | 2019-12-20 23:43:46 | [diff] [blame] | 1493 | EXPECT_FALSE(root->HasStickyUserActivation()); |
Shivani Sharma | c4f56158 | 2018-11-15 15:58:39 | [diff] [blame] | 1494 | EXPECT_FALSE(root->HasTransientUserActivation()); |
| 1495 | |
| 1496 | // Set the user activation bits. |
| 1497 | root->UpdateUserActivationState( |
Mustaq Ahmed | dc195e5b | 2020-08-04 18:45:11 | [diff] [blame] | 1498 | blink::mojom::UserActivationUpdateType::kNotifyActivation, |
| 1499 | blink::mojom::UserActivationNotificationType::kTest); |
Mustaq Ahmed | 4baa9a6e8 | 2019-12-20 23:43:46 | [diff] [blame] | 1500 | EXPECT_TRUE(root->HasStickyUserActivation()); |
Shivani Sharma | c4f56158 | 2018-11-15 15:58:39 | [diff] [blame] | 1501 | 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 Ahmed | 4baa9a6e8 | 2019-12-20 23:43:46 | [diff] [blame] | 1508 | EXPECT_FALSE(root->HasStickyUserActivation()); |
Shivani Sharma | c4f56158 | 2018-11-15 15:58:39 | [diff] [blame] | 1509 | EXPECT_FALSE(root->HasTransientUserActivation()); |
| 1510 | } |
| 1511 | |
[email protected] | 82307f6b | 2014-08-07 03:30:12 | [diff] [blame] | 1512 | class CrossProcessFrameTreeBrowserTest : public ContentBrowserTest { |
| 1513 | public: |
Fergal Daly | a1d56997 | 2021-03-16 03:24:53 | [diff] [blame] | 1514 | CrossProcessFrameTreeBrowserTest() = default; |
[email protected] | 82307f6b | 2014-08-07 03:30:12 | [diff] [blame] | 1515 | |
Peter Boström | 9b03653 | 2021-10-28 23:37:28 | [diff] [blame] | 1516 | CrossProcessFrameTreeBrowserTest(const CrossProcessFrameTreeBrowserTest&) = |
| 1517 | delete; |
| 1518 | CrossProcessFrameTreeBrowserTest& operator=( |
| 1519 | const CrossProcessFrameTreeBrowserTest&) = delete; |
| 1520 | |
dcheng | c2282aa | 2014-10-21 12:07:58 | [diff] [blame] | 1521 | void SetUpCommandLine(base::CommandLine* command_line) override { |
nick | d30fd96 | 2015-07-27 21:51:08 | [diff] [blame] | 1522 | IsolateAllSitesForTesting(command_line); |
[email protected] | 82307f6b | 2014-08-07 03:30:12 | [diff] [blame] | 1523 | } |
| 1524 | |
alexmos | 478dcbb | 2014-12-10 21:24:46 | [diff] [blame] | 1525 | void SetUpOnMainThread() override { |
| 1526 | host_resolver()->AddRule("*", "127.0.0.1"); |
alexmos | 478dcbb | 2014-12-10 21:24:46 | [diff] [blame] | 1527 | SetupCrossSiteRedirector(embedded_test_server()); |
martijn | 12d2dad | 2016-11-11 10:59:27 | [diff] [blame] | 1528 | ASSERT_TRUE(embedded_test_server()->Start()); |
alexmos | 478dcbb | 2014-12-10 21:24:46 | [diff] [blame] | 1529 | } |
[email protected] | 82307f6b | 2014-08-07 03:30:12 | [diff] [blame] | 1530 | }; |
| 1531 | |
| 1532 | // Ensure that we can complete a cross-process subframe navigation. |
[email protected] | 82307f6b | 2014-08-07 03:30:12 | [diff] [blame] | 1533 | IN_PROC_BROWSER_TEST_F(CrossProcessFrameTreeBrowserTest, |
nasko | 83fe5dc | 2015-07-01 16:34:19 | [diff] [blame] | 1534 | CreateCrossProcessSubframeProxies) { |
alexmos | 478dcbb | 2014-12-10 21:24:46 | [diff] [blame] | 1535 | GURL main_url(embedded_test_server()->GetURL("/site_per_process_main.html")); |
Alex Moshchuk | aa95adf5 | 2019-08-13 00:02:02 | [diff] [blame] | 1536 | EXPECT_TRUE(NavigateToURL(shell(), main_url)); |
[email protected] | 82307f6b | 2014-08-07 03:30:12 | [diff] [blame] | 1537 | |
| 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 Caballero | 15caeeb | 2021-10-27 09:57:55 | [diff] [blame] | 1540 | ->GetPrimaryFrameTree() |
| 1541 | .root(); |
[email protected] | 82307f6b | 2014-08-07 03:30:12 | [diff] [blame] | 1542 | |
[email protected] | 14522ce4 | 2014-08-08 18:56:16 | [diff] [blame] | 1543 | // There should not be a proxy for the root's own SiteInstance. |
Sharon Yang | 7424bda | 2021-11-04 20:27:43 | [diff] [blame] | 1544 | SiteInstanceImpl* root_instance = |
| 1545 | root->current_frame_host()->GetSiteInstance(); |
Harkiran Bolaria | d22a1dca | 2022-02-22 17:01:12 | [diff] [blame] | 1546 | EXPECT_FALSE(root->current_frame_host() |
| 1547 | ->browsing_context_state() |
| 1548 | ->GetRenderFrameProxyHost(root_instance->group())); |
[email protected] | 14522ce4 | 2014-08-08 18:56:16 | [diff] [blame] | 1549 | |
[email protected] | 82307f6b | 2014-08-07 03:30:12 | [diff] [blame] | 1550 | // Load same-site page into iframe. |
alexmos | 478dcbb | 2014-12-10 21:24:46 | [diff] [blame] | 1551 | GURL http_url(embedded_test_server()->GetURL("/title1.html")); |
Lukasz Anforowicz | 69c25dfd | 2020-11-12 21:50:20 | [diff] [blame] | 1552 | EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(0), http_url)); |
[email protected] | 82307f6b | 2014-08-07 03:30:12 | [diff] [blame] | 1553 | |
[email protected] | 82307f6b | 2014-08-07 03:30:12 | [diff] [blame] | 1554 | // Load cross-site page into iframe. |
alexmos | 478dcbb | 2014-12-10 21:24:46 | [diff] [blame] | 1555 | GURL cross_site_url( |
| 1556 | embedded_test_server()->GetURL("foo.com", "/title2.html")); |
Lukasz Anforowicz | 69c25dfd | 2020-11-12 21:50:20 | [diff] [blame] | 1557 | EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(0), cross_site_url)); |
[email protected] | 82307f6b | 2014-08-07 03:30:12 | [diff] [blame] | 1558 | |
| 1559 | // Ensure that we have created a new process for the subframe. |
nasko | e6edde3 | 2014-10-17 15:36:48 | [diff] [blame] | 1560 | ASSERT_EQ(2U, root->child_count()); |
[email protected] | 82307f6b | 2014-08-07 03:30:12 | [diff] [blame] | 1561 | FrameTreeNode* child = root->child_at(0); |
Sharon Yang | 7424bda | 2021-11-04 20:27:43 | [diff] [blame] | 1562 | SiteInstanceImpl* child_instance = |
| 1563 | child->current_frame_host()->GetSiteInstance(); |
[email protected] | 82307f6b | 2014-08-07 03:30:12 | [diff] [blame] | 1564 | RenderViewHost* rvh = child->current_frame_host()->render_view_host(); |
| 1565 | RenderProcessHost* rph = child->current_frame_host()->GetProcess(); |
| 1566 | |
Dave Tapuska | 327c06c9 | 2022-06-13 20:31:51 | [diff] [blame] | 1567 | EXPECT_NE(shell()->web_contents()->GetPrimaryMainFrame()->GetRenderViewHost(), |
| 1568 | rvh); |
[email protected] | 14522ce4 | 2014-08-08 18:56:16 | [diff] [blame] | 1569 | EXPECT_NE(shell()->web_contents()->GetSiteInstance(), child_instance); |
Dave Tapuska | 327c06c9 | 2022-06-13 20:31:51 | [diff] [blame] | 1570 | EXPECT_NE(shell()->web_contents()->GetPrimaryMainFrame()->GetProcess(), rph); |
[email protected] | 82307f6b | 2014-08-07 03:30:12 | [diff] [blame] | 1571 | |
| 1572 | // Ensure that the root node has a proxy for the child node's SiteInstance. |
Harkiran Bolaria | d22a1dca | 2022-02-22 17:01:12 | [diff] [blame] | 1573 | EXPECT_TRUE(root->current_frame_host() |
| 1574 | ->browsing_context_state() |
| 1575 | ->GetRenderFrameProxyHost(child_instance->group())); |
[email protected] | 82307f6b | 2014-08-07 03:30:12 | [diff] [blame] | 1576 | |
| 1577 | // Also ensure that the child has a proxy for the root node's SiteInstance. |
Harkiran Bolaria | d22a1dca | 2022-02-22 17:01:12 | [diff] [blame] | 1578 | EXPECT_TRUE(child->current_frame_host() |
| 1579 | ->browsing_context_state() |
| 1580 | ->GetRenderFrameProxyHost(root_instance->group())); |
[email protected] | 14522ce4 | 2014-08-08 18:56:16 | [diff] [blame] | 1581 | |
| 1582 | // The nodes should not have proxies for their own SiteInstance. |
Harkiran Bolaria | d22a1dca | 2022-02-22 17:01:12 | [diff] [blame] | 1583 | 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())); |
creis | e42f2a5 | 2014-09-18 18:14:57 | [diff] [blame] | 1589 | |
| 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] | 82307f6b | 2014-08-07 03:30:12 | [diff] [blame] | 1597 | } |
| 1598 | |
alexmos | bc7eafa | 2014-12-06 01:38:09 | [diff] [blame] | 1599 | IN_PROC_BROWSER_TEST_F(CrossProcessFrameTreeBrowserTest, |
Nasko Oskov | 0391210 | 2019-01-11 00:21:32 | [diff] [blame] | 1600 | OriginSetOnNavigations) { |
alexmos | 478dcbb | 2014-12-10 21:24:46 | [diff] [blame] | 1601 | GURL main_url(embedded_test_server()->GetURL("/site_per_process_main.html")); |
alexmos | bc7eafa | 2014-12-06 01:38:09 | [diff] [blame] | 1602 | 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 Caballero | 15caeeb | 2021-10-27 09:57:55 | [diff] [blame] | 1606 | ->GetPrimaryFrameTree() |
| 1607 | .root(); |
alexmos | bc7eafa | 2014-12-06 01:38:09 | [diff] [blame] | 1608 | |
creis | abdd2bd6 | 2015-11-21 01:14:58 | [diff] [blame] | 1609 | EXPECT_EQ(root->current_origin().Serialize() + '/', |
Mike West | 800532c | 2021-10-14 09:26:52 | [diff] [blame] | 1610 | main_url.DeprecatedGetOriginAsURL().spec()); |
alexmos | bc7eafa | 2014-12-06 01:38:09 | [diff] [blame] | 1611 | |
| 1612 | // First frame is an about:blank frame. Check that its origin is correctly |
| 1613 | // inherited from the parent. |
creis | abdd2bd6 | 2015-11-21 01:14:58 | [diff] [blame] | 1614 | EXPECT_EQ(root->child_at(0)->current_origin().Serialize() + '/', |
Mike West | 800532c | 2021-10-14 09:26:52 | [diff] [blame] | 1615 | main_url.DeprecatedGetOriginAsURL().spec()); |
alexmos | bc7eafa | 2014-12-06 01:38:09 | [diff] [blame] | 1616 | |
| 1617 | // Second frame loads a same-site page. Its origin should also be the same |
| 1618 | // as the parent. |
creis | abdd2bd6 | 2015-11-21 01:14:58 | [diff] [blame] | 1619 | EXPECT_EQ(root->child_at(1)->current_origin().Serialize() + '/', |
Mike West | 800532c | 2021-10-14 09:26:52 | [diff] [blame] | 1620 | main_url.DeprecatedGetOriginAsURL().spec()); |
alexmos | bc7eafa | 2014-12-06 01:38:09 | [diff] [blame] | 1621 | |
alexmos | bc7eafa | 2014-12-06 01:38:09 | [diff] [blame] | 1622 | // Load cross-site page into the first frame. |
alexmos | 478dcbb | 2014-12-10 21:24:46 | [diff] [blame] | 1623 | GURL cross_site_url( |
| 1624 | embedded_test_server()->GetURL("foo.com", "/title2.html")); |
Lukasz Anforowicz | 69c25dfd | 2020-11-12 21:50:20 | [diff] [blame] | 1625 | EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(0), cross_site_url)); |
alexmos | bc7eafa | 2014-12-06 01:38:09 | [diff] [blame] | 1626 | |
creis | abdd2bd6 | 2015-11-21 01:14:58 | [diff] [blame] | 1627 | EXPECT_EQ(root->child_at(0)->current_origin().Serialize() + '/', |
Mike West | 800532c | 2021-10-14 09:26:52 | [diff] [blame] | 1628 | cross_site_url.DeprecatedGetOriginAsURL().spec()); |
alexmos | bc7eafa | 2014-12-06 01:38:09 | [diff] [blame] | 1629 | |
| 1630 | // The root's origin shouldn't have changed. |
creis | abdd2bd6 | 2015-11-21 01:14:58 | [diff] [blame] | 1631 | EXPECT_EQ(root->current_origin().Serialize() + '/', |
Mike West | 800532c | 2021-10-14 09:26:52 | [diff] [blame] | 1632 | main_url.DeprecatedGetOriginAsURL().spec()); |
alexmos | bc7eafa | 2014-12-06 01:38:09 | [diff] [blame] | 1633 | |
Nasko Oskov | 0391210 | 2019-01-11 00:21:32 | [diff] [blame] | 1634 | { |
| 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 | } |
alexmos | bc7eafa | 2014-12-06 01:38:09 | [diff] [blame] | 1641 | |
| 1642 | // Navigating to a data URL should set a unique origin. This is represented |
Nasko Oskov | 0391210 | 2019-01-11 00:21:32 | [diff] [blame] | 1643 | // 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. |
creis | abdd2bd6 | 2015-11-21 01:14:58 | [diff] [blame] | 1646 | EXPECT_EQ(root->child_at(1)->current_origin().Serialize(), "null"); |
Nasko Oskov | 0391210 | 2019-01-11 00:21:32 | [diff] [blame] | 1647 | 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. |
| 1760 | IN_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 Caballero | 15caeeb | 2021-10-27 09:57:55 | [diff] [blame] | 1770 | ->GetPrimaryFrameTree() |
| 1771 | .root(); |
Nasko Oskov | 0391210 | 2019-01-11 00:21:32 | [diff] [blame] | 1772 | EXPECT_TRUE(root->current_origin().opaque()); |
Nasko Oskov | 5511938 | 2020-01-17 18:22:18 | [diff] [blame] | 1773 | EXPECT_FALSE( |
| 1774 | root->current_origin().GetTupleOrPrecursorTupleIfOpaque().IsValid()); |
Nasko Oskov | 0391210 | 2019-01-11 00:21:32 | [diff] [blame] | 1775 | 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 Amni | c736785 | 2022-11-07 17:10:40 | [diff] [blame] | 1800 | EXPECT_FALSE( |
| 1801 | child->current_origin().GetTupleOrPrecursorTupleIfOpaque().IsValid()); |
Nasko Oskov | 0391210 | 2019-01-11 00:21:32 | [diff] [blame] | 1802 | |
| 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 Amni | c736785 | 2022-11-07 17:10:40 | [diff] [blame] | 1822 | // 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 Oskov | 0391210 | 2019-01-11 00:21:32 | [diff] [blame] | 1825 | { |
| 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 Amni | c736785 | 2022-11-07 17:10:40 | [diff] [blame] | 1832 | EXPECT_NE(blob_origin, child->current_origin()); |
Nasko Oskov | 0391210 | 2019-01-11 00:21:32 | [diff] [blame] | 1833 | EXPECT_EQ(root->current_origin().GetTupleOrPrecursorTupleIfOpaque(), |
| 1834 | child->current_origin().GetTupleOrPrecursorTupleIfOpaque()); |
Rakina Zata Amni | c736785 | 2022-11-07 17:10:40 | [diff] [blame] | 1835 | EXPECT_FALSE( |
| 1836 | child->current_origin().GetTupleOrPrecursorTupleIfOpaque().IsValid()); |
Nasko Oskov | 0391210 | 2019-01-11 00:21:32 | [diff] [blame] | 1837 | } |
| 1838 | |
| 1839 | // Test to verify that about:blank iframe, which is a child of a sandboxed |
| 1840 | // iframe is not considered same origin, but precursor information is preserved |
| 1841 | // in its origin. |
| 1842 | IN_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 Caballero | 15caeeb | 2021-10-27 09:57:55 | [diff] [blame] | 1851 | ->GetPrimaryFrameTree() |
| 1852 | .root(); |
Nasko Oskov | 0391210 | 2019-01-11 00:21:32 | [diff] [blame] | 1853 | 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()); |
alexmos | bc7eafa | 2014-12-06 01:38:09 | [diff] [blame] | 1893 | } |
| 1894 | |
Ian Clelland | 5cbaaf8 | 2017-11-27 22:00:03 | [diff] [blame] | 1895 | // Ensure that a popup opened from a sandboxed main frame inherits sandbox flags |
| 1896 | // from its opener. |
| 1897 | IN_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 Caballero | 15caeeb | 2021-10-27 09:57:55 | [diff] [blame] | 1905 | ->GetPrimaryFrameTree() |
| 1906 | .root(); |
Ian Clelland | 5cbaaf8 | 2017-11-27 22:00:03 | [diff] [blame] | 1907 | |
| 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 Caballero | 15caeeb | 2021-10-27 09:57:55 | [diff] [blame] | 1917 | static_cast<WebContentsImpl*>(new_contents)->GetPrimaryFrameTree().root(); |
arthursonzogni | b93a447 | 2020-04-10 07:38:00 | [diff] [blame] | 1918 | network::mojom::WebSandboxFlags main_frame_sandbox_flags = |
Ian Clelland | 5cbaaf8 | 2017-11-27 22:00:03 | [diff] [blame] | 1919 | root->current_frame_host()->active_sandbox_flags(); |
Arthur Sonzogni | 4ba3104f | 2022-03-09 09:04:39 | [diff] [blame] | 1920 | 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 Clelland | 5cbaaf8 | 2017-11-27 22:00:03 | [diff] [blame] | 1928 | |
| 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 Sharma | c4f56158 | 2018-11-15 15:58:39 | [diff] [blame] | 1936 | // Tests that the user activation bits get cleared when a cross-site document is |
| 1937 | // installed in the frame. |
| 1938 | IN_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 Caballero | 15caeeb | 2021-10-27 09:57:55 | [diff] [blame] | 1945 | ->GetPrimaryFrameTree() |
| 1946 | .root(); |
Shivani Sharma | c4f56158 | 2018-11-15 15:58:39 | [diff] [blame] | 1947 | |
Mustaq Ahmed | 4baa9a6e8 | 2019-12-20 23:43:46 | [diff] [blame] | 1948 | EXPECT_FALSE(root->HasStickyUserActivation()); |
Shivani Sharma | c4f56158 | 2018-11-15 15:58:39 | [diff] [blame] | 1949 | EXPECT_FALSE(root->HasTransientUserActivation()); |
| 1950 | |
| 1951 | // Set the user activation bits. |
| 1952 | root->UpdateUserActivationState( |
Mustaq Ahmed | dc195e5b | 2020-08-04 18:45:11 | [diff] [blame] | 1953 | blink::mojom::UserActivationUpdateType::kNotifyActivation, |
| 1954 | blink::mojom::UserActivationNotificationType::kTest); |
Mustaq Ahmed | 4baa9a6e8 | 2019-12-20 23:43:46 | [diff] [blame] | 1955 | EXPECT_TRUE(root->HasStickyUserActivation()); |
Shivani Sharma | c4f56158 | 2018-11-15 15:58:39 | [diff] [blame] | 1956 | 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 Ahmed | 4baa9a6e8 | 2019-12-20 23:43:46 | [diff] [blame] | 1964 | EXPECT_FALSE(root->HasStickyUserActivation()); |
Shivani Sharma | c4f56158 | 2018-11-15 15:58:39 | [diff] [blame] | 1965 | EXPECT_FALSE(root->HasTransientUserActivation()); |
| 1966 | } |
| 1967 | |
Dave Tapuska | 708c273 | 2022-09-23 22:47:49 | [diff] [blame] | 1968 | class 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. |
| 1995 | IN_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 | |
nick | db193a1 | 2016-09-09 23:09:23 | [diff] [blame] | 2017 | // FrameTreeBrowserTest variant where we isolate http://*.is, Iceland's top |
nasko | abed2a5 | 2017-05-03 05:10:17 | [diff] [blame] | 2018 | // 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 |
nick | db193a1 | 2016-09-09 23:09:23 | [diff] [blame] | 2020 | // extension process, is a special place with magical powers; we want to protect |
| 2021 | // it from outsiders. |
| 2022 | class IsolateIcelandFrameTreeBrowserTest : public ContentBrowserTest { |
| 2023 | public: |
Fergal Daly | a1d56997 | 2021-03-16 03:24:53 | [diff] [blame] | 2024 | IsolateIcelandFrameTreeBrowserTest() = default; |
nick | db193a1 | 2016-09-09 23:09:23 | [diff] [blame] | 2025 | |
Peter Boström | 9b03653 | 2021-10-28 23:37:28 | [diff] [blame] | 2026 | IsolateIcelandFrameTreeBrowserTest( |
| 2027 | const IsolateIcelandFrameTreeBrowserTest&) = delete; |
| 2028 | IsolateIcelandFrameTreeBrowserTest& operator=( |
| 2029 | const IsolateIcelandFrameTreeBrowserTest&) = delete; |
| 2030 | |
nick | db193a1 | 2016-09-09 23:09:23 | [diff] [blame] | 2031 | void SetUpCommandLine(base::CommandLine* command_line) override { |
Lukasz Anforowicz | e7c87d1 | 2018-11-03 02:53:34 | [diff] [blame] | 2032 | // Blink suppresses navigations to blob URLs of origins different from the |
alexis.menard | 83f1b6d | 2017-05-17 19:37:33 | [diff] [blame] | 2033 | // 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 Anforowicz | e7c87d1 | 2018-11-03 02:53:34 | [diff] [blame] | 2036 | |
| 2037 | // ProcessSwitchForIsolatedBlob test below requires that one of URLs used in |
Lukasz Anforowicz | 2542093 | 2018-12-18 20:59:22 | [diff] [blame] | 2038 | // the test (blob:http://b.is/) belongs to an isolated origin. |
| 2039 | command_line->AppendSwitchASCII(switches::kIsolateOrigins, "http://b.is/"); |
nick | db193a1 | 2016-09-09 23:09:23 | [diff] [blame] | 2040 | } |
| 2041 | |
| 2042 | void SetUpOnMainThread() override { |
| 2043 | host_resolver()->AddRule("*", "127.0.0.1"); |
nick | db193a1 | 2016-09-09 23:09:23 | [diff] [blame] | 2044 | SetupCrossSiteRedirector(embedded_test_server()); |
martijn | 12d2dad | 2016-11-11 10:59:27 | [diff] [blame] | 2045 | ASSERT_TRUE(embedded_test_server()->Start()); |
nick | db193a1 | 2016-09-09 23:09:23 | [diff] [blame] | 2046 | } |
nick | db193a1 | 2016-09-09 23:09:23 | [diff] [blame] | 2047 | }; |
| 2048 | |
| 2049 | // Regression test for https://crbug.com/644966 |
| 2050 | IN_PROC_BROWSER_TEST_F(IsolateIcelandFrameTreeBrowserTest, |
| 2051 | ProcessSwitchForIsolatedBlob) { |
nick | db193a1 | 2016-09-09 23:09:23 | [diff] [blame] | 2052 | // Set up an iframe. |
| 2053 | WebContents* contents = shell()->web_contents(); |
| 2054 | FrameTreeNode* root = |
Carlos Caballero | 15caeeb | 2021-10-27 09:57:55 | [diff] [blame] | 2055 | static_cast<WebContentsImpl*>(contents)->GetPrimaryFrameTree().root(); |
nick | db193a1 | 2016-09-09 23:09:23 | [diff] [blame] | 2056 | GURL main_url(embedded_test_server()->GetURL( |
nick | 07fd7e1 | 2016-09-12 18:54:06 | [diff] [blame] | 2057 | "a.com", "/cross_site_iframe_factory.html?a(a)")); |
nick | db193a1 | 2016-09-09 23:09:23 | [diff] [blame] | 2058 | 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 Oskov | 0401d81 | 2021-02-05 22:20:08 | [diff] [blame] | 2062 | // http://b.is or error pages, depending on policy. |
Nick Carter | b7e7131 | 2018-08-03 23:36:13 | [diff] [blame] | 2063 | 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 Anforowicz | 2542093 | 2018-12-18 20:59:22 | [diff] [blame] | 2070 | " iframe_element.src = 'blob:http://b.is/';" |
Nick Carter | b7e7131 | 2018-08-03 23:36:13 | [diff] [blame] | 2071 | "});")); |
Fergal Daly | f052233 | 2020-07-18 06:09:46 | [diff] [blame] | 2072 | EXPECT_TRUE(WaitForLoadStop(contents)); |
nick | db193a1 | 2016-09-09 23:09:23 | [diff] [blame] | 2073 | |
| 2074 | // Make sure we did a process transfer back to "b.is". |
Aaron Colwell | e953e56 | 2019-07-24 16:47:36 | [diff] [blame] | 2075 | const std::string kExpectedSiteURL = |
Sharon Yang | 47453bb | 2025-04-25 14:48:09 | [diff] [blame] | 2076 | AreStrictSiteInstancesEnabled() |
Sharon Yang | 0227922 | 2025-01-15 19:09:19 | [diff] [blame] | 2077 | ? "http://a.com/" |
| 2078 | : SiteInstanceImpl::GetDefaultSiteURL().spec(); |
Nasko Oskov | 0401d81 | 2021-02-05 22:20:08 | [diff] [blame] | 2079 | const std::string kExpectedSubframeSiteURL = |
| 2080 | SiteIsolationPolicy::IsErrorPageIsolationEnabled(/*in_main_frame*/ false) |
| 2081 | ? "chrome-error://chromewebdata/" |
| 2082 | : "http://b.is/"; |
Aaron Colwell | e953e56 | 2019-07-24 16:47:36 | [diff] [blame] | 2083 | EXPECT_EQ(base::StringPrintf(" Site A ------------ proxies for B\n" |
| 2084 | " +--Site B ------- proxies for A\n" |
| 2085 | "Where A = %s\n" |
Nasko Oskov | 0401d81 | 2021-02-05 22:20:08 | [diff] [blame] | 2086 | " B = %s", |
| 2087 | kExpectedSiteURL.c_str(), |
| 2088 | kExpectedSubframeSiteURL.c_str()), |
Fergal Daly | 79f4429 | 2020-12-01 02:30:48 | [diff] [blame] | 2089 | DepictFrameTree(*root)); |
nick | db193a1 | 2016-09-09 23:09:23 | [diff] [blame] | 2090 | } |
| 2091 | |
Arthur Sonzogni | 6445759 | 2022-11-22 11:08:59 | [diff] [blame] | 2092 | class FrameTreeCredentiallessIframeBrowserTest : public FrameTreeBrowserTest { |
Antonio Sartori | 5abc8de | 2021-07-13 08:42:47 | [diff] [blame] | 2093 | public: |
Arthur Sonzogni | 6445759 | 2022-11-22 11:08:59 | [diff] [blame] | 2094 | FrameTreeCredentiallessIframeBrowserTest() = default; |
Antonio Sartori | 5abc8de | 2021-07-13 08:42:47 | [diff] [blame] | 2095 | |
| 2096 | void SetUpCommandLine(base::CommandLine* command_line) override { |
Antonio Sartori | 753cd6d | 2021-07-23 08:34:55 | [diff] [blame] | 2097 | command_line->AppendSwitch(switches::kEnableBlinkTestFeatures); |
Antonio Sartori | 5abc8de | 2021-07-13 08:42:47 | [diff] [blame] | 2098 | } |
| 2099 | }; |
| 2100 | |
Arthur Sonzogni | 6445759 | 2022-11-22 11:08:59 | [diff] [blame] | 2101 | // Tests the mojo propagation of the 'credentialless' attribute to the browser. |
| 2102 | IN_PROC_BROWSER_TEST_F(FrameTreeCredentiallessIframeBrowserTest, |
Antonio Sartori | 5abc8de | 2021-07-13 08:42:47 | [diff] [blame] | 2103 | 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 Caballero | 15caeeb | 2021-10-27 09:57:55 | [diff] [blame] | 2108 | ->GetPrimaryFrameTree() |
| 2109 | .root(); |
Antonio Sartori | 5abc8de | 2021-07-13 08:42:47 | [diff] [blame] | 2110 | |
Arthur Sonzogni | 6445759 | 2022-11-22 11:08:59 | [diff] [blame] | 2111 | // Not setting the attribute => the iframe is not credentialless. |
Antonio Sartori | 5abc8de | 2021-07-13 08:42:47 | [diff] [blame] | 2112 | EXPECT_TRUE(ExecJs(root, |
| 2113 | "var f = document.createElement('iframe');" |
| 2114 | "document.body.appendChild(f);")); |
| 2115 | EXPECT_EQ(1U, root->child_count()); |
Miyoung Shin | c9ff481 | 2023-01-05 08:58:05 | [diff] [blame] | 2116 | EXPECT_FALSE(root->child_at(0)->Credentialless()); |
Yifan Luo | e1a2e05d | 2022-01-12 16:47:59 | [diff] [blame] | 2117 | EXPECT_EQ(false, EvalJs(root->child_at(0)->current_frame_host(), |
Arthur Sonzogni | 6445759 | 2022-11-22 11:08:59 | [diff] [blame] | 2118 | "window.credentialless")); |
Antonio Sartori | 5abc8de | 2021-07-13 08:42:47 | [diff] [blame] | 2119 | |
Arthur Sonzogni | 6445759 | 2022-11-22 11:08:59 | [diff] [blame] | 2120 | // Setting the attribute on the iframe element makes the iframe |
| 2121 | // credentialless. |
Antonio Sartori | 5abc8de | 2021-07-13 08:42:47 | [diff] [blame] | 2122 | EXPECT_TRUE(ExecJs(root, |
| 2123 | "var d = document.createElement('div');" |
Arthur Sonzogni | 6445759 | 2022-11-22 11:08:59 | [diff] [blame] | 2124 | "d.innerHTML = '<iframe credentialless></iframe>';" |
Antonio Sartori | 5abc8de | 2021-07-13 08:42:47 | [diff] [blame] | 2125 | "document.body.appendChild(d);")); |
| 2126 | EXPECT_EQ(2U, root->child_count()); |
Miyoung Shin | c9ff481 | 2023-01-05 08:58:05 | [diff] [blame] | 2127 | EXPECT_TRUE(root->child_at(1)->Credentialless()); |
Yifan Luo | 40086747 | 2022-05-19 09:34:20 | [diff] [blame] | 2128 | EXPECT_EQ(true, EvalJs(root->child_at(1)->current_frame_host(), |
Arthur Sonzogni | 6445759 | 2022-11-22 11:08:59 | [diff] [blame] | 2129 | "window.credentialless")); |
Antonio Sartori | 5abc8de | 2021-07-13 08:42:47 | [diff] [blame] | 2130 | |
| 2131 | // Setting the attribute via javascript works. |
| 2132 | EXPECT_TRUE(ExecJs(root, |
| 2133 | "var g = document.createElement('iframe');" |
Arthur Sonzogni | 6445759 | 2022-11-22 11:08:59 | [diff] [blame] | 2134 | "g.credentialless = true;" |
Antonio Sartori | 5abc8de | 2021-07-13 08:42:47 | [diff] [blame] | 2135 | "document.body.appendChild(g);")); |
| 2136 | EXPECT_EQ(3U, root->child_count()); |
Miyoung Shin | c9ff481 | 2023-01-05 08:58:05 | [diff] [blame] | 2137 | EXPECT_TRUE(root->child_at(2)->Credentialless()); |
Yifan Luo | 40086747 | 2022-05-19 09:34:20 | [diff] [blame] | 2138 | EXPECT_EQ(true, EvalJs(root->child_at(2)->current_frame_host(), |
Arthur Sonzogni | 6445759 | 2022-11-22 11:08:59 | [diff] [blame] | 2139 | "window.credentialless")); |
Antonio Sartori | 5abc8de | 2021-07-13 08:42:47 | [diff] [blame] | 2140 | |
Arthur Sonzogni | 6445759 | 2022-11-22 11:08:59 | [diff] [blame] | 2141 | EXPECT_TRUE(ExecJs(root, "g.credentialless = false;")); |
Miyoung Shin | c9ff481 | 2023-01-05 08:58:05 | [diff] [blame] | 2142 | EXPECT_FALSE(root->child_at(2)->Credentialless()); |
Yifan Luo | 40086747 | 2022-05-19 09:34:20 | [diff] [blame] | 2143 | EXPECT_EQ(true, EvalJs(root->child_at(2)->current_frame_host(), |
Arthur Sonzogni | 6445759 | 2022-11-22 11:08:59 | [diff] [blame] | 2144 | "window.credentialless")); |
Antonio Sartori | 5abc8de | 2021-07-13 08:42:47 | [diff] [blame] | 2145 | |
Arthur Sonzogni | 6445759 | 2022-11-22 11:08:59 | [diff] [blame] | 2146 | EXPECT_TRUE(ExecJs(root, "g.credentialless = true;")); |
Miyoung Shin | c9ff481 | 2023-01-05 08:58:05 | [diff] [blame] | 2147 | EXPECT_TRUE(root->child_at(2)->Credentialless()); |
Yifan Luo | 40086747 | 2022-05-19 09:34:20 | [diff] [blame] | 2148 | EXPECT_EQ(true, EvalJs(root->child_at(2)->current_frame_host(), |
Arthur Sonzogni | 6445759 | 2022-11-22 11:08:59 | [diff] [blame] | 2149 | "window.credentialless")); |
Antonio Sartori | 5abc8de | 2021-07-13 08:42:47 | [diff] [blame] | 2150 | } |
| 2151 | |
Andrew Verge | 7233e66 | 2025-05-13 13:09:23 | [diff] [blame] | 2152 | class FrameTreeLastSuccessfulOriginBrowserTest : public FrameTreeBrowserTest { |
| 2153 | public: |
| 2154 | FrameTreeLastSuccessfulOriginBrowserTest() = default; |
| 2155 | }; |
| 2156 | |
| 2157 | IN_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 | |
| 2174 | IN_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 | |
| 2203 | IN_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 | |
| 2215 | IN_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 Lukaszewicz | 54d8e43 | 2024-09-18 20:13:17 | [diff] [blame] | 2238 | INSTANTIATE_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 | |
| 2247 | INSTANTIATE_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 | |
| 2256 | INSTANTIATE_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] | 5f96f5a6 | 2014-01-10 00:05:11 | [diff] [blame] | 2265 | } // namespace content |