| // Copyright 2021 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "build/build_config.h" |
| #include "chrome/browser/extensions/extension_browsertest.h" |
| #include "chrome/test/base/ui_test_utils.h" |
| #include "components/back_forward_cache/back_forward_cache_disable.h" |
| #include "content/public/browser/back_forward_cache.h" |
| #include "content/public/common/content_features.h" |
| #include "content/public/test/browser_test.h" |
| #include "extensions/common/extension.h" |
| #include "net/dns/mock_host_resolver.h" |
| #include "third_party/blink/public/common/scheduler/web_scheduler_tracked_feature.h" |
| #include "third_party/blink/public/mojom/frame/back_forward_cache_controller.mojom-shared.h" |
| |
| namespace extensions { |
| |
| class ExtensionBackForwardCacheBrowserTest : public ExtensionBrowserTest { |
| public: |
| explicit ExtensionBackForwardCacheBrowserTest( |
| bool all_extensions_allowed = true, |
| bool allow_content_scripts = true, |
| std::string blocked_extensions = "") { |
| // If `allow_content_scripts` is true then `all_extensions_allowed` must |
| // also be true. |
| DCHECK(!(allow_content_scripts && !all_extensions_allowed)); |
| feature_list_.InitWithFeaturesAndParameters( |
| {{features::kBackForwardCache, |
| {{"content_injection_supported", |
| allow_content_scripts ? "true" : "false"}, |
| {"TimeToLiveInBackForwardCacheInSeconds", "3600"}, |
| {"all_extensions_allowed", |
| all_extensions_allowed ? "true" : "false"}, |
| {"blocked_extensions", blocked_extensions}}}}, |
| {features::kBackForwardCacheMemoryControls}); |
| } |
| |
| void SetUpOnMainThread() override { |
| host_resolver()->AddRule("*", "127.0.0.1"); |
| ExtensionBrowserTest::SetUpOnMainThread(); |
| } |
| |
| void RunChromeRuntimeTest(const std::string& action) { |
| const Extension* extension = |
| LoadExtension(test_data_dir_.AppendASCII("back_forward_cache") |
| .AppendASCII("content_script")); |
| ASSERT_TRUE(extension); |
| |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html")); |
| GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html")); |
| |
| // 1) Navigate to A. |
| content::RenderFrameHost* rfh_a = |
| ui_test_utils::NavigateToURL(browser(), url_a); |
| content::RenderFrameDeletedObserver delete_observer_rfh_a(rfh_a); |
| |
| constexpr int kMessagingBucket = |
| (static_cast<int>(content::BackForwardCache::DisabledSource::kEmbedder) |
| << 16) + |
| static_cast<int>( |
| back_forward_cache::DisabledReasonId::kExtensionMessaging); |
| |
| EXPECT_TRUE(ExecJs( |
| rfh_a, base::StringPrintf(action.c_str(), extension->id().c_str()))); |
| |
| EXPECT_EQ(0, histogram_tester_.GetBucketCount( |
| "BackForwardCache.HistoryNavigationOutcome." |
| "DisabledForRenderFrameHostReason2", |
| kMessagingBucket)); |
| // 2) Navigate to B. |
| ui_test_utils::NavigateToURL(browser(), url_b); |
| |
| // Expect that `rfh_a` is destroyed as it wouldn't be placed in the cache |
| // since it uses the chrome.runtime API. |
| delete_observer_rfh_a.WaitUntilDeleted(); |
| |
| // 3) Go back to A. |
| content::WebContents* web_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| web_contents->GetController().GoBack(); |
| EXPECT_TRUE(WaitForLoadStop(web_contents)); |
| |
| // Validate that the not restored reason is `ExtensionMessaging` due to the |
| // chrome.runtime usage. |
| EXPECT_EQ(1, histogram_tester_.GetBucketCount( |
| "BackForwardCache.HistoryNavigationOutcome." |
| "DisabledForRenderFrameHostReason2", |
| kMessagingBucket)); |
| } |
| |
| protected: |
| base::HistogramTester histogram_tester_; |
| |
| private: |
| base::test::ScopedFeatureList feature_list_; |
| }; |
| |
| // Test that does not allow content scripts to be injected. |
| class ExtensionBackForwardCacheContentScriptDisabledBrowserTest |
| : public ExtensionBackForwardCacheBrowserTest { |
| public: |
| ExtensionBackForwardCacheContentScriptDisabledBrowserTest() |
| : ExtensionBackForwardCacheBrowserTest(/*all_extensions_allowed*/ true, |
| /*allow_content_scripts=*/false) {} |
| }; |
| |
| // Test that causes non-component extensions to disable back forward cache. |
| class ExtensionBackForwardCacheExtensionsDisabledBrowserTest |
| : public ExtensionBackForwardCacheBrowserTest { |
| public: |
| ExtensionBackForwardCacheExtensionsDisabledBrowserTest() |
| : ExtensionBackForwardCacheBrowserTest(/*all_extensions_allowed*/ false, |
| /*allow_content_scripts*/ false) {} |
| }; |
| |
| // Tests that a non-component extension that is installed prevents back forward |
| // cache. |
| IN_PROC_BROWSER_TEST_F(ExtensionBackForwardCacheExtensionsDisabledBrowserTest, |
| ScriptDisallowed) { |
| ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("trivial_extension") |
| .AppendASCII("extension"))); |
| |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html")); |
| GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html")); |
| |
| // 1) Navigate to A. |
| content::RenderFrameHost* rfh_a = |
| ui_test_utils::NavigateToURL(browser(), url_a); |
| content::RenderFrameDeletedObserver delete_observer_rfh_a(rfh_a); |
| |
| // 2) Navigate to B. |
| content::RenderFrameHost* rfh_b = |
| ui_test_utils::NavigateToURL(browser(), url_b); |
| content::RenderFrameDeletedObserver delete_observer_rfh_b(rfh_b); |
| |
| // Expect that `rfh_a` is destroyed as it wouldn't be placed in the cache |
| // since there is an active non-component loaded extension. |
| delete_observer_rfh_a.WaitUntilDeleted(); |
| } |
| |
| // Test content script injection disallow the back forward cache. |
| IN_PROC_BROWSER_TEST_F( |
| ExtensionBackForwardCacheContentScriptDisabledBrowserTest, |
| ScriptDisallowed) { |
| ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("back_forward_cache") |
| .AppendASCII("content_script"))); |
| |
| 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")); |
| |
| std::u16string expected_title = u"modified"; |
| content::TitleWatcher title_watcher( |
| browser()->tab_strip_model()->GetActiveWebContents(), expected_title); |
| |
| // 1) Navigate to A. |
| content::RenderFrameHost* rfh_a = |
| ui_test_utils::NavigateToURL(browser(), url_a); |
| content::RenderFrameDeletedObserver delete_observer_rfh_a(rfh_a); |
| EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle()); |
| |
| // 2) Navigate to B. |
| content::RenderFrameHost* rfh_b = |
| ui_test_utils::NavigateToURL(browser(), url_b); |
| content::RenderFrameDeletedObserver delete_observer_rfh_b(rfh_b); |
| |
| // Expect that `rfh_a` is destroyed as it wouldn't be placed in the cache |
| // since the active extension injected content_scripts. |
| delete_observer_rfh_a.WaitUntilDeleted(); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ExtensionBackForwardCacheBrowserTest, ScriptAllowed) { |
| ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("back_forward_cache") |
| .AppendASCII("content_script"))); |
| |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html")); |
| GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html")); |
| |
| // 1) Navigate to A. |
| content::RenderFrameHost* rfh_a = |
| ui_test_utils::NavigateToURL(browser(), url_a); |
| content::RenderFrameDeletedObserver delete_observer_rfh_a(rfh_a); |
| |
| // 2) Navigate to B. |
| content::RenderFrameHost* rfh_b = |
| ui_test_utils::NavigateToURL(browser(), url_b); |
| content::RenderFrameDeletedObserver delete_observer_rfh_b(rfh_b); |
| |
| // Ensure that `rfh_a` is in the cache. |
| EXPECT_FALSE(delete_observer_rfh_a.deleted()); |
| EXPECT_NE(rfh_a, rfh_b); |
| EXPECT_EQ(rfh_a->GetLifecycleState(), |
| content::RenderFrameHost::LifecycleState::kInBackForwardCache); |
| } |
| |
| IN_PROC_BROWSER_TEST_F( |
| ExtensionBackForwardCacheContentScriptDisabledBrowserTest, |
| CSSDisallowed) { |
| ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("back_forward_cache") |
| .AppendASCII("content_css"))); |
| |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html")); |
| GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html")); |
| |
| // 1) Navigate to A. |
| content::RenderFrameHost* rfh_a = |
| ui_test_utils::NavigateToURL(browser(), url_a); |
| content::RenderFrameDeletedObserver delete_observer_rfh_a(rfh_a); |
| |
| // 2) Navigate to B. |
| content::RenderFrameHost* rfh_b = |
| ui_test_utils::NavigateToURL(browser(), url_b); |
| content::RenderFrameDeletedObserver delete_observer_rfh_b(rfh_b); |
| |
| // Expect that `rfh_a` is destroyed as it wouldn't be placed in the cache. |
| delete_observer_rfh_a.WaitUntilDeleted(); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ExtensionBackForwardCacheBrowserTest, CSSAllowed) { |
| ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("back_forward_cache") |
| .AppendASCII("content_css"))); |
| |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html")); |
| GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html")); |
| |
| // 1) Navigate to A. |
| content::RenderFrameHost* rfh_a = |
| ui_test_utils::NavigateToURL(browser(), url_a); |
| content::RenderFrameDeletedObserver delete_observer_rfh_a(rfh_a); |
| |
| // 2) Navigate to B. |
| content::RenderFrameHost* rfh_b = |
| ui_test_utils::NavigateToURL(browser(), url_b); |
| content::RenderFrameDeletedObserver delete_observer_rfh_b(rfh_b); |
| |
| // Ensure that `rfh_a` is in the cache. |
| EXPECT_FALSE(delete_observer_rfh_a.deleted()); |
| EXPECT_NE(rfh_a, rfh_b); |
| EXPECT_EQ(rfh_a->GetLifecycleState(), |
| content::RenderFrameHost::LifecycleState::kInBackForwardCache); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ExtensionBackForwardCacheBrowserTest, |
| UnloadExtensionFlushCache) { |
| 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")); |
| |
| // Load the extension so we can unload it later. |
| const Extension* extension = |
| LoadExtension(test_data_dir_.AppendASCII("back_forward_cache") |
| .AppendASCII("content_css")); |
| ASSERT_TRUE(extension); |
| |
| // 1) Navigate to A. |
| content::RenderFrameHost* rfh_a = |
| ui_test_utils::NavigateToURL(browser(), url_a); |
| content::RenderFrameDeletedObserver delete_observer_rfh_a(rfh_a); |
| |
| // 2) Navigate to B. |
| content::RenderFrameHost* rfh_b = |
| ui_test_utils::NavigateToURL(browser(), url_b); |
| content::RenderFrameDeletedObserver delete_observer_rfh_b(rfh_b); |
| |
| // Ensure that `rfh_a` is in the cache. |
| EXPECT_FALSE(delete_observer_rfh_a.deleted()); |
| EXPECT_NE(rfh_a, rfh_b); |
| EXPECT_EQ(rfh_a->GetLifecycleState(), |
| content::RenderFrameHost::LifecycleState::kInBackForwardCache); |
| |
| // Now unload the extension after something is in the cache. |
| UnloadExtension(extension->id()); |
| |
| // Expect that `rfh_a` is destroyed as it should be cleared from the cache. |
| delete_observer_rfh_a.WaitUntilDeleted(); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ExtensionBackForwardCacheBrowserTest, |
| LoadExtensionFlushCache) { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html")); |
| GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html")); |
| |
| // 1) Navigate to A. |
| content::RenderFrameHost* rfh_a = |
| ui_test_utils::NavigateToURL(browser(), url_a); |
| content::RenderFrameDeletedObserver delete_observer_rfh_a(rfh_a); |
| |
| // 2) Navigate to B. |
| content::RenderFrameHost* rfh_b = |
| ui_test_utils::NavigateToURL(browser(), url_b); |
| |
| // Ensure that `rfh_a` is in the cache. |
| EXPECT_FALSE(delete_observer_rfh_a.deleted()); |
| EXPECT_NE(rfh_a, rfh_b); |
| EXPECT_EQ(rfh_a->GetLifecycleState(), |
| content::RenderFrameHost::LifecycleState::kInBackForwardCache); |
| |
| // Now load the extension after something is in the cache. |
| ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("back_forward_cache") |
| .AppendASCII("content_css"))); |
| |
| // Expect that `rfh_a` is destroyed as it should be cleared from the cache. |
| delete_observer_rfh_a.WaitUntilDeleted(); |
| } |
| |
| // Test if the chrome.runtime.connect API is called, the page is prevented from |
| // entering bfcache. |
| IN_PROC_BROWSER_TEST_F(ExtensionBackForwardCacheBrowserTest, |
| ChromeRuntimeConnectUsage) { |
| RunChromeRuntimeTest("chrome.runtime.connect('%s');"); |
| } |
| |
| // Test if the chrome.runtime.sendMessage API is called, the page is prevented |
| // from entering bfcache. |
| IN_PROC_BROWSER_TEST_F(ExtensionBackForwardCacheBrowserTest, |
| ChromeRuntimeSendMessageUsage) { |
| RunChromeRuntimeTest( |
| "chrome.runtime.sendMessage('%s', 'some " |
| "message');"); |
| } |
| |
| // Test if the chrome.runtime.connect API is called, the page is prevented from |
| // entering bfcache. |
| IN_PROC_BROWSER_TEST_F( |
| ExtensionBackForwardCacheContentScriptDisabledBrowserTest, |
| ChromeRuntimeConnectUsage) { |
| RunChromeRuntimeTest("chrome.runtime.connect('%s');"); |
| |
| // Validate also that the not restored reason is `IsolatedWorldScript` due to |
| // the extension injecting a content script. |
| EXPECT_EQ( |
| 1, |
| histogram_tester_.GetBucketCount( |
| "BackForwardCache.HistoryNavigationOutcome.BlocklistedFeature", |
| blink::scheduler::WebSchedulerTrackedFeature::kIsolatedWorldScript)); |
| } |
| |
| // Tests sending a message to all frames does not send it to back-forward |
| // cached frames. |
| IN_PROC_BROWSER_TEST_F(ExtensionBackForwardCacheBrowserTest, |
| MessageSentToAllFramesDoesNotSendToBackForwardCache) { |
| const Extension* extension = extension = |
| LoadExtension(test_data_dir_.AppendASCII("back_forward_cache") |
| .AppendASCII("background_page")); |
| ASSERT_TRUE(extension); |
| |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL url_a(embedded_test_server()->GetURL("a.com", "/title2.html")); |
| GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html")); |
| |
| // 1) Navigate to A. |
| content::RenderFrameHost* rfh_a = |
| ui_test_utils::NavigateToURL(browser(), url_a); |
| content::RenderFrameDeletedObserver delete_observer_rfh_a(rfh_a); |
| |
| // 2) Navigate to B. |
| content::RenderFrameHost* rfh_b = |
| ui_test_utils::NavigateToURL(browser(), url_b); |
| content::RenderFrameDeletedObserver delete_observer_rfh_b(rfh_b); |
| |
| // Ensure that `rfh_a` is in the cache. |
| EXPECT_FALSE(delete_observer_rfh_a.deleted()); |
| EXPECT_NE(rfh_a, rfh_b); |
| EXPECT_EQ(rfh_a->GetLifecycleState(), |
| content::RenderFrameHost::LifecycleState::kInBackForwardCache); |
| |
| std::u16string expected_title = u"foo"; |
| auto title_watcher = std::make_unique<content::TitleWatcher>( |
| browser()->tab_strip_model()->GetActiveWebContents(), expected_title); |
| |
| constexpr char kScript[] = |
| R"HTML( |
| chrome.tabs.executeScript({allFrames: true, code: "document.title='foo'"}) |
| )HTML"; |
| ASSERT_TRUE(ExecuteScriptInBackgroundPageNoWait(extension->id(), kScript)); |
| |
| EXPECT_EQ(expected_title, title_watcher->WaitAndGetTitle()); |
| |
| // `rfh_a` should still be in the cache. |
| EXPECT_FALSE(delete_observer_rfh_a.deleted()); |
| EXPECT_NE(rfh_a, rfh_b); |
| EXPECT_EQ(rfh_a->GetLifecycleState(), |
| content::RenderFrameHost::LifecycleState::kInBackForwardCache); |
| |
| // Expect the original title when going back to A. |
| expected_title = u"Title Of Awesomeness"; |
| title_watcher = std::make_unique<content::TitleWatcher>( |
| browser()->tab_strip_model()->GetActiveWebContents(), expected_title); |
| // Go back to A. |
| content::WebContents* web_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| web_contents->GetController().GoBack(); |
| EXPECT_TRUE(WaitForLoadStop(web_contents)); |
| |
| EXPECT_EQ(expected_title, title_watcher->WaitAndGetTitle()); |
| |
| // `rfh_b` should still be in the cache. |
| EXPECT_FALSE(delete_observer_rfh_b.deleted()); |
| EXPECT_NE(rfh_a, rfh_b); |
| EXPECT_EQ(rfh_b->GetLifecycleState(), |
| content::RenderFrameHost::LifecycleState::kInBackForwardCache); |
| |
| // Now go forward to B, and expect that it is what was set before it |
| // went into the back forward cache. |
| expected_title = u"foo"; |
| title_watcher = std::make_unique<content::TitleWatcher>( |
| browser()->tab_strip_model()->GetActiveWebContents(), expected_title); |
| web_contents->GetController().GoForward(); |
| EXPECT_TRUE(WaitForLoadStop(web_contents)); |
| |
| EXPECT_EQ(expected_title, title_watcher->WaitAndGetTitle()); |
| } |
| |
| // Tests sending a message to specific frame that is in the back forward cache |
| // fails. |
| IN_PROC_BROWSER_TEST_F(ExtensionBackForwardCacheBrowserTest, |
| MessageSentToCachedIdFails) { |
| const Extension* extension = extension = |
| LoadExtension(test_data_dir_.AppendASCII("back_forward_cache") |
| .AppendASCII("background_page")); |
| ASSERT_TRUE(extension); |
| |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL url_a(embedded_test_server()->GetURL("a.com", "/iframe_blank.html")); |
| GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html")); |
| |
| // 1) Navigate to A. |
| content::RenderFrameHost* rfh_a = |
| ui_test_utils::NavigateToURL(browser(), url_a); |
| content::RenderFrameDeletedObserver delete_observer_rfh_a(rfh_a); |
| |
| ASSERT_EQ(2u, rfh_a->GetFramesInSubtree().size()); |
| |
| // Cache the iframe's frame tree node id to send it a message later. |
| int iframe_frame_tree_node_id = |
| rfh_a->GetFramesInSubtree()[1]->GetFrameTreeNodeId(); |
| |
| // 2) Navigate to B. |
| content::RenderFrameHost* rfh_b = |
| ui_test_utils::NavigateToURL(browser(), url_b); |
| |
| // Ensure that `rfh_a` is in the cache. |
| EXPECT_FALSE(delete_observer_rfh_a.deleted()); |
| EXPECT_NE(rfh_a, rfh_b); |
| EXPECT_EQ(rfh_a->GetLifecycleState(), |
| content::RenderFrameHost::LifecycleState::kInBackForwardCache); |
| |
| std::u16string expected_title = u"foo"; |
| auto title_watcher = std::make_unique<content::TitleWatcher>( |
| browser()->tab_strip_model()->GetActiveWebContents(), expected_title); |
| |
| constexpr char kScript[] = |
| R"HTML( |
| chrome.tabs.executeScript({frameId: %d, |
| code: "document.title='foo'", |
| matchAboutBlank: true |
| }, (e) => { |
| window.domAutomationController.send(chrome.runtime.lastError ? 'false' |
| : 'true')}); |
| )HTML"; |
| EXPECT_EQ("false", |
| ExecuteScriptInBackgroundPage( |
| extension->id(), |
| base::StringPrintf(kScript, iframe_frame_tree_node_id))); |
| // Go back to A. |
| content::WebContents* web_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| web_contents->GetController().GoBack(); |
| EXPECT_TRUE(WaitForLoadStop(web_contents)); |
| |
| // Re-execute the script. |
| EXPECT_EQ("true", |
| ExecuteScriptInBackgroundPage( |
| extension->id(), |
| base::StringPrintf(kScript, iframe_frame_tree_node_id))); |
| } |
| |
| // Test that running extensions message dispatching via a ScriptContext::ForEach |
| // for back forward cached pages causes eviction of that RenderFrameHost. |
| IN_PROC_BROWSER_TEST_F(ExtensionBackForwardCacheBrowserTest, |
| StorageCallbackEvicts) { |
| const Extension* extension = extension = |
| LoadExtension(test_data_dir_.AppendASCII("back_forward_cache") |
| .AppendASCII("content_script_storage")); |
| ASSERT_TRUE(extension); |
| |
| 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")); |
| |
| // 1) Navigate to A. |
| content::RenderFrameHost* rfh_a = |
| ui_test_utils::NavigateToURL(browser(), url_a); |
| content::RenderFrameDeletedObserver delete_observer_rfh_a(rfh_a); |
| |
| // 2) Navigate to B. |
| ui_test_utils::NavigateToURL(browser(), url_b); |
| |
| // Expect that `rfh_a` is destroyed as loading page B will causes a storage |
| // event which is sent to all listeners. Since `rfh_a` is a listener but is in |
| // the back forward cache it gets evicted. |
| delete_observer_rfh_a.WaitUntilDeleted(); |
| |
| // Validate also that the eviction reason is `kJavascriptExecution` due |
| // to the extension processing a callback while in the back forward cache. |
| EXPECT_EQ(1, histogram_tester_.GetBucketCount( |
| "BackForwardCache.Eviction.Renderer", |
| blink::mojom::RendererEvictionReason::kJavaScriptExecution)); |
| } |
| |
| // Test that allows all extensions but disables bfcache in the presence of a few |
| // blocked ones. |
| class ExtensionBackForwardCacheBlockedExtensionBrowserTest |
| : public ExtensionBackForwardCacheBrowserTest { |
| public: |
| ExtensionBackForwardCacheBlockedExtensionBrowserTest() |
| : ExtensionBackForwardCacheBrowserTest( |
| /*all_extensions_allowed*/ true, |
| /*allow_content_scripts=*/true, |
| /*blocked_extensions=*/ |
| "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,mockepjebcnmhmhcahfddgfcdgkdifnc," |
| "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb") {} |
| }; |
| |
| // Tests that a blocked extension that is installed prevents back forward |
| // cache. |
| IN_PROC_BROWSER_TEST_F(ExtensionBackForwardCacheBlockedExtensionBrowserTest, |
| ScriptDisallowed) { |
| const Extension* extension = |
| LoadExtension(test_data_dir_.AppendASCII("trivial_extension") |
| .AppendASCII("extension.crx")); |
| ASSERT_TRUE(extension); |
| ASSERT_EQ(extension->id(), "mockepjebcnmhmhcahfddgfcdgkdifnc"); |
| |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html")); |
| GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html")); |
| |
| // 1) Navigate to A. |
| content::RenderFrameHost* rfh_a = |
| ui_test_utils::NavigateToURL(browser(), url_a); |
| content::RenderFrameDeletedObserver delete_observer_rfh_a(rfh_a); |
| |
| // 2) Navigate to B. |
| ui_test_utils::NavigateToURL(browser(), url_b); |
| |
| // Expect that `rfh_a` is destroyed as it wouldn't be placed in the cache |
| // since there is a blocked feature flag with id |
| // 'mockepjebcnmhmhcahfddgfcdgkdifnc'. |
| delete_observer_rfh_a.WaitUntilDeleted(); |
| } |
| |
| } // namespace extensions |