blob: db90249406efdd49424efd03eba5ec50c71e9b0b [file] [log] [blame]
Avi Drissman4e1b7bc32022-09-15 14:03:501// Copyright 2019 The Chromium Authors
arthursonzogniae02e6d2019-06-04 10:23:112// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Daniel Chengcdb951cc2020-09-03 04:06:455#include <algorithm>
arthursonzogniae02e6d2019-06-04 10:23:116#include <string>
Daniel Chengcdb951cc2020-09-03 04:06:457#include <utility>
arthursonzogniae02e6d2019-06-04 10:23:118
Dan Harringtonea4afa02024-11-06 15:26:519#include "base/feature_list.h"
Alex Ilincf96655e12019-11-21 16:13:5210#include "base/files/file_util.h"
arthursonzogniae02e6d2019-06-04 10:23:1111#include "base/files/scoped_temp_dir.h"
Daniel Chengcdb951cc2020-09-03 04:06:4512#include "base/run_loop.h"
arthursonzogniae02e6d2019-06-04 10:23:1113#include "base/strings/string_util.h"
Lei Zhange02299a2021-04-26 23:12:2414#include "base/strings/stringprintf.h"
Guido Urdanetaef4e91942020-11-09 15:06:2415#include "base/test/bind.h"
Kevin McNee48f87e792022-04-29 16:53:2416#include "base/test/scoped_feature_list.h"
arthursonzogniae02e6d2019-06-04 10:23:1117#include "base/threading/thread_restrictions.h"
danakj10f32372020-09-15 22:25:1618#include "content/browser/renderer_host/navigation_request.h"
19#include "content/browser/renderer_host/render_frame_host_impl.h"
arthursonzogniae02e6d2019-06-04 10:23:1120#include "content/browser/web_contents/web_contents_impl.h"
Rakina Zata Amni7b72d8f42023-08-24 04:07:0621#include "content/common/content_navigation_policy.h"
David Sanders7d4623d2022-04-21 07:00:1822#include "content/public/common/url_constants.h"
Peter Kasting919ce652020-05-07 10:22:3623#include "content/public/test/browser_test.h"
arthursonzogniae02e6d2019-06-04 10:23:1124#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 Amni039c7fc2021-04-12 07:52:5230#include "content/test/content_browser_test_utils_internal.h"
Daniel Chengcdb951cc2020-09-03 04:06:4531#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"
arthursonzogniae02e6d2019-06-04 10:23:1136#include "net/base/filename_util.h"
arthursonzogni736fcf52020-09-09 15:34:5437#include "net/dns/mock_host_resolver.h"
arthursonzogni07baf83582020-10-07 08:35:5338#include "services/network/public/cpp/web_sandbox_flags.h"
Kevin McNee48f87e792022-04-29 16:53:2439#include "third_party/blink/public/common/features.h"
arthursonzogniae02e6d2019-06-04 10:23:1140#include "url/gurl.h"
41#include "url/url_constants.h"
42
43namespace content {
44
45namespace {
46
47// Tests about navigations to MHTML archives.
48class 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 Caballero15caeeb2021-10-27 09:57:5555 return web_contents()->GetPrimaryFrameTree().root()->current_frame_host();
arthursonzogniae02e6d2019-06-04 10:23:1156 }
arthursonzogni736fcf52020-09-09 15:34:5457
58 protected:
59 void SetUpOnMainThread() final {
60 ContentBrowserTest::SetUpOnMainThread();
61 host_resolver()->AddRule("*", "127.0.0.1");
62 }
arthursonzogniae02e6d2019-06-04 10:23:1163};
64
65// Helper class: Build MHTML documents easily in tests.
66class MhtmlArchive {
67 public:
68 MhtmlArchive() = default;
Peter Boström828b9022021-09-21 02:28:4369
70 MhtmlArchive(const MhtmlArchive&) = delete;
71 MhtmlArchive& operator=(const MhtmlArchive&) = delete;
72
arthursonzogniae02e6d2019-06-04 10:23:1173 ~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 Anforowicz8ad91062021-01-22 19:52:3982 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
arthursonzogniae02e6d2019-06-04 10:23:1196 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 Zhang9989f272020-05-11 19:21:21131 EXPECT_TRUE(base::WriteFile(file_path, document));
arthursonzogniae02e6d2019-06-04 10:23:11132 return net::FilePathToFileURL(file_path);
133 }
134
135 private:
136 base::ScopedTempDir file_directory_;
137 std::string content_;
arthursonzogniae02e6d2019-06-04 10:23:11138};
139
140} // namespace
141
142// An MHTML document with an iframe. The iframe's document is found in the
143// archive.
144IN_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
arthursonzogniae02e6d2019-06-04 10:23:11162 EXPECT_TRUE(main_document->is_mhtml_document());
arthursonzogni3fc224b2020-10-07 10:41:16163 EXPECT_TRUE(sub_document->is_mhtml_document());
arthursonzogniae02e6d2019-06-04 10:23:11164
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.
181IN_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
arthursonzogniae02e6d2019-06-04 10:23:11196 EXPECT_TRUE(main_document->is_mhtml_document());
arthursonzogni3fc224b2020-10-07 10:41:16197 EXPECT_TRUE(sub_document->is_mhtml_document());
arthursonzogniae02e6d2019-06-04 10:23:11198
Daniel Cheng8a8a9202020-08-10 19:46:57199 // 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 Gale770f3fc2024-04-27 00:39:58203 // TODO(crbug.com/40143262): Fix this by implementing a MHTML
Daniel Cheng8a8a9202020-08-10 19:46:57204 // URLLoaderFactory; then failure to find the resource can use the standard
205 // error handling path.
206 EXPECT_TRUE(iframe_navigation_observer.has_committed());
arthursonzogniae02e6d2019-06-04 10:23:11207 EXPECT_FALSE(iframe_navigation_observer.is_error());
Daniel Cheng8a8a9202020-08-10 19:46:57208 EXPECT_EQ(GURL("http://example.com/not_found.html"),
209 sub_document->GetLastCommittedURL());
arthursonzogniae02e6d2019-06-04 10:23:11210}
211
212// An MHTML document with an iframe using a data-URL. The data-URL is not
213// defined in the MHTML archive.
Alison Gale81f4f2c72024-04-22 19:33:31214// TODO(crbug.com/40629273): Enable this test. It currently reaches a
arthursonzogniae02e6d2019-06-04 10:23:11215// DCHECK or timeout in release mode.
arthursonzogni09aa34d2019-06-04 14:44:34216IN_PROC_BROWSER_TEST_F(NavigationMhtmlBrowserTest, IframeDataUrlNotFound) {
arthursonzogniae02e6d2019-06-04 10:23:11217 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
arthursonzogni3fc224b2020-10-07 10:41:16232 EXPECT_TRUE(main_document->is_mhtml_document());
Lukasz Anforowicz39a8aef2020-12-17 23:50:15233 EXPECT_TRUE(sub_document->is_mhtml_document());
arthursonzogni3fc224b2020-10-07 10:41:16234
arthursonzogniae02e6d2019-06-04 10:23:11235 // 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.
arthursonzogni09aa34d2019-06-04 14:44:34243IN_PROC_BROWSER_TEST_F(NavigationMhtmlBrowserTest, IframeDataUrlFound) {
arthursonzogniae02e6d2019-06-04 10:23:11244 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
arthursonzogni3fc224b2020-10-07 10:41:16261 EXPECT_TRUE(main_document->is_mhtml_document());
Lukasz Anforowicz39a8aef2020-12-17 23:50:15262 EXPECT_TRUE(sub_document->is_mhtml_document());
arthursonzogni3fc224b2020-10-07 10:41:16263
arthursonzogniae02e6d2019-06-04 10:23:11264 // 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.
272IN_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 Anforowicz42d3d07f2019-06-19 01:06:42284 EXPECT_TRUE(sub_document->GetLastCommittedURL().IsAboutSrcdoc());
arthursonzogniae02e6d2019-06-04 10:23:11285
arthursonzogni3fc224b2020-10-07 10:41:16286 EXPECT_TRUE(main_document->is_mhtml_document());
287 EXPECT_TRUE(sub_document->is_mhtml_document());
288
arthursonzogniae02e6d2019-06-04 10:23:11289 // 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.
297IN_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 Anforowicz42d3d07f2019-06-19 01:06:42310 EXPECT_TRUE(sub_document->GetLastCommittedURL().IsAboutSrcdoc());
arthursonzogniae02e6d2019-06-04 10:23:11311
arthursonzogni3fc224b2020-10-07 10:41:16312 EXPECT_TRUE(main_document->is_mhtml_document());
313 EXPECT_TRUE(sub_document->is_mhtml_document());
314
arthursonzogniae02e6d2019-06-04 10:23:11315 // 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.
323IN_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 Cheng8a8a9202020-08-10 19:46:57327 // Note: this is actually treated as a
328 // same-document navigation!
arthursonzogniae02e6d2019-06-04 10:23:11329 "<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 Cheng8a8a9202020-08-10 19:46:57341
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".
arthursonzogniae02e6d2019-06-04 10:23:11359 EXPECT_EQ(iframe_url(0), GURL("about:blank"));
Daniel Cheng8a8a9202020-08-10 19:46:57360 // 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"));
arthursonzogniae02e6d2019-06-04 10:23:11367 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.
372IN_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();
arthursonzogni3fc224b2020-10-07 10:41:16387
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
arthursonzogniae02e6d2019-06-04 10:23:11392 // 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.
398IN_PROC_BROWSER_TEST_F(NavigationMhtmlBrowserTest,
399 IframeJavascriptUrlNotFound) {
Dan Harringtonea4afa02024-11-06 15:26:51400 // JS is allowed to execute when this feature is enabled.
401 if (base::FeatureList::IsEnabled(blink::features::kMHTML_Improvements)) {
402 return;
403 }
arthursonzogniae02e6d2019-06-04 10:23:11404 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 Cronined376d82020-05-01 18:37:55410 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()));
arthursonzogniae02e6d2019-06-04 10:23:11415
416 EXPECT_TRUE(NavigateToURL(shell(), mhtml_url));
Fergal Daly7723f9d2022-10-29 07:03:13417 ASSERT_TRUE(console_observer.Wait());
arthursonzogniae02e6d2019-06-04 10:23:11418
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();
arthursonzogni3fc224b2020-10-07 10:41:16423
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());
arthursonzogniae02e6d2019-06-04 10:23:11428 EXPECT_EQ(GURL(), sub_document->GetLastCommittedURL());
429}
430
431// An MHTML document with an iframe trying to load a javascript URL. The
432IN_PROC_BROWSER_TEST_F(NavigationMhtmlBrowserTest, IframeJavascriptUrlFound) {
Dan Harringtonea4afa02024-11-06 15:26:51433 // JS is allowed to execute when this feature is enabled.
434 if (base::FeatureList::IsEnabled(blink::features::kMHTML_Improvements)) {
435 return;
436 }
arthursonzogniae02e6d2019-06-04 10:23:11437 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 Cronined376d82020-05-01 18:37:55445 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()));
arthursonzogniae02e6d2019-06-04 10:23:11450
451 EXPECT_TRUE(NavigateToURL(shell(), mhtml_url));
Fergal Daly7723f9d2022-10-29 07:03:13452 ASSERT_TRUE(console_observer.Wait());
arthursonzogniae02e6d2019-06-04 10:23:11453
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();
arthursonzogni3fc224b2020-10-07 10:41:16458
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());
arthursonzogniae02e6d2019-06-04 10:23:11463 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.
470IN_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
arthursonzogni3fc224b2020-10-07 10:41:16487 EXPECT_TRUE(main_document->is_mhtml_document());
488 EXPECT_TRUE(sub_document->is_mhtml_document());
489
arthursonzogniae02e6d2019-06-04 10:23:11490 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.
502IN_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
arthursonzogni3fc224b2020-10-07 10:41:16517 EXPECT_TRUE(main_document->is_mhtml_document());
518 EXPECT_TRUE(sub_document->is_mhtml_document());
519
Daniel Cheng8a8a9202020-08-10 19:46:57520 // 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 Gale770f3fc2024-04-27 00:39:58524 // TODO(crbug.com/40143262): Fix this by implementing a MHTML
Daniel Cheng8a8a9202020-08-10 19:46:57525 // 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());
arthursonzogniae02e6d2019-06-04 10:23:11529 EXPECT_FALSE(iframe_navigation.is_error());
530}
531
arthursonzogni654ed172019-06-11 14:41:04532// Tests Content-Security-Policy: frame-ancestors enforcement in MHTML
533// subframes. It isn't enforced currently.
534// See https://crbug.com/969711.
535IN_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
arthursonzogni3fc224b2020-10-07 10:41:16547 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());
arthursonzogni654ed172019-06-11 14:41:04554
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.
arthursonzogni3fc224b2020-10-07 10:41:16559 ASSERT_EQ(1u, sub_document->child_count());
arthursonzogni654ed172019-06-11 14:41:04560}
561
arthursonzogni898dcda52021-01-21 08:50:10562// Tests CSP embedded enforcement blocking an iframes.
563// Regression test for https://crbug.com/1112965
564IN_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 Shina2dd6a42021-10-07 12:19:21586 EXPECT_FALSE(rfh_1->IsErrorDocument());
arthursonzogni898dcda52021-01-21 08:50:10587
588 // Cross-origin without Allow-CSP-From:* => response blocked;
Alison Gale770f3fc2024-04-27 00:39:58589 // TODO(crbug.com/40143262) Add support for CSPEE in MHTML documents.
arthursonzogni898dcda52021-01-21 08:50:10590 // An error page should be displayed here.
Miyoung Shina2dd6a42021-10-07 12:19:21591 EXPECT_FALSE(rfh_2->IsErrorDocument());
arthursonzogni898dcda52021-01-21 08:50:10592
593 // Cross-origin with Allow-CSP-From:* => response allowed.
Miyoung Shina2dd6a42021-10-07 12:19:21594 EXPECT_FALSE(rfh_3->IsErrorDocument());
arthursonzogni898dcda52021-01-21 08:50:10595}
596
Daniel Chengcdb951cc2020-09-03 04:06:45597IN_PROC_BROWSER_TEST_F(NavigationMhtmlBrowserTest,
598 SameDocumentNavigationWhileLoading) {
Rakina Zata Amni7b72d8f42023-08-24 04:07:06599 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 Chengcdb951cc2020-09-03 04:06:45606 // 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 Sesek3bce5dd2021-02-19 19:27:58637 mojo::CreateDataPipe(/* options */ nullptr, producer, consumer));
Daniel Chengcdb951cc2020-09-03 04:06:45638 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 Cheng95a27e92022-02-24 14:02:13658 GURL::Replacements replacements;
659 replacements.SetRefStr("fragment");
Daniel Chengcdb951cc2020-09-03 04:06:45660 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
arthursonzogni736fcf52020-09-09 15:34:54687// Check RenderFrameHostImpl::is_mhtml_document() stays true after same-document
688// navigation in MHTML document.
689// Regression test for https://crbug.com/1126391
690IN_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")));
arthursonzognid5149d92020-09-14 16:35:17699 EXPECT_TRUE(main_frame_host()->is_mhtml_document());
arthursonzogni736fcf52020-09-09 15:34:54700}
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.
705IN_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
arthursonzogni07baf83582020-10-07 08:35:53722IN_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 Harringtonea4afa02024-11-06 15:26:51746 if (base::FeatureList::IsEnabled(blink::features::kMHTML_Improvements)) {
747 default_mhtml_sandbox &= ~network::mojom::WebSandboxFlags::kScripts;
748 }
arthursonzogni07baf83582020-10-07 08:35:53749 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 Anforowicz39a8aef2020-12-17 23:50:15754// Regression test for https://crbug.com/1155862.
755IN_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 Tapuska327c06c92022-06-13 20:31:51773 shell()->web_contents()->GetPrimaryMainFrame()->ForEachRenderFrameHost(
Daniel Cheng982f2b22022-08-25 23:46:16774 [](RenderFrameHost* frame) {
Dave Tapuskaed66163f2021-09-28 14:34:54775 EXPECT_TRUE(frame->GetLastCommittedOrigin().opaque())
776 << "frame->GetLastCommittedURL() = "
777 << frame->GetLastCommittedURL();
Daniel Cheng982f2b22022-08-25 23:46:16778 });
Lukasz Anforowicz39a8aef2020-12-17 23:50:15779}
780
Lukasz Anforowicz8ad91062021-01-22 19:52:39781// Regression test for https://crbug.com/1168249.
782IN_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 Amni039c7fc2021-04-12 07:52:52808// MHTML document with a base URL of |kUnreachableWebDataURL| should not be
809// treated as an error page.
810IN_PROC_BROWSER_TEST_F(NavigationMhtmlBrowserTest, ErrorBaseURL) {
811 NavigationController& controller = web_contents()->GetController();
Carlos Caballero15caeeb2021-10-27 09:57:55812 FrameTreeNode* root = static_cast<WebContentsImpl*>(web_contents())
813 ->GetPrimaryFrameTree()
814 .root();
Rakina Zata Amni039c7fc2021-04-12 07:52:52815
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 Shina2dd6a42021-10-07 12:19:21829 EXPECT_FALSE(main_document->IsErrorDocument());
Rakina Zata Amni039c7fc2021-04-12 07:52:52830 EXPECT_FALSE(params_capturer.is_error_page());
831 EXPECT_NE(PAGE_TYPE_ERROR, controller.GetLastCommittedEntry()->GetPageType());
832}
833
Diana Qu90c3d8c2025-06-30 23:44:26834class 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).
848IN_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 McNee48f87e792022-04-29 16:53:24886class NavigationMhtmlFencedFrameBrowserTest
Dominic Farolino0b067632022-11-11 02:57:49887 : public NavigationMhtmlBrowserTest {
Kevin McNee48f87e792022-04-29 16:53:24888 public:
Kevin McNee48f87e792022-04-29 16:53:24889 NavigationMhtmlFencedFrameBrowserTest() {
890 scoped_feature_list_.InitWithFeaturesAndParameters(
Dominic Farolino0b067632022-11-11 02:57:49891 {{blink::features::kFencedFrames, {}}}, /*disabled_features=*/{});
Kevin McNee48f87e792022-04-29 16:53:24892 }
893
894 private:
895 base::test::ScopedFeatureList scoped_feature_list_;
896};
897
Dominic Farolino0b067632022-11-11 02:57:49898IN_PROC_BROWSER_TEST_F(NavigationMhtmlFencedFrameBrowserTest,
Kevin McNee48f87e792022-04-29 16:53:24899 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 Kastingd2876a22024-12-03 01:12:55915 main_document->ForEachRenderFrameHostImpl(
Daniel Cheng982f2b22022-08-25 23:46:16916 [&](RenderFrameHostImpl* rfh) { num_documents++; });
Kevin McNee48f87e792022-04-29 16:53:24917 EXPECT_EQ(1, num_documents);
918}
919
arthursonzogniae02e6d2019-06-04 10:23:11920} // namespace content