// 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 "components/browsing_topics/util.h" #include "components/history/content/browser/history_context_helper.h" #include "components/history/core/browser/history_service.h" #include "content/public/browser/browsing_topics_site_data_manager.h" #include "content/public/browser/navigation_entry.h" #include "content/public/browser/render_frame_host.h" #include "content/public/browser/render_process_host.h" #include "content/public/browser/storage_partition.h" #include "content/public/browser/web_contents.h" #include "services/metrics/public/cpp/metrics_utils.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" #include "third_party/blink/public/mojom/permissions_policy/permissions_policy_feature.mojom.h" namespace browsing_topics { // Max number of context domains to keep track of per page load to limit in-use // memory. This should also be greater than // `kBrowsingTopicsMaxNumberOfApiUsageContextDomainsToStorePerPageLoad`. constexpr int kMaxNumberOfContextDomainsToMonitor = 1000; BrowsingTopicsPageLoadDataTracker::~BrowsingTopicsPageLoadDataTracker() { if (observed_hashed_context_domains_.empty()) return; // TODO(yaoxia): consider also recording metrics when the Chrome app goes into // background, in which case the destructor may never be called. ukm::UkmRecorder* ukm_recorder = ukm::UkmRecorder::Get(); ukm::builders::BrowsingTopics_PageLoad builder(source_id_); builder.SetTopicsRequestingContextDomainsCount( ukm::GetExponentialBucketMinForCounts1000( observed_hashed_context_domains_.size())); builder.Record(ukm_recorder->Get()); } BrowsingTopicsPageLoadDataTracker::BrowsingTopicsPageLoadDataTracker( content::Page& page) : content::PageUserData(page), hashed_main_frame_host_(HashMainFrameHostForStorage( page.GetMainDocument().GetLastCommittedOrigin().host())), source_id_(page.GetMainDocument().GetPageUkmSourceId()) { DCHECK(page.IsPrimary()); // TODO(yaoxia): consider dropping the permissions policy checks. We require // that the API is used in the page, and that already implies that the // permissions policy is allowed. if ((page.GetMainDocument().IsLastCommitIPAddressPubliclyRoutable() || base::FeatureList::IsEnabled( blink::features::kBrowsingTopicsBypassIPIsPubliclyRoutableCheck)) && page.GetMainDocument().IsFeatureEnabled( blink::mojom::PermissionsPolicyFeature::kBrowsingTopics) && page.GetMainDocument().IsFeatureEnabled( blink::mojom::PermissionsPolicyFeature:: kBrowsingTopicsBackwardCompatible)) { eligible_to_commit_ = true; } } void BrowsingTopicsPageLoadDataTracker::OnBrowsingTopicsApiUsed( const HashedDomain& hashed_context_domain, history::HistoryService* history_service) { if (!eligible_to_commit_) return; // On the first API usage in the page, set the allowed bit in history. if (observed_hashed_context_domains_.empty()) { content::WebContents* web_contents = content::WebContents::FromRenderFrameHost(&page().GetMainDocument()); history_service->SetBrowsingTopicsAllowed( history::ContextIDForWebContents(web_contents), web_contents->GetController().GetLastCommittedEntry()->GetUniqueID(), web_contents->GetLastCommittedURL()); } DCHECK_LE( blink::features:: kBrowsingTopicsMaxNumberOfApiUsageContextDomainsToStorePerPageLoad .Get(), kMaxNumberOfContextDomainsToMonitor); // Cap the number of context domains to keep track of per page load to limit // in-use memory. if (observed_hashed_context_domains_.size() >= kMaxNumberOfContextDomainsToMonitor) { return; } bool is_new_domain = observed_hashed_context_domains_.insert(hashed_context_domain).second; // Ignore this context if we've already added it. if (!is_new_domain) return; // Cap the number of context domains to store to disk per page load to limit // disk memory usage. if (observed_hashed_context_domains_.size() > static_cast( blink::features:: kBrowsingTopicsMaxNumberOfApiUsageContextDomainsToStorePerPageLoad .Get())) { return; } // Persist the usage now rather than at the end of the page load, as when the // app enters background, it may be killed without further notification. page() .GetMainDocument() .GetProcess() ->GetStoragePartition() ->GetBrowsingTopicsSiteDataManager() ->OnBrowsingTopicsApiUsed(hashed_main_frame_host_, {hashed_context_domain}, base::Time::Now()); } PAGE_USER_DATA_KEY_IMPL(BrowsingTopicsPageLoadDataTracker); } // namespace browsing_topics