| // Copyright 2019 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "base/command_line.h" |
| #include "base/macros.h" |
| #include "base/test/mock_callback.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "build/build_config.h" |
| #include "build/chromeos_buildflags.h" |
| #include "chrome/browser/content_settings/host_content_settings_map_factory.h" |
| #include "chrome/browser/content_settings/mixed_content_settings_tab_helper.h" |
| #include "chrome/browser/permissions/permission_manager_factory.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/ui/browser.h" |
| #include "chrome/browser/ui/browser_content_setting_bubble_model_delegate.h" |
| #include "chrome/browser/ui/content_settings/content_setting_bubble_model.h" |
| #include "chrome/browser/ui/tabs/tab_strip_model.h" |
| #include "chrome/test/base/in_process_browser_test.h" |
| #include "chrome/test/base/ui_test_utils.h" |
| #include "components/content_settings/core/browser/host_content_settings_map.h" |
| #include "components/content_settings/core/common/content_settings.h" |
| #include "components/content_settings/core/common/content_settings_types.h" |
| #include "components/network_session_configurator/common/network_switches.h" |
| #include "components/page_load_metrics/browser/observers/core/uma_page_load_metrics_observer.h" |
| #include "components/page_load_metrics/common/page_load_metrics_constants.h" |
| #include "components/permissions/permission_manager.h" |
| #include "content/public/browser/render_process_host.h" |
| #include "content/public/common/content_features.h" |
| #include "content/public/common/content_switches.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/test_navigation_observer.h" |
| #include "content/public/test/test_utils.h" |
| #include "device/bluetooth/bluetooth_adapter_factory.h" |
| #include "device/bluetooth/test/mock_bluetooth_adapter.h" |
| #include "net/dns/mock_host_resolver.h" |
| #include "net/test/embedded_test_server/embedded_test_server.h" |
| #include "third_party/blink/public/mojom/webshare/webshare.mojom.h" |
| |
| namespace { |
| |
| // hash for std::unordered_map. |
| struct FeatureHash { |
| size_t operator()(base::Feature feature) const { |
| return base::FastHash(feature.name); |
| } |
| }; |
| |
| // compare operator for std::unordered_map. |
| struct FeatureEqualOperator { |
| bool operator()(base::Feature feature1, base::Feature feature2) const { |
| return std::strcmp(feature1.name, feature2.name) == 0; |
| } |
| }; |
| } // namespace |
| |
| class ChromeBackForwardCacheBrowserTest : public InProcessBrowserTest { |
| public: |
| ChromeBackForwardCacheBrowserTest() = default; |
| ~ChromeBackForwardCacheBrowserTest() override = default; |
| |
| void SetUp() override { |
| // Fake the BluetoothAdapter to say it's present. |
| // Used in WebBluetooth test. |
| adapter_ = |
| base::MakeRefCounted<testing::NiceMock<device::MockBluetoothAdapter>>(); |
| device::BluetoothAdapterFactory::SetAdapterForTesting(adapter_); |
| #if BUILDFLAG(IS_CHROMEOS_ASH) |
| // In CHROMEOS build, even when |adapter_| object is released at TearDown() |
| // it causes the test to fail on exit with an error indicating |adapter_| is |
| // leaked. |
| testing::Mock::AllowLeak(adapter_.get()); |
| #endif |
| |
| InProcessBrowserTest::SetUp(); |
| } |
| |
| void TearDown() override { |
| testing::Mock::VerifyAndClearExpectations(adapter_.get()); |
| adapter_.reset(); |
| InProcessBrowserTest::TearDown(); |
| } |
| |
| void SetUpOnMainThread() override { |
| host_resolver()->AddRule("*", "127.0.0.1"); |
| histogram_tester_ = std::make_unique<base::HistogramTester>(); |
| } |
| |
| // At the chrome layer, an outstanding request to /favicon.ico is made. It is |
| // made by the renderer on behalf of the browser process. It counts as an |
| // outstanding request, which prevents the page from entering the |
| // BackForwardCache, as long as it hasn't resolved. |
| // |
| // There are no real way to wait for this to complete. Not waiting would make |
| // the test potentially flaky. To prevent this, the no-favicon.html page is |
| // used, the image is not loaded from the network. |
| GURL GetURL(const std::string& host) { |
| return embedded_test_server()->GetURL( |
| host, "/back_forward_cache/no-favicon.html"); |
| } |
| |
| protected: |
| void SetUpCommandLine(base::CommandLine* command_line) override { |
| // For using an HTTPS server. |
| base::CommandLine::ForCurrentProcess()->AppendSwitch( |
| switches::kIgnoreCertificateErrors); |
| // For using WebBluetooth. |
| command_line->AppendSwitch( |
| switches::kEnableExperimentalWebPlatformFeatures); |
| |
| EnableFeatureAndSetParams(features::kBackForwardCache, |
| "TimeToLiveInBackForwardCacheInSeconds", "3600"); |
| EnableFeatureAndSetParams(features::kBackForwardCache, "enable_same_site", |
| "true"); |
| SetupFeaturesAndParameters(); |
| |
| InProcessBrowserTest::SetUpCommandLine(command_line); |
| } |
| |
| content::WebContents* web_contents() const { |
| return browser()->tab_strip_model()->GetActiveWebContents(); |
| } |
| |
| content::RenderFrameHost* current_frame_host() { |
| return web_contents()->GetMainFrame(); |
| } |
| |
| void SetupFeaturesAndParameters() { |
| std::vector<base::test::ScopedFeatureList::FeatureAndParams> |
| enabled_features; |
| |
| for (const auto& feature_param : features_with_params_) { |
| enabled_features.emplace_back(feature_param.first, feature_param.second); |
| } |
| |
| scoped_feature_list_.InitWithFeaturesAndParameters(enabled_features, {}); |
| } |
| |
| void EnableFeatureAndSetParams(base::Feature feature, |
| std::string param_name, |
| std::string param_value) { |
| features_with_params_[feature][param_name] = param_value; |
| } |
| |
| std::unique_ptr<base::HistogramTester> histogram_tester_; |
| |
| private: |
| base::test::ScopedFeatureList scoped_feature_list_; |
| scoped_refptr<device::MockBluetoothAdapter> adapter_; |
| std::unordered_map<base::Feature, |
| std::map<std::string, std::string>, |
| FeatureHash, |
| FeatureEqualOperator> |
| features_with_params_; |
| |
| DISALLOW_COPY_AND_ASSIGN(ChromeBackForwardCacheBrowserTest); |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(ChromeBackForwardCacheBrowserTest, Basic) { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| |
| // 1) Navigate to A. |
| EXPECT_TRUE(content::NavigateToURL(web_contents(), GetURL("a.com"))); |
| content::RenderFrameHost* rfh_a = current_frame_host(); |
| content::RenderFrameDeletedObserver delete_observer_rfh_a(rfh_a); |
| EXPECT_TRUE(content::ExecJs(rfh_a, "token = 'rfh_a'")); |
| |
| // 2) Navigate to B. |
| EXPECT_TRUE(content::NavigateToURL(web_contents(), GetURL("b.com"))); |
| content::RenderFrameHost* rfh_b = current_frame_host(); |
| content::RenderFrameDeletedObserver delete_observer_rfh_b(rfh_b); |
| EXPECT_TRUE(content::ExecJs(rfh_b, "token = 'rfh_b'")); |
| |
| // A is frozen in the BackForwardCache. |
| EXPECT_FALSE(delete_observer_rfh_a.deleted()); |
| |
| // 3) Navigate back. |
| web_contents()->GetController().GoBack(); |
| EXPECT_TRUE(content::WaitForLoadStop(web_contents())); |
| |
| // A is restored, B is stored. |
| EXPECT_FALSE(delete_observer_rfh_a.deleted()); |
| EXPECT_FALSE(delete_observer_rfh_b.deleted()); |
| |
| EXPECT_EQ("rfh_a", content::EvalJs(rfh_a, "token")); |
| |
| // 4) Navigate forward. |
| web_contents()->GetController().GoForward(); |
| EXPECT_TRUE(content::WaitForLoadStop(web_contents())); |
| |
| // A is stored, B is restored. |
| EXPECT_FALSE(delete_observer_rfh_a.deleted()); |
| EXPECT_FALSE(delete_observer_rfh_b.deleted()); |
| |
| EXPECT_EQ("rfh_b", content::EvalJs(rfh_b, "token")); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ChromeBackForwardCacheBrowserTest, BasicIframe) { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| |
| // 1) Navigate to A. |
| EXPECT_TRUE(content::NavigateToURL(web_contents(), GetURL("a.com"))); |
| content::RenderFrameHost* rfh_a = current_frame_host(); |
| content::RenderFrameDeletedObserver delete_observer_rfh_a(rfh_a); |
| EXPECT_TRUE(content::ExecJs(rfh_a, "token = 'rfh_a'")); |
| |
| // 2) Add an iframe B. |
| EXPECT_TRUE(content::ExecJs(rfh_a, R"( |
| let url = new URL(location.href); |
| url.hostname = 'b.com'; |
| let iframe = document.createElement('iframe'); |
| iframe.url = url; |
| document.body.appendChild(iframe); |
| )")); |
| EXPECT_TRUE(content::WaitForLoadStop(web_contents())); |
| |
| content::RenderFrameHost* rfh_b = nullptr; |
| for (content::RenderFrameHost* rfh : web_contents()->GetAllFrames()) { |
| if (rfh != rfh_a) |
| rfh_b = rfh; |
| } |
| content::RenderFrameDeletedObserver delete_observer_rfh_b(rfh_b); |
| |
| EXPECT_TRUE(content::ExecJs(rfh_a, "token = 'rfh_a'")); |
| EXPECT_TRUE(content::ExecJs(rfh_b, "token = 'rfh_b'")); |
| |
| // 2) Navigate to C. |
| EXPECT_TRUE(content::NavigateToURL(web_contents(), GetURL("c.com"))); |
| |
| // A and B are frozen. The page A(B) is stored in the BackForwardCache. |
| EXPECT_FALSE(delete_observer_rfh_a.deleted()); |
| EXPECT_FALSE(delete_observer_rfh_b.deleted()); |
| |
| // 3) Navigate back. |
| web_contents()->GetController().GoBack(); |
| EXPECT_TRUE(content::WaitForLoadStop(web_contents())); |
| |
| // The page A(B) is restored. |
| EXPECT_FALSE(delete_observer_rfh_a.deleted()); |
| EXPECT_FALSE(delete_observer_rfh_b.deleted()); |
| EXPECT_EQ("rfh_a", content::EvalJs(rfh_a, "token")); |
| EXPECT_EQ("rfh_b", content::EvalJs(rfh_b, "token")); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ChromeBackForwardCacheBrowserTest, WebBluetooth) { |
| // The test requires a mock Bluetooth adapter to perform a |
| // WebBluetooth API call. To avoid conflicts with the default Bluetooth |
| // adapter, e.g. Windows adapter, which is configured during Bluetooth |
| // initialization, the mock adapter is configured in SetUp(). |
| |
| // WebBluetooth requires HTTPS. |
| net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS); |
| https_server.AddDefaultHandlers(GetChromeTestDataDir()); |
| https_server.SetSSLConfig(net::EmbeddedTestServer::CERT_OK); |
| ASSERT_TRUE(https_server.Start()); |
| GURL url(https_server.GetURL("a.com", "/back_forward_cache/no-favicon.html")); |
| |
| EXPECT_TRUE(content::NavigateToURL(web_contents(), url)); |
| content::BackForwardCacheDisabledTester tester; |
| |
| EXPECT_EQ("device not found", content::EvalJs(current_frame_host(), R"( |
| new Promise(resolve => { |
| navigator.bluetooth.requestDevice({ |
| filters: [ |
| { services: [0x1802, 0x1803] }, |
| ] |
| }) |
| .then(() => resolve("device found")) |
| .catch(() => resolve("device not found")) |
| }); |
| )")); |
| EXPECT_TRUE(tester.IsDisabledForFrameWithReason( |
| current_frame_host()->GetProcess()->GetID(), |
| current_frame_host()->GetRoutingID(), "WebBluetooth")); |
| |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| content::RenderFrameDeletedObserver delete_observer(current_frame_host()); |
| EXPECT_TRUE(content::NavigateToURL(web_contents(), GetURL("b.com"))); |
| delete_observer.WaitUntilDeleted(); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ChromeBackForwardCacheBrowserTest, |
| PermissionContextBase) { |
| // HTTPS needed for GEOLOCATION permission |
| net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS); |
| https_server.AddDefaultHandlers(GetChromeTestDataDir()); |
| https_server.SetSSLConfig(net::EmbeddedTestServer::CERT_OK); |
| ASSERT_TRUE(https_server.Start()); |
| |
| GURL url_a(https_server.GetURL("a.com", "/title1.html")); |
| GURL url_b(https_server.GetURL("b.com", "/title1.html")); |
| |
| // 1) Navigate to A. |
| EXPECT_TRUE(NavigateToURL(web_contents(), url_a)); |
| content::RenderFrameHost* rfh_a = current_frame_host(); |
| content::RenderFrameDeletedObserver delete_observer_rfh_a(rfh_a); |
| |
| // 2) Navigate to B. |
| EXPECT_TRUE(NavigateToURL(web_contents(), url_b)); |
| ASSERT_FALSE(delete_observer_rfh_a.deleted()); |
| base::MockOnceCallback<void(ContentSetting)> callback; |
| EXPECT_CALL(callback, Run(ContentSetting::CONTENT_SETTING_ASK)); |
| PermissionManagerFactory::GetForProfile(browser()->profile()) |
| ->RequestPermission(ContentSettingsType::GEOLOCATION, rfh_a, url_a, |
| /* user_gesture = */ true, callback.Get()); |
| |
| delete_observer_rfh_a.WaitUntilDeleted(); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ChromeBackForwardCacheBrowserTest, |
| DoesNotCacheIfPictureInPicture) { |
| embedded_test_server()->ServeFilesFromSourceDirectory("content/test/data"); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| |
| // Navigate to a page with picture-in-picture functionality. |
| const base::FilePath::CharType picture_in_picture_page[] = |
| FILE_PATH_LITERAL("media/picture-in-picture/window-size.html"); |
| GURL test_page_url = ui_test_utils::GetTestUrl( |
| base::FilePath(base::FilePath::kCurrentDirectory), |
| base::FilePath(picture_in_picture_page)); |
| EXPECT_TRUE(content::NavigateToURL(web_contents(), test_page_url)); |
| |
| // Execute picture-in-picture on the page. |
| bool result = false; |
| ASSERT_TRUE(content::ExecuteScriptAndExtractBool( |
| web_contents(), "enterPictureInPicture();", &result)); |
| EXPECT_TRUE(result); |
| |
| content::RenderFrameDeletedObserver deleted(current_frame_host()); |
| |
| // Navigate away. |
| EXPECT_TRUE(content::NavigateToURL(web_contents(), GetURL("b.com"))); |
| |
| // The page uses Picture-in-Picture so it should be deleted. |
| deleted.WaitUntilDeleted(); |
| } |
| |
| #if defined(OS_ANDROID) |
| IN_PROC_BROWSER_TEST_F(ChromeBackForwardCacheBrowserTest, |
| DoesNotCacheIfWebShare) { |
| // HTTPS needed for WebShare permission. |
| net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS); |
| https_server.AddDefaultHandlers(GetChromeTestDataDir()); |
| https_server.SetSSLConfig(net::EmbeddedTestServer::CERT_OK); |
| ASSERT_TRUE(https_server.Start()); |
| |
| GURL url_a(https_server.GetURL("a.com", "/title1.html")); |
| GURL url_b(https_server.GetURL("b.com", "/title1.html")); |
| |
| // 1) Navigate to A. |
| EXPECT_TRUE(content::NavigateToURL(web_contents(), url_a)); |
| |
| // Use the WebShare feature on the empty page. |
| EXPECT_EQ("success", content::EvalJs(current_frame_host(), R"( |
| new Promise(resolve => { |
| navigator.share({title: 'the title'}) |
| .then(m => { resolve("success"); }) |
| .catch(error => { resolve(error.message); }); |
| }); |
| )")); |
| |
| content::RenderFrameDeletedObserver deleted(current_frame_host()); |
| |
| // 2) Navigate away. |
| EXPECT_TRUE(content::NavigateToURL(web_contents(), url_b)); |
| |
| // The page uses WebShare so it should be deleted. |
| deleted.WaitUntilDeleted(); |
| |
| // 3) Go back. |
| web_contents()->GetController().GoBack(); |
| EXPECT_TRUE(content::WaitForLoadStop(web_contents())); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ChromeBackForwardCacheBrowserTest, |
| DoesNotCacheIfWebNfc) { |
| // HTTPS needed for WebNfc permission. |
| net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS); |
| https_server.AddDefaultHandlers(GetChromeTestDataDir()); |
| https_server.SetSSLConfig(net::EmbeddedTestServer::CERT_OK); |
| ASSERT_TRUE(https_server.Start()); |
| |
| GURL url_a(https_server.GetURL("a.com", "/title1.html")); |
| GURL url_b(https_server.GetURL("b.com", "/title1.html")); |
| |
| // 1) Navigate to A. |
| EXPECT_TRUE(content::NavigateToURL(web_contents(), url_a)); |
| |
| // Use the WebNfc feature on the empty page. |
| EXPECT_EQ("success", content::EvalJs(current_frame_host(), R"( |
| const ndef = new NDEFReader(); |
| new Promise(async resolve => { |
| try { |
| await ndef.write("Hello"); |
| resolve('success'); |
| } catch (error) { |
| resolve(error.message); |
| } |
| }); |
| )")); |
| |
| content::RenderFrameDeletedObserver deleted(current_frame_host()); |
| |
| // 2) Navigate away. |
| EXPECT_TRUE(content::NavigateToURL(web_contents(), url_b)); |
| |
| // The page uses WebShare so it should be deleted. |
| deleted.WaitUntilDeleted(); |
| |
| // 3) Go back. |
| web_contents()->GetController().GoBack(); |
| EXPECT_TRUE(content::WaitForLoadStop(web_contents())); |
| } |
| #endif |
| |
| IN_PROC_BROWSER_TEST_F(ChromeBackForwardCacheBrowserTest, |
| RestoresMixedContentSettings) { |
| net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS); |
| https_server.AddDefaultHandlers(GetChromeTestDataDir()); |
| https_server.SetSSLConfig(net::EmbeddedTestServer::CERT_OK); |
| ASSERT_TRUE(https_server.Start()); |
| GURL url_a(https_server.GetURL("a.com", |
| "/content_setting_bubble/mixed_script.html")); |
| GURL url_b(https_server.GetURL("b.com", |
| "/content_setting_bubble/mixed_script.html")); |
| |
| // 1) Load page A that has mixed content. |
| EXPECT_TRUE(content::NavigateToURL(web_contents(), url_a)); |
| // Mixed content should be blocked at first. |
| EXPECT_FALSE(MixedContentSettingsTabHelper::FromWebContents(web_contents()) |
| ->IsRunningInsecureContentAllowed()); |
| |
| // 2) Emulate link clicking on the mixed script bubble to allow mixed content |
| // to run. |
| content::TestNavigationObserver observer( |
| browser()->tab_strip_model()->GetActiveWebContents()); |
| std::unique_ptr<ContentSettingBubbleModel> model( |
| ContentSettingBubbleModel::CreateContentSettingBubbleModel( |
| browser()->content_setting_bubble_model_delegate(), |
| browser()->tab_strip_model()->GetActiveWebContents(), |
| ContentSettingsType::MIXEDSCRIPT)); |
| model->OnCustomLinkClicked(); |
| |
| // 3) Wait for reload. |
| observer.Wait(); |
| |
| // Mixed content should no longer be blocked. |
| EXPECT_TRUE(MixedContentSettingsTabHelper::FromWebContents(web_contents()) |
| ->IsRunningInsecureContentAllowed()); |
| |
| // 4) Navigate to page B, which should use a different SiteInstance and |
| // resets the mixed content settings. |
| EXPECT_TRUE(content::NavigateToURL(web_contents(), url_b)); |
| // Mixed content should be blocked in the new page. |
| EXPECT_FALSE(MixedContentSettingsTabHelper::FromWebContents(web_contents()) |
| ->IsRunningInsecureContentAllowed()); |
| |
| // 5) Go back to page A. |
| web_contents()->GetController().GoBack(); |
| EXPECT_TRUE(content::WaitForLoadStop(web_contents())); |
| // Mixed content settings is restored, so it's no longer blocked. |
| EXPECT_TRUE(MixedContentSettingsTabHelper::FromWebContents(web_contents()) |
| ->IsRunningInsecureContentAllowed()); |
| } |
| |
| class MetricsChromeBackForwardCacheBrowserTest |
| : public ChromeBackForwardCacheBrowserTest, |
| public ::testing::WithParamInterface<std::string> { |
| public: |
| MetricsChromeBackForwardCacheBrowserTest() = default; |
| ~MetricsChromeBackForwardCacheBrowserTest() override = default; |
| |
| protected: |
| void SetUpCommandLine(base::CommandLine* command_line) override { |
| // Set BufferTimerDelayMillis to a high number so that metrics update on the |
| // renderer won't be sent to the browser by the periodic upload. |
| EnableFeatureAndSetParams( |
| page_load_metrics::kPageLoadMetricsTimerDelayFeature, |
| "BufferTimerDelayMillis", "100000"); |
| ChromeBackForwardCacheBrowserTest::SetUpCommandLine(command_line); |
| } |
| }; |
| |
| IN_PROC_BROWSER_TEST_P(MetricsChromeBackForwardCacheBrowserTest, |
| FirstInputDelay) { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| |
| GURL url1(embedded_test_server()->GetURL("a.com", "/title1.html")); |
| GURL url2(embedded_test_server()->GetURL( |
| (GetParam() == "SameSite") ? "a.com" : "b.com", "/title2.html")); |
| |
| EXPECT_THAT(histogram_tester_->GetAllSamples( |
| internal::kHistogramFirstContentfulPaint), |
| testing::IsEmpty()); |
| |
| // 1) Navigate to url1. |
| EXPECT_TRUE(content::NavigateToURL(web_contents(), url1)); |
| content::RenderFrameHost* rfh_a = current_frame_host(); |
| content::RenderFrameDeletedObserver delete_observer_rfh_a(rfh_a); |
| |
| // Simulate mouse click. FirstInputDelay won't get updated immediately. |
| content::SimulateMouseClickAt(web_contents(), 0, |
| blink::WebMouseEvent::Button::kLeft, |
| gfx::Point(100, 100)); |
| // Run arbitrary script and run tasks in the brwoser to ensure the input is |
| // processed in the renderer. |
| EXPECT_TRUE(content::ExecJs(rfh_a, "var foo = 42;")); |
| base::RunLoop().RunUntilIdle(); |
| content::FetchHistogramsFromChildProcesses(); |
| histogram_tester_->ExpectTotalCount(internal::kHistogramFirstInputDelay, 0); |
| |
| // 2) Immediately navigate to url2. |
| if (GetParam() == "CrossSiteRendererInitiated") { |
| EXPECT_TRUE(content::NavigateToURLFromRenderer(web_contents(), url2)); |
| } else { |
| EXPECT_TRUE(content::NavigateToURL(web_contents(), url2)); |
| } |
| EXPECT_FALSE(delete_observer_rfh_a.deleted()); |
| |
| content::FetchHistogramsFromChildProcesses(); |
| if (GetParam() != "CrossSiteBrowserInitiated" || |
| rfh_a->GetProcess() == current_frame_host()->GetProcess()) { |
| // - For "SameSite" case, since the old and new RenderFrame share a process, |
| // the metrics update will be sent to the browser during commit and won't |
| // get ignored, successfully updating the FirstInputDelay histogram. |
| // - For "CrossSiteRendererInitiated" case, FirstInputDelay was sent when |
| // the renderer-initiated navigation started on the old frame. |
| // - For "CrossSiteBrowserInitiated" case, if the old and new RenderFrame |
| // share a process, the metrics update will be sent to the browser during |
| // commit and won't get ignored, successfully updating the histogram. |
| histogram_tester_->ExpectTotalCount(internal::kHistogramFirstInputDelay, 1); |
| } else { |
| // Note that in some cases the metrics might flakily get updated in time, |
| // before the browser changed the current RFH. So, we can neither expect it |
| // to be 0 all the time or 1 all the time. |
| // TODO(crbug.com/1150242): Support updating metrics consistently on |
| // cross-RFH cross-process navigations. |
| } |
| } |
| |
| std::vector<std::string> MetricsChromeBackForwardCacheBrowserTestValues() { |
| return {"SameSite", "CrossSiteRendererInitiated", |
| "CrossSiteBrowserInitiated"}; |
| } |
| |
| INSTANTIATE_TEST_SUITE_P( |
| All, |
| MetricsChromeBackForwardCacheBrowserTest, |
| testing::ValuesIn(MetricsChromeBackForwardCacheBrowserTestValues())); |