1 // Copyright 2019 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "base/command_line.h"
6 #include "base/functional/callback.h"
7 #include "base/test/bind.h"
8 #include "base/test/mock_callback.h"
9 #include "base/test/scoped_feature_list.h"
10 #include "base/test/scoped_logging_settings.h"
11 #include "build/build_config.h"
12 #include "build/chromeos_buildflags.h"
13 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
14 #include "chrome/browser/content_settings/mixed_content_settings_tab_helper.h"
15 #include "chrome/browser/pdf/pdf_extension_test_util.h"
16 #include "chrome/browser/profiles/profile.h"
17 #include "chrome/browser/task_manager/task_manager_tester.h"
18 #include "chrome/browser/ui/browser.h"
19 #include "chrome/browser/ui/browser_content_setting_bubble_model_delegate.h"
20 #include "chrome/browser/ui/content_settings/content_setting_bubble_model.h"
21 #include "chrome/browser/ui/tabs/tab_strip_model.h"
22 #include "chrome/grit/generated_resources.h"
23 #include "chrome/test/base/in_process_browser_test.h"
24 #include "chrome/test/base/ui_test_utils.h"
25 #include "components/content_settings/core/browser/host_content_settings_map.h"
26 #include "components/content_settings/core/common/content_settings.h"
27 #include "components/content_settings/core/common/content_settings_types.h"
28 #include "components/metrics/content/subprocess_metrics_provider.h"
29 #include "components/network_session_configurator/common/network_switches.h"
30 #include "components/page_load_metrics/browser/observers/core/uma_page_load_metrics_observer.h"
31 #include "components/permissions/permission_manager.h"
32 #include "content/public/browser/permission_controller.h"
33 #include "content/public/browser/permission_request_description.h"
34 #include "content/public/browser/render_process_host.h"
35 #include "content/public/common/content_features.h"
36 #include "content/public/common/content_switches.h"
37 #include "content/public/test/back_forward_cache_util.h"
38 #include "content/public/test/browser_test.h"
39 #include "content/public/test/browser_test_utils.h"
40 #include "content/public/test/test_navigation_observer.h"
41 #include "content/public/test/test_utils.h"
42 #include "net/dns/mock_host_resolver.h"
43 #include "net/test/embedded_test_server/embedded_test_server.h"
44 #include "pdf/buildflags.h"
45 #include "third_party/blink/public/common/permissions/permission_utils.h"
46 #include "third_party/blink/public/common/scheduler/web_scheduler_tracked_feature.h"
47 #include "third_party/blink/public/mojom/webshare/webshare.mojom.h"
48 #include "ui/base/l10n/l10n_util.h"
50 class ChromeBackForwardCacheBrowserTest : public InProcessBrowserTest {
52 ChromeBackForwardCacheBrowserTest() = default;
54 ChromeBackForwardCacheBrowserTest(const ChromeBackForwardCacheBrowserTest&) =
56 ChromeBackForwardCacheBrowserTest& operator=(
57 const ChromeBackForwardCacheBrowserTest&) = delete;
59 ~ChromeBackForwardCacheBrowserTest() override = default;
61 void SetUpOnMainThread() override {
62 host_resolver()->AddRule("*", "127.0.0.1");
63 histogram_tester_ = std::make_unique<base::HistogramTester>();
66 // At the chrome layer, an outstanding request to /favicon.ico is made. It is
67 // made by the renderer on behalf of the browser process. It counts as an
68 // outstanding request, which prevents the page from entering the
69 // BackForwardCache, as long as it hasn't resolved.
71 // There are no real way to wait for this to complete. Not waiting would make
72 // the test potentially flaky. To prevent this, the no-favicon.html page is
73 // used, the image is not loaded from the network.
74 GURL GetURL(const std::string& host) {
75 return embedded_test_server()->GetURL(
76 host, "/back_forward_cache/no-favicon.html");
80 void SetUpCommandLine(base::CommandLine* command_line) override {
81 // For using an HTTPS server.
82 base::CommandLine::ForCurrentProcess()->AppendSwitch(
83 switches::kIgnoreCertificateErrors);
84 // For using WebBluetooth.
85 command_line->AppendSwitch(
86 switches::kEnableExperimentalWebPlatformFeatures);
88 SetupFeaturesAndParameters();
91 content::WebContents* web_contents() const {
92 return browser()->tab_strip_model()->GetActiveWebContents();
95 content::RenderFrameHost* current_frame_host() {
96 return web_contents()->GetPrimaryMainFrame();
99 void SetupFeaturesAndParameters() {
100 scoped_feature_list_.InitWithFeaturesAndParameters(
101 content::GetDefaultEnabledBackForwardCacheFeaturesForTesting(),
102 content::GetDefaultDisabledBackForwardCacheFeaturesForTesting(
103 {// Entry to the cache can be slow during testing and cause
105 features::kBackForwardCacheEntryTimeout}));
106 vmodule_switches_.InitWithSwitches("back_forward_cache_impl=1");
109 std::unique_ptr<base::HistogramTester> histogram_tester_;
112 base::test::ScopedFeatureList scoped_feature_list_;
113 logging::ScopedVmoduleSwitches vmodule_switches_;
116 IN_PROC_BROWSER_TEST_F(ChromeBackForwardCacheBrowserTest, Basic) {
117 ASSERT_TRUE(embedded_test_server()->Start());
120 EXPECT_TRUE(content::NavigateToURL(web_contents(), GetURL("a.com")));
121 content::RenderFrameHostWrapper rfh_a(current_frame_host());
124 EXPECT_TRUE(content::NavigateToURL(web_contents(), GetURL("b.com")));
125 content::RenderFrameHostWrapper rfh_b(current_frame_host());
127 // A is frozen in the BackForwardCache.
128 EXPECT_EQ(rfh_a->GetLifecycleState(),
129 content::RenderFrameHost::LifecycleState::kInBackForwardCache);
132 web_contents()->GetController().GoBack();
133 EXPECT_TRUE(content::WaitForLoadStop(web_contents()));
135 // A is restored, B is stored.
136 EXPECT_EQ(rfh_b->GetLifecycleState(),
137 content::RenderFrameHost::LifecycleState::kInBackForwardCache);
139 // 4) Navigate forward.
140 web_contents()->GetController().GoForward();
141 EXPECT_TRUE(content::WaitForLoadStop(web_contents()));
143 // A is stored, B is restored.
144 EXPECT_EQ(rfh_a->GetLifecycleState(),
145 content::RenderFrameHost::LifecycleState::kInBackForwardCache);
148 IN_PROC_BROWSER_TEST_F(ChromeBackForwardCacheBrowserTest, BasicIframe) {
149 ASSERT_TRUE(embedded_test_server()->Start());
152 EXPECT_TRUE(content::NavigateToURL(web_contents(), GetURL("a.com")));
153 content::RenderFrameHostWrapper rfh_a(current_frame_host());
155 // 2) Add an iframe B.
156 EXPECT_TRUE(content::ExecJs(rfh_a.get(), R"(
157 let url = new URL(location.href);
158 url.hostname = 'b.com';
159 let iframe = document.createElement('iframe');
161 document.body.appendChild(iframe);
163 EXPECT_TRUE(content::WaitForLoadStop(web_contents()));
165 content::RenderFrameHost* rfh_b = nullptr;
166 rfh_a->ForEachRenderFrameHost([&](content::RenderFrameHost* rfh) {
167 if (rfh != rfh_a.get())
171 content::RenderFrameHostWrapper rfh_b_wrapper(rfh_b);
174 EXPECT_TRUE(content::NavigateToURL(web_contents(), GetURL("c.com")));
175 content::RenderFrameHostWrapper rfh_c(current_frame_host());
177 // A and B are frozen. The page A(B) is stored in the BackForwardCache.
178 EXPECT_EQ(rfh_a->GetLifecycleState(),
179 content::RenderFrameHost::LifecycleState::kInBackForwardCache);
180 EXPECT_EQ(rfh_b_wrapper->GetLifecycleState(),
181 content::RenderFrameHost::LifecycleState::kInBackForwardCache);
184 web_contents()->GetController().GoBack();
185 EXPECT_TRUE(content::WaitForLoadStop(web_contents()));
187 // The page A(B) is restored and C is frozen.
188 EXPECT_EQ(rfh_c->GetLifecycleState(),
189 content::RenderFrameHost::LifecycleState::kInBackForwardCache);
192 IN_PROC_BROWSER_TEST_F(ChromeBackForwardCacheBrowserTest,
193 PermissionContextBase) {
194 // HTTPS needed for GEOLOCATION permission
195 net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
196 https_server.AddDefaultHandlers(GetChromeTestDataDir());
197 https_server.SetSSLConfig(net::EmbeddedTestServer::CERT_TEST_NAMES);
198 ASSERT_TRUE(https_server.Start());
200 GURL url_a(https_server.GetURL("a.test", "/title1.html"));
201 GURL url_b(https_server.GetURL("b.test", "/title1.html"));
204 EXPECT_TRUE(NavigateToURL(web_contents(), url_a));
205 content::RenderFrameHostWrapper rfh_a(current_frame_host());
208 EXPECT_TRUE(NavigateToURL(web_contents(), url_b));
209 EXPECT_EQ(rfh_a->GetLifecycleState(),
210 content::RenderFrameHost::LifecycleState::kInBackForwardCache);
211 base::MockOnceCallback<void(blink::mojom::PermissionStatus)> callback;
212 EXPECT_CALL(callback, Run(blink::mojom::PermissionStatus::ASK));
215 ->GetPermissionController()
216 ->RequestPermissionFromCurrentDocument(
218 content::PermissionRequestDescription(
219 blink::PermissionType::GEOLOCATION, /* user_gesture = */ true),
222 // Ensure |rfh_a| is evicted from the cache because it is not allowed to
223 // service the GEOLOCATION permission request.
224 ASSERT_TRUE(rfh_a.WaitUntilRenderFrameDeleted());
227 IN_PROC_BROWSER_TEST_F(ChromeBackForwardCacheBrowserTest,
228 DoesNotCacheIfPictureInPicture) {
229 embedded_test_server()->ServeFilesFromSourceDirectory("content/test/data");
230 ASSERT_TRUE(embedded_test_server()->Start());
232 // Navigate to a page with picture-in-picture functionality.
233 const base::FilePath::CharType picture_in_picture_page[] =
234 FILE_PATH_LITERAL("media/picture-in-picture/window-size.html");
235 GURL test_page_url = ui_test_utils::GetTestUrl(
236 base::FilePath(base::FilePath::kCurrentDirectory),
237 base::FilePath(picture_in_picture_page));
238 EXPECT_TRUE(content::NavigateToURL(web_contents(), test_page_url));
239 content::RenderFrameHostWrapper rfh(current_frame_host());
241 // Execute picture-in-picture on the page.
242 ASSERT_EQ(true, content::EvalJs(web_contents(), "enterPictureInPicture();"));
245 EXPECT_TRUE(content::NavigateToURL(web_contents(), GetURL("b.com")));
247 // The page uses Picture-in-Picture so it must be evicted from the cache and
249 ASSERT_TRUE(rfh.WaitUntilRenderFrameDeleted());
252 #if BUILDFLAG(IS_ANDROID)
253 IN_PROC_BROWSER_TEST_F(ChromeBackForwardCacheBrowserTest,
254 DoesNotCacheIfWebShare) {
255 // HTTPS needed for WebShare permission.
256 net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
257 https_server.AddDefaultHandlers(GetChromeTestDataDir());
258 https_server.SetSSLConfig(net::EmbeddedTestServer::CERT_TEST_NAMES);
259 ASSERT_TRUE(https_server.Start());
261 GURL url_a(https_server.GetURL("a.test", "/title1.html"));
262 GURL url_b(https_server.GetURL("b.test", "/title1.html"));
265 EXPECT_TRUE(content::NavigateToURL(web_contents(), url_a));
266 content::RenderFrameHostWrapper rfh_a(current_frame_host());
268 // Use the WebShare feature on the empty page.
269 EXPECT_EQ("success", content::EvalJs(current_frame_host(), R"(
270 new Promise(resolve => {
271 navigator.share({title: 'the title'})
272 .then(m => { resolve("success"); })
273 .catch(error => { resolve(error.message); });
278 EXPECT_TRUE(content::NavigateToURL(web_contents(), url_b));
280 // The page uses WebShare so it must be evicted from the cache and deleted.
281 ASSERT_TRUE(rfh_a.WaitUntilRenderFrameDeleted());
284 web_contents()->GetController().GoBack();
285 EXPECT_TRUE(content::WaitForLoadStop(web_contents()));
288 IN_PROC_BROWSER_TEST_F(ChromeBackForwardCacheBrowserTest,
289 DoesNotCacheIfWebNfc) {
290 // HTTPS needed for WebNfc permission.
291 net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
292 https_server.AddDefaultHandlers(GetChromeTestDataDir());
293 https_server.SetSSLConfig(net::EmbeddedTestServer::CERT_TEST_NAMES);
294 ASSERT_TRUE(https_server.Start());
296 GURL url_a(https_server.GetURL("a.test", "/title1.html"));
297 GURL url_b(https_server.GetURL("b.test", "/title1.html"));
300 EXPECT_TRUE(content::NavigateToURL(web_contents(), url_a));
301 content::RenderFrameHostWrapper rfh_a(current_frame_host());
303 // Use the WebNfc feature on the empty page.
304 EXPECT_EQ("success", content::EvalJs(current_frame_host(), R"(
305 const ndef = new NDEFReader();
306 new Promise(async resolve => {
308 await ndef.write("Hello");
311 resolve(error.message);
317 EXPECT_TRUE(content::NavigateToURL(web_contents(), url_b));
319 // The page uses WebNfc so it must be evicted from the cache and deleted.
320 ASSERT_TRUE(rfh_a.WaitUntilRenderFrameDeleted());
323 web_contents()->GetController().GoBack();
324 EXPECT_TRUE(content::WaitForLoadStop(web_contents()));
328 IN_PROC_BROWSER_TEST_F(ChromeBackForwardCacheBrowserTest,
329 RestoresMixedContentSettings) {
330 net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
331 https_server.AddDefaultHandlers(GetChromeTestDataDir());
332 https_server.SetSSLConfig(net::EmbeddedTestServer::CERT_TEST_NAMES);
333 ASSERT_TRUE(https_server.Start());
334 GURL url_a(https_server.GetURL("a.test",
335 "/content_setting_bubble/mixed_script.html"));
336 GURL url_b(https_server.GetURL("b.test",
337 "/content_setting_bubble/mixed_script.html"));
339 // 1) Load page A that has mixed content.
340 EXPECT_TRUE(content::NavigateToURL(web_contents(), url_a));
341 // Mixed content should be blocked at first.
342 EXPECT_FALSE(MixedContentSettingsTabHelper::FromWebContents(web_contents())
343 ->IsRunningInsecureContentAllowed(*current_frame_host()));
345 // 2) Emulate link clicking on the mixed script bubble to allow mixed content
347 content::TestNavigationObserver observer(
348 browser()->tab_strip_model()->GetActiveWebContents());
349 std::unique_ptr<ContentSettingBubbleModel> model(
350 ContentSettingBubbleModel::CreateContentSettingBubbleModel(
351 browser()->content_setting_bubble_model_delegate(),
352 browser()->tab_strip_model()->GetActiveWebContents(),
353 ContentSettingsType::MIXEDSCRIPT));
354 model->OnCustomLinkClicked();
356 // 3) Wait for reload.
358 content::RenderFrameHostWrapper rfh_a(current_frame_host());
360 // Mixed content should no longer be blocked.
361 EXPECT_TRUE(MixedContentSettingsTabHelper::FromWebContents(web_contents())
362 ->IsRunningInsecureContentAllowed(*current_frame_host()));
364 // 4) Navigate to page B, which should use a different SiteInstance and
365 // resets the mixed content settings.
366 EXPECT_TRUE(content::NavigateToURL(web_contents(), url_b));
367 // Mixed content should be blocked in the new page.
368 EXPECT_FALSE(MixedContentSettingsTabHelper::FromWebContents(web_contents())
369 ->IsRunningInsecureContentAllowed(*current_frame_host()));
371 // 5) A is stored in BackForwardCache.
372 EXPECT_EQ(rfh_a->GetLifecycleState(),
373 content::RenderFrameHost::LifecycleState::kInBackForwardCache);
375 // 6) Go back to page A.
376 web_contents()->GetController().GoBack();
377 EXPECT_TRUE(content::WaitForLoadStop(web_contents()));
378 // Mixed content settings is restored, so it's no longer blocked.
379 EXPECT_TRUE(MixedContentSettingsTabHelper::FromWebContents(web_contents())
380 ->IsRunningInsecureContentAllowed(*current_frame_host()));
383 class MetricsChromeBackForwardCacheBrowserTest
384 : public ChromeBackForwardCacheBrowserTest,
385 public ::testing::WithParamInterface<std::string> {
387 MetricsChromeBackForwardCacheBrowserTest() = default;
388 ~MetricsChromeBackForwardCacheBrowserTest() override = default;
391 void SetUpCommandLine(base::CommandLine* command_line) override {
392 // TODO(crbug.com/1224780): This test used an experiment param (which no
393 // longer exists) to suppress the metrics send timer. If and when the test
394 // is re-enabled, it should be updated to use a different mechanism.
395 ChromeBackForwardCacheBrowserTest::SetUpCommandLine(command_line);
399 // Flaky https://crbug.com/1224780
400 IN_PROC_BROWSER_TEST_P(MetricsChromeBackForwardCacheBrowserTest,
401 DISABLED_FirstInputDelay) {
402 ASSERT_TRUE(embedded_test_server()->Start());
404 GURL url1(embedded_test_server()->GetURL("a.com", "/title1.html"));
405 GURL url2(embedded_test_server()->GetURL(
406 (GetParam() == "SameSite") ? "a.com" : "b.com", "/title2.html"));
408 EXPECT_THAT(histogram_tester_->GetAllSamples(
409 internal::kHistogramFirstContentfulPaint),
412 // 1) Navigate to url1.
413 EXPECT_TRUE(content::NavigateToURL(web_contents(), url1));
414 content::RenderFrameHostWrapper rfh_url1(current_frame_host());
416 // Simulate mouse click. FirstInputDelay won't get updated immediately.
417 content::SimulateMouseClickAt(web_contents(), 0,
418 blink::WebMouseEvent::Button::kLeft,
419 gfx::Point(100, 100));
420 // Run arbitrary script and run tasks in the browser to ensure the input is
421 // processed in the renderer.
422 EXPECT_TRUE(content::ExecJs(rfh_url1.get(), "var foo = 42;"));
423 base::RunLoop().RunUntilIdle();
424 content::FetchHistogramsFromChildProcesses();
425 histogram_tester_->ExpectTotalCount(internal::kHistogramFirstInputDelay, 0);
427 // 2) Immediately navigate to url2.
428 if (GetParam() == "CrossSiteRendererInitiated") {
429 EXPECT_TRUE(content::NavigateToURLFromRenderer(web_contents(), url2));
431 EXPECT_TRUE(content::NavigateToURL(web_contents(), url2));
434 // Ensure |rfh_url1| is cached.
435 EXPECT_EQ(rfh_url1->GetLifecycleState(),
436 content::RenderFrameHost::LifecycleState::kInBackForwardCache);
438 content::FetchHistogramsFromChildProcesses();
439 if (GetParam() != "CrossSiteBrowserInitiated" ||
440 rfh_url1.get()->GetProcess() == current_frame_host()->GetProcess()) {
441 // - For "SameSite" case, since the old and new RenderFrame share a process,
442 // the metrics update will be sent to the browser during commit and won't
443 // get ignored, successfully updating the FirstInputDelay histogram.
444 // - For "CrossSiteRendererInitiated" case, FirstInputDelay was sent when
445 // the renderer-initiated navigation started on the old frame.
446 // - For "CrossSiteBrowserInitiated" case, if the old and new RenderFrame
447 // share a process, the metrics update will be sent to the browser during
448 // commit and won't get ignored, successfully updating the histogram.
449 histogram_tester_->ExpectTotalCount(internal::kHistogramFirstInputDelay, 1);
451 // Note that in some cases the metrics might flakily get updated in time,
452 // before the browser changed the current RFH. So, we can neither expect it
453 // to be 0 all the time or 1 all the time.
454 // TODO(crbug.com/1150242): Support updating metrics consistently on
455 // cross-RFH cross-process navigations.
459 std::vector<std::string> MetricsChromeBackForwardCacheBrowserTestValues() {
460 return {"SameSite", "CrossSiteRendererInitiated",
461 "CrossSiteBrowserInitiated"};
464 INSTANTIATE_TEST_SUITE_P(
466 MetricsChromeBackForwardCacheBrowserTest,
467 testing::ValuesIn(MetricsChromeBackForwardCacheBrowserTestValues()));
469 // Ensure that BackForwardCache RenderFrameHosts are shown in the Task Manager.
470 IN_PROC_BROWSER_TEST_F(ChromeBackForwardCacheBrowserTest,
471 ShowMainFrameInTaskManager) {
472 ASSERT_TRUE(embedded_test_server()->Start());
474 GURL url_a(embedded_test_server()->GetURL("a.com", "/title2.html"));
475 const std::u16string expected_url_a_active_title = l10n_util::GetStringFUTF16(
476 IDS_TASK_MANAGER_TAB_PREFIX, u"Title Of Awesomeness");
477 const std::u16string expected_url_a_cached_title = l10n_util::GetStringFUTF16(
478 IDS_TASK_MANAGER_BACK_FORWARD_CACHE_PREFIX, u"http://a.com/");
480 GURL url_b(embedded_test_server()->GetURL("b.com", "/title3.html"));
481 const std::u16string expected_url_b_active_title = l10n_util::GetStringFUTF16(
482 IDS_TASK_MANAGER_TAB_PREFIX, u"Title Of More Awesomeness");
483 const std::u16string expected_url_b_cached_title = l10n_util::GetStringFUTF16(
484 IDS_TASK_MANAGER_BACK_FORWARD_CACHE_PREFIX, u"http://b.com/");
487 task_manager::TaskManagerTester::Create(base::RepeatingClosure());
489 // 1) Navigate to |url_a|.
490 ASSERT_TRUE(content::NavigateToURL(web_contents(), url_a));
491 content::RenderFrameHostWrapper rfh_a(current_frame_host());
493 // 2) Navigate to |url_b|.
494 ASSERT_TRUE(content::NavigateToURL(web_contents(), url_b));
495 content::RenderFrameHostWrapper rfh_b(current_frame_host());
497 // 3) Verify |url_a| is in the BackForwardCache.
498 ASSERT_EQ(rfh_a->GetLifecycleState(),
499 content::RenderFrameHost::LifecycleState::kInBackForwardCache);
501 // 4) Ensure both tabs show up in Task Manager.
502 task_manager::browsertest_util::WaitForTaskManagerRows(
503 1, expected_url_b_active_title);
504 task_manager::browsertest_util::WaitForTaskManagerRows(
505 1, expected_url_a_cached_title);
506 EXPECT_THAT(tester->GetWebContentsTaskTitles(),
507 ::testing::ElementsAre(expected_url_b_active_title,
508 expected_url_a_cached_title));
510 // 5) Navigate back to |url_a|.
511 web_contents()->GetController().GoBack();
512 ASSERT_TRUE(content::WaitForLoadStop(web_contents()));
514 // 6) Verify |url_b| is in the BackForwardCache.
515 ASSERT_EQ(rfh_b->GetLifecycleState(),
516 content::RenderFrameHost::LifecycleState::kInBackForwardCache);
518 // 7) Ensure both tabs show up in Task Manager.
519 task_manager::browsertest_util::WaitForTaskManagerRows(
520 1, expected_url_a_active_title);
521 task_manager::browsertest_util::WaitForTaskManagerRows(
522 1, expected_url_b_cached_title);
523 EXPECT_THAT(tester->GetWebContentsTaskTitles(),
524 ::testing::ElementsAre(expected_url_a_active_title,
525 expected_url_b_cached_title));
528 // Ensure that BackForwardCache cross-site subframes are shown in the Task
530 IN_PROC_BROWSER_TEST_F(ChromeBackForwardCacheBrowserTest,
531 ShowCrossSiteOOPIFInTaskManager) {
532 ASSERT_TRUE(embedded_test_server()->Start());
534 // Load a page on a.com with cross-site iframes on b.com and c.com.
536 embedded_test_server()->GetURL("a.com", "/iframe_cross_site.html"));
537 const std::u16string expected_url_a_cached_title = l10n_util::GetStringFUTF16(
538 IDS_TASK_MANAGER_BACK_FORWARD_CACHE_PREFIX, u"http://a.com/");
539 const std::u16string expected_url_a_cached_subframe_b_title =
540 l10n_util::GetStringFUTF16(
541 IDS_TASK_MANAGER_BACK_FORWARD_CACHE_SUBFRAME_PREFIX,
543 const std::u16string expected_url_a_cached_subframe_c_title =
544 l10n_util::GetStringFUTF16(
545 IDS_TASK_MANAGER_BACK_FORWARD_CACHE_SUBFRAME_PREFIX,
548 GURL url_b(embedded_test_server()->GetURL("b.com", "/title3.html"));
549 const std::u16string expected_url_b_active_title = l10n_util::GetStringFUTF16(
550 IDS_TASK_MANAGER_TAB_PREFIX, u"Title Of More Awesomeness");
553 task_manager::TaskManagerTester::Create(base::RepeatingClosure());
555 // 1) Navigate to |url_a|.
556 EXPECT_TRUE(content::NavigateToURL(web_contents(), url_a));
557 content::RenderFrameHostWrapper rfh_a(current_frame_host());
559 // 2) Navigate to |url_b|.
560 EXPECT_TRUE(content::NavigateToURL(web_contents(), url_b));
562 // 3) Verify |url_a| is in the BackForwardCache.
563 EXPECT_EQ(rfh_a->GetLifecycleState(),
564 content::RenderFrameHost::LifecycleState::kInBackForwardCache);
566 // 4) Ensure the subframe tasks for |url_a| show up in Task Manager.
567 task_manager::browsertest_util::WaitForTaskManagerRows(
568 1, expected_url_b_active_title);
569 task_manager::browsertest_util::WaitForTaskManagerRows(
570 1, expected_url_a_cached_title);
571 task_manager::browsertest_util::WaitForTaskManagerRows(
572 1, expected_url_a_cached_subframe_b_title);
573 task_manager::browsertest_util::WaitForTaskManagerRows(
574 1, expected_url_a_cached_subframe_c_title);
575 EXPECT_THAT(tester->GetWebContentsTaskTitles(),
576 ::testing::ElementsAre(expected_url_b_active_title,
577 expected_url_a_cached_title,
578 expected_url_a_cached_subframe_b_title,
579 expected_url_a_cached_subframe_c_title));
582 // Ensure that BackForwardCache same-site subframes are not shown in the Task
584 IN_PROC_BROWSER_TEST_F(ChromeBackForwardCacheBrowserTest,
585 DoNotShowSameSiteSubframeInTaskManager) {
586 ASSERT_TRUE(embedded_test_server()->Start());
588 // Load a page on a.com with an a.com iframe.
589 GURL url_a(embedded_test_server()->GetURL("a.com", "/iframe.html"));
590 const std::u16string expected_url_a_cached_title = l10n_util::GetStringFUTF16(
591 IDS_TASK_MANAGER_BACK_FORWARD_CACHE_PREFIX, u"http://a.com/");
593 GURL url_b(embedded_test_server()->GetURL("b.com", "/title3.html"));
594 const std::u16string expected_url_b_active_title = l10n_util::GetStringFUTF16(
595 IDS_TASK_MANAGER_TAB_PREFIX, u"Title Of More Awesomeness");
598 task_manager::TaskManagerTester::Create(base::RepeatingClosure());
600 // 1) Navigate to |url_a|.
601 EXPECT_TRUE(content::NavigateToURL(web_contents(), url_a));
602 content::RenderFrameHostWrapper rfh_a(current_frame_host());
604 // 2) Navigate to |url_b|.
605 EXPECT_TRUE(content::NavigateToURL(web_contents(), url_b));
607 // 3) Verify |url_a| is in the BackForwardCache.
608 EXPECT_EQ(rfh_a->GetLifecycleState(),
609 content::RenderFrameHost::LifecycleState::kInBackForwardCache);
611 // 4) Ensure that only one task for |url_a| shows up in Task Manager.
612 task_manager::browsertest_util::WaitForTaskManagerRows(
613 1, expected_url_b_active_title);
614 task_manager::browsertest_util::WaitForTaskManagerRows(
615 1, expected_url_a_cached_title);
616 EXPECT_THAT(tester->GetWebContentsTaskTitles(),
617 ::testing::ElementsAre(expected_url_b_active_title,
618 expected_url_a_cached_title));
621 class ChromeBackForwardCacheBrowserWithEmbedTest
622 : public ChromeBackForwardCacheBrowserTest,
623 public ::testing::WithParamInterface<std::string> {
625 ChromeBackForwardCacheBrowserWithEmbedTest() = default;
626 ~ChromeBackForwardCacheBrowserWithEmbedTest() override = default;
628 static std::string GetSrcAttributeForTag(const std::string& tag) {
629 return tag == "embed" ? "src" : "data";
632 void SetUpOnMainThread() override {
633 ChromeBackForwardCacheBrowserTest::SetUpOnMainThread();
634 ASSERT_TRUE(embedded_test_server()->Start());
638 void ExpectBlocklistedFeature(
639 blink::scheduler::WebSchedulerTrackedFeature feature,
640 base::Location location) {
641 content::FetchHistogramsFromChildProcesses();
642 base::HistogramBase::Sample sample = base::HistogramBase::Sample(feature);
643 base::Bucket expected_blocklisted(sample, 1);
645 EXPECT_THAT(histogram_tester_->GetAllSamples(
646 "BackForwardCache.HistoryNavigationOutcome."
647 "BlocklistedFeature"),
648 testing::Contains(expected_blocklisted))
649 << location.ToString();
651 EXPECT_THAT(histogram_tester_->GetAllSamples(
652 "BackForwardCache.AllSites.HistoryNavigationOutcome."
653 "BlocklistedFeature"),
654 testing::Contains(expected_blocklisted))
655 << location.ToString();
658 void ExpectNotRestoredReasonHaveInnerContents(base::Location location) {
659 // BackForwardCacheMetrics::NotRestoredReason::kHaveInnerContents
661 content::FetchHistogramsFromChildProcesses();
662 base::HistogramBase::Sample sample = base::HistogramBase::Sample(reason);
663 base::Bucket expected_not_restored(sample, 1);
665 EXPECT_THAT(histogram_tester_->GetAllSamples(
666 "BackForwardCache.HistoryNavigationOutcome."
667 "NotRestoredReason"),
668 testing::Contains(expected_not_restored))
669 << location.ToString();
671 EXPECT_THAT(histogram_tester_->GetAllSamples(
672 "BackForwardCache.AllSites.HistoryNavigationOutcome."
673 "NotRestoredReason"),
674 testing::Contains(expected_not_restored))
675 << location.ToString();
678 INSTANTIATE_TEST_SUITE_P(
680 ChromeBackForwardCacheBrowserWithEmbedTest,
681 testing::ValuesIn<std::vector<std::string>>({"embed", "object"}));
683 // TODO(crbug.com/1491942): This fails with the field trial testing config.
684 class ChromeBackForwardCacheBrowserWithEmbedTestNoTestingConfig
685 : public ChromeBackForwardCacheBrowserWithEmbedTest {
687 void SetUpCommandLine(base::CommandLine* command_line) override {
688 ChromeBackForwardCacheBrowserWithEmbedTest::SetUpCommandLine(command_line);
689 command_line->AppendSwitch("disable-field-trial-config");
692 INSTANTIATE_TEST_SUITE_P(
694 ChromeBackForwardCacheBrowserWithEmbedTestNoTestingConfig,
695 testing::ValuesIn<std::vector<std::string>>({"embed", "object"}));
697 IN_PROC_BROWSER_TEST_P(
698 ChromeBackForwardCacheBrowserWithEmbedTestNoTestingConfig,
699 DoesNotCachePageWithEmbeddedPlugin) {
700 const auto tag = GetParam();
701 const auto page_with_plugin = base::StringPrintf(
702 "/back_forward_cache/page_with_%s_plugin.html", tag.c_str());
704 // Navigate to A, a page with embedded Pepper plugin.
705 ASSERT_TRUE(content::NavigateToURL(
707 embedded_test_server()->GetURL("a.com", page_with_plugin)));
708 content::RenderFrameHostWrapper rfh_a(current_frame_host());
711 bool will_change_rfh =
712 rfh_a->ShouldChangeRenderFrameHostOnSameSiteNavigation();
714 ASSERT_TRUE(content::NavigateToURL(
715 web_contents(), embedded_test_server()->GetURL("a.com", "/title2.html")));
717 // Verify A is NOT stored in the BackForwardCache.
718 if (will_change_rfh) {
719 EXPECT_TRUE(rfh_a.WaitUntilRenderFrameDeleted());
721 EXPECT_NE(rfh_a->GetLifecycleState(),
722 content::RenderFrameHost::LifecycleState::kInBackForwardCache);
725 // Navigate back to A.
726 ASSERT_TRUE(content::HistoryGoBack(web_contents()));
727 // Verify A is not restored from BackForwardCache due to |kContainsPlugins|.
728 ExpectBlocklistedFeature(
729 blink::scheduler::WebSchedulerTrackedFeature::kContainsPlugins,
733 #if BUILDFLAG(ENABLE_PDF)
734 IN_PROC_BROWSER_TEST_P(
735 ChromeBackForwardCacheBrowserWithEmbedTestNoTestingConfig,
736 DoesNotCachePageWithEmbeddedPdf) {
737 const auto tag = GetParam();
738 const auto page_with_pdf = base::StringPrintf(
739 "/back_forward_cache/page_with_%s_pdf.html", tag.c_str());
741 // Navigate to A, a page with embedded PDF.
742 ASSERT_TRUE(content::NavigateToURL(
743 web_contents(), embedded_test_server()->GetURL("a.com", page_with_pdf)));
744 ASSERT_TRUE(pdf_extension_test_util::EnsurePDFHasLoaded(
745 web_contents(), /*wait_for_hit_test_data=*/true, tag));
746 content::RenderFrameHostWrapper rfh_a(current_frame_host());
749 bool will_change_rfh =
750 rfh_a->ShouldChangeRenderFrameHostOnSameSiteNavigation();
752 ASSERT_TRUE(content::NavigateToURL(
753 web_contents(), embedded_test_server()->GetURL("a.com", "/title2.html")));
755 // Verify A is NOT stored in the BackForwardCache.
756 if (will_change_rfh) {
757 EXPECT_TRUE(rfh_a.WaitUntilRenderFrameDeleted());
759 EXPECT_NE(rfh_a->GetLifecycleState(),
760 content::RenderFrameHost::LifecycleState::kInBackForwardCache);
763 // Navigate back to A.
764 ASSERT_TRUE(content::HistoryGoBack(web_contents()));
765 // Verify A is not restored from BackForwardCache. Loading PDF plugins
766 // in chrome actually creates a nested WebContents which takes precedent over
767 // the blocklisted feature kContainsPlugins.
768 ExpectNotRestoredReasonHaveInnerContents(FROM_HERE);
770 #endif // BUILDFLAG(ENABLE_PDF)
772 // Flaky on Mac: crbug.com/1492026
773 #if BUILDFLAG(IS_MAC)
774 #define MAYBE_DoesNotCachePageWithEmbeddedPdfAppendedOnPageLoaded DISABLED_DoesNotCachePageWithEmbeddedPdfAppendedOnPageLoaded
776 #define MAYBE_DoesNotCachePageWithEmbeddedPdfAppendedOnPageLoaded DoesNotCachePageWithEmbeddedPdfAppendedOnPageLoaded
778 IN_PROC_BROWSER_TEST_P(ChromeBackForwardCacheBrowserWithEmbedTest,
779 MAYBE_DoesNotCachePageWithEmbeddedPdfAppendedOnPageLoaded) {
780 const auto tag = GetParam();
783 ASSERT_TRUE(content::NavigateToURL(
784 web_contents(), embedded_test_server()->GetURL("a.com", "/title1.html")));
785 content::RenderFrameHostWrapper rfh_a(current_frame_host());
786 // Embed a PDF into A, and wait until PDF is loaded.
787 ASSERT_TRUE(content::ExecJs(
788 rfh_a.get(), content::JsReplace(R"(
789 new Promise(async resolve => {
790 let el = document.createElement($1);
791 el.type = 'application/pdf';
792 el[$2] = '/pdf/test.pdf';
793 el.onload = e => resolve();
794 document.body.append(el);
797 tag, GetSrcAttributeForTag(tag))));
800 ASSERT_TRUE(content::NavigateToURL(
801 web_contents(), embedded_test_server()->GetURL("a.com", "/title2.html")));
803 // Verify A is NOT stored in the BackForwardCache.
804 if (content::WillSameSiteNavigationChangeRenderFrameHosts(
805 /*is_main_frame=*/true)) {
806 EXPECT_TRUE(rfh_a.WaitUntilRenderFrameDeleted());
808 EXPECT_NE(rfh_a->GetLifecycleState(),
809 content::RenderFrameHost::LifecycleState::kInBackForwardCache);
812 // Navigate back to A.
813 ASSERT_TRUE(content::HistoryGoBack(web_contents()));
814 // Verify A is not restored from BackForwardCache. Loading PDF plugins
815 // in chrome actually creates a nested WebContents which takes precedent over
816 // the blocklisted feature kContainsPlugins.
817 ExpectNotRestoredReasonHaveInnerContents(FROM_HERE);
820 IN_PROC_BROWSER_TEST_P(ChromeBackForwardCacheBrowserWithEmbedTest,
821 DoesCachePageWithEmbeddedHtml) {
822 const auto tag = GetParam();
823 const auto page_with_html = base::StringPrintf(
824 "/back_forward_cache/page_with_%s_html.html", tag.c_str());
826 // Navigate to A, a page with embedded HTML.
827 ASSERT_TRUE(content::NavigateToURL(
828 web_contents(), embedded_test_server()->GetURL("a.com", page_with_html)));
829 content::RenderFrameHostWrapper rfh_a(current_frame_host());
832 ASSERT_TRUE(content::NavigateToURL(
833 web_contents(), embedded_test_server()->GetURL("a.com", "/title2.html")));
835 // Verify A is stored in the BackForwardCache.
836 EXPECT_EQ(rfh_a->GetLifecycleState(),
837 content::RenderFrameHost::LifecycleState::kInBackForwardCache);
840 // Flaky on Mac and Linux: crbug.com/1492026
841 #if (BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX))
842 #define MAYBE_DoesNotCachePageWithEmbeddedHtmlMutatedIntoPdf DISABLED_DoesNotCachePageWithEmbeddedHtmlMutatedIntoPdf
844 #define MAYBE_DoesNotCachePageWithEmbeddedHtmlMutatedIntoPdf DoesNotCachePageWithEmbeddedHtmlMutatedIntoPdf
846 IN_PROC_BROWSER_TEST_P(ChromeBackForwardCacheBrowserWithEmbedTest,
847 MAYBE_DoesNotCachePageWithEmbeddedHtmlMutatedIntoPdf) {
848 const auto tag = GetParam();
849 const auto page_with_html = base::StringPrintf(
850 "/back_forward_cache/page_with_%s_html.html", tag.c_str());
852 // Navigate to A, a page with embedded HTML.
853 ASSERT_TRUE(content::NavigateToURL(
854 web_contents(), embedded_test_server()->GetURL("a.com", page_with_html)));
855 content::RenderFrameHostWrapper rfh_a(current_frame_host());
856 // Mutate the embed into PDF, and wait until PDF is loaded.
857 ASSERT_TRUE(content::ExecJs(
858 rfh_a.get(), content::JsReplace(R"(
859 new Promise(async resolve => {
860 let el = document.getElementById($1);
861 el.type = 'application/pdf';
862 el[$2] = '/pdf/test.pdf';
863 el.onload = e => resolve();
866 tag, GetSrcAttributeForTag(tag))));
868 bool will_change_rfh =
869 rfh_a->ShouldChangeRenderFrameHostOnSameSiteNavigation();
871 ASSERT_TRUE(content::NavigateToURL(
872 web_contents(), embedded_test_server()->GetURL("a.com", "/title2.html")));
874 // Verify A is NOT stored in the BackForwardCache.
875 if (will_change_rfh) {
876 EXPECT_TRUE(rfh_a.WaitUntilRenderFrameDeleted());
878 EXPECT_NE(rfh_a->GetLifecycleState(),
879 content::RenderFrameHost::LifecycleState::kInBackForwardCache);
882 // Navigate back to A.
883 ASSERT_TRUE(content::HistoryGoBack(web_contents()));
884 // Verify A is not restored from BackForwardCache. Loading PDF plugins
885 // in chrome actually creates a nested WebContents which takes precedent over
886 // the blocklisted feature kContainsPlugins.
887 ExpectNotRestoredReasonHaveInnerContents(FROM_HERE);
890 IN_PROC_BROWSER_TEST_P(ChromeBackForwardCacheBrowserWithEmbedTest,
891 DoesCachePageWithEmbeddedPdfMutatedIntoHtml) {
892 const auto tag = GetParam();
893 const auto page_with_pdf = base::StringPrintf(
894 "/back_forward_cache/page_with_%s_pdf.html", tag.c_str());
896 // Navigate to A, a page with embedded PDF.
897 ASSERT_TRUE(content::NavigateToURL(
898 web_contents(), embedded_test_server()->GetURL("a.com", page_with_pdf)));
899 content::RenderFrameHostWrapper rfh_a(current_frame_host());
900 // Mutate the embed into HTML, and wait until HTML is loaded.
901 ASSERT_TRUE(content::ExecJs(
902 rfh_a.get(), content::JsReplace(R"(
903 new Promise(async resolve => {
904 let el = document.getElementById($1);
905 el.type = 'text/html';
906 el[$2] = '/title1.html';
907 el.onload = e => resolve();
910 tag, GetSrcAttributeForTag(tag))));
913 ASSERT_TRUE(content::NavigateToURL(
914 web_contents(), embedded_test_server()->GetURL("a.com", "/title2.html")));
916 // Verify A is stored in the BackForwardCache.
917 EXPECT_EQ(rfh_a->GetLifecycleState(),
918 content::RenderFrameHost::LifecycleState::kInBackForwardCache);