[email protected] | 5f96f5a6 | 2014-01-10 00:05:11 | [diff] [blame] | 1 | // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
Ken Rockot | 86bea1c | 2017-05-16 06:21:35 | [diff] [blame] | 5 | #include "base/command_line.h" |
avi | b734894 | 2015-12-25 20:57:10 | [diff] [blame] | 6 | #include "base/macros.h" |
Aaron Colwell | e953e56 | 2019-07-24 16:47:36 | [diff] [blame] | 7 | #include "base/strings/stringprintf.h" |
shivanigithub | f33e469c | 2021-08-02 13:52:20 | [diff] [blame] | 8 | #include "base/synchronization/lock.h" |
shivanigithub | 93878d0 | 2021-06-15 11:37:53 | [diff] [blame] | 9 | #include "base/test/scoped_feature_list.h" |
avi | b734894 | 2015-12-25 20:57:10 | [diff] [blame] | 10 | #include "build/build_config.h" |
Dominic Farolino | 316e793 | 2021-09-03 21:13:59 | [diff] [blame] | 11 | #include "content/browser/fenced_frame/fenced_frame.h" |
shivanigithub | 93878d0 | 2021-06-15 11:37:53 | [diff] [blame] | 12 | #include "content/browser/fenced_frame/fenced_frame_url_mapping.h" |
danakj | c492bf8 | 2020-09-09 20:02:44 | [diff] [blame] | 13 | #include "content/browser/renderer_host/frame_tree.h" |
| 14 | #include "content/browser/renderer_host/frame_tree_node.h" |
| 15 | #include "content/browser/renderer_host/render_frame_host_impl.h" |
[email protected] | 5f96f5a6 | 2014-01-10 00:05:11 | [diff] [blame] | 16 | #include "content/browser/renderer_host/render_view_host_impl.h" |
| 17 | #include "content/browser/web_contents/web_contents_impl.h" |
shivanigithub | f33e469c | 2021-08-02 13:52:20 | [diff] [blame] | 18 | #include "content/public/browser/browser_thread.h" |
[email protected] | 5f96f5a6 | 2014-01-10 00:05:11 | [diff] [blame] | 19 | #include "content/public/browser/notification_service.h" |
| 20 | #include "content/public/browser/notification_types.h" |
Nasko Oskov | 0401d81 | 2021-02-05 22:20:08 | [diff] [blame] | 21 | #include "content/public/browser/site_isolation_policy.h" |
Annie Sullivan | 35977ad | 2019-04-30 21:04:28 | [diff] [blame] | 22 | #include "content/public/common/content_features.h" |
nick | db193a1 | 2016-09-09 23:09:23 | [diff] [blame] | 23 | #include "content/public/common/content_switches.h" |
Nasko Oskov | 03912101 | 2019-01-11 00:21:32 | [diff] [blame] | 24 | #include "content/public/common/origin_util.h" |
[email protected] | 5f96f5a6 | 2014-01-10 00:05:11 | [diff] [blame] | 25 | #include "content/public/common/url_constants.h" |
Peter Kasting | 919ce65 | 2020-05-07 10:22:36 | [diff] [blame] | 26 | #include "content/public/test/browser_test.h" |
[email protected] | 5f96f5a6 | 2014-01-10 00:05:11 | [diff] [blame] | 27 | #include "content/public/test/browser_test_utils.h" |
[email protected] | 6e9def1 | 2014-03-27 20:23:28 | [diff] [blame] | 28 | #include "content/public/test/content_browser_test.h" |
| 29 | #include "content/public/test/content_browser_test_utils.h" |
John Abd-El-Malek | 3fea4c4 | 2019-01-10 01:15:42 | [diff] [blame] | 30 | #include "content/public/test/test_frame_navigation_observer.h" |
[email protected] | 5f96f5a6 | 2014-01-10 00:05:11 | [diff] [blame] | 31 | #include "content/public/test/test_navigation_observer.h" |
| 32 | #include "content/public/test/test_utils.h" |
| 33 | #include "content/shell/browser/shell.h" |
nick | db193a1 | 2016-09-09 23:09:23 | [diff] [blame] | 34 | #include "content/shell/common/shell_switches.h" |
[email protected] | 82307f6b | 2014-08-07 03:30:12 | [diff] [blame] | 35 | #include "content/test/content_browser_test_utils_internal.h" |
shivanigithub | f33e469c | 2021-08-02 13:52:20 | [diff] [blame] | 36 | #include "content/test/resource_load_observer.h" |
[email protected] | 5f96f5a6 | 2014-01-10 00:05:11 | [diff] [blame] | 37 | #include "net/dns/mock_host_resolver.h" |
alexmos | 478dcbb | 2014-12-10 21:24:46 | [diff] [blame] | 38 | #include "net/test/embedded_test_server/embedded_test_server.h" |
shivanigithub | f33e469c | 2021-08-02 13:52:20 | [diff] [blame] | 39 | #include "net/test/embedded_test_server/http_request.h" |
arthursonzogni | b93a447 | 2020-04-10 07:38:00 | [diff] [blame] | 40 | #include "services/network/public/cpp/web_sandbox_flags.h" |
| 41 | #include "services/network/public/mojom/web_sandbox_flags.mojom-shared.h" |
Gyuyoung Kim | 107c2a0 | 2021-04-13 01:49:30 | [diff] [blame] | 42 | #include "third_party/blink/public/common/chrome_debug_urls.h" |
shivanigithub | 93878d0 | 2021-06-15 11:37:53 | [diff] [blame] | 43 | #include "third_party/blink/public/common/features.h" |
Antonio Gomes | 4b2c513 | 2020-01-16 11:49:48 | [diff] [blame] | 44 | #include "third_party/blink/public/mojom/frame/user_activation_update_types.mojom.h" |
nick | 1466c84 | 2015-11-25 20:08:06 | [diff] [blame] | 45 | #include "url/url_constants.h" |
[email protected] | 5f96f5a6 | 2014-01-10 00:05:11 | [diff] [blame] | 46 | |
| 47 | namespace content { |
| 48 | |
nick | db193a1 | 2016-09-09 23:09:23 | [diff] [blame] | 49 | namespace { |
| 50 | |
Nick Carter | b7e7131 | 2018-08-03 23:36:13 | [diff] [blame] | 51 | EvalJsResult GetOriginFromRenderer(FrameTreeNode* node) { |
Philip Jägenstedt | 67302a2 | 2018-09-14 09:58:05 | [diff] [blame] | 52 | return EvalJs(node, "self.origin"); |
nick | db193a1 | 2016-09-09 23:09:23 | [diff] [blame] | 53 | } |
| 54 | |
| 55 | } // namespace |
| 56 | |
[email protected] | 5f96f5a6 | 2014-01-10 00:05:11 | [diff] [blame] | 57 | class FrameTreeBrowserTest : public ContentBrowserTest { |
| 58 | public: |
Fergal Daly | a1d56997 | 2021-03-16 03:24:53 | [diff] [blame] | 59 | FrameTreeBrowserTest() = default; |
[email protected] | 5f96f5a6 | 2014-01-10 00:05:11 | [diff] [blame] | 60 | |
alexmos | 478dcbb | 2014-12-10 21:24:46 | [diff] [blame] | 61 | void SetUpOnMainThread() override { |
| 62 | host_resolver()->AddRule("*", "127.0.0.1"); |
alexmos | 478dcbb | 2014-12-10 21:24:46 | [diff] [blame] | 63 | SetupCrossSiteRedirector(embedded_test_server()); |
martijn | 12d2dad | 2016-11-11 10:59:27 | [diff] [blame] | 64 | ASSERT_TRUE(embedded_test_server()->Start()); |
alexmos | 478dcbb | 2014-12-10 21:24:46 | [diff] [blame] | 65 | } |
| 66 | |
[email protected] | 5f96f5a6 | 2014-01-10 00:05:11 | [diff] [blame] | 67 | private: |
| 68 | DISALLOW_COPY_AND_ASSIGN(FrameTreeBrowserTest); |
| 69 | }; |
| 70 | |
| 71 | // Ensures FrameTree correctly reflects page structure during navigations. |
| 72 | IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, FrameTreeShape) { |
alexmos | 478dcbb | 2014-12-10 21:24:46 | [diff] [blame] | 73 | GURL base_url = embedded_test_server()->GetURL("A.com", "/site_isolation/"); |
[email protected] | 5f96f5a6 | 2014-01-10 00:05:11 | [diff] [blame] | 74 | |
| 75 | // Load doc without iframes. Verify FrameTree just has root. |
| 76 | // Frame tree: |
| 77 | // Site-A Root |
Alex Moshchuk | aa95adf5 | 2019-08-13 00:02:02 | [diff] [blame] | 78 | EXPECT_TRUE(NavigateToURL(shell(), base_url.Resolve("blank.html"))); |
Aran Gilman | 37d1163 | 2019-10-08 23:07:15 | [diff] [blame] | 79 | FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents()) |
Carlos Caballero | 15caeeb | 2021-10-27 09:57:55 | [diff] [blame^] | 80 | ->GetPrimaryFrameTree() |
| 81 | .root(); |
[email protected] | 5f96f5a6 | 2014-01-10 00:05:11 | [diff] [blame] | 82 | EXPECT_EQ(0U, root->child_count()); |
| 83 | |
| 84 | // Add 2 same-site frames. Verify 3 nodes in tree with proper names. |
| 85 | // Frame tree: |
| 86 | // Site-A Root -- Site-A frame1 |
| 87 | // \-- Site-A frame2 |
| 88 | WindowedNotificationObserver observer1( |
| 89 | content::NOTIFICATION_LOAD_STOP, |
| 90 | content::Source<NavigationController>( |
| 91 | &shell()->web_contents()->GetController())); |
Alex Moshchuk | aa95adf5 | 2019-08-13 00:02:02 | [diff] [blame] | 92 | EXPECT_TRUE(NavigateToURL(shell(), base_url.Resolve("frames-X-X.html"))); |
[email protected] | 5f96f5a6 | 2014-01-10 00:05:11 | [diff] [blame] | 93 | observer1.Wait(); |
| 94 | ASSERT_EQ(2U, root->child_count()); |
| 95 | EXPECT_EQ(0U, root->child_at(0)->child_count()); |
| 96 | EXPECT_EQ(0U, root->child_at(1)->child_count()); |
| 97 | } |
| 98 | |
| 99 | // TODO(ajwong): Talk with nasko and merge this functionality with |
| 100 | // FrameTreeShape. |
| 101 | IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, FrameTreeShape2) { |
Alex Moshchuk | aa95adf5 | 2019-08-13 00:02:02 | [diff] [blame] | 102 | EXPECT_TRUE(NavigateToURL( |
| 103 | shell(), embedded_test_server()->GetURL("/frame_tree/top.html"))); |
[email protected] | 5f96f5a6 | 2014-01-10 00:05:11 | [diff] [blame] | 104 | |
| 105 | WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents()); |
Carlos Caballero | 15caeeb | 2021-10-27 09:57:55 | [diff] [blame^] | 106 | FrameTreeNode* root = wc->GetPrimaryFrameTree().root(); |
[email protected] | 5f96f5a6 | 2014-01-10 00:05:11 | [diff] [blame] | 107 | |
[email protected] | 58faf94 | 2014-02-20 21:03:58 | [diff] [blame] | 108 | // Check that the root node is properly created. |
[email protected] | 5f96f5a6 | 2014-01-10 00:05:11 | [diff] [blame] | 109 | ASSERT_EQ(3UL, root->child_count()); |
| 110 | EXPECT_EQ(std::string(), root->frame_name()); |
[email protected] | 5f96f5a6 | 2014-01-10 00:05:11 | [diff] [blame] | 111 | |
| 112 | ASSERT_EQ(2UL, root->child_at(0)->child_count()); |
| 113 | EXPECT_STREQ("1-1-name", root->child_at(0)->frame_name().c_str()); |
| 114 | |
| 115 | // Verify the deepest node exists and has the right name. |
| 116 | ASSERT_EQ(2UL, root->child_at(2)->child_count()); |
| 117 | EXPECT_EQ(1UL, root->child_at(2)->child_at(1)->child_count()); |
| 118 | EXPECT_EQ(0UL, root->child_at(2)->child_at(1)->child_at(0)->child_count()); |
Aran Gilman | 37d1163 | 2019-10-08 23:07:15 | [diff] [blame] | 119 | EXPECT_STREQ( |
| 120 | "3-1-name", |
[email protected] | 5f96f5a6 | 2014-01-10 00:05:11 | [diff] [blame] | 121 | root->child_at(2)->child_at(1)->child_at(0)->frame_name().c_str()); |
| 122 | |
| 123 | // Navigate to about:blank, which should leave only the root node of the frame |
| 124 | // tree in the browser process. |
Alex Moshchuk | aa95adf5 | 2019-08-13 00:02:02 | [diff] [blame] | 125 | EXPECT_TRUE( |
| 126 | NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html"))); |
[email protected] | 5f96f5a6 | 2014-01-10 00:05:11 | [diff] [blame] | 127 | |
Carlos Caballero | 15caeeb | 2021-10-27 09:57:55 | [diff] [blame^] | 128 | root = wc->GetPrimaryFrameTree().root(); |
[email protected] | 5f96f5a6 | 2014-01-10 00:05:11 | [diff] [blame] | 129 | EXPECT_EQ(0UL, root->child_count()); |
| 130 | EXPECT_EQ(std::string(), root->frame_name()); |
[email protected] | 5f96f5a6 | 2014-01-10 00:05:11 | [diff] [blame] | 131 | } |
| 132 | |
| 133 | // Test that we can navigate away if the previous renderer doesn't clean up its |
| 134 | // child frames. |
| 135 | IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, FrameTreeAfterCrash) { |
Alex Moshchuk | aa95adf5 | 2019-08-13 00:02:02 | [diff] [blame] | 136 | EXPECT_TRUE(NavigateToURL( |
| 137 | shell(), embedded_test_server()->GetURL("/frame_tree/top.html"))); |
[email protected] | 5f96f5a6 | 2014-01-10 00:05:11 | [diff] [blame] | 138 | |
creis | e42f2a5 | 2014-09-18 18:14:57 | [diff] [blame] | 139 | // Ensure the view and frame are live. |
Lingqi Chi | 641aeee | 2021-09-08 00:32:58 | [diff] [blame] | 140 | RenderFrameHostImpl* rfh1 = static_cast<RenderFrameHostImpl*>( |
| 141 | shell()->web_contents()->GetMainFrame()); |
| 142 | RenderViewHost* rvh = rfh1->GetRenderViewHost(); |
creis | e42f2a5 | 2014-09-18 18:14:57 | [diff] [blame] | 143 | EXPECT_TRUE(rvh->IsRenderViewLive()); |
arthursonzogni | f4c4a4a | 2019-08-08 18:54:44 | [diff] [blame] | 144 | EXPECT_TRUE(rfh1->IsRenderFrameLive()); |
creis | e42f2a5 | 2014-09-18 18:14:57 | [diff] [blame] | 145 | |
[email protected] | 5f96f5a6 | 2014-01-10 00:05:11 | [diff] [blame] | 146 | // Crash the renderer so that it doesn't send any FrameDetached messages. |
| 147 | RenderProcessHostWatcher crash_observer( |
| 148 | shell()->web_contents(), |
| 149 | RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT); |
Wez | 0abfbf51 | 2018-03-03 01:54:45 | [diff] [blame] | 150 | ASSERT_TRUE( |
| 151 | shell()->web_contents()->GetMainFrame()->GetProcess()->Shutdown(0)); |
[email protected] | 5f96f5a6 | 2014-01-10 00:05:11 | [diff] [blame] | 152 | crash_observer.Wait(); |
| 153 | |
[email protected] | 58faf94 | 2014-02-20 21:03:58 | [diff] [blame] | 154 | // The frame tree should be cleared. |
[email protected] | 5f96f5a6 | 2014-01-10 00:05:11 | [diff] [blame] | 155 | WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents()); |
Carlos Caballero | 15caeeb | 2021-10-27 09:57:55 | [diff] [blame^] | 156 | FrameTreeNode* root = wc->GetPrimaryFrameTree().root(); |
[email protected] | 5f96f5a6 | 2014-01-10 00:05:11 | [diff] [blame] | 157 | EXPECT_EQ(0UL, root->child_count()); |
[email protected] | 5f96f5a6 | 2014-01-10 00:05:11 | [diff] [blame] | 158 | |
creis | e42f2a5 | 2014-09-18 18:14:57 | [diff] [blame] | 159 | // Ensure the view and frame aren't live anymore. |
| 160 | EXPECT_FALSE(rvh->IsRenderViewLive()); |
arthursonzogni | f4c4a4a | 2019-08-08 18:54:44 | [diff] [blame] | 161 | EXPECT_FALSE(rfh1->IsRenderFrameLive()); |
creis | e42f2a5 | 2014-09-18 18:14:57 | [diff] [blame] | 162 | |
[email protected] | 5f96f5a6 | 2014-01-10 00:05:11 | [diff] [blame] | 163 | // Navigate to a new URL. |
alexmos | 478dcbb | 2014-12-10 21:24:46 | [diff] [blame] | 164 | GURL url(embedded_test_server()->GetURL("/title1.html")); |
Alex Moshchuk | aa95adf5 | 2019-08-13 00:02:02 | [diff] [blame] | 165 | EXPECT_TRUE(NavigateToURL(shell(), url)); |
[email protected] | 5f96f5a6 | 2014-01-10 00:05:11 | [diff] [blame] | 166 | EXPECT_EQ(0UL, root->child_count()); |
[email protected] | 58faf94 | 2014-02-20 21:03:58 | [diff] [blame] | 167 | EXPECT_EQ(url, root->current_url()); |
creis | e42f2a5 | 2014-09-18 18:14:57 | [diff] [blame] | 168 | |
arthursonzogni | f4c4a4a | 2019-08-08 18:54:44 | [diff] [blame] | 169 | RenderFrameHostImpl* rfh2 = root->current_frame_host(); |
creis | e42f2a5 | 2014-09-18 18:14:57 | [diff] [blame] | 170 | // Ensure the view and frame are live again. |
| 171 | EXPECT_TRUE(rvh->IsRenderViewLive()); |
arthursonzogni | f4c4a4a | 2019-08-08 18:54:44 | [diff] [blame] | 172 | EXPECT_TRUE(rfh2->IsRenderFrameLive()); |
[email protected] | 5f96f5a6 | 2014-01-10 00:05:11 | [diff] [blame] | 173 | } |
| 174 | |
| 175 | // Test that we can navigate away if the previous renderer doesn't clean up its |
| 176 | // child frames. |
Nasko Oskov | 31c45ff | 2019-10-16 01:15:19 | [diff] [blame] | 177 | IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, NavigateWithLeftoverFrames) { |
alexmos | 478dcbb | 2014-12-10 21:24:46 | [diff] [blame] | 178 | GURL base_url = embedded_test_server()->GetURL("A.com", "/site_isolation/"); |
[email protected] | 5f96f5a6 | 2014-01-10 00:05:11 | [diff] [blame] | 179 | |
Alex Moshchuk | aa95adf5 | 2019-08-13 00:02:02 | [diff] [blame] | 180 | EXPECT_TRUE(NavigateToURL( |
| 181 | shell(), embedded_test_server()->GetURL("/frame_tree/top.html"))); |
[email protected] | 5f96f5a6 | 2014-01-10 00:05:11 | [diff] [blame] | 182 | |
| 183 | // Hang the renderer so that it doesn't send any FrameDetached messages. |
| 184 | // (This navigation will never complete, so don't wait for it.) |
Gyuyoung Kim | 107c2a0 | 2021-04-13 01:49:30 | [diff] [blame] | 185 | shell()->LoadURL(GURL(blink::kChromeUIHangURL)); |
[email protected] | 5f96f5a6 | 2014-01-10 00:05:11 | [diff] [blame] | 186 | |
| 187 | // Check that the frame tree still has children. |
| 188 | WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents()); |
Carlos Caballero | 15caeeb | 2021-10-27 09:57:55 | [diff] [blame^] | 189 | FrameTreeNode* root = wc->GetPrimaryFrameTree().root(); |
[email protected] | 5f96f5a6 | 2014-01-10 00:05:11 | [diff] [blame] | 190 | ASSERT_EQ(3UL, root->child_count()); |
| 191 | |
| 192 | // Navigate to a new URL. We use LoadURL because NavigateToURL will try to |
| 193 | // wait for the previous navigation to stop. |
| 194 | TestNavigationObserver tab_observer(wc, 1); |
| 195 | shell()->LoadURL(base_url.Resolve("blank.html")); |
| 196 | tab_observer.Wait(); |
| 197 | |
[email protected] | 58faf94 | 2014-02-20 21:03:58 | [diff] [blame] | 198 | // The frame tree should now be cleared. |
[email protected] | 5f96f5a6 | 2014-01-10 00:05:11 | [diff] [blame] | 199 | EXPECT_EQ(0UL, root->child_count()); |
[email protected] | 5f96f5a6 | 2014-01-10 00:05:11 | [diff] [blame] | 200 | } |
| 201 | |
creis | e42f2a5 | 2014-09-18 18:14:57 | [diff] [blame] | 202 | // Ensure that IsRenderFrameLive is true for main frames and same-site iframes. |
| 203 | IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, IsRenderFrameLive) { |
alexmos | 478dcbb | 2014-12-10 21:24:46 | [diff] [blame] | 204 | GURL main_url(embedded_test_server()->GetURL("/frame_tree/top.html")); |
Alex Moshchuk | aa95adf5 | 2019-08-13 00:02:02 | [diff] [blame] | 205 | EXPECT_TRUE(NavigateToURL(shell(), main_url)); |
creis | e42f2a5 | 2014-09-18 18:14:57 | [diff] [blame] | 206 | |
| 207 | // It is safe to obtain the root frame tree node here, as it doesn't change. |
| 208 | FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents()) |
Carlos Caballero | 15caeeb | 2021-10-27 09:57:55 | [diff] [blame^] | 209 | ->GetPrimaryFrameTree() |
| 210 | .root(); |
creis | e42f2a5 | 2014-09-18 18:14:57 | [diff] [blame] | 211 | |
| 212 | // The root and subframe should each have a live RenderFrame. |
| 213 | EXPECT_TRUE( |
| 214 | root->current_frame_host()->render_view_host()->IsRenderViewLive()); |
| 215 | EXPECT_TRUE(root->current_frame_host()->IsRenderFrameLive()); |
| 216 | EXPECT_TRUE(root->child_at(0)->current_frame_host()->IsRenderFrameLive()); |
| 217 | |
| 218 | // Load a same-site page into iframe and it should still be live. |
alexmos | 478dcbb | 2014-12-10 21:24:46 | [diff] [blame] | 219 | GURL http_url(embedded_test_server()->GetURL("/title1.html")); |
Lukasz Anforowicz | 69c25dfd | 2020-11-12 21:50:20 | [diff] [blame] | 220 | EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(0), http_url)); |
creis | e42f2a5 | 2014-09-18 18:14:57 | [diff] [blame] | 221 | EXPECT_TRUE( |
| 222 | root->current_frame_host()->render_view_host()->IsRenderViewLive()); |
| 223 | EXPECT_TRUE(root->current_frame_host()->IsRenderFrameLive()); |
| 224 | EXPECT_TRUE(root->child_at(0)->current_frame_host()->IsRenderFrameLive()); |
| 225 | } |
| 226 | |
alexmos | bc7eafa | 2014-12-06 01:38:09 | [diff] [blame] | 227 | // Ensure that origins are correctly set on navigations. |
| 228 | IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, OriginSetOnNavigation) { |
nick | 1466c84 | 2015-11-25 20:08:06 | [diff] [blame] | 229 | GURL about_blank(url::kAboutBlankURL); |
| 230 | GURL main_url( |
| 231 | embedded_test_server()->GetURL("a.com", "/frame_tree/top.html")); |
alexmos | bc7eafa | 2014-12-06 01:38:09 | [diff] [blame] | 232 | EXPECT_TRUE(NavigateToURL(shell(), main_url)); |
nick | 1466c84 | 2015-11-25 20:08:06 | [diff] [blame] | 233 | WebContents* contents = shell()->web_contents(); |
alexmos | bc7eafa | 2014-12-06 01:38:09 | [diff] [blame] | 234 | |
| 235 | // 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] | 236 | FrameTreeNode* root = |
Carlos Caballero | 15caeeb | 2021-10-27 09:57:55 | [diff] [blame^] | 237 | static_cast<WebContentsImpl*>(contents)->GetPrimaryFrameTree().root(); |
alexmos | bc7eafa | 2014-12-06 01:38:09 | [diff] [blame] | 238 | |
| 239 | // Extra '/' is added because the replicated origin is serialized in RFC 6454 |
| 240 | // format, which dictates no trailing '/', whereas GURL::GetOrigin does put a |
| 241 | // '/' at the end. |
Mike West | 800532c | 2021-10-14 09:26:52 | [diff] [blame] | 242 | EXPECT_EQ(main_url.DeprecatedGetOriginAsURL().spec(), |
nick | 1466c84 | 2015-11-25 20:08:06 | [diff] [blame] | 243 | root->current_origin().Serialize() + '/'); |
| 244 | EXPECT_EQ( |
Mike West | 800532c | 2021-10-14 09:26:52 | [diff] [blame] | 245 | main_url.DeprecatedGetOriginAsURL().spec(), |
nick | 1466c84 | 2015-11-25 20:08:06 | [diff] [blame] | 246 | root->current_frame_host()->GetLastCommittedOrigin().Serialize() + '/'); |
alexmos | bc7eafa | 2014-12-06 01:38:09 | [diff] [blame] | 247 | |
nick | 1466c84 | 2015-11-25 20:08:06 | [diff] [blame] | 248 | // The iframe is inititially same-origin. |
| 249 | EXPECT_TRUE( |
| 250 | root->current_frame_host()->GetLastCommittedOrigin().IsSameOriginWith( |
| 251 | root->child_at(0)->current_frame_host()->GetLastCommittedOrigin())); |
| 252 | EXPECT_EQ(root->current_origin().Serialize(), GetOriginFromRenderer(root)); |
| 253 | EXPECT_EQ(root->child_at(0)->current_origin().Serialize(), |
| 254 | GetOriginFromRenderer(root->child_at(0))); |
| 255 | |
| 256 | // Navigate the iframe cross-origin. |
| 257 | GURL frame_url(embedded_test_server()->GetURL("b.com", "/title1.html")); |
Lukasz Anforowicz | 69c25dfd | 2020-11-12 21:50:20 | [diff] [blame] | 258 | EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(0), frame_url)); |
nick | 1466c84 | 2015-11-25 20:08:06 | [diff] [blame] | 259 | EXPECT_EQ(frame_url, root->child_at(0)->current_url()); |
Mike West | 800532c | 2021-10-14 09:26:52 | [diff] [blame] | 260 | EXPECT_EQ(frame_url.DeprecatedGetOriginAsURL().spec(), |
nick | 1466c84 | 2015-11-25 20:08:06 | [diff] [blame] | 261 | root->child_at(0)->current_origin().Serialize() + '/'); |
| 262 | EXPECT_FALSE( |
| 263 | root->current_frame_host()->GetLastCommittedOrigin().IsSameOriginWith( |
| 264 | root->child_at(0)->current_frame_host()->GetLastCommittedOrigin())); |
| 265 | EXPECT_EQ(root->current_origin().Serialize(), GetOriginFromRenderer(root)); |
| 266 | EXPECT_EQ(root->child_at(0)->current_origin().Serialize(), |
| 267 | GetOriginFromRenderer(root->child_at(0))); |
alexmos | bc7eafa | 2014-12-06 01:38:09 | [diff] [blame] | 268 | |
nick | 1466c84 | 2015-11-25 20:08:06 | [diff] [blame] | 269 | // Parent-initiated about:blank navigation should inherit the parent's a.com |
| 270 | // origin. |
| 271 | NavigateIframeToURL(contents, "1-1-id", about_blank); |
| 272 | EXPECT_EQ(about_blank, root->child_at(0)->current_url()); |
Mike West | 800532c | 2021-10-14 09:26:52 | [diff] [blame] | 273 | EXPECT_EQ(main_url.DeprecatedGetOriginAsURL().spec(), |
nick | 1466c84 | 2015-11-25 20:08:06 | [diff] [blame] | 274 | root->child_at(0)->current_origin().Serialize() + '/'); |
| 275 | EXPECT_EQ(root->current_frame_host()->GetLastCommittedOrigin().Serialize(), |
| 276 | root->child_at(0) |
| 277 | ->current_frame_host() |
| 278 | ->GetLastCommittedOrigin() |
| 279 | .Serialize()); |
| 280 | EXPECT_TRUE( |
| 281 | root->current_frame_host()->GetLastCommittedOrigin().IsSameOriginWith( |
| 282 | root->child_at(0)->current_frame_host()->GetLastCommittedOrigin())); |
| 283 | EXPECT_EQ(root->current_origin().Serialize(), GetOriginFromRenderer(root)); |
| 284 | EXPECT_EQ(root->child_at(0)->current_origin().Serialize(), |
| 285 | GetOriginFromRenderer(root->child_at(0))); |
alexmos | bc7eafa | 2014-12-06 01:38:09 | [diff] [blame] | 286 | |
| 287 | GURL data_url("data:text/html,foo"); |
| 288 | EXPECT_TRUE(NavigateToURL(shell(), data_url)); |
| 289 | |
| 290 | // Navigating to a data URL should set a unique origin. This is represented |
| 291 | // as "null" per RFC 6454. |
nick | 1466c84 | 2015-11-25 20:08:06 | [diff] [blame] | 292 | EXPECT_EQ("null", root->current_origin().Serialize()); |
Chris Palmer | ab5e5b5 | 2018-09-28 19:19:30 | [diff] [blame] | 293 | EXPECT_TRUE(contents->GetMainFrame()->GetLastCommittedOrigin().opaque()); |
nick | 1466c84 | 2015-11-25 20:08:06 | [diff] [blame] | 294 | EXPECT_EQ("null", GetOriginFromRenderer(root)); |
alexmos | bc7eafa | 2014-12-06 01:38:09 | [diff] [blame] | 295 | |
| 296 | // Re-navigating to a normal URL should update the origin. |
| 297 | EXPECT_TRUE(NavigateToURL(shell(), main_url)); |
Mike West | 800532c | 2021-10-14 09:26:52 | [diff] [blame] | 298 | EXPECT_EQ(main_url.DeprecatedGetOriginAsURL().spec(), |
nick | 1466c84 | 2015-11-25 20:08:06 | [diff] [blame] | 299 | root->current_origin().Serialize() + '/'); |
| 300 | EXPECT_EQ( |
Mike West | 800532c | 2021-10-14 09:26:52 | [diff] [blame] | 301 | main_url.DeprecatedGetOriginAsURL().spec(), |
nick | 1466c84 | 2015-11-25 20:08:06 | [diff] [blame] | 302 | contents->GetMainFrame()->GetLastCommittedOrigin().Serialize() + '/'); |
Chris Palmer | ab5e5b5 | 2018-09-28 19:19:30 | [diff] [blame] | 303 | EXPECT_FALSE(contents->GetMainFrame()->GetLastCommittedOrigin().opaque()); |
nick | 1466c84 | 2015-11-25 20:08:06 | [diff] [blame] | 304 | EXPECT_EQ(root->current_origin().Serialize(), GetOriginFromRenderer(root)); |
alexmos | bc7eafa | 2014-12-06 01:38:09 | [diff] [blame] | 305 | } |
| 306 | |
nick | 9f34e289 | 2016-01-12 21:01:07 | [diff] [blame] | 307 | // Tests a cross-origin navigation to a blob URL. The main frame initiates this |
| 308 | // navigation on its grandchild. It should wind up in the main frame's process. |
| 309 | IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, NavigateGrandchildToBlob) { |
| 310 | WebContents* contents = shell()->web_contents(); |
| 311 | FrameTreeNode* root = |
Carlos Caballero | 15caeeb | 2021-10-27 09:57:55 | [diff] [blame^] | 312 | static_cast<WebContentsImpl*>(contents)->GetPrimaryFrameTree().root(); |
nick | 9f34e289 | 2016-01-12 21:01:07 | [diff] [blame] | 313 | |
| 314 | // First, snapshot the FrameTree for a normal A(B(A)) case where all frames |
| 315 | // are served over http. The blob test should result in the same structure. |
| 316 | EXPECT_TRUE(NavigateToURL( |
| 317 | shell(), embedded_test_server()->GetURL( |
| 318 | "a.com", "/cross_site_iframe_factory.html?a(b(a))"))); |
Fergal Daly | 79f4429 | 2020-12-01 02:30:48 | [diff] [blame] | 319 | std::string reference_tree = DepictFrameTree(*root); |
nick | 9f34e289 | 2016-01-12 21:01:07 | [diff] [blame] | 320 | |
| 321 | GURL main_url(embedded_test_server()->GetURL( |
| 322 | "a.com", "/cross_site_iframe_factory.html?a(b(c))")); |
| 323 | EXPECT_TRUE(NavigateToURL(shell(), main_url)); |
| 324 | |
| 325 | // The root node will initiate the navigation; its grandchild node will be the |
| 326 | // target of the navigation. |
| 327 | FrameTreeNode* target = root->child_at(0)->child_at(0); |
| 328 | |
creis | b2d5f1f3 | 2016-11-07 23:25:05 | [diff] [blame] | 329 | RenderFrameDeletedObserver deleted_observer(target->current_frame_host()); |
Nick Carter | b7e7131 | 2018-08-03 23:36:13 | [diff] [blame] | 330 | std::string html = |
| 331 | "<html><body><div>This is blob content.</div>" |
| 332 | "<script>" |
Philip Jägenstedt | 67302a2 | 2018-09-14 09:58:05 | [diff] [blame] | 333 | "window.parent.parent.postMessage('HI', self.origin);" |
Nick Carter | b7e7131 | 2018-08-03 23:36:13 | [diff] [blame] | 334 | "</script></body></html>"; |
| 335 | std::string script = JsReplace( |
| 336 | "new Promise((resolve) => {" |
| 337 | " window.addEventListener('message', resolve, false);" |
| 338 | " var blob = new Blob([$1], {type: 'text/html'});" |
| 339 | " var blob_url = URL.createObjectURL(blob);" |
| 340 | " frames[0][0].location.href = blob_url;" |
| 341 | "}).then((event) => {" |
nick | 9f34e289 | 2016-01-12 21:01:07 | [diff] [blame] | 342 | " document.body.appendChild(document.createTextNode(event.data));" |
Nick Carter | b7e7131 | 2018-08-03 23:36:13 | [diff] [blame] | 343 | " return event.source.location.href;" |
| 344 | "});", |
| 345 | html); |
| 346 | std::string blob_url_string = EvalJs(root, script).ExtractString(); |
creis | b2d5f1f3 | 2016-11-07 23:25:05 | [diff] [blame] | 347 | // Wait for the RenderFrame to go away, if this will be cross-process. |
| 348 | if (AreAllSitesIsolatedForTesting()) |
| 349 | deleted_observer.WaitUntilDeleted(); |
nick | 9f34e289 | 2016-01-12 21:01:07 | [diff] [blame] | 350 | EXPECT_EQ(GURL(blob_url_string), target->current_url()); |
| 351 | EXPECT_EQ(url::kBlobScheme, target->current_url().scheme()); |
Chris Palmer | ab5e5b5 | 2018-09-28 19:19:30 | [diff] [blame] | 352 | EXPECT_FALSE(target->current_origin().opaque()); |
nick | 9f34e289 | 2016-01-12 21:01:07 | [diff] [blame] | 353 | EXPECT_EQ("a.com", target->current_origin().host()); |
| 354 | EXPECT_EQ(url::kHttpScheme, target->current_origin().scheme()); |
Nick Carter | b7e7131 | 2018-08-03 23:36:13 | [diff] [blame] | 355 | EXPECT_EQ("This is blob content.", |
| 356 | EvalJs(target, "document.body.children[0].innerHTML")); |
Fergal Daly | 79f4429 | 2020-12-01 02:30:48 | [diff] [blame] | 357 | EXPECT_EQ(reference_tree, DepictFrameTree(*root)); |
nick | 9f34e289 | 2016-01-12 21:01:07 | [diff] [blame] | 358 | } |
| 359 | |
| 360 | IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, NavigateChildToAboutBlank) { |
| 361 | GURL main_url(embedded_test_server()->GetURL( |
| 362 | "a.com", "/cross_site_iframe_factory.html?a(b(c))")); |
| 363 | EXPECT_TRUE(NavigateToURL(shell(), main_url)); |
| 364 | WebContentsImpl* contents = |
| 365 | static_cast<WebContentsImpl*>(shell()->web_contents()); |
| 366 | |
| 367 | // The leaf node (c.com) will be navigated. Its parent node (b.com) will |
| 368 | // initiate the navigation. |
| 369 | FrameTreeNode* target = |
Carlos Caballero | 15caeeb | 2021-10-27 09:57:55 | [diff] [blame^] | 370 | contents->GetPrimaryFrameTree().root()->child_at(0)->child_at(0); |
Harkiran Bolaria | 4eaae307 | 2021-09-21 10:45:48 | [diff] [blame] | 371 | RenderFrameHost* initiator_rfh = target->parent(); |
nick | 9f34e289 | 2016-01-12 21:01:07 | [diff] [blame] | 372 | |
| 373 | // Give the target a name. |
Nick Carter | b7e7131 | 2018-08-03 23:36:13 | [diff] [blame] | 374 | EXPECT_TRUE(ExecJs(target, "window.name = 'target';")); |
nick | 9f34e289 | 2016-01-12 21:01:07 | [diff] [blame] | 375 | |
| 376 | // Use window.open(about:blank), then poll the document for access. |
Nick Carter | b7e7131 | 2018-08-03 23:36:13 | [diff] [blame] | 377 | EvalJsResult about_blank_origin = EvalJs( |
Harkiran Bolaria | 4eaae307 | 2021-09-21 10:45:48 | [diff] [blame] | 378 | initiator_rfh, |
Nick Carter | b7e7131 | 2018-08-03 23:36:13 | [diff] [blame] | 379 | "new Promise(resolve => {" |
| 380 | " var didNavigate = false;" |
| 381 | " var intervalID = setInterval(function() {" |
| 382 | " if (!didNavigate) {" |
| 383 | " didNavigate = true;" |
| 384 | " window.open('about:blank', 'target');" |
| 385 | " }" |
| 386 | " // Poll the document until it doesn't throw a SecurityError.\n" |
| 387 | " try {" |
| 388 | " frames[0].document.write('Hi from ' + document.domain);" |
| 389 | " } catch (e) { return; }" |
| 390 | " clearInterval(intervalID);" |
Philip Jägenstedt | 67302a2 | 2018-09-14 09:58:05 | [diff] [blame] | 391 | " resolve(frames[0].self.origin);" |
Nick Carter | b7e7131 | 2018-08-03 23:36:13 | [diff] [blame] | 392 | " }, 16);" |
| 393 | "});"); |
Nick Carter | b7e7131 | 2018-08-03 23:36:13 | [diff] [blame] | 394 | EXPECT_EQ(target->current_origin(), about_blank_origin); |
Nate Chapin | ab67b04 | 2021-05-04 21:00:51 | [diff] [blame] | 395 | EXPECT_EQ(GURL(url::kAboutBlankURL), target->current_url()); |
| 396 | EXPECT_EQ(url::kAboutScheme, target->current_url().scheme()); |
Chris Palmer | ab5e5b5 | 2018-09-28 19:19:30 | [diff] [blame] | 397 | EXPECT_FALSE(target->current_origin().opaque()); |
nick | 9f34e289 | 2016-01-12 21:01:07 | [diff] [blame] | 398 | EXPECT_EQ("b.com", target->current_origin().host()); |
| 399 | EXPECT_EQ(url::kHttpScheme, target->current_origin().scheme()); |
nick | 9f34e289 | 2016-01-12 21:01:07 | [diff] [blame] | 400 | |
Nick Carter | b7e7131 | 2018-08-03 23:36:13 | [diff] [blame] | 401 | EXPECT_EQ("Hi from b.com", EvalJs(target, "document.body.innerHTML")); |
nick | 9f34e289 | 2016-01-12 21:01:07 | [diff] [blame] | 402 | } |
| 403 | |
| 404 | // Nested iframes, three origins: A(B(C)). Frame A navigates C to about:blank |
| 405 | // (via window.open). This should wind up in A's origin per the spec. Test fails |
| 406 | // because of http://crbug.com/564292 |
| 407 | IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, |
| 408 | DISABLED_NavigateGrandchildToAboutBlank) { |
| 409 | GURL main_url(embedded_test_server()->GetURL( |
| 410 | "a.com", "/cross_site_iframe_factory.html?a(b(c))")); |
| 411 | EXPECT_TRUE(NavigateToURL(shell(), main_url)); |
| 412 | WebContentsImpl* contents = |
| 413 | static_cast<WebContentsImpl*>(shell()->web_contents()); |
| 414 | |
| 415 | // The leaf node (c.com) will be navigated. Its grandparent node (a.com) will |
| 416 | // initiate the navigation. |
| 417 | FrameTreeNode* target = |
Carlos Caballero | 15caeeb | 2021-10-27 09:57:55 | [diff] [blame^] | 418 | contents->GetPrimaryFrameTree().root()->child_at(0)->child_at(0); |
Harkiran Bolaria | 4eaae307 | 2021-09-21 10:45:48 | [diff] [blame] | 419 | RenderFrameHost* initiator_rfh = target->parent()->GetParent(); |
nick | 9f34e289 | 2016-01-12 21:01:07 | [diff] [blame] | 420 | |
| 421 | // Give the target a name. |
Nick Carter | b7e7131 | 2018-08-03 23:36:13 | [diff] [blame] | 422 | EXPECT_TRUE(ExecJs(target, "window.name = 'target';")); |
nick | 9f34e289 | 2016-01-12 21:01:07 | [diff] [blame] | 423 | |
| 424 | // Use window.open(about:blank), then poll the document for access. |
Nick Carter | b7e7131 | 2018-08-03 23:36:13 | [diff] [blame] | 425 | EvalJsResult about_blank_origin = |
Harkiran Bolaria | 4eaae307 | 2021-09-21 10:45:48 | [diff] [blame] | 426 | EvalJs(initiator_rfh, |
Nick Carter | b7e7131 | 2018-08-03 23:36:13 | [diff] [blame] | 427 | "new Promise((resolve) => {" |
| 428 | " var didNavigate = false;" |
| 429 | " var intervalID = setInterval(() => {" |
| 430 | " if (!didNavigate) {" |
| 431 | " didNavigate = true;" |
| 432 | " window.open('about:blank', 'target');" |
| 433 | " }" |
| 434 | " // May raise a SecurityError, that's expected.\n" |
| 435 | " try {" |
| 436 | " frames[0][0].document.write('Hi from ' + document.domain);" |
| 437 | " } catch (e) { return; }" |
| 438 | " clearInterval(intervalID);" |
Philip Jägenstedt | 67302a2 | 2018-09-14 09:58:05 | [diff] [blame] | 439 | " resolve(frames[0][0].self.origin);" |
Nick Carter | b7e7131 | 2018-08-03 23:36:13 | [diff] [blame] | 440 | " }, 16);" |
| 441 | "});"); |
| 442 | EXPECT_EQ(target->current_origin(), about_blank_origin); |
nick | 9f34e289 | 2016-01-12 21:01:07 | [diff] [blame] | 443 | EXPECT_EQ(GURL(url::kAboutBlankURL), target->current_url()); |
| 444 | EXPECT_EQ(url::kAboutScheme, target->current_url().scheme()); |
Chris Palmer | ab5e5b5 | 2018-09-28 19:19:30 | [diff] [blame] | 445 | EXPECT_FALSE(target->current_origin().opaque()); |
nick | 9f34e289 | 2016-01-12 21:01:07 | [diff] [blame] | 446 | EXPECT_EQ("a.com", target->current_origin().host()); |
| 447 | EXPECT_EQ(url::kHttpScheme, target->current_origin().scheme()); |
nick | 9f34e289 | 2016-01-12 21:01:07 | [diff] [blame] | 448 | |
Nick Carter | b7e7131 | 2018-08-03 23:36:13 | [diff] [blame] | 449 | EXPECT_EQ("Hi from a.com", EvalJs(target, "document.body.innerHTML")); |
nick | 9f34e289 | 2016-01-12 21:01:07 | [diff] [blame] | 450 | } |
| 451 | |
Nasko Oskov | 03912101 | 2019-01-11 00:21:32 | [diff] [blame] | 452 | // Tests a cross-origin navigation to a data: URL. The main frame initiates this |
| 453 | // navigation on its grandchild. It should wind up in the main frame's process |
| 454 | // and have precursor origin of the main frame origin. |
| 455 | IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, NavigateGrandchildToDataUrl) { |
| 456 | GURL main_url(embedded_test_server()->GetURL( |
| 457 | "a.com", "/cross_site_iframe_factory.html?a(b(c))")); |
| 458 | EXPECT_TRUE(NavigateToURL(shell(), main_url)); |
| 459 | WebContentsImpl* contents = |
| 460 | static_cast<WebContentsImpl*>(shell()->web_contents()); |
| 461 | |
| 462 | // The leaf node (c.com) will be navigated. Its grandparent node (a.com) will |
| 463 | // initiate the navigation. |
| 464 | FrameTreeNode* target = |
Carlos Caballero | 15caeeb | 2021-10-27 09:57:55 | [diff] [blame^] | 465 | contents->GetPrimaryFrameTree().root()->child_at(0)->child_at(0); |
Harkiran Bolaria | 4eaae307 | 2021-09-21 10:45:48 | [diff] [blame] | 466 | RenderFrameHostImpl* initiator_rfh = target->parent()->GetParent(); |
Nasko Oskov | 03912101 | 2019-01-11 00:21:32 | [diff] [blame] | 467 | |
| 468 | // Give the target a name. |
| 469 | EXPECT_TRUE(ExecJs(target, "window.name = 'target';")); |
| 470 | |
| 471 | // Navigate the target frame through the initiator frame. |
| 472 | { |
| 473 | TestFrameNavigationObserver observer(target); |
Harkiran Bolaria | 4eaae307 | 2021-09-21 10:45:48 | [diff] [blame] | 474 | EXPECT_TRUE(ExecJs(initiator_rfh, |
| 475 | "window.open('data:text/html,content', 'target');")); |
Nasko Oskov | 03912101 | 2019-01-11 00:21:32 | [diff] [blame] | 476 | observer.Wait(); |
| 477 | } |
| 478 | |
| 479 | url::Origin original_target_origin = |
| 480 | target->current_frame_host()->GetLastCommittedOrigin(); |
| 481 | EXPECT_TRUE(original_target_origin.opaque()); |
| 482 | EXPECT_EQ(original_target_origin.GetTupleOrPrecursorTupleIfOpaque(), |
| 483 | url::SchemeHostPort(main_url)); |
| 484 | |
| 485 | // Navigate the grandchild frame again cross-process to foo.com, then |
| 486 | // go back in session history. The origin for the data: URL must be preserved. |
| 487 | { |
| 488 | TestFrameNavigationObserver observer(target); |
| 489 | EXPECT_TRUE(ExecJs(target, JsReplace("window.location = $1", |
| 490 | embedded_test_server()->GetURL( |
| 491 | "foo.com", "/title2.html")))); |
| 492 | observer.Wait(); |
| 493 | } |
| 494 | EXPECT_NE(original_target_origin, |
| 495 | target->current_frame_host()->GetLastCommittedOrigin()); |
| 496 | { |
| 497 | TestFrameNavigationObserver observer(target); |
| 498 | contents->GetController().GoBack(); |
| 499 | observer.Wait(); |
| 500 | } |
| 501 | |
| 502 | url::Origin target_origin = |
| 503 | target->current_frame_host()->GetLastCommittedOrigin(); |
| 504 | EXPECT_TRUE(target_origin.opaque()); |
| 505 | EXPECT_EQ(target_origin.GetTupleOrPrecursorTupleIfOpaque(), |
| 506 | url::SchemeHostPort(main_url)); |
| 507 | EXPECT_EQ(target_origin, original_target_origin); |
| 508 | } |
| 509 | |
nasko | faa01fb | 2016-04-30 01:04:17 | [diff] [blame] | 510 | // Ensures that iframe with srcdoc is always put in the same origin as its |
| 511 | // parent frame. |
| 512 | IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, ChildFrameWithSrcdoc) { |
| 513 | GURL main_url(embedded_test_server()->GetURL( |
| 514 | "a.com", "/cross_site_iframe_factory.html?a(b)")); |
| 515 | EXPECT_TRUE(NavigateToURL(shell(), main_url)); |
| 516 | WebContentsImpl* contents = |
| 517 | static_cast<WebContentsImpl*>(shell()->web_contents()); |
Carlos Caballero | 15caeeb | 2021-10-27 09:57:55 | [diff] [blame^] | 518 | FrameTreeNode* root = contents->GetPrimaryFrameTree().root(); |
nasko | faa01fb | 2016-04-30 01:04:17 | [diff] [blame] | 519 | EXPECT_EQ(1U, root->child_count()); |
| 520 | |
| 521 | FrameTreeNode* child = root->child_at(0); |
Philip Jägenstedt | 67302a2 | 2018-09-14 09:58:05 | [diff] [blame] | 522 | std::string frame_origin = EvalJs(child, "self.origin;").ExtractString(); |
nasko | faa01fb | 2016-04-30 01:04:17 | [diff] [blame] | 523 | EXPECT_TRUE( |
| 524 | child->current_frame_host()->GetLastCommittedOrigin().IsSameOriginWith( |
Daniel Cheng | 88186bd5 | 2017-10-20 08:14:46 | [diff] [blame] | 525 | url::Origin::Create(GURL(frame_origin)))); |
nasko | faa01fb | 2016-04-30 01:04:17 | [diff] [blame] | 526 | EXPECT_FALSE( |
| 527 | root->current_frame_host()->GetLastCommittedOrigin().IsSameOriginWith( |
Daniel Cheng | 88186bd5 | 2017-10-20 08:14:46 | [diff] [blame] | 528 | url::Origin::Create(GURL(frame_origin)))); |
nasko | faa01fb | 2016-04-30 01:04:17 | [diff] [blame] | 529 | |
| 530 | // Create a new iframe with srcdoc and add it to the main frame. It should |
| 531 | // be created in the same SiteInstance as the parent. |
| 532 | { |
Aran Gilman | 37d1163 | 2019-10-08 23:07:15 | [diff] [blame] | 533 | std::string script( |
| 534 | "var f = document.createElement('iframe');" |
| 535 | "f.srcdoc = 'some content';" |
| 536 | "document.body.appendChild(f)"); |
nasko | faa01fb | 2016-04-30 01:04:17 | [diff] [blame] | 537 | TestNavigationObserver observer(shell()->web_contents()); |
Nick Carter | b7e7131 | 2018-08-03 23:36:13 | [diff] [blame] | 538 | EXPECT_TRUE(ExecJs(root, script)); |
nasko | faa01fb | 2016-04-30 01:04:17 | [diff] [blame] | 539 | EXPECT_EQ(2U, root->child_count()); |
| 540 | observer.Wait(); |
| 541 | |
Lukasz Anforowicz | 42d3d07f | 2019-06-19 01:06:42 | [diff] [blame] | 542 | EXPECT_TRUE(root->child_at(1)->current_url().IsAboutSrcdoc()); |
Peter Kasting | eb8c3ce | 2021-08-20 04:39:35 | [diff] [blame] | 543 | EvalJsResult js_result = EvalJs(root->child_at(1), "self.origin"); |
Mike West | 800532c | 2021-10-14 09:26:52 | [diff] [blame] | 544 | EXPECT_EQ(root->current_frame_host() |
| 545 | ->GetLastCommittedURL() |
| 546 | .DeprecatedGetOriginAsURL(), |
Peter Kasting | eb8c3ce | 2021-08-20 04:39:35 | [diff] [blame] | 547 | GURL(js_result.ExtractString())); |
Mike West | 800532c | 2021-10-14 09:26:52 | [diff] [blame] | 548 | EXPECT_NE(child->current_frame_host() |
| 549 | ->GetLastCommittedURL() |
| 550 | .DeprecatedGetOriginAsURL(), |
Peter Kasting | eb8c3ce | 2021-08-20 04:39:35 | [diff] [blame] | 551 | GURL(js_result.ExtractString())); |
nasko | faa01fb | 2016-04-30 01:04:17 | [diff] [blame] | 552 | } |
| 553 | |
| 554 | // Set srcdoc on the existing cross-site frame. It should navigate the frame |
| 555 | // back to the origin of the parent. |
| 556 | { |
Aran Gilman | 37d1163 | 2019-10-08 23:07:15 | [diff] [blame] | 557 | std::string script( |
| 558 | "var f = document.getElementById('child-0');" |
| 559 | "f.srcdoc = 'some content';"); |
nasko | faa01fb | 2016-04-30 01:04:17 | [diff] [blame] | 560 | TestNavigationObserver observer(shell()->web_contents()); |
Nick Carter | b7e7131 | 2018-08-03 23:36:13 | [diff] [blame] | 561 | EXPECT_TRUE(ExecJs(root, script)); |
nasko | faa01fb | 2016-04-30 01:04:17 | [diff] [blame] | 562 | observer.Wait(); |
| 563 | |
Lukasz Anforowicz | 42d3d07f | 2019-06-19 01:06:42 | [diff] [blame] | 564 | EXPECT_TRUE(child->current_url().IsAboutSrcdoc()); |
Avi Drissman | a3144c58 | 2021-10-14 17:00:26 | [diff] [blame] | 565 | EXPECT_EQ(root->current_frame_host()->GetLastCommittedOrigin().Serialize(), |
| 566 | EvalJs(child, "self.origin")); |
nasko | faa01fb | 2016-04-30 01:04:17 | [diff] [blame] | 567 | } |
| 568 | } |
| 569 | |
Ian Clelland | 5cbaaf8 | 2017-11-27 22:00:03 | [diff] [blame] | 570 | // Ensure that sandbox flags are correctly set in the main frame when set by |
| 571 | // Content-Security-Policy header. |
| 572 | IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, SandboxFlagsSetForMainFrame) { |
| 573 | GURL main_url(embedded_test_server()->GetURL("/csp_sandboxed_frame.html")); |
| 574 | EXPECT_TRUE(NavigateToURL(shell(), main_url)); |
| 575 | |
| 576 | // It is safe to obtain the root frame tree node here, as it doesn't change. |
| 577 | FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents()) |
Carlos Caballero | 15caeeb | 2021-10-27 09:57:55 | [diff] [blame^] | 578 | ->GetPrimaryFrameTree() |
| 579 | .root(); |
Ian Clelland | 5cbaaf8 | 2017-11-27 22:00:03 | [diff] [blame] | 580 | |
| 581 | // Verify that sandbox flags are set properly for the root FrameTreeNode and |
| 582 | // RenderFrameHost. Root frame is sandboxed with "allow-scripts". |
arthursonzogni | b93a447 | 2020-04-10 07:38:00 | [diff] [blame] | 583 | EXPECT_EQ(network::mojom::WebSandboxFlags::kNone, |
Ian Clelland | 5cbaaf8 | 2017-11-27 22:00:03 | [diff] [blame] | 584 | root->effective_frame_policy().sandbox_flags); |
arthursonzogni | b93a447 | 2020-04-10 07:38:00 | [diff] [blame] | 585 | EXPECT_EQ(network::mojom::WebSandboxFlags::kAll & |
| 586 | ~network::mojom::WebSandboxFlags::kScripts & |
| 587 | ~network::mojom::WebSandboxFlags::kAutomaticFeatures, |
Ian Clelland | 5cbaaf8 | 2017-11-27 22:00:03 | [diff] [blame] | 588 | root->active_sandbox_flags()); |
| 589 | EXPECT_EQ(root->active_sandbox_flags(), |
| 590 | root->current_frame_host()->active_sandbox_flags()); |
| 591 | |
| 592 | // Verify that child frames inherit sandbox flags from the root. First frame |
| 593 | // has no explicitly set flags of its own, and should inherit those from the |
| 594 | // root. Second frame is completely sandboxed. |
arthursonzogni | b93a447 | 2020-04-10 07:38:00 | [diff] [blame] | 595 | EXPECT_EQ(network::mojom::WebSandboxFlags::kAll & |
| 596 | ~network::mojom::WebSandboxFlags::kScripts & |
| 597 | ~network::mojom::WebSandboxFlags::kAutomaticFeatures, |
Ian Clelland | 5cbaaf8 | 2017-11-27 22:00:03 | [diff] [blame] | 598 | root->child_at(0)->effective_frame_policy().sandbox_flags); |
arthursonzogni | b93a447 | 2020-04-10 07:38:00 | [diff] [blame] | 599 | EXPECT_EQ(network::mojom::WebSandboxFlags::kAll & |
| 600 | ~network::mojom::WebSandboxFlags::kScripts & |
| 601 | ~network::mojom::WebSandboxFlags::kAutomaticFeatures, |
Ian Clelland | 5cbaaf8 | 2017-11-27 22:00:03 | [diff] [blame] | 602 | root->child_at(0)->active_sandbox_flags()); |
| 603 | EXPECT_EQ(root->child_at(0)->active_sandbox_flags(), |
| 604 | root->child_at(0)->current_frame_host()->active_sandbox_flags()); |
arthursonzogni | b93a447 | 2020-04-10 07:38:00 | [diff] [blame] | 605 | EXPECT_EQ(network::mojom::WebSandboxFlags::kAll, |
Ian Clelland | 5cbaaf8 | 2017-11-27 22:00:03 | [diff] [blame] | 606 | root->child_at(1)->effective_frame_policy().sandbox_flags); |
arthursonzogni | b93a447 | 2020-04-10 07:38:00 | [diff] [blame] | 607 | EXPECT_EQ(network::mojom::WebSandboxFlags::kAll, |
Ian Clelland | 5cbaaf8 | 2017-11-27 22:00:03 | [diff] [blame] | 608 | root->child_at(1)->active_sandbox_flags()); |
| 609 | EXPECT_EQ(root->child_at(1)->active_sandbox_flags(), |
| 610 | root->child_at(1)->current_frame_host()->active_sandbox_flags()); |
| 611 | |
| 612 | // Navigating the main frame to a different URL should clear sandbox flags. |
| 613 | GURL unsandboxed_url(embedded_test_server()->GetURL("/title1.html")); |
Lukasz Anforowicz | 69c25dfd | 2020-11-12 21:50:20 | [diff] [blame] | 614 | EXPECT_TRUE(NavigateToURLFromRenderer(root, unsandboxed_url)); |
Ian Clelland | 5cbaaf8 | 2017-11-27 22:00:03 | [diff] [blame] | 615 | |
| 616 | // Verify that sandbox flags are cleared properly for the root FrameTreeNode |
| 617 | // and RenderFrameHost. |
arthursonzogni | b93a447 | 2020-04-10 07:38:00 | [diff] [blame] | 618 | EXPECT_EQ(network::mojom::WebSandboxFlags::kNone, |
Ian Clelland | 5cbaaf8 | 2017-11-27 22:00:03 | [diff] [blame] | 619 | root->effective_frame_policy().sandbox_flags); |
arthursonzogni | b93a447 | 2020-04-10 07:38:00 | [diff] [blame] | 620 | EXPECT_EQ(network::mojom::WebSandboxFlags::kNone, |
| 621 | root->active_sandbox_flags()); |
| 622 | EXPECT_EQ(network::mojom::WebSandboxFlags::kNone, |
Ian Clelland | 5cbaaf8 | 2017-11-27 22:00:03 | [diff] [blame] | 623 | root->current_frame_host()->active_sandbox_flags()); |
| 624 | } |
| 625 | |
alexmos | f832a2f | 2015-01-27 22:44:03 | [diff] [blame] | 626 | // Ensure that sandbox flags are correctly set when child frames are created. |
| 627 | IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, SandboxFlagsSetForChildFrames) { |
| 628 | GURL main_url(embedded_test_server()->GetURL("/sandboxed_frames.html")); |
| 629 | EXPECT_TRUE(NavigateToURL(shell(), main_url)); |
| 630 | |
| 631 | // It is safe to obtain the root frame tree node here, as it doesn't change. |
| 632 | FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents()) |
Carlos Caballero | 15caeeb | 2021-10-27 09:57:55 | [diff] [blame^] | 633 | ->GetPrimaryFrameTree() |
| 634 | .root(); |
alexmos | f832a2f | 2015-01-27 22:44:03 | [diff] [blame] | 635 | |
| 636 | // Verify that sandbox flags are set properly for all FrameTreeNodes. |
| 637 | // First frame is completely sandboxed; second frame uses "allow-scripts", |
| 638 | // which resets both SandboxFlags::Scripts and |
| 639 | // SandboxFlags::AutomaticFeatures bits per blink::parseSandboxPolicy(), and |
| 640 | // third frame has "allow-scripts allow-same-origin". |
arthursonzogni | b93a447 | 2020-04-10 07:38:00 | [diff] [blame] | 641 | EXPECT_EQ(network::mojom::WebSandboxFlags::kNone, |
Ian Clelland | cdc4f31 | 2017-10-13 22:24:12 | [diff] [blame] | 642 | root->effective_frame_policy().sandbox_flags); |
arthursonzogni | b93a447 | 2020-04-10 07:38:00 | [diff] [blame] | 643 | EXPECT_EQ(network::mojom::WebSandboxFlags::kAll, |
Ian Clelland | cdc4f31 | 2017-10-13 22:24:12 | [diff] [blame] | 644 | root->child_at(0)->effective_frame_policy().sandbox_flags); |
arthursonzogni | b93a447 | 2020-04-10 07:38:00 | [diff] [blame] | 645 | EXPECT_EQ(network::mojom::WebSandboxFlags::kAll & |
| 646 | ~network::mojom::WebSandboxFlags::kScripts & |
| 647 | ~network::mojom::WebSandboxFlags::kAutomaticFeatures, |
Ian Clelland | cdc4f31 | 2017-10-13 22:24:12 | [diff] [blame] | 648 | root->child_at(1)->effective_frame_policy().sandbox_flags); |
arthursonzogni | b93a447 | 2020-04-10 07:38:00 | [diff] [blame] | 649 | EXPECT_EQ(network::mojom::WebSandboxFlags::kAll & |
| 650 | ~network::mojom::WebSandboxFlags::kScripts & |
| 651 | ~network::mojom::WebSandboxFlags::kAutomaticFeatures & |
| 652 | ~network::mojom::WebSandboxFlags::kOrigin, |
Ian Clelland | cdc4f31 | 2017-10-13 22:24:12 | [diff] [blame] | 653 | root->child_at(2)->effective_frame_policy().sandbox_flags); |
alexmos | f832a2f | 2015-01-27 22:44:03 | [diff] [blame] | 654 | |
| 655 | // Sandboxed frames should set a unique origin unless they have the |
| 656 | // "allow-same-origin" directive. |
alexmos | 6e94010 | 2016-01-19 22:47:25 | [diff] [blame] | 657 | EXPECT_EQ("null", root->child_at(0)->current_origin().Serialize()); |
| 658 | EXPECT_EQ("null", root->child_at(1)->current_origin().Serialize()); |
Mike West | 800532c | 2021-10-14 09:26:52 | [diff] [blame] | 659 | EXPECT_EQ(main_url.DeprecatedGetOriginAsURL().spec(), |
alexmos | 6e94010 | 2016-01-19 22:47:25 | [diff] [blame] | 660 | root->child_at(2)->current_origin().Serialize() + "/"); |
alexmos | f832a2f | 2015-01-27 22:44:03 | [diff] [blame] | 661 | |
| 662 | // Navigating to a different URL should not clear sandbox flags. |
| 663 | GURL frame_url(embedded_test_server()->GetURL("/title1.html")); |
| 664 | NavigateFrameToURL(root->child_at(0), frame_url); |
arthursonzogni | b93a447 | 2020-04-10 07:38:00 | [diff] [blame] | 665 | EXPECT_EQ(network::mojom::WebSandboxFlags::kAll, |
Ian Clelland | cdc4f31 | 2017-10-13 22:24:12 | [diff] [blame] | 666 | root->child_at(0)->effective_frame_policy().sandbox_flags); |
alexmos | f832a2f | 2015-01-27 22:44:03 | [diff] [blame] | 667 | } |
| 668 | |
Ian Clelland | 5cbaaf8 | 2017-11-27 22:00:03 | [diff] [blame] | 669 | // Ensure that sandbox flags are correctly set in the child frames when set by |
| 670 | // Content-Security-Policy header, and in combination with the sandbox iframe |
| 671 | // attribute. |
| 672 | IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, |
| 673 | SandboxFlagsSetByCSPForChildFrames) { |
| 674 | GURL main_url(embedded_test_server()->GetURL("/sandboxed_frames_csp.html")); |
| 675 | EXPECT_TRUE(NavigateToURL(shell(), main_url)); |
| 676 | |
| 677 | // It is safe to obtain the root frame tree node here, as it doesn't change. |
| 678 | FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents()) |
Carlos Caballero | 15caeeb | 2021-10-27 09:57:55 | [diff] [blame^] | 679 | ->GetPrimaryFrameTree() |
| 680 | .root(); |
Ian Clelland | 5cbaaf8 | 2017-11-27 22:00:03 | [diff] [blame] | 681 | |
| 682 | // Verify that sandbox flags are set properly for all FrameTreeNodes. |
| 683 | // First frame has no iframe sandbox flags, but the framed document is served |
| 684 | // with a CSP header which sets "allow-scripts", "allow-popups" and |
| 685 | // "allow-pointer-lock". |
| 686 | // Second frame is sandboxed with "allow-scripts", "allow-pointer-lock" and |
| 687 | // "allow-orientation-lock", and the framed document is also served with a CSP |
| 688 | // header which uses "allow-popups" and "allow-pointer-lock". The resulting |
| 689 | // sandbox for the frame should only have "allow-pointer-lock". |
arthursonzogni | b93a447 | 2020-04-10 07:38:00 | [diff] [blame] | 690 | EXPECT_EQ(network::mojom::WebSandboxFlags::kNone, |
Ian Clelland | 5cbaaf8 | 2017-11-27 22:00:03 | [diff] [blame] | 691 | root->effective_frame_policy().sandbox_flags); |
arthursonzogni | b93a447 | 2020-04-10 07:38:00 | [diff] [blame] | 692 | EXPECT_EQ(network::mojom::WebSandboxFlags::kNone, |
| 693 | root->active_sandbox_flags()); |
Ian Clelland | 5cbaaf8 | 2017-11-27 22:00:03 | [diff] [blame] | 694 | EXPECT_EQ(root->active_sandbox_flags(), |
| 695 | root->current_frame_host()->active_sandbox_flags()); |
arthursonzogni | b93a447 | 2020-04-10 07:38:00 | [diff] [blame] | 696 | EXPECT_EQ(network::mojom::WebSandboxFlags::kNone, |
Ian Clelland | 5cbaaf8 | 2017-11-27 22:00:03 | [diff] [blame] | 697 | root->child_at(0)->effective_frame_policy().sandbox_flags); |
arthursonzogni | b93a447 | 2020-04-10 07:38:00 | [diff] [blame] | 698 | EXPECT_EQ(network::mojom::WebSandboxFlags::kAll & |
| 699 | ~network::mojom::WebSandboxFlags::kScripts & |
| 700 | ~network::mojom::WebSandboxFlags::kAutomaticFeatures & |
| 701 | ~network::mojom::WebSandboxFlags::kPopups & |
| 702 | ~network::mojom::WebSandboxFlags::kPointerLock, |
Ian Clelland | 5cbaaf8 | 2017-11-27 22:00:03 | [diff] [blame] | 703 | root->child_at(0)->active_sandbox_flags()); |
| 704 | EXPECT_EQ(root->child_at(0)->active_sandbox_flags(), |
| 705 | root->child_at(0)->current_frame_host()->active_sandbox_flags()); |
arthursonzogni | b93a447 | 2020-04-10 07:38:00 | [diff] [blame] | 706 | EXPECT_EQ(network::mojom::WebSandboxFlags::kAll & |
| 707 | ~network::mojom::WebSandboxFlags::kScripts & |
| 708 | ~network::mojom::WebSandboxFlags::kAutomaticFeatures & |
| 709 | ~network::mojom::WebSandboxFlags::kPointerLock & |
| 710 | ~network::mojom::WebSandboxFlags::kOrientationLock, |
Ian Clelland | 5cbaaf8 | 2017-11-27 22:00:03 | [diff] [blame] | 711 | root->child_at(1)->effective_frame_policy().sandbox_flags); |
arthursonzogni | b93a447 | 2020-04-10 07:38:00 | [diff] [blame] | 712 | EXPECT_EQ(network::mojom::WebSandboxFlags::kAll & |
| 713 | ~network::mojom::WebSandboxFlags::kScripts & |
| 714 | ~network::mojom::WebSandboxFlags::kAutomaticFeatures & |
| 715 | ~network::mojom::WebSandboxFlags::kPointerLock, |
Ian Clelland | 5cbaaf8 | 2017-11-27 22:00:03 | [diff] [blame] | 716 | root->child_at(1)->active_sandbox_flags()); |
| 717 | EXPECT_EQ(root->child_at(1)->active_sandbox_flags(), |
| 718 | root->child_at(1)->current_frame_host()->active_sandbox_flags()); |
| 719 | |
| 720 | // Navigating to a different URL *should* clear CSP-set sandbox flags, but |
| 721 | // should retain those flags set by the frame owner. |
| 722 | GURL frame_url(embedded_test_server()->GetURL("/title1.html")); |
| 723 | |
Lukasz Anforowicz | 69c25dfd | 2020-11-12 21:50:20 | [diff] [blame] | 724 | EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(0), frame_url)); |
arthursonzogni | b93a447 | 2020-04-10 07:38:00 | [diff] [blame] | 725 | EXPECT_EQ(network::mojom::WebSandboxFlags::kNone, |
Ian Clelland | 5cbaaf8 | 2017-11-27 22:00:03 | [diff] [blame] | 726 | root->child_at(0)->effective_frame_policy().sandbox_flags); |
arthursonzogni | b93a447 | 2020-04-10 07:38:00 | [diff] [blame] | 727 | EXPECT_EQ(network::mojom::WebSandboxFlags::kNone, |
Ian Clelland | 5cbaaf8 | 2017-11-27 22:00:03 | [diff] [blame] | 728 | root->child_at(0)->active_sandbox_flags()); |
| 729 | EXPECT_EQ(root->child_at(0)->active_sandbox_flags(), |
| 730 | root->child_at(0)->current_frame_host()->active_sandbox_flags()); |
| 731 | |
Lukasz Anforowicz | 69c25dfd | 2020-11-12 21:50:20 | [diff] [blame] | 732 | EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(1), frame_url)); |
arthursonzogni | b93a447 | 2020-04-10 07:38:00 | [diff] [blame] | 733 | EXPECT_EQ(network::mojom::WebSandboxFlags::kAll & |
| 734 | ~network::mojom::WebSandboxFlags::kScripts & |
| 735 | ~network::mojom::WebSandboxFlags::kAutomaticFeatures & |
| 736 | ~network::mojom::WebSandboxFlags::kPointerLock & |
| 737 | ~network::mojom::WebSandboxFlags::kOrientationLock, |
Ian Clelland | 5cbaaf8 | 2017-11-27 22:00:03 | [diff] [blame] | 738 | root->child_at(1)->effective_frame_policy().sandbox_flags); |
arthursonzogni | b93a447 | 2020-04-10 07:38:00 | [diff] [blame] | 739 | EXPECT_EQ(network::mojom::WebSandboxFlags::kAll & |
| 740 | ~network::mojom::WebSandboxFlags::kScripts & |
| 741 | ~network::mojom::WebSandboxFlags::kAutomaticFeatures & |
| 742 | ~network::mojom::WebSandboxFlags::kPointerLock & |
| 743 | ~network::mojom::WebSandboxFlags::kOrientationLock, |
Ian Clelland | 5cbaaf8 | 2017-11-27 22:00:03 | [diff] [blame] | 744 | root->child_at(1)->active_sandbox_flags()); |
| 745 | EXPECT_EQ(root->child_at(1)->active_sandbox_flags(), |
| 746 | root->child_at(1)->current_frame_host()->active_sandbox_flags()); |
| 747 | } |
| 748 | |
alexmos | e201c7cd | 2015-06-10 17:14:21 | [diff] [blame] | 749 | // Ensure that a popup opened from a subframe sets its opener to the subframe's |
| 750 | // FrameTreeNode, and that the opener is cleared if the subframe is destroyed. |
| 751 | IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, SubframeOpenerSetForNewWindow) { |
| 752 | GURL main_url(embedded_test_server()->GetURL("/frame_tree/top.html")); |
| 753 | EXPECT_TRUE(NavigateToURL(shell(), main_url)); |
| 754 | |
| 755 | // It is safe to obtain the root frame tree node here, as it doesn't change. |
| 756 | FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents()) |
Carlos Caballero | 15caeeb | 2021-10-27 09:57:55 | [diff] [blame^] | 757 | ->GetPrimaryFrameTree() |
| 758 | .root(); |
alexmos | e201c7cd | 2015-06-10 17:14:21 | [diff] [blame] | 759 | |
| 760 | // Open a new window from a subframe. |
| 761 | ShellAddedObserver new_shell_observer; |
| 762 | GURL popup_url(embedded_test_server()->GetURL("foo.com", "/title1.html")); |
Nick Carter | b7e7131 | 2018-08-03 23:36:13 | [diff] [blame] | 763 | EXPECT_TRUE( |
| 764 | ExecJs(root->child_at(0), JsReplace("window.open($1);", popup_url))); |
alexmos | e201c7cd | 2015-06-10 17:14:21 | [diff] [blame] | 765 | Shell* new_shell = new_shell_observer.GetShell(); |
| 766 | WebContents* new_contents = new_shell->web_contents(); |
Fergal Daly | f052233 | 2020-07-18 06:09:46 | [diff] [blame] | 767 | EXPECT_TRUE(WaitForLoadStop(new_contents)); |
alexmos | e201c7cd | 2015-06-10 17:14:21 | [diff] [blame] | 768 | |
| 769 | // Check that the new window's opener points to the correct subframe on |
| 770 | // original window. |
| 771 | FrameTreeNode* popup_root = |
Carlos Caballero | 15caeeb | 2021-10-27 09:57:55 | [diff] [blame^] | 772 | static_cast<WebContentsImpl*>(new_contents)->GetPrimaryFrameTree().root(); |
alexmos | e201c7cd | 2015-06-10 17:14:21 | [diff] [blame] | 773 | EXPECT_EQ(root->child_at(0), popup_root->opener()); |
| 774 | |
| 775 | // Close the original window. This should clear the new window's opener. |
| 776 | shell()->Close(); |
| 777 | EXPECT_EQ(nullptr, popup_root->opener()); |
| 778 | } |
| 779 | |
Shivani Sharma | c4f56158 | 2018-11-15 15:58:39 | [diff] [blame] | 780 | // Tests that the user activation bits get cleared when a same-site document is |
| 781 | // installed in the frame. |
| 782 | IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, |
| 783 | ClearUserActivationForNewDocument) { |
| 784 | GURL main_url(embedded_test_server()->GetURL("/frame_tree/top.html")); |
| 785 | EXPECT_TRUE(NavigateToURL(shell(), main_url)); |
| 786 | |
| 787 | // It is safe to obtain the root frame tree node here, as it doesn't change. |
| 788 | FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents()) |
Carlos Caballero | 15caeeb | 2021-10-27 09:57:55 | [diff] [blame^] | 789 | ->GetPrimaryFrameTree() |
| 790 | .root(); |
Shivani Sharma | c4f56158 | 2018-11-15 15:58:39 | [diff] [blame] | 791 | |
Mustaq Ahmed | 4baa9a6e8 | 2019-12-20 23:43:46 | [diff] [blame] | 792 | EXPECT_FALSE(root->HasStickyUserActivation()); |
Shivani Sharma | c4f56158 | 2018-11-15 15:58:39 | [diff] [blame] | 793 | EXPECT_FALSE(root->HasTransientUserActivation()); |
| 794 | |
| 795 | // Set the user activation bits. |
| 796 | root->UpdateUserActivationState( |
Mustaq Ahmed | dc195e5b | 2020-08-04 18:45:11 | [diff] [blame] | 797 | blink::mojom::UserActivationUpdateType::kNotifyActivation, |
| 798 | blink::mojom::UserActivationNotificationType::kTest); |
Mustaq Ahmed | 4baa9a6e8 | 2019-12-20 23:43:46 | [diff] [blame] | 799 | EXPECT_TRUE(root->HasStickyUserActivation()); |
Shivani Sharma | c4f56158 | 2018-11-15 15:58:39 | [diff] [blame] | 800 | EXPECT_TRUE(root->HasTransientUserActivation()); |
| 801 | |
| 802 | // Install a new same-site document to check the clearing of user activation |
| 803 | // bits. |
| 804 | GURL url(embedded_test_server()->GetURL("/title1.html")); |
| 805 | EXPECT_TRUE(NavigateToURL(shell(), url)); |
| 806 | |
Mustaq Ahmed | 4baa9a6e8 | 2019-12-20 23:43:46 | [diff] [blame] | 807 | EXPECT_FALSE(root->HasStickyUserActivation()); |
Shivani Sharma | c4f56158 | 2018-11-15 15:58:39 | [diff] [blame] | 808 | EXPECT_FALSE(root->HasTransientUserActivation()); |
| 809 | } |
| 810 | |
Dominic Farolino | 316e793 | 2021-09-03 21:13:59 | [diff] [blame] | 811 | class FencedFrameTreeBrowserTest |
| 812 | : public FrameTreeBrowserTest, |
| 813 | public ::testing::WithParamInterface< |
| 814 | blink::features::FencedFramesImplementationType> { |
shivanigithub | 93878d0 | 2021-06-15 11:37:53 | [diff] [blame] | 815 | public: |
Dominic Farolino | 316e793 | 2021-09-03 21:13:59 | [diff] [blame] | 816 | // Provides meaningful param names instead of /0 and /1. |
| 817 | static std::string DescribeParams( |
| 818 | const ::testing::TestParamInfo<ParamType>& info) { |
| 819 | switch (info.param) { |
| 820 | case blink::features::FencedFramesImplementationType::kShadowDOM: |
| 821 | return "ShadowDOM"; |
| 822 | case blink::features::FencedFramesImplementationType::kMPArch: |
| 823 | return "MPArch"; |
| 824 | } |
| 825 | } |
| 826 | |
shivanigithub | 93878d0 | 2021-06-15 11:37:53 | [diff] [blame] | 827 | FencedFrameTreeBrowserTest() |
| 828 | : https_server_(net::EmbeddedTestServer::TYPE_HTTPS) { |
shivanigithub | f3e92da | 2021-10-07 15:37:41 | [diff] [blame] | 829 | scoped_feature_list_.InitWithFeaturesAndParameters( |
| 830 | {{blink::features::kFencedFrames, |
| 831 | {{"implementation_type", |
| 832 | GetParam() == |
| 833 | blink::features::FencedFramesImplementationType::kShadowDOM |
| 834 | ? "shadow_dom" |
| 835 | : "mparch"}}}, |
| 836 | {blink::features::kThirdPartyStoragePartitioning, {}}}, |
| 837 | {/* disabled_features */}); |
Dominic Farolino | 316e793 | 2021-09-03 21:13:59 | [diff] [blame] | 838 | } |
| 839 | |
| 840 | // `node` is expected to be the child FrameTreeNode created in response to a |
| 841 | // <fencedframe> element being created. This test class is parameterized over |
| 842 | // the MPArch and the ShadowDOM implementation of fenced frames, which is why |
| 843 | // this method: |
| 844 | // - Returns `node` if we're in the ShadowDOM version |
| 845 | // - Returns the FrameTreeNode of the fenced frame's inner FrameTree, if |
| 846 | // we're in the MPArch version of fenced frames |
| 847 | FrameTreeNode* GetFencedFrameRootNode(FrameTreeNode* node) { |
| 848 | if (GetParam() == |
| 849 | blink::features::FencedFramesImplementationType::kShadowDOM) |
| 850 | return node; |
| 851 | |
| 852 | int inner_node_id = |
| 853 | node->current_frame_host()->inner_tree_main_frame_tree_node_id(); |
| 854 | return FrameTreeNode::GloballyFindByID(inner_node_id); |
| 855 | } |
| 856 | |
| 857 | // This is needed because `TestFrameNavigationObserver` doesn't work properly |
| 858 | // from within the context of a fenced frame's FrameTree. See the comments |
| 859 | // below. |
| 860 | void NavigateFrameInsideFencedFrameTreeAndWaitForFinishedLoad( |
| 861 | const ToRenderFrameHost& adapter, |
| 862 | GURL url, |
Nan Lin | c4aa6f8 | 2021-10-14 19:15:24 | [diff] [blame] | 863 | const std::string& navigate_script, |
| 864 | net::Error expected_net_error_code = net::OK) { |
Dominic Farolino | 316e793 | 2021-09-03 21:13:59 | [diff] [blame] | 865 | RenderFrameHostImpl* rfh = |
| 866 | static_cast<RenderFrameHostImpl*>(adapter.render_frame_host()); |
Nan Lin | c4aa6f8 | 2021-10-14 19:15:24 | [diff] [blame] | 867 | |
| 868 | FrameTreeNode* frame_tree_node = rfh->frame_tree_node(); |
| 869 | EXPECT_TRUE(frame_tree_node->IsInFencedFrameTree()); |
Dominic Farolino | 316e793 | 2021-09-03 21:13:59 | [diff] [blame] | 870 | |
| 871 | // For the ShadowDOM version of fenced frames, we can just use a |
| 872 | // `TestFrameNavigationObserver` as normal directly on the frame that is |
| 873 | // navigating. |
| 874 | if (GetParam() == |
| 875 | blink::features::FencedFramesImplementationType::kShadowDOM) { |
| 876 | TestFrameNavigationObserver observer(rfh); |
| 877 | EXPECT_EQ(url.spec(), EvalJs(rfh->GetParent(), navigate_script)); |
| 878 | observer.Wait(); |
Nan Lin | c4aa6f8 | 2021-10-14 19:15:24 | [diff] [blame] | 879 | EXPECT_EQ(observer.last_net_error_code(), expected_net_error_code); |
Dominic Farolino | 316e793 | 2021-09-03 21:13:59 | [diff] [blame] | 880 | return; |
| 881 | } |
| 882 | |
| 883 | // For the MPArch version of fenced frames, `TestFrameNavigationObserver` |
| 884 | // does not fully work inside of a fenced frame FrameTree. `WaitForCommit()` |
| 885 | // works, but `Wait()` always times out because it expects to hear the |
| 886 | // DidFinishedLoad event from the outer WebContents, which is not |
| 887 | // communicated by nested FrameTrees. |
| 888 | FencedFrame* fenced_frame = GetMatchingFencedFrameInOuterFrameTree(rfh); |
| 889 | EXPECT_EQ(url.spec(), |
Dave Tapuska | 5dd5f982 | 2021-09-10 20:57:15 | [diff] [blame] | 890 | EvalJs(rfh->GetParentOrOuterDocument(), navigate_script)); |
Dominic Farolino | 316e793 | 2021-09-03 21:13:59 | [diff] [blame] | 891 | fenced_frame->WaitForDidStopLoadingForTesting(); |
Nan Lin | c4aa6f8 | 2021-10-14 19:15:24 | [diff] [blame] | 892 | // `rfh` might be destroyed and invalid at this point. |
| 893 | EXPECT_EQ(frame_tree_node->current_frame_host()->IsErrorDocument(), |
| 894 | expected_net_error_code != net::OK); |
shivanigithub | 93878d0 | 2021-06-15 11:37:53 | [diff] [blame] | 895 | } |
| 896 | |
shivanigithub | f33e469c | 2021-08-02 13:52:20 | [diff] [blame] | 897 | void SetUpOnMainThread() override { |
| 898 | // Set up the host resolver to allow serving separate sites, so we can |
| 899 | // perform cross-process navigation. |
| 900 | host_resolver()->AddRule("*", "127.0.0.1"); |
| 901 | |
| 902 | // Fenced frames require potentially trustworthy URLs so creating an https |
| 903 | // server. |
| 904 | https_server_.RegisterRequestMonitor( |
| 905 | base::BindRepeating(&FencedFrameTreeBrowserTest::ObserveRequestHeaders, |
| 906 | base::Unretained(this))); |
| 907 | https_server_.ServeFilesFromSourceDirectory(GetTestDataFilePath()); |
| 908 | https_server_.SetSSLConfig(net::EmbeddedTestServer::CERT_TEST_NAMES); |
| 909 | ASSERT_TRUE(https_server_.Start()); |
| 910 | } |
| 911 | |
| 912 | // Invoked on "EmbeddedTestServer IO Thread". |
| 913 | void ObserveRequestHeaders(const net::test_server::HttpRequest& request) { |
| 914 | base::AutoLock auto_lock(requests_lock_); |
| 915 | std::string val = request.headers.find("Cookie") != request.headers.end() |
| 916 | ? request.headers.at("Cookie").c_str() |
| 917 | : ""; |
| 918 | cookie_headers_map_.insert(std::make_pair(request.GetURL().path(), val)); |
Nan Lin | d91c815 | 2021-10-21 16:22:37 | [diff] [blame] | 919 | |
| 920 | val = request.headers.find("Sec-Fetch-Dest") != request.headers.end() |
| 921 | ? request.headers.at("Sec-Fetch-Dest").c_str() |
| 922 | : ""; |
| 923 | sec_fetch_dest_headers_map_.insert( |
| 924 | std::make_pair(request.GetURL().path(), val)); |
shivanigithub | f33e469c | 2021-08-02 13:52:20 | [diff] [blame] | 925 | } |
| 926 | |
| 927 | // Returns true if the cookie header was present in the last request received |
| 928 | // by the server with the same `url.path()`. Also asserts that the cookie |
| 929 | // header value matches that given in `expected_value`, if it exists. Also |
| 930 | // clears the value that was just checked by the method invocation. |
| 931 | bool CheckAndClearCookieHeader( |
| 932 | const GURL& url, |
Nan Lin | d91c815 | 2021-10-21 16:22:37 | [diff] [blame] | 933 | const std::string& expected_value = "", |
| 934 | const base::Location& from_here = base::Location::Current()) { |
shivanigithub | f33e469c | 2021-08-02 13:52:20 | [diff] [blame] | 935 | base::AutoLock auto_lock(requests_lock_); |
| 936 | SCOPED_TRACE(from_here.ToString()); |
| 937 | DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 938 | std::string file_name = url.path(); |
| 939 | CHECK(cookie_headers_map_.find(file_name) != cookie_headers_map_.end()); |
| 940 | std::string header = cookie_headers_map_[file_name]; |
| 941 | EXPECT_EQ(expected_value, header); |
| 942 | cookie_headers_map_.erase(file_name); |
| 943 | return !header.empty(); |
| 944 | } |
| 945 | |
Nan Lin | d91c815 | 2021-10-21 16:22:37 | [diff] [blame] | 946 | bool CheckAndClearSecFetchDestHeader( |
| 947 | const GURL& url, |
| 948 | const std::string& expected_value = "", |
| 949 | const base::Location& from_here = base::Location::Current()) { |
| 950 | base::AutoLock auto_lock(requests_lock_); |
| 951 | SCOPED_TRACE(from_here.ToString()); |
| 952 | DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 953 | std::string file_name = url.path(); |
| 954 | CHECK(sec_fetch_dest_headers_map_.find(file_name) != |
| 955 | sec_fetch_dest_headers_map_.end()); |
| 956 | std::string header = sec_fetch_dest_headers_map_[file_name]; |
| 957 | EXPECT_EQ(expected_value, header); |
| 958 | sec_fetch_dest_headers_map_.erase(file_name); |
| 959 | return !header.empty(); |
| 960 | } |
| 961 | |
shivanigithub | 93878d0 | 2021-06-15 11:37:53 | [diff] [blame] | 962 | net::EmbeddedTestServer* https_server() { return &https_server_; } |
| 963 | |
shivanigithub | f33e469c | 2021-08-02 13:52:20 | [diff] [blame] | 964 | ~FencedFrameTreeBrowserTest() override { |
| 965 | // Shutdown the server explicitly so that there is no race with the |
| 966 | // destruction of cookie_headers_map_ and invocation of RequestMonitor. |
| 967 | EXPECT_TRUE(https_server_.ShutdownAndWaitUntilComplete()); |
| 968 | } |
| 969 | |
shivanigithub | 93878d0 | 2021-06-15 11:37:53 | [diff] [blame] | 970 | private: |
Dominic Farolino | 316e793 | 2021-09-03 21:13:59 | [diff] [blame] | 971 | // This method takes in a RenderFrameHostImpl that must be inside a fenced |
| 972 | // frame FrameTree, and returns the FencedFrame* object that represents this |
| 973 | // inner FrameTree from the outer FrameTree. |
| 974 | FencedFrame* GetMatchingFencedFrameInOuterFrameTree( |
| 975 | RenderFrameHostImpl* rfh) { |
| 976 | EXPECT_EQ(GetParam(), |
| 977 | blink::features::FencedFramesImplementationType::kMPArch); |
| 978 | // `rfh` doesn't always have to be a root frame, since this needs to work |
| 979 | // for arbitrary frames within a fenced frame. |
| 980 | EXPECT_TRUE(rfh->frame_tree_node()->IsInFencedFrameTree()); |
| 981 | |
| 982 | RenderFrameHostImpl* outer_delegate_frame = |
Dave Tapuska | 5dd5f982 | 2021-09-10 20:57:15 | [diff] [blame] | 983 | rfh->GetMainFrame()->GetParentOrOuterDocument(); |
Dominic Farolino | 316e793 | 2021-09-03 21:13:59 | [diff] [blame] | 984 | |
| 985 | std::vector<FencedFrame*> fenced_frames = |
| 986 | outer_delegate_frame->GetFencedFrames(); |
| 987 | EXPECT_FALSE(fenced_frames.empty()); |
| 988 | |
| 989 | for (FencedFrame* fenced_frame : fenced_frames) { |
| 990 | if (fenced_frame->GetInnerRoot() == rfh->GetMainFrame()) { |
| 991 | return fenced_frame; |
| 992 | } |
| 993 | } |
| 994 | |
| 995 | NOTREACHED(); |
| 996 | return nullptr; |
| 997 | } |
| 998 | |
shivanigithub | 93878d0 | 2021-06-15 11:37:53 | [diff] [blame] | 999 | base::test::ScopedFeatureList scoped_feature_list_; |
shivanigithub | f33e469c | 2021-08-02 13:52:20 | [diff] [blame] | 1000 | base::Lock requests_lock_; |
| 1001 | std::map<std::string, std::string> cookie_headers_map_ |
| 1002 | GUARDED_BY(requests_lock_); |
Nan Lin | d91c815 | 2021-10-21 16:22:37 | [diff] [blame] | 1003 | std::map<std::string, std::string> sec_fetch_dest_headers_map_ |
| 1004 | GUARDED_BY(requests_lock_); |
shivanigithub | 93878d0 | 2021-06-15 11:37:53 | [diff] [blame] | 1005 | net::EmbeddedTestServer https_server_; |
| 1006 | }; |
| 1007 | |
| 1008 | // Tests that the fenced frame gets navigated to an actual url given a urn:uuid. |
Dominic Farolino | 316e793 | 2021-09-03 21:13:59 | [diff] [blame] | 1009 | IN_PROC_BROWSER_TEST_P(FencedFrameTreeBrowserTest, |
shivanigithub | 93878d0 | 2021-06-15 11:37:53 | [diff] [blame] | 1010 | CheckFencedFrameNavigationWithUUID) { |
shivanigithub | f33e469c | 2021-08-02 13:52:20 | [diff] [blame] | 1011 | GURL main_url = https_server()->GetURL("b.test", "/hello.html"); |
shivanigithub | 93878d0 | 2021-06-15 11:37:53 | [diff] [blame] | 1012 | EXPECT_TRUE(NavigateToURL(shell(), main_url)); |
shivanigithub | 93878d0 | 2021-06-15 11:37:53 | [diff] [blame] | 1013 | // It is safe to obtain the root frame tree node here, as it doesn't change. |
| 1014 | FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents()) |
Carlos Caballero | 15caeeb | 2021-10-27 09:57:55 | [diff] [blame^] | 1015 | ->GetPrimaryFrameTree() |
| 1016 | .root(); |
shivanigithub | 93878d0 | 2021-06-15 11:37:53 | [diff] [blame] | 1017 | |
| 1018 | { |
| 1019 | EXPECT_TRUE(ExecJs(root, |
| 1020 | "var f = document.createElement('fencedframe');" |
| 1021 | "document.body.appendChild(f);")); |
| 1022 | } |
| 1023 | EXPECT_EQ(1U, root->child_count()); |
Dominic Farolino | 316e793 | 2021-09-03 21:13:59 | [diff] [blame] | 1024 | FrameTreeNode* fenced_frame_root_node = |
| 1025 | GetFencedFrameRootNode(root->child_at(0)); |
shivanigithub | 93878d0 | 2021-06-15 11:37:53 | [diff] [blame] | 1026 | |
Dominic Farolino | 316e793 | 2021-09-03 21:13:59 | [diff] [blame] | 1027 | EXPECT_TRUE(fenced_frame_root_node->IsFencedFrameRoot()); |
| 1028 | EXPECT_TRUE(fenced_frame_root_node->IsInFencedFrameTree()); |
shivanigithub | 93878d0 | 2021-06-15 11:37:53 | [diff] [blame] | 1029 | |
Nan Lin | c4aa6f8 | 2021-10-14 19:15:24 | [diff] [blame] | 1030 | GURL https_url( |
| 1031 | https_server()->GetURL("a.test", "/fenced_frames/title1.html")); |
shivanigithub | 93878d0 | 2021-06-15 11:37:53 | [diff] [blame] | 1032 | FencedFrameURLMapping& url_mapping = |
| 1033 | root->current_frame_host()->GetPage().fenced_frame_urls_map(); |
| 1034 | GURL urn_uuid = url_mapping.AddFencedFrameURL(https_url); |
| 1035 | EXPECT_TRUE(urn_uuid.is_valid()); |
| 1036 | |
| 1037 | std::string navigate_urn_script = JsReplace("f.src = $1;", urn_uuid.spec()); |
| 1038 | |
| 1039 | { |
Dominic Farolino | 316e793 | 2021-09-03 21:13:59 | [diff] [blame] | 1040 | TestFrameNavigationObserver observer(fenced_frame_root_node); |
shivanigithub | 93878d0 | 2021-06-15 11:37:53 | [diff] [blame] | 1041 | EXPECT_EQ(urn_uuid.spec(), EvalJs(root, navigate_urn_script)); |
Dominic Farolino | 316e793 | 2021-09-03 21:13:59 | [diff] [blame] | 1042 | observer.WaitForCommit(); |
shivanigithub | 93878d0 | 2021-06-15 11:37:53 | [diff] [blame] | 1043 | } |
| 1044 | |
Dominic Farolino | 316e793 | 2021-09-03 21:13:59 | [diff] [blame] | 1045 | EXPECT_EQ( |
| 1046 | https_url, |
| 1047 | fenced_frame_root_node->current_frame_host()->GetLastCommittedURL()); |
| 1048 | EXPECT_EQ( |
| 1049 | url::Origin::Create(https_url), |
| 1050 | fenced_frame_root_node->current_frame_host()->GetLastCommittedOrigin()); |
shivanigithub | 93878d0 | 2021-06-15 11:37:53 | [diff] [blame] | 1051 | |
| 1052 | // Parent will still see the src as the urn_uuid and not the mapped url. |
| 1053 | EXPECT_EQ(urn_uuid.spec(), EvalJs(root, "f.src")); |
| 1054 | |
| 1055 | // The parent will not be able to access window.frames[0] as fenced frames are |
| 1056 | // not visible via frames[]. |
| 1057 | EXPECT_FALSE(ExecJs(root, "window.frames[0].location")); |
| 1058 | EXPECT_EQ(0, EvalJs(root, "window.frames.length")); |
| 1059 | } |
| 1060 | |
Dominic Farolino | 316e793 | 2021-09-03 21:13:59 | [diff] [blame] | 1061 | IN_PROC_BROWSER_TEST_P(FencedFrameTreeBrowserTest, CheckFencedFrameNoCookies) { |
shivanigithub | f33e469c | 2021-08-02 13:52:20 | [diff] [blame] | 1062 | // Create an a.test main page and set cookies. Then create a same-origin |
| 1063 | // fenced frame. Its request should not carry the cookies that were set. |
| 1064 | GURL main_url = https_server()->GetURL("a.test", "/hello.html"); |
| 1065 | EXPECT_TRUE(NavigateToURL(shell(), main_url)); |
| 1066 | |
| 1067 | // It is safe to obtain the root frame tree node here, as it doesn't change. |
| 1068 | RenderFrameHostImpl* root_rfh = |
| 1069 | static_cast<WebContentsImpl*>(shell()->web_contents()) |
Carlos Caballero | 15caeeb | 2021-10-27 09:57:55 | [diff] [blame^] | 1070 | ->GetPrimaryFrameTree() |
| 1071 | .root() |
shivanigithub | f33e469c | 2021-08-02 13:52:20 | [diff] [blame] | 1072 | ->current_frame_host(); |
| 1073 | |
| 1074 | // Set SameSite=Lax and SameSite=None cookies and retrieve them. |
| 1075 | EXPECT_TRUE(ExecJs(root_rfh, |
| 1076 | "document.cookie = 'B=2; SameSite=Lax';" |
| 1077 | "document.cookie = 'C=2; SameSite=None; Secure';")); |
| 1078 | EXPECT_EQ("B=2; C=2", EvalJs(root_rfh, "document.cookie;")); |
| 1079 | |
| 1080 | // Test the fenced frame. |
| 1081 | EXPECT_TRUE(ExecJs(root_rfh, |
| 1082 | "var f = document.createElement('fencedframe');" |
| 1083 | "document.body.appendChild(f);")); |
| 1084 | EXPECT_EQ(1U, root_rfh->child_count()); |
| 1085 | |
Dominic Farolino | 316e793 | 2021-09-03 21:13:59 | [diff] [blame] | 1086 | FrameTreeNode* fenced_frame_root_node = |
| 1087 | GetFencedFrameRootNode(root_rfh->child_at(0)); |
shivanigithub | f33e469c | 2021-08-02 13:52:20 | [diff] [blame] | 1088 | |
Dominic Farolino | 316e793 | 2021-09-03 21:13:59 | [diff] [blame] | 1089 | EXPECT_TRUE(fenced_frame_root_node->IsFencedFrameRoot()); |
| 1090 | EXPECT_TRUE(fenced_frame_root_node->IsInFencedFrameTree()); |
shivanigithub | f33e469c | 2021-08-02 13:52:20 | [diff] [blame] | 1091 | |
Nan Lin | c4aa6f8 | 2021-10-14 19:15:24 | [diff] [blame] | 1092 | GURL https_url( |
| 1093 | https_server()->GetURL("a.test", "/fenced_frames/title1.html")); |
shivanigithub | f33e469c | 2021-08-02 13:52:20 | [diff] [blame] | 1094 | FencedFrameURLMapping& url_mapping = |
| 1095 | root_rfh->GetPage().fenced_frame_urls_map(); |
| 1096 | GURL urn_uuid = url_mapping.AddFencedFrameURL(https_url); |
| 1097 | EXPECT_TRUE(urn_uuid.is_valid()); |
| 1098 | |
| 1099 | std::string navigate_urn_script = JsReplace("f.src = $1;", urn_uuid.spec()); |
Dominic Farolino | 316e793 | 2021-09-03 21:13:59 | [diff] [blame] | 1100 | NavigateFrameInsideFencedFrameTreeAndWaitForFinishedLoad( |
| 1101 | fenced_frame_root_node, urn_uuid, navigate_urn_script); |
| 1102 | EXPECT_EQ( |
| 1103 | https_url, |
| 1104 | fenced_frame_root_node->current_frame_host()->GetLastCommittedURL()); |
| 1105 | EXPECT_EQ( |
| 1106 | url::Origin::Create(https_url), |
| 1107 | fenced_frame_root_node->current_frame_host()->GetLastCommittedOrigin()); |
shivanigithub | f33e469c | 2021-08-02 13:52:20 | [diff] [blame] | 1108 | |
| 1109 | EXPECT_FALSE(CheckAndClearCookieHeader(https_url)); |
| 1110 | |
| 1111 | // Run the same test for an iframe inside the fenced frame. It shouldn't be |
| 1112 | // able to send cookies either. |
| 1113 | // Add a nested iframe inside the fenced frame. |
Dominic Farolino | 316e793 | 2021-09-03 21:13:59 | [diff] [blame] | 1114 | EXPECT_EQ(0U, fenced_frame_root_node->child_count()); |
| 1115 | EXPECT_TRUE(ExecJs(fenced_frame_root_node, |
shivanigithub | f33e469c | 2021-08-02 13:52:20 | [diff] [blame] | 1116 | "var f1 = document.createElement('iframe');" |
| 1117 | "document.body.appendChild(f1);")); |
Dominic Farolino | 316e793 | 2021-09-03 21:13:59 | [diff] [blame] | 1118 | EXPECT_EQ(1U, fenced_frame_root_node->child_count()); |
| 1119 | EXPECT_FALSE(fenced_frame_root_node->child_at(0)->IsFencedFrameRoot()); |
| 1120 | EXPECT_TRUE(fenced_frame_root_node->child_at(0)->IsInFencedFrameTree()); |
shivanigithub | f33e469c | 2021-08-02 13:52:20 | [diff] [blame] | 1121 | std::string navigate_script = JsReplace("f1.src = $1;", main_url.spec()); |
| 1122 | |
Dominic Farolino | 316e793 | 2021-09-03 21:13:59 | [diff] [blame] | 1123 | NavigateFrameInsideFencedFrameTreeAndWaitForFinishedLoad( |
| 1124 | fenced_frame_root_node->child_at(0), main_url, navigate_script); |
| 1125 | |
| 1126 | EXPECT_EQ(main_url, fenced_frame_root_node->child_at(0) |
| 1127 | ->current_frame_host() |
| 1128 | ->GetLastCommittedURL()); |
| 1129 | EXPECT_EQ(url::Origin::Create(main_url), fenced_frame_root_node->child_at(0) |
shivanigithub | f33e469c | 2021-08-02 13:52:20 | [diff] [blame] | 1130 | ->current_frame_host() |
| 1131 | ->GetLastCommittedOrigin()); |
| 1132 | EXPECT_FALSE(CheckAndClearCookieHeader(main_url)); |
| 1133 | |
| 1134 | // Check that a subresource request from the main document should have the |
| 1135 | // cookies since that is outside the fenced frame tree. |
| 1136 | ResourceLoadObserver observer(shell()); |
| 1137 | GURL image_url = https_server()->GetURL("a.test", "/image.jpg"); |
| 1138 | EXPECT_TRUE( |
| 1139 | ExecJs(root_rfh, JsReplace("var img = document.createElement('img');" |
| 1140 | "document.body.appendChild(img);", |
| 1141 | image_url))); |
| 1142 | std::string load_script = JsReplace("img.src = $1;", image_url.spec()); |
| 1143 | EXPECT_EQ(image_url.spec(), EvalJs(root_rfh, load_script)); |
| 1144 | observer.WaitForResourceCompletion(image_url); |
| 1145 | EXPECT_TRUE(CheckAndClearCookieHeader(image_url, "B=2; C=2")); |
| 1146 | } |
| 1147 | |
shivanigithub | f3ddff5 | 2021-07-03 22:06:30 | [diff] [blame] | 1148 | // Tests when a frame is considered a fenced frame or being inside a fenced |
| 1149 | // frame tree. |
Dominic Farolino | 316e793 | 2021-09-03 21:13:59 | [diff] [blame] | 1150 | IN_PROC_BROWSER_TEST_P(FencedFrameTreeBrowserTest, CheckIsFencedFrame) { |
shivanigithub | f33e469c | 2021-08-02 13:52:20 | [diff] [blame] | 1151 | GURL main_url(https_server()->GetURL("a.test", "/hello.html")); |
shivanigithub | f3ddff5 | 2021-07-03 22:06:30 | [diff] [blame] | 1152 | EXPECT_TRUE(NavigateToURL(shell(), main_url)); |
| 1153 | |
| 1154 | // It is safe to obtain the root frame tree node here, as it doesn't change. |
| 1155 | FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents()) |
Carlos Caballero | 15caeeb | 2021-10-27 09:57:55 | [diff] [blame^] | 1156 | ->GetPrimaryFrameTree() |
| 1157 | .root(); |
shivanigithub | f3ddff5 | 2021-07-03 22:06:30 | [diff] [blame] | 1158 | |
| 1159 | EXPECT_TRUE(ExecJs(root, |
Dominic Farolino | 316e793 | 2021-09-03 21:13:59 | [diff] [blame] | 1160 | "var fenced_frame = document.createElement('fencedframe');" |
| 1161 | "document.body.appendChild(fenced_frame);")); |
| 1162 | |
shivanigithub | f3ddff5 | 2021-07-03 22:06:30 | [diff] [blame] | 1163 | EXPECT_EQ(1U, root->child_count()); |
Dominic Farolino | 316e793 | 2021-09-03 21:13:59 | [diff] [blame] | 1164 | |
| 1165 | FrameTreeNode* fenced_frame_root_node = |
| 1166 | GetFencedFrameRootNode(root->child_at(0)); |
| 1167 | EXPECT_TRUE(fenced_frame_root_node->IsFencedFrameRoot()); |
| 1168 | EXPECT_TRUE(fenced_frame_root_node->IsInFencedFrameTree()); |
shivanigithub | f3ddff5 | 2021-07-03 22:06:30 | [diff] [blame] | 1169 | |
| 1170 | // Add an iframe. |
| 1171 | EXPECT_TRUE(ExecJs(root, |
Dominic Farolino | 316e793 | 2021-09-03 21:13:59 | [diff] [blame] | 1172 | "var iframe = document.createElement('iframe');" |
| 1173 | "document.body.appendChild(iframe);")); |
shivanigithub | f3ddff5 | 2021-07-03 22:06:30 | [diff] [blame] | 1174 | EXPECT_EQ(2U, root->child_count()); |
Dominic Farolino | 4bc10ee | 2021-08-31 00:37:36 | [diff] [blame] | 1175 | EXPECT_FALSE(root->child_at(1)->IsFencedFrameRoot()); |
shivanigithub | f3ddff5 | 2021-07-03 22:06:30 | [diff] [blame] | 1176 | EXPECT_FALSE(root->child_at(1)->IsInFencedFrameTree()); |
| 1177 | |
| 1178 | // Add a nested iframe inside the fenced frame. |
Dominic Farolino | 316e793 | 2021-09-03 21:13:59 | [diff] [blame] | 1179 | // Before we execute script on the fenced frame, we must navigate it once. |
| 1180 | // This is because the root of a FrameTree does not call |
| 1181 | // RenderFrameHostImpl::RenderFrameCreated() on its owned RFHI, meaning there |
| 1182 | // is nothing to execute script in. |
| 1183 | { |
| 1184 | // Navigate the fenced frame. |
Nan Lin | c4aa6f8 | 2021-10-14 19:15:24 | [diff] [blame] | 1185 | GURL fenced_frame_url( |
| 1186 | https_server()->GetURL("a.test", "/fenced_frames/title1.html")); |
Dominic Farolino | 316e793 | 2021-09-03 21:13:59 | [diff] [blame] | 1187 | std::string navigate_script = |
| 1188 | JsReplace("fenced_frame.src = $1;", fenced_frame_url.spec()); |
| 1189 | NavigateFrameInsideFencedFrameTreeAndWaitForFinishedLoad( |
| 1190 | fenced_frame_root_node, fenced_frame_url, navigate_script); |
| 1191 | } |
| 1192 | |
| 1193 | EXPECT_TRUE(ExecJs(fenced_frame_root_node, |
| 1194 | "var nested_iframe = " |
| 1195 | "document.createElement('iframe');document.body." |
| 1196 | "appendChild(nested_iframe);")); |
| 1197 | EXPECT_EQ(1U, fenced_frame_root_node->child_count()); |
| 1198 | EXPECT_FALSE(fenced_frame_root_node->child_at(0)->IsFencedFrameRoot()); |
| 1199 | EXPECT_TRUE(fenced_frame_root_node->child_at(0)->IsInFencedFrameTree()); |
shivanigithub | f3ddff5 | 2021-07-03 22:06:30 | [diff] [blame] | 1200 | |
| 1201 | // Add a nested fenced frame. |
Dominic Farolino | 316e793 | 2021-09-03 21:13:59 | [diff] [blame] | 1202 | EXPECT_TRUE(ExecJs(fenced_frame_root_node, |
| 1203 | "var nested_fenced_frame = " |
| 1204 | "document.createElement('fencedframe');document.body." |
| 1205 | "appendChild(nested_fenced_frame);")); |
| 1206 | EXPECT_EQ(2U, fenced_frame_root_node->child_count()); |
| 1207 | |
| 1208 | FrameTreeNode* nested_fenced_frame_root_node = |
| 1209 | GetFencedFrameRootNode(fenced_frame_root_node->child_at(1)); |
| 1210 | EXPECT_TRUE(nested_fenced_frame_root_node->IsFencedFrameRoot()); |
| 1211 | EXPECT_TRUE(nested_fenced_frame_root_node->IsInFencedFrameTree()); |
shivanigithub | f3ddff5 | 2021-07-03 22:06:30 | [diff] [blame] | 1212 | } |
| 1213 | |
shivanigithub | 4cd016a | 2021-09-20 21:10:30 | [diff] [blame] | 1214 | // Tests a nonce is correctly set in the isolation info for a fenced frame tree. |
shivanigithub | f3e92da | 2021-10-07 15:37:41 | [diff] [blame] | 1215 | IN_PROC_BROWSER_TEST_P(FencedFrameTreeBrowserTest, |
| 1216 | CheckIsolationInfoAndStorageKeyNonce) { |
shivanigithub | 4cd016a | 2021-09-20 21:10:30 | [diff] [blame] | 1217 | GURL main_url(https_server()->GetURL("a.test", "/hello.html")); |
| 1218 | EXPECT_TRUE(NavigateToURL(shell(), main_url)); |
| 1219 | |
| 1220 | // It is safe to obtain the root frame tree node here, as it doesn't change. |
| 1221 | FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents()) |
Carlos Caballero | 15caeeb | 2021-10-27 09:57:55 | [diff] [blame^] | 1222 | ->GetPrimaryFrameTree() |
| 1223 | .root(); |
shivanigithub | 4cd016a | 2021-09-20 21:10:30 | [diff] [blame] | 1224 | |
| 1225 | EXPECT_TRUE(ExecJs(root, |
| 1226 | "var f = document.createElement('fencedframe');" |
| 1227 | "document.body.appendChild(f);")); |
| 1228 | EXPECT_EQ(1U, root->child_count()); |
| 1229 | |
| 1230 | auto* fenced_frame = GetFencedFrameRootNode(root->child_at(0)); |
| 1231 | EXPECT_TRUE(fenced_frame->IsFencedFrameRoot()); |
| 1232 | EXPECT_TRUE(fenced_frame->IsInFencedFrameTree()); |
| 1233 | |
shivanigithub | f3e92da | 2021-10-07 15:37:41 | [diff] [blame] | 1234 | // Before we check the IsolationInfo/StorageKey on the fenced frame, we must |
| 1235 | // navigate it once. This is because the root of a FrameTree does not call |
shivanigithub | 4cd016a | 2021-09-20 21:10:30 | [diff] [blame] | 1236 | // RenderFrameHostImpl::RenderFrameCreated() on its owned RFHI. |
| 1237 | { |
| 1238 | // Navigate the fenced frame. |
Nan Lin | c4aa6f8 | 2021-10-14 19:15:24 | [diff] [blame] | 1239 | GURL fenced_frame_url( |
| 1240 | https_server()->GetURL("a.test", "/fenced_frames/title1.html")); |
shivanigithub | 4cd016a | 2021-09-20 21:10:30 | [diff] [blame] | 1241 | std::string navigate_script = |
| 1242 | JsReplace("f.src = $1;", fenced_frame_url.spec()); |
| 1243 | NavigateFrameInsideFencedFrameTreeAndWaitForFinishedLoad( |
| 1244 | fenced_frame, fenced_frame_url, navigate_script); |
| 1245 | } |
| 1246 | |
| 1247 | // There should be a nonce in the IsolationInfo. |
| 1248 | const net::IsolationInfo& isolation_info = |
| 1249 | fenced_frame->current_frame_host()->GetIsolationInfoForSubresources(); |
| 1250 | EXPECT_TRUE(isolation_info.nonce().has_value()); |
| 1251 | absl::optional<base::UnguessableToken> fenced_frame_nonce = |
| 1252 | fenced_frame->fenced_frame_nonce(); |
| 1253 | EXPECT_TRUE(fenced_frame_nonce.has_value()); |
| 1254 | EXPECT_EQ(fenced_frame_nonce.value(), isolation_info.nonce().value()); |
| 1255 | |
shivanigithub | f3e92da | 2021-10-07 15:37:41 | [diff] [blame] | 1256 | // There should be a nonce in the StorageKey. |
| 1257 | EXPECT_TRUE( |
| 1258 | fenced_frame->current_frame_host()->storage_key().nonce().has_value()); |
| 1259 | EXPECT_EQ(fenced_frame_nonce.value(), |
| 1260 | fenced_frame->current_frame_host()->storage_key().nonce().value()); |
| 1261 | |
| 1262 | // Add an iframe. It should not have a nonce. |
shivanigithub | 4cd016a | 2021-09-20 21:10:30 | [diff] [blame] | 1263 | EXPECT_TRUE(ExecJs(root, |
| 1264 | "var subframe = document.createElement('iframe');" |
| 1265 | "document.body.appendChild(subframe);")); |
| 1266 | EXPECT_EQ(2U, root->child_count()); |
| 1267 | auto* iframe = root->child_at(1); |
| 1268 | EXPECT_FALSE(iframe->IsFencedFrameRoot()); |
| 1269 | EXPECT_FALSE(iframe->IsInFencedFrameTree()); |
| 1270 | const net::IsolationInfo& iframe_isolation_info = |
| 1271 | iframe->current_frame_host()->GetIsolationInfoForSubresources(); |
| 1272 | EXPECT_FALSE(iframe_isolation_info.nonce().has_value()); |
shivanigithub | f3e92da | 2021-10-07 15:37:41 | [diff] [blame] | 1273 | EXPECT_FALSE(iframe->current_frame_host()->storage_key().nonce().has_value()); |
shivanigithub | 4cd016a | 2021-09-20 21:10:30 | [diff] [blame] | 1274 | |
| 1275 | // Navigate the iframe. It should still not have a nonce. |
| 1276 | EXPECT_TRUE(NavigateToURLFromRenderer( |
| 1277 | iframe, https_server()->GetURL("b.test", "/title1.html"))); |
| 1278 | const net::IsolationInfo& iframe_new_isolation_info = |
| 1279 | iframe->current_frame_host()->GetIsolationInfoForSubresources(); |
shivanigithub | f3e92da | 2021-10-07 15:37:41 | [diff] [blame] | 1280 | |
shivanigithub | 4cd016a | 2021-09-20 21:10:30 | [diff] [blame] | 1281 | EXPECT_FALSE(iframe_new_isolation_info.nonce().has_value()); |
shivanigithub | f3e92da | 2021-10-07 15:37:41 | [diff] [blame] | 1282 | EXPECT_FALSE(iframe->current_frame_host()->storage_key().nonce().has_value()); |
shivanigithub | 4cd016a | 2021-09-20 21:10:30 | [diff] [blame] | 1283 | |
| 1284 | // Add a nested iframe inside the fenced frame. |
| 1285 | EXPECT_TRUE(ExecJs(fenced_frame, |
| 1286 | "var iframe_within_ff = document.createElement('iframe');" |
| 1287 | "document.body.appendChild(iframe_within_ff);")); |
| 1288 | EXPECT_EQ(1U, fenced_frame->child_count()); |
| 1289 | EXPECT_FALSE(fenced_frame->child_at(0)->IsFencedFrameRoot()); |
| 1290 | EXPECT_TRUE(fenced_frame->child_at(0)->IsInFencedFrameTree()); |
| 1291 | const net::IsolationInfo& nested_iframe_isolation_info = |
| 1292 | fenced_frame->child_at(0) |
| 1293 | ->current_frame_host() |
| 1294 | ->GetIsolationInfoForSubresources(); |
| 1295 | EXPECT_TRUE(nested_iframe_isolation_info.nonce().has_value()); |
| 1296 | |
| 1297 | // Check that a nested iframe in the fenced frame tree has the same nonce |
| 1298 | // value as its parent. |
| 1299 | EXPECT_EQ(fenced_frame_nonce.value(), |
| 1300 | nested_iframe_isolation_info.nonce().value()); |
| 1301 | absl::optional<base::UnguessableToken> nested_iframe_nonce = |
| 1302 | fenced_frame->child_at(0)->fenced_frame_nonce(); |
| 1303 | EXPECT_EQ(nested_iframe_isolation_info.nonce().value(), |
| 1304 | nested_iframe_nonce.value()); |
shivanigithub | f3e92da | 2021-10-07 15:37:41 | [diff] [blame] | 1305 | EXPECT_EQ(fenced_frame_nonce.value(), fenced_frame->child_at(0) |
| 1306 | ->current_frame_host() |
| 1307 | ->storage_key() |
| 1308 | .nonce() |
| 1309 | .value()); |
shivanigithub | 4cd016a | 2021-09-20 21:10:30 | [diff] [blame] | 1310 | |
| 1311 | // Navigate the iframe. It should still have the same nonce. |
| 1312 | { |
shivanigithub | f3e92da | 2021-10-07 15:37:41 | [diff] [blame] | 1313 | GURL https_url(https_server()->GetURL("b.test", "/title1.html")); |
shivanigithub | 4cd016a | 2021-09-20 21:10:30 | [diff] [blame] | 1314 | std::string navigate_script = |
| 1315 | JsReplace("iframe_within_ff.src = $1;", https_url.spec()); |
| 1316 | NavigateFrameInsideFencedFrameTreeAndWaitForFinishedLoad( |
| 1317 | fenced_frame->child_at(0), https_url, navigate_script); |
| 1318 | } |
| 1319 | const net::IsolationInfo& nested_iframe_new_isolation_info = |
| 1320 | fenced_frame->child_at(0) |
| 1321 | ->current_frame_host() |
| 1322 | ->GetIsolationInfoForSubresources(); |
| 1323 | EXPECT_EQ(nested_iframe_new_isolation_info.nonce().value(), |
| 1324 | nested_iframe_nonce.value()); |
shivanigithub | f3e92da | 2021-10-07 15:37:41 | [diff] [blame] | 1325 | EXPECT_EQ(fenced_frame_nonce.value(), fenced_frame->child_at(0) |
| 1326 | ->current_frame_host() |
| 1327 | ->storage_key() |
| 1328 | .nonce() |
| 1329 | .value()); |
shivanigithub | 4cd016a | 2021-09-20 21:10:30 | [diff] [blame] | 1330 | |
| 1331 | // Add a nested fenced frame. |
| 1332 | EXPECT_TRUE( |
| 1333 | ExecJs(fenced_frame, |
| 1334 | "var nested_fenced_frame = document.createElement('fencedframe');" |
| 1335 | "document.body.appendChild(nested_fenced_frame);")); |
| 1336 | EXPECT_EQ(2U, fenced_frame->child_count()); |
| 1337 | auto* nested_fenced_frame = GetFencedFrameRootNode(fenced_frame->child_at(1)); |
| 1338 | EXPECT_TRUE(nested_fenced_frame->IsFencedFrameRoot()); |
| 1339 | EXPECT_TRUE(nested_fenced_frame->IsInFencedFrameTree()); |
| 1340 | absl::optional<base::UnguessableToken> nested_fframe_nonce = |
| 1341 | nested_fenced_frame->fenced_frame_nonce(); |
| 1342 | EXPECT_TRUE(nested_fframe_nonce.has_value()); |
| 1343 | |
| 1344 | // Check that a nested fenced frame has a different value than its parent |
| 1345 | // fenced frame. |
| 1346 | EXPECT_NE(fenced_frame_nonce.value(), nested_fframe_nonce.value()); |
| 1347 | |
| 1348 | // Check that the nonce does not change when there is a cross-document |
| 1349 | // navigation. |
| 1350 | { |
| 1351 | // Navigate the fenced frame. |
Nan Lin | c4aa6f8 | 2021-10-14 19:15:24 | [diff] [blame] | 1352 | GURL fenced_frame_url( |
| 1353 | https_server()->GetURL("b.test", "/fenced_frames/title1.html")); |
shivanigithub | 4cd016a | 2021-09-20 21:10:30 | [diff] [blame] | 1354 | std::string navigate_script = |
| 1355 | JsReplace("f.src = $1;", fenced_frame_url.spec()); |
| 1356 | NavigateFrameInsideFencedFrameTreeAndWaitForFinishedLoad( |
| 1357 | fenced_frame, fenced_frame_url, navigate_script); |
| 1358 | } |
| 1359 | |
| 1360 | absl::optional<base::UnguessableToken> new_fenced_frame_nonce = |
| 1361 | fenced_frame->fenced_frame_nonce(); |
| 1362 | EXPECT_NE(absl::nullopt, new_fenced_frame_nonce); |
| 1363 | EXPECT_EQ(new_fenced_frame_nonce.value(), fenced_frame_nonce.value()); |
| 1364 | } |
| 1365 | |
shivanigithub | f3e92da | 2021-10-07 15:37:41 | [diff] [blame] | 1366 | // Tests that a fenced frame and a same-origin iframe at the same level do not |
| 1367 | // share the same storage partition. |
| 1368 | IN_PROC_BROWSER_TEST_P(FencedFrameTreeBrowserTest, CheckUniqueStorage) { |
| 1369 | GURL main_url(https_server()->GetURL("a.test", "/hello.html")); |
| 1370 | EXPECT_TRUE(NavigateToURL(shell(), main_url)); |
| 1371 | |
| 1372 | // It is safe to obtain the root frame tree node here, as it doesn't change. |
| 1373 | FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents()) |
Carlos Caballero | 15caeeb | 2021-10-27 09:57:55 | [diff] [blame^] | 1374 | ->GetPrimaryFrameTree() |
| 1375 | .root(); |
shivanigithub | f3e92da | 2021-10-07 15:37:41 | [diff] [blame] | 1376 | |
| 1377 | EXPECT_TRUE(ExecJs(root, |
| 1378 | "var f = document.createElement('fencedframe');" |
| 1379 | "document.body.appendChild(f);")); |
| 1380 | EXPECT_EQ(1U, root->child_count()); |
| 1381 | |
| 1382 | auto* fenced_frame = GetFencedFrameRootNode(root->child_at(0)); |
| 1383 | EXPECT_TRUE(fenced_frame->IsFencedFrameRoot()); |
| 1384 | EXPECT_TRUE(fenced_frame->IsInFencedFrameTree()); |
| 1385 | |
| 1386 | // Before we check the storage key on the fenced frame, we must navigate it |
| 1387 | // once. This is because the root of a FrameTree does not call |
| 1388 | // RenderFrameHostImpl::RenderFrameCreated() on its owned RFHI. |
| 1389 | { |
| 1390 | // Navigate the fenced frame. |
Nan Lin | c4aa6f8 | 2021-10-14 19:15:24 | [diff] [blame] | 1391 | GURL fenced_frame_url( |
| 1392 | https_server()->GetURL("a.test", "/fenced_frames/title1.html")); |
shivanigithub | f3e92da | 2021-10-07 15:37:41 | [diff] [blame] | 1393 | std::string navigate_script = |
| 1394 | JsReplace("f.src = $1;", fenced_frame_url.spec()); |
| 1395 | NavigateFrameInsideFencedFrameTreeAndWaitForFinishedLoad( |
| 1396 | fenced_frame, fenced_frame_url, navigate_script); |
| 1397 | } |
| 1398 | |
| 1399 | // There should be a nonce in the StorageKey. |
| 1400 | EXPECT_TRUE( |
| 1401 | fenced_frame->current_frame_host()->storage_key().nonce().has_value()); |
| 1402 | |
| 1403 | absl::optional<base::UnguessableToken> fenced_frame_nonce = |
| 1404 | fenced_frame->fenced_frame_nonce(); |
| 1405 | EXPECT_TRUE(fenced_frame_nonce.has_value()); |
| 1406 | EXPECT_EQ(fenced_frame_nonce.value(), |
| 1407 | fenced_frame->current_frame_host()->storage_key().nonce().value()); |
| 1408 | |
| 1409 | // Add an iframe. |
| 1410 | EXPECT_TRUE(ExecJs(root, |
| 1411 | "var subframe = document.createElement('iframe');" |
| 1412 | "document.body.appendChild(subframe);")); |
| 1413 | EXPECT_EQ(2U, root->child_count()); |
| 1414 | auto* iframe = root->child_at(1); |
| 1415 | EXPECT_FALSE(iframe->IsFencedFrameRoot()); |
| 1416 | EXPECT_FALSE(iframe->IsInFencedFrameTree()); |
| 1417 | EXPECT_FALSE(iframe->current_frame_host()->storage_key().nonce().has_value()); |
| 1418 | |
| 1419 | // Navigate the iframe. It should still not have a nonce. |
| 1420 | EXPECT_TRUE(NavigateToURLFromRenderer( |
| 1421 | iframe, https_server()->GetURL("a.test", "/title1.html"))); |
| 1422 | |
| 1423 | EXPECT_FALSE(iframe->current_frame_host()->storage_key().nonce().has_value()); |
| 1424 | |
| 1425 | // Set and read a value in the fenced frame's local storage. |
| 1426 | EXPECT_TRUE(ExecJs(fenced_frame, "localStorage[\"foo\"] = \"a\"")); |
| 1427 | EXPECT_EQ("a", EvalJs(fenced_frame, "localStorage[\"foo\"]")); |
| 1428 | |
| 1429 | // Set and read a value in the iframe's local storage. |
| 1430 | EXPECT_TRUE(ExecJs(iframe, "localStorage[\"foo\"] = \"b\"")); |
| 1431 | EXPECT_EQ("b", EvalJs(iframe, "localStorage[\"foo\"]")); |
| 1432 | |
| 1433 | // Set and read a value in the top-frame's local storage. |
| 1434 | EXPECT_TRUE(ExecJs(root, "localStorage[\"foo\"] = \"c\"")); |
| 1435 | EXPECT_EQ("c", EvalJs(root, "localStorage[\"foo\"]")); |
| 1436 | |
| 1437 | // TODO(crbug.com/1199077) This should return "a" once StorageKey starts |
| 1438 | // using the nonce for partitioning. Also remove the shadowDOM specific check |
| 1439 | // once nonce support is complete (for MPArch, possibly due to a separate |
| 1440 | // process and incomplete nonce support, it is returning "a" on some |
| 1441 | // platforms). |
| 1442 | if (GetParam() == |
| 1443 | blink::features::FencedFramesImplementationType::kShadowDOM) { |
| 1444 | EXPECT_EQ("c", EvalJs(fenced_frame, "localStorage[\"foo\"]")); |
| 1445 | } |
| 1446 | } |
| 1447 | |
Nan Lin | c4aa6f8 | 2021-10-14 19:15:24 | [diff] [blame] | 1448 | IN_PROC_BROWSER_TEST_P(FencedFrameTreeBrowserTest, |
| 1449 | CheckFencedFrameNotNavigatedWithoutOptIn) { |
| 1450 | GURL main_url = https_server()->GetURL("b.test", "/hello.html"); |
| 1451 | EXPECT_TRUE(NavigateToURL(shell(), main_url)); |
| 1452 | // It is safe to obtain the root frame tree node here, as it doesn't change. |
| 1453 | FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents()) |
Carlos Caballero | 15caeeb | 2021-10-27 09:57:55 | [diff] [blame^] | 1454 | ->GetPrimaryFrameTree() |
| 1455 | .root(); |
Nan Lin | c4aa6f8 | 2021-10-14 19:15:24 | [diff] [blame] | 1456 | |
| 1457 | { |
| 1458 | EXPECT_TRUE(ExecJs(root, |
| 1459 | "var f = document.createElement('fencedframe');" |
| 1460 | "document.body.appendChild(f);")); |
| 1461 | } |
| 1462 | EXPECT_EQ(1U, root->child_count()); |
| 1463 | FrameTreeNode* fenced_frame_root_node = |
| 1464 | GetFencedFrameRootNode(root->child_at(0)); |
| 1465 | |
| 1466 | GURL https_url(https_server()->GetURL("a.test", "/title1.html")); |
| 1467 | FencedFrameURLMapping& url_mapping = |
| 1468 | root->current_frame_host()->GetPage().fenced_frame_urls_map(); |
| 1469 | GURL urn_uuid = url_mapping.AddFencedFrameURL(https_url); |
| 1470 | EXPECT_TRUE(urn_uuid.is_valid()); |
| 1471 | |
| 1472 | std::string navigate_urn_script = JsReplace("f.src = $1;", urn_uuid.spec()); |
| 1473 | NavigateFrameInsideFencedFrameTreeAndWaitForFinishedLoad( |
| 1474 | fenced_frame_root_node, urn_uuid, navigate_urn_script, |
| 1475 | net::ERR_BLOCKED_BY_RESPONSE); |
| 1476 | } |
| 1477 | |
Nan Lin | d91c815 | 2021-10-21 16:22:37 | [diff] [blame] | 1478 | IN_PROC_BROWSER_TEST_P(FencedFrameTreeBrowserTest, CheckSecFetchDestHeader) { |
| 1479 | GURL main_url(https_server()->GetURL("a.test", "/hello.html")); |
| 1480 | EXPECT_TRUE(NavigateToURL(shell(), main_url)); |
| 1481 | // It is safe to obtain the root frame tree node here, as it doesn't change. |
| 1482 | FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents()) |
| 1483 | ->GetFrameTree() |
| 1484 | ->root(); |
| 1485 | |
| 1486 | EXPECT_TRUE(ExecJs(root, |
| 1487 | "var fenced_frame = document.createElement('fencedframe');" |
| 1488 | "document.body.appendChild(fenced_frame);")); |
| 1489 | |
| 1490 | EXPECT_EQ(1U, root->child_count()); |
| 1491 | |
| 1492 | FrameTreeNode* fenced_frame_root_node = |
| 1493 | GetFencedFrameRootNode(root->child_at(0)); |
| 1494 | |
| 1495 | { |
| 1496 | // Navigate the fenced frame. |
| 1497 | GURL fenced_frame_url( |
| 1498 | https_server()->GetURL("a.test", "/fenced_frames/title1.html")); |
| 1499 | std::string navigate_script = |
| 1500 | JsReplace("fenced_frame.src = $1;", fenced_frame_url.spec()); |
| 1501 | NavigateFrameInsideFencedFrameTreeAndWaitForFinishedLoad( |
| 1502 | fenced_frame_root_node, fenced_frame_url, navigate_script); |
| 1503 | EXPECT_TRUE( |
| 1504 | CheckAndClearSecFetchDestHeader(fenced_frame_url, "fencedframe")); |
| 1505 | } |
| 1506 | |
| 1507 | // Add a nested iframe inside the fenced frame. |
| 1508 | EXPECT_TRUE(ExecJs(fenced_frame_root_node, |
| 1509 | "var iframe = document.createElement('iframe');" |
| 1510 | "document.body.appendChild(iframe);")); |
| 1511 | EXPECT_EQ(1U, fenced_frame_root_node->child_count()); |
| 1512 | |
| 1513 | { |
| 1514 | // Navigate the iframe. |
| 1515 | GURL iframe_url(https_server()->GetURL("a.test", "/title2.html")); |
| 1516 | std::string navigate_script = |
| 1517 | JsReplace("iframe.src = $1;", iframe_url.spec()); |
| 1518 | NavigateFrameInsideFencedFrameTreeAndWaitForFinishedLoad( |
| 1519 | fenced_frame_root_node->child_at(0), iframe_url, navigate_script); |
| 1520 | EXPECT_TRUE(CheckAndClearSecFetchDestHeader(iframe_url, "iframe")); |
| 1521 | } |
| 1522 | } |
| 1523 | |
Dominic Farolino | 316e793 | 2021-09-03 21:13:59 | [diff] [blame] | 1524 | INSTANTIATE_TEST_SUITE_P( |
| 1525 | All, |
| 1526 | FencedFrameTreeBrowserTest, |
| 1527 | ::testing::Values( |
| 1528 | blink::features::FencedFramesImplementationType::kShadowDOM, |
| 1529 | blink::features::FencedFramesImplementationType::kMPArch), |
| 1530 | &FencedFrameTreeBrowserTest::DescribeParams); |
| 1531 | |
[email protected] | 82307f6b | 2014-08-07 03:30:12 | [diff] [blame] | 1532 | class CrossProcessFrameTreeBrowserTest : public ContentBrowserTest { |
| 1533 | public: |
Fergal Daly | a1d56997 | 2021-03-16 03:24:53 | [diff] [blame] | 1534 | CrossProcessFrameTreeBrowserTest() = default; |
[email protected] | 82307f6b | 2014-08-07 03:30:12 | [diff] [blame] | 1535 | |
dcheng | c2282aa | 2014-10-21 12:07:58 | [diff] [blame] | 1536 | void SetUpCommandLine(base::CommandLine* command_line) override { |
nick | d30fd96 | 2015-07-27 21:51:08 | [diff] [blame] | 1537 | IsolateAllSitesForTesting(command_line); |
[email protected] | 82307f6b | 2014-08-07 03:30:12 | [diff] [blame] | 1538 | } |
| 1539 | |
alexmos | 478dcbb | 2014-12-10 21:24:46 | [diff] [blame] | 1540 | void SetUpOnMainThread() override { |
| 1541 | host_resolver()->AddRule("*", "127.0.0.1"); |
alexmos | 478dcbb | 2014-12-10 21:24:46 | [diff] [blame] | 1542 | SetupCrossSiteRedirector(embedded_test_server()); |
martijn | 12d2dad | 2016-11-11 10:59:27 | [diff] [blame] | 1543 | ASSERT_TRUE(embedded_test_server()->Start()); |
alexmos | 478dcbb | 2014-12-10 21:24:46 | [diff] [blame] | 1544 | } |
| 1545 | |
[email protected] | 82307f6b | 2014-08-07 03:30:12 | [diff] [blame] | 1546 | private: |
| 1547 | DISALLOW_COPY_AND_ASSIGN(CrossProcessFrameTreeBrowserTest); |
| 1548 | }; |
| 1549 | |
| 1550 | // Ensure that we can complete a cross-process subframe navigation. |
[email protected] | 82307f6b | 2014-08-07 03:30:12 | [diff] [blame] | 1551 | IN_PROC_BROWSER_TEST_F(CrossProcessFrameTreeBrowserTest, |
nasko | 83fe5dc | 2015-07-01 16:34:19 | [diff] [blame] | 1552 | CreateCrossProcessSubframeProxies) { |
alexmos | 478dcbb | 2014-12-10 21:24:46 | [diff] [blame] | 1553 | GURL main_url(embedded_test_server()->GetURL("/site_per_process_main.html")); |
Alex Moshchuk | aa95adf5 | 2019-08-13 00:02:02 | [diff] [blame] | 1554 | EXPECT_TRUE(NavigateToURL(shell(), main_url)); |
[email protected] | 82307f6b | 2014-08-07 03:30:12 | [diff] [blame] | 1555 | |
| 1556 | // It is safe to obtain the root frame tree node here, as it doesn't change. |
| 1557 | FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents()) |
Carlos Caballero | 15caeeb | 2021-10-27 09:57:55 | [diff] [blame^] | 1558 | ->GetPrimaryFrameTree() |
| 1559 | .root(); |
[email protected] | 82307f6b | 2014-08-07 03:30:12 | [diff] [blame] | 1560 | |
[email protected] | 14522ce4 | 2014-08-08 18:56:16 | [diff] [blame] | 1561 | // There should not be a proxy for the root's own SiteInstance. |
| 1562 | SiteInstance* root_instance = root->current_frame_host()->GetSiteInstance(); |
| 1563 | EXPECT_FALSE(root->render_manager()->GetRenderFrameProxyHost(root_instance)); |
| 1564 | |
[email protected] | 82307f6b | 2014-08-07 03:30:12 | [diff] [blame] | 1565 | // Load same-site page into iframe. |
alexmos | 478dcbb | 2014-12-10 21:24:46 | [diff] [blame] | 1566 | GURL http_url(embedded_test_server()->GetURL("/title1.html")); |
Lukasz Anforowicz | 69c25dfd | 2020-11-12 21:50:20 | [diff] [blame] | 1567 | EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(0), http_url)); |
[email protected] | 82307f6b | 2014-08-07 03:30:12 | [diff] [blame] | 1568 | |
[email protected] | 82307f6b | 2014-08-07 03:30:12 | [diff] [blame] | 1569 | // Load cross-site page into iframe. |
alexmos | 478dcbb | 2014-12-10 21:24:46 | [diff] [blame] | 1570 | GURL cross_site_url( |
| 1571 | embedded_test_server()->GetURL("foo.com", "/title2.html")); |
Lukasz Anforowicz | 69c25dfd | 2020-11-12 21:50:20 | [diff] [blame] | 1572 | EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(0), cross_site_url)); |
[email protected] | 82307f6b | 2014-08-07 03:30:12 | [diff] [blame] | 1573 | |
| 1574 | // Ensure that we have created a new process for the subframe. |
nasko | e6edde3 | 2014-10-17 15:36:48 | [diff] [blame] | 1575 | ASSERT_EQ(2U, root->child_count()); |
[email protected] | 82307f6b | 2014-08-07 03:30:12 | [diff] [blame] | 1576 | FrameTreeNode* child = root->child_at(0); |
[email protected] | 14522ce4 | 2014-08-08 18:56:16 | [diff] [blame] | 1577 | SiteInstance* child_instance = child->current_frame_host()->GetSiteInstance(); |
[email protected] | 82307f6b | 2014-08-07 03:30:12 | [diff] [blame] | 1578 | RenderViewHost* rvh = child->current_frame_host()->render_view_host(); |
| 1579 | RenderProcessHost* rph = child->current_frame_host()->GetProcess(); |
| 1580 | |
Lucas Furukawa Gadani | b9e03af | 2020-10-27 20:30:37 | [diff] [blame] | 1581 | EXPECT_NE(shell()->web_contents()->GetMainFrame()->GetRenderViewHost(), rvh); |
[email protected] | 14522ce4 | 2014-08-08 18:56:16 | [diff] [blame] | 1582 | EXPECT_NE(shell()->web_contents()->GetSiteInstance(), child_instance); |
Lukasz Anforowicz | 9e0ce4e | 2017-09-28 19:09:15 | [diff] [blame] | 1583 | EXPECT_NE(shell()->web_contents()->GetMainFrame()->GetProcess(), rph); |
[email protected] | 82307f6b | 2014-08-07 03:30:12 | [diff] [blame] | 1584 | |
| 1585 | // Ensure that the root node has a proxy for the child node's SiteInstance. |
[email protected] | 14522ce4 | 2014-08-08 18:56:16 | [diff] [blame] | 1586 | EXPECT_TRUE(root->render_manager()->GetRenderFrameProxyHost(child_instance)); |
[email protected] | 82307f6b | 2014-08-07 03:30:12 | [diff] [blame] | 1587 | |
| 1588 | // Also ensure that the child has a proxy for the root node's SiteInstance. |
[email protected] | 14522ce4 | 2014-08-08 18:56:16 | [diff] [blame] | 1589 | EXPECT_TRUE(child->render_manager()->GetRenderFrameProxyHost(root_instance)); |
| 1590 | |
| 1591 | // The nodes should not have proxies for their own SiteInstance. |
| 1592 | EXPECT_FALSE(root->render_manager()->GetRenderFrameProxyHost(root_instance)); |
| 1593 | EXPECT_FALSE( |
| 1594 | child->render_manager()->GetRenderFrameProxyHost(child_instance)); |
creis | e42f2a5 | 2014-09-18 18:14:57 | [diff] [blame] | 1595 | |
| 1596 | // Ensure that the RenderViews and RenderFrames are all live. |
| 1597 | EXPECT_TRUE( |
| 1598 | root->current_frame_host()->render_view_host()->IsRenderViewLive()); |
| 1599 | EXPECT_TRUE( |
| 1600 | child->current_frame_host()->render_view_host()->IsRenderViewLive()); |
| 1601 | EXPECT_TRUE(root->current_frame_host()->IsRenderFrameLive()); |
| 1602 | EXPECT_TRUE(root->child_at(0)->current_frame_host()->IsRenderFrameLive()); |
[email protected] | 82307f6b | 2014-08-07 03:30:12 | [diff] [blame] | 1603 | } |
| 1604 | |
alexmos | bc7eafa | 2014-12-06 01:38:09 | [diff] [blame] | 1605 | IN_PROC_BROWSER_TEST_F(CrossProcessFrameTreeBrowserTest, |
Nasko Oskov | 03912101 | 2019-01-11 00:21:32 | [diff] [blame] | 1606 | OriginSetOnNavigations) { |
alexmos | 478dcbb | 2014-12-10 21:24:46 | [diff] [blame] | 1607 | GURL main_url(embedded_test_server()->GetURL("/site_per_process_main.html")); |
alexmos | bc7eafa | 2014-12-06 01:38:09 | [diff] [blame] | 1608 | EXPECT_TRUE(NavigateToURL(shell(), main_url)); |
| 1609 | |
| 1610 | // It is safe to obtain the root frame tree node here, as it doesn't change. |
| 1611 | FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents()) |
Carlos Caballero | 15caeeb | 2021-10-27 09:57:55 | [diff] [blame^] | 1612 | ->GetPrimaryFrameTree() |
| 1613 | .root(); |
alexmos | bc7eafa | 2014-12-06 01:38:09 | [diff] [blame] | 1614 | |
creis | abdd2bd6 | 2015-11-21 01:14:58 | [diff] [blame] | 1615 | EXPECT_EQ(root->current_origin().Serialize() + '/', |
Mike West | 800532c | 2021-10-14 09:26:52 | [diff] [blame] | 1616 | main_url.DeprecatedGetOriginAsURL().spec()); |
alexmos | bc7eafa | 2014-12-06 01:38:09 | [diff] [blame] | 1617 | |
| 1618 | // First frame is an about:blank frame. Check that its origin is correctly |
| 1619 | // inherited from the parent. |
creis | abdd2bd6 | 2015-11-21 01:14:58 | [diff] [blame] | 1620 | EXPECT_EQ(root->child_at(0)->current_origin().Serialize() + '/', |
Mike West | 800532c | 2021-10-14 09:26:52 | [diff] [blame] | 1621 | main_url.DeprecatedGetOriginAsURL().spec()); |
alexmos | bc7eafa | 2014-12-06 01:38:09 | [diff] [blame] | 1622 | |
| 1623 | // Second frame loads a same-site page. Its origin should also be the same |
| 1624 | // as the parent. |
creis | abdd2bd6 | 2015-11-21 01:14:58 | [diff] [blame] | 1625 | EXPECT_EQ(root->child_at(1)->current_origin().Serialize() + '/', |
Mike West | 800532c | 2021-10-14 09:26:52 | [diff] [blame] | 1626 | main_url.DeprecatedGetOriginAsURL().spec()); |
alexmos | bc7eafa | 2014-12-06 01:38:09 | [diff] [blame] | 1627 | |
alexmos | bc7eafa | 2014-12-06 01:38:09 | [diff] [blame] | 1628 | // Load cross-site page into the first frame. |
alexmos | 478dcbb | 2014-12-10 21:24:46 | [diff] [blame] | 1629 | GURL cross_site_url( |
| 1630 | embedded_test_server()->GetURL("foo.com", "/title2.html")); |
Lukasz Anforowicz | 69c25dfd | 2020-11-12 21:50:20 | [diff] [blame] | 1631 | EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(0), cross_site_url)); |
alexmos | bc7eafa | 2014-12-06 01:38:09 | [diff] [blame] | 1632 | |
creis | abdd2bd6 | 2015-11-21 01:14:58 | [diff] [blame] | 1633 | EXPECT_EQ(root->child_at(0)->current_origin().Serialize() + '/', |
Mike West | 800532c | 2021-10-14 09:26:52 | [diff] [blame] | 1634 | cross_site_url.DeprecatedGetOriginAsURL().spec()); |
alexmos | bc7eafa | 2014-12-06 01:38:09 | [diff] [blame] | 1635 | |
| 1636 | // The root's origin shouldn't have changed. |
creis | abdd2bd6 | 2015-11-21 01:14:58 | [diff] [blame] | 1637 | EXPECT_EQ(root->current_origin().Serialize() + '/', |
Mike West | 800532c | 2021-10-14 09:26:52 | [diff] [blame] | 1638 | main_url.DeprecatedGetOriginAsURL().spec()); |
alexmos | bc7eafa | 2014-12-06 01:38:09 | [diff] [blame] | 1639 | |
Nasko Oskov | 03912101 | 2019-01-11 00:21:32 | [diff] [blame] | 1640 | { |
| 1641 | GURL data_url("data:text/html,foo"); |
| 1642 | TestNavigationObserver observer(shell()->web_contents()); |
| 1643 | EXPECT_TRUE( |
| 1644 | ExecJs(root->child_at(1), JsReplace("window.location = $1", data_url))); |
| 1645 | observer.Wait(); |
| 1646 | } |
alexmos | bc7eafa | 2014-12-06 01:38:09 | [diff] [blame] | 1647 | |
| 1648 | // Navigating to a data URL should set a unique origin. This is represented |
Nasko Oskov | 03912101 | 2019-01-11 00:21:32 | [diff] [blame] | 1649 | // as "null" per RFC 6454. A frame navigating itself to a data: URL does not |
| 1650 | // require a process transfer, but should retain the original origin |
| 1651 | // as its precursor. |
creis | abdd2bd6 | 2015-11-21 01:14:58 | [diff] [blame] | 1652 | EXPECT_EQ(root->child_at(1)->current_origin().Serialize(), "null"); |
Nasko Oskov | 03912101 | 2019-01-11 00:21:32 | [diff] [blame] | 1653 | EXPECT_TRUE(root->child_at(1)->current_origin().opaque()); |
| 1654 | ASSERT_EQ( |
| 1655 | url::SchemeHostPort(main_url), |
| 1656 | root->child_at(1)->current_origin().GetTupleOrPrecursorTupleIfOpaque()) |
| 1657 | << "Expected the precursor origin to be preserved; should be the " |
| 1658 | "initiator of a data: navigation."; |
| 1659 | |
| 1660 | // Adding an <iframe sandbox srcdoc=> frame should result in a unique origin |
| 1661 | // that is different-origin from its data: URL parent. |
| 1662 | { |
| 1663 | TestNavigationObserver observer(shell()->web_contents()); |
| 1664 | |
| 1665 | ASSERT_EQ(0U, root->child_at(1)->child_count()); |
| 1666 | EXPECT_TRUE( |
| 1667 | ExecJs(root->child_at(1), JsReplace( |
| 1668 | R"( |
| 1669 | var iframe = document.createElement('iframe'); |
| 1670 | iframe.setAttribute('sandbox', 'allow-scripts'); |
| 1671 | iframe.srcdoc = $1; |
| 1672 | document.body.appendChild(iframe); |
| 1673 | )", |
| 1674 | "<html><body>This sandboxed doc should " |
| 1675 | "be different-origin.</body></html>"))); |
| 1676 | observer.Wait(); |
| 1677 | ASSERT_EQ(1U, root->child_at(1)->child_count()); |
| 1678 | } |
| 1679 | |
| 1680 | url::Origin root_origin = root->current_origin(); |
| 1681 | url::Origin child_1 = root->child_at(1)->current_origin(); |
| 1682 | url::Origin child_1_0 = root->child_at(1)->child_at(0)->current_origin(); |
| 1683 | EXPECT_FALSE(root_origin.opaque()); |
| 1684 | EXPECT_TRUE(child_1.opaque()); |
| 1685 | EXPECT_TRUE(child_1_0.opaque()); |
| 1686 | EXPECT_NE(child_1, child_1_0); |
| 1687 | EXPECT_EQ(url::SchemeHostPort(main_url), |
| 1688 | root_origin.GetTupleOrPrecursorTupleIfOpaque()); |
| 1689 | EXPECT_EQ(url::SchemeHostPort(main_url), |
| 1690 | child_1.GetTupleOrPrecursorTupleIfOpaque()); |
| 1691 | EXPECT_EQ(url::SchemeHostPort(main_url), |
| 1692 | child_1_0.GetTupleOrPrecursorTupleIfOpaque()); |
| 1693 | |
| 1694 | { |
| 1695 | TestNavigationObserver observer(shell()->web_contents()); |
| 1696 | |
| 1697 | ASSERT_EQ(1U, root->child_at(1)->child_count()); |
| 1698 | EXPECT_TRUE( |
| 1699 | ExecJs(root->child_at(1), JsReplace( |
| 1700 | R"( |
| 1701 | var iframe = document.createElement('iframe'); |
| 1702 | iframe.srcdoc = $1; |
| 1703 | document.body.appendChild(iframe); |
| 1704 | )", |
| 1705 | "<html><body>This srcdoc document should " |
| 1706 | "be same-origin.</body></html>"))); |
| 1707 | observer.Wait(); |
| 1708 | ASSERT_EQ(2U, root->child_at(1)->child_count()); |
| 1709 | } |
| 1710 | EXPECT_EQ(root_origin, root->current_origin()); |
| 1711 | EXPECT_EQ(child_1, root->child_at(1)->current_origin()); |
| 1712 | EXPECT_EQ(child_1_0, root->child_at(1)->child_at(0)->current_origin()); |
| 1713 | url::Origin child_1_1 = root->child_at(1)->child_at(1)->current_origin(); |
| 1714 | EXPECT_EQ(child_1, child_1_1); |
| 1715 | EXPECT_NE(child_1_0, child_1_1); |
| 1716 | |
| 1717 | { |
| 1718 | TestNavigationObserver observer(shell()->web_contents()); |
| 1719 | |
| 1720 | ASSERT_EQ(2U, root->child_at(1)->child_count()); |
| 1721 | EXPECT_TRUE( |
| 1722 | ExecJs(root->child_at(1), JsReplace( |
| 1723 | R"( |
| 1724 | var iframe = document.createElement('iframe'); |
| 1725 | iframe.src = 'data:text/html;base64,' + btoa($1); |
| 1726 | document.body.appendChild(iframe); |
| 1727 | )", |
| 1728 | "<html><body>This data: doc should be " |
| 1729 | "different-origin.</body></html>"))); |
| 1730 | observer.Wait(); |
| 1731 | ASSERT_EQ(3U, root->child_at(1)->child_count()); |
| 1732 | } |
| 1733 | EXPECT_EQ(root_origin, root->current_origin()); |
| 1734 | EXPECT_EQ(child_1, root->child_at(1)->current_origin()); |
| 1735 | EXPECT_EQ(child_1_0, root->child_at(1)->child_at(0)->current_origin()); |
| 1736 | EXPECT_EQ(child_1_1, root->child_at(1)->child_at(1)->current_origin()); |
| 1737 | url::Origin child_1_2 = root->child_at(1)->child_at(2)->current_origin(); |
| 1738 | EXPECT_NE(child_1, child_1_2); |
| 1739 | EXPECT_NE(child_1_0, child_1_2); |
| 1740 | EXPECT_NE(child_1_1, child_1_2); |
| 1741 | EXPECT_EQ(url::SchemeHostPort(main_url), |
| 1742 | child_1_2.GetTupleOrPrecursorTupleIfOpaque()); |
| 1743 | |
| 1744 | // If the parent navigates its child to a data URL, it should transfer |
| 1745 | // to the parent's process, and the precursor origin should track the |
| 1746 | // parent's origin. |
| 1747 | { |
| 1748 | GURL data_url("data:text/html,foo2"); |
| 1749 | TestNavigationObserver observer(shell()->web_contents()); |
| 1750 | EXPECT_TRUE(ExecJs(root, JsReplace("frames[0].location = $1", data_url))); |
| 1751 | observer.Wait(); |
| 1752 | EXPECT_EQ(data_url, root->child_at(0)->current_url()); |
| 1753 | } |
| 1754 | |
| 1755 | EXPECT_EQ(root->child_at(0)->current_origin().Serialize(), "null"); |
| 1756 | EXPECT_TRUE(root->child_at(0)->current_origin().opaque()); |
| 1757 | EXPECT_EQ( |
| 1758 | url::SchemeHostPort(main_url), |
| 1759 | root->child_at(0)->current_origin().GetTupleOrPrecursorTupleIfOpaque()); |
| 1760 | EXPECT_EQ(root->current_frame_host()->GetProcess(), |
| 1761 | root->child_at(0)->current_frame_host()->GetProcess()); |
| 1762 | } |
| 1763 | |
| 1764 | // Test to verify that a blob: URL that is created by a unique opaque origin |
| 1765 | // will correctly set the origin_to_commit on a session history navigation. |
| 1766 | IN_PROC_BROWSER_TEST_F(CrossProcessFrameTreeBrowserTest, |
| 1767 | OriginForBlobUrlsFromUniqueOpaqueOrigin) { |
| 1768 | // Start off with a navigation to data: URL in the main frame. It should |
| 1769 | // result in a unique opaque origin without any precursor information. |
| 1770 | GURL data_url("data:text/html,foo<iframe id='child' src='" + |
| 1771 | embedded_test_server()->GetURL("/title1.html").spec() + |
| 1772 | "'></iframe>"); |
| 1773 | EXPECT_TRUE(NavigateToURL(shell(), data_url)); |
| 1774 | |
| 1775 | FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents()) |
Carlos Caballero | 15caeeb | 2021-10-27 09:57:55 | [diff] [blame^] | 1776 | ->GetPrimaryFrameTree() |
| 1777 | .root(); |
Nasko Oskov | 03912101 | 2019-01-11 00:21:32 | [diff] [blame] | 1778 | EXPECT_TRUE(root->current_origin().opaque()); |
Nasko Oskov | 5511938 | 2020-01-17 18:22:18 | [diff] [blame] | 1779 | EXPECT_FALSE( |
| 1780 | root->current_origin().GetTupleOrPrecursorTupleIfOpaque().IsValid()); |
Nasko Oskov | 03912101 | 2019-01-11 00:21:32 | [diff] [blame] | 1781 | EXPECT_EQ(1UL, root->child_count()); |
| 1782 | FrameTreeNode* child = root->child_at(0); |
| 1783 | |
| 1784 | // Create a blob: URL and navigate the child frame to it. |
| 1785 | std::string html = "<html><body>This is blob content.</body></html>"; |
| 1786 | std::string script = JsReplace( |
| 1787 | "var blob = new Blob([$1], {type: 'text/html'});" |
| 1788 | "var blob_url = URL.createObjectURL(blob);" |
| 1789 | "document.getElementById('child').src = blob_url;" |
| 1790 | "blob_url;", |
| 1791 | html); |
| 1792 | GURL blob_url; |
| 1793 | { |
| 1794 | TestFrameNavigationObserver observer(child); |
| 1795 | blob_url = GURL(EvalJs(root, script).ExtractString()); |
| 1796 | observer.Wait(); |
| 1797 | EXPECT_EQ(blob_url, child->current_frame_host()->GetLastCommittedURL()); |
| 1798 | } |
| 1799 | |
| 1800 | // We expect the frame to have committed in an opaque origin which contains |
| 1801 | // the same precursor information - none. |
| 1802 | url::Origin blob_origin = child->current_origin(); |
| 1803 | EXPECT_TRUE(blob_origin.opaque()); |
| 1804 | EXPECT_EQ(root->current_origin().GetTupleOrPrecursorTupleIfOpaque(), |
| 1805 | blob_origin.GetTupleOrPrecursorTupleIfOpaque()); |
| 1806 | |
| 1807 | // Navigate the frame away to any web URL. |
| 1808 | { |
| 1809 | GURL url(embedded_test_server()->GetURL("/title2.html")); |
| 1810 | TestFrameNavigationObserver observer(child); |
| 1811 | EXPECT_TRUE(ExecJs(child, JsReplace("window.location = $1", url))); |
| 1812 | observer.Wait(); |
| 1813 | EXPECT_EQ(url, child->current_frame_host()->GetLastCommittedURL()); |
| 1814 | } |
| 1815 | EXPECT_FALSE(child->current_origin().opaque()); |
| 1816 | EXPECT_TRUE(shell()->web_contents()->GetController().CanGoBack()); |
| 1817 | EXPECT_EQ(3, shell()->web_contents()->GetController().GetEntryCount()); |
| 1818 | EXPECT_EQ( |
| 1819 | 2, shell()->web_contents()->GetController().GetLastCommittedEntryIndex()); |
| 1820 | |
| 1821 | // Verify the blob URL still exists in the main frame, which keeps it alive |
| 1822 | // allowing a session history navigation back to succeed. |
| 1823 | EXPECT_EQ(blob_url, GURL(EvalJs(root, "blob_url;").ExtractString())); |
| 1824 | |
| 1825 | // Now navigate back in session history. It should successfully go back to |
| 1826 | // the blob: URL. |
| 1827 | { |
| 1828 | TestFrameNavigationObserver observer(child); |
| 1829 | shell()->web_contents()->GetController().GoBack(); |
| 1830 | observer.Wait(); |
| 1831 | } |
| 1832 | EXPECT_EQ(blob_url, child->current_frame_host()->GetLastCommittedURL()); |
| 1833 | EXPECT_TRUE(child->current_origin().opaque()); |
| 1834 | EXPECT_EQ(blob_origin, child->current_origin()); |
| 1835 | EXPECT_EQ(root->current_origin().GetTupleOrPrecursorTupleIfOpaque(), |
| 1836 | child->current_origin().GetTupleOrPrecursorTupleIfOpaque()); |
| 1837 | } |
| 1838 | |
| 1839 | // Test to verify that about:blank iframe, which is a child of a sandboxed |
| 1840 | // iframe is not considered same origin, but precursor information is preserved |
| 1841 | // in its origin. |
| 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 | 03912101 | 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(); |
arthursonzogni | b93a447 | 2020-04-10 07:38:00 | [diff] [blame] | 1920 | EXPECT_EQ(network::mojom::WebSandboxFlags::kAll & |
| 1921 | ~network::mojom::WebSandboxFlags::kPopups & |
| 1922 | ~network::mojom::WebSandboxFlags::kPointerLock & |
| 1923 | ~network::mojom::WebSandboxFlags::kScripts & |
| 1924 | ~network::mojom::WebSandboxFlags::kAutomaticFeatures, |
Ian Clelland | 5cbaaf8 | 2017-11-27 22:00:03 | [diff] [blame] | 1925 | main_frame_sandbox_flags); |
| 1926 | |
| 1927 | EXPECT_EQ(main_frame_sandbox_flags, |
| 1928 | popup_root->effective_frame_policy().sandbox_flags); |
| 1929 | EXPECT_EQ(main_frame_sandbox_flags, popup_root->active_sandbox_flags()); |
| 1930 | EXPECT_EQ(main_frame_sandbox_flags, |
| 1931 | popup_root->current_frame_host()->active_sandbox_flags()); |
| 1932 | } |
| 1933 | |
Shivani Sharma | c4f56158 | 2018-11-15 15:58:39 | [diff] [blame] | 1934 | // Tests that the user activation bits get cleared when a cross-site document is |
| 1935 | // installed in the frame. |
| 1936 | IN_PROC_BROWSER_TEST_F(CrossProcessFrameTreeBrowserTest, |
| 1937 | ClearUserActivationForNewDocument) { |
| 1938 | GURL main_url(embedded_test_server()->GetURL("/frame_tree/top.html")); |
| 1939 | EXPECT_TRUE(NavigateToURL(shell(), main_url)); |
| 1940 | |
| 1941 | // It is safe to obtain the root frame tree node here, as it doesn't change. |
| 1942 | FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents()) |
Carlos Caballero | 15caeeb | 2021-10-27 09:57:55 | [diff] [blame^] | 1943 | ->GetPrimaryFrameTree() |
| 1944 | .root(); |
Shivani Sharma | c4f56158 | 2018-11-15 15:58:39 | [diff] [blame] | 1945 | |
Mustaq Ahmed | 4baa9a6e8 | 2019-12-20 23:43:46 | [diff] [blame] | 1946 | EXPECT_FALSE(root->HasStickyUserActivation()); |
Shivani Sharma | c4f56158 | 2018-11-15 15:58:39 | [diff] [blame] | 1947 | EXPECT_FALSE(root->HasTransientUserActivation()); |
| 1948 | |
| 1949 | // Set the user activation bits. |
| 1950 | root->UpdateUserActivationState( |
Mustaq Ahmed | dc195e5b | 2020-08-04 18:45:11 | [diff] [blame] | 1951 | blink::mojom::UserActivationUpdateType::kNotifyActivation, |
| 1952 | blink::mojom::UserActivationNotificationType::kTest); |
Mustaq Ahmed | 4baa9a6e8 | 2019-12-20 23:43:46 | [diff] [blame] | 1953 | EXPECT_TRUE(root->HasStickyUserActivation()); |
Shivani Sharma | c4f56158 | 2018-11-15 15:58:39 | [diff] [blame] | 1954 | EXPECT_TRUE(root->HasTransientUserActivation()); |
| 1955 | |
| 1956 | // Install a new cross-site document to check the clearing of user activation |
| 1957 | // bits. |
| 1958 | GURL cross_site_url( |
| 1959 | embedded_test_server()->GetURL("foo.com", "/title2.html")); |
| 1960 | EXPECT_TRUE(NavigateToURL(shell(), cross_site_url)); |
| 1961 | |
Mustaq Ahmed | 4baa9a6e8 | 2019-12-20 23:43:46 | [diff] [blame] | 1962 | EXPECT_FALSE(root->HasStickyUserActivation()); |
Shivani Sharma | c4f56158 | 2018-11-15 15:58:39 | [diff] [blame] | 1963 | EXPECT_FALSE(root->HasTransientUserActivation()); |
| 1964 | } |
| 1965 | |
nick | db193a1 | 2016-09-09 23:09:23 | [diff] [blame] | 1966 | // FrameTreeBrowserTest variant where we isolate http://*.is, Iceland's top |
nasko | abed2a5 | 2017-05-03 05:10:17 | [diff] [blame] | 1967 | // level domain. This is an analogue to isolating extensions, which we can use |
| 1968 | // inside content_browsertests, where extensions don't exist. Iceland, like an |
nick | db193a1 | 2016-09-09 23:09:23 | [diff] [blame] | 1969 | // extension process, is a special place with magical powers; we want to protect |
| 1970 | // it from outsiders. |
| 1971 | class IsolateIcelandFrameTreeBrowserTest : public ContentBrowserTest { |
| 1972 | public: |
Fergal Daly | a1d56997 | 2021-03-16 03:24:53 | [diff] [blame] | 1973 | IsolateIcelandFrameTreeBrowserTest() = default; |
nick | db193a1 | 2016-09-09 23:09:23 | [diff] [blame] | 1974 | |
| 1975 | void SetUpCommandLine(base::CommandLine* command_line) override { |
Lukasz Anforowicz | e7c87d1 | 2018-11-03 02:53:34 | [diff] [blame] | 1976 | // Blink suppresses navigations to blob URLs of origins different from the |
alexis.menard | 83f1b6d | 2017-05-17 19:37:33 | [diff] [blame] | 1977 | // frame initiating the navigation. We disable those checks for this test, |
| 1978 | // to test what happens in a compromise scenario. |
| 1979 | command_line->AppendSwitch(switches::kDisableWebSecurity); |
Lukasz Anforowicz | e7c87d1 | 2018-11-03 02:53:34 | [diff] [blame] | 1980 | |
| 1981 | // ProcessSwitchForIsolatedBlob test below requires that one of URLs used in |
Lukasz Anforowicz | 2542093 | 2018-12-18 20:59:22 | [diff] [blame] | 1982 | // the test (blob:http://b.is/) belongs to an isolated origin. |
| 1983 | command_line->AppendSwitchASCII(switches::kIsolateOrigins, "http://b.is/"); |
nick | db193a1 | 2016-09-09 23:09:23 | [diff] [blame] | 1984 | } |
| 1985 | |
| 1986 | void SetUpOnMainThread() override { |
| 1987 | host_resolver()->AddRule("*", "127.0.0.1"); |
nick | db193a1 | 2016-09-09 23:09:23 | [diff] [blame] | 1988 | SetupCrossSiteRedirector(embedded_test_server()); |
martijn | 12d2dad | 2016-11-11 10:59:27 | [diff] [blame] | 1989 | ASSERT_TRUE(embedded_test_server()->Start()); |
nick | db193a1 | 2016-09-09 23:09:23 | [diff] [blame] | 1990 | } |
| 1991 | |
| 1992 | private: |
| 1993 | DISALLOW_COPY_AND_ASSIGN(IsolateIcelandFrameTreeBrowserTest); |
| 1994 | }; |
| 1995 | |
| 1996 | // Regression test for https://crbug.com/644966 |
| 1997 | IN_PROC_BROWSER_TEST_F(IsolateIcelandFrameTreeBrowserTest, |
| 1998 | ProcessSwitchForIsolatedBlob) { |
nick | db193a1 | 2016-09-09 23:09:23 | [diff] [blame] | 1999 | // Set up an iframe. |
| 2000 | WebContents* contents = shell()->web_contents(); |
| 2001 | FrameTreeNode* root = |
Carlos Caballero | 15caeeb | 2021-10-27 09:57:55 | [diff] [blame^] | 2002 | static_cast<WebContentsImpl*>(contents)->GetPrimaryFrameTree().root(); |
nick | db193a1 | 2016-09-09 23:09:23 | [diff] [blame] | 2003 | GURL main_url(embedded_test_server()->GetURL( |
nick | 07fd7e1 | 2016-09-12 18:54:06 | [diff] [blame] | 2004 | "a.com", "/cross_site_iframe_factory.html?a(a)")); |
nick | db193a1 | 2016-09-09 23:09:23 | [diff] [blame] | 2005 | EXPECT_TRUE(NavigateToURL(shell(), main_url)); |
| 2006 | |
| 2007 | // The navigation targets an invalid blob url; that's intentional to trigger |
| 2008 | // an error response. The response should commit in a process dedicated to |
Nasko Oskov | 0401d81 | 2021-02-05 22:20:08 | [diff] [blame] | 2009 | // http://b.is or error pages, depending on policy. |
Nick Carter | b7e7131 | 2018-08-03 23:36:13 | [diff] [blame] | 2010 | EXPECT_EQ( |
| 2011 | "done", |
| 2012 | EvalJs( |
| 2013 | root, |
| 2014 | "new Promise((resolve) => {" |
| 2015 | " var iframe_element = document.getElementsByTagName('iframe')[0];" |
| 2016 | " iframe_element.onload = () => resolve('done');" |
Lukasz Anforowicz | 2542093 | 2018-12-18 20:59:22 | [diff] [blame] | 2017 | " iframe_element.src = 'blob:http://b.is/';" |
Nick Carter | b7e7131 | 2018-08-03 23:36:13 | [diff] [blame] | 2018 | "});")); |
Fergal Daly | f052233 | 2020-07-18 06:09:46 | [diff] [blame] | 2019 | EXPECT_TRUE(WaitForLoadStop(contents)); |
nick | db193a1 | 2016-09-09 23:09:23 | [diff] [blame] | 2020 | |
| 2021 | // Make sure we did a process transfer back to "b.is". |
Aaron Colwell | e953e56 | 2019-07-24 16:47:36 | [diff] [blame] | 2022 | const std::string kExpectedSiteURL = |
| 2023 | AreDefaultSiteInstancesEnabled() |
| 2024 | ? SiteInstanceImpl::GetDefaultSiteURL().spec() |
| 2025 | : "http://a.com/"; |
Nasko Oskov | 0401d81 | 2021-02-05 22:20:08 | [diff] [blame] | 2026 | const std::string kExpectedSubframeSiteURL = |
| 2027 | SiteIsolationPolicy::IsErrorPageIsolationEnabled(/*in_main_frame*/ false) |
| 2028 | ? "chrome-error://chromewebdata/" |
| 2029 | : "http://b.is/"; |
Aaron Colwell | e953e56 | 2019-07-24 16:47:36 | [diff] [blame] | 2030 | EXPECT_EQ(base::StringPrintf(" Site A ------------ proxies for B\n" |
| 2031 | " +--Site B ------- proxies for A\n" |
| 2032 | "Where A = %s\n" |
Nasko Oskov | 0401d81 | 2021-02-05 22:20:08 | [diff] [blame] | 2033 | " B = %s", |
| 2034 | kExpectedSiteURL.c_str(), |
| 2035 | kExpectedSubframeSiteURL.c_str()), |
Fergal Daly | 79f4429 | 2020-12-01 02:30:48 | [diff] [blame] | 2036 | DepictFrameTree(*root)); |
nick | db193a1 | 2016-09-09 23:09:23 | [diff] [blame] | 2037 | } |
| 2038 | |
Antonio Sartori | 5abc8de | 2021-07-13 08:42:47 | [diff] [blame] | 2039 | class FrameTreeAnonymousIframeBrowserTest : public FrameTreeBrowserTest { |
| 2040 | public: |
| 2041 | FrameTreeAnonymousIframeBrowserTest() = default; |
| 2042 | |
| 2043 | void SetUpCommandLine(base::CommandLine* command_line) override { |
Antonio Sartori | 753cd6d | 2021-07-23 08:34:55 | [diff] [blame] | 2044 | command_line->AppendSwitch(switches::kEnableBlinkTestFeatures); |
Antonio Sartori | 5abc8de | 2021-07-13 08:42:47 | [diff] [blame] | 2045 | } |
| 2046 | }; |
| 2047 | |
| 2048 | // Tests the mojo propagation of the 'anonymous' attribute to the browser. |
| 2049 | IN_PROC_BROWSER_TEST_F(FrameTreeAnonymousIframeBrowserTest, |
| 2050 | AttributeIsPropagatedToBrowser) { |
| 2051 | GURL main_url(embedded_test_server()->GetURL("/hello.html")); |
| 2052 | EXPECT_TRUE(NavigateToURL(shell(), main_url)); |
| 2053 | |
| 2054 | FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents()) |
Carlos Caballero | 15caeeb | 2021-10-27 09:57:55 | [diff] [blame^] | 2055 | ->GetPrimaryFrameTree() |
| 2056 | .root(); |
Antonio Sartori | 5abc8de | 2021-07-13 08:42:47 | [diff] [blame] | 2057 | |
| 2058 | // Not setting the attribute => the iframe is not anonymous. |
| 2059 | EXPECT_TRUE(ExecJs(root, |
| 2060 | "var f = document.createElement('iframe');" |
| 2061 | "document.body.appendChild(f);")); |
| 2062 | EXPECT_EQ(1U, root->child_count()); |
| 2063 | EXPECT_FALSE(root->child_at(0)->anonymous()); |
| 2064 | |
| 2065 | // Setting the attribute on the iframe element makes the iframe anonymous. |
| 2066 | EXPECT_TRUE(ExecJs(root, |
| 2067 | "var d = document.createElement('div');" |
| 2068 | "d.innerHTML = '<iframe anonymous></iframe>';" |
| 2069 | "document.body.appendChild(d);")); |
| 2070 | EXPECT_EQ(2U, root->child_count()); |
| 2071 | EXPECT_TRUE(root->child_at(1)->anonymous()); |
| 2072 | |
| 2073 | // Setting the attribute via javascript works. |
| 2074 | EXPECT_TRUE(ExecJs(root, |
| 2075 | "var g = document.createElement('iframe');" |
| 2076 | "g.anonymous = true;" |
| 2077 | "document.body.appendChild(g);")); |
| 2078 | EXPECT_EQ(3U, root->child_count()); |
| 2079 | EXPECT_TRUE(root->child_at(2)->anonymous()); |
| 2080 | |
| 2081 | EXPECT_TRUE(ExecJs(root, "g.anonymous = false;")); |
| 2082 | EXPECT_FALSE(root->child_at(2)->anonymous()); |
| 2083 | |
| 2084 | EXPECT_TRUE(ExecJs(root, "g.anonymous = true;")); |
| 2085 | EXPECT_TRUE(root->child_at(2)->anonymous()); |
| 2086 | } |
| 2087 | |
[email protected] | 5f96f5a6 | 2014-01-10 00:05:11 | [diff] [blame] | 2088 | } // namespace content |