blob: 12d660e8d866eee708c0d65867a1bbc2410852f8 [file] [log] [blame]
// Copyright 2019 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/content_settings/browser/ui/cookie_controls_controller.h"
#include <memory>
#include "base/feature_list.h"
#include "base/functional/bind.h"
#include "base/json/values_util.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/user_metrics.h"
#include "base/metrics/user_metrics_action.h"
#include "base/observer_list.h"
#include "components/browsing_data/content/browsing_data_helper.h"
#include "components/browsing_data/content/local_shared_objects_container.h"
#include "components/content_settings/browser/page_specific_content_settings.h"
#include "components/content_settings/browser/ui/cookie_controls_view.h"
#include "components/content_settings/core/browser/content_settings_utils.h"
#include "components/content_settings/core/browser/cookie_settings.h"
#include "components/content_settings/core/common/content_settings.h"
#include "components/content_settings/core/common/content_settings_pattern.h"
#include "components/content_settings/core/common/content_settings_utils.h"
#include "components/content_settings/core/common/cookie_blocking_3pcd_status.h"
#include "components/content_settings/core/common/cookie_controls_enforcement.h"
#include "components/content_settings/core/common/cookie_controls_status.h"
#include "components/content_settings/core/common/features.h"
#include "components/content_settings/core/common/pref_names.h"
#include "components/content_settings/core/common/third_party_site_data_access_type.h"
#include "components/prefs/pref_service.h"
#include "components/privacy_sandbox/tracking_protection_settings.h"
#include "components/site_engagement/content/site_engagement_service.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/reload_type.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/url_constants.h"
#include "net/cookies/site_for_cookies.h"
#include "services/metrics/public/cpp/ukm_builders.h"
#include "services/metrics/public/cpp/ukm_recorder.h"
#include "services/metrics/public/cpp/ukm_source_id.h"
using base::UserMetricsAction;
using site_engagement::SiteEngagementService;
namespace {
constexpr char kEntryPointAnimatedKey[] = "entry_point_animated";
constexpr char kLastExpirationKey[] = "last_expiration";
constexpr char kLastVisitedActiveException[] = "last_visited_active_exception";
constexpr char kActivationsCountKey[] = "activations_count_key";
base::Value::Dict GetMetadata(HostContentSettingsMap* settings_map,
const GURL& url) {
base::Value stored_value = settings_map->GetWebsiteSetting(
url, url, ContentSettingsType::COOKIE_CONTROLS_METADATA);
if (!stored_value.is_dict()) {
return base::Value::Dict();
}
return std::move(stored_value.GetDict());
}
bool WasEntryPointAlreadyAnimated(const base::Value::Dict& metadata) {
std::optional<bool> entry_point_animated =
metadata.FindBool(kEntryPointAnimatedKey);
return entry_point_animated.has_value() && entry_point_animated.value();
}
int GetActivationCount(const base::Value::Dict& metadata) {
return metadata.FindInt(kActivationsCountKey).value_or(0);
}
bool HasExceptionExpiredSinceLastVisit(const base::Value::Dict& metadata) {
auto last_expiration = base::ValueToTime(metadata.Find(kLastExpirationKey))
.value_or(base::Time());
auto last_visited =
base::ValueToTime(metadata.Find(kLastVisitedActiveException))
.value_or(base::Time());
return !last_expiration.is_null() // Exception should have an expiration,
&& last_expiration < base::Time::Now() // that has already expired,
&& !last_visited.is_null() // from a previous visit,
&& last_visited < last_expiration; // with no visit since.
}
void ApplyMetadataChanges(HostContentSettingsMap* settings_map,
const GURL& url,
base::Value::Dict&& dict) {
settings_map->SetWebsiteSettingDefaultScope(
url, url, ContentSettingsType::COOKIE_CONTROLS_METADATA,
base::Value(std::move(dict)));
}
ThirdPartySiteDataAccessType GetSiteDataAccessType(int allowed_sites,
int blocked_sites) {
if (blocked_sites > 0) {
return ThirdPartySiteDataAccessType::kAnyBlockedThirdPartySiteAccesses;
}
if (allowed_sites > 0) {
return ThirdPartySiteDataAccessType::kAnyAllowedThirdPartySiteAccesses;
}
return ThirdPartySiteDataAccessType::kNoThirdPartySiteAccesses;
}
} // namespace
namespace content_settings {
CookieControlsController::CookieControlsController(
scoped_refptr<CookieSettings> cookie_settings,
scoped_refptr<CookieSettings> original_cookie_settings,
HostContentSettingsMap* settings_map,
privacy_sandbox::TrackingProtectionSettings* tracking_protection_settings)
: cookie_settings_(cookie_settings),
original_cookie_settings_(original_cookie_settings),
settings_map_(settings_map),
tracking_protection_settings_(tracking_protection_settings) {
cookie_observation_.Observe(cookie_settings_.get());
}
CookieControlsController::~CookieControlsController() = default;
void CookieControlsController::OnUiClosing() {
auto* web_contents = GetWebContents();
if (should_reload_ && web_contents && !web_contents->IsBeingDestroyed()) {
web_contents->GetController().Reload(content::ReloadType::NORMAL, true);
}
should_reload_ = false;
}
void CookieControlsController::Update(content::WebContents* web_contents) {
DCHECK(web_contents);
if (!tab_observer_ || GetWebContents() != web_contents) {
tab_observer_ = std::make_unique<TabObserver>(this, web_contents);
SetUserChangedCookieBlockingForSite(false);
}
auto status = GetStatus(web_contents);
int third_party_allowed_sites = GetAllowedThirdPartyCookiesSitesCount();
int third_party_blocked_sites = GetBlockedThirdPartyCookiesSitesCount();
for (auto& observer : observers_) {
observer.OnStatusChanged(status.status, status.controls_visible,
status.protections_on, status.enforcement,
status.blocking_status, status.expiration);
observer.OnSitesCountChanged(third_party_allowed_sites,
third_party_blocked_sites);
observer.OnUserBypassIconStatusChanged(
status.controls_visible, status.protections_on, status.blocking_status);
observer.OnBreakageConfidenceLevelChanged(
GetConfidenceLevel(status.status, status.enforcement,
SiteDataAccessed(third_party_allowed_sites,
third_party_blocked_sites)));
}
}
CookieControlsController::Status CookieControlsController::GetStatus(
content::WebContents* web_contents) {
if (!cookie_settings_->ShouldBlockThirdPartyCookies()) {
return {CookieControlsStatus::kDisabled,
/*controls_visible=*/false,
/*protections_on=*/false,
CookieControlsEnforcement::kNoEnforcement,
CookieBlocking3pcdStatus::kNotIn3pcd,
base::Time()};
}
const GURL& url = web_contents->GetLastCommittedURL();
if (url.SchemeIs(content::kChromeUIScheme) ||
url.SchemeIs(kExtensionScheme)) {
return {CookieControlsStatus::kDisabled,
/*controls_visible=*/false,
/*protections_on=*/false,
CookieControlsEnforcement::kNoEnforcement,
CookieBlocking3pcdStatus::kNotIn3pcd,
base::Time()};
}
SettingInfo info;
bool is_allowed = cookie_settings_->IsThirdPartyAccessAllowed(url, &info);
const bool is_default_setting =
info.primary_pattern == ContentSettingsPattern::Wildcard() &&
info.secondary_pattern == ContentSettingsPattern::Wildcard();
// The UI can reset only host-scoped (without wildcards in the domain) or
// site-scoped exceptions.
const bool host_or_site_scoped_exception =
!info.secondary_pattern.HasDomainWildcard() ||
info.secondary_pattern ==
ContentSettingsPattern::FromURLToSchemefulSitePattern(url);
// Rules from regular mode can't be temporarily overridden in incognito.
bool exception_exists_in_regular_profile = false;
if (is_allowed && original_cookie_settings_) {
SettingInfo original_info;
original_cookie_settings_->IsThirdPartyAccessAllowed(url, &original_info);
exception_exists_in_regular_profile =
original_info.primary_pattern != ContentSettingsPattern::Wildcard() ||
original_info.secondary_pattern != ContentSettingsPattern::Wildcard();
}
CookieControlsStatus status = is_allowed
? CookieControlsStatus::kDisabledForSite
: CookieControlsStatus::kEnabled;
CookieBlocking3pcdStatus blocking_status =
CookieBlocking3pcdStatus::kNotIn3pcd;
if (tracking_protection_settings_ &&
tracking_protection_settings_->IsTrackingProtection3pcdEnabled()) {
blocking_status =
tracking_protection_settings_->AreAllThirdPartyCookiesBlocked()
? CookieBlocking3pcdStatus::kAll
: CookieBlocking3pcdStatus::kLimited;
}
CookieControlsEnforcement enforcement;
bool controls_visible = true;
if (info.source == SETTING_SOURCE_TPCD_GRANT &&
blocking_status == CookieBlocking3pcdStatus::kLimited) {
controls_visible = false;
enforcement = CookieControlsEnforcement::kEnforcedByTpcdGrant;
} else if (info.source == SETTING_SOURCE_POLICY) {
enforcement = CookieControlsEnforcement::kEnforcedByPolicy;
} else if (info.source == SETTING_SOURCE_EXTENSION) {
enforcement = CookieControlsEnforcement::kEnforcedByExtension;
} else if (exception_exists_in_regular_profile ||
(!is_default_setting && !host_or_site_scoped_exception)) {
// If the exception cannot be reset in-context because of the nature of the
// setting, display as managed by setting.
enforcement = CookieControlsEnforcement::kEnforcedByCookieSetting;
} else {
enforcement = CookieControlsEnforcement::kNoEnforcement;
}
return {status,
/*controls_visible=*/controls_visible,
/*protections_on=*/!is_allowed,
enforcement,
blocking_status,
info.metadata.expiration()};
}
CookieControlsBreakageConfidenceLevel
CookieControlsController::GetConfidenceLevel(
CookieControlsStatus status,
CookieControlsEnforcement enforcement,
bool site_data_accessed) {
// If 3PC cookies are not blocked by default:
switch (status) {
case CookieControlsStatus::kDisabled:
case CookieControlsStatus::kUninitialized:
return CookieControlsBreakageConfidenceLevel::kUninitialized;
case CookieControlsStatus::kDisabledForSite:
if (enforcement == CookieControlsEnforcement::kEnforcedByTpcdGrant) {
return CookieControlsBreakageConfidenceLevel::kUninitialized;
}
return CookieControlsBreakageConfidenceLevel::kMedium;
case CookieControlsStatus::kEnabled:
// Check other conditions to determine the level.
break;
}
// 3PCD prevents SameSite=None cookies from being sent when the top-level
// document is sandboxed without `allow-origin`. For instance when loaded
// with: `Content-Security-Policy: sandbox`. In that case, we render the UI to
// allow the user to opt into sending SameSite=None cookies again in those
// contexts.
if (HasOriginSandboxedTopLevelDocument()) {
return CookieControlsBreakageConfidenceLevel::kMedium;
}
// If no 3P sites have attempted to access site data, nor were any stateful
// bounces recorded, return low confidence. Take into account both allow and
// blocked counts, since the breakage might be related to storage
// partitioning. Partitioned site will be allowed to access partitioned
// storage.
if (!site_data_accessed) {
return CookieControlsBreakageConfidenceLevel::kLow;
}
// Return `kMedium` confidence for incognito and Guest profile. We don't want
// to show high-confidence UI (animation, IPH) in these cases as we can't
// persist their usage cross-session. This puts us at high risk of
// over-triggering noisy UI and annoying users.
auto* web_contents = GetWebContents();
if (web_contents->GetBrowserContext()->IsOffTheRecord()) {
return CookieControlsBreakageConfidenceLevel::kMedium;
}
// TODO(crbug.com/1446230): Check if FedCM was requested.
const GURL& url = web_contents->GetLastCommittedURL();
if (cookie_settings_->HasAnyFrameRequestedStorageAccess(url)) {
return CookieControlsBreakageConfidenceLevel::kMedium;
}
// If the user is returning to the site after their previous exception has
// expired, return high confidence. The order of this check is important,
// as the site may now be using SAA / FedCM instead of relying on 3PC. It
// should also come before any check for whether the entrypoint was already
// animated.
if (has_exception_expired_since_last_visit_) {
return CookieControlsBreakageConfidenceLevel::kHigh;
}
// Check if the entry point was already animated for the site and return
// medium confidence in that case.
if (WasEntryPointAlreadyAnimated(GetMetadata(settings_map_, url))) {
return CookieControlsBreakageConfidenceLevel::kMedium;
}
if (recent_reloads_count_ >= features::kUserBypassUIReloadCount.Get()) {
return CookieControlsBreakageConfidenceLevel::kHigh;
}
if (SiteEngagementService::IsEngagementAtLeast(
GetSiteEngagementScore(), blink::mojom::EngagementLevel::HIGH)) {
return CookieControlsBreakageConfidenceLevel::kHigh;
}
// Default to a medium confidence level, as by this point the site has
// accessed 3P storage, but there is no signal that would give us high
// confidence.
return CookieControlsBreakageConfidenceLevel::kMedium;
}
bool CookieControlsController::HasOriginSandboxedTopLevelDocument() const {
return GetWebContents()->GetPrimaryMainFrame()->IsSandboxed(
network::mojom::WebSandboxFlags::kOrigin);
}
void CookieControlsController::OnCookieBlockingEnabledForSite(
bool block_third_party_cookies) {
const GURL& url = GetWebContents()->GetLastCommittedURL();
should_reload_ = true;
if (block_third_party_cookies) {
base::RecordAction(UserMetricsAction("CookieControls.Bubble.TurnOn"));
cookie_settings_->ResetThirdPartyCookieSetting(url);
return;
}
CHECK(!block_third_party_cookies);
base::RecordAction(UserMetricsAction("CookieControls.Bubble.TurnOff"));
cookie_settings_->SetCookieSettingForUserBypass(url);
// Record expiration metadata for the newly created exception, and increased
// the activation count.
base::Value::Dict metadata = GetMetadata(settings_map_, url);
metadata.Set(kLastExpirationKey,
base::TimeToValue(GetStatus(GetWebContents()).expiration));
metadata.Set(kActivationsCountKey, GetActivationCount(metadata) + 1);
ApplyMetadataChanges(settings_map_, url, std::move(metadata));
RecordActivationMetrics();
}
void CookieControlsController::OnEntryPointAnimated() {
const GURL& url = GetWebContents()->GetLastCommittedURL();
base::Value::Dict metadata = GetMetadata(settings_map_, url);
metadata.Set(kEntryPointAnimatedKey, base::Value(true));
ApplyMetadataChanges(settings_map_, url, std::move(metadata));
}
bool CookieControlsController::FirstPartyCookiesBlocked() {
// No overrides are given since existing ones only pertain to 3P checks.
const GURL& url = GetWebContents()->GetLastCommittedURL();
return !cookie_settings_->IsFullCookieAccessAllowed(
url, net::SiteForCookies::FromUrl(url), url::Origin::Create(url),
net::CookieSettingOverrides());
}
bool CookieControlsController::HasUserChangedCookieBlockingForSite() {
return user_changed_cookie_blocking_;
}
void CookieControlsController::SetUserChangedCookieBlockingForSite(
bool changed) {
// Avoid a toggle back and forth being marked as "changed".
user_changed_cookie_blocking_ = changed && !user_changed_cookie_blocking_;
}
CookieControlsBreakageConfidenceLevel
CookieControlsController::GetBreakageConfidenceLevel() {
auto status = GetStatus(GetWebContents());
int third_party_allowed_sites = GetAllowedThirdPartyCookiesSitesCount();
int third_party_blocked_sites = GetBlockedThirdPartyCookiesSitesCount();
return GetConfidenceLevel(
status.status, status.enforcement,
SiteDataAccessed(third_party_allowed_sites, third_party_blocked_sites));
}
CookieControlsStatus CookieControlsController::GetCookieControlsStatus() {
auto status = GetStatus(GetWebContents());
return status.status;
}
int CookieControlsController::GetAllowedThirdPartyCookiesSitesCount() const {
auto* pscs = content_settings::PageSpecificContentSettings::GetForPage(
GetWebContents()->GetPrimaryPage());
if (!pscs) {
return 0;
}
return browsing_data::GetUniqueThirdPartyCookiesHostCount(
GetWebContents()->GetLastCommittedURL(),
pscs->allowed_local_shared_objects(),
*(pscs->allowed_browsing_data_model()));
}
int CookieControlsController::GetBlockedThirdPartyCookiesSitesCount() const {
auto* pscs = content_settings::PageSpecificContentSettings::GetForPage(
GetWebContents()->GetPrimaryPage());
if (!pscs) {
return 0;
}
return browsing_data::GetUniqueThirdPartyCookiesHostCount(
GetWebContents()->GetLastCommittedURL(),
pscs->blocked_local_shared_objects(),
*(pscs->blocked_browsing_data_model()));
}
int CookieControlsController::GetStatefulBounceCount() const {
auto* pscs = content_settings::PageSpecificContentSettings::GetForPage(
GetWebContents()->GetPrimaryPage());
if (pscs) {
return pscs->stateful_bounce_count();
} else {
return 0;
}
}
bool CookieControlsController::SiteDataAccessed(int third_party_allowed_sites,
int third_party_blocked_sites) {
return third_party_allowed_sites + third_party_blocked_sites +
GetStatefulBounceCount() !=
0;
}
void CookieControlsController::PresentBlockedCookieCounter() {
auto status = GetStatus(GetWebContents());
int third_party_allowed_sites = GetAllowedThirdPartyCookiesSitesCount();
int third_party_blocked_sites = GetBlockedThirdPartyCookiesSitesCount();
for (auto& observer : observers_) {
observer.OnSitesCountChanged(third_party_allowed_sites,
third_party_blocked_sites);
observer.OnBreakageConfidenceLevelChanged(
GetConfidenceLevel(status.status, status.enforcement,
SiteDataAccessed(third_party_allowed_sites,
third_party_blocked_sites)));
}
}
void CookieControlsController::OnPageReloadDetected(int recent_reloads_count) {
if (HasUserChangedCookieBlockingForSite() && recent_reloads_count > 0) {
waiting_for_page_load_finish_ = true;
}
SetUserChangedCookieBlockingForSite(false);
// Cache whether the expiration has expired since last visit before updating
// the last visited metadata.
const GURL& url = GetWebContents()->GetLastCommittedURL();
has_exception_expired_since_last_visit_ =
HasExceptionExpiredSinceLastVisit(GetMetadata(settings_map_, url));
// We only care about visits with active expirations, if there is an active
// exception, update the last visited time, otherwise clear it.
base::Value::Dict metadata = GetMetadata(settings_map_, url);
auto status = GetStatus(GetWebContents());
if (status.status == CookieControlsStatus::kDisabledForSite) {
metadata.Set(kLastVisitedActiveException,
base::TimeToValue(base::Time::Now()));
} else {
metadata.Remove(kLastVisitedActiveException);
}
ApplyMetadataChanges(settings_map_, url, std::move(metadata));
recent_reloads_count_ = recent_reloads_count;
// Only inform the observers if there is a potential confidence level change.
if (recent_reloads_count_ < features::kUserBypassUIReloadCount.Get() &&
!has_exception_expired_since_last_visit_) {
return;
}
for (auto& observer : observers_) {
observer.OnBreakageConfidenceLevelChanged(GetConfidenceLevel(
status.status, status.enforcement,
SiteDataAccessed(GetAllowedThirdPartyCookiesSitesCount(),
GetBlockedThirdPartyCookiesSitesCount())));
}
}
void CookieControlsController::OnPageFinishedLoading() {
if (!waiting_for_page_load_finish_) {
return;
}
waiting_for_page_load_finish_ = false;
for (auto& observer : observers_) {
observer.OnFinishedPageReloadWithChangedSettings();
}
}
void CookieControlsController::OnThirdPartyCookieBlockingChanged(
bool block_third_party_cookies) {
if (GetWebContents()) {
Update(GetWebContents());
}
}
void CookieControlsController::OnCookieSettingChanged() {
if (GetWebContents()) {
Update(GetWebContents());
}
}
content::WebContents* CookieControlsController::GetWebContents() const {
if (!tab_observer_) {
return nullptr;
}
return tab_observer_->content::WebContentsObserver::web_contents();
}
void CookieControlsController::AddObserver(CookieControlsObserver* obs) {
observers_.AddObserver(obs);
}
void CookieControlsController::RemoveObserver(CookieControlsObserver* obs) {
observers_.RemoveObserver(obs);
}
double CookieControlsController::GetSiteEngagementScore() {
auto* web_contents = GetWebContents();
return SiteEngagementService::Get(web_contents->GetBrowserContext())
->GetScore(web_contents->GetVisibleURL());
}
void CookieControlsController::RecordActivationMetrics() {
const GURL& url = GetWebContents()->GetLastCommittedURL();
// Metrics, related to confidence signals:
// TODO(crbug.com/1446230): Add CookieControlsActivated.FedCmInitiated
base::UmaHistogramBoolean(
"Privacy.CookieControlsActivated.SaaRequested",
cookie_settings_->HasAnyFrameRequestedStorageAccess(url));
base::UmaHistogramCounts100(
"Privacy.CookieControlsActivated.PageRefreshCount",
recent_reloads_count_);
base::UmaHistogramExactLinear(
"Privacy.CookieControlsActivated.SiteEngagementScore",
GetSiteEngagementScore(), 100);
int third_party_allowed_sites = GetAllowedThirdPartyCookiesSitesCount();
int third_party_blocked_sites = GetBlockedThirdPartyCookiesSitesCount();
auto site_data_access_type = GetSiteDataAccessType(third_party_allowed_sites,
third_party_blocked_sites);
base::UmaHistogramEnumeration(
"Privacy.CookieControlsActivated.SiteDataAccessType",
site_data_access_type);
// Record activation UKM.
// TODO(crbug.com/1446230): Include FedCM information.
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
auto ukm_source_id =
GetWebContents()->GetPrimaryMainFrame()->GetPageUkmSourceId();
ukm::builders::ThirdPartyCookies_CookieControlsActivated(ukm_source_id)
.SetFedCmInitiated(false)
.SetStorageAccessAPIRequested(
cookie_settings_->HasAnyFrameRequestedStorageAccess(url))
.SetPageRefreshCount(std::clamp(recent_reloads_count_, 0, 10))
.SetRepeatedActivation(
GetActivationCount(GetMetadata(settings_map_, url)) > 1)
.SetSiteEngagementLevel(static_cast<uint64_t>(
SiteEngagementService::Get(GetWebContents()->GetBrowserContext())
->GetEngagementLevel(url)))
.SetThirdPartySiteDataAccessType(
static_cast<uint64_t>(site_data_access_type))
.Record(ukm::UkmRecorder::Get());
// TODO(crbug.com/1446230): Add metrics, related to repeated activations.
}
CookieControlsController::TabObserver::TabObserver(
CookieControlsController* cookie_controls,
content::WebContents* web_contents)
: content_settings::PageSpecificContentSettings::SiteDataObserver(
web_contents),
content::WebContentsObserver(web_contents),
cookie_controls_(cookie_controls) {
last_visited_url_ =
content::WebContentsObserver::web_contents()->GetVisibleURL();
}
CookieControlsController::TabObserver::~TabObserver() = default;
void CookieControlsController::TabObserver::OnSiteDataAccessed(
const AccessDetails& access_details) {
if (access_details.site_data_type != SiteDataType::kCookies) {
cookie_controls_->PresentBlockedCookieCounter();
return;
}
// When User Bypass is enabled, a large number of string comparisons are
// performed to determine what sites are 3P / 1P. Cookie accesses are
// reported _very_ frequently as many sites are always reading or writing to
// the same cookie, and there is no caching of these accesses anywhere before
// here (in constrast to JS storage, which does cache accesses earlier).
// A simple cache of cookie accesses is used here to limit the number of
// repeated updates.
// We can't cache all types of accesses here, because the `site_data_type` is
// not always populated with sufficient granularity (often aliasing to
// kUnknown). This is relevant as some daya types may impact the block 3P
// count, while others may not.
// TODO(crbug.com/1271155): Replace the SiteDataType with the Browsing Data
// Model's StorageType, which would let us remove an enum, and let us cache
// all accesses here.
if (cookie_accessed_set_.count(access_details)) {
return;
}
cookie_accessed_set_.insert(access_details);
cookie_controls_->PresentBlockedCookieCounter();
}
void CookieControlsController::TabObserver::OnStatefulBounceDetected() {
cookie_controls_->PresentBlockedCookieCounter();
}
void CookieControlsController::TabObserver::PrimaryPageChanged(
content::Page& page) {
const GURL& current_url =
content::WebContentsObserver::web_contents()->GetVisibleURL();
cookie_accessed_set_.clear();
if (current_url != last_visited_url_) {
reload_count_ = 0;
timer_.Stop();
} else {
if (!timer_.IsRunning()) {
timer_.Start(FROM_HERE, features::kUserBypassUIReloadTime.Get(), this,
&CookieControlsController::TabObserver::ResetReloadCounter);
}
reload_count_++;
}
last_visited_url_ = current_url;
cookie_controls_->OnPageReloadDetected(reload_count_);
}
void CookieControlsController::TabObserver::DidStopLoading() {
cookie_controls_->OnPageFinishedLoading();
}
void CookieControlsController::TabObserver::ResetReloadCounter() {
reload_count_ = 0;
}
} // namespace content_settings