| // Copyright 2018 The Chromium Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include "base/command_line.h" | 
 | #include "base/test/scoped_feature_list.h" | 
 | #include "content/browser/frame_host/back_forward_cache.h" | 
 | #include "content/browser/frame_host/frame_tree_node.h" | 
 | #include "content/browser/web_contents/web_contents_impl.h" | 
 | #include "content/public/browser/site_isolation_policy.h" | 
 | #include "content/public/common/content_features.h" | 
 | #include "content/public/common/content_switches.h" | 
 | #include "content/public/test/browser_test_utils.h" | 
 | #include "content/public/test/content_browser_test.h" | 
 | #include "content/public/test/content_browser_test_utils.h" | 
 | #include "content/public/test/navigation_handle_observer.h" | 
 | #include "content/public/test/test_utils.h" | 
 | #include "content/public/test/url_loader_interceptor.h" | 
 | #include "content/shell/browser/shell.h" | 
 | #include "content/test/content_browser_test_utils_internal.h" | 
 | #include "net/dns/mock_host_resolver.h" | 
 | #include "net/test/embedded_test_server/controllable_http_response.h" | 
 | #include "net/test/embedded_test_server/embedded_test_server.h" | 
 | #include "testing/gmock/include/gmock/gmock.h" | 
 | #include "third_party/blink/public/common/scheduler/web_scheduler_tracked_feature.h" | 
 |  | 
 | using testing::Each; | 
 | using testing::Not; | 
 |  | 
 | namespace content { | 
 |  | 
 | namespace { | 
 |  | 
 | // Test about the BackForwardCache. | 
 | class BackForwardCacheBrowserTest : public ContentBrowserTest { | 
 |  protected: | 
 |   void SetUpCommandLine(base::CommandLine* command_line) override { | 
 |     base::CommandLine::ForCurrentProcess()->AppendSwitch( | 
 |         switches::kUseFakeUIForMediaStream); | 
 |     feature_list_.InitAndEnableFeature(features::kBackForwardCache); | 
 |     ContentBrowserTest::SetUpCommandLine(command_line); | 
 |   } | 
 |  | 
 |   void SetUpOnMainThread() override { | 
 |     host_resolver()->AddRule("*", "127.0.0.1"); | 
 |     ContentBrowserTest::SetUpOnMainThread(); | 
 |   } | 
 |  | 
 |   WebContentsImpl* web_contents() const { | 
 |     return static_cast<WebContentsImpl*>(shell()->web_contents()); | 
 |   } | 
 |  | 
 |   RenderFrameHostImpl* current_frame_host() { | 
 |     return web_contents()->GetFrameTree()->root()->current_frame_host(); | 
 |   } | 
 |  | 
 |  private: | 
 |   base::test::ScopedFeatureList feature_list_; | 
 | }; | 
 |  | 
 | // Match RenderFrameHostImpl* that are in the BackForwardCache. | 
 | MATCHER(InBackForwardCache, "") { | 
 |   return arg->is_in_back_forward_cache(); | 
 | } | 
 |  | 
 | // Match RenderFrameDeleteObserver* which observed deletion of the RenderFrame. | 
 | MATCHER(Deleted, "") { | 
 |   return arg->deleted(); | 
 | } | 
 |  | 
 | // Helper function to pass an initializer list to the EXPECT_THAT macro. This is | 
 | // indeed the identity function. | 
 | std::initializer_list<RenderFrameHostImpl*> Elements( | 
 |     std::initializer_list<RenderFrameHostImpl*> t) { | 
 |   return t; | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 | // Navigate from A to B and go back. | 
 | IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, Basic) { | 
 |   ASSERT_TRUE(embedded_test_server()->Start()); | 
 |   const GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html")); | 
 |   const GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html")); | 
 |   const url::Origin origin_a = url::Origin::Create(url_a); | 
 |   const url::Origin origin_b = url::Origin::Create(url_b); | 
 |  | 
 |   // 1) Navigate to A. | 
 |   EXPECT_TRUE(NavigateToURL(shell(), url_a)); | 
 |   RenderFrameHostImpl* rfh_a = current_frame_host(); | 
 |   RenderFrameDeletedObserver delete_observer_rfh_a(rfh_a); | 
 |  | 
 |   // 2) Navigate to B. | 
 |   EXPECT_TRUE(NavigateToURL(shell(), url_b)); | 
 |   RenderFrameHostImpl* rfh_b = current_frame_host(); | 
 |   RenderFrameDeletedObserver delete_observer_rfh_b(rfh_b); | 
 |   EXPECT_FALSE(delete_observer_rfh_a.deleted()); | 
 |   EXPECT_TRUE(rfh_a->is_in_back_forward_cache()); | 
 |   EXPECT_EQ(rfh_a->GetVisibilityState(), PageVisibilityState::kHidden); | 
 |   EXPECT_EQ(origin_a, rfh_a->GetLastCommittedOrigin()); | 
 |   EXPECT_EQ(origin_b, rfh_b->GetLastCommittedOrigin()); | 
 |   EXPECT_FALSE(rfh_b->is_in_back_forward_cache()); | 
 |   EXPECT_EQ(rfh_b->GetVisibilityState(), PageVisibilityState::kVisible); | 
 |  | 
 |   // 3) Go back to A. | 
 |   web_contents()->GetController().GoBack(); | 
 |   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents())); | 
 |   EXPECT_FALSE(delete_observer_rfh_a.deleted()); | 
 |   EXPECT_FALSE(delete_observer_rfh_b.deleted()); | 
 |   EXPECT_EQ(origin_a, rfh_a->GetLastCommittedOrigin()); | 
 |   EXPECT_EQ(origin_b, rfh_b->GetLastCommittedOrigin()); | 
 |   EXPECT_EQ(rfh_a, current_frame_host()); | 
 |   EXPECT_FALSE(rfh_a->is_in_back_forward_cache()); | 
 |   EXPECT_EQ(rfh_a->GetVisibilityState(), PageVisibilityState::kVisible); | 
 |   EXPECT_TRUE(rfh_b->is_in_back_forward_cache()); | 
 |   EXPECT_EQ(rfh_b->GetVisibilityState(), PageVisibilityState::kHidden); | 
 | } | 
 |  | 
 | // Navigate from A to B and go back. | 
 | IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, BasicDocumentInitiated) { | 
 |   ASSERT_TRUE(embedded_test_server()->Start()); | 
 |   const GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html")); | 
 |   const GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html")); | 
 |  | 
 |   // 1) Navigate to A. | 
 |   EXPECT_TRUE(NavigateToURL(shell(), url_a)); | 
 |   RenderFrameHostImpl* rfh_a = current_frame_host(); | 
 |   RenderFrameDeletedObserver delete_observer_rfh_a(rfh_a); | 
 |  | 
 |   // 2) Navigate to B. | 
 |   EXPECT_TRUE(ExecJs(shell(), JsReplace("location = $1;", url_b.spec()))); | 
 |   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents())); | 
 |   RenderFrameHostImpl* rfh_b = current_frame_host(); | 
 |   RenderFrameDeletedObserver delete_observer_rfh_b(rfh_b); | 
 |   EXPECT_FALSE(delete_observer_rfh_a.deleted()); | 
 |   EXPECT_TRUE(rfh_a->is_in_back_forward_cache()); | 
 |   EXPECT_FALSE(rfh_b->is_in_back_forward_cache()); | 
 |  | 
 |   // The two pages are using different BrowsingInstances. | 
 |   EXPECT_FALSE(rfh_a->GetSiteInstance()->IsRelatedSiteInstance( | 
 |       rfh_b->GetSiteInstance())); | 
 |  | 
 |   // 3) Go back to A. | 
 |   EXPECT_TRUE(ExecJs(shell(), "history.back();")); | 
 |   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents())); | 
 |   EXPECT_FALSE(delete_observer_rfh_a.deleted()); | 
 |   EXPECT_FALSE(delete_observer_rfh_b.deleted()); | 
 |   EXPECT_EQ(rfh_a, current_frame_host()); | 
 |   EXPECT_FALSE(rfh_a->is_in_back_forward_cache()); | 
 |   EXPECT_TRUE(rfh_b->is_in_back_forward_cache()); | 
 | } | 
 |  | 
 | // Navigate from back and forward repeatedly. | 
 | IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, | 
 |                        NavigateBackForwardRepeatedly) { | 
 |   ASSERT_TRUE(embedded_test_server()->Start()); | 
 |   const GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html")); | 
 |   const GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html")); | 
 |  | 
 |   // 1) Navigate to A. | 
 |   EXPECT_TRUE(NavigateToURL(shell(), url_a)); | 
 |   RenderFrameHostImpl* rfh_a = current_frame_host(); | 
 |   RenderFrameDeletedObserver delete_observer_rfh_a(rfh_a); | 
 |  | 
 |   // 2) Navigate to B. | 
 |   EXPECT_TRUE(NavigateToURL(shell(), url_b)); | 
 |   RenderFrameHostImpl* rfh_b = current_frame_host(); | 
 |   RenderFrameDeletedObserver delete_observer_rfh_b(rfh_b); | 
 |   EXPECT_TRUE(rfh_a->is_in_back_forward_cache()); | 
 |   EXPECT_FALSE(rfh_b->is_in_back_forward_cache()); | 
 |  | 
 |   // 3) Go back to A. | 
 |   web_contents()->GetController().GoBack(); | 
 |   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents())); | 
 |  | 
 |   EXPECT_EQ(rfh_a, current_frame_host()); | 
 |   EXPECT_FALSE(rfh_a->is_in_back_forward_cache()); | 
 |   EXPECT_TRUE(rfh_b->is_in_back_forward_cache()); | 
 |  | 
 |   // 4) Go forward to B. | 
 |   web_contents()->GetController().GoForward(); | 
 |   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents())); | 
 |  | 
 |   EXPECT_EQ(rfh_b, current_frame_host()); | 
 |   EXPECT_TRUE(rfh_a->is_in_back_forward_cache()); | 
 |   EXPECT_FALSE(rfh_b->is_in_back_forward_cache()); | 
 |  | 
 |   // 5) Go back to A. | 
 |   web_contents()->GetController().GoBack(); | 
 |   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents())); | 
 |  | 
 |   EXPECT_EQ(rfh_a, current_frame_host()); | 
 |   EXPECT_FALSE(rfh_a->is_in_back_forward_cache()); | 
 |   EXPECT_TRUE(rfh_b->is_in_back_forward_cache()); | 
 |  | 
 |   // 6) Go forward to B. | 
 |   web_contents()->GetController().GoForward(); | 
 |   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents())); | 
 |  | 
 |   EXPECT_EQ(rfh_b, current_frame_host()); | 
 |   EXPECT_TRUE(rfh_a->is_in_back_forward_cache()); | 
 |   EXPECT_FALSE(rfh_b->is_in_back_forward_cache()); | 
 |  | 
 |   EXPECT_FALSE(delete_observer_rfh_a.deleted()); | 
 |   EXPECT_FALSE(delete_observer_rfh_b.deleted()); | 
 | } | 
 |  | 
 | // The current page can't enter the BackForwardCache if another page can script | 
 | // it. This can happen when one document opens a popup using window.open() for | 
 | // instance. It prevents the BackForwardCache from being used. | 
 | IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, WindowOpen) { | 
 |   // This test assumes cross-site navigation staying in the same | 
 |   // BrowsingInstance to use a different SiteInstance. Otherwise, it will | 
 |   // timeout at step 2). | 
 |   if (!SiteIsolationPolicy::UseDedicatedProcessesForAllSites()) | 
 |     return; | 
 |  | 
 |   ASSERT_TRUE(embedded_test_server()->Start()); | 
 |   const GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html")); | 
 |   const GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html")); | 
 |  | 
 |   // 1) Navigate to A and open a popup. | 
 |   EXPECT_TRUE(NavigateToURL(shell(), url_a)); | 
 |   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents())); | 
 |   RenderFrameHostImpl* rfh_a = current_frame_host(); | 
 |   RenderFrameDeletedObserver delete_observer_rfh_a(rfh_a); | 
 |   EXPECT_EQ(1u, rfh_a->GetSiteInstance()->GetRelatedActiveContentsCount()); | 
 |   Shell* popup = OpenPopup(rfh_a, url_a, ""); | 
 |   EXPECT_EQ(2u, rfh_a->GetSiteInstance()->GetRelatedActiveContentsCount()); | 
 |  | 
 |   // 2) Navigate to B. The previous document can't enter the BackForwardCache, | 
 |   // because of the popup. | 
 |   EXPECT_TRUE(ExecJs(rfh_a, JsReplace("location = $1;", url_b.spec()))); | 
 |   delete_observer_rfh_a.WaitUntilDeleted(); | 
 |   RenderFrameHostImpl* rfh_b = current_frame_host(); | 
 |   EXPECT_EQ(2u, rfh_b->GetSiteInstance()->GetRelatedActiveContentsCount()); | 
 |  | 
 |   // 3) Go back to A. The previous document can't enter the BackForwardCache, | 
 |   // because of the popup. | 
 |   RenderFrameDeletedObserver delete_observer_rfh_b(rfh_b); | 
 |   EXPECT_TRUE(ExecJs(rfh_b, "history.back();")); | 
 |   delete_observer_rfh_b.WaitUntilDeleted(); | 
 |  | 
 |   // 4) Make the popup drop the window.opener connection. It happens when the | 
 |   //    user does an omnibox-initiated navigation, which happens in a new | 
 |   //    BrowsingInstance. | 
 |   RenderFrameHostImpl* rfh_a_new = current_frame_host(); | 
 |   EXPECT_EQ(2u, rfh_a_new->GetSiteInstance()->GetRelatedActiveContentsCount()); | 
 |   EXPECT_TRUE(NavigateToURL(popup, url_b)); | 
 |   EXPECT_EQ(1u, rfh_a_new->GetSiteInstance()->GetRelatedActiveContentsCount()); | 
 |  | 
 |   // 5) Navigate to B again. In theory, the current document should be able to | 
 |   // enter the BackForwardCache. It can't, because the RenderFrameHost still | 
 |   // "remembers" it had access to the popup. See | 
 |   // RenderFrameHostImpl::scheduler_tracked_features(). | 
 |   RenderFrameDeletedObserver delete_observer_rfh_a_new(rfh_a_new); | 
 |   EXPECT_TRUE(ExecJs(rfh_a_new, JsReplace("location = $1;", url_b.spec()))); | 
 |   EXPECT_TRUE(WaitForLoadStop(web_contents())); | 
 |   delete_observer_rfh_a_new.WaitUntilDeleted(); | 
 |  | 
 |   // 6) Go back to A. The current document can finally enter the | 
 |   // BackForwardCache, because it is alone in its BrowsingInstance and has never | 
 |   // been related to any other document. | 
 |   RenderFrameHostImpl* rfh_b_new = current_frame_host(); | 
 |   RenderFrameDeletedObserver delete_observer_rfh_b_new(rfh_b_new); | 
 |   EXPECT_TRUE(ExecJs(rfh_b_new, "history.back();")); | 
 |   EXPECT_TRUE(WaitForLoadStop(web_contents())); | 
 |   EXPECT_FALSE(delete_observer_rfh_b_new.deleted()); | 
 |   EXPECT_TRUE(rfh_b_new->is_in_back_forward_cache()); | 
 | } | 
 |  | 
 | // Navigate from A(B) to C and go back. | 
 | IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, BasicIframe) { | 
 |   ASSERT_TRUE(embedded_test_server()->Start()); | 
 |   const GURL url_a(embedded_test_server()->GetURL( | 
 |       "a.com", "/cross_site_iframe_factory.html?a(b)")); | 
 |   const GURL url_c(embedded_test_server()->GetURL("c.com", "/title1.html")); | 
 |  | 
 |   // 1) Navigate to A(B). | 
 |   EXPECT_TRUE(NavigateToURL(shell(), url_a)); | 
 |   RenderFrameHostImpl* rfh_a = current_frame_host(); | 
 |   RenderFrameHostImpl* rfh_b = rfh_a->child_at(0)->current_frame_host(); | 
 |   RenderFrameDeletedObserver delete_observer_rfh_a(rfh_a); | 
 |   RenderFrameDeletedObserver delete_observer_rfh_b(rfh_b); | 
 |  | 
 |   // 2) Navigate to C. | 
 |   EXPECT_TRUE(NavigateToURL(shell(), url_c)); | 
 |   RenderFrameHostImpl* rfh_c = current_frame_host(); | 
 |   RenderFrameDeletedObserver delete_observer_rfh_c(rfh_c); | 
 |   EXPECT_FALSE(delete_observer_rfh_a.deleted()); | 
 |   EXPECT_FALSE(delete_observer_rfh_b.deleted()); | 
 |   EXPECT_TRUE(rfh_a->is_in_back_forward_cache()); | 
 |   EXPECT_TRUE(rfh_b->is_in_back_forward_cache()); | 
 |   EXPECT_FALSE(rfh_c->is_in_back_forward_cache()); | 
 |  | 
 |   // 3) Go back to A(B). | 
 |   web_contents()->GetController().GoBack(); | 
 |   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents())); | 
 |   EXPECT_FALSE(delete_observer_rfh_a.deleted()); | 
 |   EXPECT_FALSE(delete_observer_rfh_b.deleted()); | 
 |   EXPECT_FALSE(delete_observer_rfh_c.deleted()); | 
 |   EXPECT_EQ(rfh_a, current_frame_host()); | 
 |   EXPECT_FALSE(rfh_a->is_in_back_forward_cache()); | 
 |   EXPECT_FALSE(rfh_b->is_in_back_forward_cache()); | 
 |   EXPECT_TRUE(rfh_c->is_in_back_forward_cache()); | 
 | } | 
 |  | 
 | // Ensure flushing the BackForwardCache works properly. | 
 | IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, BackForwardCacheFlush) { | 
 |   ASSERT_TRUE(embedded_test_server()->Start()); | 
 |   const GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html")); | 
 |   const GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html")); | 
 |  | 
 |   // 1) Navigate to A. | 
 |   EXPECT_TRUE(NavigateToURL(shell(), url_a)); | 
 |   RenderFrameHostImpl* rfh_a = current_frame_host(); | 
 |   RenderFrameDeletedObserver delete_observer_rfh_a(rfh_a); | 
 |  | 
 |   // 2) Navigate to B. | 
 |   EXPECT_TRUE(NavigateToURL(shell(), url_b)); | 
 |   RenderFrameHostImpl* rfh_b = current_frame_host(); | 
 |   RenderFrameDeletedObserver delete_observer_rfh_b(rfh_b); | 
 |   EXPECT_FALSE(delete_observer_rfh_a.deleted()); | 
 |  | 
 |   // 3) Flush A. | 
 |   web_contents()->GetController().back_forward_cache().Flush(); | 
 |   EXPECT_TRUE(delete_observer_rfh_a.deleted()); | 
 |   EXPECT_FALSE(delete_observer_rfh_b.deleted()); | 
 |  | 
 |   // 4) Go back to a new A. | 
 |   web_contents()->GetController().GoBack(); | 
 |   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents())); | 
 |   EXPECT_TRUE(delete_observer_rfh_a.deleted()); | 
 |   EXPECT_FALSE(delete_observer_rfh_b.deleted()); | 
 |  | 
 |   // 5) Flush B. | 
 |   web_contents()->GetController().back_forward_cache().Flush(); | 
 |   EXPECT_TRUE(delete_observer_rfh_b.deleted()); | 
 | } | 
 |  | 
 | // Check the visible URL in the omnibox is properly updated when restoring a | 
 | // document from the BackForwardCache. | 
 | IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, VisibleURL) { | 
 |   ASSERT_TRUE(embedded_test_server()->Start()); | 
 |   const GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html")); | 
 |   const GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html")); | 
 |  | 
 |   // 1) Go to A. | 
 |   EXPECT_TRUE(NavigateToURL(shell(), url_a)); | 
 |  | 
 |   // 2) Go to B. | 
 |   EXPECT_TRUE(NavigateToURL(shell(), url_b)); | 
 |  | 
 |   // 3) Go back to A. | 
 |   web_contents()->GetController().GoBack(); | 
 |   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents())); | 
 |   EXPECT_EQ(url_a, web_contents()->GetVisibleURL()); | 
 |  | 
 |   // 4) Go forward to B. | 
 |   web_contents()->GetController().GoForward(); | 
 |   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents())); | 
 |   EXPECT_EQ(url_b, web_contents()->GetVisibleURL()); | 
 | } | 
 |  | 
 | // Test documents are evicted from the BackForwardCache at some point. | 
 | IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, CacheEviction) { | 
 |   ASSERT_TRUE(embedded_test_server()->Start()); | 
 |   const GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html")); | 
 |   const GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html")); | 
 |  | 
 |   EXPECT_TRUE(NavigateToURL(shell(), url_a));  // BackForwardCache size is 0. | 
 |   RenderFrameHostImpl* rfh_a = current_frame_host(); | 
 |   RenderFrameDeletedObserver delete_observer_rfh_a(rfh_a); | 
 |  | 
 |   EXPECT_TRUE(NavigateToURL(shell(), url_b));  // BackForwardCache size is 1. | 
 |   RenderFrameHostImpl* rfh_b = current_frame_host(); | 
 |   RenderFrameDeletedObserver delete_observer_rfh_b(rfh_b); | 
 |  | 
 |   // The number of document the BackForwardCache can hold per tab. | 
 |   static constexpr size_t kBackForwardCacheLimit = 3; | 
 |  | 
 |   for (size_t i = 2; i < kBackForwardCacheLimit; ++i) { | 
 |     EXPECT_TRUE(NavigateToURL(shell(), i % 2 ? url_b : url_a)); | 
 |     // After |i+1| navigations, |i| documents went into the BackForwardCache. | 
 |     // When |i| is greater than the BackForwardCache size limit, they are | 
 |     // evicted: | 
 |     EXPECT_EQ(i >= kBackForwardCacheLimit + 1, delete_observer_rfh_a.deleted()); | 
 |     EXPECT_EQ(i >= kBackForwardCacheLimit + 2, delete_observer_rfh_b.deleted()); | 
 |   } | 
 | } | 
 |  | 
 | // Similar to BackForwardCacheBrowserTest.SubframeSurviveCache* | 
 | // Test case: a1(b2) -> c3 -> a1(b2) | 
 | IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, SubframeSurviveCache1) { | 
 |   ASSERT_TRUE(embedded_test_server()->Start()); | 
 |   const GURL url_a(embedded_test_server()->GetURL( | 
 |       "a.com", "/cross_site_iframe_factory.html?a(b)")); | 
 |   const GURL url_c(embedded_test_server()->GetURL("c.com", "/title1.html")); | 
 |  | 
 |   std::vector<RenderFrameDeletedObserver*> rfh_observer; | 
 |  | 
 |   // 1) Navigate to a1(b2). | 
 |   EXPECT_TRUE(NavigateToURL(shell(), url_a)); | 
 |   RenderFrameHostImpl* a1 = current_frame_host(); | 
 |   RenderFrameHostImpl* b2 = a1->child_at(0)->current_frame_host(); | 
 |   RenderFrameDeletedObserver a1_observer(a1), b2_observer(b2); | 
 |   rfh_observer.insert(rfh_observer.end(), {&a1_observer, &b2_observer}); | 
 |   EXPECT_TRUE(ExecJs(b2, "window.alive = 'I am alive';")); | 
 |  | 
 |   // 2) Navigate to c3. | 
 |   EXPECT_TRUE(NavigateToURL(shell(), url_c)); | 
 |   RenderFrameHostImpl* c3 = current_frame_host(); | 
 |   RenderFrameDeletedObserver c3_observer(c3); | 
 |   rfh_observer.push_back(&c3_observer); | 
 |   ASSERT_THAT(rfh_observer, Each(Not(Deleted()))); | 
 |   EXPECT_THAT(Elements({a1, b2}), Each(InBackForwardCache())); | 
 |   EXPECT_THAT(c3, Not(InBackForwardCache())); | 
 |  | 
 |   // 3) Go back to a1(b2). | 
 |   web_contents()->GetController().GoBack(); | 
 |   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents())); | 
 |   ASSERT_THAT(rfh_observer, Each(Not(Deleted()))); | 
 |   EXPECT_THAT(Elements({a1, b2}), Each(Not(InBackForwardCache()))); | 
 |   EXPECT_THAT(c3, InBackForwardCache()); | 
 |  | 
 |   // Even after a new IPC round trip with the renderer, b2 must still be alive. | 
 |   EXPECT_EQ("I am alive", EvalJs(b2, "window.alive")); | 
 |   EXPECT_FALSE(b2_observer.deleted()); | 
 | } | 
 |  | 
 | // Similar to BackForwardCacheBrowserTest.SubframeSurviveCache* | 
 | // Test case: a1(b2) -> b3 -> a1(b2). | 
 | IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, SubframeSurviveCache2) { | 
 |   ASSERT_TRUE(embedded_test_server()->Start()); | 
 |   const GURL url_a(embedded_test_server()->GetURL( | 
 |       "a.com", "/cross_site_iframe_factory.html?a(b)")); | 
 |   const GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html")); | 
 |  | 
 |   std::vector<RenderFrameDeletedObserver*> rfh_observer; | 
 |  | 
 |   // 1) Navigate to a1(b2). | 
 |   EXPECT_TRUE(NavigateToURL(shell(), url_a)); | 
 |   RenderFrameHostImpl* a1 = current_frame_host(); | 
 |   RenderFrameHostImpl* b2 = a1->child_at(0)->current_frame_host(); | 
 |   RenderFrameDeletedObserver a1_observer(a1), b2_observer(b2); | 
 |   rfh_observer.insert(rfh_observer.end(), {&a1_observer, &b2_observer}); | 
 |   EXPECT_TRUE(ExecJs(b2, "window.alive = 'I am alive';")); | 
 |  | 
 |   // 2) Navigate to b3. | 
 |   EXPECT_TRUE(NavigateToURL(shell(), url_b)); | 
 |   RenderFrameHostImpl* b3 = current_frame_host(); | 
 |   RenderFrameDeletedObserver b3_observer(b3); | 
 |   rfh_observer.push_back(&b3_observer); | 
 |   ASSERT_THAT(rfh_observer, Each(Not(Deleted()))); | 
 |   EXPECT_THAT(Elements({a1, b2}), Each(InBackForwardCache())); | 
 |   EXPECT_THAT(b3, Not(InBackForwardCache())); | 
 |  | 
 |   // 3) Go back to a1(b2). | 
 |   web_contents()->GetController().GoBack(); | 
 |   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents())); | 
 |   ASSERT_THAT(rfh_observer, Each(Not(Deleted()))); | 
 |   EXPECT_EQ(a1, current_frame_host()); | 
 |   EXPECT_THAT(Elements({a1, b2}), Each(Not(InBackForwardCache()))); | 
 |   EXPECT_THAT(b3, InBackForwardCache()); | 
 |  | 
 |   // Even after a new IPC round trip with the renderer, b2 must still be alive. | 
 |   EXPECT_EQ("I am alive", EvalJs(b2, "window.alive")); | 
 |   EXPECT_FALSE(b2_observer.deleted()); | 
 | } | 
 |  | 
 | // Similar to BackForwardCacheBrowserTest.tSubframeSurviveCache* | 
 | // Test case: a1(b2) -> b3(a4) -> a1(b2) -> b3(a4) | 
 | IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, SubframeSurviveCache3) { | 
 |   ASSERT_TRUE(embedded_test_server()->Start()); | 
 |   const GURL url_a(embedded_test_server()->GetURL( | 
 |       "a.com", "/cross_site_iframe_factory.html?a(b)")); | 
 |   const GURL url_b(embedded_test_server()->GetURL( | 
 |       "b.com", "/cross_site_iframe_factory.html?b(a)")); | 
 |  | 
 |   std::vector<RenderFrameDeletedObserver*> rfh_observer; | 
 |  | 
 |   // 1) Navigate to a1(b2). | 
 |   EXPECT_TRUE(NavigateToURL(shell(), url_a)); | 
 |   RenderFrameHostImpl* a1 = current_frame_host(); | 
 |   RenderFrameHostImpl* b2 = a1->child_at(0)->current_frame_host(); | 
 |   RenderFrameDeletedObserver a1_observer(a1), b2_observer(b2); | 
 |   rfh_observer.insert(rfh_observer.end(), {&a1_observer, &b2_observer}); | 
 |   EXPECT_TRUE(ExecJs(b2, "window.alive = 'I am alive';")); | 
 |  | 
 |   // 2) Navigate to b3(a4) | 
 |   EXPECT_TRUE(NavigateToURL(shell(), url_b)); | 
 |   RenderFrameHostImpl* b3 = current_frame_host(); | 
 |   RenderFrameHostImpl* a4 = b3->child_at(0)->current_frame_host(); | 
 |   RenderFrameDeletedObserver b3_observer(b3), a4_observer(a4); | 
 |   rfh_observer.insert(rfh_observer.end(), {&b3_observer, &a4_observer}); | 
 |   ASSERT_THAT(rfh_observer, Each(Not(Deleted()))); | 
 |   EXPECT_THAT(Elements({a1, b2}), Each(InBackForwardCache())); | 
 |   EXPECT_THAT(Elements({b3, a4}), Each(Not(InBackForwardCache()))); | 
 |   EXPECT_TRUE(ExecJs(a4, "window.alive = 'I am alive';")); | 
 |  | 
 |   // 3) Go back to a1(b2). | 
 |   web_contents()->GetController().GoBack(); | 
 |   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents())); | 
 |   ASSERT_THAT(rfh_observer, Each(Not(Deleted()))); | 
 |   EXPECT_EQ(a1, current_frame_host()); | 
 |   EXPECT_THAT(Elements({a1, b2}), Each(Not(InBackForwardCache()))); | 
 |   EXPECT_THAT(Elements({b3, a4}), Each(InBackForwardCache())); | 
 |  | 
 |   // Even after a new IPC round trip with the renderer, b2 must still be alive. | 
 |   EXPECT_EQ("I am alive", EvalJs(b2, "window.alive")); | 
 |   EXPECT_FALSE(b2_observer.deleted()); | 
 |  | 
 |   // 4) Go forward to b3(a4). | 
 |   web_contents()->GetController().GoForward(); | 
 |   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents())); | 
 |   ASSERT_THAT(rfh_observer, Each(Not(Deleted()))); | 
 |   EXPECT_EQ(b3, current_frame_host()); | 
 |   EXPECT_THAT(Elements({a1, b2}), Each(InBackForwardCache())); | 
 |   EXPECT_THAT(Elements({b3, a4}), Each(Not(InBackForwardCache()))); | 
 |  | 
 |   // Even after a new IPC round trip with the renderer, a4 must still be alive. | 
 |   EXPECT_EQ("I am alive", EvalJs(a4, "window.alive")); | 
 |   EXPECT_FALSE(a4_observer.deleted()); | 
 | } | 
 |  | 
 | // Similar to BackForwardCacheBrowserTest.SubframeSurviveCache* | 
 | // Test case: a1(b2) -> b3 -> a4 -> b5 -> a1(b2). | 
 | IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, SubframeSurviveCache4) { | 
 |   ASSERT_TRUE(embedded_test_server()->Start()); | 
 |   const GURL url_ab(embedded_test_server()->GetURL( | 
 |       "a.com", "/cross_site_iframe_factory.html?a(b)")); | 
 |   const GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html")); | 
 |   const GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html")); | 
 |  | 
 |   std::vector<RenderFrameDeletedObserver*> rfh_observer; | 
 |  | 
 |   // 1) Navigate to a1(b2). | 
 |   EXPECT_TRUE(NavigateToURL(shell(), url_ab)); | 
 |   RenderFrameHostImpl* a1 = current_frame_host(); | 
 |   RenderFrameHostImpl* b2 = a1->child_at(0)->current_frame_host(); | 
 |   RenderFrameDeletedObserver a1_observer(a1), b2_observer(b2); | 
 |   rfh_observer.insert(rfh_observer.end(), {&a1_observer, &b2_observer}); | 
 |   EXPECT_TRUE(ExecJs(b2, "window.alive = 'I am alive';")); | 
 |  | 
 |   // 2) Navigate to b3. | 
 |   EXPECT_TRUE(NavigateToURL(shell(), url_b)); | 
 |   RenderFrameHostImpl* b3 = current_frame_host(); | 
 |   RenderFrameDeletedObserver b3_observer(b3); | 
 |   rfh_observer.push_back(&b3_observer); | 
 |   ASSERT_THAT(rfh_observer, Each(Not(Deleted()))); | 
 |   EXPECT_THAT(Elements({a1, b2}), Each(InBackForwardCache())); | 
 |   EXPECT_THAT(b3, Not(InBackForwardCache())); | 
 |  | 
 |   // 3) Navigate to a4. | 
 |   EXPECT_TRUE(NavigateToURL(shell(), url_a)); | 
 |   RenderFrameHostImpl* a4 = current_frame_host(); | 
 |   RenderFrameDeletedObserver a4_observer(a4); | 
 |   rfh_observer.push_back(&a4_observer); | 
 |   ASSERT_THAT(rfh_observer, Each(Not(Deleted()))); | 
 |  | 
 |   // 4) Navigate to b5 | 
 |   EXPECT_TRUE(NavigateToURL(shell(), url_b)); | 
 |   RenderFrameHostImpl* b5 = current_frame_host(); | 
 |   RenderFrameDeletedObserver b5_observer(b5); | 
 |   rfh_observer.push_back(&b5_observer); | 
 |   ASSERT_THAT(rfh_observer, Each(Not(Deleted()))); | 
 |   EXPECT_THAT(Elements({a1, b2, b3, a4}), Each(InBackForwardCache())); | 
 |   EXPECT_THAT(b5, Not(InBackForwardCache())); | 
 |  | 
 |   // 3) Go back to a1(b2). | 
 |   web_contents()->GetController().GoToOffset(-3); | 
 |   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents())); | 
 |   EXPECT_EQ(a1, current_frame_host()); | 
 |   ASSERT_THAT(rfh_observer, Each(Not(Deleted()))); | 
 |   EXPECT_THAT(Elements({b3, a4, b5}), Each(InBackForwardCache())); | 
 |   EXPECT_THAT(Elements({a1, b2}), Each(Not(InBackForwardCache()))); | 
 |  | 
 |   // Even after a new IPC round trip with the renderer, b2 must still be alive. | 
 |   EXPECT_EQ("I am alive", EvalJs(b2, "window.alive")); | 
 |   EXPECT_FALSE(b2_observer.deleted()); | 
 | } | 
 |  | 
 | IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, | 
 |                        NavigationsAreFullyCommitted) { | 
 |   ASSERT_TRUE(embedded_test_server()->Start()); | 
 |  | 
 |   // During a navigation, the document being navigated *away from* can either be | 
 |   // deleted or stored into the BackForwardCache. The document being navigated | 
 |   // *to* can either be new or restored from the BackForwardCache. | 
 |   // | 
 |   // This test covers every combination: | 
 |   // | 
 |   //  1. Navigate to a cacheable page (()->A) | 
 |   //  2. Navigate to an uncacheable page (A->B) | 
 |   //  3. Go Back to a cached page (B->A) | 
 |   //  4. Navigate to a cacheable page (A->C) | 
 |   //  5. Go Back to a cached page (C->A) | 
 |   // | 
 |   // +-+-------+----------------+---------------+ | 
 |   // |#|nav    | curr_document  | dest_document | | 
 |   // +-+-------+----------------+---------------| | 
 |   // |1|(()->A)| N/A            | new           | | 
 |   // |2|(A->B) | cached         | new           | | 
 |   // |3|(B->A) | deleted        | restored      | | 
 |   // |4|(A->C) | cached         | new           | | 
 |   // |5|(C->A) | cached         | restored      | | 
 |   // +-+-------+----------------+---------------+ | 
 |   // | 
 |   // As part of these navigations we check that LastCommittedURL was updated, | 
 |   // to verify that the frame wasn't simply swapped in without actually | 
 |   // committing. | 
 |  | 
 |   const GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html")); | 
 |   const GURL url_b(embedded_test_server()->GetURL( | 
 |       "b.com", "/back_forward_cache/page_with_dedicated_worker.html")); | 
 |   const GURL url_c(embedded_test_server()->GetURL("c.com", "/title1.html")); | 
 |  | 
 |   // 1. Navigate to a cacheable page (A). | 
 |   EXPECT_TRUE(NavigateToURL(shell(), url_a)); | 
 |   RenderFrameHostImpl* rfh_a = current_frame_host(); | 
 |   RenderFrameDeletedObserver delete_observer_rfh_a(rfh_a); | 
 |  | 
 |   // 2. Navigate from a cacheable page to an uncacheable page (A->B). | 
 |   EXPECT_TRUE(NavigateToURL(shell(), url_b)); | 
 |   EXPECT_EQ(web_contents()->GetLastCommittedURL(), url_b); | 
 |   RenderFrameHostImpl* rfh_b = current_frame_host(); | 
 |   RenderFrameDeletedObserver delete_observer_rfh_b(rfh_b); | 
 |  | 
 |   // Page A should be in the cache. | 
 |   EXPECT_FALSE(delete_observer_rfh_a.deleted()); | 
 |   EXPECT_TRUE(rfh_a->is_in_back_forward_cache()); | 
 |  | 
 |   // 3. Navigate from an uncacheable to a cached page page (B->A). | 
 |   web_contents()->GetController().GoBack(); | 
 |   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents())); | 
 |   EXPECT_EQ(web_contents()->GetLastCommittedURL(), url_a); | 
 |  | 
 |   // Page B should be deleted (not cached). | 
 |   delete_observer_rfh_b.WaitUntilDeleted(); | 
 |  | 
 |   // 4. Navigate from a cacheable page to a cacheable page (A->C). | 
 |   EXPECT_TRUE(NavigateToURL(shell(), url_c)); | 
 |   EXPECT_EQ(web_contents()->GetLastCommittedURL(), url_c); | 
 |   RenderFrameHostImpl* rfh_c = current_frame_host(); | 
 |   RenderFrameDeletedObserver delete_observer_rfh_c(rfh_c); | 
 |  | 
 |   // Page A should be in the cache. | 
 |   EXPECT_FALSE(delete_observer_rfh_a.deleted()); | 
 |   EXPECT_TRUE(rfh_a->is_in_back_forward_cache()); | 
 |  | 
 |   // 5. Navigate from a cacheable page to a cached page (C->A). | 
 |   web_contents()->GetController().GoBack(); | 
 |   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents())); | 
 |   EXPECT_EQ(web_contents()->GetLastCommittedURL(), url_a); | 
 |  | 
 |   // Page C should be in the cache. | 
 |   EXPECT_FALSE(delete_observer_rfh_c.deleted()); | 
 |   EXPECT_TRUE(rfh_c->is_in_back_forward_cache()); | 
 | } | 
 |  | 
 | IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, | 
 |                        PageWithDedicatedWorkerNotCached) { | 
 |   ASSERT_TRUE(embedded_test_server()->Start()); | 
 |  | 
 |   EXPECT_TRUE(NavigateToURL( | 
 |       shell(), | 
 |       embedded_test_server()->GetURL( | 
 |           "a.com", "/back_forward_cache/page_with_dedicated_worker.html"))); | 
 |   RenderFrameDeletedObserver delete_observer_rfh_a(current_frame_host()); | 
 |  | 
 |   // Navigate away. | 
 |   EXPECT_TRUE(NavigateToURL( | 
 |       shell(), embedded_test_server()->GetURL("b.com", "/title1.html"))); | 
 |  | 
 |   // The page with the unsupported feature should be deleted (not cached). | 
 |   delete_observer_rfh_a.WaitUntilDeleted(); | 
 | } | 
 |  | 
 | // Check that unload event handlers are not dispatched when the page goes | 
 | // into BackForwardCache. | 
 | IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, | 
 |                        ConfirmUnloadEventNotFired) { | 
 |   ASSERT_TRUE(embedded_test_server()->Start()); | 
 |   const GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html")); | 
 |   const GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html")); | 
 |  | 
 |   // 1) Navigate to A. | 
 |   EXPECT_TRUE(NavigateToURL(shell(), url_a)); | 
 |   RenderFrameHostImpl* rfh_a = current_frame_host(); | 
 |   RenderFrameDeletedObserver delete_observer_rfh_a(rfh_a); | 
 |  | 
 |   // 2) Set unload handler and check the title. | 
 |   EXPECT_TRUE(ExecJs(rfh_a, | 
 |                      "document.title = 'loaded!';" | 
 |                      "window.addEventListener('unload', () => {" | 
 |                      "  document.title = 'unloaded!';" | 
 |                      "});")); | 
 |   { | 
 |     base::string16 title_when_loaded = base::UTF8ToUTF16("loaded!"); | 
 |     TitleWatcher title_watcher(web_contents(), title_when_loaded); | 
 |     EXPECT_EQ(title_watcher.WaitAndGetTitle(), title_when_loaded); | 
 |   } | 
 |  | 
 |   // 3) Navigate to B. | 
 |   EXPECT_TRUE(NavigateToURL(shell(), url_b)); | 
 |   RenderFrameHostImpl* rfh_b = current_frame_host(); | 
 |   RenderFrameDeletedObserver delete_observer_rfh_b(rfh_b); | 
 |   EXPECT_FALSE(delete_observer_rfh_a.deleted()); | 
 |   EXPECT_TRUE(rfh_a->is_in_back_forward_cache()); | 
 |   EXPECT_FALSE(rfh_b->is_in_back_forward_cache()); | 
 |  | 
 |   // 4) Go back to A and check the title again. | 
 |   web_contents()->GetController().GoBack(); | 
 |   EXPECT_TRUE(WaitForLoadStop(web_contents())); | 
 |   EXPECT_FALSE(delete_observer_rfh_a.deleted()); | 
 |   EXPECT_FALSE(delete_observer_rfh_b.deleted()); | 
 |   EXPECT_EQ(rfh_a, current_frame_host()); | 
 |   EXPECT_TRUE(rfh_b->is_in_back_forward_cache()); | 
 |   { | 
 |     base::string16 title_when_loaded = base::UTF8ToUTF16("loaded!"); | 
 |     TitleWatcher title_watcher(web_contents(), title_when_loaded); | 
 |     EXPECT_EQ(title_watcher.WaitAndGetTitle(), title_when_loaded); | 
 |   } | 
 | } | 
 |  | 
 | IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, | 
 |                        DoesNotCacheIfRecordingAudio) { | 
 |   ASSERT_TRUE(embedded_test_server()->Start()); | 
 |  | 
 |   // Navigate to an empty page. | 
 |   GURL url(embedded_test_server()->GetURL("/title1.html")); | 
 |   EXPECT_TRUE(NavigateToURL(shell(), url)); | 
 |  | 
 |   // Request for audio recording. | 
 |   EXPECT_EQ("success", EvalJs(current_frame_host(), R"( | 
 |     new Promise(resolve => { | 
 |       navigator.mediaDevices.getUserMedia({audio: true}) | 
 |         .then(m => { resolve("success"); }) | 
 |         .catch(() => { resolve("error"); }); | 
 |     }); | 
 |   )")); | 
 |  | 
 |   RenderFrameDeletedObserver deleted(current_frame_host()); | 
 |  | 
 |   // 2) Navigate away. | 
 |   shell()->LoadURL(embedded_test_server()->GetURL("b.com", "/title1.html")); | 
 |  | 
 |   // The page was still recording audio when we navigated away, so it shouldn't | 
 |   // have been cached. | 
 |   deleted.WaitUntilDeleted(); | 
 | } | 
 |  | 
 | IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, | 
 |                        DoesNotCacheIfMainFrameStillLoading) { | 
 |   net::test_server::ControllableHttpResponse response(embedded_test_server(), | 
 |                                                       "/main_document"); | 
 |   ASSERT_TRUE(embedded_test_server()->Start()); | 
 |  | 
 |   // 1) Navigate to a page that doesn't finish loading. | 
 |   GURL url(embedded_test_server()->GetURL("a.com", "/main_document")); | 
 |   TestNavigationManager navigation_manager(shell()->web_contents(), url); | 
 |   shell()->LoadURL(url); | 
 |  | 
 |   // The navigation starts. | 
 |   EXPECT_TRUE(navigation_manager.WaitForRequestStart()); | 
 |   navigation_manager.ResumeNavigation(); | 
 |  | 
 |   // The server sends the first part of the response and waits. | 
 |   response.WaitForRequest(); | 
 |   response.Send( | 
 |       "HTTP/1.1 200 OK\r\n" | 
 |       "Content-Type: text/html; charset=utf-8\r\n" | 
 |       "\r\n" | 
 |       "<html><body> ... "); | 
 |  | 
 |   // The navigation finishes while the body is still loading. | 
 |   navigation_manager.WaitForNavigationFinished(); | 
 |   RenderFrameDeletedObserver delete_observer_rfh_a(current_frame_host()); | 
 |  | 
 |   // 2) Navigate away. | 
 |   shell()->LoadURL(embedded_test_server()->GetURL("b.com", "/title1.html")); | 
 |  | 
 |   // The page was still loading when we navigated away, so it shouldn't have | 
 |   // been cached. | 
 |   delete_observer_rfh_a.WaitUntilDeleted(); | 
 | } | 
 |  | 
 | IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, | 
 |                        DoesNotCacheIfImageStillLoading) { | 
 |   ASSERT_TRUE(embedded_test_server()->Start()); | 
 |  | 
 |   // 1) Navigate to a page with an image that loads forever. | 
 |   GURL url(embedded_test_server()->GetURL("a.com", | 
 |                                           "/infinitely_loading_image.html")); | 
 |   TestNavigationManager navigation_manager(shell()->web_contents(), url); | 
 |   shell()->LoadURL(url); | 
 |  | 
 |   // The navigation finishes while the image is still loading. | 
 |   navigation_manager.WaitForNavigationFinished(); | 
 |   RenderFrameDeletedObserver delete_observer_rfh_a(current_frame_host()); | 
 |  | 
 |   // 2) Navigate away. | 
 |   shell()->LoadURL(embedded_test_server()->GetURL("b.com", "/title1.html")); | 
 |  | 
 |   // The page was still loading when we navigated away, so it shouldn't have | 
 |   // been cached. | 
 |   delete_observer_rfh_a.WaitUntilDeleted(); | 
 | } | 
 |  | 
 | // Test is flaky (https://crbug.com/986742). | 
 | IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, | 
 |                        DISABLED_LoadingSubframeDoesNotPreventCaching) { | 
 |   // Note: This test is only documenting current behavior. Not trying to say it | 
 |   // should work this way... | 
 |  | 
 |   ASSERT_TRUE(embedded_test_server()->Start()); | 
 |  | 
 |   // 1) Navigate to a page with an iframe that loads forever. | 
 |   GURL url(embedded_test_server()->GetURL("a.com", | 
 |                                           "/infinitely_loading_iframe.html")); | 
 |   TestNavigationManager navigation_manager(shell()->web_contents(), url); | 
 |   shell()->LoadURL(url); | 
 |  | 
 |   // The navigation finishes while the iframe is still loading. | 
 |   navigation_manager.WaitForNavigationFinished(); | 
 |   RenderFrameHostImpl* rfh_a = current_frame_host(); | 
 |   RenderFrameDeletedObserver delete_observer_rfh_a(rfh_a); | 
 |  | 
 |   // 2) Navigate away. | 
 |   shell()->LoadURL(embedded_test_server()->GetURL("b.com", "/title1.html")); | 
 |   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents())); | 
 |  | 
 |   // The page with the infinitely loading iframe was cached. | 
 |   EXPECT_FALSE(delete_observer_rfh_a.deleted()); | 
 |   EXPECT_TRUE(rfh_a->is_in_back_forward_cache()); | 
 | } | 
 |  | 
 | IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, DoesNotCacheIfWebGL) { | 
 |   ASSERT_TRUE(embedded_test_server()->Start()); | 
 |  | 
 |   // 1) Navigate to a page with WebGL usage | 
 |   GURL url(embedded_test_server()->GetURL( | 
 |       "example.com", "/back_forward_cache/page_with_webgl.html")); | 
 |   EXPECT_TRUE(NavigateToURL(shell(), url)); | 
 |  | 
 |   RenderFrameDeletedObserver delete_observer_rfh_a(current_frame_host()); | 
 |  | 
 |   // 2) Navigate away. | 
 |   shell()->LoadURL(embedded_test_server()->GetURL("b.com", "/title1.html")); | 
 |  | 
 |   // The page had an active WebGL context when we navigated away, | 
 |   // so it shouldn't have been cached. | 
 | } | 
 |  | 
 | IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, DoesNotCacheIfHttpError) { | 
 |   ASSERT_TRUE(embedded_test_server()->Start()); | 
 |  | 
 |   GURL error_url(embedded_test_server()->GetURL("a.com", "/page404.html")); | 
 |   GURL url(embedded_test_server()->GetURL("b.com", "/title1.html")); | 
 |  | 
 |   // Navigate to an error page. | 
 |   EXPECT_TRUE(NavigateToURL(shell(), error_url)); | 
 |   EXPECT_EQ(net::HTTP_NOT_FOUND, current_frame_host()->last_http_status_code()); | 
 |   RenderFrameDeletedObserver delete_rfh_a(current_frame_host()); | 
 |  | 
 |   // Navigate away. | 
 |   EXPECT_TRUE(NavigateToURL(shell(), url)); | 
 |  | 
 |   // The page did not return 200 (OK), so it shouldn't have been cached. | 
 |   delete_rfh_a.WaitUntilDeleted(); | 
 | } | 
 |  | 
 | IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, | 
 |                        DoesNotCacheIfPageUnreachable) { | 
 |   ASSERT_TRUE(embedded_test_server()->Start()); | 
 |  | 
 |   GURL error_url(embedded_test_server()->GetURL("a.com", "/empty.html")); | 
 |   GURL url(embedded_test_server()->GetURL("b.com", "/title1.html")); | 
 |  | 
 |   std::unique_ptr<URLLoaderInterceptor> url_interceptor = | 
 |       URLLoaderInterceptor::SetupRequestFailForURL(error_url, | 
 |                                                    net::ERR_DNS_TIMED_OUT); | 
 |  | 
 |   // Start with a successful navigation to a document. | 
 |   EXPECT_TRUE(NavigateToURL(shell(), url)); | 
 |   EXPECT_EQ(net::HTTP_OK, current_frame_host()->last_http_status_code()); | 
 |  | 
 |   // Navigate to an error page. | 
 |   NavigationHandleObserver observer(shell()->web_contents(), error_url); | 
 |   EXPECT_FALSE(NavigateToURL(shell(), error_url)); | 
 |   EXPECT_TRUE(observer.is_error()); | 
 |   EXPECT_EQ(net::ERR_DNS_TIMED_OUT, observer.net_error_code()); | 
 |   EXPECT_EQ( | 
 |       GURL(kUnreachableWebDataURL), | 
 |       shell()->web_contents()->GetMainFrame()->GetSiteInstance()->GetSiteURL()); | 
 |   EXPECT_EQ(net::OK, current_frame_host()->last_http_status_code()); | 
 |  | 
 |   RenderFrameDeletedObserver delete_rfh_a(current_frame_host()); | 
 |  | 
 |   // Navigate away. | 
 |   EXPECT_TRUE(NavigateToURL(shell(), url)); | 
 |  | 
 |   // The page had a networking error, so it shouldn't have been cached. | 
 |   delete_rfh_a.WaitUntilDeleted(); | 
 | } | 
 |  | 
 | IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, | 
 |                        DisableBackforwardCacheForTesting) { | 
 |   ASSERT_TRUE(embedded_test_server()->Start()); | 
 |  | 
 |   // Disable the BackForwardCache. | 
 |   web_contents()->GetController().back_forward_cache().DisableForTesting(); | 
 |  | 
 |   // Navigate to a page that would normally be cacheable. | 
 |   NavigateToURL(shell(), | 
 |                 embedded_test_server()->GetURL("a.com", "/title1.html")); | 
 |   RenderFrameDeletedObserver delete_observer_rfh_a(current_frame_host()); | 
 |  | 
 |   // Navigate away. | 
 |   NavigateToURL(shell(), | 
 |                 embedded_test_server()->GetURL("b.com", "/title1.html")); | 
 |  | 
 |   // The page should be deleted (not cached). | 
 |   delete_observer_rfh_a.WaitUntilDeleted(); | 
 | } | 
 |  | 
 | // Navigate from A to B, then cause JavaScript execution on A, then go back. | 
 | // Test the RenderFrameHost in the cache is evicted by JavaScript. | 
 | IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, | 
 |                        EvictionOnJavaScriptExecution) { | 
 |   ASSERT_TRUE(embedded_test_server()->Start()); | 
 |   GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html")); | 
 |   GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html")); | 
 |  | 
 |   // 1) Navigate to A. | 
 |   EXPECT_TRUE(NavigateToURL(shell(), url_a)); | 
 |   RenderFrameHostImpl* rfh_a = current_frame_host(); | 
 |   RenderFrameDeletedObserver delete_observer_rfh_a(rfh_a); | 
 |  | 
 |   // 2) Navigate to B. | 
 |   EXPECT_TRUE(NavigateToURL(shell(), url_b)); | 
 |   RenderFrameHostImpl* rfh_b = current_frame_host(); | 
 |   RenderFrameDeletedObserver delete_observer_rfh_b(rfh_b); | 
 |  | 
 |   EXPECT_FALSE(delete_observer_rfh_a.deleted()); | 
 |   EXPECT_FALSE(delete_observer_rfh_b.deleted()); | 
 |   EXPECT_TRUE(rfh_a->is_in_back_forward_cache()); | 
 |   EXPECT_FALSE(rfh_b->is_in_back_forward_cache()); | 
 |  | 
 |   // 3) Execute JavaScript on A. | 
 |   // | 
 |   // Run JavaScript on a page in the back-forward cache. The page should be | 
 |   // evicted. As the frame is deleted, ExecJs returns false without executing. | 
 |   EXPECT_FALSE(ExecJs(rfh_a, "console.log('hi');")); | 
 |  | 
 |   // RenderFrameHost A is evicted from the BackForwardCache: | 
 |   delete_observer_rfh_a.WaitUntilDeleted(); | 
 | } | 
 |  | 
 | // Similar to BackForwardCacheBrowserTest.EvictionOnJavaScriptExecution. | 
 | // Test case: A(B) -> C -> JS on B -> A(B) | 
 | IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, | 
 |                        EvictionOnJavaScriptExecutionIframe) { | 
 |   ASSERT_TRUE(embedded_test_server()->Start()); | 
 |   GURL url_a(embedded_test_server()->GetURL( | 
 |       "a.com", "/cross_site_iframe_factory.html?a(b)")); | 
 |   GURL url_c(embedded_test_server()->GetURL("c.com", "/title1.html")); | 
 |  | 
 |   // 1) Navigate to A(B). | 
 |   EXPECT_TRUE(NavigateToURL(shell(), url_a)); | 
 |   RenderFrameHostImpl* rfh_a = current_frame_host(); | 
 |   RenderFrameHostImpl* rfh_b = rfh_a->child_at(0)->current_frame_host(); | 
 |   RenderFrameDeletedObserver delete_observer_rfh_a(rfh_a); | 
 |   RenderFrameDeletedObserver delete_observer_rfh_b(rfh_b); | 
 |  | 
 |   // 2) Navigate to C. | 
 |   EXPECT_TRUE(NavigateToURL(shell(), url_c)); | 
 |   RenderFrameHostImpl* rfh_c = current_frame_host(); | 
 |   RenderFrameDeletedObserver delete_observer_rfh_c(rfh_c); | 
 |  | 
 |   EXPECT_FALSE(delete_observer_rfh_a.deleted()); | 
 |   EXPECT_FALSE(delete_observer_rfh_b.deleted()); | 
 |   EXPECT_FALSE(delete_observer_rfh_c.deleted()); | 
 |   EXPECT_TRUE(rfh_a->is_in_back_forward_cache()); | 
 |   EXPECT_TRUE(rfh_b->is_in_back_forward_cache()); | 
 |   EXPECT_FALSE(rfh_c->is_in_back_forward_cache()); | 
 |  | 
 |   // 3) Execute JavaScript on B. | 
 |   // | 
 |   // As the frame is deleted, ExecJs returns false without executing. | 
 |   EXPECT_FALSE(ExecJs(rfh_b, "console.log('hi');")); | 
 |  | 
 |   // The A(B) page is evicted. So A and B are removed: | 
 |   delete_observer_rfh_a.WaitUntilDeleted(); | 
 |   delete_observer_rfh_b.WaitUntilDeleted(); | 
 | } | 
 |  | 
 | IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, | 
 |                        EvictionOnJavaScriptExecutionInAnotherWorld) { | 
 |   ASSERT_TRUE(embedded_test_server()->Start()); | 
 |   GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html")); | 
 |   GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html")); | 
 |  | 
 |   // 1) Navigate to A. | 
 |   EXPECT_TRUE(NavigateToURL(shell(), url_a)); | 
 |   RenderFrameHostImpl* rfh_a = current_frame_host(); | 
 |   RenderFrameDeletedObserver delete_observer_rfh_a(rfh_a); | 
 |  | 
 |   // 2) Execute JavaScript on A in a new world. This ensures a new world. | 
 |   const int32_t kNewWorldId = content::ISOLATED_WORLD_ID_CONTENT_END + 1; | 
 |   EXPECT_TRUE(ExecJs(rfh_a, "console.log('hi');", | 
 |                      EXECUTE_SCRIPT_DEFAULT_OPTIONS, kNewWorldId)); | 
 |  | 
 |   // 3) Navigate to B. | 
 |   EXPECT_TRUE(NavigateToURL(shell(), url_b)); | 
 |   RenderFrameHostImpl* rfh_b = current_frame_host(); | 
 |   RenderFrameDeletedObserver delete_observer_rfh_b(rfh_b); | 
 |  | 
 |   EXPECT_FALSE(delete_observer_rfh_a.deleted()); | 
 |   EXPECT_FALSE(delete_observer_rfh_b.deleted()); | 
 |   EXPECT_TRUE(rfh_a->is_in_back_forward_cache()); | 
 |   EXPECT_FALSE(rfh_b->is_in_back_forward_cache()); | 
 |  | 
 |   // 4) Execute JavaScript on A in the new world. | 
 |   EXPECT_FALSE(ExecJs(rfh_a, "console.log('hi');", | 
 |                       EXECUTE_SCRIPT_DEFAULT_OPTIONS, kNewWorldId)); | 
 |  | 
 |   // RenderFrameHost A is evicted from the BackForwardCache: | 
 |   delete_observer_rfh_a.WaitUntilDeleted(); | 
 | } | 
 |  | 
 | // Tests the events are fired when going back from the cache. | 
 | IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, Events) { | 
 |   ASSERT_TRUE(embedded_test_server()->Start()); | 
 |   GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html")); | 
 |   GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html")); | 
 |  | 
 |   // 1) Navigate to A. | 
 |   EXPECT_TRUE(NavigateToURL(shell(), url_a)); | 
 |   RenderFrameHostImpl* rfh_a = current_frame_host(); | 
 |   RenderFrameDeletedObserver delete_observer_rfh_a(rfh_a); | 
 |   EXPECT_TRUE(ExecJs(shell(), R"( | 
 |     window.testObservedEvents = []; | 
 |     let event_list = [ | 
 |       'visibilitychange', | 
 |       'pagehide', | 
 |       'pageshow', | 
 |       'freeze', | 
 |       'resume', | 
 |     ]; | 
 |     for (event of event_list) { | 
 |       let event2 = event; | 
 |       document.addEventListener(event, | 
 |                                 () => window.testObservedEvents.push(event2)); | 
 |     } | 
 |   )")); | 
 |  | 
 |   // 2) Navigate to B. | 
 |   EXPECT_TRUE(NavigateToURL(shell(), url_b)); | 
 |   RenderFrameHostImpl* rfh_b = current_frame_host(); | 
 |   RenderFrameDeletedObserver delete_observer_rfh_b(rfh_b); | 
 |  | 
 |   EXPECT_FALSE(delete_observer_rfh_a.deleted()); | 
 |   EXPECT_FALSE(delete_observer_rfh_b.deleted()); | 
 |   EXPECT_TRUE(rfh_a->is_in_back_forward_cache()); | 
 |   EXPECT_FALSE(rfh_b->is_in_back_forward_cache()); | 
 |  | 
 |   // 3) Go back to A. Confirm that expected events are fired. | 
 |   web_contents()->GetController().GoBack(); | 
 |   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents())); | 
 |   EXPECT_FALSE(delete_observer_rfh_a.deleted()); | 
 |   EXPECT_FALSE(delete_observer_rfh_b.deleted()); | 
 |   EXPECT_EQ(rfh_a, current_frame_host()); | 
 |   EXPECT_EQ( | 
 |       ListValueOf("visibilitychange", "freeze", "resume", "visibilitychange"), | 
 |       EvalJs(shell(), "window.testObservedEvents")); | 
 | } | 
 |  | 
 | }  // namespace content |