1 // Copyright 2022 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "components/browsing_topics/browsing_topics_page_load_data_tracker.h"
7 #include "base/files/scoped_temp_dir.h"
8 #include "base/memory/raw_ptr.h"
9 #include "base/test/bind.h"
10 #include "base/test/scoped_feature_list.h"
11 #include "components/browsing_topics/test_util.h"
12 #include "components/history/content/browser/history_context_helper.h"
13 #include "components/history/core/browser/history_database_params.h"
14 #include "components/history/core/browser/history_service.h"
15 #include "components/history/core/test/test_history_database.h"
16 #include "components/ukm/test_ukm_recorder.h"
17 #include "content/public/browser/browser_context.h"
18 #include "content/public/browser/navigation_entry.h"
19 #include "content/public/test/back_forward_cache_util.h"
20 #include "content/public/test/browsing_topics_test_util.h"
21 #include "content/public/test/navigation_simulator.h"
22 #include "content/public/test/test_utils.h"
23 #include "content/public/test/web_contents_tester.h"
24 #include "content/test/test_render_view_host.h"
25 #include "services/metrics/public/cpp/metrics_utils.h"
26 #include "services/metrics/public/cpp/ukm_builders.h"
27 #include "third_party/blink/public/common/permissions_policy/origin_with_possible_wildcards.h"
29 namespace browsing_topics {
31 class BrowsingTopicsPageLoadDataTrackerTest
32 : public content::RenderViewHostTestHarness {
34 BrowsingTopicsPageLoadDataTrackerTest() {
35 scoped_feature_list_.InitWithFeatures(
36 /*enabled_features=*/{blink::features::kBrowsingTopics},
37 /*disabled_features=*/{});
39 EXPECT_TRUE(temp_dir_.CreateUniqueTempDir());
41 history_service_ = std::make_unique<history::HistoryService>();
42 history_service_->Init(
43 history::TestHistoryDatabaseParamsForPath(temp_dir_.GetPath()));
46 ~BrowsingTopicsPageLoadDataTrackerTest() override = default;
48 void SetUp() override {
49 content::RenderViewHostTestHarness::SetUp();
51 // The test assumes pages gets deleted after navigation, triggering metrics
52 // recording. Disable back/forward cache to ensure that pages don't get
53 // preserved in the cache.
54 content::DisableBackForwardCacheForTesting(
55 web_contents(), content::BackForwardCache::TEST_REQUIRES_NO_CACHING);
58 void TearDown() override {
59 DCHECK(history_service_);
61 base::RunLoop run_loop;
62 history_service_->SetOnBackendDestroyTask(run_loop.QuitClosure());
63 history_service_.reset();
66 content::RenderViewHostTestHarness::TearDown();
69 void NavigateToPage(const GURL& url,
70 bool publicly_routable,
71 bool browsing_topics_permissions_policy_allowed,
72 bool interest_cohort_permissions_policy_allowed,
73 bool browser_initiated = true,
74 bool has_user_gesture = false,
75 bool add_same_document_nav = false) {
76 std::unique_ptr<content::NavigationSimulator> simulator;
77 if (browser_initiated) {
78 simulator = content::NavigationSimulator::CreateBrowserInitiated(
81 simulator = content::NavigationSimulator::CreateRendererInitiated(
84 simulator->SetHasUserGesture(has_user_gesture);
86 if (!publicly_routable) {
87 net::IPAddress address;
88 EXPECT_TRUE(address.AssignFromIPLiteral("0.0.0.0"));
89 simulator->SetSocketAddress(net::IPEndPoint(address, /*port=*/0));
92 blink::ParsedPermissionsPolicy policy;
94 if (!browsing_topics_permissions_policy_allowed) {
96 blink::mojom::PermissionsPolicyFeature::kBrowsingTopics,
97 /*allowed_origins=*/std::vector<blink::OriginWithPossibleWildcards>(),
98 /*self_if_matches=*/absl::nullopt,
99 /*matches_all_origins=*/false,
100 /*matches_opaque_src=*/false);
103 if (!interest_cohort_permissions_policy_allowed) {
105 blink::mojom::PermissionsPolicyFeature::
106 kBrowsingTopicsBackwardCompatible,
107 /*allowed_origins=*/std::vector<blink::OriginWithPossibleWildcards>(),
108 /*self_if_matches=*/absl::nullopt,
109 /*matches_all_origins=*/false,
110 /*matches_opaque_src=*/false);
113 simulator->SetPermissionsPolicyHeader(std::move(policy));
116 if (add_same_document_nav) {
117 content::NavigationSimulator::CreateRendererInitiated(url, main_rfh())
118 ->CommitSameDocument();
121 history_service_->AddPage(
122 url, base::Time::Now(),
123 history::ContextIDForWebContents(web_contents()),
124 web_contents()->GetController().GetLastCommittedEntry()->GetUniqueID(),
126 /*redirects=*/{}, ui::PageTransition::PAGE_TRANSITION_TYPED,
127 history::VisitSource::SOURCE_BROWSED,
128 /*did_replace_entry=*/false);
131 BrowsingTopicsPageLoadDataTracker* GetBrowsingTopicsPageLoadDataTracker() {
132 return BrowsingTopicsPageLoadDataTracker::GetOrCreateForPage(
133 web_contents()->GetPrimaryMainFrame()->GetPage());
136 content::BrowsingTopicsSiteDataManager* topics_site_data_manager() {
137 return web_contents()
138 ->GetBrowserContext()
139 ->GetDefaultStoragePartition()
140 ->GetBrowsingTopicsSiteDataManager();
144 base::test::ScopedFeatureList scoped_feature_list_;
146 std::unique_ptr<history::HistoryService> history_service_;
148 base::ScopedTempDir temp_dir_;
151 TEST_F(BrowsingTopicsPageLoadDataTrackerTest, OneUsage) {
152 GURL url("https://foo.com");
153 NavigateToPage(url, /*publicly_routable=*/true,
154 /*browsing_topics_permissions_policy_allowed=*/true,
155 /*interest_cohort_permissions_policy_allowed=*/true);
157 EXPECT_FALSE(BrowsingTopicsEligibleForURLVisit(history_service_.get(), url));
159 content::GetBrowsingTopicsApiUsage(topics_site_data_manager()).empty());
161 GetBrowsingTopicsPageLoadDataTracker()->OnBrowsingTopicsApiUsed(
162 HashedDomain(123), "bar.com", history_service_.get());
164 EXPECT_TRUE(BrowsingTopicsEligibleForURLVisit(history_service_.get(), url));
166 std::vector<ApiUsageContext> api_usage_contexts =
167 content::GetBrowsingTopicsApiUsage(topics_site_data_manager());
168 EXPECT_EQ(api_usage_contexts.size(), 1u);
169 EXPECT_EQ(api_usage_contexts[0].hashed_main_frame_host,
170 HashMainFrameHostForStorage("foo.com"));
171 EXPECT_EQ(api_usage_contexts[0].hashed_context_domain, HashedDomain(123));
174 TEST_F(BrowsingTopicsPageLoadDataTrackerTest, TwoUsages) {
175 GURL url("https://foo.com");
176 NavigateToPage(url, /*publicly_routable=*/true,
177 /*browsing_topics_permissions_policy_allowed=*/true,
178 /*interest_cohort_permissions_policy_allowed=*/true);
180 GetBrowsingTopicsPageLoadDataTracker()->OnBrowsingTopicsApiUsed(
181 HashedDomain(123), "bar.com", history_service_.get());
182 GetBrowsingTopicsPageLoadDataTracker()->OnBrowsingTopicsApiUsed(
183 HashedDomain(456), "buzz.com", history_service_.get());
185 EXPECT_TRUE(BrowsingTopicsEligibleForURLVisit(history_service_.get(), url));
187 std::vector<ApiUsageContext> api_usage_contexts =
188 content::GetBrowsingTopicsApiUsage(topics_site_data_manager());
189 EXPECT_EQ(api_usage_contexts.size(), 2u);
190 EXPECT_EQ(api_usage_contexts[0].hashed_main_frame_host,
191 HashMainFrameHostForStorage("foo.com"));
192 EXPECT_EQ(api_usage_contexts[0].hashed_context_domain, HashedDomain(123));
193 EXPECT_EQ(api_usage_contexts[1].hashed_main_frame_host,
194 HashMainFrameHostForStorage("foo.com"));
195 EXPECT_EQ(api_usage_contexts[1].hashed_context_domain, HashedDomain(456));
198 TEST_F(BrowsingTopicsPageLoadDataTrackerTest, OneUsage_PageLoadUkm) {
199 ukm::TestAutoSetUkmRecorder ukm_recorder;
201 NavigateToPage(GURL("https://foo.com"), /*publicly_routable=*/true,
202 /*browsing_topics_permissions_policy_allowed=*/true,
203 /*interest_cohort_permissions_policy_allowed=*/true);
205 GetBrowsingTopicsPageLoadDataTracker()->OnBrowsingTopicsApiUsed(
206 HashedDomain(123), "bar.com", history_service_.get());
208 NavigateToPage(GURL(url::kAboutBlankURL), /*publicly_routable=*/true,
209 /*browsing_topics_permissions_policy_allowed=*/true,
210 /*interest_cohort_permissions_policy_allowed=*/true);
212 auto entries = ukm_recorder.GetEntriesByName(
213 ukm::builders::BrowsingTopics_PageLoad::kEntryName);
214 EXPECT_EQ(1u, entries.size());
216 ASSERT_EQ(1, ukm::GetExponentialBucketMinForCounts1000(1));
218 ukm_recorder.ExpectEntryMetric(entries.back(),
219 ukm::builders::BrowsingTopics_PageLoad::
220 kTopicsRequestingContextDomainsCountName,
224 TEST_F(BrowsingTopicsPageLoadDataTrackerTest, OneThousandUsages_PageLoadUkm) {
225 ukm::TestAutoSetUkmRecorder ukm_recorder;
227 NavigateToPage(GURL("https://foo.com"), /*publicly_routable=*/true,
228 /*browsing_topics_permissions_policy_allowed=*/true,
229 /*interest_cohort_permissions_policy_allowed=*/true);
231 for (int i = 0; i < 1000; ++i) {
232 GetBrowsingTopicsPageLoadDataTracker()->OnBrowsingTopicsApiUsed(
233 HashedDomain(i), "i.com", history_service_.get());
236 NavigateToPage(GURL(url::kAboutBlankURL), /*publicly_routable=*/true,
237 /*browsing_topics_permissions_policy_allowed=*/true,
238 /*interest_cohort_permissions_policy_allowed=*/true);
240 auto entries = ukm_recorder.GetEntriesByName(
241 ukm::builders::BrowsingTopics_PageLoad::kEntryName);
242 EXPECT_EQ(1u, entries.size());
244 ASSERT_EQ(943, ukm::GetExponentialBucketMinForCounts1000(1000));
246 ukm_recorder.ExpectEntryMetric(entries.back(),
247 ukm::builders::BrowsingTopics_PageLoad::
248 kTopicsRequestingContextDomainsCountName,
252 TEST_F(BrowsingTopicsPageLoadDataTrackerTest, TwoThousandUsages_PageLoadUkm) {
253 ukm::TestAutoSetUkmRecorder ukm_recorder;
255 NavigateToPage(GURL("https://foo.com"), /*publicly_routable=*/true,
256 /*browsing_topics_permissions_policy_allowed=*/true,
257 /*interest_cohort_permissions_policy_allowed=*/true);
259 for (int i = 0; i < 2000; ++i) {
260 GetBrowsingTopicsPageLoadDataTracker()->OnBrowsingTopicsApiUsed(
261 HashedDomain(i), "i.com", history_service_.get());
264 NavigateToPage(GURL(url::kAboutBlankURL), /*publicly_routable=*/true,
265 /*browsing_topics_permissions_policy_allowed=*/true,
266 /*interest_cohort_permissions_policy_allowed=*/true);
268 auto entries = ukm_recorder.GetEntriesByName(
269 ukm::builders::BrowsingTopics_PageLoad::kEntryName);
270 EXPECT_EQ(1u, entries.size());
272 ASSERT_EQ(943, ukm::GetExponentialBucketMinForCounts1000(1000));
273 ASSERT_EQ(1896, ukm::GetExponentialBucketMinForCounts1000(2000));
275 ukm_recorder.ExpectEntryMetric(entries.back(),
276 ukm::builders::BrowsingTopics_PageLoad::
277 kTopicsRequestingContextDomainsCountName,
281 TEST_F(BrowsingTopicsPageLoadDataTrackerTest, DuplicateDomains) {
282 GURL url("https://foo.com");
283 NavigateToPage(url, /*publicly_routable=*/true,
284 /*browsing_topics_permissions_policy_allowed=*/true,
285 /*interest_cohort_permissions_policy_allowed=*/true);
287 GetBrowsingTopicsPageLoadDataTracker()->OnBrowsingTopicsApiUsed(
288 HashedDomain(123), "bar.com", history_service_.get());
289 GetBrowsingTopicsPageLoadDataTracker()->OnBrowsingTopicsApiUsed(
290 HashedDomain(456), "buzz.com", history_service_.get());
291 GetBrowsingTopicsPageLoadDataTracker()->OnBrowsingTopicsApiUsed(
292 HashedDomain(123), "bar.com", history_service_.get());
294 EXPECT_TRUE(BrowsingTopicsEligibleForURLVisit(history_service_.get(), url));
296 std::vector<ApiUsageContext> api_usage_contexts =
297 content::GetBrowsingTopicsApiUsage(topics_site_data_manager());
298 EXPECT_EQ(api_usage_contexts.size(), 2u);
299 EXPECT_EQ(api_usage_contexts[0].hashed_main_frame_host,
300 HashMainFrameHostForStorage("foo.com"));
301 EXPECT_EQ(api_usage_contexts[0].hashed_context_domain, HashedDomain(123));
302 EXPECT_EQ(api_usage_contexts[1].hashed_main_frame_host,
303 HashMainFrameHostForStorage("foo.com"));
304 EXPECT_EQ(api_usage_contexts[1].hashed_context_domain, HashedDomain(456));
306 // The second HashedDomain(123) shouldn't update the database. Verify this by
307 // verifying that the timestamp for HashedDomain(123) is no greater than the
308 // timestamp for HashedDomain(456).
309 EXPECT_LE(api_usage_contexts[0].time, api_usage_contexts[1].time);
312 TEST_F(BrowsingTopicsPageLoadDataTrackerTest, NumberOfDomainsExceedsLimit) {
313 GURL url("https://foo.com");
314 NavigateToPage(url, /*publicly_routable=*/true,
315 /*browsing_topics_permissions_policy_allowed=*/true,
316 /*interest_cohort_permissions_policy_allowed=*/true);
318 for (int i = 0; i < 31; ++i) {
319 GetBrowsingTopicsPageLoadDataTracker()->OnBrowsingTopicsApiUsed(
320 HashedDomain(i), "i.com", history_service_.get());
323 EXPECT_TRUE(BrowsingTopicsEligibleForURLVisit(history_service_.get(), url));
325 std::vector<ApiUsageContext> api_usage_contexts =
326 content::GetBrowsingTopicsApiUsage(topics_site_data_manager());
328 EXPECT_EQ(api_usage_contexts.size(), 30u);
330 for (int i = 0; i < 30; ++i) {
331 EXPECT_EQ(api_usage_contexts[i].hashed_main_frame_host,
332 HashMainFrameHostForStorage("foo.com"));
333 EXPECT_EQ(api_usage_contexts[i].hashed_context_domain, HashedDomain(i));
337 TEST_F(BrowsingTopicsPageLoadDataTrackerTest, NotPubliclyRoutable) {
338 GURL url("https://foo.com");
339 NavigateToPage(url, /*publicly_routable=*/false,
340 /*browsing_topics_permissions_policy_allowed=*/true,
341 /*interest_cohort_permissions_policy_allowed=*/true);
343 GetBrowsingTopicsPageLoadDataTracker()->OnBrowsingTopicsApiUsed(
344 HashedDomain(123), "bar.com", history_service_.get());
346 EXPECT_FALSE(BrowsingTopicsEligibleForURLVisit(history_service_.get(), url));
348 content::GetBrowsingTopicsApiUsage(topics_site_data_manager()).empty());
351 TEST_F(BrowsingTopicsPageLoadDataTrackerTest,
352 BrowsingTopicsPermissionsPolicyNotAllowed) {
353 GURL url("https://foo.com");
354 NavigateToPage(url, /*publicly_routable=*/true,
355 /*browsing_topics_permissions_policy_allowed=*/false,
356 /*interest_cohort_permissions_policy_allowed=*/true);
358 GetBrowsingTopicsPageLoadDataTracker()->OnBrowsingTopicsApiUsed(
359 HashedDomain(123), "bar.com", history_service_.get());
361 EXPECT_FALSE(BrowsingTopicsEligibleForURLVisit(history_service_.get(), url));
363 content::GetBrowsingTopicsApiUsage(topics_site_data_manager()).empty());
366 TEST_F(BrowsingTopicsPageLoadDataTrackerTest,
367 InterestCohortPermissionsPolicyNotAllowed) {
368 GURL url("https://foo.com");
369 NavigateToPage(url, /*publicly_routable=*/true,
370 /*browsing_topics_permissions_policy_allowed=*/true,
371 /*interest_cohort_permissions_policy_allowed=*/false);
373 GetBrowsingTopicsPageLoadDataTracker()->OnBrowsingTopicsApiUsed(
374 HashedDomain(123), "bar.com", history_service_.get());
376 EXPECT_FALSE(BrowsingTopicsEligibleForURLVisit(history_service_.get(), url));
378 content::GetBrowsingTopicsApiUsage(topics_site_data_manager()).empty());
381 TEST_F(BrowsingTopicsPageLoadDataTrackerTest,
382 RendererInitiatedWithUserGesture) {
383 GURL url("https://foo.com");
384 NavigateToPage(url, /*publicly_routable=*/true,
385 /*browsing_topics_permissions_policy_allowed=*/true,
386 /*interest_cohort_permissions_policy_allowed=*/true,
387 /*browser_initiated=*/false,
388 /*has_user_gesture=*/true);
390 GetBrowsingTopicsPageLoadDataTracker()->OnBrowsingTopicsApiUsed(
391 HashedDomain(123), "bar.com", history_service_.get());
393 EXPECT_TRUE(BrowsingTopicsEligibleForURLVisit(history_service_.get(), url));
395 std::vector<ApiUsageContext> api_usage_contexts =
396 content::GetBrowsingTopicsApiUsage(topics_site_data_manager());
397 EXPECT_EQ(api_usage_contexts.size(), 1u);
398 EXPECT_EQ(api_usage_contexts[0].hashed_main_frame_host,
399 HashMainFrameHostForStorage("foo.com"));
400 EXPECT_EQ(api_usage_contexts[0].hashed_context_domain, HashedDomain(123));
403 TEST_F(BrowsingTopicsPageLoadDataTrackerTest, RendererInitiatedNoUserGesture) {
404 GURL url("https://foo.com");
405 NavigateToPage(url, /*publicly_routable=*/true,
406 /*browsing_topics_permissions_policy_allowed=*/true,
407 /*interest_cohort_permissions_policy_allowed=*/true,
408 /*browser_initiated=*/false,
409 /*has_user_gesture=*/false);
411 GetBrowsingTopicsPageLoadDataTracker()->OnBrowsingTopicsApiUsed(
412 HashedDomain(123), "bar.com", history_service_.get());
414 EXPECT_FALSE(BrowsingTopicsEligibleForURLVisit(history_service_.get(), url));
416 content::GetBrowsingTopicsApiUsage(topics_site_data_manager()).empty());
419 TEST_F(BrowsingTopicsPageLoadDataTrackerTest,
420 RendererInitiatedPlusExtraSamePageNav) {
421 GURL url("https://foo.com");
422 NavigateToPage(url, /*publicly_routable=*/true,
423 /*browsing_topics_permissions_policy_allowed=*/true,
424 /*interest_cohort_permissions_policy_allowed=*/true,
425 /*browser_initiated=*/false,
426 /*has_user_gesture=*/true,
427 /*add_same_document_nav=*/true);
429 GetBrowsingTopicsPageLoadDataTracker()->OnBrowsingTopicsApiUsed(
430 HashedDomain(123), "bar.com", history_service_.get());
432 EXPECT_TRUE(BrowsingTopicsEligibleForURLVisit(history_service_.get(), url));
434 std::vector<ApiUsageContext> api_usage_contexts =
435 content::GetBrowsingTopicsApiUsage(topics_site_data_manager());
436 EXPECT_EQ(api_usage_contexts.size(), 1u);
437 EXPECT_EQ(api_usage_contexts[0].hashed_main_frame_host,
438 HashMainFrameHostForStorage("foo.com"));
439 EXPECT_EQ(api_usage_contexts[0].hashed_context_domain, HashedDomain(123));
442 } // namespace browsing_topics