blob: f26009c71b3a3abc3941e418ac84c05c7968b7f8 [file] [log] [blame]
// Copyright 2024 The Chromium Authors
// 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_redirect_observer.h"
#include "base/containers/contains.h"
#include "components/browsing_topics/browsing_topics_page_load_data_tracker.h"
#include "content/public/browser/navigation_handle.h"
#include "third_party/blink/public/common/features.h"
namespace browsing_topics {
// static
void BrowsingTopicsRedirectObserver::MaybeCreateForWebContents(
content::WebContents* web_contents) {
if (!base::FeatureList::IsEnabled(blink::features::kBrowsingTopics)) {
return;
}
BrowsingTopicsRedirectObserver::CreateForWebContents(web_contents);
}
BrowsingTopicsRedirectObserver::BrowsingTopicsRedirectObserver(
content::WebContents* web_contents)
: content::WebContentsObserver(web_contents),
content::WebContentsUserData<BrowsingTopicsRedirectObserver>(
*web_contents) {}
BrowsingTopicsRedirectObserver::~BrowsingTopicsRedirectObserver() = default;
void BrowsingTopicsRedirectObserver::ReadyToCommitNavigation(
content::NavigationHandle* navigation_handle) {
CHECK(
!base::Contains(pending_navigations_redirect_state_, navigation_handle));
if (!navigation_handle->IsInPrimaryMainFrame()) {
return;
}
if (!navigation_handle->IsRendererInitiated()) {
return;
}
if (navigation_handle->GetNavigationInitiatorActivationAndAdStatus() !=
blink::mojom::NavigationInitiatorActivationAndAdStatus::
kDidNotStartWithTransientActivation) {
return;
}
// This is the first commit to the RFH (e.g. new tab page).
if (navigation_handle->GetWebContents()
->GetController()
.IsInitialNavigation()) {
return;
}
// Renderer-initiated same-doc navigations should only go straight to
// `DidFinishNavigation`, skipping `ReadyToCommitNavigation`.
CHECK(!navigation_handle->IsSameDocument());
auto* previous_page_tracker =
BrowsingTopicsPageLoadDataTracker::GetOrCreateForPage(
navigation_handle->GetWebContents()
->GetPrimaryMainFrame()
->GetPage());
int pending_navigation_redirect_count =
previous_page_tracker->redirect_count() + 1;
int pending_navigation_redirect_with_topics_invoked_count =
previous_page_tracker->redirect_with_topics_invoked_count();
if (previous_page_tracker->topics_invoked()) {
pending_navigation_redirect_with_topics_invoked_count++;
}
pending_navigations_redirect_state_.emplace(
navigation_handle,
PendingNavigationRedirectState{
.redirect_count = pending_navigation_redirect_count,
.redirect_with_topics_invoked_count =
pending_navigation_redirect_with_topics_invoked_count});
}
void BrowsingTopicsRedirectObserver::DidFinishNavigation(
content::NavigationHandle* navigation_handle) {
auto it = pending_navigations_redirect_state_.find(navigation_handle);
// The new page isn't eligible for continued redirect tracking (i.e. the early
// return scenarios in `ReadyToCommitNavigation`), or the navigation does not
// commit (e.g. same-doc or download). For those cases, there's no need to
// explicitly initialize the redirect status.
if (it == pending_navigations_redirect_state_.end()) {
return;
}
auto extracted = pending_navigations_redirect_state_.extract(it);
// This could happen if the renderer process crashed between
// `ReadyToCommitNavigation` and `DidFinishNavigation`.
if (!navigation_handle->HasCommitted()) {
return;
}
content::Page& page = navigation_handle->GetRenderFrameHost()->GetPage();
auto* page_tracker = BrowsingTopicsPageLoadDataTracker::GetForPage(page);
// If the page already exists (in bfcache), we don't bother to update its
// redirect status, as the page won't be able to learn new information (via
// URL params).
if (!page_tracker) {
BrowsingTopicsPageLoadDataTracker::CreateForPage(
page, extracted.mapped().redirect_count,
extracted.mapped().redirect_with_topics_invoked_count);
}
}
WEB_CONTENTS_USER_DATA_KEY_IMPL(BrowsingTopicsRedirectObserver);
} // namespace browsing_topics