diff --git a/chrome/browser/navigation_predictor/anchor_element_preloader.cc b/chrome/browser/navigation_predictor/anchor_element_preloader.cc index 550ca3f69273af..0289774a94213f 100644 --- a/chrome/browser/navigation_predictor/anchor_element_preloader.cc +++ b/chrome/browser/navigation_predictor/anchor_element_preloader.cc @@ -3,9 +3,19 @@ // found in the LICENSE file. #include "chrome/browser/navigation_predictor/anchor_element_preloader.h" +#include "base/metrics/histogram_functions.h" #include "chrome/browser/predictors/loading_predictor.h" #include "chrome/browser/predictors/loading_predictor_factory.h" +#include "chrome/browser/prefetch/prefetch_prefs.h" +#include "components/ukm/content/source_url_recorder.h" #include "content/public/browser/browser_context.h" +#include "content/public/browser/web_contents.h" +#include "services/metrics/public/cpp/ukm_builders.h" +#include "services/metrics/public/cpp/ukm_recorder.h" +#include "third_party/blink/public/common/features.h" + +const char kPreloadingAnchorElementPreloaderPreloadingTriggered[] = + "Preloading.AnchorElementPreloader.PreloadingTriggered"; AnchorElementPreloader::AnchorElementPreloader( content::RenderFrameHost* render_frame_host, @@ -24,6 +34,22 @@ void AnchorElementPreloader::Create( } void AnchorElementPreloader::OnPointerDown(const GURL& target) { + if (!prefetch::IsSomePreloadingEnabled( + *Profile::FromBrowserContext(render_frame_host()->GetBrowserContext()) + ->GetPrefs())) { + return; + } + + RecordUmaPreloadedTriggered(AnchorElementPreloaderType::kPreconnect); + + RecordUkmPreloadType(AnchorElementPreloaderType::kPreconnect); + + if (base::GetFieldTrialParamByFeatureAsBool( + blink::features::kAnchorElementInteraction, "preconnect_holdback", + false)) { + return; + } + auto* loading_predictor = predictors::LoadingPredictorFactory::GetForProfile( Profile::FromBrowserContext(render_frame_host()->GetBrowserContext())); @@ -37,3 +63,20 @@ void AnchorElementPreloader::OnPointerDown(const GURL& target) { loading_predictor->PreconnectURLIfAllowed(target, /*allow_credentials=*/true, network_isolation_key); } + +void AnchorElementPreloader::RecordUmaPreloadedTriggered( + AnchorElementPreloaderType preload) { + base::UmaHistogramEnumeration( + kPreloadingAnchorElementPreloaderPreloadingTriggered, preload); +} + +void AnchorElementPreloader::RecordUkmPreloadType( + AnchorElementPreloaderType type) { + ukm::SourceId source_id = ukm::GetSourceIdForWebContentsDocument( + content::WebContents::FromRenderFrameHost(render_frame_host())); + + ukm::UkmRecorder* ukm_recorder = ukm::UkmRecorder::Get(); + ukm::builders::Preloading_AnchorInteraction(source_id) + .SetAnchorElementPreloaderType(static_cast(type)) + .Record(ukm_recorder); +} diff --git a/chrome/browser/navigation_predictor/anchor_element_preloader.h b/chrome/browser/navigation_predictor/anchor_element_preloader.h index a6962c597d75c7..0af5b6572d9d22 100644 --- a/chrome/browser/navigation_predictor/anchor_element_preloader.h +++ b/chrome/browser/navigation_predictor/anchor_element_preloader.h @@ -8,6 +8,16 @@ #include "content/public/browser/document_service.h" #include "third_party/blink/public/mojom/loader/anchor_element_interaction_host.mojom.h" +extern const char kPreloadingAnchorElementPreloaderPreloadingTriggered[]; + +// These values are persisted to logs. Entries should not be renumbered and +// numeric values should never be reused. +enum class AnchorElementPreloaderType { + kUnspecified = 0, + kPreconnect = 1, + kMaxValue = kPreconnect, +}; + class AnchorElementPreloader : content::DocumentService { public: @@ -21,8 +31,13 @@ class AnchorElementPreloader content::RenderFrameHost* render_frame_host, mojo::PendingReceiver receiver); + // Preconnects to the given URL `target`. void OnPointerDown(const GURL& target) override; + + void RecordUmaPreloadedTriggered(AnchorElementPreloaderType); + + void RecordUkmPreloadType(AnchorElementPreloaderType); }; #endif // CHROME_BROWSER_NAVIGATION_PREDICTOR_ANCHOR_ELEMENT_PRELOADER_H_ diff --git a/chrome/browser/navigation_predictor/anchor_element_preloader_browsertest.cc b/chrome/browser/navigation_predictor/anchor_element_preloader_browsertest.cc index da9f4ff5269309..c919fba75bf668 100644 --- a/chrome/browser/navigation_predictor/anchor_element_preloader_browsertest.cc +++ b/chrome/browser/navigation_predictor/anchor_element_preloader_browsertest.cc @@ -1,7 +1,10 @@ // Copyright 2022 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/test/metrics/histogram_tester.h" #include "base/test/scoped_feature_list.h" +#include "base/test/task_environment.h" +#include "chrome/browser/navigation_predictor/anchor_element_preloader.h" #include "chrome/browser/predictors/loading_predictor.h" #include "chrome/browser/predictors/loading_predictor_factory.h" #include "chrome/browser/predictors/preconnect_manager.h" @@ -10,11 +13,14 @@ #include "chrome/browser/ui/browser.h" #include "chrome/test/base/in_process_browser_test.h" #include "chrome/test/base/ui_test_utils.h" +#include "components/ukm/content/source_url_recorder.h" +#include "components/ukm/test_ukm_recorder.h" #include "content/public/test/browser_test.h" #include "content/public/test/browser_test_utils.h" #include "net/base/features.h" #include "net/dns/mock_host_resolver.h" #include "net/test/embedded_test_server/embedded_test_server.h" +#include "services/metrics/public/cpp/ukm_builders.h" #include "third_party/blink/public/common/features.h" namespace { @@ -24,9 +30,13 @@ class AnchorElementPreloaderBrowserTest public: static constexpr char kFakeSearch[] = "https://www.fakesearch.com/"; - void SetUp() override { + virtual void SetFeatures() { feature_list_.InitAndEnableFeature( blink::features::kAnchorElementInteraction); + } + + void SetUp() override { + SetFeatures(); https_server_ = std::make_unique( net::EmbeddedTestServer::TYPE_HTTPS); https_server_->ServeFilesFromSourceDirectory("chrome/test/data/preload"); @@ -41,6 +51,8 @@ class AnchorElementPreloaderBrowserTest auto* loading_predictor = predictors::LoadingPredictorFactory::GetForProfile( browser()->profile()); + histogram_tester_ = std::make_unique(); + test_ukm_recorder_ = std::make_unique(); ASSERT_TRUE(loading_predictor); loading_predictor->preconnect_manager()->SetObserverForTesting(this); } @@ -74,6 +86,12 @@ class AnchorElementPreloaderBrowserTest run_loop_->Quit(); } + ukm::TestAutoSetUkmRecorder* test_ukm_recorder() { + return test_ukm_recorder_.get(); + } + + base::HistogramTester* histogram_tester() { return histogram_tester_.get(); } + protected: int preresolve_count_; base::test::ScopedFeatureList feature_list_; @@ -81,6 +99,8 @@ class AnchorElementPreloaderBrowserTest private: std::unique_ptr https_server_; std::unique_ptr run_loop_; + std::unique_ptr test_ukm_recorder_; + std::unique_ptr histogram_tester_; }; IN_PROC_BROWSER_TEST_F(AnchorElementPreloaderBrowserTest, OneAnchorTest) { @@ -92,9 +112,27 @@ IN_PROC_BROWSER_TEST_F(AnchorElementPreloaderBrowserTest, OneAnchorTest) { const a = document.getElementById('anchor1'); var e = new PointerEvent('pointerdown'); a.dispatchEvent(e); - )")); + )")); WaitForPreresolveCountForURL(1); EXPECT_EQ(1, preresolve_count_); + ukm::SourceId ukm_source_id = ukm::GetSourceIdForWebContentsDocument( + browser()->tab_strip_model()->GetActiveWebContents()); + + histogram_tester()->ExpectTotalCount( + kPreloadingAnchorElementPreloaderPreloadingTriggered, 1); + + histogram_tester()->ExpectUniqueSample( + kPreloadingAnchorElementPreloaderPreloadingTriggered, + AnchorElementPreloaderType::kPreconnect, 1); + + auto ukm_entries = test_ukm_recorder()->GetEntries( + ukm::builders::Preloading_AnchorInteraction::kEntryName, + {ukm::builders::Preloading_AnchorInteraction:: + kAnchorElementPreloaderTypeName}); + + EXPECT_EQ(ukm_entries.size(), 1u); + + EXPECT_EQ(ukm_entries[0].source_id, ukm_source_id); } IN_PROC_BROWSER_TEST_F(AnchorElementPreloaderBrowserTest, InvalidHref) { @@ -106,8 +144,22 @@ IN_PROC_BROWSER_TEST_F(AnchorElementPreloaderBrowserTest, InvalidHref) { const a = document.getElementById('anchor2'); var e = new PointerEvent('pointerdown'); a.dispatchEvent(e); - )")); + )")); EXPECT_EQ(0, preresolve_count_); + + histogram_tester()->ExpectTotalCount( + kPreloadingAnchorElementPreloaderPreloadingTriggered, 0); + + histogram_tester()->ExpectUniqueSample( + kPreloadingAnchorElementPreloaderPreloadingTriggered, + AnchorElementPreloaderType::kPreconnect, 0); + + auto ukm_entries = test_ukm_recorder()->GetEntries( + ukm::builders::Preloading_AnchorInteraction::kEntryName, + {ukm::builders::Preloading_AnchorInteraction:: + kAnchorElementPreloaderTypeName}); + + EXPECT_EQ(ukm_entries.size(), 0u); } IN_PROC_BROWSER_TEST_F(AnchorElementPreloaderBrowserTest, IframeTest) { @@ -124,6 +176,25 @@ IN_PROC_BROWSER_TEST_F(AnchorElementPreloaderBrowserTest, IframeTest) { )")); WaitForPreresolveCountForURL(1); EXPECT_EQ(1, preresolve_count_); + + histogram_tester()->ExpectTotalCount( + kPreloadingAnchorElementPreloaderPreloadingTriggered, 1); + + histogram_tester()->ExpectUniqueSample( + kPreloadingAnchorElementPreloaderPreloadingTriggered, + AnchorElementPreloaderType::kPreconnect, 1); + + ukm::SourceId ukm_source_id = ukm::GetSourceIdForWebContentsDocument( + browser()->tab_strip_model()->GetActiveWebContents()); + + auto ukm_entries = test_ukm_recorder()->GetEntries( + ukm::builders::Preloading_AnchorInteraction::kEntryName, + {ukm::builders::Preloading_AnchorInteraction:: + kAnchorElementPreloaderTypeName}); + + EXPECT_EQ(ukm_entries.size(), 1u); + + EXPECT_EQ(ukm_entries[0].source_id, ukm_source_id); } IN_PROC_BROWSER_TEST_F(AnchorElementPreloaderBrowserTest, @@ -140,5 +211,66 @@ IN_PROC_BROWSER_TEST_F(AnchorElementPreloaderBrowserTest, a.dispatchEvent(e); )")); EXPECT_EQ(0, preresolve_count_); + + histogram_tester()->ExpectTotalCount( + kPreloadingAnchorElementPreloaderPreloadingTriggered, 0); + + histogram_tester()->ExpectUniqueSample( + kPreloadingAnchorElementPreloaderPreloadingTriggered, + AnchorElementPreloaderType::kPreconnect, 0); + + auto ukm_entries = test_ukm_recorder()->GetEntries( + ukm::builders::Preloading_AnchorInteraction::kEntryName, + {ukm::builders::Preloading_AnchorInteraction:: + kAnchorElementPreloaderTypeName}); + + EXPECT_EQ(ukm_entries.size(), 0u); +} + +class AnchorElementPreloaderHoldbackBrowserTest + : public AnchorElementPreloaderBrowserTest { + public: + void SetFeatures() override { + feature_list_holdback_.InitAndEnableFeatureWithParameters( + blink::features::kAnchorElementInteraction, + {{"preconnect_holdback", "true"}}); + } + + private: + base::test::ScopedFeatureList feature_list_holdback_; +}; + +IN_PROC_BROWSER_TEST_F(AnchorElementPreloaderHoldbackBrowserTest, + PreconnectHoldbackTest) { + const GURL& url = GetTestURL("/one_anchor.html"); + EXPECT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); + + EXPECT_TRUE(content::ExecuteScript( + browser()->tab_strip_model()->GetActiveWebContents(), + R"( + const a = document.getElementById('anchor1'); + var e = new PointerEvent('pointerdown'); + a.dispatchEvent(e); + )")); + EXPECT_EQ(0, preresolve_count_); + + histogram_tester()->ExpectTotalCount( + kPreloadingAnchorElementPreloaderPreloadingTriggered, 1); + + histogram_tester()->ExpectUniqueSample( + kPreloadingAnchorElementPreloaderPreloadingTriggered, + AnchorElementPreloaderType::kPreconnect, 1); + + ukm::SourceId ukm_source_id = ukm::GetSourceIdForWebContentsDocument( + browser()->tab_strip_model()->GetActiveWebContents()); + + auto ukm_entries = test_ukm_recorder()->GetEntries( + ukm::builders::Preloading_AnchorInteraction::kEntryName, + {ukm::builders::Preloading_AnchorInteraction:: + kAnchorElementPreloaderTypeName}); + + EXPECT_EQ(ukm_entries.size(), 1u); + + EXPECT_EQ(ukm_entries[0].source_id, ukm_source_id); } } // namespace diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml index 0567af3078e336..f94c5f4d87c89c 100644 --- a/tools/metrics/histograms/enums.xml +++ b/tools/metrics/histograms/enums.xml @@ -1728,6 +1728,11 @@ Unknown properties are collapsed to zero. --> label="0x7340, Navi 14 [Radeon RX 5500/5500M / Pro 5500M]"/> + + + + + diff --git a/tools/metrics/histograms/metadata/others/histograms.xml b/tools/metrics/histograms/metadata/others/histograms.xml index 5d00cab7bfff3b..1eba5c2d27248a 100644 --- a/tools/metrics/histograms/metadata/others/histograms.xml +++ b/tools/metrics/histograms/metadata/others/histograms.xml @@ -9372,6 +9372,18 @@ chromium-metrics-reviews@google.com. + + jacobstanley@google.com + spelchat@chromium.org + curranmax@chromium.org + chrome-brapp-loading@google.com + + Logged once for each preconnection request sent to the browser. Associated + with the Source ID of the page triggering the preconnection. + + + diff --git a/tools/metrics/ukm/ukm.xml b/tools/metrics/ukm/ukm.xml index e8ff40b55df9a9..c5931d4c8255c4 100644 --- a/tools/metrics/ukm/ukm.xml +++ b/tools/metrics/ukm/ukm.xml @@ -16160,6 +16160,23 @@ be describing additional metrics about the same event. + + jacobstanley@google.com + curranmax@chromium.org + chrome-brapp-loading@google.com + + Logged once for each preconnection request sent to the browser in resopnse + to the user interacting with an anchor element. Associated with the Source + ID of the page triggering the preconnection. + + + + One event for each preload operation that was triggered on the current + page. + + + + ksakamoto@chromium.org nhiroki@chromium.org