#include "base/types/expected.h"
#include "content/browser/back_forward_cache_browsertest.h"
#include "base/command_line.h"
#include "base/functional/bind.h"
#include "base/memory/raw_ptr.h"
#include "base/metrics/metrics_hashes.h"
#include "base/task/single_thread_task_runner.h"
#include "base/test/bind.h"
#include "base/test/test_mock_time_task_runner.h"
#include "build/build_config.h"
#include "content/browser/accessibility/browser_accessibility.h"
#include "content/browser/renderer_host/back_forward_cache_disable.h"
#include "content/browser/renderer_host/back_forward_cache_impl.h"
#include "content/browser/renderer_host/navigation_request.h"
#include "content/browser/renderer_host/render_frame_host_impl.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/common/content_navigation_policy.h"
#include "content/public/browser/browser_accessibility_state.h"
#include "content/public/browser/disallow_activation_reason.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/common/content_features.h"
#include "content/public/common/content_switches.h"
#include "content/public/test/accessibility_notification_waiter.h"
#include "content/public/test/back_forward_cache_util.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/content_browser_test_utils.h"
#include "content/public/test/fenced_frame_test_util.h"
#include "content/public/test/mock_web_contents_observer.h"
#include "content/public/test/test_navigation_observer.h"
#include "content/public/test/test_utils.h"
#include "content/shell/browser/shell.h"
#include "content/shell/browser/shell_content_browser_client.h"
#include "content/test/content_browser_test_utils_internal.h"
#include "content/test/did_commit_navigation_interceptor.h"
#include "content/test/echo.test-mojom.h"
#include "media/base/media_switches.h"
#include "net/test/embedded_test_server/controllable_http_response.h"
#include "third_party/blink/public/common/features.h"
#include "ui/accessibility/ax_action_data.h"
#include "ui/accessibility/ax_node_id_forward.h"
using testing::_;
using testing::Each;
using ::testing::ElementsAre;
using ::testing::Not;
using ::testing::UnorderedElementsAreArray;
namespace content {
using NotRestoredReason = BackForwardCacheMetrics::NotRestoredReason;
IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, BackForwardCacheFlush) {
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"));
EXPECT_TRUE(NavigateToURL(shell(), url_a));
RenderFrameHostImpl* rfh_a = current_frame_host();
RenderFrameDeletedObserver delete_observer_rfh_a(rfh_a);
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());
web_contents()->GetController().GetBackForwardCache().Flush();
delete_observer_rfh_a.WaitUntilDeleted();
EXPECT_FALSE(delete_observer_rfh_b.deleted());
ASSERT_TRUE(HistoryGoBack(web_contents()));
EXPECT_FALSE(delete_observer_rfh_b.deleted());
web_contents()->GetController().GetBackForwardCache().Flush();
delete_observer_rfh_b.WaitUntilDeleted();
}
IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, ForEachRenderFrameHost) {
DoNotFailForUnexpectedMessagesWhileCached();
ASSERT_TRUE(embedded_test_server()->Start());
GURL url_a(embedded_test_server()->GetURL(
"a.com", "/cross_site_iframe_factory.html?a(b(c),d)"));
GURL url_e(embedded_test_server()->GetURL("e.com", "/title1.html"));
std::vector<RenderFrameDeletedObserver*> rfh_observers;
EXPECT_TRUE(NavigateToURL(shell(), url_a));
RenderFrameHostImpl* rfh_a = current_frame_host();
RenderFrameHostImpl* rfh_b = rfh_a->child_at(0)->current_frame_host();
RenderFrameHostImpl* rfh_c = rfh_b->child_at(0)->current_frame_host();
RenderFrameHostImpl* rfh_d = rfh_a->child_at(1)->current_frame_host();
RenderFrameDeletedObserver a_observer(rfh_a), b_observer(rfh_b),
c_observer(rfh_c), d_observer(rfh_d);
rfh_observers.insert(rfh_observers.end(),
{&a_observer, &b_observer, &c_observer, &d_observer});
EXPECT_THAT(CollectAllRenderFrameHosts(rfh_a),
::testing::ElementsAre(rfh_a, rfh_b, rfh_d, rfh_c));
EXPECT_THAT(CollectAllRenderFrameHosts(web_contents()),
::testing::ElementsAre(rfh_a, rfh_b, rfh_d, rfh_c));
EXPECT_TRUE(NavigateToURL(shell(), url_e));
RenderFrameHostImpl* rfh_e = current_frame_host();
RenderFrameDeletedObserver e_observer(rfh_e);
rfh_observers.push_back(&e_observer);
ASSERT_THAT(rfh_observers, Each(Not(Deleted())));
EXPECT_THAT(Elements({rfh_a, rfh_b, rfh_c, rfh_d}),
Each(InBackForwardCache()));
EXPECT_THAT(rfh_e, Not(InBackForwardCache()));
EXPECT_THAT(CollectAllRenderFrameHosts(rfh_e), ::testing::ElementsAre(rfh_e));
EXPECT_THAT(CollectAllRenderFrameHosts(rfh_a),
::testing::ElementsAre(rfh_a, rfh_b, rfh_d, rfh_c));
EXPECT_THAT(CollectAllRenderFrameHosts(rfh_b),
::testing::ElementsAre(rfh_b, rfh_c));
EXPECT_THAT(
CollectAllRenderFrameHosts(web_contents()),
::testing::UnorderedElementsAre(rfh_a, rfh_b, rfh_c, rfh_d, rfh_e));
{
bool stopped = false;
web_contents()->ForEachRenderFrameHostWithAction(
[&](RenderFrameHostImpl* rfh) {
EXPECT_FALSE(stopped);
stopped = true;
return RenderFrameHost::FrameIterationAction::kStop;
});
}
EXPECT_EQ(nullptr, rfh_a->GetParentOrOuterDocument());
EXPECT_EQ(rfh_a, rfh_b->GetParentOrOuterDocument());
EXPECT_EQ(rfh_b, rfh_c->GetParentOrOuterDocument());
EXPECT_EQ(rfh_a, rfh_d->GetParentOrOuterDocument());
EXPECT_EQ(nullptr, rfh_e->GetParentOrOuterDocument());
EXPECT_EQ(rfh_a, rfh_a->GetOutermostMainFrame());
EXPECT_EQ(rfh_a, rfh_b->GetOutermostMainFrame());
EXPECT_EQ(rfh_a, rfh_c->GetOutermostMainFrame());
EXPECT_EQ(rfh_a, rfh_d->GetOutermostMainFrame());
EXPECT_EQ(rfh_e, rfh_e->GetOutermostMainFrame());
EXPECT_EQ(nullptr, rfh_a->GetParentOrOuterDocumentOrEmbedder());
EXPECT_EQ(rfh_a, rfh_b->GetParentOrOuterDocumentOrEmbedder());
EXPECT_EQ(rfh_b, rfh_c->GetParentOrOuterDocumentOrEmbedder());
EXPECT_EQ(rfh_a, rfh_d->GetParentOrOuterDocumentOrEmbedder());
EXPECT_EQ(nullptr, rfh_e->GetParentOrOuterDocumentOrEmbedder());
EXPECT_EQ(rfh_a, rfh_a->GetOutermostMainFrameOrEmbedder());
EXPECT_EQ(rfh_a, rfh_b->GetOutermostMainFrameOrEmbedder());
EXPECT_EQ(rfh_a, rfh_c->GetOutermostMainFrameOrEmbedder());
EXPECT_EQ(rfh_a, rfh_d->GetOutermostMainFrameOrEmbedder());
EXPECT_EQ(rfh_e, rfh_e->GetOutermostMainFrameOrEmbedder());
}
IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
ForEachRenderFrameHostWithSpeculative) {
IsolateAllSitesForTesting(base::CommandLine::ForCurrentProcess());
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"));
GURL url_c(embedded_test_server()->GetURL("c.com", "/title1.html"));
std::vector<RenderFrameDeletedObserver*> rfh_observers;
EXPECT_TRUE(NavigateToURL(shell(), url_a));
RenderFrameHostImpl* rfh_a = current_frame_host();
RenderFrameDeletedObserver a_observer(rfh_a);
rfh_observers.push_back(&a_observer);
EXPECT_TRUE(NavigateToURL(shell(), url_b));
RenderFrameHostImpl* rfh_b = current_frame_host();
RenderFrameDeletedObserver b_observer(rfh_b);
rfh_observers.push_back(&b_observer);
ASSERT_THAT(rfh_observers, Each(Not(Deleted())));
TestNavigationManager nav_manager(web_contents(), url_c);
shell()->LoadURL(url_c);
ASSERT_TRUE(nav_manager.WaitForRequestStart());
RenderFrameHostImpl* rfh_c =
rfh_b->frame_tree_node()->render_manager()->speculative_frame_host();
ASSERT_TRUE(rfh_c);
EXPECT_EQ(RenderFrameHostImpl::LifecycleStateImpl::kInBackForwardCache,
rfh_a->lifecycle_state());
EXPECT_FALSE(rfh_a->GetPage().IsPrimary());
EXPECT_EQ(RenderFrameHostImpl::LifecycleStateImpl::kActive,
rfh_b->lifecycle_state());
EXPECT_TRUE(rfh_b->GetPage().IsPrimary());
EXPECT_EQ(RenderFrameHostImpl::LifecycleStateImpl::kSpeculative,
rfh_c->lifecycle_state());
EXPECT_FALSE(rfh_c->GetPage().IsPrimary());
EXPECT_THAT(CollectAllRenderFrameHostsIncludingSpeculative(rfh_a),
::testing::ElementsAre(rfh_a));
EXPECT_THAT(CollectAllRenderFrameHostsIncludingSpeculative(rfh_b),
::testing::UnorderedElementsAre(rfh_b, rfh_c));
EXPECT_THAT(CollectAllRenderFrameHostsIncludingSpeculative(rfh_c),
::testing::ElementsAre(rfh_c));
EXPECT_THAT(CollectAllRenderFrameHostsIncludingSpeculative(web_contents()),
::testing::UnorderedElementsAre(rfh_a, rfh_b, rfh_c));
}
IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
NavigationsAreFullyCommitted) {
DoNotFailForUnexpectedMessagesWhileCached();
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"));
GURL url_c(embedded_test_server()->GetURL("c.com", "/title1.html"));
ASSERT_TRUE(NavigateToURL(shell(), url_a));
RenderFrameHostImplWrapper rfh_a(current_frame_host());
ASSERT_TRUE(NavigateToURL(shell(), url_b));
EXPECT_EQ(web_contents()->GetLastCommittedURL(), url_b);
RenderFrameHostImplWrapper rfh_b(current_frame_host());
EXPECT_FALSE(rfh_a.IsDestroyed());
EXPECT_TRUE(rfh_a->IsInBackForwardCache());
ASSERT_TRUE(HistoryGoBack(web_contents()));
EXPECT_EQ(web_contents()->GetLastCommittedURL(), url_a);
rfh_b->DisableBackForwardCache(RenderFrameHostDisabledForTestingReason());
ASSERT_TRUE(rfh_b.WaitUntilRenderFrameDeleted());
ExpectRestored(FROM_HERE);
ASSERT_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);
EXPECT_FALSE(rfh_a.IsDestroyed());
EXPECT_TRUE(rfh_a->IsInBackForwardCache());
ASSERT_TRUE(HistoryGoBack(web_contents()));
EXPECT_EQ(web_contents()->GetLastCommittedURL(), url_a);
EXPECT_FALSE(delete_observer_rfh_c.deleted());
EXPECT_TRUE(rfh_c->IsInBackForwardCache());
ExpectRestored(FROM_HERE);
}
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_CHROMEOS)
#define MAYBE_ProxiesAreStoredAndRestored DISABLED_ProxiesAreStoredAndRestored
#else
#define MAYBE_ProxiesAreStoredAndRestored ProxiesAreStoredAndRestored
#endif
IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
MAYBE_ProxiesAreStoredAndRestored) {
if (!AreAllSitesIsolatedForTesting())
return;
ASSERT_TRUE(embedded_test_server()->Start());
GURL url_a(embedded_test_server()->GetURL(
"a.com", "/cross_site_iframe_factory.html?a(i,j)"));
GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html"));
GURL url_c(embedded_test_server()->GetURL(
"c.com", "/cross_site_iframe_factory.html?c(k,l,m)"));
NavigationControllerImpl& controller = web_contents()->GetController();
BackForwardCacheImpl& cache = controller.GetBackForwardCache();
ASSERT_TRUE(NavigateToURL(shell(), url_a));
EXPECT_EQ(2u, render_frame_host_manager()
->current_frame_host()
->browsing_context_state()
->GetProxyCount());
RenderFrameHostImplWrapper rfh_a(current_frame_host());
std::string frame_tree_a = DepictFrameTree(rfh_a->frame_tree_node());
ASSERT_TRUE(NavigateToURL(shell(), url_b));
EXPECT_EQ(0u, render_frame_host_manager()
->current_frame_host()
->browsing_context_state()
->GetProxyCount());
RenderFrameHostImplWrapper rfh_b(current_frame_host());
EXPECT_FALSE(rfh_a.IsDestroyed());
EXPECT_TRUE(rfh_a->IsInBackForwardCache());
auto cached_entry = cache.GetOrEvictEntry(rfh_a->nav_entry_id());
EXPECT_TRUE(cached_entry.has_value());
EXPECT_EQ(2u, cached_entry.value()->proxy_hosts_size());
ASSERT_TRUE(HistoryGoBack(web_contents()));
EXPECT_EQ(2u, render_frame_host_manager()
->current_frame_host()
->browsing_context_state()
->GetProxyCount());
rfh_b->DisableBackForwardCache(RenderFrameHostDisabledForTestingReason());
ASSERT_TRUE(rfh_b.WaitUntilRenderFrameDeleted());
EXPECT_EQ(2u, render_frame_host_manager()
->current_frame_host()
->browsing_context_state()
->GetProxyCount());
EXPECT_EQ(frame_tree_a,
DepictFrameTree(current_frame_host()->frame_tree_node()));
ASSERT_TRUE(NavigateToURL(shell(), url_c));
EXPECT_EQ(3u, render_frame_host_manager()
->current_frame_host()
->browsing_context_state()
->GetProxyCount());
RenderFrameHostImplWrapper rfh_c(current_frame_host());
EXPECT_FALSE(rfh_a.IsDestroyed());
EXPECT_TRUE(rfh_a->IsInBackForwardCache());
cached_entry = cache.GetOrEvictEntry(rfh_a->nav_entry_id());
EXPECT_TRUE(cached_entry.has_value());
EXPECT_EQ(2u, cached_entry.value()->proxy_hosts_size());
ASSERT_TRUE(HistoryGoBack(web_contents()));
EXPECT_EQ(2u, render_frame_host_manager()
->current_frame_host()
->browsing_context_state()
->GetProxyCount());
EXPECT_EQ(frame_tree_a,
DepictFrameTree(current_frame_host()->frame_tree_node()));
EXPECT_FALSE(rfh_c.IsDestroyed());
EXPECT_TRUE(rfh_c->IsInBackForwardCache());
cached_entry = cache.GetOrEvictEntry(rfh_c->nav_entry_id());
EXPECT_TRUE(cached_entry.has_value());
EXPECT_EQ(3u, cached_entry.value()->proxy_hosts_size());
}
IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
RestoredProxiesAreFunctional) {
if (!AreAllSitesIsolatedForTesting())
return;
ASSERT_TRUE(embedded_test_server()->Start());
GURL url_a(embedded_test_server()->GetURL(
"a.com", "/cross_site_iframe_factory.html?a(z)"));
GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html"));
GURL url_c(embedded_test_server()->GetURL("c.com", "/title2.html"));
NavigationControllerImpl& controller = web_contents()->GetController();
EXPECT_TRUE(NavigateToURL(shell(), url_a));
RenderFrameHostImpl* rfh_a = current_frame_host();
EXPECT_TRUE(NavigateToURL(shell(), url_b));
RenderFrameHostImpl* rfh_b = current_frame_host();
DisableBFCacheForRFHForTesting(rfh_b);
ASSERT_TRUE(HistoryGoBack(web_contents()));
RenderFrameHostImpl* iframe =
rfh_a->frame_tree_node()->child_at(0)->current_frame_host();
EXPECT_TRUE(ExecJs(iframe, "top.location.href = '" + url_c.spec() + "';"));
EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
EXPECT_EQ(url_c, controller.GetLastCommittedEntry()->GetURL());
}
IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
DISABLED_LogIpcPostedToCachedFrame) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL url(embedded_test_server()->GetURL("/title1.html"));
EXPECT_TRUE(NavigateToURL(shell(), url));
RenderFrameHostImpl* rfh_a = current_frame_host();
EXPECT_TRUE(NavigateToURL(
shell(), embedded_test_server()->GetURL("b.com", "/title1.html")));
base::RunLoop run_loop;
rfh_a->RequestTextSurroundingSelection(
base::BindOnce(
[](base::RepeatingClosure quit_closure, const std::u16string& str,
uint32_t num, uint32_t num2) { quit_closure.Run(); },
run_loop.QuitClosure()),
1);
run_loop.Run();
base::RunLoop run_loop2;
rfh_a->GetHighPriorityLocalFrame()->DispatchBeforeUnload(
false,
base::BindOnce([](base::RepeatingClosure quit_closure, bool proceed,
base::TimeTicks start_time,
base::TimeTicks end_time) { quit_closure.Run(); },
run_loop2.QuitClosure()));
run_loop2.Run();
std::vector<base::HistogramBase::Sample> samples = {
base::HistogramBase::Sample(
base::TaskAnnotator::ScopedSetIpcHash::MD5HashMetricName(
"blink.mojom.HighPriorityLocalFrame")),
base::HistogramBase::Sample(
base::TaskAnnotator::ScopedSetIpcHash::MD5HashMetricName(
"blink.mojom.LocalFrame"))};
for (base::HistogramBase::Sample sample : samples) {
FetchHistogramsFromChildProcesses();
EXPECT_TRUE(HistogramContainsIntValue(
sample, histogram_tester().GetAllSamples(
"BackForwardCache.Experimental."
"UnexpectedIPCMessagePostedToCachedFrame.MethodHash")));
}
}
IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
DisableBackforwardCacheForTesting) {
ASSERT_TRUE(embedded_test_server()->Start());
web_contents()->GetController().GetBackForwardCache().DisableForTesting(
BackForwardCacheImpl::TEST_REQUIRES_NO_CACHING);
EXPECT_TRUE(NavigateToURL(
shell(), embedded_test_server()->GetURL("a.com", "/title1.html")));
RenderFrameDeletedObserver delete_observer_rfh_a(current_frame_host());
EXPECT_TRUE(NavigateToURL(
shell(), embedded_test_server()->GetURL("b.com", "/title1.html")));
delete_observer_rfh_a.WaitUntilDeleted();
}
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"));
EXPECT_TRUE(NavigateToURL(shell(), url_a));
RenderFrameHostImpl* rfh_a = current_frame_host();
RenderFrameDeletedObserver delete_observer_rfh_a(rfh_a);
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->IsInBackForwardCache());
EXPECT_FALSE(rfh_b->IsInBackForwardCache());
EvictByJavaScript(rfh_a);
delete_observer_rfh_a.WaitUntilDeleted();
ASSERT_TRUE(HistoryGoBack(web_contents()));
ExpectNotRestored({NotRestoredReason::kJavaScriptExecution}, {}, {}, {}, {},
FROM_HERE);
}
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"));
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);
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->IsInBackForwardCache());
EXPECT_TRUE(rfh_b->IsInBackForwardCache());
EXPECT_FALSE(rfh_c->IsInBackForwardCache());
EvictByJavaScript(rfh_b);
delete_observer_rfh_a.WaitUntilDeleted();
delete_observer_rfh_b.WaitUntilDeleted();
ASSERT_TRUE(HistoryGoBack(web_contents()));
ExpectNotRestored({NotRestoredReason::kJavaScriptExecution}, {}, {}, {}, {},
FROM_HERE);
}
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"));
EXPECT_TRUE(NavigateToURL(shell(), url_a));
RenderFrameHostImpl* rfh_a = current_frame_host();
RenderFrameDeletedObserver delete_observer_rfh_a(rfh_a);
const int32_t kNewWorldId = content::ISOLATED_WORLD_ID_CONTENT_END + 1;
EXPECT_TRUE(ExecJs(rfh_a, "console.log('hi');",
EXECUTE_SCRIPT_DEFAULT_OPTIONS, kNewWorldId));
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->IsInBackForwardCache());
EXPECT_FALSE(rfh_b->IsInBackForwardCache());
EXPECT_FALSE(ExecJs(rfh_a, "console.log('hi');",
EXECUTE_SCRIPT_DEFAULT_OPTIONS, kNewWorldId));
delete_observer_rfh_a.WaitUntilDeleted();
ASSERT_TRUE(HistoryGoBack(web_contents()));
ExpectNotRestored({NotRestoredReason::kJavaScriptExecution}, {}, {}, {}, {},
FROM_HERE);
}
IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, PostMessageDelivered) {
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"));
ASSERT_TRUE(NavigateToURL(shell(), url_a));
RenderFrameHostImplWrapper rfh_a(current_frame_host());
RenderFrameHostImplWrapper rfh_b(rfh_a->child_at(0)->current_frame_host());
ASSERT_TRUE(ExecJs(rfh_b.get(), R"(
localStorage.setItem('postMessage_dispatched', 'not_dispatched');
window.addEventListener('message', (event) => {
console.log(`Received message: ${event.data}`);
localStorage.setItem('postMessage_dispatched', 'dispatched');
});
)"));
ASSERT_TRUE(ExecJs(rfh_a.get(), R"(
window.addEventListener("pagehide", (event) => {
document.getElementById('child-0')
.contentWindow.postMessage('foo', '*');
}, false);
)"));
ASSERT_TRUE(NavigateToURL(shell(), url_c));
EXPECT_TRUE(rfh_a->IsInBackForwardCache());
ASSERT_TRUE(HistoryGoBack(web_contents()));
ExpectRestored(FROM_HERE);
EXPECT_EQ("dispatched",
GetLocalStorage(rfh_b.get(), "postMessage_dispatched"));
}
IN_PROC_BROWSER_TEST_F(
BackForwardCacheBrowserTest,
PagehideMakesPageIneligibleForBackForwardCacheAndNotCountedInCacheSize) {
ASSERT_TRUE(CreateHttpsServer()->Start());
GURL url_a(https_server()->GetURL("a.com", "/title1.html"));
GURL url_b(https_server()->GetURL(
"b.com", "/back_forward_cache/page_with_broadcastchannel.html"));
GURL url_c(https_server()->GetURL("c.com", "/title1.html"));
EXPECT_TRUE(NavigateToURL(shell(), url_a));
RenderFrameHostImpl* rfh_a = current_frame_host();
EXPECT_TRUE(NavigateToURL(shell(), url_b));
RenderFrameHostImpl* rfh_b = current_frame_host();
RenderFrameDeletedObserver deleted_observer_rfh_b(rfh_b);
EXPECT_TRUE(rfh_a->IsInBackForwardCache());
EXPECT_TRUE(
ExecJs(rfh_b, "setShouldAcquireBroadcastChannelInPageHide(true);"));
EXPECT_TRUE(NavigateToURL(shell(), url_c));
deleted_observer_rfh_b.WaitUntilDeleted();
EXPECT_TRUE(rfh_a->IsInBackForwardCache());
ASSERT_TRUE(HistoryGoBack(web_contents()));
ExpectNotRestored(
{NotRestoredReason::kBlocklistedFeatures},
{blink::scheduler::WebSchedulerTrackedFeature::kBroadcastChannel}, {}, {},
{}, FROM_HERE);
RenderFrameHostImpl* rfh_b_2 = current_frame_host();
EXPECT_TRUE(
ExecJs(rfh_b_2, "setShouldAcquireBroadcastChannelInPageHide(false);"));
ASSERT_TRUE(HistoryGoForward(web_contents()));
ExpectRestored(FROM_HERE);
EXPECT_TRUE(rfh_b_2->IsInBackForwardCache());
}
class BackForwardCacheEntryTimeoutBrowserTest
: public BackForwardCacheBrowserTest {
protected:
void SetUpCommandLine(base::CommandLine* command_line) override {
BackForwardCacheBrowserTest::SetUpCommandLine(command_line);
feature_list_.InitAndEnableFeature(features::kBackForwardCacheEntryTimeout);
}
private:
base::test::ScopedFeatureList feature_list_;
};
IN_PROC_BROWSER_TEST_F(BackForwardCacheEntryTimeoutBrowserTest, BusyPagehide) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL url_a(embedded_test_server()->GetURL("a.test", "/title1.html"));
GURL url_b(embedded_test_server()->GetURL("b.test", "/title1.html"));
ASSERT_TRUE(NavigateToURL(shell(), url_a));
RenderFrameHostImplWrapper rfh(current_frame_host());
ASSERT_TRUE(ExecJs(rfh.get(), R"(
addEventListener("pagehide", () => {while(1){}});
)"));
ASSERT_TRUE(NavigateToURL(shell(), url_b));
ASSERT_TRUE(rfh.WaitUntilRenderFrameDeleted());
ASSERT_TRUE(HistoryGoBack(web_contents()));
ExpectNotRestored({NotRestoredReason::kTimeoutPuttingInCache}, {}, {}, {}, {},
FROM_HERE);
}
IN_PROC_BROWSER_TEST_F(BackForwardCacheEntryTimeoutBrowserTest,
EvictPageWithInfiniteLoop) {
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"));
EXPECT_TRUE(NavigateToURL(shell(), url_a));
RenderFrameHostImplWrapper rfh_a(current_frame_host());
rfh_a->GetBackForwardCacheMetrics()->SetObserverForTesting(this);
ExecuteScriptAsync(rfh_a.get(), R"(
let i = 0;
while (true) { i++; }
)");
RenderProcessHost* process = rfh_a.get()->GetProcess();
RenderProcessHostWatcher destruction_observer(
process, RenderProcessHostWatcher::WATCH_FOR_HOST_DESTRUCTION);
EXPECT_TRUE(NavigateToURL(shell(), url_b));
RenderFrameHostImplWrapper rfh_b(current_frame_host());
destruction_observer.Wait();
EXPECT_TRUE(rfh_a.WaitUntilRenderFrameDeleted());
EXPECT_EQ(current_frame_host(), rfh_b.get());
ASSERT_TRUE(HistoryGoBack(web_contents()));
ExpectNotRestored({NotRestoredReason::kTimeoutPuttingInCache}, {}, {}, {}, {},
FROM_HERE);
EXPECT_THAT(GetTreeResult()->GetDocumentResult(),
MatchesDocumentResult(
NotRestoredReasons(NotRestoredReason::kTimeoutPuttingInCache),
BlockListedFeatures()));
}
IN_PROC_BROWSER_TEST_F(
BackForwardCacheBrowserTest,
ReissuesNavigationIfEvictedDuringNavigation_BeforeResponse) {
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", "/title2.html"));
EXPECT_TRUE(NavigateToURL(shell(), url_a));
RenderFrameHostImpl* rfh_a = current_frame_host();
RenderFrameDeletedObserver delete_observer_rfh_a(rfh_a);
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->IsInBackForwardCache());
EXPECT_NE(rfh_a, rfh_b);
web_contents()->GetController().GoBack();
EvictByJavaScript(rfh_a);
EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
delete_observer_rfh_a.WaitUntilDeleted();
RenderFrameHostImpl* rfh_a2 = current_frame_host();
EXPECT_NE(rfh_a2, rfh_b);
EXPECT_EQ(rfh_a2->GetLastCommittedURL(), url_a);
ExpectNotRestored({NotRestoredReason::kJavaScriptExecution}, {}, {}, {}, {},
FROM_HERE);
}
IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
FlushCacheDuringNavigationToCachedPage) {
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", "/title2.html"));
EXPECT_TRUE(NavigateToURL(shell(), url_a));
RenderFrameHostImpl* rfh_a1 = current_frame_host();
RenderFrameDeletedObserver delete_observer_rfh_a1(rfh_a1);
EXPECT_TRUE(NavigateToURL(shell(), url_b));
RenderFrameHostImpl* rfh_b2 = current_frame_host();
RenderFrameDeletedObserver delete_observer_rfh_b2(rfh_b2);
EXPECT_FALSE(delete_observer_rfh_a1.deleted());
EXPECT_TRUE(rfh_a1->IsInBackForwardCache());
EXPECT_NE(rfh_a1, rfh_b2);
{
TestActivationManager activation_manager(shell()->web_contents(), url_a);
web_contents()->GetController().GoBack();
EXPECT_TRUE(activation_manager.WaitForBeforeChecks());
web_contents()->GetController().GetBackForwardCache().Flush();
activation_manager.WaitForNavigationFinished();
EXPECT_FALSE(activation_manager.was_committed());
}
EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
delete_observer_rfh_a1.WaitUntilDeleted();
EXPECT_TRUE(rfh_b2->IsInBackForwardCache());
RenderFrameHostImpl* rfh_a3 = current_frame_host();
EXPECT_EQ(rfh_a3->GetLastCommittedURL(), url_a);
}
IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
EvictsFromCacheIfRendererProcessCrashes) {
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"));
EXPECT_TRUE(NavigateToURL(shell(), url_a));
RenderFrameHostImpl* rfh_a = current_frame_host();
EXPECT_TRUE(NavigateToURL(shell(), url_b));
RenderFrameHostImpl* rfh_b = current_frame_host();
EXPECT_TRUE(rfh_a->IsInBackForwardCache());
{
RenderProcessHost* process = rfh_a->GetProcess();
RenderProcessHostWatcher crash_observer(
process, RenderProcessHostWatcher::WATCH_FOR_HOST_DESTRUCTION);
EXPECT_TRUE(process->Shutdown(0));
crash_observer.Wait();
}
EXPECT_EQ(current_frame_host(), rfh_b);
ASSERT_TRUE(HistoryGoBack(web_contents()));
ExpectNotRestored({NotRestoredReason::kRendererProcessKilled}, {}, {}, {}, {},
FROM_HERE);
}
IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
SchedulerTrackedFeaturesUpdatedWhileStoring) {
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"));
EXPECT_TRUE(NavigateToURL(shell(), url_a));
RenderFrameHostImpl* rfh_a = current_frame_host();
RenderFrameDeletedObserver delete_observer_rfh_a(rfh_a);
EXPECT_TRUE(ExecJs(rfh_a, R"(
document.addEventListener('freeze', event => {
window.foo = new BroadcastChannel('foo');
});
)"));
EXPECT_TRUE(NavigateToURL(shell(), url_b));
delete_observer_rfh_a.WaitUntilDeleted();
}
IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, SameSiteNavigationCaching) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL url_a1(embedded_test_server()->GetURL("a.com", "/title1.html"));
GURL url_a2(embedded_test_server()->GetURL("a.com", "/title2.html"));
EXPECT_TRUE(NavigateToURL(shell(), url_a1));
RenderFrameHostImpl* rfh_a1 = current_frame_host();
RenderFrameDeletedObserver delete_rfh_a1(rfh_a1);
auto browsing_instance_id =
rfh_a1->GetSiteInstance()->GetBrowsingInstanceId();
EXPECT_TRUE(NavigateToURL(shell(), url_a2));
RenderFrameHostImpl* rfh_a2 = current_frame_host();
EXPECT_NE(browsing_instance_id,
rfh_a2->GetSiteInstance()->GetBrowsingInstanceId());
EXPECT_TRUE(rfh_a1->IsInBackForwardCache());
EXPECT_NE(rfh_a1, rfh_a2);
}
IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, TimedEviction) {
scoped_refptr<base::TestMockTimeTaskRunner> task_runner =
base::MakeRefCounted<base::TestMockTimeTaskRunner>();
web_contents()->GetController().GetBackForwardCache().SetTaskRunnerForTesting(
task_runner);
base::TimeDelta time_to_live_in_back_forward_cache =
BackForwardCacheImpl::GetTimeToLiveInBackForwardCache();
EXPECT_EQ(time_to_live_in_back_forward_cache, base::Seconds(3600));
base::TimeDelta delta = base::Milliseconds(1);
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"));
EXPECT_TRUE(NavigateToURL(shell(), url_a));
RenderFrameHostImplWrapper rfh_a(current_frame_host());
rfh_a->GetBackForwardCacheMetrics()->SetObserverForTesting(this);
EXPECT_TRUE(NavigateToURL(shell(), url_b));
RenderFrameHostImplWrapper rfh_b(current_frame_host());
task_runner->FastForwardBy(time_to_live_in_back_forward_cache - delta);
ASSERT_FALSE(rfh_a.IsDestroyed());
EXPECT_TRUE(rfh_a->IsInBackForwardCache());
task_runner->FastForwardBy(delta);
EXPECT_EQ(current_frame_host(), rfh_b.get());
ASSERT_TRUE(HistoryGoBack(web_contents()));
ExpectNotRestored({NotRestoredReason::kTimeout}, {}, {}, {}, {}, FROM_HERE);
EXPECT_THAT(
GetTreeResult()->GetDocumentResult(),
MatchesDocumentResult(NotRestoredReasons(NotRestoredReason::kTimeout),
BlockListedFeatures()));
}
IN_PROC_BROWSER_TEST_F(
BackForwardCacheBrowserTest,
DisableBackForwardCachePreventsDocumentsFromBeingCached) {
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"));
url::Origin origin_a = url::Origin::Create(url_a);
url::Origin origin_b = url::Origin::Create(url_b);
EXPECT_TRUE(NavigateToURL(shell(), url_a));
RenderFrameHostImpl* rfh_a = current_frame_host();
RenderFrameDeletedObserver delete_observer_rfh_a(rfh_a);
DisableBFCacheForRFHForTesting(rfh_a);
EXPECT_TRUE(NavigateToURL(shell(), url_b));
delete_observer_rfh_a.WaitUntilDeleted();
ASSERT_TRUE(HistoryGoBack(web_contents()));
ExpectNotRestored({NotRestoredReason::kDisableForRenderFrameHostCalled}, {},
{}, {RenderFrameHostDisabledForTestingReason()}, {},
FROM_HERE);
}
IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
DisableBackForwardIsNoOpIfRfhIsGone) {
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"));
url::Origin origin_a = url::Origin::Create(url_a);
url::Origin origin_b = url::Origin::Create(url_b);
EXPECT_TRUE(NavigateToURL(shell(), url_a));
RenderFrameHostImpl* rfh_a = current_frame_host();
RenderFrameDeletedObserver delete_observer_rfh_a(rfh_a);
GlobalRenderFrameHostId rfh_a_id = rfh_a->GetGlobalId();
DisableBFCacheForRFHForTesting(rfh_a_id);
EXPECT_TRUE(NavigateToURL(shell(), url_b));
delete_observer_rfh_a.WaitUntilDeleted();
DisableBFCacheForRFHForTesting(rfh_a_id);
ASSERT_TRUE(HistoryGoBack(web_contents()));
ExpectNotRestored({NotRestoredReason::kDisableForRenderFrameHostCalled}, {},
{}, {RenderFrameHostDisabledForTestingReason()}, {},
FROM_HERE);
}
IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
DisableBackForwardCacheIframe) {
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("b.com", "/title1.html"));
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);
DisableBFCacheForRFHForTesting(rfh_b);
EXPECT_TRUE(NavigateToURL(shell(), url_c));
delete_observer_rfh_a.WaitUntilDeleted();
delete_observer_rfh_b.WaitUntilDeleted();
ASSERT_TRUE(HistoryGoBack(web_contents()));
ExpectNotRestored({NotRestoredReason::kDisableForRenderFrameHostCalled}, {},
{}, {RenderFrameHostDisabledForTestingReason()}, {},
FROM_HERE);
}
IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
DisableBackForwardEvictsIfAlreadyInCache) {
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"));
url::Origin origin_a = url::Origin::Create(url_a);
url::Origin origin_b = url::Origin::Create(url_b);
EXPECT_TRUE(NavigateToURL(shell(), url_a));
RenderFrameHostImpl* rfh_a = current_frame_host();
RenderFrameDeletedObserver delete_observer_rfh_a(rfh_a);
EXPECT_TRUE(NavigateToURL(shell(), url_b));
EXPECT_TRUE(rfh_a->IsInBackForwardCache());
EXPECT_FALSE(rfh_a->is_evicted_from_back_forward_cache());
DisableBFCacheForRFHForTesting(rfh_a);
delete_observer_rfh_a.WaitUntilDeleted();
ASSERT_TRUE(HistoryGoBack(web_contents()));
ExpectNotRestored({NotRestoredReason::kDisableForRenderFrameHostCalled}, {},
{}, {RenderFrameHostDisabledForTestingReason()}, {},
FROM_HERE);
}
IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, MetricsNotRecorded) {
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"));
GURL url_b2(embedded_test_server()->GetURL("b.com", "/title1.html#2"));
EXPECT_TRUE(NavigateToURL(shell(), url_a));
EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
EXPECT_TRUE(NavigateToURL(shell(), url_b));
EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
EXPECT_TRUE(NavigateToURLFromRenderer(shell(), url_b2));
ASSERT_TRUE(HistoryGoBack(web_contents()));
ExpectOutcomeDidNotChange(FROM_HERE);
EXPECT_TRUE(NavigateToURL(shell(), url_a));
EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
ExpectOutcomeDidNotChange(FROM_HERE);
}
class BackForwardCacheBrowserTestWithDomainControlEnabled
: public BackForwardCacheBrowserTest {
protected:
void SetUpCommandLine(base::CommandLine* command_line) override {
std::string allowed_websites =
"https://a.allowed/back_forward_cache/, "
"https://b.allowed/back_forward_cache/allowed_path.html";
EnableFeatureAndSetParams(features::kBackForwardCache, "allowed_websites",
allowed_websites);
BackForwardCacheBrowserTest::SetUpCommandLine(command_line);
}
};
IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTestWithDomainControlEnabled,
CachePagesWithMatchedURLs) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL url_a(embedded_test_server()->GetURL(
"a.allowed", "/back_forward_cache/allowed_path.html"));
GURL url_b(embedded_test_server()->GetURL(
"b.allowed", "/back_forward_cache/allowed_path.html?query=bar"));
EXPECT_TRUE(NavigateToURL(shell(), url_a));
RenderFrameHostImpl* rfh_a = current_frame_host();
RenderFrameDeletedObserver delete_observer_rfh_a(rfh_a);
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->IsInBackForwardCache());
ASSERT_TRUE(HistoryGoBack(web_contents()));
EXPECT_EQ(rfh_a, current_frame_host());
EXPECT_FALSE(delete_observer_rfh_b.deleted());
EXPECT_TRUE(rfh_b->IsInBackForwardCache());
}
IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTestWithDomainControlEnabled,
DoNotCachePagesWithUnMatchedURLs) {
DisableCheckingMetricsForAllSites();
ASSERT_TRUE(embedded_test_server()->Start());
GURL url_a(embedded_test_server()->GetURL(
"a.disallowed", "/back_forward_cache/disallowed_path.html"));
GURL url_b(embedded_test_server()->GetURL(
"b.allowed", "/back_forward_cache/disallowed_path.html"));
GURL url_c(embedded_test_server()->GetURL(
"c.disallowed", "/back_forward_cache/disallowed_path.html"));
EXPECT_TRUE(NavigateToURL(shell(), url_a));
RenderFrameHostImpl* rfh_a = current_frame_host();
RenderFrameDeletedObserver delete_observer_rfh_a(rfh_a);
EXPECT_TRUE(NavigateToURL(shell(), url_b));
RenderFrameHostImpl* rfh_b = current_frame_host();
RenderFrameDeletedObserver delete_observer_rfh_b(rfh_b);
EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
delete_observer_rfh_a.WaitUntilDeleted();
EXPECT_TRUE(NavigateToURL(shell(), url_c));
EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
delete_observer_rfh_b.WaitUntilDeleted();
ASSERT_TRUE(HistoryGoBack(web_contents()));
ExpectOutcomeDidNotChange(FROM_HERE);
ExpectNotRestoredDidNotChange(FROM_HERE);
}
class BackForwardCacheBrowserTestWithBlockedWebsites
: public BackForwardCacheBrowserTest {
protected:
void SetUpCommandLine(base::CommandLine* command_line) override {
std::string blocked_websites =
"https://a.blocked/, "
"https://b.blocked/";
EnableFeatureAndSetParams(features::kBackForwardCache, "blocked_websites",
blocked_websites);
BackForwardCacheBrowserTest::SetUpCommandLine(command_line);
}
};
IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTestWithBlockedWebsites,
NavigateFromAllowedPageToDisallowedPage) {
DisableCheckingMetricsForAllSites();
ASSERT_TRUE(embedded_test_server()->Start());
GURL url_a(embedded_test_server()->GetURL(
"a.allowed", "/back_forward_cache/allowed_path.html"));
GURL url_b(embedded_test_server()->GetURL(
"b.blocked", "/back_forward_cache/disallowed_path.html"));
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_TRUE(NavigateToURL(shell(), url_b));
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->IsInBackForwardCache());
ASSERT_TRUE(HistoryGoBack(web_contents()));
EXPECT_EQ(rfh_a, current_frame_host());
ExpectRestored(FROM_HERE);
delete_observer_rfh_b.WaitUntilDeleted();
EXPECT_TRUE(delete_observer_rfh_b.deleted());
ASSERT_TRUE(HistoryGoForward(web_contents()));
ExpectOutcomeDidNotChange(FROM_HERE);
ExpectNotRestoredDidNotChange(FROM_HERE);
}
IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTestWithBlockedWebsites,
NavigateFromDisallowedPageToAllowedPage) {
DisableCheckingMetricsForAllSites();
ASSERT_TRUE(embedded_test_server()->Start());
GURL url_a(embedded_test_server()->GetURL(
"a.blocked", "/back_forward_cache/disallowed_path.html"));
GURL url_b(embedded_test_server()->GetURL(
"b.allowed", "/back_forward_cache/allowed_path.html"));
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_TRUE(NavigateToURL(shell(), url_b));
EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
RenderFrameHostImpl* rfh_b = current_frame_host();
RenderFrameDeletedObserver delete_observer_rfh_b(rfh_b);
delete_observer_rfh_a.WaitUntilDeleted();
EXPECT_TRUE(delete_observer_rfh_a.deleted());
ASSERT_TRUE(HistoryGoBack(web_contents()));
ExpectOutcomeDidNotChange(FROM_HERE);
ExpectNotRestoredDidNotChange(FROM_HERE);
EXPECT_FALSE(delete_observer_rfh_b.deleted());
EXPECT_TRUE(rfh_b->IsInBackForwardCache());
ASSERT_TRUE(HistoryGoForward(web_contents()));
ExpectRestored(FROM_HERE);
}
class BackForwardCacheBrowserTestForAllowedWebsitesUrlPatterns
: public BackForwardCacheBrowserTest {
protected:
void SetUpCommandLine(base::CommandLine* command_line) override {
std::string allowed_websites =
"https://a.com/,"
"https://b.com/path,"
"https://c.com/path/";
EnableFeatureAndSetParams(features::kBackForwardCache, "allowed_websites",
allowed_websites);
BackForwardCacheBrowserTest::SetUpCommandLine(command_line);
}
};
IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTestForAllowedWebsitesUrlPatterns,
AllowedWebsitesUrlPatterns) {
BackForwardCacheImpl& bfcache =
web_contents()->GetController().GetBackForwardCache();
EXPECT_FALSE(bfcache.IsAllowed(GURL("https://a.org/")));
EXPECT_TRUE(bfcache.IsAllowed(GURL("https://a.com/")));
EXPECT_TRUE(bfcache.IsAllowed(GURL("https://a.com")));
EXPECT_TRUE(bfcache.IsAllowed(GURL("https://a.com:123/")));
EXPECT_TRUE(bfcache.IsAllowed(GURL("https://a.com:123/?x=1")));
EXPECT_TRUE(bfcache.IsAllowed(GURL("http://a.com/")));
EXPECT_TRUE(bfcache.IsAllowed(GURL("https://a.com/path")));
EXPECT_FALSE(bfcache.IsAllowed(GURL("https://prefix.a.com/")));
EXPECT_FALSE(bfcache.IsAllowed(GURL("https://b.com/")));
EXPECT_TRUE(bfcache.IsAllowed(GURL("https://b.com/path")));
EXPECT_TRUE(bfcache.IsAllowed(GURL("https://b.com/path/")));
EXPECT_TRUE(bfcache.IsAllowed(GURL("https://b.com/path_abc")));
EXPECT_TRUE(bfcache.IsAllowed(GURL("https://b.com/path_abc?x=1")));
EXPECT_FALSE(bfcache.IsAllowed(GURL("https://c.com/path")));
}
class BackForwardCacheBrowserTestForBlockedWebsitesUrlPatterns
: public BackForwardCacheBrowserTest {
protected:
void SetUpCommandLine(base::CommandLine* command_line) override {
std::string blocked_websites =
"https://a.com/,"
"https://b.com/path,"
"https://c.com/path/";
EnableFeatureAndSetParams(features::kBackForwardCache, "blocked_websites",
blocked_websites);
BackForwardCacheBrowserTest::SetUpCommandLine(command_line);
}
};
IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTestForBlockedWebsitesUrlPatterns,
BlockedWebsitesUrlPatterns) {
BackForwardCacheImpl& bfcache =
web_contents()->GetController().GetBackForwardCache();
EXPECT_TRUE(bfcache.IsAllowed(GURL("https://a.org/")));
EXPECT_FALSE(bfcache.IsAllowed(GURL("https://a.com/")));
EXPECT_FALSE(bfcache.IsAllowed(GURL("https://a.com")));
EXPECT_FALSE(bfcache.IsAllowed(GURL("https://a.com:123/")));
EXPECT_FALSE(bfcache.IsAllowed(GURL("https://a.com:123/?x=1")));
EXPECT_FALSE(bfcache.IsAllowed(GURL("http://a.com/")));
EXPECT_FALSE(bfcache.IsAllowed(GURL("https://a.com/path")));
EXPECT_TRUE(bfcache.IsAllowed(GURL("https://prefix.a.com/")));
EXPECT_TRUE(bfcache.IsAllowed(GURL("https://b.com/")));
EXPECT_FALSE(bfcache.IsAllowed(GURL("https://b.com/path")));
EXPECT_FALSE(bfcache.IsAllowed(GURL("https://b.com/path/")));
EXPECT_FALSE(bfcache.IsAllowed(GURL("https://b.com/path_abc")));
EXPECT_FALSE(bfcache.IsAllowed(GURL("https://b.com/path_abc?x=1")));
EXPECT_TRUE(bfcache.IsAllowed(GURL("https://c.com/path")));
}
class BackForwardCacheBrowserTestForWebsitesUrlPatterns
: public BackForwardCacheBrowserTest {
protected:
void SetUpCommandLine(base::CommandLine* command_line) override {
std::string allowed_websites = "https://a.com/";
EnableFeatureAndSetParams(features::kBackForwardCache, "allowed_websites",
allowed_websites);
std::string blocked_websites = "https://a.com/";
EnableFeatureAndSetParams(features::kBackForwardCache, "blocked_websites",
blocked_websites);
BackForwardCacheBrowserTest::SetUpCommandLine(command_line);
}
};
IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTestForWebsitesUrlPatterns,
WebsitesUrlPatterns) {
BackForwardCacheImpl& bfcache =
web_contents()->GetController().GetBackForwardCache();
EXPECT_FALSE(bfcache.IsAllowed(GURL("https://a.com/")));
EXPECT_FALSE(bfcache.IsAllowed(GURL("https://a.com")));
}
class BackForwardCacheBrowserTestWithBlockedCgiParams
: public BackForwardCacheBrowserTest {
protected:
void SetUpCommandLine(base::CommandLine* command_line) override {
std::string blocked_cgi_params = "ibp=1|tbm=1";
EnableFeatureAndSetParams(features::kBackForwardCache, "blocked_cgi_params",
blocked_cgi_params);
BackForwardCacheBrowserTest::SetUpCommandLine(command_line);
}
};
IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTestWithBlockedCgiParams,
NavigateFromAllowedPageToDisallowedPage) {
DisableCheckingMetricsForAllSites();
ASSERT_TRUE(embedded_test_server()->Start());
GURL url_allowed(
embedded_test_server()->GetURL("a.llowed", "/title1.html?tbm=0"));
GURL url_not_allowed(
embedded_test_server()->GetURL("nota.llowed", "/title1.html?tbm=1"));
EXPECT_TRUE(NavigateToURL(shell(), url_allowed));
EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
RenderFrameHostImpl* rfh_allowed = current_frame_host();
RenderFrameDeletedObserver delete_observer_rfh_allowed(rfh_allowed);
EXPECT_TRUE(NavigateToURL(shell(), url_not_allowed));
EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
RenderFrameHostImpl* rfh_not_allowed = current_frame_host();
RenderFrameDeletedObserver delete_observer_rfh_not_allowed(rfh_not_allowed);
EXPECT_FALSE(delete_observer_rfh_allowed.deleted());
EXPECT_TRUE(rfh_allowed->IsInBackForwardCache());
ASSERT_TRUE(HistoryGoBack(web_contents()));
EXPECT_EQ(rfh_allowed, current_frame_host());
ExpectRestored(FROM_HERE);
delete_observer_rfh_not_allowed.WaitUntilDeleted();
EXPECT_TRUE(delete_observer_rfh_not_allowed.deleted());
ASSERT_TRUE(HistoryGoForward(web_contents()));
ExpectOutcomeDidNotChange(FROM_HERE);
ExpectNotRestoredDidNotChange(FROM_HERE);
}
IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTestWithBlockedCgiParams,
NavigateFromDisallowedPageToAllowedPage) {
DisableCheckingMetricsForAllSites();
ASSERT_TRUE(embedded_test_server()->Start());
GURL url_allowed(
embedded_test_server()->GetURL("a.llowed", "/title1.html?tbm=0"));
GURL url_not_allowed(
embedded_test_server()->GetURL("nota.llowed", "/title1.html?tbm=1"));
EXPECT_TRUE(NavigateToURL(shell(), url_not_allowed));
EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
RenderFrameHostImpl* rfh_not_allowed = current_frame_host();
RenderFrameDeletedObserver delete_observer_rfh_not_allowed(rfh_not_allowed);
EXPECT_TRUE(NavigateToURL(shell(), url_allowed));
EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
RenderFrameHostImpl* rfh_allowed = current_frame_host();
RenderFrameDeletedObserver delete_observer_rfh_allowed(rfh_allowed);
delete_observer_rfh_not_allowed.WaitUntilDeleted();
EXPECT_TRUE(delete_observer_rfh_not_allowed.deleted());
ASSERT_TRUE(HistoryGoBack(web_contents()));
ExpectOutcomeDidNotChange(FROM_HERE);
ExpectNotRestoredDidNotChange(FROM_HERE);
EXPECT_FALSE(delete_observer_rfh_allowed.deleted());
EXPECT_TRUE(rfh_allowed->IsInBackForwardCache());
ASSERT_TRUE(HistoryGoForward(web_contents()));
ExpectRestored(FROM_HERE);
}
IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, WebPreferences) {
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", "/title2.html"));
EXPECT_TRUE(NavigateToURL(shell(), url_a));
RenderFrameHostImpl* rfh_a = current_frame_host();
auto browsing_instance_id = rfh_a->GetSiteInstance()->GetBrowsingInstanceId();
EXPECT_EQ(
true,
EvalJs(web_contents(),
"window.matchMedia('(prefers-color-scheme: light)').matches"));
EXPECT_TRUE(NavigateToURL(shell(), url_b));
RenderFrameHostImpl* rfh_b = current_frame_host();
EXPECT_NE(browsing_instance_id,
rfh_b->GetSiteInstance()->GetBrowsingInstanceId());
EXPECT_TRUE(rfh_a->IsInBackForwardCache());
EXPECT_NE(rfh_a, rfh_b);
blink::web_pref::WebPreferences prefs =
web_contents()->GetOrCreateWebPreferences();
prefs.preferred_color_scheme = blink::mojom::PreferredColorScheme::kDark;
web_contents()->SetWebPreferences(prefs);
EXPECT_EQ(
true,
EvalJs(web_contents(),
"window.matchMedia('(prefers-color-scheme: dark)').matches"));
ASSERT_TRUE(HistoryGoBack(web_contents()));
EXPECT_EQ(rfh_a, current_frame_host());
EXPECT_EQ(
true,
EvalJs(web_contents(),
"window.matchMedia('(prefers-color-scheme: dark)').matches"));
}
IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, NestedWebContents) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL url(embedded_test_server()->GetURL("a.com", "/page_with_iframe.html"));
GURL url_inner(embedded_test_server()->GetURL("a.com", "/title2.html"));
EXPECT_TRUE(NavigateToURL(shell(), url));
RenderFrameHostImpl* rfh_a = current_frame_host();
RenderFrameHostImpl* child = rfh_a->child_at(0)->current_frame_host();
EXPECT_TRUE(child);
auto* inner_contents = CreateAndAttachInnerContents(child);
EXPECT_TRUE(NavigateToURL(inner_contents, url_inner));
RenderFrameDeletedObserver deleted(rfh_a);
shell()->LoadURL(embedded_test_server()->GetURL("b.com", "/title1.html"));
deleted.WaitUntilDeleted();
ASSERT_TRUE(HistoryGoBack(web_contents()));
ExpectNotRestored({NotRestoredReason::kHaveInnerContents}, {}, {}, {}, {},
FROM_HERE);
}
IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, RestoreWhilePendingCommit) {
net::test_server::ControllableHttpResponse response(embedded_test_server(),
"/main_document");
ASSERT_TRUE(embedded_test_server()->Start());
GURL url1(embedded_test_server()->GetURL("a.com", "/title1.html"));
GURL url2(embedded_test_server()->GetURL("b.com", "/title2.html"));
GURL url3(embedded_test_server()->GetURL("c.com", "/main_document"));
EXPECT_TRUE(NavigateToURL(shell(), url1));
RenderFrameHost* rfh1 = current_frame_host();
EXPECT_TRUE(NavigateToURL(shell(), url2));
shell()->LoadURL(url3);
response.WaitForRequest();
ASSERT_TRUE(HistoryGoBack(web_contents()));
EXPECT_EQ(rfh1, current_frame_host());
}
IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
IsInactiveAndDisallowActivationIsNoopWhenActive) {
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"));
EXPECT_TRUE(NavigateToURL(shell(), url_a));
EXPECT_FALSE(current_frame_host()->IsInactiveAndDisallowActivation(
DisallowActivationReasonId::kForTesting));
EXPECT_TRUE(NavigateToURL(shell(), url_b));
ASSERT_TRUE(HistoryGoBack(web_contents()));
ExpectRestored(FROM_HERE);
}
IN_PROC_BROWSER_TEST_F(
BackForwardCacheBrowserTest,
IsInactiveAndDisallowActivationDoesEvictForCachedFrames) {
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"));
EXPECT_TRUE(NavigateToURL(shell(), url_a));
RenderFrameDeletedObserver delete_observer_rfh_a(current_frame_host());
RenderFrameHostImpl* rfh_a = current_frame_host();
EXPECT_TRUE(NavigateToURL(shell(), url_b));
const uint64_t reason = DisallowActivationReasonId::kForTesting;
EXPECT_TRUE(rfh_a->IsInactiveAndDisallowActivation(reason));
ASSERT_TRUE(HistoryGoBack(web_contents()));
ExpectNotRestored({NotRestoredReason::kIgnoreEventAndEvict}, {}, {}, {},
{reason}, FROM_HERE);
}
class BackForwardCacheDisabledThroughCommandLineBrowserTest
: public BackForwardCacheBrowserTest {
protected:
void SetUpCommandLine(base::CommandLine* command_line) override {
BackForwardCacheBrowserTest::SetUpCommandLine(command_line);
command_line->AppendSwitch(switches::kDisableBackForwardCache);
EnableFeatureAndSetParams(blink::features::kLoadingTasksUnfreezable,
"max_buffered_bytes_per_process", "1000");
}
};
IN_PROC_BROWSER_TEST_F(BackForwardCacheDisabledThroughCommandLineBrowserTest,
BFCacheDisabled) {
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"));
EXPECT_FALSE(base::FieldTrialList::IsTrialActive(
base::FeatureList::GetFieldTrial(features::kBackForwardCache)
->trial_name()));
EXPECT_FALSE(IsBackForwardCacheEnabled());
EXPECT_FALSE(base::FieldTrialList::IsTrialActive(
base::FeatureList::GetFieldTrial(features::kBackForwardCache)
->trial_name()));
EXPECT_TRUE(NavigateToURL(shell(), url_a));
RenderFrameHostImpl* rfh_a = current_frame_host();
RenderFrameDeletedObserver delete_observer_rfh_a(rfh_a);
EXPECT_TRUE(NavigateToURL(shell(), url_b));
delete_observer_rfh_a.WaitUntilDeleted();
ExpectOutcomeDidNotChange(FROM_HERE);
ExpectNotRestoredDidNotChange(FROM_HERE);
EXPECT_FALSE(base::FieldTrialList::IsTrialActive(
base::FeatureList::GetFieldTrial(features::kBackForwardCache)
->trial_name()));
}
IN_PROC_BROWSER_TEST_F(BackForwardCacheDisabledThroughCommandLineBrowserTest,
BFCacheDisabled_NetworkRequests) {
net::test_server::ControllableHttpResponse image_response(
embedded_test_server(), "/image.png");
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"));
EXPECT_FALSE(base::FieldTrialList::IsTrialActive(
base::FeatureList::GetFieldTrial(features::kBackForwardCache)
->trial_name()));
EXPECT_FALSE(IsBackForwardCacheEnabled());
EXPECT_FALSE(base::FieldTrialList::IsTrialActive(
base::FeatureList::GetFieldTrial(features::kBackForwardCache)
->trial_name()));
EXPECT_TRUE(NavigateToURL(shell(), url_a));
RenderFrameHostImpl* rfh_a = current_frame_host();
RenderFrameDeletedObserver delete_observer_rfh_a(rfh_a);
EXPECT_TRUE(ExecJs(rfh_a, R"(
var image = document.createElement("img");
image.src = "image.png";
document.body.appendChild(image);
)"));
image_response.WaitForRequest();
image_response.Send(net::HTTP_OK, "image/png");
image_response.Send("image_body");
image_response.Done();
EXPECT_TRUE(NavigateToURL(shell(), url_b));
delete_observer_rfh_a.WaitUntilDeleted();
ExpectOutcomeDidNotChange(FROM_HERE);
ExpectNotRestoredDidNotChange(FROM_HERE);
EXPECT_FALSE(base::FieldTrialList::IsTrialActive(
base::FeatureList::GetFieldTrial(features::kBackForwardCache)
->trial_name()));
}
IN_PROC_BROWSER_TEST_F(
BackForwardCacheBrowserTest,
EvictingDocumentsInRelatedSiteInstancesDoesNotRestartNavigation) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL url_a1(embedded_test_server()->GetURL("a.com", "/title1.html#part1"));
GURL url_a2(embedded_test_server()->GetURL("a.com", "/title1.html#part2"));
GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html"));
EXPECT_TRUE(NavigateToURL(shell(), url_a1));
EXPECT_TRUE(NavigateToURL(shell(), url_a2));
EXPECT_TRUE(NavigateToURL(shell(), url_b));
web_contents()->GetController().GoBack();
ASSERT_TRUE(HistoryGoBack(web_contents()));
EXPECT_EQ(url_a1, web_contents()->GetLastCommittedURL());
}
namespace {
class ExecJsInDidFinishNavigation : public WebContentsObserver {
public:
explicit ExecJsInDidFinishNavigation(WebContents* web_contents)
: WebContentsObserver(web_contents) {}
void DidFinishNavigation(NavigationHandle* navigation_handle) override {
if (!navigation_handle->IsInMainFrame() ||
!navigation_handle->HasCommitted() ||
navigation_handle->IsSameDocument()) {
return;
}
ExecuteScriptAsync(navigation_handle->GetRenderFrameHost(),
"var foo = 42;");
}
};
}
IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
MessageFromDidFinishNavigation) {
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"));
EXPECT_TRUE(NavigateToURL(shell(), url_a));
RenderFrameHostImpl* rfh_a = current_frame_host();
EXPECT_TRUE(ExecJs(rfh_a, "window.alive = 'I am alive';"));
EXPECT_TRUE(NavigateToURL(shell(), url_b));
ExecJsInDidFinishNavigation observer(shell()->web_contents());
ASSERT_TRUE(HistoryGoBack(web_contents()));
EXPECT_EQ("I am alive", EvalJs(rfh_a, "window.alive"));
EXPECT_FALSE(web_contents()->IsLoading());
EXPECT_EQ(rfh_a, current_frame_host());
}
#if BUILDFLAG(IS_ANDROID)
IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
ChildImportanceTestForBackForwardCachedPagesTest) {
web_contents()->SetPrimaryMainFrameImportance(
ChildProcessImportance::MODERATE);
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"));
EXPECT_TRUE(NavigateToURL(shell(), url_a));
RenderFrameHostImpl* rfh_a = current_frame_host();
RenderFrameDeletedObserver delete_observer_rfh_a(rfh_a);
EXPECT_TRUE(NavigateToURL(shell(), url_b));
ASSERT_FALSE(delete_observer_rfh_a.deleted());
EXPECT_EQ(ChildProcessImportance::NORMAL,
rfh_a->GetProcess()->GetEffectiveImportance());
ASSERT_TRUE(HistoryGoBack(web_contents()));
EXPECT_EQ(ChildProcessImportance::MODERATE,
rfh_a->GetProcess()->GetEffectiveImportance());
}
#endif
IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, PageshowMetrics) {
DoNotFailForUnexpectedMessagesWhileCached();
ASSERT_TRUE(embedded_test_server()->Start());
const char kHistogramName[] =
"BackForwardCache.MainFrameHasPageshowListenersOnRestore";
const GURL url1(embedded_test_server()->GetURL("a.com", "/title1.html"));
const GURL url2(embedded_test_server()->GetURL("b.com", "/title1.html"));
EXPECT_TRUE(NavigateToURL(shell(), url1));
EXPECT_TRUE(ExecJs(current_frame_host(), R"(
window.foo = 42;
)"));
EXPECT_TRUE(NavigateToURL(shell(), url2));
ASSERT_TRUE(HistoryGoBack(web_contents()));
EXPECT_EQ(42, EvalJs(current_frame_host(), "window.foo"));
content::FetchHistogramsFromChildProcesses();
EXPECT_THAT(histogram_tester().GetAllSamples(kHistogramName),
ElementsAre(base::Bucket(0, 1)));
EXPECT_TRUE(ExecJs(current_frame_host(), R"(
window.addEventListener("pageshow", () => {});
)"));
EXPECT_TRUE(NavigateToURL(shell(), url2));
ASSERT_TRUE(HistoryGoBack(web_contents()));
EXPECT_EQ(42, EvalJs(current_frame_host(), "window.foo"));
content::FetchHistogramsFromChildProcesses();
EXPECT_THAT(histogram_tester().GetAllSamples(kHistogramName),
ElementsAre(base::Bucket(0, 1), base::Bucket(1, 1)));
}
IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, CheckIsActive) {
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"));
EXPECT_TRUE(NavigateToURL(shell(), url_a));
RenderFrameHostImpl* rfh_a = current_frame_host();
RenderFrameHostImpl* rfh_b = rfh_a->child_at(0)->current_frame_host();
EXPECT_TRUE(rfh_a->IsActive());
EXPECT_TRUE(rfh_b->IsActive());
EXPECT_TRUE(NavigateToURL(shell(), url_c));
EXPECT_TRUE(rfh_a->IsInBackForwardCache());
EXPECT_TRUE(rfh_b->IsInBackForwardCache());
EXPECT_FALSE(rfh_a->IsActive());
EXPECT_FALSE(rfh_b->IsActive());
}
IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
CheckLifecycleStateTransition) {
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", "/title2.html"));
EXPECT_TRUE(NavigateToURL(shell(), url_a));
RenderFrameHostImpl* rfh_a = current_frame_host();
EXPECT_EQ(RenderFrameHostImpl::LifecycleStateImpl::kActive,
rfh_a->lifecycle_state());
EXPECT_EQ(RenderFrameHost::LifecycleState::kActive,
rfh_a->GetLifecycleState());
EXPECT_TRUE(rfh_a->GetPage().IsPrimary());
EXPECT_TRUE(rfh_a->IsInPrimaryMainFrame());
{
::testing::NiceMock<MockWebContentsObserver> state_change_observer(
web_contents());
EXPECT_CALL(state_change_observer,
RenderFrameHostStateChanged(
rfh_a, RenderFrameHost::LifecycleState::kActive,
RenderFrameHost::LifecycleState::kInBackForwardCache));
EXPECT_CALL(state_change_observer,
RenderFrameHostStateChanged(
::testing::Not(rfh_a),
RenderFrameHost::LifecycleState::kPendingCommit,
RenderFrameHost::LifecycleState::kActive));
EXPECT_TRUE(NavigateToURL(shell(), url_b));
}
RenderFrameHostImpl* rfh_b = current_frame_host();
EXPECT_TRUE(rfh_a->IsInBackForwardCache());
EXPECT_EQ(RenderFrameHostImpl::LifecycleStateImpl::kInBackForwardCache,
rfh_a->lifecycle_state());
EXPECT_EQ(RenderFrameHost::LifecycleState::kInBackForwardCache,
rfh_a->GetLifecycleState());
EXPECT_FALSE(rfh_a->GetPage().IsPrimary());
EXPECT_FALSE(rfh_a->IsInPrimaryMainFrame());
EXPECT_EQ(RenderFrameHostImpl::LifecycleStateImpl::kActive,
rfh_b->lifecycle_state());
EXPECT_EQ(RenderFrameHost::LifecycleState::kActive,
rfh_b->GetLifecycleState());
EXPECT_TRUE(rfh_b->GetPage().IsPrimary());
EXPECT_TRUE(rfh_b->IsInPrimaryMainFrame());
{
::testing::NiceMock<MockWebContentsObserver> state_change_observer(
web_contents());
EXPECT_CALL(state_change_observer,
RenderFrameHostStateChanged(
rfh_a, RenderFrameHost::LifecycleState::kInBackForwardCache,
RenderFrameHost::LifecycleState::kActive));
EXPECT_CALL(state_change_observer,
RenderFrameHostStateChanged(
rfh_b, RenderFrameHost::LifecycleState::kActive,
RenderFrameHost::LifecycleState::kInBackForwardCache));
ASSERT_TRUE(HistoryGoBack(web_contents()));
}
EXPECT_EQ(RenderFrameHostImpl::LifecycleStateImpl::kActive,
rfh_a->lifecycle_state());
EXPECT_TRUE(rfh_a->GetPage().IsPrimary());
EXPECT_TRUE(rfh_a->IsInPrimaryMainFrame());
EXPECT_TRUE(rfh_b->IsInBackForwardCache());
EXPECT_EQ(RenderFrameHostImpl::LifecycleStateImpl::kInBackForwardCache,
rfh_b->lifecycle_state());
EXPECT_FALSE(rfh_b->GetPage().IsPrimary());
EXPECT_FALSE(rfh_b->IsInPrimaryMainFrame());
}
IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
CheckLifecycleStateTransitionWithSubframes) {
IsolateAllSitesForTesting(base::CommandLine::ForCurrentProcess());
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", "/cross_site_iframe_factory.html?c(d)"));
EXPECT_TRUE(NavigateToURL(shell(), url_a));
RenderFrameHostImpl* rfh_a = current_frame_host();
RenderFrameHostImpl* rfh_b = rfh_a->child_at(0)->current_frame_host();
EXPECT_FALSE(rfh_a->IsInBackForwardCache());
EXPECT_FALSE(rfh_b->IsInBackForwardCache());
EXPECT_EQ(RenderFrameHostImpl::LifecycleStateImpl::kActive,
rfh_a->lifecycle_state());
EXPECT_EQ(RenderFrameHost::LifecycleState::kActive,
rfh_a->GetLifecycleState());
EXPECT_EQ(RenderFrameHostImpl::LifecycleStateImpl::kActive,
rfh_b->lifecycle_state());
EXPECT_EQ(RenderFrameHost::LifecycleState::kActive,
rfh_b->GetLifecycleState());
{
::testing::NiceMock<MockWebContentsObserver> state_change_observer(
web_contents());
EXPECT_CALL(state_change_observer,
RenderFrameHostStateChanged(
rfh_a, RenderFrameHost::LifecycleState::kActive,
RenderFrameHost::LifecycleState::kInBackForwardCache));
EXPECT_CALL(state_change_observer,
RenderFrameHostStateChanged(
rfh_b, RenderFrameHost::LifecycleState::kActive,
RenderFrameHost::LifecycleState::kInBackForwardCache));
EXPECT_CALL(state_change_observer,
RenderFrameHostStateChanged(
::testing::Not(::testing::AnyOf(rfh_a, rfh_b)),
RenderFrameHost::LifecycleState::kPendingCommit,
RenderFrameHost::LifecycleState::kActive))
.Times(2);
EXPECT_CALL(state_change_observer,
RenderFrameHostStateChanged(
::testing::Not(::testing::AnyOf(rfh_a, rfh_b)),
RenderFrameHost::LifecycleState::kActive,
RenderFrameHost::LifecycleState::kPendingDeletion));
EXPECT_TRUE(NavigateToURL(shell(), url_c));
}
RenderFrameHostImpl* rfh_c = current_frame_host();
RenderFrameHostImpl* rfh_d = rfh_c->child_at(0)->current_frame_host();
EXPECT_TRUE(rfh_a->IsInBackForwardCache());
EXPECT_TRUE(rfh_b->IsInBackForwardCache());
EXPECT_FALSE(rfh_c->IsInBackForwardCache());
EXPECT_FALSE(rfh_d->IsInBackForwardCache());
EXPECT_EQ(RenderFrameHostImpl::LifecycleStateImpl::kInBackForwardCache,
rfh_a->lifecycle_state());
EXPECT_EQ(RenderFrameHost::LifecycleState::kInBackForwardCache,
rfh_a->GetLifecycleState());
EXPECT_EQ(RenderFrameHostImpl::LifecycleStateImpl::kInBackForwardCache,
rfh_b->lifecycle_state());
EXPECT_EQ(RenderFrameHost::LifecycleState::kInBackForwardCache,
rfh_b->GetLifecycleState());
EXPECT_EQ(RenderFrameHostImpl::LifecycleStateImpl::kActive,
rfh_c->lifecycle_state());
EXPECT_EQ(RenderFrameHost::LifecycleState::kActive,
rfh_c->GetLifecycleState());
EXPECT_EQ(RenderFrameHostImpl::LifecycleStateImpl::kActive,
rfh_d->lifecycle_state());
EXPECT_EQ(RenderFrameHost::LifecycleState::kActive,
rfh_d->GetLifecycleState());
{
::testing::NiceMock<MockWebContentsObserver> state_change_observer(
web_contents());
EXPECT_CALL(state_change_observer,
RenderFrameHostStateChanged(
rfh_a, RenderFrameHost::LifecycleState::kInBackForwardCache,
RenderFrameHost::LifecycleState::kActive));
EXPECT_CALL(state_change_observer,
RenderFrameHostStateChanged(
rfh_b, RenderFrameHost::LifecycleState::kInBackForwardCache,
RenderFrameHost::LifecycleState::kActive));
EXPECT_CALL(state_change_observer,
RenderFrameHostStateChanged(
rfh_c, RenderFrameHost::LifecycleState::kActive,
RenderFrameHost::LifecycleState::kInBackForwardCache));
EXPECT_CALL(state_change_observer,
RenderFrameHostStateChanged(
rfh_d, RenderFrameHost::LifecycleState::kActive,
RenderFrameHost::LifecycleState::kInBackForwardCache));
ASSERT_TRUE(HistoryGoBack(web_contents()));
}
EXPECT_FALSE(rfh_a->IsInBackForwardCache());
EXPECT_FALSE(rfh_b->IsInBackForwardCache());
EXPECT_TRUE(rfh_c->IsInBackForwardCache());
EXPECT_TRUE(rfh_d->IsInBackForwardCache());
EXPECT_EQ(RenderFrameHostImpl::LifecycleStateImpl::kActive,
rfh_a->lifecycle_state());
EXPECT_EQ(RenderFrameHost::LifecycleState::kActive,
rfh_a->GetLifecycleState());
EXPECT_EQ(RenderFrameHostImpl::LifecycleStateImpl::kActive,
rfh_b->lifecycle_state());
EXPECT_EQ(RenderFrameHost::LifecycleState::kActive,
rfh_b->GetLifecycleState());
EXPECT_EQ(RenderFrameHostImpl::LifecycleStateImpl::kInBackForwardCache,
rfh_c->lifecycle_state());
EXPECT_EQ(RenderFrameHost::LifecycleState::kInBackForwardCache,
rfh_c->GetLifecycleState());
EXPECT_EQ(RenderFrameHostImpl::LifecycleStateImpl::kInBackForwardCache,
rfh_d->lifecycle_state());
EXPECT_EQ(RenderFrameHost::LifecycleState::kInBackForwardCache,
rfh_d->GetLifecycleState());
}
namespace {
class EchoFakeWithFilter final : public mojom::Echo {
public:
explicit EchoFakeWithFilter(mojo::PendingReceiver<mojom::Echo> receiver,
std::unique_ptr<mojo::MessageFilter> filter)
: receiver_(this, std::move(receiver)) {
receiver_.SetFilter(std::move(filter));
}
~EchoFakeWithFilter() override = default;
void EchoString(const std::string& input,
EchoStringCallback callback) override {
std::move(callback).Run(input);
}
private:
mojo::Receiver<mojom::Echo> receiver_;
};
}
IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
MessageReceivedOnAssociatedInterfaceWhileCached) {
DoNotFailForUnexpectedMessagesWhileCached();
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"));
EXPECT_TRUE(NavigateToURL(shell(), url_a));
RenderFrameHostImpl* rfh_a = current_frame_host();
RenderFrameDeletedObserver delete_observer_rfh_a(rfh_a);
PageLifecycleStateManagerTestDelegate delegate(
rfh_a->render_view_host()->GetPageLifecycleStateManager());
EXPECT_TRUE(NavigateToURL(shell(), url_b));
ASSERT_TRUE(delegate.WaitForInBackForwardCacheAck());
ASSERT_FALSE(delete_observer_rfh_a.deleted());
EXPECT_TRUE(rfh_a->IsInBackForwardCache());
mojo::Remote<mojom::Echo> remote;
EchoFakeWithFilter echo(
remote.BindNewPipeAndPassReceiver(),
rfh_a->CreateMessageFilterForAssociatedReceiver(mojom::Echo::Name_));
base::RunLoop loop;
remote->EchoString(
"", base::BindLambdaForTesting([&](const std::string&) { loop.Quit(); }));
loop.Run();
ExpectBucketCount(
"BackForwardCache.UnexpectedRendererToBrowserMessage.InterfaceName",
base::HistogramBase::Sample(
static_cast<int32_t>(base::HashMetricName(mojom::Echo::Name_))),
1);
}
IN_PROC_BROWSER_TEST_F(
BackForwardCacheBrowserTest,
MessageReceivedOnAssociatedInterfaceWhileCachedForProcessWithNonCachedPages) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL url_a(embedded_test_server()->GetURL("/title1.html"));
GURL url_b(embedded_test_server()->GetURL("/title2.html"));
EXPECT_TRUE(NavigateToURL(shell(), url_a));
RenderFrameHostImpl* rfh_a = current_frame_host();
RenderFrameDeletedObserver delete_observer_rfh_a(rfh_a);
PageLifecycleStateManagerTestDelegate delegate(
rfh_a->render_view_host()->GetPageLifecycleStateManager());
EXPECT_TRUE(NavigateToURL(shell(), url_b));
ASSERT_TRUE(delegate.WaitForInBackForwardCacheAck());
RenderFrameHostImpl* rfh_b = current_frame_host();
ASSERT_FALSE(delete_observer_rfh_a.deleted());
EXPECT_TRUE(rfh_a->IsInBackForwardCache());
ASSERT_EQ(rfh_a->GetProcess(), rfh_b->GetProcess());
mojo::Remote<mojom::Echo> remote;
EchoFakeWithFilter echo(
remote.BindNewPipeAndPassReceiver(),
rfh_a->CreateMessageFilterForAssociatedReceiver(mojom::Echo::Name_));
remote->EchoString("", base::NullCallback());
base::RunLoop().RunUntilIdle();
ASSERT_TRUE(HistoryGoBack(web_contents()));
ExpectRestored(FROM_HERE);
}
IN_PROC_BROWSER_TEST_F(
HighCacheSizeBackForwardCacheBrowserTest,
MessageReceivedOnAssociatedInterfaceForProcessWithMultipleCachedPages) {
DoNotFailForUnexpectedMessagesWhileCached();
ASSERT_TRUE(embedded_test_server()->Start());
GURL url_a_1(embedded_test_server()->GetURL("a.com", "/title1.html"));
GURL url_a_2(embedded_test_server()->GetURL("a.com", "/title2.html"));
GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html"));
EXPECT_TRUE(NavigateToURL(shell(), url_a_1));
RenderFrameHostImpl* rfh_a_1 = current_frame_host();
RenderFrameDeletedObserver delete_observer_rfh_a_1(rfh_a_1);
EXPECT_TRUE(NavigateToURL(shell(), url_a_2));
RenderFrameHostImpl* rfh_a_2 = current_frame_host();
RenderFrameDeletedObserver delete_observer_rfh_a_2(rfh_a_2);
EXPECT_TRUE(NavigateToURL(shell(), url_b));
RenderFrameHostImpl* rfh_b = current_frame_host();
RenderFrameDeletedObserver delete_observer_rfh_b(rfh_b);
ASSERT_FALSE(delete_observer_rfh_a_1.deleted());
ASSERT_FALSE(delete_observer_rfh_a_2.deleted());
EXPECT_TRUE(rfh_a_1->IsInBackForwardCache());
EXPECT_TRUE(rfh_a_2->IsInBackForwardCache());
ASSERT_EQ(rfh_a_1->GetProcess(), rfh_a_2->GetProcess());
mojo::Remote<mojom::Echo> remote;
EchoFakeWithFilter echo(
remote.BindNewPipeAndPassReceiver(),
rfh_a_1->CreateMessageFilterForAssociatedReceiver(mojom::Echo::Name_));
base::RunLoop loop;
remote->EchoString(
"", base::BindLambdaForTesting([&](const std::string&) { loop.Quit(); }));
loop.Run();
ExpectBucketCount(
"BackForwardCache.UnexpectedRendererToBrowserMessage.InterfaceName",
base::HistogramBase::Sample(
static_cast<int32_t>(base::HashMetricName(mojom::Echo::Name_))),
1);
EXPECT_FALSE(delete_observer_rfh_b.deleted());
}
IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
MessageReceivedOnAssociatedInterfaceWhileFreezing) {
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"));
url::Origin origin_a = url::Origin::Create(url_a);
url::Origin origin_b = url::Origin::Create(url_b);
EXPECT_TRUE(NavigateToURL(shell(), url_a));
RenderFrameHostImpl* rfh_a = current_frame_host();
RenderFrameDeletedObserver delete_observer_rfh_a(rfh_a);
PageLifecycleStateManagerTestDelegate delegate(
rfh_a->render_view_host()->GetPageLifecycleStateManager());
mojo::Remote<mojom::Echo> remote;
EchoFakeWithFilter echo(
remote.BindNewPipeAndPassReceiver(),
rfh_a->CreateMessageFilterForAssociatedReceiver(mojom::Echo::Name_));
delegate.OnStoreInBackForwardCacheSent(base::BindLambdaForTesting(
[&]() { remote->EchoString("", base::NullCallback()); }));
delegate.OnRestoreFromBackForwardCacheSent(base::BindLambdaForTesting(
[&]() { remote->EchoString("", base::NullCallback()); }));
EXPECT_TRUE(NavigateToURL(shell(), url_b));
ASSERT_TRUE(HistoryGoBack(web_contents()));
ExpectRestored(FROM_HERE);
}
IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
ShouldNotSwapBrowsingInstanceWhenPageWillNotBeCached) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL url_1(embedded_test_server()->GetURL("/title1.html"));
GURL url_2(embedded_test_server()->GetURL("/title2.html"));
GURL url_3(embedded_test_server()->GetURL("/title3.html"));
EXPECT_TRUE(NavigateToURL(shell(), url_1));
RenderFrameHostImpl* rfh_1 = current_frame_host();
scoped_refptr<SiteInstanceImpl> site_instance_1 =
static_cast<SiteInstanceImpl*>(rfh_1->GetSiteInstance());
EXPECT_TRUE(NavigateToURL(shell(), url_2));
RenderFrameHostImpl* rfh_2 = current_frame_host();
RenderFrameDeletedObserver rfh_2_deleted_observer(rfh_2);
scoped_refptr<SiteInstanceImpl> site_instance_2 =
static_cast<SiteInstanceImpl*>(rfh_2->GetSiteInstance());
EXPECT_TRUE(rfh_1->IsInBackForwardCache());
EXPECT_FALSE(site_instance_1->IsRelatedSiteInstance(site_instance_2.get()));
DisableBFCacheForRFHForTesting(rfh_2->GetGlobalId());
EXPECT_TRUE(NavigateToURL(shell(), url_3));
RenderFrameHostImpl* rfh_3 = current_frame_host();
scoped_refptr<SiteInstanceImpl> site_instance_3 =
static_cast<SiteInstanceImpl*>(rfh_3->GetSiteInstance());
EXPECT_EQ(site_instance_2, site_instance_3);
if (rfh_2 != rfh_3) {
rfh_2_deleted_observer.WaitUntilDeleted();
}
}
IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
RendererInitiatedSameSiteNavigationReusesProcess) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL url_1(embedded_test_server()->GetURL("/title1.html"));
GURL url_2(embedded_test_server()->GetURL("/title2.html"));
EXPECT_TRUE(NavigateToURL(shell(), url_1));
scoped_refptr<SiteInstanceImpl> site_instance_1 =
web_contents()->GetPrimaryMainFrame()->GetSiteInstance();
EXPECT_TRUE(NavigateToURLFromRenderer(shell(), url_2));
scoped_refptr<SiteInstanceImpl> site_instance_2 =
web_contents()->GetPrimaryMainFrame()->GetSiteInstance();
EXPECT_FALSE(site_instance_1->IsRelatedSiteInstance(site_instance_2.get()));
EXPECT_EQ(site_instance_1->GetProcess(), site_instance_2->GetProcess());
}
IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
BrowserInitiatedSameSiteNavigationReusesProcess) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL url_1(embedded_test_server()->GetURL("/title1.html"));
GURL url_2(embedded_test_server()->GetURL("/title2.html"));
EXPECT_TRUE(NavigateToURL(shell(), url_1));
scoped_refptr<SiteInstanceImpl> site_instance_1 =
web_contents()->GetPrimaryMainFrame()->GetSiteInstance();
EXPECT_TRUE(NavigateToURL(shell(), url_2));
scoped_refptr<SiteInstanceImpl> site_instance_2 =
web_contents()->GetPrimaryMainFrame()->GetSiteInstance();
EXPECT_FALSE(site_instance_1->IsRelatedSiteInstance(site_instance_2.get()));
EXPECT_EQ(site_instance_1->GetProcess(), site_instance_2->GetProcess());
ASSERT_TRUE(HistoryGoBack(web_contents()));
EXPECT_EQ(web_contents()->GetLastCommittedURL(), url_1);
scoped_refptr<SiteInstanceImpl> site_instance_1_history_nav =
web_contents()->GetPrimaryMainFrame()->GetSiteInstance();
EXPECT_EQ(site_instance_1_history_nav, site_instance_1);
EXPECT_EQ(site_instance_1_history_nav->GetProcess(),
site_instance_1->GetProcess());
}
IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
CrossSiteNavigationDoesNotReuseProcess) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL a1_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
GURL b_url(embedded_test_server()->GetURL("b.com", "/title1.html"));
GURL a2_url(embedded_test_server()->GetURL("a.com", "/title2.html"));
EXPECT_TRUE(NavigateToURL(shell(), a1_url));
scoped_refptr<SiteInstanceImpl> a1_site_instance =
web_contents()->GetPrimaryMainFrame()->GetSiteInstance();
EXPECT_TRUE(NavigateToURL(shell(), b_url));
scoped_refptr<SiteInstanceImpl> b_site_instance =
web_contents()->GetPrimaryMainFrame()->GetSiteInstance();
EXPECT_FALSE(a1_site_instance->IsRelatedSiteInstance(b_site_instance.get()));
EXPECT_NE(a1_site_instance->GetProcess(), b_site_instance->GetProcess());
EXPECT_TRUE(NavigateToURLFromRenderer(shell(), a2_url));
scoped_refptr<SiteInstanceImpl> a2_site_instance =
web_contents()->GetPrimaryMainFrame()->GetSiteInstance();
EXPECT_FALSE(b_site_instance->IsRelatedSiteInstance(a2_site_instance.get()));
EXPECT_NE(b_site_instance->GetProcess(), a2_site_instance->GetProcess());
}
class RenderViewHostDeletedObserver : public WebContentsObserver {
public:
explicit RenderViewHostDeletedObserver(RenderViewHost* rvh)
: WebContentsObserver(WebContents::FromRenderViewHost(rvh)),
render_view_host_(rvh),
deleted_(false) {}
void RenderViewDeleted(RenderViewHost* render_view_host) override {
if (render_view_host_ == render_view_host)
deleted_ = true;
}
bool deleted() const { return deleted_; }
private:
raw_ptr<RenderViewHost, DanglingUntriaged> render_view_host_;
bool deleted_;
};
IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
RenderViewHostDeletedOnEviction) {
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"));
NavigationControllerImpl& controller = web_contents()->GetController();
BackForwardCacheImpl& cache = controller.GetBackForwardCache();
EXPECT_TRUE(NavigateToURL(shell(), url_a));
RenderFrameHostImpl* rfh_a = current_frame_host();
RenderFrameDeletedObserver delete_observer_rfh_a(rfh_a);
RenderViewHostDeletedObserver delete_observer_rvh_a(
rfh_a->GetRenderViewHost());
RenderProcessHost* process = rfh_a->GetProcess();
RenderProcessHostWatcher destruction_observer(
process, RenderProcessHostWatcher::WATCH_FOR_HOST_DESTRUCTION);
cache.Flush();
EXPECT_TRUE(NavigateToURL(shell(), url_b));
EXPECT_TRUE(rfh_a->IsInBackForwardCache());
EXPECT_EQ(1u, cache.GetEntries().size());
EXPECT_TRUE(rfh_a->IsInactiveAndDisallowActivation(
DisallowActivationReasonId::kForTesting));
destruction_observer.Wait();
ASSERT_TRUE(delete_observer_rvh_a.deleted());
delete_observer_rfh_a.WaitUntilDeleted();
EXPECT_EQ(0u, cache.GetEntries().size());
}
IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
CrossProcessSubFrameRenderViewHostDeletedOnEviction) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL url_a(embedded_test_server()->GetURL(
"a.com", "/cross_site_iframe_factory.html?a(b)"));
GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html"));
EXPECT_TRUE(NavigateToURL(shell(), url_a));
RenderFrameHostImpl* a1 = current_frame_host();
RenderFrameHostImpl* b1 = a1->child_at(0)->current_frame_host();
RenderFrameDeletedObserver delete_observer_rfh_b1(b1);
RenderViewHostDeletedObserver delete_observer_rvh_b1(b1->GetRenderViewHost());
RenderProcessHost* process = b1->GetProcess();
RenderProcessHostWatcher destruction_observer(
process, RenderProcessHostWatcher::WATCH_FOR_HOST_DESTRUCTION);
EXPECT_TRUE(NavigateToURL(shell(), url_b));
EXPECT_TRUE(a1->IsInBackForwardCache());
EXPECT_TRUE(a1->IsInactiveAndDisallowActivation(
DisallowActivationReasonId::kForTesting));
destruction_observer.Wait();
ASSERT_TRUE(delete_observer_rvh_b1.deleted());
delete_observer_rfh_b1.WaitUntilDeleted();
}
IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
SameProcessSubFrameRenderViewHostDeletedOnEviction) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL url_a(embedded_test_server()->GetURL(
"a.com", "/cross_site_iframe_factory.html?a(a)"));
GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html"));
EXPECT_TRUE(NavigateToURL(shell(), url_a));
RenderFrameHostImpl* a1 = current_frame_host();
RenderFrameHostImpl* a2 = a1->child_at(0)->current_frame_host();
RenderFrameDeletedObserver delete_observer_rfh_a2(a2);
RenderViewHostDeletedObserver delete_observer_rvh_a2(a2->GetRenderViewHost());
RenderProcessHost* process = a2->GetProcess();
RenderProcessHostWatcher destruction_observer(
process, RenderProcessHostWatcher::WATCH_FOR_HOST_DESTRUCTION);
EXPECT_TRUE(NavigateToURL(shell(), url_b));
EXPECT_TRUE(a1->IsInBackForwardCache());
EXPECT_TRUE(a1->IsInactiveAndDisallowActivation(
DisallowActivationReasonId::kForTesting));
destruction_observer.Wait();
ASSERT_TRUE(delete_observer_rvh_a2.deleted());
delete_observer_rfh_a2.WaitUntilDeleted();
}
IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
NavigationCancelledAfterJsEvictionWasDisabled) {
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"));
EXPECT_TRUE(NavigateToURL(shell(), url_a));
RenderFrameHostImpl* rfh_a = current_frame_host();
RenderFrameDeletedObserver delete_observer_rfh_a(rfh_a);
PageLifecycleStateManagerTestDelegate delegate(
rfh_a->render_view_host()->GetPageLifecycleStateManager());
EXPECT_TRUE(NavigateToURL(shell(), url_b));
RenderFrameHostImpl* rfh_b = current_frame_host();
delegate.OnDisableJsEvictionSent(base::BindLambdaForTesting([&]() {
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(&WebContentsImpl::Stop,
base::Unretained(web_contents())));
}));
ASSERT_TRUE(HistoryGoBack(web_contents()));
EXPECT_EQ(rfh_b, current_frame_host());
delete_observer_rfh_a.WaitUntilDeleted();
ASSERT_TRUE(HistoryGoBack(web_contents()));
ExpectNotRestored({NotRestoredReason::kNavigationCancelledWhileRestoring}, {},
{}, {}, {}, FROM_HERE);
}
IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
SubframeNavigationDoesNotRecordMetrics) {
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"));
EXPECT_TRUE(NavigateToURL(shell(), url_a));
RenderFrameHostImpl* rfh_a = current_frame_host();
EXPECT_TRUE(NavigateFrameToURL(rfh_a->child_at(0), url_c));
EXPECT_EQ(url_c,
rfh_a->child_at(0)->current_frame_host()->GetLastCommittedURL());
EXPECT_FALSE(rfh_a->IsInBackForwardCache());
ASSERT_TRUE(HistoryGoBack(web_contents()));
EXPECT_TRUE(
rfh_a->child_at(0)->current_frame_host()->GetLastCommittedURL().DomainIs(
"b.com"));
EXPECT_FALSE(rfh_a->IsInBackForwardCache());
ExpectOutcomeDidNotChange(FROM_HERE);
}
IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
EnsureIsolationInfoForSubresourcesNotEmpty) {
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"));
BackForwardCacheImpl& cache =
web_contents()->GetController().GetBackForwardCache();
EXPECT_TRUE(NavigateToURL(shell(), url_a));
RenderFrameHostImpl* rfh_a = current_frame_host();
cache.Flush();
EXPECT_TRUE(NavigateToURL(shell(), url_b));
RenderFrameHostImpl* rfh_b = current_frame_host();
EXPECT_TRUE(rfh_a->IsInBackForwardCache());
EXPECT_EQ(1u, cache.GetEntries().size());
ASSERT_TRUE(HistoryGoBack(web_contents()));
EXPECT_EQ(rfh_a, current_frame_host());
EXPECT_TRUE(rfh_b->IsInBackForwardCache());
EXPECT_EQ(1u, cache.GetEntries().size());
EXPECT_FALSE(rfh_a->GetIsolationInfoForSubresources().IsEmpty());
ASSERT_TRUE(HistoryGoForward(web_contents()));
EXPECT_EQ(rfh_b, current_frame_host());
EXPECT_TRUE(rfh_a->IsInBackForwardCache());
EXPECT_EQ(1u, cache.GetEntries().size());
EXPECT_FALSE(rfh_b->GetIsolationInfoForSubresources().IsEmpty());
}
IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
DoNotRestoreWhenIsOverridingUserAgentDiffers) {
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"));
GURL url_c(embedded_test_server()->GetURL("c.com", "/title1.html"));
NavigationControllerImpl& controller = web_contents()->GetController();
FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
->GetPrimaryFrameTree()
.root();
const std::string user_agent_override = "foo";
{
FrameNavigateParamsCapturer params_capturer(root);
EXPECT_TRUE(NavigateToURL(shell(), url_a));
params_capturer.Wait();
EXPECT_FALSE(params_capturer.is_overriding_user_agent());
EXPECT_NE(user_agent_override,
EvalJs(shell()->web_contents(), "navigator.userAgent"));
}
RenderFrameHostImpl* rfh_a = current_frame_host();
UserAgentInjector injector(shell()->web_contents(), user_agent_override);
{
FrameNavigateParamsCapturer params_capturer(root);
EXPECT_TRUE(NavigateToURL(shell(), url_b));
params_capturer.Wait();
EXPECT_TRUE(params_capturer.is_overriding_user_agent());
EXPECT_EQ(user_agent_override,
EvalJs(shell()->web_contents(), "navigator.userAgent"));
}
EXPECT_TRUE(rfh_a->IsInBackForwardCache());
RenderFrameHostImpl* rfh_b = current_frame_host();
{
RenderFrameDeletedObserver delete_observer(rfh_a);
FrameNavigateParamsCapturer params_capturer(root);
controller.GoBack();
params_capturer.Wait();
delete_observer.WaitUntilDeleted();
EXPECT_TRUE(params_capturer.is_overriding_user_agent());
EXPECT_EQ(user_agent_override,
EvalJs(shell()->web_contents(), "navigator.userAgent"));
ExpectNotRestored({NotRestoredReason::kUserAgentOverrideDiffers}, {}, {},
{}, {}, FROM_HERE);
}
EXPECT_TRUE(rfh_b->IsInBackForwardCache());
{
FrameNavigateParamsCapturer params_capturer(root);
controller.GoForward();
params_capturer.Wait();
EXPECT_TRUE(params_capturer.is_overriding_user_agent());
EXPECT_EQ(user_agent_override,
EvalJs(shell()->web_contents(), "navigator.userAgent"));
EXPECT_EQ(rfh_b, current_frame_host());
ExpectRestored(FROM_HERE);
}
injector.set_is_overriding_user_agent(false);
{
FrameNavigateParamsCapturer params_capturer(root);
EXPECT_TRUE(NavigateToURL(shell(), url_c));
params_capturer.Wait();
EXPECT_FALSE(params_capturer.is_overriding_user_agent());
EXPECT_NE(user_agent_override,
EvalJs(shell()->web_contents(), "navigator.userAgent"));
}
EXPECT_TRUE(rfh_b->IsInBackForwardCache());
{
FrameNavigateParamsCapturer params_capturer(root);
RenderFrameDeletedObserver delete_observer(rfh_b);
controller.GoBack();
params_capturer.Wait();
delete_observer.WaitUntilDeleted();
EXPECT_FALSE(params_capturer.is_overriding_user_agent());
EXPECT_NE(user_agent_override,
EvalJs(shell()->web_contents(), "navigator.userAgent"));
ExpectNotRestored({NotRestoredReason::kUserAgentOverrideDiffers}, {}, {},
{}, {}, FROM_HERE);
}
}
IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
RestoreWhenUserAgentOverrideDiffers) {
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"));
NavigationControllerImpl& controller = web_contents()->GetController();
FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
->GetPrimaryFrameTree()
.root();
const std::string user_agent_override_1 = "foo";
UserAgentInjector injector(shell()->web_contents(), user_agent_override_1);
{
FrameNavigateParamsCapturer params_capturer(root);
EXPECT_TRUE(NavigateToURL(shell(), url_a));
params_capturer.Wait();
EXPECT_TRUE(params_capturer.is_overriding_user_agent());
EXPECT_EQ(user_agent_override_1,
EvalJs(shell()->web_contents(), "navigator.userAgent"));
}
RenderFrameHostImpl* rfh_a = current_frame_host();
EXPECT_TRUE(NavigateToURL(shell(), url_b));
EXPECT_TRUE(rfh_a->IsInBackForwardCache());
const std::string user_agent_override_2 = "bar";
injector.set_user_agent_override(user_agent_override_2);
{
FrameNavigateParamsCapturer params_capturer(root);
controller.GoBack();
params_capturer.Wait();
EXPECT_TRUE(params_capturer.is_overriding_user_agent());
EXPECT_EQ(user_agent_override_1,
EvalJs(shell()->web_contents(), "navigator.userAgent"));
EXPECT_EQ(rfh_a, current_frame_host());
ExpectRestored(FROM_HERE);
}
{
FrameNavigateParamsCapturer params_capturer(root);
EXPECT_TRUE(NavigateToURL(shell(), url_b));
params_capturer.Wait();
EXPECT_TRUE(params_capturer.is_overriding_user_agent());
EXPECT_EQ(user_agent_override_2,
EvalJs(shell()->web_contents(), "navigator.userAgent"));
}
}
IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
WebContentsDestroyedWhileRestoringThePageFromBFCache) {
ASSERT_TRUE(embedded_test_server()->Start());
Shell* shell = CreateBrowser();
GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html"));
GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html"));
EXPECT_TRUE(NavigateToURL(shell, url_a));
EXPECT_TRUE(NavigateToURL(shell, url_b));
TestActivationManager activation_manager(shell->web_contents(), url_a);
shell->web_contents()->GetController().GoBack();
EXPECT_TRUE(activation_manager.WaitForBeforeChecks());
::testing::NiceMock<MockWebContentsObserver> observer(shell->web_contents());
EXPECT_CALL(observer, DidFinishNavigation(_))
.WillOnce(::testing::Invoke([](NavigationHandle* handle) {
EXPECT_FALSE(handle->HasCommitted());
EXPECT_TRUE(handle->IsServedFromBackForwardCache());
EXPECT_TRUE(NavigationRequest::From(handle)
->rfh_restored_from_back_forward_cache()
->GetRoutingID());
}));
shell->Close();
}
IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
DelegateDoesNotSupportBackForwardCache) {
web_contents()->SetDelegate(nullptr);
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"));
EXPECT_TRUE(NavigateToURL(shell(), url_a));
RenderFrameHostImpl* rfh_a = current_frame_host();
RenderFrameDeletedObserver delete_observer_rfh_a(rfh_a);
EXPECT_TRUE(NavigateToURL(shell(), url_b));
RenderFrameHostImpl* rfh_b = current_frame_host();
RenderFrameDeletedObserver delete_observer_rfh_b(rfh_b);
ASSERT_TRUE(HistoryGoToOffset(web_contents(), -1));
ExpectNotRestored({NotRestoredReason::kBackForwardCacheDisabledForDelegate},
{}, {}, {}, {}, FROM_HERE);
}
IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, NoThrottlesOnCacheRestore) {
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"));
ASSERT_TRUE(NavigateToURL(shell(), url_a));
RenderFrameHostImpl* rfh_a = current_frame_host();
RenderFrameDeletedObserver delete_observer_rfh_a(rfh_a);
bool did_register_throttles = false;
content::ShellContentBrowserClient::Get()
->set_create_throttles_for_navigation_callback(base::BindLambdaForTesting(
[&did_register_throttles](content::NavigationHandle* handle)
-> std::vector<std::unique_ptr<content::NavigationThrottle>> {
did_register_throttles = true;
return std::vector<std::unique_ptr<content::NavigationThrottle>>();
}));
ASSERT_TRUE(NavigateToURL(shell(), url_b));
RenderFrameHostImpl* rfh_b = current_frame_host();
RenderFrameDeletedObserver delete_observer_rfh_b(rfh_b);
ASSERT_FALSE(delete_observer_rfh_a.deleted());
ASSERT_TRUE(rfh_a->IsInBackForwardCache());
EXPECT_TRUE(did_register_throttles);
did_register_throttles = false;
ASSERT_TRUE(HistoryGoBack(web_contents()));
EXPECT_FALSE(did_register_throttles);
ExpectRestored(FROM_HERE);
}
IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
BackNavigationFromCrashedPage) {
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"));
url::Origin origin_a = url::Origin::Create(url_a);
url::Origin origin_b = url::Origin::Create(url_b);
EXPECT_TRUE(NavigateToURL(shell(), url_a));
RenderFrameHostImpl* rfh_a = current_frame_host();
RenderFrameDeletedObserver delete_observer_rfh_a(rfh_a);
EXPECT_FALSE(web_contents()->IsCrashed());
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->IsInBackForwardCache());
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->IsInBackForwardCache());
EXPECT_EQ(rfh_b->GetVisibilityState(), PageVisibilityState::kVisible);
EXPECT_FALSE(web_contents()->IsCrashed());
CrashTab(web_contents());
EXPECT_TRUE(web_contents()->IsCrashed());
EXPECT_TRUE(delete_observer_rfh_b.deleted());
ASSERT_TRUE(HistoryGoBack(web_contents()));
EXPECT_FALSE(delete_observer_rfh_a.deleted());
EXPECT_EQ(origin_a, rfh_a->GetLastCommittedOrigin());
EXPECT_EQ(rfh_a, current_frame_host());
EXPECT_FALSE(rfh_a->IsInBackForwardCache());
EXPECT_EQ(rfh_a->GetVisibilityState(), PageVisibilityState::kVisible);
EXPECT_FALSE(web_contents()->IsCrashed());
ExpectRestored(FROM_HERE);
}
class InjectCreateChildFrame : public DidCommitNavigationInterceptor {
public:
InjectCreateChildFrame(WebContents* web_contents, const GURL& url)
: DidCommitNavigationInterceptor(web_contents), url_(url) {}
InjectCreateChildFrame(const InjectCreateChildFrame&) = delete;
InjectCreateChildFrame& operator=(const InjectCreateChildFrame&) = delete;
bool was_called() { return was_called_; }
private:
bool WillProcessDidCommitNavigation(
RenderFrameHost* render_frame_host,
NavigationRequest* navigation_request,
mojom::DidCommitProvisionalLoadParamsPtr*,
mojom::DidCommitProvisionalLoadInterfaceParamsPtr* interface_params)
override {
if (!was_called_ && navigation_request &&
navigation_request->GetURL() == url_) {
EXPECT_TRUE(ExecuteScript(
web_contents(),
"document.body.appendChild(document.createElement('iframe'));"));
}
was_called_ = true;
return true;
}
bool was_called_ = false;
GURL url_;
};
IN_PROC_BROWSER_TEST_F(
BackForwardCacheBrowserTest,
InjectSubframeDuringPendingCrossBrowsingInstanceNavigation) {
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", "/title2.html"));
ASSERT_TRUE(NavigateToURL(shell(), url_a));
RenderFrameHostImplWrapper rfh_a(current_frame_host());
EXPECT_EQ(0U, rfh_a->child_count());
{
InjectCreateChildFrame injector(shell()->web_contents(), url_b);
TestNavigationObserver navigation_observer(shell()->web_contents(), 1);
shell()->LoadURL(url_b);
navigation_observer.Wait();
EXPECT_EQ(url_b, shell()->web_contents()->GetLastCommittedURL());
EXPECT_TRUE(injector.was_called());
}
EXPECT_TRUE(rfh_a->IsInBackForwardCache());
ASSERT_EQ(1U, rfh_a->child_count());
EXPECT_TRUE(rfh_a->child_at(0)
->render_manager()
->GetAllProxyHostsForTesting()
.empty());
RenderFrameHostImplWrapper rfh_b(current_frame_host());
ASSERT_TRUE(HistoryGoBack(web_contents()));
EXPECT_EQ(rfh_a.get(), current_frame_host());
EXPECT_TRUE(rfh_b->IsInBackForwardCache());
RenderFrameHostCreatedObserver frame_observer(shell()->web_contents(), 1);
EXPECT_TRUE(ExecuteScript(
rfh_a->child_at(0),
"document.body.appendChild(document.createElement('iframe'));"));
frame_observer.Wait();
EXPECT_EQ(1U, rfh_a->child_at(0)->child_count());
EXPECT_TRUE(ExecuteScript(rfh_a->child_at(0)->child_at(0), "true"));
}
class BackForwardCacheBrowserTestWithFlagForScreenReader
: public BackForwardCacheBrowserTest,
public ::testing::WithParamInterface<bool> {
protected:
void SetUpCommandLine(base::CommandLine* command_line) override {
if (IsBackForwardCacheEnabledForScreenReader()) {
EnableFeatureAndSetParams(
features::kEnableBackForwardCacheForScreenReader, "", "true");
} else {
DisableFeature(features::kEnableBackForwardCacheForScreenReader);
}
BackForwardCacheBrowserTest::SetUpCommandLine(command_line);
}
bool IsBackForwardCacheEnabledForScreenReader() { return GetParam(); }
};
INSTANTIATE_TEST_SUITE_P(All,
BackForwardCacheBrowserTestWithFlagForScreenReader,
::testing::Bool());
IN_PROC_BROWSER_TEST_P(BackForwardCacheBrowserTestWithFlagForScreenReader,
ScreenReaderOn) {
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"));
BackForwardCacheDisabledTester tester;
EnableAccessibilityForWebContents(shell()->web_contents());
EXPECT_TRUE(NavigateToURL(shell(), url_a));
RenderFrameHostImplWrapper rfh_a(current_frame_host());
int process_id = current_frame_host()->GetProcess()->GetID();
int routing_id = current_frame_host()->GetRoutingID();
EXPECT_TRUE(NavigateToURL(shell(), url_b));
if (IsBackForwardCacheEnabledForScreenReader()) {
EXPECT_TRUE(rfh_a.get());
EXPECT_TRUE(rfh_a->IsInBackForwardCache());
ASSERT_TRUE(HistoryGoBack(web_contents()));
ExpectRestored(FROM_HERE);
} else {
EXPECT_TRUE(rfh_a.WaitUntilRenderFrameDeleted());
ASSERT_TRUE(HistoryGoBack(web_contents()));
auto reason = BackForwardCacheDisable::DisabledReason(
BackForwardCacheDisable::DisabledReasonId::kScreenReader);
ExpectNotRestored({NotRestoredReason::kDisableForRenderFrameHostCalled}, {},
{}, {reason}, {}, FROM_HERE);
EXPECT_TRUE(
tester.IsDisabledForFrameWithReason(process_id, routing_id, reason));
}
}
class BackForwardCacheBrowserTestWithFlagForAXEvents
: public BackForwardCacheBrowserTest,
public ::testing::WithParamInterface<bool> {
protected:
void SetUpCommandLine(base::CommandLine* command_line) override {
EnableFeatureAndSetParams(features::kEnableBackForwardCacheForScreenReader,
"", "true");
if (ShouldEvictOnAXEvents()) {
EnableFeatureAndSetParams(features::kEvictOnAXEvents, "", "true");
} else {
DisableFeature(features::kEvictOnAXEvents);
}
BackForwardCacheBrowserTest::SetUpCommandLine(command_line);
}
bool ShouldEvictOnAXEvents() { return GetParam(); }
};
INSTANTIATE_TEST_SUITE_P(All,
BackForwardCacheBrowserTestWithFlagForAXEvents,
::testing::Bool());
IN_PROC_BROWSER_TEST_P(BackForwardCacheBrowserTestWithFlagForAXEvents,
EvictOnAccessibilityEventsOrNot) {
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"));
EXPECT_TRUE(NavigateToURL(shell()->web_contents(), url_a));
RenderFrameHostImplWrapper rfh_a(current_frame_host());
EnableAccessibilityForWebContents(shell()->web_contents());
AccessibilityNotificationWaiter waiter_complete(
shell()->web_contents(), ui::kAXModeComplete,
ax::mojom::Event::kLoadComplete);
ASSERT_TRUE(waiter_complete.WaitForNotification());
EXPECT_TRUE(NavigateToURL(shell()->web_contents(), url_b));
RenderFrameHostImplWrapper rfh_b(current_frame_host());
EXPECT_TRUE(rfh_a.get());
EXPECT_TRUE(rfh_a->IsInBackForwardCache());
BrowserAccessibilityManager* manager =
rfh_a->GetOrCreateBrowserAccessibilityManager();
manager->SetGeneratedEventCallbackForTesting(
base::BindRepeating([](RenderFrameHostImpl* render_frame_host,
ui::AXEventGenerator::Event event,
ui::AXNodeID event_target_id) { FAIL(); }));
blink::mojom::AXUpdatesAndEventsPtr updates_and_events =
blink::mojom::AXUpdatesAndEvents::New();
ui::AXTreeUpdate update;
update.root_id = 1;
updates_and_events->updates.emplace_back(update);
updates_and_events->events.emplace_back(
0, ax::mojom::Event::kChildrenChanged);
rfh_a->HandleAXEventsForTests(rfh_a->GetAXTreeID(),
std::move(updates_and_events),
0);
manager->SetGeneratedEventCallbackForTesting(
GeneratedEventCallbackForTesting());
ASSERT_TRUE(HistoryGoBack(web_contents()));
if (ShouldEvictOnAXEvents()) {
const uint64_t reason = DisallowActivationReasonId::kAXEvent;
ExpectNotRestored({NotRestoredReason::kIgnoreEventAndEvict}, {}, {}, {},
{reason}, FROM_HERE);
} else {
AccessibilityNotificationWaiter waiter_start(shell()->web_contents(),
ui::kAXModeComplete,
ax::mojom::Event::kLoadStart);
EXPECT_EQ(current_frame_host(), rfh_a.get());
ExpectRestored(FROM_HERE);
ASSERT_TRUE(waiter_start.WaitForNotification());
auto* waiter_start_rfhi = static_cast<RenderFrameHostImpl*>(
waiter_start.event_browser_accessibility_manager()->delegate());
EXPECT_EQ(waiter_start_rfhi, rfh_a.get());
}
}
class BackgroundForegroundProcessLimitBackForwardCacheBrowserTest
: public BackForwardCacheBrowserTest {
protected:
void SetUpCommandLine(base::CommandLine* command_line) override {
EnableFeatureAndSetParams(features::kBackForwardCache, "cache_size",
base::NumberToString(kBackForwardCacheSize));
EnableFeatureAndSetParams(
features::kBackForwardCache, "foreground_cache_size",
base::NumberToString(kForegroundBackForwardCacheSize));
BackForwardCacheBrowserTest::SetUpCommandLine(command_line);
}
void ExpectCached(const RenderFrameHostImplWrapper& rfh,
bool cached,
bool backgrounded) {
EXPECT_FALSE(rfh.IsDestroyed());
EXPECT_EQ(cached, rfh->IsInBackForwardCache());
EXPECT_EQ(backgrounded, rfh->GetProcess()->IsProcessBackgrounded());
}
const size_t kBackForwardCacheSize = 4;
const size_t kForegroundBackForwardCacheSize = 2;
};
IN_PROC_BROWSER_TEST_F(
BackgroundForegroundProcessLimitBackForwardCacheBrowserTest,
CacheEvictionSameSite) {
ASSERT_TRUE(embedded_test_server()->Start());
std::vector<RenderFrameHostImplWrapper> rfhs;
for (size_t i = 0; i <= kBackForwardCacheSize * 2; ++i) {
SCOPED_TRACE(i);
GURL url(embedded_test_server()->GetURL(
"a.com", base::StringPrintf("/title1.html?i=%zu", i)));
ASSERT_TRUE(NavigateToURL(shell(), url));
rfhs.emplace_back(current_frame_host());
EXPECT_FALSE(rfhs.back()->GetProcess()->IsProcessBackgrounded());
for (size_t j = 0; j <= i; ++j) {
SCOPED_TRACE(j);
if (i - j <= kForegroundBackForwardCacheSize) {
ExpectCached(rfhs[j], i != j,
false);
} else {
ASSERT_TRUE(rfhs[j].WaitUntilRenderFrameDeleted());
}
}
}
for (size_t i = 0; i <= kBackForwardCacheSize * 2 - 1; ++i) {
SCOPED_TRACE(i);
ASSERT_TRUE(HistoryGoBack(web_contents()));
if (i < kForegroundBackForwardCacheSize) {
ExpectRestored(FROM_HERE);
} else {
ExpectNotRestored({NotRestoredReason::kForegroundCacheLimit}, {}, {}, {},
{}, FROM_HERE);
}
}
}
IN_PROC_BROWSER_TEST_F(
BackgroundForegroundProcessLimitBackForwardCacheBrowserTest,
CacheEvictionCrossSite) {
ASSERT_TRUE(embedded_test_server()->Start());
std::vector<RenderFrameHostImplWrapper> rfhs;
for (size_t i = 0; i <= kBackForwardCacheSize * 2; ++i) {
SCOPED_TRACE(i);
GURL url(embedded_test_server()->GetURL(base::StringPrintf("a%zu.test", i),
"/title1.html"));
ASSERT_TRUE(NavigateToURL(shell(), url));
rfhs.emplace_back(current_frame_host());
EXPECT_FALSE(rfhs.back()->GetProcess()->IsProcessBackgrounded());
for (size_t j = 0; j <= i; ++j) {
SCOPED_TRACE(j);
if (i - j <= kBackForwardCacheSize) {
EXPECT_FALSE(rfhs[j].IsDestroyed());
ExpectCached(rfhs[j], i != j,
i != j);
} else {
ASSERT_TRUE(rfhs[j].WaitUntilRenderFrameDeleted());
}
}
}
for (size_t i = 0; i <= kBackForwardCacheSize * 2 - 1; ++i) {
SCOPED_TRACE(i);
ASSERT_TRUE(HistoryGoBack(web_contents()));
if (i < kBackForwardCacheSize) {
ExpectRestored(FROM_HERE);
} else {
ExpectNotRestored({NotRestoredReason::kCacheLimit}, {}, {}, {}, {},
FROM_HERE);
}
}
}
IN_PROC_BROWSER_TEST_F(
BackgroundForegroundProcessLimitBackForwardCacheBrowserTest,
ChangeToForeground) {
ASSERT_TRUE(embedded_test_server()->Start());
std::vector<RenderFrameHostImplWrapper> rfhs;
for (size_t i = 0; i < kBackForwardCacheSize; ++i) {
SCOPED_TRACE(i);
GURL url(embedded_test_server()->GetURL(base::StringPrintf("a%zu.test", i),
"/title1.html"));
ASSERT_TRUE(NavigateToURL(shell(), url));
rfhs.emplace_back(current_frame_host());
EXPECT_FALSE(rfhs.back()->GetProcess()->IsProcessBackgrounded());
}
for (size_t i = 0; i < kBackForwardCacheSize - 1; ++i) {
SCOPED_TRACE(i);
ExpectCached(rfhs[i], true, true);
}
GURL url(embedded_test_server()->GetURL("a4.test", "/title1.html"));
ASSERT_TRUE(NavigateToURL(shell(), url));
RenderFrameHostImpl* rfh = current_frame_host();
ASSERT_FALSE(rfh->GetProcess()->IsProcessBackgrounded());
rfhs[1]->GetProcess()->SetPriorityOverride(
true);
rfhs[2]->GetProcess()->SetPriorityOverride(
true);
rfhs[3]->GetProcess()->SetPriorityOverride(
true);
ASSERT_TRUE(rfhs[1].WaitUntilRenderFrameDeleted());
ExpectCached(rfhs[0], true, true);
ExpectCached(rfhs[2], true, false);
ExpectCached(rfhs[3], true, false);
}
IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, TestTimeToLiveParameter) {
scoped_refptr<base::TestMockTimeTaskRunner> task_runner =
base::MakeRefCounted<base::TestMockTimeTaskRunner>();
web_contents()->GetController().GetBackForwardCache().SetTaskRunnerForTesting(
task_runner);
base::TimeDelta time_to_live_in_back_forward_cache =
BackForwardCacheImpl::GetTimeToLiveInBackForwardCache();
EXPECT_EQ(time_to_live_in_back_forward_cache, base::Seconds(3600));
base::TimeDelta delta = base::Milliseconds(1);
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"));
EXPECT_TRUE(NavigateToURL(shell(), url_a));
RenderFrameHostImplWrapper rfh_a(current_frame_host());
EXPECT_TRUE(NavigateToURL(shell(), url_b));
RenderFrameHostImplWrapper rfh_b(current_frame_host());
task_runner->FastForwardBy(time_to_live_in_back_forward_cache - delta);
ASSERT_FALSE(rfh_a.IsDestroyed());
EXPECT_TRUE(rfh_a->IsInBackForwardCache());
task_runner->FastForwardBy(delta);
ASSERT_TRUE(rfh_a.WaitUntilRenderFrameDeleted());
EXPECT_EQ(current_frame_host(), rfh_b.get());
ASSERT_TRUE(HistoryGoBack(web_contents()));
ExpectNotRestored({BackForwardCacheMetrics::NotRestoredReason::kTimeout}, {},
{}, {}, {}, FROM_HERE);
}
IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
ErrorDocumentNotCachedWithSecondError) {
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"));
ASSERT_TRUE(NavigateToURL(web_contents(), url_a));
NavigateAndBlock(url_b, 0);
RenderFrameHostImplWrapper rfh_b(current_frame_host());
ASSERT_TRUE(HistoryGoBack(web_contents()));
ExpectRestored(FROM_HERE);
ASSERT_TRUE(rfh_b.WaitUntilRenderFrameDeleted());
NavigateAndBlock(url_b, 1);
ExpectNotRestored(
{NotRestoredReason::kHTTPStatusNotOK, NotRestoredReason::kNoResponseHead,
NotRestoredReason::kErrorDocument},
{}, {}, {}, {}, FROM_HERE);
}
IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
ErrorDocumentNotCachedWithoutSecondError) {
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"));
ASSERT_TRUE(NavigateToURL(web_contents(), url_a));
NavigateAndBlock(url_b, 0);
RenderFrameHostImplWrapper rfh_b(current_frame_host());
int history_entry_id =
web_contents()->GetController().GetLastCommittedEntry()->GetUniqueID();
ASSERT_TRUE(HistoryGoBack(web_contents()));
ASSERT_TRUE(rfh_b.WaitUntilRenderFrameDeleted());
ExpectRestored(FROM_HERE);
ASSERT_TRUE(HistoryGoForward(web_contents()));
ASSERT_NE(
history_entry_id,
web_contents()->GetController().GetLastCommittedEntry()->GetUniqueID());
ExpectNotRestored(
{NotRestoredReason::kHTTPStatusNotOK, NotRestoredReason::kNoResponseHead,
NotRestoredReason::kErrorDocument},
{}, {}, {}, {}, FROM_HERE);
}
class BackForwardCacheBrowserTestWithFencedFrames
: public BackForwardCacheBrowserTest {
public:
BackForwardCacheBrowserTestWithFencedFrames() = default;
~BackForwardCacheBrowserTestWithFencedFrames() override = default;
test::FencedFrameTestHelper& fenced_frame_test_helper() {
return *fenced_frame_test_helper_;
}
private:
void SetUpCommandLine(base::CommandLine* command_line) override {
EnableFeatureAndSetParams(blink::features::kFencedFrames, "", "");
EnableFeatureAndSetParams(features::kPrivacySandboxAdsAPIsOverride, "", "");
BackForwardCacheBrowserTest::SetUpCommandLine(command_line);
fenced_frame_test_helper_ = std::make_unique<test::FencedFrameTestHelper>();
}
std::unique_ptr<test::FencedFrameTestHelper> fenced_frame_test_helper_;
};
IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTestWithFencedFrames,
CachesFencedFramesSimple) {
CreateHttpsServer();
ASSERT_TRUE(https_server()->Start());
GURL url_a(https_server()->GetURL("a.test", "/fenced_frames/title1.html"));
GURL url_b(https_server()->GetURL("b.test", "/title1.html"));
EXPECT_TRUE(NavigateToURL(shell(), url_a));
RenderFrameHostImplWrapper rfh_a(current_frame_host());
const GURL fenced_frame_url =
https_server()->GetURL("c.test", "/fenced_frames/title1.html");
RenderFrameHostImplWrapper fenced_frame(
fenced_frame_test_helper().CreateFencedFrame(
web_contents()->GetPrimaryMainFrame(), fenced_frame_url));
EXPECT_TRUE(WaitForDOMContentLoaded(fenced_frame.get()));
EXPECT_TRUE(NavigateToURL(shell(), url_b));
EXPECT_TRUE(rfh_a->IsInBackForwardCache());
EXPECT_TRUE(fenced_frame->IsInBackForwardCache());
ASSERT_TRUE(HistoryGoBack(web_contents()));
ExpectRestored(FROM_HERE);
EXPECT_FALSE(fenced_frame->IsInBackForwardCache());
}
IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTestWithFencedFrames,
InnerFrameStorageSupport) {
CreateHttpsServer();
ASSERT_TRUE(https_server()->Start());
GURL url_a(https_server()->GetURL(
"a.test", "/fenced_frames/basic_fenced_frame_src.html"));
GURL url_b(https_server()->GetURL("b.test", "/title1.html"));
ASSERT_TRUE(NavigateToURL(shell(), url_a));
RenderFrameHostImplWrapper rfh_a(current_frame_host());
EXPECT_FALSE(rfh_a->IsInBackForwardCache());
EXPECT_EQ(1u, rfh_a->frame_tree_node()->child_count());
RenderFrameHostImplWrapper first_delegate_frame(
rfh_a->frame_tree_node()->child_at(0)->current_frame_host());
RenderFrameHostImplWrapper first_fenced_frame(
FrameTreeNode::GloballyFindByID(
first_delegate_frame->inner_tree_main_frame_tree_node_id())
->current_frame_host());
EXPECT_FALSE(first_delegate_frame->IsInBackForwardCache());
ASSERT_TRUE(first_fenced_frame);
EXPECT_FALSE(first_fenced_frame->IsInBackForwardCache());
GURL title_fenced_frame(
https_server()->GetURL("a.test", "/fenced_frames/title1.html"));
RenderFrameHostImplWrapper second_fenced_frame(
fenced_frame_test_helper().CreateFencedFrame(rfh_a.get(),
title_fenced_frame));
ASSERT_TRUE(second_fenced_frame);
EXPECT_EQ(2u, rfh_a->frame_tree_node()->child_count());
RenderFrameHostImplWrapper second_delegate_frame(
rfh_a->frame_tree_node()->child_at(1)->current_frame_host());
EXPECT_TRUE(WaitForDOMContentLoaded(second_fenced_frame.get()));
RenderFrameHostImplWrapper nested_fenced_frame(
fenced_frame_test_helper().CreateFencedFrame(second_fenced_frame.get(),
title_fenced_frame));
ASSERT_TRUE(nested_fenced_frame);
EXPECT_EQ(1u, second_fenced_frame->frame_tree_node()->child_count());
RenderFrameHostImplWrapper nested_delegate_frame(
second_fenced_frame->frame_tree_node()
->child_at(0)
->current_frame_host());
EXPECT_TRUE(WaitForDOMContentLoaded(nested_fenced_frame.get()));
StartRecordingEvents(first_fenced_frame.get());
StartRecordingEvents(second_fenced_frame.get());
StartRecordingEvents(nested_fenced_frame.get());
ASSERT_TRUE(NavigateToURL(shell(), url_b));
RenderFrameHostImplWrapper rfh_b(current_frame_host());
EXPECT_FALSE(rfh_b->IsInBackForwardCache());
ASSERT_TRUE(rfh_a->IsInBackForwardCache());
EXPECT_TRUE(first_delegate_frame->IsInBackForwardCache());
EXPECT_TRUE(first_fenced_frame->IsInBackForwardCache());
EXPECT_TRUE(second_delegate_frame->IsInBackForwardCache());
EXPECT_TRUE(second_fenced_frame->IsInBackForwardCache());
EXPECT_TRUE(nested_delegate_frame->IsInBackForwardCache());
EXPECT_TRUE(nested_fenced_frame->IsInBackForwardCache());
ASSERT_TRUE(HistoryGoBack(web_contents()));
EXPECT_EQ(current_frame_host(), rfh_a.get());
EXPECT_FALSE(rfh_a->IsInBackForwardCache());
EXPECT_FALSE(first_delegate_frame->IsInBackForwardCache());
EXPECT_FALSE(first_fenced_frame->IsInBackForwardCache());
EXPECT_FALSE(second_delegate_frame->IsInBackForwardCache());
EXPECT_FALSE(second_fenced_frame->IsInBackForwardCache());
EXPECT_FALSE(nested_delegate_frame->IsInBackForwardCache());
EXPECT_FALSE(nested_fenced_frame->IsInBackForwardCache());
base::Value matching_events =
ListValueOf("window.pagehide.persisted", "document.visibilitychange",
"window.visibilitychange", "document.freeze",
"document.resume", "document.visibilitychange",
"window.visibilitychange", "window.pageshow.persisted");
MatchEventList(first_fenced_frame.get(), matching_events.Clone());
MatchEventList(second_fenced_frame.get(), matching_events.Clone());
MatchEventList(nested_fenced_frame.get(), matching_events.Clone());
ASSERT_TRUE(HistoryGoForward(web_contents()));
EXPECT_TRUE(rfh_a->IsInBackForwardCache());
EXPECT_TRUE(first_delegate_frame->IsInBackForwardCache());
EXPECT_TRUE(first_fenced_frame->IsInBackForwardCache());
EXPECT_TRUE(second_delegate_frame->IsInBackForwardCache());
EXPECT_TRUE(second_fenced_frame->IsInBackForwardCache());
EXPECT_TRUE(nested_delegate_frame->IsInBackForwardCache());
EXPECT_TRUE(nested_fenced_frame->IsInBackForwardCache());
ASSERT_TRUE(HistoryGoBack(web_contents()));
EXPECT_EQ(current_frame_host(), rfh_a.get());
EXPECT_FALSE(rfh_a->IsInBackForwardCache());
EXPECT_FALSE(first_delegate_frame->IsInBackForwardCache());
EXPECT_FALSE(first_fenced_frame->IsInBackForwardCache());
EXPECT_FALSE(second_delegate_frame->IsInBackForwardCache());
EXPECT_FALSE(second_fenced_frame->IsInBackForwardCache());
EXPECT_FALSE(nested_delegate_frame->IsInBackForwardCache());
EXPECT_FALSE(nested_fenced_frame->IsInBackForwardCache());
}
IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTestWithFencedFrames,
OuterDocumentTimeEviction) {
CreateHttpsServer();
ASSERT_TRUE(https_server()->Start());
scoped_refptr<base::TestMockTimeTaskRunner> task_runner =
base::MakeRefCounted<base::TestMockTimeTaskRunner>();
web_contents()->GetController().GetBackForwardCache().SetTaskRunnerForTesting(
task_runner);
base::TimeDelta time_to_live_in_back_forward_cache =
BackForwardCacheImpl::GetTimeToLiveInBackForwardCache();
EXPECT_EQ(time_to_live_in_back_forward_cache, base::Seconds(3600));
base::TimeDelta delta = base::Milliseconds(1);
GURL url_a(https_server()->GetURL("a.test", "/title1.html"));
GURL url_b(https_server()->GetURL("b.test", "/title1.html"));
ASSERT_TRUE(NavigateToURL(shell(), url_a));
RenderFrameHostImplWrapper rfh_a(current_frame_host());
GURL fenced_frame_url(
https_server()->GetURL("a.test", "/fenced_frames/empty.html"));
RenderFrameHostImplWrapper fenced_frame_rfh(
fenced_frame_test_helper().CreateFencedFrame(rfh_a.get(),
fenced_frame_url));
ASSERT_TRUE(fenced_frame_rfh);
EXPECT_TRUE(WaitForDOMContentLoaded(fenced_frame_rfh.get()));
ASSERT_TRUE(NavigateToURL(shell(), url_b));
RenderFrameHostImplWrapper rfh_b(current_frame_host());
EXPECT_FALSE(rfh_b->IsBackForwardCacheEvictionTimeRunningForTesting());
task_runner->FastForwardBy(time_to_live_in_back_forward_cache - delta);
ASSERT_TRUE(rfh_a);
EXPECT_TRUE(rfh_a->IsBackForwardCacheEvictionTimeRunningForTesting());
EXPECT_TRUE(rfh_a->IsInBackForwardCache());
EXPECT_FALSE(
fenced_frame_rfh->IsBackForwardCacheEvictionTimeRunningForTesting());
EXPECT_TRUE(fenced_frame_rfh->IsInBackForwardCache());
task_runner->FastForwardBy(delta);
ASSERT_TRUE(fenced_frame_rfh.WaitUntilRenderFrameDeleted());
ASSERT_TRUE(rfh_a.WaitUntilRenderFrameDeleted());
EXPECT_EQ(current_frame_host(), rfh_b.get());
ASSERT_TRUE(HistoryGoBack(web_contents()));
ExpectNotRestored({BackForwardCacheMetrics::NotRestoredReason::kTimeout}, {},
{}, {}, {}, FROM_HERE);
}
IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTestWithFencedFrames,
TreeResults) {
CreateHttpsServer();
ASSERT_TRUE(https_server()->Start());
GURL url_a(https_server()->GetURL("a.test", "/title1.html"));
ASSERT_TRUE(NavigateToURL(shell(), url_a));
RenderFrameHostImplWrapper rfh_a(current_frame_host());
GURL fenced_frame_url_a(
https_server()->GetURL("a.test", "/fenced_frames/title1.html?value=a"));
GURL fenced_frame_url_b(
https_server()->GetURL("b.test", "/fenced_frames/title1.html?value=b"));
GURL fenced_frame_url_c(
https_server()->GetURL("c.test", "/fenced_frames/title1.html?value=c"));
RenderFrameHostImplWrapper fenced_frame_a(
fenced_frame_test_helper().CreateFencedFrame(rfh_a.get(),
fenced_frame_url_a));
RenderFrameHostImplWrapper fenced_frame_b(
fenced_frame_test_helper().CreateFencedFrame(rfh_a.get(),
fenced_frame_url_b));
RenderFrameHostImplWrapper fenced_frame_c(
fenced_frame_test_helper().CreateFencedFrame(fenced_frame_b.get(),
fenced_frame_url_c));
EXPECT_TRUE(fenced_frame_a);
EXPECT_TRUE(fenced_frame_b);
EXPECT_TRUE(fenced_frame_c);
EXPECT_TRUE(WaitForDOMContentLoaded(fenced_frame_a.get()));
EXPECT_TRUE(WaitForDOMContentLoaded(fenced_frame_b.get()));
EXPECT_TRUE(WaitForDOMContentLoaded(fenced_frame_c.get()));
fenced_frame_c->UseDummyStickyBackForwardCacheDisablingFeatureForTesting();
BackForwardCacheCanStoreDocumentResultWithTree can_store_result =
web_contents()
->GetController()
.GetBackForwardCache()
.GetCurrentBackForwardCacheEligibility(rfh_a.get());
ASSERT_TRUE(can_store_result.tree_reasons);
EXPECT_EQ(url_a, can_store_result.tree_reasons->GetUrl());
EXPECT_EQ(2u, can_store_result.tree_reasons->GetChildren().size());
EXPECT_THAT(
can_store_result.tree_reasons->GetDocumentResult(),
MatchesDocumentResult(NotRestoredReasons(), BlockListedFeatures()));
auto& child_a_results = can_store_result.tree_reasons->GetChildren().at(0);
EXPECT_EQ(fenced_frame_url_a, child_a_results->GetUrl());
EXPECT_FALSE(child_a_results->IsSameOrigin());
EXPECT_EQ(0u, child_a_results->GetChildren().size());
auto& child_b_results = can_store_result.tree_reasons->GetChildren().at(1);
EXPECT_EQ(fenced_frame_url_b, child_b_results->GetUrl());
EXPECT_FALSE(child_b_results->IsSameOrigin());
EXPECT_EQ(1u, child_b_results->GetChildren().size());
auto& child_c_results = child_b_results->GetChildren().at(0);
EXPECT_EQ(fenced_frame_url_c, child_c_results->GetUrl());
EXPECT_FALSE(child_c_results->IsSameOrigin());
EXPECT_THAT(child_c_results->GetDocumentResult(),
MatchesDocumentResult(
NotRestoredReasons(NotRestoredReason::kBlocklistedFeatures),
BlockListedFeatures(
blink::scheduler::WebSchedulerTrackedFeature::kDummy)));
blink::mojom::BackForwardCacheNotRestoredReasonsPtr web_reasons =
can_store_result.tree_reasons->GetWebExposedNotRestoredReasons();
EXPECT_TRUE(web_reasons->same_origin_details);
EXPECT_EQ(web_reasons->blocked, blink::mojom::BFCacheBlocked::kNo);
EXPECT_EQ(2u, web_reasons->same_origin_details->children.size());
EXPECT_FALSE(
web_reasons->same_origin_details->children.at(0)->same_origin_details);
EXPECT_FALSE(
web_reasons->same_origin_details->children.at(1)->same_origin_details);
}
IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTestWithFencedFrames,
EvictionOnInnerFrameTree) {
DoNotFailForUnexpectedMessagesWhileCached();
CreateHttpsServer();
ASSERT_TRUE(https_server()->Start());
GURL url_a(https_server()->GetURL("a.test", "/fenced_frames/title1.html"));
GURL url_b(https_server()->GetURL("b.test", "/title1.html"));
EXPECT_TRUE(NavigateToURL(shell(), url_a));
RenderFrameHostImplWrapper rfh_a(current_frame_host());
const GURL fenced_frame_url =
https_server()->GetURL("c.test", "/fenced_frames/title1.html");
RenderFrameHostImpl* fenced_frame = static_cast<RenderFrameHostImpl*>(
fenced_frame_test_helper().CreateFencedFrame(
web_contents()->GetPrimaryMainFrame(), fenced_frame_url));
EXPECT_TRUE(WaitForDOMContentLoaded(fenced_frame));
RenderFrameDeletedObserver delete_observer_fenced_frame(fenced_frame);
EXPECT_TRUE(NavigateToURL(shell(), url_b));
EXPECT_TRUE(rfh_a->IsInBackForwardCache());
EvictByJavaScript(fenced_frame);
delete_observer_fenced_frame.WaitUntilDeleted();
ASSERT_TRUE(HistoryGoBack(web_contents()));
ExpectNotRestored({NotRestoredReason::kJavaScriptExecution}, {}, {}, {}, {},
FROM_HERE);
}
}