blob: 398a0501ead188530dfaa1ee44f2b0e464caf392 [file] [log] [blame]
// 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 "components/browsing_topics/browsing_topics_page_load_data_tracker.h"
#include "base/files/scoped_temp_dir.h"
#include "base/memory/raw_ptr.h"
#include "base/test/bind.h"
#include "base/test/scoped_feature_list.h"
#include "components/browsing_topics/test_util.h"
#include "components/history/content/browser/history_context_helper.h"
#include "components/history/core/browser/history_database_params.h"
#include "components/history/core/browser/history_service.h"
#include "components/history/core/test/test_history_database.h"
#include "components/ukm/test_ukm_recorder.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/test/back_forward_cache_util.h"
#include "content/public/test/browsing_topics_test_util.h"
#include "content/public/test/navigation_simulator.h"
#include "content/public/test/test_utils.h"
#include "content/public/test/web_contents_tester.h"
#include "content/test/test_render_view_host.h"
#include "services/metrics/public/cpp/metrics_utils.h"
#include "services/metrics/public/cpp/ukm_builders.h"
namespace browsing_topics {
class BrowsingTopicsPageLoadDataTrackerTest
: public content::RenderViewHostTestHarness {
public:
BrowsingTopicsPageLoadDataTrackerTest() {
scoped_feature_list_.InitWithFeatures(
/*enabled_features=*/{blink::features::kBrowsingTopics},
/*disabled_features=*/{});
EXPECT_TRUE(temp_dir_.CreateUniqueTempDir());
history_service_ = std::make_unique<history::HistoryService>();
history_service_->Init(
history::TestHistoryDatabaseParamsForPath(temp_dir_.GetPath()));
}
~BrowsingTopicsPageLoadDataTrackerTest() override = default;
void SetUp() override {
content::RenderViewHostTestHarness::SetUp();
// The test assumes pages gets deleted after navigation, triggering metrics
// recording. Disable back/forward cache to ensure that pages don't get
// preserved in the cache.
content::DisableBackForwardCacheForTesting(
web_contents(), content::BackForwardCache::TEST_REQUIRES_NO_CACHING);
}
void TearDown() override {
DCHECK(history_service_);
base::RunLoop run_loop;
history_service_->SetOnBackendDestroyTask(run_loop.QuitClosure());
history_service_.reset();
run_loop.Run();
content::RenderViewHostTestHarness::TearDown();
}
void NavigateToPage(const GURL& url,
bool publicly_routable,
bool browsing_topics_permissions_policy_allowed,
bool interest_cohort_permissions_policy_allowed) {
auto simulator = content::NavigationSimulator::CreateBrowserInitiated(
url, web_contents());
simulator->SetTransition(ui::PageTransition::PAGE_TRANSITION_TYPED);
if (!publicly_routable) {
net::IPAddress address;
EXPECT_TRUE(address.AssignFromIPLiteral("0.0.0.0"));
simulator->SetSocketAddress(net::IPEndPoint(address, /*port=*/0));
}
blink::ParsedPermissionsPolicy policy;
if (!browsing_topics_permissions_policy_allowed) {
policy.emplace_back(
blink::mojom::PermissionsPolicyFeature::kBrowsingTopics,
/*values=*/std::vector<url::Origin>(), /*matches_all_origins=*/false,
/*matches_opaque_src=*/false);
}
if (!interest_cohort_permissions_policy_allowed) {
policy.emplace_back(blink::mojom::PermissionsPolicyFeature::
kBrowsingTopicsBackwardCompatible,
/*values=*/std::vector<url::Origin>(),
/*matches_all_origins=*/false,
/*matches_opaque_src=*/false);
}
simulator->SetPermissionsPolicyHeader(std::move(policy));
simulator->Commit();
history_service_->AddPage(
url, base::Time::Now(),
history::ContextIDForWebContents(web_contents()),
web_contents()->GetController().GetLastCommittedEntry()->GetUniqueID(),
/*referrer=*/GURL(),
/*redirects=*/{}, ui::PageTransition::PAGE_TRANSITION_TYPED,
history::VisitSource::SOURCE_BROWSED,
/*did_replace_entry=*/false,
/*floc_allowed=*/false);
}
BrowsingTopicsPageLoadDataTracker* GetBrowsingTopicsPageLoadDataTracker() {
return BrowsingTopicsPageLoadDataTracker::GetOrCreateForPage(
web_contents()->GetPrimaryMainFrame()->GetPage());
}
content::BrowsingTopicsSiteDataManager* topics_site_data_manager() {
return web_contents()
->GetPrimaryMainFrame()
->GetProcess()
->GetStoragePartition()
->GetBrowsingTopicsSiteDataManager();
}
protected:
base::test::ScopedFeatureList scoped_feature_list_;
std::unique_ptr<history::HistoryService> history_service_;
base::ScopedTempDir temp_dir_;
};
TEST_F(BrowsingTopicsPageLoadDataTrackerTest, OneUsage) {
GURL url("https://foo.com");
NavigateToPage(url, /*publicly_routable=*/true,
/*browsing_topics_permissions_policy_allowed=*/true,
/*interest_cohort_permissions_policy_allowed=*/true);
EXPECT_FALSE(BrowsingTopicsEligibleForURLVisit(history_service_.get(), url));
EXPECT_TRUE(
content::GetBrowsingTopicsApiUsage(topics_site_data_manager()).empty());
GetBrowsingTopicsPageLoadDataTracker()->OnBrowsingTopicsApiUsed(
HashedDomain(123), history_service_.get());
EXPECT_TRUE(BrowsingTopicsEligibleForURLVisit(history_service_.get(), url));
std::vector<ApiUsageContext> api_usage_contexts =
content::GetBrowsingTopicsApiUsage(topics_site_data_manager());
EXPECT_EQ(api_usage_contexts.size(), 1u);
EXPECT_EQ(api_usage_contexts[0].hashed_main_frame_host,
HashMainFrameHostForStorage("foo.com"));
EXPECT_EQ(api_usage_contexts[0].hashed_context_domain, HashedDomain(123));
}
TEST_F(BrowsingTopicsPageLoadDataTrackerTest, TwoUsages) {
GURL url("https://foo.com");
NavigateToPage(url, /*publicly_routable=*/true,
/*browsing_topics_permissions_policy_allowed=*/true,
/*interest_cohort_permissions_policy_allowed=*/true);
GetBrowsingTopicsPageLoadDataTracker()->OnBrowsingTopicsApiUsed(
HashedDomain(123), history_service_.get());
GetBrowsingTopicsPageLoadDataTracker()->OnBrowsingTopicsApiUsed(
HashedDomain(456), history_service_.get());
EXPECT_TRUE(BrowsingTopicsEligibleForURLVisit(history_service_.get(), url));
std::vector<ApiUsageContext> api_usage_contexts =
content::GetBrowsingTopicsApiUsage(topics_site_data_manager());
EXPECT_EQ(api_usage_contexts.size(), 2u);
EXPECT_EQ(api_usage_contexts[0].hashed_main_frame_host,
HashMainFrameHostForStorage("foo.com"));
EXPECT_EQ(api_usage_contexts[0].hashed_context_domain, HashedDomain(123));
EXPECT_EQ(api_usage_contexts[1].hashed_main_frame_host,
HashMainFrameHostForStorage("foo.com"));
EXPECT_EQ(api_usage_contexts[1].hashed_context_domain, HashedDomain(456));
}
TEST_F(BrowsingTopicsPageLoadDataTrackerTest, OneUsage_PageLoadUkm) {
ukm::TestAutoSetUkmRecorder ukm_recorder;
NavigateToPage(GURL("https://foo.com"), /*publicly_routable=*/true,
/*browsing_topics_permissions_policy_allowed=*/true,
/*interest_cohort_permissions_policy_allowed=*/true);
GetBrowsingTopicsPageLoadDataTracker()->OnBrowsingTopicsApiUsed(
HashedDomain(123), history_service_.get());
NavigateToPage(GURL(url::kAboutBlankURL), /*publicly_routable=*/true,
/*browsing_topics_permissions_policy_allowed=*/true,
/*interest_cohort_permissions_policy_allowed=*/true);
auto entries = ukm_recorder.GetEntriesByName(
ukm::builders::BrowsingTopics_PageLoad::kEntryName);
EXPECT_EQ(1u, entries.size());
ASSERT_EQ(1, ukm::GetExponentialBucketMinForCounts1000(1));
ukm_recorder.ExpectEntryMetric(entries.back(),
ukm::builders::BrowsingTopics_PageLoad::
kTopicsRequestingContextDomainsCountName,
1);
}
TEST_F(BrowsingTopicsPageLoadDataTrackerTest, OneThousandUsages_PageLoadUkm) {
ukm::TestAutoSetUkmRecorder ukm_recorder;
NavigateToPage(GURL("https://foo.com"), /*publicly_routable=*/true,
/*browsing_topics_permissions_policy_allowed=*/true,
/*interest_cohort_permissions_policy_allowed=*/true);
for (int i = 0; i < 1000; ++i) {
GetBrowsingTopicsPageLoadDataTracker()->OnBrowsingTopicsApiUsed(
HashedDomain(i), history_service_.get());
}
NavigateToPage(GURL(url::kAboutBlankURL), /*publicly_routable=*/true,
/*browsing_topics_permissions_policy_allowed=*/true,
/*interest_cohort_permissions_policy_allowed=*/true);
auto entries = ukm_recorder.GetEntriesByName(
ukm::builders::BrowsingTopics_PageLoad::kEntryName);
EXPECT_EQ(1u, entries.size());
ASSERT_EQ(943, ukm::GetExponentialBucketMinForCounts1000(1000));
ukm_recorder.ExpectEntryMetric(entries.back(),
ukm::builders::BrowsingTopics_PageLoad::
kTopicsRequestingContextDomainsCountName,
943);
}
TEST_F(BrowsingTopicsPageLoadDataTrackerTest, TwoThousandUsages_PageLoadUkm) {
ukm::TestAutoSetUkmRecorder ukm_recorder;
NavigateToPage(GURL("https://foo.com"), /*publicly_routable=*/true,
/*browsing_topics_permissions_policy_allowed=*/true,
/*interest_cohort_permissions_policy_allowed=*/true);
for (int i = 0; i < 2000; ++i) {
GetBrowsingTopicsPageLoadDataTracker()->OnBrowsingTopicsApiUsed(
HashedDomain(i), history_service_.get());
}
NavigateToPage(GURL(url::kAboutBlankURL), /*publicly_routable=*/true,
/*browsing_topics_permissions_policy_allowed=*/true,
/*interest_cohort_permissions_policy_allowed=*/true);
auto entries = ukm_recorder.GetEntriesByName(
ukm::builders::BrowsingTopics_PageLoad::kEntryName);
EXPECT_EQ(1u, entries.size());
ASSERT_EQ(943, ukm::GetExponentialBucketMinForCounts1000(1000));
ASSERT_EQ(1896, ukm::GetExponentialBucketMinForCounts1000(2000));
ukm_recorder.ExpectEntryMetric(entries.back(),
ukm::builders::BrowsingTopics_PageLoad::
kTopicsRequestingContextDomainsCountName,
943);
}
TEST_F(BrowsingTopicsPageLoadDataTrackerTest, DuplicateDomains) {
GURL url("https://foo.com");
NavigateToPage(url, /*publicly_routable=*/true,
/*browsing_topics_permissions_policy_allowed=*/true,
/*interest_cohort_permissions_policy_allowed=*/true);
GetBrowsingTopicsPageLoadDataTracker()->OnBrowsingTopicsApiUsed(
HashedDomain(123), history_service_.get());
GetBrowsingTopicsPageLoadDataTracker()->OnBrowsingTopicsApiUsed(
HashedDomain(456), history_service_.get());
GetBrowsingTopicsPageLoadDataTracker()->OnBrowsingTopicsApiUsed(
HashedDomain(123), history_service_.get());
EXPECT_TRUE(BrowsingTopicsEligibleForURLVisit(history_service_.get(), url));
std::vector<ApiUsageContext> api_usage_contexts =
content::GetBrowsingTopicsApiUsage(topics_site_data_manager());
EXPECT_EQ(api_usage_contexts.size(), 2u);
EXPECT_EQ(api_usage_contexts[0].hashed_main_frame_host,
HashMainFrameHostForStorage("foo.com"));
EXPECT_EQ(api_usage_contexts[0].hashed_context_domain, HashedDomain(123));
EXPECT_EQ(api_usage_contexts[1].hashed_main_frame_host,
HashMainFrameHostForStorage("foo.com"));
EXPECT_EQ(api_usage_contexts[1].hashed_context_domain, HashedDomain(456));
// The second HashedDomain(123) shouldn't update the database. Verify this by
// verifying that the timestamp for HashedDomain(123) is no greater than the
// timestamp for HashedDomain(456).
EXPECT_LE(api_usage_contexts[0].time, api_usage_contexts[1].time);
}
TEST_F(BrowsingTopicsPageLoadDataTrackerTest, NumberOfDomainsExceedsLimit) {
GURL url("https://foo.com");
NavigateToPage(url, /*publicly_routable=*/true,
/*browsing_topics_permissions_policy_allowed=*/true,
/*interest_cohort_permissions_policy_allowed=*/true);
for (int i = 0; i < 31; ++i) {
GetBrowsingTopicsPageLoadDataTracker()->OnBrowsingTopicsApiUsed(
HashedDomain(i), history_service_.get());
}
EXPECT_TRUE(BrowsingTopicsEligibleForURLVisit(history_service_.get(), url));
std::vector<ApiUsageContext> api_usage_contexts =
content::GetBrowsingTopicsApiUsage(topics_site_data_manager());
EXPECT_EQ(api_usage_contexts.size(), 30u);
for (int i = 0; i < 30; ++i) {
EXPECT_EQ(api_usage_contexts[i].hashed_main_frame_host,
HashMainFrameHostForStorage("foo.com"));
EXPECT_EQ(api_usage_contexts[i].hashed_context_domain, HashedDomain(i));
}
}
TEST_F(BrowsingTopicsPageLoadDataTrackerTest, NotPubliclyRoutable) {
GURL url("https://foo.com");
NavigateToPage(url, /*publicly_routable=*/false,
/*browsing_topics_permissions_policy_allowed=*/true,
/*interest_cohort_permissions_policy_allowed=*/true);
GetBrowsingTopicsPageLoadDataTracker()->OnBrowsingTopicsApiUsed(
HashedDomain(123), history_service_.get());
EXPECT_FALSE(BrowsingTopicsEligibleForURLVisit(history_service_.get(), url));
EXPECT_TRUE(
content::GetBrowsingTopicsApiUsage(topics_site_data_manager()).empty());
}
TEST_F(BrowsingTopicsPageLoadDataTrackerTest,
BrowsingTopicsPermissionsPolicyNotAllowed) {
GURL url("https://foo.com");
NavigateToPage(url, /*publicly_routable=*/true,
/*browsing_topics_permissions_policy_allowed=*/false,
/*interest_cohort_permissions_policy_allowed=*/true);
GetBrowsingTopicsPageLoadDataTracker()->OnBrowsingTopicsApiUsed(
HashedDomain(123), history_service_.get());
EXPECT_FALSE(BrowsingTopicsEligibleForURLVisit(history_service_.get(), url));
EXPECT_TRUE(
content::GetBrowsingTopicsApiUsage(topics_site_data_manager()).empty());
}
TEST_F(BrowsingTopicsPageLoadDataTrackerTest,
InterestCohortPermissionsPolicyNotAllowed) {
GURL url("https://foo.com");
NavigateToPage(url, /*publicly_routable=*/true,
/*browsing_topics_permissions_policy_allowed=*/true,
/*interest_cohort_permissions_policy_allowed=*/false);
GetBrowsingTopicsPageLoadDataTracker()->OnBrowsingTopicsApiUsed(
HashedDomain(123), history_service_.get());
EXPECT_FALSE(BrowsingTopicsEligibleForURLVisit(history_service_.get(), url));
EXPECT_TRUE(
content::GetBrowsingTopicsApiUsage(topics_site_data_manager()).empty());
}
} // namespace browsing_topics