Avi Drissman | 4e1b7bc3 | 2022-09-15 14:03:50 | [diff] [blame] | 1 | // Copyright 2019 The Chromium Authors |
arthursonzogni | ae02e6d | 2019-06-04 10:23:11 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
Daniel Cheng | cdb951cc | 2020-09-03 04:06:45 | [diff] [blame] | 5 | #include <algorithm> |
arthursonzogni | ae02e6d | 2019-06-04 10:23:11 | [diff] [blame] | 6 | #include <string> |
Daniel Cheng | cdb951cc | 2020-09-03 04:06:45 | [diff] [blame] | 7 | #include <utility> |
arthursonzogni | ae02e6d | 2019-06-04 10:23:11 | [diff] [blame] | 8 | |
Dan Harrington | ea4afa0 | 2024-11-06 15:26:51 | [diff] [blame] | 9 | #include "base/feature_list.h" |
Alex Ilin | cf96655e1 | 2019-11-21 16:13:52 | [diff] [blame] | 10 | #include "base/files/file_util.h" |
arthursonzogni | ae02e6d | 2019-06-04 10:23:11 | [diff] [blame] | 11 | #include "base/files/scoped_temp_dir.h" |
Daniel Cheng | cdb951cc | 2020-09-03 04:06:45 | [diff] [blame] | 12 | #include "base/run_loop.h" |
arthursonzogni | ae02e6d | 2019-06-04 10:23:11 | [diff] [blame] | 13 | #include "base/strings/string_util.h" |
Lei Zhang | e02299a | 2021-04-26 23:12:24 | [diff] [blame] | 14 | #include "base/strings/stringprintf.h" |
Guido Urdaneta | ef4e9194 | 2020-11-09 15:06:24 | [diff] [blame] | 15 | #include "base/test/bind.h" |
Kevin McNee | 48f87e79 | 2022-04-29 16:53:24 | [diff] [blame] | 16 | #include "base/test/scoped_feature_list.h" |
arthursonzogni | ae02e6d | 2019-06-04 10:23:11 | [diff] [blame] | 17 | #include "base/threading/thread_restrictions.h" |
danakj | 10f3237 | 2020-09-15 22:25:16 | [diff] [blame] | 18 | #include "content/browser/renderer_host/navigation_request.h" |
| 19 | #include "content/browser/renderer_host/render_frame_host_impl.h" |
arthursonzogni | ae02e6d | 2019-06-04 10:23:11 | [diff] [blame] | 20 | #include "content/browser/web_contents/web_contents_impl.h" |
Rakina Zata Amni | 7b72d8f4 | 2023-08-24 04:07:06 | [diff] [blame] | 21 | #include "content/common/content_navigation_policy.h" |
David Sanders | 7d4623d | 2022-04-21 07:00:18 | [diff] [blame] | 22 | #include "content/public/common/url_constants.h" |
Peter Kasting | 919ce65 | 2020-05-07 10:22:36 | [diff] [blame] | 23 | #include "content/public/test/browser_test.h" |
arthursonzogni | ae02e6d | 2019-06-04 10:23:11 | [diff] [blame] | 24 | #include "content/public/test/browser_test_utils.h" |
| 25 | #include "content/public/test/content_browser_test.h" |
| 26 | #include "content/public/test/content_browser_test_utils.h" |
| 27 | #include "content/public/test/navigation_handle_observer.h" |
| 28 | #include "content/public/test/test_utils.h" |
| 29 | #include "content/shell/browser/shell.h" |
Rakina Zata Amni | 039c7fc | 2021-04-12 07:52:52 | [diff] [blame] | 30 | #include "content/test/content_browser_test_utils_internal.h" |
Daniel Cheng | cdb951cc | 2020-09-03 04:06:45 | [diff] [blame] | 31 | #include "mojo/public/c/system/trap.h" |
| 32 | #include "mojo/public/c/system/types.h" |
| 33 | #include "mojo/public/cpp/system/data_pipe.h" |
| 34 | #include "mojo/public/cpp/system/handle_signals_state.h" |
| 35 | #include "mojo/public/cpp/system/simple_watcher.h" |
arthursonzogni | ae02e6d | 2019-06-04 10:23:11 | [diff] [blame] | 36 | #include "net/base/filename_util.h" |
arthursonzogni | 736fcf5 | 2020-09-09 15:34:54 | [diff] [blame] | 37 | #include "net/dns/mock_host_resolver.h" |
arthursonzogni | 07baf8358 | 2020-10-07 08:35:53 | [diff] [blame] | 38 | #include "services/network/public/cpp/web_sandbox_flags.h" |
Kevin McNee | 48f87e79 | 2022-04-29 16:53:24 | [diff] [blame] | 39 | #include "third_party/blink/public/common/features.h" |
arthursonzogni | ae02e6d | 2019-06-04 10:23:11 | [diff] [blame] | 40 | #include "url/gurl.h" |
| 41 | #include "url/url_constants.h" |
| 42 | |
| 43 | namespace content { |
| 44 | |
| 45 | namespace { |
| 46 | |
| 47 | // Tests about navigations to MHTML archives. |
| 48 | class NavigationMhtmlBrowserTest : public ContentBrowserTest { |
| 49 | public: |
| 50 | WebContentsImpl* web_contents() const { |
| 51 | return static_cast<WebContentsImpl*>(shell()->web_contents()); |
| 52 | } |
| 53 | |
| 54 | RenderFrameHostImpl* main_frame_host() { |
Carlos Caballero | 15caeeb | 2021-10-27 09:57:55 | [diff] [blame] | 55 | return web_contents()->GetPrimaryFrameTree().root()->current_frame_host(); |
arthursonzogni | ae02e6d | 2019-06-04 10:23:11 | [diff] [blame] | 56 | } |
arthursonzogni | 736fcf5 | 2020-09-09 15:34:54 | [diff] [blame] | 57 | |
| 58 | protected: |
| 59 | void SetUpOnMainThread() final { |
| 60 | ContentBrowserTest::SetUpOnMainThread(); |
| 61 | host_resolver()->AddRule("*", "127.0.0.1"); |
| 62 | } |
arthursonzogni | ae02e6d | 2019-06-04 10:23:11 | [diff] [blame] | 63 | }; |
| 64 | |
| 65 | // Helper class: Build MHTML documents easily in tests. |
| 66 | class MhtmlArchive { |
| 67 | public: |
| 68 | MhtmlArchive() = default; |
Peter Boström | 828b902 | 2021-09-21 02:28:43 | [diff] [blame] | 69 | |
| 70 | MhtmlArchive(const MhtmlArchive&) = delete; |
| 71 | MhtmlArchive& operator=(const MhtmlArchive&) = delete; |
| 72 | |
arthursonzogni | ae02e6d | 2019-06-04 10:23:11 | [diff] [blame] | 73 | ~MhtmlArchive() { |
| 74 | base::ScopedAllowBlockingForTesting allow_blocking_; |
| 75 | EXPECT_TRUE(file_directory_.Delete()); |
| 76 | } |
| 77 | |
| 78 | void AddResource(const std::string content) { |
| 79 | content_ += "\n--MHTML_BOUNDARY\n" + content; |
| 80 | } |
| 81 | |
Lukasz Anforowicz | 8ad9106 | 2021-01-22 19:52:39 | [diff] [blame] | 82 | void AddResource(const GURL& url, |
| 83 | const std::string mime_type, |
| 84 | const std::string headers, |
| 85 | const std::string body) { |
| 86 | const char* document_template = |
| 87 | "Content-Type: $1\n" |
| 88 | "Content-Location: $2\n" |
| 89 | "$3" |
| 90 | "\n" |
| 91 | "$4"; |
| 92 | AddResource(base::ReplaceStringPlaceholders( |
| 93 | document_template, {mime_type, url.spec(), headers, body}, nullptr)); |
| 94 | } |
| 95 | |
arthursonzogni | ae02e6d | 2019-06-04 10:23:11 | [diff] [blame] | 96 | void AddHtmlDocument(const GURL& url, |
| 97 | const std::string headers, |
| 98 | const std::string body) { |
| 99 | const char* document_template = |
| 100 | "Content-Type: text/html\n" |
| 101 | "Content-Location: $1\n" |
| 102 | "$2" |
| 103 | "\n" |
| 104 | "$3"; |
| 105 | AddResource(base::ReplaceStringPlaceholders( |
| 106 | document_template, {url.spec(), headers, body}, nullptr)); |
| 107 | } |
| 108 | |
| 109 | void AddHtmlDocument(const GURL& url, const std::string body) { |
| 110 | AddHtmlDocument(url, "" /* headers */, body); |
| 111 | } |
| 112 | |
| 113 | // Writes the MHTML archive into a file and returns its URL. |
| 114 | const GURL Write(const std::string& file) { |
| 115 | const char* document_header = |
| 116 | "From: The chromium developers\n" |
| 117 | "Subject: <the subject>\n" |
| 118 | "Date: Mon, May 27 2019 11:55:42 GMT+0200\n" |
| 119 | "MIME-Version: 1.0\n" |
| 120 | "Content-Type: multipart/related;" |
| 121 | " boundary=\"MHTML_BOUNDARY\";" |
| 122 | " type=\"text/html\"\n"; |
| 123 | std::string document = document_header + content_ + "\n--MHTML_BOUNDARY--"; |
| 124 | |
| 125 | // MHTML uses carriage return before every new lines. |
| 126 | base::ReplaceChars(document, "\n", "\r\n", &document); |
| 127 | |
| 128 | base::ScopedAllowBlockingForTesting allow_blocking_; |
| 129 | EXPECT_TRUE(file_directory_.CreateUniqueTempDir()); |
| 130 | base::FilePath file_path = file_directory_.GetPath().AppendASCII(file); |
Lei Zhang | 9989f27 | 2020-05-11 19:21:21 | [diff] [blame] | 131 | EXPECT_TRUE(base::WriteFile(file_path, document)); |
arthursonzogni | ae02e6d | 2019-06-04 10:23:11 | [diff] [blame] | 132 | return net::FilePathToFileURL(file_path); |
| 133 | } |
| 134 | |
| 135 | private: |
| 136 | base::ScopedTempDir file_directory_; |
| 137 | std::string content_; |
arthursonzogni | ae02e6d | 2019-06-04 10:23:11 | [diff] [blame] | 138 | }; |
| 139 | |
| 140 | } // namespace |
| 141 | |
| 142 | // An MHTML document with an iframe. The iframe's document is found in the |
| 143 | // archive. |
| 144 | IN_PROC_BROWSER_TEST_F(NavigationMhtmlBrowserTest, IframeFound) { |
| 145 | MhtmlArchive mhtml_archive; |
| 146 | mhtml_archive.AddHtmlDocument( |
| 147 | GURL("http://example.com"), |
| 148 | "<iframe src=\"http://example.com/found.html\"></iframe>"); |
| 149 | mhtml_archive.AddHtmlDocument(GURL("http://example.com/found.html"), |
| 150 | "<iframe></iframe>"); |
| 151 | GURL mhtml_url = mhtml_archive.Write("index.mhtml"); |
| 152 | |
| 153 | NavigationHandleObserver iframe_navigation( |
| 154 | web_contents(), GURL("http://example.com/found.html")); |
| 155 | EXPECT_TRUE(NavigateToURL(shell(), mhtml_url)); |
| 156 | |
| 157 | RenderFrameHostImpl* main_document = main_frame_host(); |
| 158 | ASSERT_EQ(1u, main_document->child_count()); |
| 159 | RenderFrameHostImpl* sub_document = |
| 160 | main_document->child_at(0)->current_frame_host(); |
| 161 | |
arthursonzogni | ae02e6d | 2019-06-04 10:23:11 | [diff] [blame] | 162 | EXPECT_TRUE(main_document->is_mhtml_document()); |
arthursonzogni | 3fc224b | 2020-10-07 10:41:16 | [diff] [blame] | 163 | EXPECT_TRUE(sub_document->is_mhtml_document()); |
arthursonzogni | ae02e6d | 2019-06-04 10:23:11 | [diff] [blame] | 164 | |
| 165 | // When the iframe's content is loaded from the MHTML archive, a successful |
| 166 | // commit using the provided URL happens, even if the resource wasn't loaded |
| 167 | // from this URL initially. |
| 168 | EXPECT_EQ(GURL("http://example.com/found.html"), |
| 169 | sub_document->GetLastCommittedURL()); |
| 170 | EXPECT_TRUE(iframe_navigation.has_committed()); |
| 171 | EXPECT_FALSE(iframe_navigation.is_error()); |
| 172 | |
| 173 | // Check the iframe is properly loaded. EvalJs("document.body.innerHTML") |
| 174 | // can't be used, because javascript is disabled. Instead, check it was able |
| 175 | // to load an iframe. |
| 176 | EXPECT_EQ(1u, sub_document->child_count()); |
| 177 | } |
| 178 | |
| 179 | // An MHTML document with an iframe. The iframe's document is not found in the |
| 180 | // archive. |
| 181 | IN_PROC_BROWSER_TEST_F(NavigationMhtmlBrowserTest, IframeNotFound) { |
| 182 | MhtmlArchive mhtml_archive; |
| 183 | mhtml_archive.AddHtmlDocument( |
| 184 | GURL("http://example.com"), |
| 185 | "<iframe src=\"http://example.com/not_found.html\"></iframe>"); |
| 186 | GURL mhtml_url = mhtml_archive.Write("index.mhtml"); |
| 187 | NavigationHandleObserver iframe_navigation_observer( |
| 188 | web_contents(), GURL("http://example.com/not_found.html")); |
| 189 | EXPECT_TRUE(NavigateToURL(shell(), mhtml_url)); |
| 190 | |
| 191 | RenderFrameHostImpl* main_document = main_frame_host(); |
| 192 | ASSERT_EQ(1u, main_document->child_count()); |
| 193 | RenderFrameHostImpl* sub_document = |
| 194 | main_document->child_at(0)->current_frame_host(); |
| 195 | |
arthursonzogni | ae02e6d | 2019-06-04 10:23:11 | [diff] [blame] | 196 | EXPECT_TRUE(main_document->is_mhtml_document()); |
arthursonzogni | 3fc224b | 2020-10-07 10:41:16 | [diff] [blame] | 197 | EXPECT_TRUE(sub_document->is_mhtml_document()); |
arthursonzogni | ae02e6d | 2019-06-04 10:23:11 | [diff] [blame] | 198 | |
Daniel Cheng | 8a8a920 | 2020-08-10 19:46:57 | [diff] [blame] | 199 | // This should commit as a failed navigation, but the browser side doesn't |
| 200 | // have enough information to make that determination. On the renderer side, |
| 201 | // there's no existing way to turn `CommitNavigation()` into |
| 202 | // `CommitFailedNavigation()`. |
Alison Gale | 770f3fc | 2024-04-27 00:39:58 | [diff] [blame] | 203 | // TODO(crbug.com/40143262): Fix this by implementing a MHTML |
Daniel Cheng | 8a8a920 | 2020-08-10 19:46:57 | [diff] [blame] | 204 | // URLLoaderFactory; then failure to find the resource can use the standard |
| 205 | // error handling path. |
| 206 | EXPECT_TRUE(iframe_navigation_observer.has_committed()); |
arthursonzogni | ae02e6d | 2019-06-04 10:23:11 | [diff] [blame] | 207 | EXPECT_FALSE(iframe_navigation_observer.is_error()); |
Daniel Cheng | 8a8a920 | 2020-08-10 19:46:57 | [diff] [blame] | 208 | EXPECT_EQ(GURL("http://example.com/not_found.html"), |
| 209 | sub_document->GetLastCommittedURL()); |
arthursonzogni | ae02e6d | 2019-06-04 10:23:11 | [diff] [blame] | 210 | } |
| 211 | |
| 212 | // An MHTML document with an iframe using a data-URL. The data-URL is not |
| 213 | // defined in the MHTML archive. |
Alison Gale | 81f4f2c7 | 2024-04-22 19:33:31 | [diff] [blame] | 214 | // TODO(crbug.com/40629273): Enable this test. It currently reaches a |
arthursonzogni | ae02e6d | 2019-06-04 10:23:11 | [diff] [blame] | 215 | // DCHECK or timeout in release mode. |
arthursonzogni | 09aa34d | 2019-06-04 14:44:34 | [diff] [blame] | 216 | IN_PROC_BROWSER_TEST_F(NavigationMhtmlBrowserTest, IframeDataUrlNotFound) { |
arthursonzogni | ae02e6d | 2019-06-04 10:23:11 | [diff] [blame] | 217 | MhtmlArchive mhtml_archive; |
| 218 | mhtml_archive.AddHtmlDocument( |
| 219 | GURL("http://example.com"), |
| 220 | "<iframe src=\"data:text/html,<iframe></iframe>\"></iframe>"); |
| 221 | GURL mhtml_url = mhtml_archive.Write("index.mhtml"); |
| 222 | |
| 223 | EXPECT_TRUE(NavigateToURL(shell(), mhtml_url)); |
| 224 | RenderFrameHostImpl* main_document = main_frame_host(); |
| 225 | |
| 226 | ASSERT_EQ(1u, main_document->child_count()); |
| 227 | RenderFrameHostImpl* sub_document = |
| 228 | main_document->child_at(0)->current_frame_host(); |
| 229 | EXPECT_EQ(GURL("data:text/html,<iframe></iframe>"), |
| 230 | sub_document->GetLastCommittedURL()); |
| 231 | |
arthursonzogni | 3fc224b | 2020-10-07 10:41:16 | [diff] [blame] | 232 | EXPECT_TRUE(main_document->is_mhtml_document()); |
Lukasz Anforowicz | 39a8aef | 2020-12-17 23:50:15 | [diff] [blame] | 233 | EXPECT_TRUE(sub_document->is_mhtml_document()); |
arthursonzogni | 3fc224b | 2020-10-07 10:41:16 | [diff] [blame] | 234 | |
arthursonzogni | ae02e6d | 2019-06-04 10:23:11 | [diff] [blame] | 235 | // Check the iframe is properly loaded. EvalJs("document.body.innerHTML") |
| 236 | // can't be used, because javascript is disabled. Instead, check it was able |
| 237 | // to load an iframe. |
| 238 | EXPECT_EQ(1u, sub_document->child_count()); |
| 239 | } |
| 240 | |
| 241 | // An MHTML document with an iframe using a data-URL. The data-URL IS defined in |
| 242 | // the MHTML archive, but isn't used, per https://crbug.com/969696. |
arthursonzogni | 09aa34d | 2019-06-04 14:44:34 | [diff] [blame] | 243 | IN_PROC_BROWSER_TEST_F(NavigationMhtmlBrowserTest, IframeDataUrlFound) { |
arthursonzogni | ae02e6d | 2019-06-04 10:23:11 | [diff] [blame] | 244 | MhtmlArchive mhtml_archive; |
| 245 | mhtml_archive.AddHtmlDocument( |
| 246 | GURL("http://example.com"), |
| 247 | "<iframe src=\"data:text/html,<iframe></iframe>\"></iframe>"); |
| 248 | mhtml_archive.AddHtmlDocument(GURL("data:text/html,<iframe></iframe>"), |
| 249 | "no iframes"); |
| 250 | GURL mhtml_url = mhtml_archive.Write("index.mhtml"); |
| 251 | |
| 252 | EXPECT_TRUE(NavigateToURL(shell(), mhtml_url)); |
| 253 | RenderFrameHostImpl* main_document = main_frame_host(); |
| 254 | |
| 255 | ASSERT_EQ(1u, main_document->child_count()); |
| 256 | RenderFrameHostImpl* sub_document = |
| 257 | main_document->child_at(0)->current_frame_host(); |
| 258 | EXPECT_EQ(GURL("data:text/html,<iframe></iframe>"), |
| 259 | sub_document->GetLastCommittedURL()); |
| 260 | |
arthursonzogni | 3fc224b | 2020-10-07 10:41:16 | [diff] [blame] | 261 | EXPECT_TRUE(main_document->is_mhtml_document()); |
Lukasz Anforowicz | 39a8aef | 2020-12-17 23:50:15 | [diff] [blame] | 262 | EXPECT_TRUE(sub_document->is_mhtml_document()); |
arthursonzogni | 3fc224b | 2020-10-07 10:41:16 | [diff] [blame] | 263 | |
arthursonzogni | ae02e6d | 2019-06-04 10:23:11 | [diff] [blame] | 264 | // Check the iframe is properly loaded. EvalJs("document.body.innerHTML") |
| 265 | // can't be used, because javascript is disabled. Instead, check it was able |
| 266 | // to load an iframe. |
| 267 | EXPECT_EQ(1u, sub_document->child_count()); |
| 268 | } |
| 269 | |
| 270 | // An iframe uses its srcdoc attribute and the about:srcdoc is not defined in |
| 271 | // the MHTML archive. |
| 272 | IN_PROC_BROWSER_TEST_F(NavigationMhtmlBrowserTest, IframeAboutSrcdocNoFound) { |
| 273 | MhtmlArchive mhtml_archive; |
| 274 | mhtml_archive.AddHtmlDocument( |
| 275 | GURL("http://example.com"), |
| 276 | "<iframe srcdoc=\"<iframe></iframe>\"></iframe>"); |
| 277 | GURL mhtml_url = mhtml_archive.Write("index.mhtml"); |
| 278 | EXPECT_TRUE(NavigateToURL(shell(), mhtml_url)); |
| 279 | |
| 280 | RenderFrameHostImpl* main_document = main_frame_host(); |
| 281 | ASSERT_EQ(1u, main_document->child_count()); |
| 282 | RenderFrameHostImpl* sub_document = |
| 283 | main_document->child_at(0)->current_frame_host(); |
Lukasz Anforowicz | 42d3d07f | 2019-06-19 01:06:42 | [diff] [blame] | 284 | EXPECT_TRUE(sub_document->GetLastCommittedURL().IsAboutSrcdoc()); |
arthursonzogni | ae02e6d | 2019-06-04 10:23:11 | [diff] [blame] | 285 | |
arthursonzogni | 3fc224b | 2020-10-07 10:41:16 | [diff] [blame] | 286 | EXPECT_TRUE(main_document->is_mhtml_document()); |
| 287 | EXPECT_TRUE(sub_document->is_mhtml_document()); |
| 288 | |
arthursonzogni | ae02e6d | 2019-06-04 10:23:11 | [diff] [blame] | 289 | // Check the iframe is properly loaded. EvalJs("document.body.innerHTML") |
| 290 | // can't be used, because javascript is disabled. Instead, check it was able |
| 291 | // to load an iframe. |
| 292 | EXPECT_EQ(1u, sub_document->child_count()); |
| 293 | } |
| 294 | |
| 295 | // An iframe uses its srcdoc attribute and the about:srcdoc IS defined in |
| 296 | // the MHTML archive. Its content is NEVER loaded from the MHTML archive. |
| 297 | IN_PROC_BROWSER_TEST_F(NavigationMhtmlBrowserTest, IframeAboutSrcdocFound) { |
| 298 | MhtmlArchive mhtml_archive; |
| 299 | mhtml_archive.AddHtmlDocument( |
| 300 | GURL("http://example.com"), |
| 301 | "<iframe srcdoc=\"<iframe></iframe>\"></iframe>"); |
| 302 | mhtml_archive.AddHtmlDocument(GURL("about:srcdoc'"), "no iframe"); |
| 303 | GURL mhtml_url = mhtml_archive.Write("index.mhtml"); |
| 304 | EXPECT_TRUE(NavigateToURL(shell(), mhtml_url)); |
| 305 | |
| 306 | RenderFrameHostImpl* main_document = main_frame_host(); |
| 307 | ASSERT_EQ(1u, main_document->child_count()); |
| 308 | RenderFrameHostImpl* sub_document = |
| 309 | main_document->child_at(0)->current_frame_host(); |
Lukasz Anforowicz | 42d3d07f | 2019-06-19 01:06:42 | [diff] [blame] | 310 | EXPECT_TRUE(sub_document->GetLastCommittedURL().IsAboutSrcdoc()); |
arthursonzogni | ae02e6d | 2019-06-04 10:23:11 | [diff] [blame] | 311 | |
arthursonzogni | 3fc224b | 2020-10-07 10:41:16 | [diff] [blame] | 312 | EXPECT_TRUE(main_document->is_mhtml_document()); |
| 313 | EXPECT_TRUE(sub_document->is_mhtml_document()); |
| 314 | |
arthursonzogni | ae02e6d | 2019-06-04 10:23:11 | [diff] [blame] | 315 | // Check the iframe is properly loaded. EvalJs("document.body.innerHTML") |
| 316 | // can't be used, because javascript is disabled. Instead, check it was able |
| 317 | // to load an iframe. |
| 318 | EXPECT_EQ(1u, sub_document->child_count()); |
| 319 | } |
| 320 | |
| 321 | // An MHTML document with an iframe loading the about:blank document. The |
| 322 | // about:blank resource is not defined in the MHTML archive. |
| 323 | IN_PROC_BROWSER_TEST_F(NavigationMhtmlBrowserTest, IframeAboutBlankNotFound) { |
| 324 | MhtmlArchive mhtml_archive; |
| 325 | mhtml_archive.AddHtmlDocument(GURL("http://example.com"), |
| 326 | "<iframe src=\"about:blank\"></iframe>" |
Daniel Cheng | 8a8a920 | 2020-08-10 19:46:57 | [diff] [blame] | 327 | // Note: this is actually treated as a |
| 328 | // same-document navigation! |
arthursonzogni | ae02e6d | 2019-06-04 10:23:11 | [diff] [blame] | 329 | "<iframe src=\"about:blank#fragment\"></iframe>" |
| 330 | "<iframe src=\"about:blank?query\"></iframe>"); |
| 331 | GURL mhtml_url = mhtml_archive.Write("index.mhtml"); |
| 332 | EXPECT_TRUE(NavigateToURL(shell(), mhtml_url)); |
| 333 | |
| 334 | RenderFrameHostImpl* main_document = main_frame_host(); |
| 335 | ASSERT_EQ(3u, main_document->child_count()); |
| 336 | auto iframe_url = [main_document](int index) { |
| 337 | return main_document->child_at(index) |
| 338 | ->current_frame_host() |
| 339 | ->GetLastCommittedURL(); |
| 340 | }; |
Daniel Cheng | 8a8a920 | 2020-08-10 19:46:57 | [diff] [blame] | 341 | |
| 342 | // about:blank in MHTML has some very unusual behavior. When navigating to |
| 343 | // about:blank in the context of a MHTML archive, the renderer-side MHTML |
| 344 | // handler actually attempts to look up the resource for about:blank<...>" in |
| 345 | // the MHTML archive. |
| 346 | // |
| 347 | // Prior to https://crrev.com/c/2335323, failing to find the resource in the |
| 348 | // MHTML archive usually led to the commit being silently dropped (see |
| 349 | // `IframeNotFound` and `IframeContentIdNotFound`). However, about:blank |
| 350 | // behaved differently, due to a special case in frame_loader.cc's |
| 351 | // `ShouldNavigate()` for URLs that will load as an empty document. |
| 352 | // |
| 353 | // However, after https://crrev.com/c/23335323, loading about:blank without a |
| 354 | // corresponding resource in the MHTML archive will be treated as loading |
| 355 | // static data rather than loading an empty document. This affects the timing |
| 356 | // of load completion; loading an empty document synchronously completes |
| 357 | // during `CommitNavigation()`, while loading static data (even if the data is |
| 358 | // empty) completes "later". |
arthursonzogni | ae02e6d | 2019-06-04 10:23:11 | [diff] [blame] | 359 | EXPECT_EQ(iframe_url(0), GURL("about:blank")); |
Daniel Cheng | 8a8a920 | 2020-08-10 19:46:57 | [diff] [blame] | 360 | // Note: unlike the other two subframe navigations, this navigation actually |
| 361 | // succeeds as a same-document navigation... |
| 362 | // Note 2: this same-document navigation is performed asynchronously. Prior to |
| 363 | // https://crrev.com/c/23335323, the test would consider the page as loaded |
| 364 | // before the fragment navigation completed, resulting in an empty last |
| 365 | // committed URL. |
| 366 | EXPECT_EQ(iframe_url(1), GURL("about:blank#fragment")); |
arthursonzogni | ae02e6d | 2019-06-04 10:23:11 | [diff] [blame] | 367 | EXPECT_EQ(iframe_url(2), GURL("about:blank?query")); |
| 368 | } |
| 369 | |
| 370 | // An MHTML document with an iframe loading the about:blank document AND the |
| 371 | // about:blank document is a resource of the MHTML archive. |
| 372 | IN_PROC_BROWSER_TEST_F(NavigationMhtmlBrowserTest, IframeAboutBlankFound) { |
| 373 | MhtmlArchive mhtml_archive; |
| 374 | mhtml_archive.AddHtmlDocument(GURL("http://example.com"), |
| 375 | "<iframe src=\"about:blank\"></iframe>"); |
| 376 | mhtml_archive.AddHtmlDocument( |
| 377 | GURL(url::kAboutBlankURL), |
| 378 | "<iframe src=\"http://example.com/found.html\">/iframe>"); |
| 379 | mhtml_archive.AddHtmlDocument(GURL("http://example.com/found.html"), ""); |
| 380 | GURL mhtml_url = mhtml_archive.Write("index.mhtml"); |
| 381 | EXPECT_TRUE(NavigateToURL(shell(), mhtml_url)); |
| 382 | |
| 383 | RenderFrameHostImpl* main_document = main_frame_host(); |
| 384 | ASSERT_EQ(1u, main_document->child_count()); |
| 385 | RenderFrameHostImpl* about_blank_document = |
| 386 | main_document->child_at(0)->current_frame_host(); |
arthursonzogni | 3fc224b | 2020-10-07 10:41:16 | [diff] [blame] | 387 | |
| 388 | EXPECT_TRUE(main_document->is_mhtml_document()); |
| 389 | // TODO(arthursonzogni): This should be true here. |
| 390 | EXPECT_FALSE(about_blank_document->is_mhtml_document()); |
| 391 | |
arthursonzogni | ae02e6d | 2019-06-04 10:23:11 | [diff] [blame] | 392 | // about:blank is loaded from the archive, so it has an iframe. |
| 393 | // See https://crbug.com/969667 |
| 394 | ASSERT_EQ(1u, about_blank_document->child_count()); |
| 395 | } |
| 396 | |
| 397 | // An MHTML document with an iframe trying to load a javascript URL. |
| 398 | IN_PROC_BROWSER_TEST_F(NavigationMhtmlBrowserTest, |
| 399 | IframeJavascriptUrlNotFound) { |
Dan Harrington | ea4afa0 | 2024-11-06 15:26:51 | [diff] [blame] | 400 | // JS is allowed to execute when this feature is enabled. |
| 401 | if (base::FeatureList::IsEnabled(blink::features::kMHTML_Improvements)) { |
| 402 | return; |
| 403 | } |
arthursonzogni | ae02e6d | 2019-06-04 10:23:11 | [diff] [blame] | 404 | MhtmlArchive mhtml_archive; |
| 405 | mhtml_archive.AddHtmlDocument( |
| 406 | GURL("http://example.com"), |
| 407 | "<iframe src=\"javascript:console.log('test')\"></iframe>"); |
| 408 | GURL mhtml_url = mhtml_archive.Write("index.mhtml"); |
| 409 | |
Devlin Cronin | ed376d8 | 2020-05-01 18:37:55 | [diff] [blame] | 410 | WebContentsConsoleObserver console_observer(web_contents()); |
| 411 | console_observer.SetPattern(base::StringPrintf( |
| 412 | "Blocked script execution in '%s' because the document's frame " |
| 413 | "is sandboxed and the 'allow-scripts' permission is not set.", |
| 414 | mhtml_url.spec().c_str())); |
arthursonzogni | ae02e6d | 2019-06-04 10:23:11 | [diff] [blame] | 415 | |
| 416 | EXPECT_TRUE(NavigateToURL(shell(), mhtml_url)); |
Fergal Daly | 7723f9d | 2022-10-29 07:03:13 | [diff] [blame] | 417 | ASSERT_TRUE(console_observer.Wait()); |
arthursonzogni | ae02e6d | 2019-06-04 10:23:11 | [diff] [blame] | 418 | |
| 419 | RenderFrameHostImpl* main_document = main_frame_host(); |
| 420 | ASSERT_EQ(1u, main_document->child_count()); |
| 421 | RenderFrameHostImpl* sub_document = |
| 422 | main_document->child_at(0)->current_frame_host(); |
arthursonzogni | 3fc224b | 2020-10-07 10:41:16 | [diff] [blame] | 423 | |
| 424 | EXPECT_TRUE(main_document->is_mhtml_document()); |
| 425 | |
| 426 | // The |sub_document| is the initial empty document. |
| 427 | EXPECT_FALSE(sub_document->is_mhtml_document()); |
arthursonzogni | ae02e6d | 2019-06-04 10:23:11 | [diff] [blame] | 428 | EXPECT_EQ(GURL(), sub_document->GetLastCommittedURL()); |
| 429 | } |
| 430 | |
| 431 | // An MHTML document with an iframe trying to load a javascript URL. The |
| 432 | IN_PROC_BROWSER_TEST_F(NavigationMhtmlBrowserTest, IframeJavascriptUrlFound) { |
Dan Harrington | ea4afa0 | 2024-11-06 15:26:51 | [diff] [blame] | 433 | // JS is allowed to execute when this feature is enabled. |
| 434 | if (base::FeatureList::IsEnabled(blink::features::kMHTML_Improvements)) { |
| 435 | return; |
| 436 | } |
arthursonzogni | ae02e6d | 2019-06-04 10:23:11 | [diff] [blame] | 437 | MhtmlArchive mhtml_archive; |
| 438 | mhtml_archive.AddHtmlDocument( |
| 439 | GURL("http://example.com"), |
| 440 | "<iframe src=\"javascript:console.log('test')\"></iframe>"); |
| 441 | mhtml_archive.AddHtmlDocument(GURL("javascript:console.log('test')"), |
| 442 | "<iframe></iframe>"); |
| 443 | GURL mhtml_url = mhtml_archive.Write("index.mhtml"); |
| 444 | |
Devlin Cronin | ed376d8 | 2020-05-01 18:37:55 | [diff] [blame] | 445 | WebContentsConsoleObserver console_observer(web_contents()); |
| 446 | console_observer.SetPattern(base::StringPrintf( |
| 447 | "Blocked script execution in '%s' because the document's frame " |
| 448 | "is sandboxed and the 'allow-scripts' permission is not set.", |
| 449 | mhtml_url.spec().c_str())); |
arthursonzogni | ae02e6d | 2019-06-04 10:23:11 | [diff] [blame] | 450 | |
| 451 | EXPECT_TRUE(NavigateToURL(shell(), mhtml_url)); |
Fergal Daly | 7723f9d | 2022-10-29 07:03:13 | [diff] [blame] | 452 | ASSERT_TRUE(console_observer.Wait()); |
arthursonzogni | ae02e6d | 2019-06-04 10:23:11 | [diff] [blame] | 453 | |
| 454 | RenderFrameHostImpl* main_document = main_frame_host(); |
| 455 | ASSERT_EQ(1u, main_document->child_count()); |
| 456 | RenderFrameHostImpl* sub_document = |
| 457 | main_document->child_at(0)->current_frame_host(); |
arthursonzogni | 3fc224b | 2020-10-07 10:41:16 | [diff] [blame] | 458 | |
| 459 | EXPECT_TRUE(main_document->is_mhtml_document()); |
| 460 | |
| 461 | // The |sub_document| is the initial empty document. |
| 462 | EXPECT_FALSE(sub_document->is_mhtml_document()); |
arthursonzogni | ae02e6d | 2019-06-04 10:23:11 | [diff] [blame] | 463 | EXPECT_EQ(GURL(), sub_document->GetLastCommittedURL()); |
| 464 | |
| 465 | EXPECT_EQ(0u, sub_document->child_count()); |
| 466 | } |
| 467 | |
| 468 | // Load iframe with the content-ID scheme. The resource is found in the MHTML |
| 469 | // archive. |
| 470 | IN_PROC_BROWSER_TEST_F(NavigationMhtmlBrowserTest, IframeContentIdFound) { |
| 471 | MhtmlArchive mhtml_archive; |
| 472 | mhtml_archive.AddHtmlDocument(GURL("http://example.com"), |
| 473 | "<iframe src=\"cid:iframe\"></iframe>"); |
| 474 | mhtml_archive.AddHtmlDocument(GURL("http://example.com/found.html"), |
| 475 | "Content-ID: <iframe>\n", "<iframe></iframe>"); |
| 476 | GURL mhtml_url = mhtml_archive.Write("index.mhtml"); |
| 477 | |
| 478 | NavigationHandleObserver iframe_navigation(web_contents(), |
| 479 | GURL("cid:iframe")); |
| 480 | EXPECT_TRUE(NavigateToURL(shell(), mhtml_url)); |
| 481 | |
| 482 | RenderFrameHostImpl* main_document = main_frame_host(); |
| 483 | ASSERT_EQ(1u, main_document->child_count()); |
| 484 | RenderFrameHostImpl* sub_document = |
| 485 | main_document->child_at(0)->current_frame_host(); |
| 486 | |
arthursonzogni | 3fc224b | 2020-10-07 10:41:16 | [diff] [blame] | 487 | EXPECT_TRUE(main_document->is_mhtml_document()); |
| 488 | EXPECT_TRUE(sub_document->is_mhtml_document()); |
| 489 | |
arthursonzogni | ae02e6d | 2019-06-04 10:23:11 | [diff] [blame] | 490 | EXPECT_EQ(GURL("cid:iframe"), sub_document->GetLastCommittedURL()); |
| 491 | EXPECT_TRUE(iframe_navigation.has_committed()); |
| 492 | EXPECT_FALSE(iframe_navigation.is_error()); |
| 493 | |
| 494 | // Check the iframe is properly loaded. EvalJs("document.body.innerHTML") |
| 495 | // can't be used, because javascript is disabled. Instead, check it was able |
| 496 | // to load an iframe. |
| 497 | EXPECT_EQ(1u, sub_document->child_count()); |
| 498 | } |
| 499 | |
| 500 | // Load iframe with the content-ID scheme. The resource is not found in the |
| 501 | // MHTML archive. |
| 502 | IN_PROC_BROWSER_TEST_F(NavigationMhtmlBrowserTest, IframeContentIdNotFound) { |
| 503 | MhtmlArchive mhtml_archive; |
| 504 | mhtml_archive.AddHtmlDocument(GURL("http://example.com"), |
| 505 | "<iframe src=\"cid:iframe\"></iframe>"); |
| 506 | GURL mhtml_url = mhtml_archive.Write("index.mhtml"); |
| 507 | |
| 508 | NavigationHandleObserver iframe_navigation(web_contents(), |
| 509 | GURL("cid:iframe")); |
| 510 | EXPECT_TRUE(NavigateToURL(shell(), mhtml_url)); |
| 511 | |
| 512 | RenderFrameHostImpl* main_document = main_frame_host(); |
| 513 | ASSERT_EQ(1u, main_document->child_count()); |
| 514 | RenderFrameHostImpl* sub_document = |
| 515 | main_document->child_at(0)->current_frame_host(); |
| 516 | |
arthursonzogni | 3fc224b | 2020-10-07 10:41:16 | [diff] [blame] | 517 | EXPECT_TRUE(main_document->is_mhtml_document()); |
| 518 | EXPECT_TRUE(sub_document->is_mhtml_document()); |
| 519 | |
Daniel Cheng | 8a8a920 | 2020-08-10 19:46:57 | [diff] [blame] | 520 | // This should commit as a failed navigation, but the browser side doesn't |
| 521 | // have enough information to make that determination. On the renderer side, |
| 522 | // there's no existing way to turn `CommitNavigation()` into |
| 523 | // `CommitFailedNavigation()`. |
Alison Gale | 770f3fc | 2024-04-27 00:39:58 | [diff] [blame] | 524 | // TODO(crbug.com/40143262): Fix this by implementing a MHTML |
Daniel Cheng | 8a8a920 | 2020-08-10 19:46:57 | [diff] [blame] | 525 | // URLLoaderFactory; then failure to find the resource can use the standard |
| 526 | // error handling path. |
| 527 | EXPECT_EQ(GURL("cid:iframe"), sub_document->GetLastCommittedURL()); |
| 528 | EXPECT_TRUE(iframe_navigation.has_committed()); |
arthursonzogni | ae02e6d | 2019-06-04 10:23:11 | [diff] [blame] | 529 | EXPECT_FALSE(iframe_navigation.is_error()); |
| 530 | } |
| 531 | |
arthursonzogni | 654ed17 | 2019-06-11 14:41:04 | [diff] [blame] | 532 | // Tests Content-Security-Policy: frame-ancestors enforcement in MHTML |
| 533 | // subframes. It isn't enforced currently. |
| 534 | // See https://crbug.com/969711. |
| 535 | IN_PROC_BROWSER_TEST_F(NavigationMhtmlBrowserTest, CspFrameAncestor) { |
| 536 | MhtmlArchive mhtml_archive; |
| 537 | mhtml_archive.AddHtmlDocument( |
| 538 | GURL("http://example.com/main"), |
| 539 | "<iframe src=\"http://example.com/subframe\"></iframe>"); |
| 540 | mhtml_archive.AddHtmlDocument( |
| 541 | GURL("http://example.com/subframe"), |
| 542 | "Content-Security-Policy: frame-ancestors 'none'\n", "<iframe></iframe>"); |
| 543 | GURL mhtml_url = mhtml_archive.Write("index.mhtml"); |
| 544 | |
| 545 | EXPECT_TRUE(NavigateToURL(shell(), mhtml_url)); |
| 546 | |
arthursonzogni | 3fc224b | 2020-10-07 10:41:16 | [diff] [blame] | 547 | RenderFrameHostImpl* main_document = main_frame_host(); |
| 548 | ASSERT_EQ(1u, main_document->child_count()); |
| 549 | RenderFrameHostImpl* sub_document = |
| 550 | main_document->child_at(0)->current_frame_host(); |
| 551 | |
| 552 | EXPECT_TRUE(main_document->is_mhtml_document()); |
| 553 | EXPECT_TRUE(sub_document->is_mhtml_document()); |
arthursonzogni | 654ed17 | 2019-06-11 14:41:04 | [diff] [blame] | 554 | |
| 555 | // Currently, frame-ancestors is not enforced. See https://crbug.com/969711. |
| 556 | // Check that the iframe is properly loaded. EvalJs("document.body.innerHTML") |
| 557 | // can't be used, because javascript is disabled. Instead, check it was able |
| 558 | // to load an iframe. |
arthursonzogni | 3fc224b | 2020-10-07 10:41:16 | [diff] [blame] | 559 | ASSERT_EQ(1u, sub_document->child_count()); |
arthursonzogni | 654ed17 | 2019-06-11 14:41:04 | [diff] [blame] | 560 | } |
| 561 | |
arthursonzogni | 898dcda5 | 2021-01-21 08:50:10 | [diff] [blame] | 562 | // Tests CSP embedded enforcement blocking an iframes. |
| 563 | // Regression test for https://crbug.com/1112965 |
| 564 | IN_PROC_BROWSER_TEST_F(NavigationMhtmlBrowserTest, CSPEmbeddedEnforcement) { |
| 565 | MhtmlArchive mhtml_archive; |
| 566 | mhtml_archive.AddHtmlDocument( |
| 567 | GURL("http://a.com"), |
| 568 | "<iframe csp=\"sandbox\" src=\"http://a.com/\"></iframe>" |
| 569 | "<iframe csp=\"sandbox\" src=\"http://b.com/\"></iframe>" |
| 570 | "<iframe csp=\"sandbox\" src=\"http://b.com/allow\"></iframe>"); |
| 571 | mhtml_archive.AddHtmlDocument(GURL("http://a.com/"), ""); |
| 572 | mhtml_archive.AddHtmlDocument(GURL("http://b.com/"), ""); |
| 573 | mhtml_archive.AddHtmlDocument(GURL("http://b.com/allow"), "Allow-CSP-From: *", |
| 574 | ""); |
| 575 | GURL mhtml_url = mhtml_archive.Write("index.mhtml"); |
| 576 | |
| 577 | EXPECT_TRUE(NavigateToURL(shell(), mhtml_url)); |
| 578 | |
| 579 | RenderFrameHostImpl* main_document = main_frame_host(); |
| 580 | ASSERT_EQ(3u, main_document->child_count()); |
| 581 | RenderFrameHostImpl* rfh_1 = main_document->child_at(0)->current_frame_host(); |
| 582 | RenderFrameHostImpl* rfh_2 = main_document->child_at(0)->current_frame_host(); |
| 583 | RenderFrameHostImpl* rfh_3 = main_document->child_at(0)->current_frame_host(); |
| 584 | |
| 585 | // Same-origin without Allow-CSP-From:* => response allowed. |
Miyoung Shin | a2dd6a4 | 2021-10-07 12:19:21 | [diff] [blame] | 586 | EXPECT_FALSE(rfh_1->IsErrorDocument()); |
arthursonzogni | 898dcda5 | 2021-01-21 08:50:10 | [diff] [blame] | 587 | |
| 588 | // Cross-origin without Allow-CSP-From:* => response blocked; |
Alison Gale | 770f3fc | 2024-04-27 00:39:58 | [diff] [blame] | 589 | // TODO(crbug.com/40143262) Add support for CSPEE in MHTML documents. |
arthursonzogni | 898dcda5 | 2021-01-21 08:50:10 | [diff] [blame] | 590 | // An error page should be displayed here. |
Miyoung Shin | a2dd6a4 | 2021-10-07 12:19:21 | [diff] [blame] | 591 | EXPECT_FALSE(rfh_2->IsErrorDocument()); |
arthursonzogni | 898dcda5 | 2021-01-21 08:50:10 | [diff] [blame] | 592 | |
| 593 | // Cross-origin with Allow-CSP-From:* => response allowed. |
Miyoung Shin | a2dd6a4 | 2021-10-07 12:19:21 | [diff] [blame] | 594 | EXPECT_FALSE(rfh_3->IsErrorDocument()); |
arthursonzogni | 898dcda5 | 2021-01-21 08:50:10 | [diff] [blame] | 595 | } |
| 596 | |
Daniel Cheng | cdb951cc | 2020-09-03 04:06:45 | [diff] [blame] | 597 | IN_PROC_BROWSER_TEST_F(NavigationMhtmlBrowserTest, |
| 598 | SameDocumentNavigationWhileLoading) { |
Rakina Zata Amni | 7b72d8f4 | 2023-08-24 04:07:06 | [diff] [blame] | 599 | if (ShouldCreateNewHostForAllFrames() && |
| 600 | ShouldQueueNavigationsWhenPendingCommitRFHExists()) { |
| 601 | GTEST_SKIP() << "When RenderDocument + navigation queueing is enabled, the " |
| 602 | "same-document navigation won't cancel the cross-document " |
| 603 | "navigation"; |
| 604 | } |
| 605 | |
Daniel Cheng | cdb951cc | 2020-09-03 04:06:45 | [diff] [blame] | 606 | // Load a MHTML archive normally so there's a renderer process for file://. |
| 607 | MhtmlArchive mhtml_archive; |
| 608 | mhtml_archive.AddHtmlDocument(GURL("http://example.com/main"), |
| 609 | "<p>Hello world!</p>"); |
| 610 | const GURL mhtml_url = mhtml_archive.Write("index.mhtml"); |
| 611 | EXPECT_TRUE(NavigateToURL(shell(), mhtml_url)); |
| 612 | |
| 613 | const RenderProcessHost* const rph = main_frame_host()->GetProcess(); |
| 614 | |
| 615 | // Navigate to another MHTML archive which will reuse the same renderer. |
| 616 | MhtmlArchive mhtml_archive2; |
| 617 | mhtml_archive2.AddHtmlDocument(GURL("http://example.com/main2"), |
| 618 | "<p>Hello world again!</p>"); |
| 619 | const GURL mhtml_url2 = mhtml_archive2.Write("index2.mhtml"); |
| 620 | |
| 621 | TestNavigationManager manager(web_contents(), mhtml_url2); |
| 622 | shell()->LoadURL(mhtml_url2); |
| 623 | |
| 624 | EXPECT_TRUE(manager.WaitForResponse()); |
| 625 | // The new navigation should not have committed yet. |
| 626 | EXPECT_EQ(mhtml_url, main_frame_host()->GetLastCommittedURL()); |
| 627 | |
| 628 | // Make sure it actually picked the same process. |
| 629 | NavigationRequest* request = |
| 630 | NavigationRequest::From(manager.GetNavigationHandle()); |
| 631 | EXPECT_EQ(rph, request->GetRenderFrameHost()->GetProcess()); |
| 632 | |
| 633 | // Delay the response body from being received by the renderer. |
| 634 | mojo::ScopedDataPipeConsumerHandle consumer; |
| 635 | mojo::ScopedDataPipeProducerHandle producer; |
| 636 | ASSERT_EQ(MOJO_RESULT_OK, |
Robert Sesek | 3bce5dd | 2021-02-19 19:27:58 | [diff] [blame] | 637 | mojo::CreateDataPipe(/* options */ nullptr, producer, consumer)); |
Daniel Cheng | cdb951cc | 2020-09-03 04:06:45 | [diff] [blame] | 638 | using std::swap; |
| 639 | swap(request->mutable_response_body_for_testing(), consumer); |
| 640 | |
| 641 | // Resume the navigation, which should send a |CommitNavigation()| to the |
| 642 | // renderer. |
| 643 | manager.ResumeNavigation(); |
| 644 | |
| 645 | // Archive loading is split into two phases: first, the entire response body |
| 646 | // is read and parsed into an MHTML archive by |MHTMLBodyLoaderClient|, and |
| 647 | // then the renderer commits the response. Since the data pipe for the |
| 648 | // response body was swapped out above, the renderer should not have committed |
| 649 | // a navigation to |mhtml_url2|. |
| 650 | // Note: Ideally, this should resume the navigation and wait for a signal that |
| 651 | // the renderer is attempting to read the response body. Unfortunately, no |
| 652 | // such signal exsts. As-is, this check is imperfect. |
| 653 | EXPECT_EQ(mhtml_url, main_frame_host()->GetLastCommittedURL()); |
| 654 | EXPECT_TRUE(web_contents()->IsLoading()); |
| 655 | |
| 656 | // While archive loading is still in progress and nothing has been committed, |
| 657 | // trigger a same-document navigation. |
Daniel Cheng | 95a27e9 | 2022-02-24 14:02:13 | [diff] [blame] | 658 | GURL::Replacements replacements; |
| 659 | replacements.SetRefStr("fragment"); |
Daniel Cheng | cdb951cc | 2020-09-03 04:06:45 | [diff] [blame] | 660 | const GURL mhtml_url_with_fragment = |
| 661 | mhtml_url.ReplaceComponents(replacements); |
| 662 | // TODO(dcheng): Using NavigateToURL() here seems to cause the test to hang. |
| 663 | // Figure out why. |
| 664 | shell()->LoadURL(mhtml_url_with_fragment); |
| 665 | |
| 666 | // The same-document navigation should cancel MHTML loading. On the browser |
| 667 | // side, this can be observed by waiting for the peer handle to be closed by |
| 668 | // the renderer. |
| 669 | base::RunLoop run_loop; |
| 670 | mojo::SimpleWatcher watcher(FROM_HERE, |
| 671 | mojo::SimpleWatcher::ArmingPolicy::AUTOMATIC); |
| 672 | watcher.Watch( |
| 673 | producer.get(), MOJO_HANDLE_SIGNAL_PEER_CLOSED, |
| 674 | MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED, |
| 675 | base::BindLambdaForTesting( |
| 676 | [&](MojoResult result, const mojo::HandleSignalsState& state) { |
| 677 | EXPECT_EQ(MOJO_RESULT_OK, result); |
| 678 | EXPECT_TRUE(state.peer_closed()); |
| 679 | run_loop.Quit(); |
| 680 | })); |
| 681 | run_loop.Run(); |
| 682 | |
| 683 | WaitForLoadStop(web_contents()); |
| 684 | EXPECT_EQ(mhtml_url_with_fragment, main_frame_host()->GetLastCommittedURL()); |
| 685 | } |
| 686 | |
arthursonzogni | 736fcf5 | 2020-09-09 15:34:54 | [diff] [blame] | 687 | // Check RenderFrameHostImpl::is_mhtml_document() stays true after same-document |
| 688 | // navigation in MHTML document. |
| 689 | // Regression test for https://crbug.com/1126391 |
| 690 | IN_PROC_BROWSER_TEST_F(NavigationMhtmlBrowserTest, |
| 691 | SameDocumentNavigationPreservesMhtmlFlag) { |
| 692 | MhtmlArchive mhtml_archive; |
| 693 | mhtml_archive.AddHtmlDocument(GURL("http://a.com/a"), ""); |
| 694 | GURL mhtml_url = mhtml_archive.Write("index.mhtml"); |
| 695 | EXPECT_TRUE(NavigateToURL(shell(), mhtml_url)); |
| 696 | EXPECT_TRUE(main_frame_host()->is_mhtml_document()); |
| 697 | EXPECT_TRUE(NavigateToURL( |
| 698 | shell(), GURL(main_frame_host()->GetLastCommittedURL().spec() + "#foo"))); |
arthursonzogni | d5149d9 | 2020-09-14 16:35:17 | [diff] [blame] | 699 | EXPECT_TRUE(main_frame_host()->is_mhtml_document()); |
arthursonzogni | 736fcf5 | 2020-09-09 15:34:54 | [diff] [blame] | 700 | } |
| 701 | |
| 702 | // Check RenderFrameHostImpl::is_mhtml_document() is correctly set for history |
| 703 | // navigation to MHTML document. It should continue to work when restored from |
| 704 | // the BackForwardCache. |
| 705 | IN_PROC_BROWSER_TEST_F(NavigationMhtmlBrowserTest, |
| 706 | BackNavigationPreservesMhtmlFlag) { |
| 707 | ASSERT_TRUE(embedded_test_server()->Start()); |
| 708 | |
| 709 | MhtmlArchive mhtml_archive; |
| 710 | mhtml_archive.AddHtmlDocument(GURL("http://a.com/a"), ""); |
| 711 | GURL mhtml_url = mhtml_archive.Write("index.mhtml"); |
| 712 | EXPECT_TRUE(NavigateToURL(shell(), mhtml_url)); |
| 713 | EXPECT_TRUE(main_frame_host()->is_mhtml_document()); |
| 714 | EXPECT_TRUE(NavigateToURL( |
| 715 | shell(), embedded_test_server()->GetURL("b.com", "/title1.html"))); |
| 716 | EXPECT_FALSE(main_frame_host()->is_mhtml_document()); |
| 717 | web_contents()->GetController().GoBack(); |
| 718 | WaitForLoadStop(web_contents()); |
| 719 | EXPECT_TRUE(main_frame_host()->is_mhtml_document()); |
| 720 | } |
| 721 | |
arthursonzogni | 07baf8358 | 2020-10-07 08:35:53 | [diff] [blame] | 722 | IN_PROC_BROWSER_TEST_F(NavigationMhtmlBrowserTest, SandboxedIframe) { |
| 723 | MhtmlArchive mhtml_archive; |
| 724 | mhtml_archive.AddHtmlDocument(GURL("http://a.com"), "", R"( |
| 725 | <iframe src="http://a.com/unsandboxed.html" ></iframe> |
| 726 | <iframe src="http://a.com/sandboxed.html" sandbox></iframe> |
| 727 | )"); |
| 728 | mhtml_archive.AddHtmlDocument(GURL("http://a.com/sandboxed.html"), ""); |
| 729 | mhtml_archive.AddHtmlDocument(GURL("http://a.com/unsandboxed.html"), ""); |
| 730 | GURL mhtml_url = mhtml_archive.Write("index.mhtml"); |
| 731 | |
| 732 | EXPECT_TRUE(NavigateToURL(shell(), mhtml_url)); |
| 733 | |
| 734 | RenderFrameHostImpl* rfh_main = main_frame_host(); |
| 735 | ASSERT_EQ(2u, rfh_main->child_count()); |
| 736 | RenderFrameHostImpl* rfh_unsandboxed = |
| 737 | rfh_main->child_at(0)->current_frame_host(); |
| 738 | RenderFrameHostImpl* rfh_sandboxed = |
| 739 | rfh_main->child_at(1)->current_frame_host(); |
| 740 | |
| 741 | auto strict_sandbox = network::mojom::WebSandboxFlags::kAll; |
| 742 | auto default_mhtml_sandbox = |
| 743 | ~network::mojom::WebSandboxFlags::kPopups & |
| 744 | ~network::mojom::WebSandboxFlags::kPropagatesToAuxiliaryBrowsingContexts; |
| 745 | |
Dan Harrington | ea4afa0 | 2024-11-06 15:26:51 | [diff] [blame] | 746 | if (base::FeatureList::IsEnabled(blink::features::kMHTML_Improvements)) { |
| 747 | default_mhtml_sandbox &= ~network::mojom::WebSandboxFlags::kScripts; |
| 748 | } |
arthursonzogni | 07baf8358 | 2020-10-07 08:35:53 | [diff] [blame] | 749 | EXPECT_EQ(default_mhtml_sandbox, rfh_main->active_sandbox_flags()); |
| 750 | EXPECT_EQ(default_mhtml_sandbox, rfh_unsandboxed->active_sandbox_flags()); |
| 751 | EXPECT_EQ(strict_sandbox, rfh_sandboxed->active_sandbox_flags()); |
| 752 | } |
| 753 | |
Lukasz Anforowicz | 39a8aef | 2020-12-17 23:50:15 | [diff] [blame] | 754 | // Regression test for https://crbug.com/1155862. |
| 755 | IN_PROC_BROWSER_TEST_F(NavigationMhtmlBrowserTest, DataIframe) { |
| 756 | MhtmlArchive mhtml_archive; |
| 757 | mhtml_archive.AddHtmlDocument( |
| 758 | GURL("http://127.0.0.1/starte.html"), "", |
| 759 | R"( <iframe src="http://8.8.8.8/test.html"></iframe> |
| 760 | <iframe src="data:text/html,blah1"></iframe> |
| 761 | <iframe src="about:blank?foo=123"></iframe> )"); |
| 762 | mhtml_archive.AddHtmlDocument(GURL("http://8.8.8.8/test.html"), "", R"( |
| 763 | <iframe src="data:text/html,blah2"></iframe> |
| 764 | <iframe src="about:blank?foo=123"></iframe> )"); |
| 765 | mhtml_archive.AddHtmlDocument(GURL("about:blank?foo=123"), "", "foo"); |
| 766 | GURL mhtml_url = mhtml_archive.Write("index.mhtml"); |
| 767 | |
| 768 | // The main test verification is that the navigation below succeeds (without |
| 769 | // crashing in NavigationRequest::GetOriginForURLLoaderFactory). |
| 770 | EXPECT_TRUE(NavigateToURL(shell(), mhtml_url)); |
| 771 | |
| 772 | // All MHTML frames should have an opaque origin. |
Dave Tapuska | 327c06c9 | 2022-06-13 20:31:51 | [diff] [blame] | 773 | shell()->web_contents()->GetPrimaryMainFrame()->ForEachRenderFrameHost( |
Daniel Cheng | 982f2b2 | 2022-08-25 23:46:16 | [diff] [blame] | 774 | [](RenderFrameHost* frame) { |
Dave Tapuska | ed66163f | 2021-09-28 14:34:54 | [diff] [blame] | 775 | EXPECT_TRUE(frame->GetLastCommittedOrigin().opaque()) |
| 776 | << "frame->GetLastCommittedURL() = " |
| 777 | << frame->GetLastCommittedURL(); |
Daniel Cheng | 982f2b2 | 2022-08-25 23:46:16 | [diff] [blame] | 778 | }); |
Lukasz Anforowicz | 39a8aef | 2020-12-17 23:50:15 | [diff] [blame] | 779 | } |
| 780 | |
Lukasz Anforowicz | 8ad9106 | 2021-01-22 19:52:39 | [diff] [blame] | 781 | // Regression test for https://crbug.com/1168249. |
| 782 | IN_PROC_BROWSER_TEST_F(NavigationMhtmlBrowserTest, PreloadedTextTrack) { |
| 783 | // The test uses a cross-site subframe, so any HTTP requests that reach the |
| 784 | // NetworkService will have `network::ResourceRequest::request_initiator` with |
| 785 | // a tuple (or precursor tuple in case of opaque origins expected for MHTML |
| 786 | // documents) that is incompatible with `request_initiator_origin_lock` in |
| 787 | // `network::mojom::URLLoaderFactoryParams`. |
| 788 | MhtmlArchive mhtml_archive; |
| 789 | mhtml_archive.AddHtmlDocument( |
| 790 | GURL("http://main.com/main.html"), "", |
| 791 | R"( <iframe src="http://subframe.com/subframe.html"></iframe> )"); |
| 792 | mhtml_archive.AddHtmlDocument( |
| 793 | GURL("http://subframe.com/subframe.html"), "", |
| 794 | R"( <link rel="preload" href="http://resource.com/track" as="track"> )"); |
| 795 | mhtml_archive.AddResource(GURL("http://resource.com/track"), "text/vtt", "", |
| 796 | "fake text track body"); |
| 797 | GURL mhtml_url = mhtml_archive.Write("index.mhtml"); |
| 798 | |
| 799 | EXPECT_TRUE(NavigateToURL(shell(), mhtml_url)); |
| 800 | |
| 801 | // The main verification is that ResourceFetcher::StartLoad didn't reach |
| 802 | // NOTREACHED assertion (against HTTP resource loads triggered from MHTML |
| 803 | // documents). To detect such NOTREACHED (via renderer crash) it is sufficient |
| 804 | // for the test to wait for DidStopLoading notification (which is done |
| 805 | // underneath NavigateToURL called above). |
| 806 | } |
| 807 | |
Rakina Zata Amni | 039c7fc | 2021-04-12 07:52:52 | [diff] [blame] | 808 | // MHTML document with a base URL of |kUnreachableWebDataURL| should not be |
| 809 | // treated as an error page. |
| 810 | IN_PROC_BROWSER_TEST_F(NavigationMhtmlBrowserTest, ErrorBaseURL) { |
| 811 | NavigationController& controller = web_contents()->GetController(); |
Carlos Caballero | 15caeeb | 2021-10-27 09:57:55 | [diff] [blame] | 812 | FrameTreeNode* root = static_cast<WebContentsImpl*>(web_contents()) |
| 813 | ->GetPrimaryFrameTree() |
| 814 | .root(); |
Rakina Zata Amni | 039c7fc | 2021-04-12 07:52:52 | [diff] [blame] | 815 | |
| 816 | // Prepare an MHTML document with the base URL set to the error page URL. |
| 817 | MhtmlArchive mhtml_archive; |
| 818 | mhtml_archive.AddHtmlDocument(GURL(kUnreachableWebDataURL), "foo"); |
| 819 | GURL mhtml_url = mhtml_archive.Write("index.mhtml"); |
| 820 | |
| 821 | // Navigate to the MHTML document. |
| 822 | FrameNavigateParamsCapturer params_capturer(root); |
| 823 | EXPECT_TRUE(NavigateToURL(shell(), mhtml_url)); |
| 824 | params_capturer.Wait(); |
| 825 | |
| 826 | // Check that the RenderFrameHost, NavigationRequest and NavigationEntry all |
| 827 | // agree that the document is not an error page. |
| 828 | RenderFrameHostImpl* main_document = main_frame_host(); |
Miyoung Shin | a2dd6a4 | 2021-10-07 12:19:21 | [diff] [blame] | 829 | EXPECT_FALSE(main_document->IsErrorDocument()); |
Rakina Zata Amni | 039c7fc | 2021-04-12 07:52:52 | [diff] [blame] | 830 | EXPECT_FALSE(params_capturer.is_error_page()); |
| 831 | EXPECT_NE(PAGE_TYPE_ERROR, controller.GetLastCommittedEntry()->GetPageType()); |
| 832 | } |
| 833 | |
Diana Qu | 90c3d8c | 2025-06-30 23:44:26 | [diff] [blame] | 834 | class NavigationMhtmlFragmentBrowserTest : public NavigationMhtmlBrowserTest { |
| 835 | public: |
| 836 | NavigationMhtmlFragmentBrowserTest() { |
| 837 | scoped_feature_list_.InitAndEnableFeature( |
| 838 | blink::features::kTreatMhtmlInitialDocumentLoadsAsCrossDocument); |
| 839 | } |
| 840 | |
| 841 | private: |
| 842 | base::test::ScopedFeatureList scoped_feature_list_; |
| 843 | }; |
| 844 | |
| 845 | // Verifies that the first navigation to about:blank#fragment inside an MHTML |
| 846 | // iframe is cross-document (RFH swap / opaque origin) and that a subsequent |
| 847 | // fragment navigation is same-document (no RFH swap, origin unchanged). |
| 848 | IN_PROC_BROWSER_TEST_F(NavigationMhtmlFragmentBrowserTest, |
| 849 | MhtmlAboutBlankFragment_FirstIsCrossDoc_RestAreSameDoc) { |
| 850 | MhtmlArchive mhtml; |
| 851 | mhtml.AddHtmlDocument(GURL("http://example.com"), |
| 852 | "<iframe src=\"about:blank#foo\"></iframe>"); |
| 853 | const GURL mhtml_url = mhtml.Write("index.mhtml"); |
| 854 | |
| 855 | NavigationHandleObserver first_nav(web_contents(), GURL("about:blank#foo")); |
| 856 | ASSERT_TRUE(NavigateToURL(shell(), mhtml_url)); |
| 857 | |
| 858 | RenderFrameHostImpl* main_rfh = main_frame_host(); |
| 859 | ASSERT_EQ(1u, main_rfh->child_count()); |
| 860 | base::WeakPtr<RenderFrameHostImpl> child_rfh = |
| 861 | main_rfh->child_at(0)->current_frame_host()->GetWeakPtr(); |
| 862 | ASSERT_TRUE(child_rfh); |
| 863 | const url::Origin first_origin = child_rfh->GetLastCommittedOrigin(); |
| 864 | |
| 865 | // The initial nav must be cross-document since it uses a new opaque origin. |
| 866 | EXPECT_TRUE(first_nav.has_committed()); |
| 867 | EXPECT_FALSE(first_nav.is_same_document()); |
| 868 | EXPECT_TRUE(first_origin.opaque()); |
| 869 | |
| 870 | // Same document fragment navigation. |
| 871 | NavigationHandleObserver second_nav(web_contents(), GURL("about:blank#bar")); |
| 872 | EXPECT_TRUE(ExecJs(child_rfh, "location.href = 'about:blank#bar';")); |
| 873 | ASSERT_TRUE(WaitForLoadStop(web_contents())); |
| 874 | |
| 875 | base::WeakPtr<RenderFrameHostImpl> after_rfh = |
| 876 | main_rfh->child_at(0)->current_frame_host()->GetWeakPtr(); |
| 877 | ASSERT_TRUE(after_rfh); |
| 878 | |
| 879 | // The second fragment navigation is same-document and doesn't change RFH. |
| 880 | EXPECT_EQ(child_rfh.get(), after_rfh.get()); |
| 881 | EXPECT_TRUE(second_nav.has_committed()); |
| 882 | EXPECT_TRUE(second_nav.is_same_document()); |
| 883 | EXPECT_EQ(first_origin, after_rfh->GetLastCommittedOrigin()); |
| 884 | } |
| 885 | |
Kevin McNee | 48f87e79 | 2022-04-29 16:53:24 | [diff] [blame] | 886 | class NavigationMhtmlFencedFrameBrowserTest |
Dominic Farolino | 0b06763 | 2022-11-11 02:57:49 | [diff] [blame] | 887 | : public NavigationMhtmlBrowserTest { |
Kevin McNee | 48f87e79 | 2022-04-29 16:53:24 | [diff] [blame] | 888 | public: |
Kevin McNee | 48f87e79 | 2022-04-29 16:53:24 | [diff] [blame] | 889 | NavigationMhtmlFencedFrameBrowserTest() { |
| 890 | scoped_feature_list_.InitWithFeaturesAndParameters( |
Dominic Farolino | 0b06763 | 2022-11-11 02:57:49 | [diff] [blame] | 891 | {{blink::features::kFencedFrames, {}}}, /*disabled_features=*/{}); |
Kevin McNee | 48f87e79 | 2022-04-29 16:53:24 | [diff] [blame] | 892 | } |
| 893 | |
| 894 | private: |
| 895 | base::test::ScopedFeatureList scoped_feature_list_; |
| 896 | }; |
| 897 | |
Dominic Farolino | 0b06763 | 2022-11-11 02:57:49 | [diff] [blame] | 898 | IN_PROC_BROWSER_TEST_F(NavigationMhtmlFencedFrameBrowserTest, |
Kevin McNee | 48f87e79 | 2022-04-29 16:53:24 | [diff] [blame] | 899 | MhtmlCannotCreateFencedFrame) { |
| 900 | MhtmlArchive mhtml_archive; |
| 901 | mhtml_archive.AddHtmlDocument( |
| 902 | GURL("http://example.com"), |
| 903 | "<fencedframe src=\"http://example.com/found.html\"></fencedframe>"); |
| 904 | mhtml_archive.AddHtmlDocument(GURL("http://example.com/found.html"), |
| 905 | "<iframe></iframe>"); |
| 906 | GURL mhtml_url = mhtml_archive.Write("index.mhtml"); |
| 907 | |
| 908 | EXPECT_TRUE(NavigateToURL(shell(), mhtml_url)); |
| 909 | |
| 910 | RenderFrameHostImpl* main_document = main_frame_host(); |
| 911 | EXPECT_TRUE(main_document->is_mhtml_document()); |
| 912 | // Ensure nothing was created for the fencedframe element. Only a single |
| 913 | // RenderFrameHost, the `main_document`, should exist. |
| 914 | int num_documents = 0; |
Peter Kasting | d2876a2 | 2024-12-03 01:12:55 | [diff] [blame] | 915 | main_document->ForEachRenderFrameHostImpl( |
Daniel Cheng | 982f2b2 | 2022-08-25 23:46:16 | [diff] [blame] | 916 | [&](RenderFrameHostImpl* rfh) { num_documents++; }); |
Kevin McNee | 48f87e79 | 2022-04-29 16:53:24 | [diff] [blame] | 917 | EXPECT_EQ(1, num_documents); |
| 918 | } |
| 919 | |
arthursonzogni | ae02e6d | 2019-06-04 10:23:11 | [diff] [blame] | 920 | } // namespace content |