blob: 2424fbecbb45336f4bc67c28ee0dc59d3202123e [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.
#ifndef CHROME_BROWSER_DIPS_DIPS_NAVIGATION_FLOW_DETECTOR_H_
#define CHROME_BROWSER_DIPS_DIPS_NAVIGATION_FLOW_DETECTOR_H_
#include <optional>
#include <string>
#include "base/memory/raw_ptr.h"
#include "base/memory/raw_ref.h"
#include "base/memory/weak_ptr.h"
#include "base/time/clock.h"
#include "base/time/default_clock.h"
#include "base/time/time.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/browser/web_contents_user_data.h"
#include "services/metrics/public/cpp/ukm_source_id.h"
#include "third_party/blink/public/mojom/frame/frame.mojom-forward.h"
#include "url/gurl.h"
class DIPSService;
namespace content {
struct CookieAccessDetails;
class NavigationHandle;
class RenderFrameHost;
} // namespace content
namespace dips {
struct PageVisitInfo {
PageVisitInfo();
PageVisitInfo(PageVisitInfo&& other);
std::string site;
ukm::SourceId source_id;
bool did_page_access_cookies;
bool did_page_access_storage;
bool did_page_receive_user_activation;
bool did_page_have_successful_waa;
std::optional<bool> was_navigation_to_page_renderer_initiated;
std::optional<bool> was_navigation_to_page_user_initiated;
std::optional<bool> did_site_have_prior_activation_record;
};
} // namespace dips
// Detects possible navigation flows with the aim of discovering how to
// distinguish user-interest navigation flows from navigational tracking.
// Currently only reports UKM to inform how we might identify possible
// navigational tracking by sites that also perform user-interest activity.
class DipsNavigationFlowDetector
: public content::WebContentsObserver,
public content::WebContentsUserData<DipsNavigationFlowDetector> {
public:
~DipsNavigationFlowDetector() override;
static void MaybeCreateForWebContents(content::WebContents* web_contents);
void SetClockForTesting(base::Clock* clock) {
CHECK(clock);
clock_ = *clock;
}
protected:
explicit DipsNavigationFlowDetector(content::WebContents* web_contents,
DIPSService* dips_service);
void MaybeEmitUkmForPreviousPage();
bool CanEmitUkmForPreviousPage() {
bool page_is_in_series_of_three = two_pages_ago_visit_info_.has_value() &&
previous_page_visit_info_.has_value() &&
current_page_visit_info_.has_value();
if (!page_is_in_series_of_three) {
return false;
}
bool page_has_valid_source_id =
previous_page_visit_info_->source_id != ukm::kInvalidSourceId;
bool site_had_triggering_storage_access =
previous_page_visit_info_->did_page_access_cookies ||
previous_page_visit_info_->did_page_access_storage;
bool is_site_different_from_prior_page =
previous_page_visit_info_->site != two_pages_ago_visit_info_->site;
bool is_site_different_from_next_page =
previous_page_visit_info_->site != current_page_visit_info_->site;
return page_has_valid_source_id && site_had_triggering_storage_access &&
is_site_different_from_prior_page &&
is_site_different_from_next_page;
}
private:
// So the controller can call the constructor.
friend class DipsNavigationFlowController;
// So WebContentsUserData::CreateForWebContents can call the constructor.
friend class content::WebContentsUserData<DipsNavigationFlowDetector>;
// start WebContentsObserver overrides
void DidFinishNavigation(
content::NavigationHandle* navigation_handle) override;
// For client-initiated cookie accesses, and late-reported cookie accesses in
// navigations.
void OnCookiesAccessed(content::RenderFrameHost* render_frame_host,
const content::CookieAccessDetails& details) override;
// For cookie accesses in navigations.
void OnCookiesAccessed(content::NavigationHandle* navigation_handle,
const content::CookieAccessDetails& details) override;
void NotifyStorageAccessed(content::RenderFrameHost* render_frame_host,
blink::mojom::StorageTypeAccessed storage_type,
bool blocked) override;
void FrameReceivedUserActivation(
content::RenderFrameHost* render_frame_host) override;
void WebAuthnAssertionRequestSucceeded(
content::RenderFrameHost* render_frame_host) override;
// end WebContentsObserver overrides
void CheckIfSiteHadPriorActivation(GURL url);
void GotDipsInteraction(std::string site_read_state_for,
bool had_interaction);
std::optional<dips::PageVisitInfo> two_pages_ago_visit_info_;
std::optional<dips::PageVisitInfo> previous_page_visit_info_;
std::optional<dips::PageVisitInfo> current_page_visit_info_;
base::Time last_page_change_time_;
long bucketized_previous_page_visit_duration_;
// raw_ptr<> is safe here because DIPSService is a KeyedService, associated
// with the BrowserContext/Profile, which will outlive the WebContents that
// DipsNavigationFlowDetector is observing.
raw_ptr<DIPSService> dips_service_;
raw_ref<base::Clock> clock_{*base::DefaultClock::GetInstance()};
base::WeakPtrFactory<DipsNavigationFlowDetector> weak_factory_{this};
WEB_CONTENTS_USER_DATA_KEY_DECL();
};
#endif // CHROME_BROWSER_DIPS_DIPS_NAVIGATION_FLOW_DETECTOR_H_