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 "components/browsing_topics/util.h"
8 #include "components/history/content/browser/history_context_helper.h"
9 #include "components/history/core/browser/history_service.h"
10 #include "content/public/browser/browser_context.h"
11 #include "content/public/browser/browsing_topics_site_data_manager.h"
12 #include "content/public/browser/navigation_entry.h"
13 #include "content/public/browser/render_frame_host.h"
14 #include "content/public/browser/render_process_host.h"
15 #include "content/public/browser/storage_partition.h"
16 #include "content/public/browser/web_contents.h"
17 #include "services/metrics/public/cpp/metrics_utils.h"
18 #include "services/metrics/public/cpp/ukm_builders.h"
19 #include "services/metrics/public/cpp/ukm_recorder.h"
20 #include "third_party/blink/public/common/features.h"
21 #include "third_party/blink/public/mojom/permissions_policy/permissions_policy_feature.mojom.h"
23 namespace browsing_topics {
25 // Max number of context domains to keep track of per page load to limit in-use
26 // memory. This should also be greater than
27 // `kBrowsingTopicsMaxNumberOfApiUsageContextDomainsToStorePerPageLoad`.
28 constexpr int kMaxNumberOfContextDomainsToMonitor = 1000;
30 BrowsingTopicsPageLoadDataTracker::~BrowsingTopicsPageLoadDataTracker() {
31 if (observed_hashed_context_domains_.empty())
34 // TODO(yaoxia): consider also recording metrics when the Chrome app goes into
35 // background, in which case the destructor may never be called.
36 ukm::UkmRecorder* ukm_recorder = ukm::UkmRecorder::Get();
37 ukm::builders::BrowsingTopics_PageLoad builder(source_id_);
39 builder.SetTopicsRequestingContextDomainsCount(
40 ukm::GetExponentialBucketMinForCounts1000(
41 observed_hashed_context_domains_.size()));
43 builder.Record(ukm_recorder->Get());
46 BrowsingTopicsPageLoadDataTracker::BrowsingTopicsPageLoadDataTracker(
48 : content::PageUserData<BrowsingTopicsPageLoadDataTracker>(page),
49 hashed_main_frame_host_(HashMainFrameHostForStorage(
50 page.GetMainDocument().GetLastCommittedOrigin().host())) {
51 CHECK(page.IsPrimary());
52 source_id_ = page.GetMainDocument().GetPageUkmSourceId();
54 // TODO(yaoxia): consider dropping the permissions policy checks. We require
55 // that the API is used in the page, and that already implies that the
56 // permissions policy is allowed.
58 if ((page.GetMainDocument().IsLastCommitIPAddressPubliclyRoutable() ||
59 base::FeatureList::IsEnabled(
60 blink::features::kBrowsingTopicsBypassIPIsPubliclyRoutableCheck)) &&
61 page.GetMainDocument().IsFeatureEnabled(
62 blink::mojom::PermissionsPolicyFeature::kBrowsingTopics) &&
63 page.GetMainDocument().IsFeatureEnabled(
64 blink::mojom::PermissionsPolicyFeature::
65 kBrowsingTopicsBackwardCompatible) &&
66 page.GetMainDocument().IsLastCrossDocumentNavigationStartedByUser()) {
67 eligible_to_commit_ = true;
71 void BrowsingTopicsPageLoadDataTracker::OnBrowsingTopicsApiUsed(
72 const HashedDomain& hashed_context_domain,
73 const std::string& context_domain,
74 history::HistoryService* history_service) {
75 if (!eligible_to_commit_)
78 // On the first API usage in the page, set the allowed bit in history.
79 if (observed_hashed_context_domains_.empty()) {
80 content::WebContents* web_contents =
81 content::WebContents::FromRenderFrameHost(&page().GetMainDocument());
83 history_service->SetBrowsingTopicsAllowed(
84 history::ContextIDForWebContents(web_contents),
85 web_contents->GetController().GetLastCommittedEntry()->GetUniqueID(),
86 web_contents->GetLastCommittedURL());
91 kBrowsingTopicsMaxNumberOfApiUsageContextDomainsToStorePerPageLoad
93 kMaxNumberOfContextDomainsToMonitor);
95 // Cap the number of context domains to keep track of per page load to limit
97 if (observed_hashed_context_domains_.size() >=
98 kMaxNumberOfContextDomainsToMonitor) {
103 observed_hashed_context_domains_.insert(hashed_context_domain).second;
105 // Ignore this context if we've already added it.
109 // Cap the number of context domains to store to disk per page load to limit
110 // disk memory usage.
111 if (observed_hashed_context_domains_.size() >
114 kBrowsingTopicsMaxNumberOfApiUsageContextDomainsToStorePerPageLoad
119 // Persist the usage now rather than at the end of the page load, as when the
120 // app enters background, it may be killed without further notification.
124 ->GetDefaultStoragePartition()
125 ->GetBrowsingTopicsSiteDataManager()
126 ->OnBrowsingTopicsApiUsed(hashed_main_frame_host_, hashed_context_domain,
127 context_domain, base::Time::Now());
130 PAGE_USER_DATA_KEY_IMPL(BrowsingTopicsPageLoadDataTracker);
132 } // namespace browsing_topics