blob: b26c62d180d5c9af18497f0965079518d3ae870c [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 <string>
#include "base/test/metrics/histogram_tester.h"
#include "chrome/browser/content_settings/cookie_settings_factory.h"
#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
#include "chrome/browser/content_settings/page_specific_content_settings_delegate.h"
#include "chrome/browser/fingerprinting_protection/chrome_fingerprinting_protection_web_contents_helper_factory.h"
#include "chrome/browser/privacy_sandbox/tracking_protection_settings_factory.h"
#include "chrome/test/base/chrome_render_view_host_test_harness.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/cookie_settings.h"
#include "components/content_settings/core/common/content_settings.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/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/content_settings/core/common/tracking_protection_feature.h"
#include "components/fingerprinting_protection_filter/browser/fingerprinting_protection_web_contents_helper.h"
#include "components/fingerprinting_protection_filter/common/fingerprinting_protection_filter_features.h"
#include "components/prefs/pref_service.h"
#include "components/privacy_sandbox/privacy_sandbox_features.h"
#include "components/privacy_sandbox/tracking_protection_settings.h"
#include "components/site_engagement/content/site_engagement_service.h"
#include "components/strings/grit/privacy_sandbox_strings.h"
#include "components/subresource_filter/core/mojom/subresource_filter.mojom-shared.h"
#include "components/ukm/test_ukm_recorder.h"
#include "content/public/test/navigation_simulator.h"
#include "content/public/test/web_contents_tester.h"
#include "net/http/http_response_headers.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/l10n/l10n_util.h"
namespace {
using StorageType =
content_settings::mojom::ContentSettingsManager::StorageType;
using BlockingStatus = content_settings::TrackingProtectionBlockingStatus;
using FeatureType = content_settings::TrackingProtectionFeatureType;
using TrackingProtectionFeature = content_settings::TrackingProtectionFeature;
constexpr char kCookieControlsActivatedSaaHistogram[] =
"Privacy.CookieControlsActivated.SaaRequested";
constexpr char kCookieControlsActivatedRefreshCountHistogram[] =
"Privacy.CookieControlsActivated.PageRefreshCount";
constexpr char kCookieControlsActivatedSiteEngagementHistogram[] =
"Privacy.CookieControlsActivated.SiteEngagementScore";
constexpr char kCookieControlsActivatedSiteDataAccessHistogram[] =
"Privacy.CookieControlsActivated.SiteDataAccessType";
constexpr char kUrl[] = "https://example.com";
class MockCookieControlsObserver
: public content_settings::CookieControlsObserver {
public:
MOCK_METHOD(void,
OnStatusChanged,
(/*controls_visible*/ bool,
/*protections_on*/ bool,
CookieControlsEnforcement,
CookieBlocking3pcdStatus,
base::Time,
std::vector<TrackingProtectionFeature>));
MOCK_METHOD(void,
OnCookieControlsIconStatusChanged,
(/*icon_visible*/ bool,
/*protections_on*/ bool,
CookieBlocking3pcdStatus,
/*should_highlight*/ bool));
MOCK_METHOD(void, OnFinishedPageReloadWithChangedSettings, ());
MOCK_METHOD(void, OnReloadThresholdExceeded, ());
};
blink::StorageKey CreateUnpartitionedStorageKey(const GURL& url) {
return blink::StorageKey::CreateFirstParty(url::Origin::Create(url));
}
} // namespace
std::ostream& operator<<(std::ostream& os,
const CookieControlsEnforcement& enforcement) {
switch (enforcement) {
case CookieControlsEnforcement::kNoEnforcement:
return os << "kNoEnforcement";
case CookieControlsEnforcement::kEnforcedByCookieSetting:
return os << "kEnforcedByCookieSetting";
case CookieControlsEnforcement::kEnforcedByExtension:
return os << "kEnforcedByExtension";
case CookieControlsEnforcement::kEnforcedByPolicy:
return os << "kEnforcedByPolicy";
case CookieControlsEnforcement::kEnforcedByTpcdGrant:
return os << "kEnforcedByTpcdGrant";
}
}
class CookieControlsUserBypassTest : public ChromeRenderViewHostTestHarness {
public:
CookieControlsUserBypassTest()
: ChromeRenderViewHostTestHarness(
base::test::TaskEnvironment::TimeSource::MOCK_TIME) {
// NOTE: we make the exception short (hours rather than days) to prevent it
// from timing out
feature_list_.InitWithFeaturesAndParameters(
{{content_settings::features::kUserBypassUI,
{{"expiration", "3h"}, {"reload-count", "2"}}}},
{});
}
protected:
void SetUp() override {
ChromeRenderViewHostTestHarness::SetUp();
content_settings::PageSpecificContentSettings::CreateForWebContents(
web_contents(),
std::make_unique<PageSpecificContentSettingsDelegate>(web_contents()));
profile()->GetPrefs()->SetInteger(
prefs::kCookieControlsMode,
static_cast<int>(
content_settings::CookieControlsMode::kBlockThirdParty));
NavigateAndCommit(GURL("chrome://newtab"));
cookie_settings_ = CookieSettingsFactory::GetForProfile(profile());
tracking_protection_settings_ =
TrackingProtectionSettingsFactory::GetForProfile(profile());
cookie_controls_ =
std::make_unique<content_settings::CookieControlsController>(
cookie_settings_, nullptr,
HostContentSettingsMapFactory::GetForProfile(profile()),
tracking_protection_settings_,
/*is_incognito_profile=*/false);
cookie_controls_->AddObserver(mock());
testing::Mock::VerifyAndClearExpectations(mock());
ukm_recorder_ = std::make_unique<ukm::TestAutoSetUkmRecorder>();
}
void TearDown() override {
cookie_controls_->RemoveObserver(mock());
cookie_controls_.reset();
tracking_protection_settings_ = nullptr;
ChromeRenderViewHostTestHarness::TearDown();
}
void ValidateCookieControlsActivatedUKM(
bool fed_cm_initiated,
bool storage_access_api_requested,
int page_refresh_count,
bool repeated_activation,
blink::mojom::EngagementLevel site_engagement_level,
ThirdPartySiteDataAccessType site_data_access_type) {
auto entries = ukm_recorder_->GetEntriesByName(
"ThirdPartyCookies.CookieControlsActivated");
ASSERT_EQ(1u, entries.size());
auto* entry = entries.front().get();
ukm_recorder_->ExpectEntryMetric(entry, "FedCmInitiated", fed_cm_initiated);
ukm_recorder_->ExpectEntryMetric(entry, "StorageAccessAPIRequested",
storage_access_api_requested);
ukm_recorder_->ExpectEntryMetric(entry, "PageRefreshCount",
page_refresh_count);
ukm_recorder_->ExpectEntryMetric(entry, "RepeatedActivation",
repeated_activation);
ukm_recorder_->ExpectEntryMetric(
entry, "SiteEngagementLevel",
static_cast<uint64_t>(site_engagement_level));
ukm_recorder_->ExpectEntryMetric(
entry, "ThirdPartySiteDataAccessType",
static_cast<uint64_t>(site_data_access_type));
// Ideally we would check the associated URL directly, but that is
// evidently non-trivial, so we settle for making sure the right ID was
// used.
EXPECT_EQ(web_contents()->GetPrimaryMainFrame()->GetPageUkmSourceId(),
entry->source_id);
// Reset the recorder, tests should check every UKM report they expect.
ukm_recorder_ = std::make_unique<ukm::TestAutoSetUkmRecorder>();
}
MockCookieControlsObserver* mock() { return &mock_; }
base::Time zero_expiration() const { return base::Time(); }
base::Time expiration() const {
auto delta =
content_settings::features::kUserBypassUIExceptionExpiration.Get();
return delta.is_zero() ? base::Time() : base::Time::Now() + delta;
}
content_settings::CookieControlsController* cookie_controls() {
return cookie_controls_.get();
}
content_settings::CookieSettings* cookie_settings() {
return cookie_settings_.get();
}
privacy_sandbox::TrackingProtectionSettings* tracking_protection_settings() {
return tracking_protection_settings_;
}
content_settings::PageSpecificContentSettings*
page_specific_content_settings() {
return content_settings::PageSpecificContentSettings::GetForFrame(
web_contents()->GetPrimaryMainFrame());
}
void FastForwardBy(base::TimeDelta delta) {
task_environment()->FastForwardBy(delta);
}
void FastForwardTo(base::Time target) {
task_environment()->FastForwardBy(target - base::Time::Now());
}
std::vector<TrackingProtectionFeature>
GetThirdPartyCookiesFeatureForEnforcement(
CookieControlsEnforcement enforcement,
BlockingStatus blocking_status) {
return {{FeatureType::kThirdPartyCookies, enforcement, blocking_status}};
}
private:
MockCookieControlsObserver mock_;
base::test::ScopedFeatureList feature_list_;
std::unique_ptr<ukm::TestAutoSetUkmRecorder> ukm_recorder_;
std::unique_ptr<content_settings::CookieControlsController> cookie_controls_;
scoped_refptr<content_settings::CookieSettings> cookie_settings_;
raw_ptr<privacy_sandbox::TrackingProtectionSettings>
tracking_protection_settings_;
};
TEST_F(CookieControlsUserBypassTest, CookieBlockingChanged) {
// Check that the controller correctly keeps track of whether the effective
// cookie blocking setting for the page has been changed by
// `SetUserChangedCookieBlockingForSite`.
cookie_controls()->Update(web_contents());
NavigateAndCommit(GURL(kUrl));
EXPECT_FALSE(cookie_controls()->HasUserChangedCookieBlockingForSite());
cookie_controls()->SetUserChangedCookieBlockingForSite(false);
EXPECT_FALSE(cookie_controls()->HasUserChangedCookieBlockingForSite());
cookie_controls()->SetUserChangedCookieBlockingForSite(true);
EXPECT_TRUE(cookie_controls()->HasUserChangedCookieBlockingForSite());
// Changing the toggle back should clear it.
cookie_controls()->SetUserChangedCookieBlockingForSite(true);
EXPECT_FALSE(cookie_controls()->HasUserChangedCookieBlockingForSite());
// Navigating to the same page should clear it.
cookie_controls()->SetUserChangedCookieBlockingForSite(true);
EXPECT_TRUE(cookie_controls()->HasUserChangedCookieBlockingForSite());
NavigateAndCommit(GURL(kUrl));
EXPECT_FALSE(cookie_controls()->HasUserChangedCookieBlockingForSite());
// Navigating to a different page should also clear it.
cookie_controls()->SetUserChangedCookieBlockingForSite(true);
EXPECT_TRUE(cookie_controls()->HasUserChangedCookieBlockingForSite());
NavigateAndCommit(GURL("https://thirdparty.com"));
EXPECT_FALSE(cookie_controls()->HasUserChangedCookieBlockingForSite());
}
TEST_F(CookieControlsUserBypassTest, SiteCounts) {
base::HistogramTester t;
// Visiting a website should enable the UI.
NavigateAndCommit(GURL(kUrl));
EXPECT_CALL(*mock(),
OnStatusChanged(
/*controls_visible=*/true, /*protections_on=*/true,
CookieControlsEnforcement::kNoEnforcement,
CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration(),
GetThirdPartyCookiesFeatureForEnforcement(
CookieControlsEnforcement::kNoEnforcement,
BlockingStatus::kBlocked)));
EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
/*icon_visible=*/false, /*protections_on=*/true,
CookieBlocking3pcdStatus::kNotIn3pcd,
/*should_highlight=*/false));
cookie_controls()->Update(web_contents());
testing::Mock::VerifyAndClearExpectations(mock());
// Accessing cookies should be notified.
EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
/*icon_visible=*/true, /*protections_on=*/true,
CookieBlocking3pcdStatus::kNotIn3pcd,
/*should_highlight=*/false));
page_specific_content_settings()->OnBrowsingDataAccessed(
CreateUnpartitionedStorageKey(GURL("https://thirdparty.com")),
BrowsingDataModel::StorageType::kQuotaStorage,
/*blocked=*/false);
testing::Mock::VerifyAndClearExpectations(mock());
page_specific_content_settings()->OnBrowsingDataAccessed(
CreateUnpartitionedStorageKey(GURL("https://anotherthirdparty.com")),
BrowsingDataModel::StorageType::kQuotaStorage,
/*blocked=*/true);
// Enabling third-party cookies records metrics.
EXPECT_CALL(*mock(), OnStatusChanged(
/*controls_visible=*/true, /*protections_on=*/false,
CookieControlsEnforcement::kNoEnforcement,
CookieBlocking3pcdStatus::kNotIn3pcd, expiration(),
GetThirdPartyCookiesFeatureForEnforcement(
CookieControlsEnforcement::kNoEnforcement,
BlockingStatus::kAllowed)));
EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
/*icon_visible=*/true, /*protections_on=*/false,
CookieBlocking3pcdStatus::kNotIn3pcd,
/*should_highlight=*/false));
cookie_controls()->OnCookieBlockingEnabledForSite(false);
t.ExpectUniqueSample(kCookieControlsActivatedSaaHistogram, false, 1);
t.ExpectUniqueSample(kCookieControlsActivatedRefreshCountHistogram, 0, 1);
t.ExpectUniqueSample(kCookieControlsActivatedSiteEngagementHistogram, 0, 1);
t.ExpectUniqueSample(
kCookieControlsActivatedSiteDataAccessHistogram,
ThirdPartySiteDataAccessType::kAnyBlockedThirdPartySiteAccesses, 1);
}
TEST_F(CookieControlsUserBypassTest, NewTabPage) {
EXPECT_CALL(*mock(),
OnStatusChanged(
/*controls_visible=*/false, /*protections_on=*/false,
CookieControlsEnforcement::kNoEnforcement,
CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration(),
GetThirdPartyCookiesFeatureForEnforcement(
CookieControlsEnforcement::kNoEnforcement,
BlockingStatus::kAllowed)));
EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
/*icon_visible=*/false, /*protections_on=*/false,
CookieBlocking3pcdStatus::kNotIn3pcd,
/*should_highlight=*/false));
cookie_controls()->Update(web_contents());
}
TEST_F(CookieControlsUserBypassTest, PreferenceDisabled) {
NavigateAndCommit(GURL(kUrl));
EXPECT_CALL(*mock(),
OnStatusChanged(
/*controls_visible=*/true, /*protections_on=*/true,
CookieControlsEnforcement::kNoEnforcement,
CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration(),
GetThirdPartyCookiesFeatureForEnforcement(
CookieControlsEnforcement::kNoEnforcement,
BlockingStatus::kBlocked)));
EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
/*icon_visible=*/false, /*protections_on=*/true,
CookieBlocking3pcdStatus::kNotIn3pcd,
/*should_highlight=*/false));
cookie_controls()->Update(web_contents());
testing::Mock::VerifyAndClearExpectations(mock());
// Disabling the feature should disable the UI.
EXPECT_CALL(*mock(),
OnStatusChanged(
/*controls_visible=*/false, /*protections_on=*/false,
CookieControlsEnforcement::kNoEnforcement,
CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration(),
GetThirdPartyCookiesFeatureForEnforcement(
CookieControlsEnforcement::kNoEnforcement,
BlockingStatus::kAllowed)));
EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
/*icon_visible=*/false, /*protections_on=*/false,
CookieBlocking3pcdStatus::kNotIn3pcd,
/*should_highlight=*/false));
profile()->GetPrefs()->SetInteger(
prefs::kCookieControlsMode,
static_cast<int>(content_settings::CookieControlsMode::kOff));
testing::Mock::VerifyAndClearExpectations(mock());
}
TEST_F(CookieControlsUserBypassTest, AllCookiesBlocked) {
base::HistogramTester t;
NavigateAndCommit(GURL(kUrl));
EXPECT_CALL(*mock(),
OnStatusChanged(
/*controls_visible=*/true, /*protections_on=*/true,
CookieControlsEnforcement::kNoEnforcement,
CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration(),
GetThirdPartyCookiesFeatureForEnforcement(
CookieControlsEnforcement::kNoEnforcement,
BlockingStatus::kBlocked)));
EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
/*icon_visible=*/false, /*protections_on=*/true,
CookieBlocking3pcdStatus::kNotIn3pcd,
/*should_highlight=*/false));
cookie_controls()->Update(web_contents());
testing::Mock::VerifyAndClearExpectations(mock());
// Disable all cookies - an OnStatusCallback should get triggered.
EXPECT_CALL(*mock(),
OnStatusChanged(
/*controls_visible=*/true, /*protections_on=*/true,
CookieControlsEnforcement::kNoEnforcement,
CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration(),
GetThirdPartyCookiesFeatureForEnforcement(
CookieControlsEnforcement::kNoEnforcement,
BlockingStatus::kBlocked)));
EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
/*icon_visible=*/false, /*protections_on=*/true,
CookieBlocking3pcdStatus::kNotIn3pcd,
/*should_highlight=*/false));
cookie_settings()->SetDefaultCookieSetting(CONTENT_SETTING_BLOCK);
testing::Mock::VerifyAndClearExpectations(mock());
// Disable cookie blocking for example.com.
EXPECT_CALL(*mock(), OnStatusChanged(
/*controls_visible=*/true, /*protections_on=*/false,
CookieControlsEnforcement::kNoEnforcement,
CookieBlocking3pcdStatus::kNotIn3pcd, expiration(),
GetThirdPartyCookiesFeatureForEnforcement(
CookieControlsEnforcement::kNoEnforcement,
BlockingStatus::kAllowed)));
EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
/*icon_visible=*/true, /*protections_on=*/false,
CookieBlocking3pcdStatus::kNotIn3pcd,
/*should_highlight=*/false));
cookie_controls()->OnCookieBlockingEnabledForSite(false);
t.ExpectUniqueSample(kCookieControlsActivatedSaaHistogram, false, 1);
t.ExpectUniqueSample(kCookieControlsActivatedRefreshCountHistogram, 0, 1);
t.ExpectUniqueSample(kCookieControlsActivatedSiteEngagementHistogram, 0, 1);
t.ExpectUniqueSample(kCookieControlsActivatedSiteDataAccessHistogram,
ThirdPartySiteDataAccessType::kNoThirdPartySiteAccesses,
1);
testing::Mock::VerifyAndClearExpectations(mock());
}
TEST_F(CookieControlsUserBypassTest, DisableForSite) {
NavigateAndCommit(GURL(kUrl));
EXPECT_CALL(*mock(),
OnStatusChanged(
/*controls_visible=*/true, /*protections_on=*/true,
CookieControlsEnforcement::kNoEnforcement,
CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration(),
GetThirdPartyCookiesFeatureForEnforcement(
CookieControlsEnforcement::kNoEnforcement,
BlockingStatus::kBlocked)));
EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
/*icon_visible=*/false, /*protections_on=*/true,
CookieBlocking3pcdStatus::kNotIn3pcd,
/*should_highlight=*/false));
cookie_controls()->Update(web_contents());
testing::Mock::VerifyAndClearExpectations(mock());
// Disabling cookie blocking for example.com should update the ui.
EXPECT_CALL(*mock(), OnStatusChanged(
/*controls_visible=*/true, /*protections_on=*/false,
CookieControlsEnforcement::kNoEnforcement,
CookieBlocking3pcdStatus::kNotIn3pcd, expiration(),
GetThirdPartyCookiesFeatureForEnforcement(
CookieControlsEnforcement::kNoEnforcement,
BlockingStatus::kAllowed)));
EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
/*icon_visible=*/true, /*protections_on=*/false,
CookieBlocking3pcdStatus::kNotIn3pcd,
/*should_highlight=*/false));
cookie_controls()->OnCookieBlockingEnabledForSite(false);
testing::Mock::VerifyAndClearExpectations(mock());
// Visiting some other site, should re-enable protections.
NavigateAndCommit(GURL("https://somethingelse.com"));
EXPECT_CALL(*mock(),
OnStatusChanged(
/*controls_visible=*/true, /*protections_on=*/true,
CookieControlsEnforcement::kNoEnforcement,
CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration(),
GetThirdPartyCookiesFeatureForEnforcement(
CookieControlsEnforcement::kNoEnforcement,
BlockingStatus::kBlocked)));
EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
/*icon_visible=*/false, /*protections_on=*/true,
CookieBlocking3pcdStatus::kNotIn3pcd,
/*should_highlight=*/false));
cookie_controls()->Update(web_contents());
testing::Mock::VerifyAndClearExpectations(mock());
// Visiting example.com should turn protections off.
NavigateAndCommit(GURL(kUrl));
EXPECT_CALL(*mock(), OnStatusChanged(
/*controls_visible=*/true, /*protections_on=*/false,
CookieControlsEnforcement::kNoEnforcement,
CookieBlocking3pcdStatus::kNotIn3pcd, expiration(),
GetThirdPartyCookiesFeatureForEnforcement(
CookieControlsEnforcement::kNoEnforcement,
BlockingStatus::kAllowed)));
EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
/*icon_visible=*/true, /*protections_on=*/false,
CookieBlocking3pcdStatus::kNotIn3pcd,
/*should_highlight=*/false));
cookie_controls()->Update(web_contents());
testing::Mock::VerifyAndClearExpectations(mock());
// Enabling example.com again should re-enable protections.
EXPECT_CALL(*mock(),
OnStatusChanged(
/*controls_visible=*/true, /*protections_on=*/true,
CookieControlsEnforcement::kNoEnforcement,
CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration(),
GetThirdPartyCookiesFeatureForEnforcement(
CookieControlsEnforcement::kNoEnforcement,
BlockingStatus::kBlocked)));
EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
/*icon_visible=*/false, /*protections_on=*/true,
CookieBlocking3pcdStatus::kNotIn3pcd,
/*should_highlight=*/false));
cookie_controls()->OnCookieBlockingEnabledForSite(true);
testing::Mock::VerifyAndClearExpectations(mock());
}
TEST_F(CookieControlsUserBypassTest, Incognito) {
NavigateAndCommit(GURL(kUrl));
EXPECT_CALL(*mock(),
OnStatusChanged(
/*controls_visible=*/true, /*protections_on=*/true,
CookieControlsEnforcement::kNoEnforcement,
CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration(),
GetThirdPartyCookiesFeatureForEnforcement(
CookieControlsEnforcement::kNoEnforcement,
BlockingStatus::kBlocked)));
EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
/*icon_visible=*/false, /*protections_on=*/true,
CookieBlocking3pcdStatus::kNotIn3pcd,
/*should_highlight=*/false));
cookie_controls()->Update(web_contents());
testing::Mock::VerifyAndClearExpectations(mock());
// Create incognito web_contents and CookieControlsController.
std::unique_ptr<content::WebContents> incognito_web_contents =
content::WebContentsTester::CreateTestWebContents(
profile()->GetPrimaryOTRProfile(/*create_if_needed=*/true), nullptr);
content_settings::PageSpecificContentSettings::CreateForWebContents(
incognito_web_contents.get(),
std::make_unique<PageSpecificContentSettingsDelegate>(
incognito_web_contents.get()));
auto* tester = content::WebContentsTester::For(incognito_web_contents.get());
MockCookieControlsObserver incognito_mock;
content_settings::CookieControlsController incognito_cookie_controls(
CookieSettingsFactory::GetForProfile(
profile()->GetPrimaryOTRProfile(/*create_if_needed=*/true)),
CookieSettingsFactory::GetForProfile(profile()),
HostContentSettingsMapFactory::GetForProfile(profile()),
TrackingProtectionSettingsFactory::GetForProfile(profile()),
/*is_incognito_profile=*/true);
incognito_cookie_controls.AddObserver(&incognito_mock);
// Navigate incognito web_contents to the same URL.
tester->NavigateAndCommit(GURL(kUrl));
EXPECT_CALL(incognito_mock,
OnStatusChanged(
/*controls_visible=*/true, /*protections_on=*/true,
CookieControlsEnforcement::kNoEnforcement,
CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration(),
GetThirdPartyCookiesFeatureForEnforcement(
CookieControlsEnforcement::kNoEnforcement,
BlockingStatus::kBlocked)));
EXPECT_CALL(
incognito_mock,
OnCookieControlsIconStatusChanged(
/*icon_visible=*/false, /*protections_on=*/true,
CookieBlocking3pcdStatus::kNotIn3pcd, /*should_highlight=*/false));
incognito_cookie_controls.Update(incognito_web_contents.get());
testing::Mock::VerifyAndClearExpectations(mock());
testing::Mock::VerifyAndClearExpectations(&incognito_mock);
// Allow cookies in regular mode should also allow in incognito but enforced
// through regular mode.
EXPECT_CALL(*mock(), OnStatusChanged(
/*controls_visible=*/true, /*protections_on=*/false,
CookieControlsEnforcement::kNoEnforcement,
CookieBlocking3pcdStatus::kNotIn3pcd, expiration(),
GetThirdPartyCookiesFeatureForEnforcement(
CookieControlsEnforcement::kNoEnforcement,
BlockingStatus::kAllowed)));
EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
/*icon_visible=*/true, /*protections_on=*/false,
CookieBlocking3pcdStatus::kNotIn3pcd,
/*should_highlight=*/false));
EXPECT_CALL(incognito_mock,
OnStatusChanged(
/*controls_visible=*/true, /*protections_on=*/false,
CookieControlsEnforcement::kEnforcedByCookieSetting,
CookieBlocking3pcdStatus::kNotIn3pcd, expiration(),
GetThirdPartyCookiesFeatureForEnforcement(
CookieControlsEnforcement::kEnforcedByCookieSetting,
BlockingStatus::kAllowed)));
EXPECT_CALL(
incognito_mock,
OnCookieControlsIconStatusChanged(
/*icon_visible=*/true, /*protections_on=*/false,
CookieBlocking3pcdStatus::kNotIn3pcd, /*should_highlight=*/false));
cookie_controls()->OnCookieBlockingEnabledForSite(false);
testing::Mock::VerifyAndClearExpectations(mock());
testing::Mock::VerifyAndClearExpectations(&incognito_mock);
// This should be enforced regardless of the default cookie setting in the
// default profile.
EXPECT_CALL(*mock(), OnStatusChanged(
/*controls_visible=*/false, /*protections_on=*/false,
CookieControlsEnforcement::kNoEnforcement,
CookieBlocking3pcdStatus::kNotIn3pcd,
// Although there is an allow exception with an
// expiration, because the default allow never
// expires, zero_expiration is correct.
zero_expiration(),
GetThirdPartyCookiesFeatureForEnforcement(
CookieControlsEnforcement::kNoEnforcement,
BlockingStatus::kAllowed)));
EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
/*icon_visible=*/false, /*protections_on=*/false,
CookieBlocking3pcdStatus::kNotIn3pcd,
/*should_highlight=*/false));
EXPECT_CALL(incognito_mock,
OnStatusChanged(
/*controls_visible=*/true, /*protections_on=*/false,
CookieControlsEnforcement::kEnforcedByCookieSetting,
CookieBlocking3pcdStatus::kNotIn3pcd, expiration(),
GetThirdPartyCookiesFeatureForEnforcement(
CookieControlsEnforcement::kEnforcedByCookieSetting,
BlockingStatus::kAllowed)));
EXPECT_CALL(
incognito_mock,
OnCookieControlsIconStatusChanged(
/*icon_visible=*/true, /*protections_on=*/false,
CookieBlocking3pcdStatus::kNotIn3pcd, /*should_highlight=*/false));
profile()->GetPrefs()->SetInteger(
prefs::kCookieControlsMode,
static_cast<int>(content_settings::CookieControlsMode::kIncognitoOnly));
incognito_cookie_controls.Update(incognito_web_contents.get());
testing::Mock::VerifyAndClearExpectations(mock());
testing::Mock::VerifyAndClearExpectations(&incognito_mock);
}
TEST_F(CookieControlsUserBypassTest, ThirdPartyCookiesException) {
// Create third party cookies exception.
cookie_settings()->SetThirdPartyCookieSetting(
GURL(kUrl), ContentSetting::CONTENT_SETTING_ALLOW);
NavigateAndCommit(GURL(kUrl));
// Third-party cookie exceptions are handled in the same way as exceptions
// created for user bypass.
EXPECT_CALL(*mock(),
OnStatusChanged(
/*controls_visible=*/true, /*protections_on=*/false,
CookieControlsEnforcement::kNoEnforcement,
CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration(),
GetThirdPartyCookiesFeatureForEnforcement(
CookieControlsEnforcement::kNoEnforcement,
BlockingStatus::kAllowed)));
EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
/*icon_visible=*/true, /*protections_on=*/false,
CookieBlocking3pcdStatus::kNotIn3pcd,
/*should_highlight=*/false));
cookie_controls()->Update(web_contents());
testing::Mock::VerifyAndClearExpectations(mock());
// Disabling 3PC for example.com again should change status to kEnabled.
EXPECT_CALL(*mock(),
OnStatusChanged(
/*controls_visible=*/true, /*protections_on=*/true,
CookieControlsEnforcement::kNoEnforcement,
CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration(),
GetThirdPartyCookiesFeatureForEnforcement(
CookieControlsEnforcement::kNoEnforcement,
BlockingStatus::kBlocked)));
EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
/*icon_visible=*/false, /*protections_on=*/true,
CookieBlocking3pcdStatus::kNotIn3pcd,
/*should_highlight=*/false));
cookie_controls()->OnCookieBlockingEnabledForSite(true);
testing::Mock::VerifyAndClearExpectations(mock());
}
TEST_F(CookieControlsUserBypassTest, FrequentPageReloads) {
// Update on the initial web contents to ensure the tab observer is setup.
cookie_controls()->Update(web_contents());
auto* hcsm = HostContentSettingsMapFactory::GetForProfile(profile());
EXPECT_CALL(*mock(),
OnStatusChanged(
/*controls_visible=*/true, /*protections_on=*/true,
CookieControlsEnforcement::kNoEnforcement,
CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration(),
GetThirdPartyCookiesFeatureForEnforcement(
CookieControlsEnforcement::kNoEnforcement,
BlockingStatus::kBlocked)));
EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
/*icon_visible=*/false, /*protections_on=*/true,
CookieBlocking3pcdStatus::kNotIn3pcd,
/*should_highlight=*/false));
NavigateAndCommit(GURL(kUrl));
cookie_controls()->Update(web_contents());
testing::Mock::VerifyAndClearExpectations(mock());
// Accessing cookies should be notified.
EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
/*icon_visible=*/true, /*protections_on=*/true,
CookieBlocking3pcdStatus::kNotIn3pcd,
/*should_highlight=*/false));
page_specific_content_settings()->OnBrowsingDataAccessed(
CreateUnpartitionedStorageKey(GURL("https://thirdparty.com")),
BrowsingDataModel::StorageType::kQuotaStorage,
/*blocked=*/false);
testing::Mock::VerifyAndClearExpectations(mock());
// Reload the page and simulate accessing storage on page load.
EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
/*icon_visible=*/true, /*protections_on=*/true,
CookieBlocking3pcdStatus::kNotIn3pcd,
/*should_highlight=*/false));
NavigateAndCommit(GURL(kUrl));
page_specific_content_settings()->OnBrowsingDataAccessed(
CreateUnpartitionedStorageKey(GURL("https://thirdparty.com")),
BrowsingDataModel::StorageType::kQuotaStorage,
/*blocked=*/false);
testing::Mock::VerifyAndClearExpectations(mock());
// After the second reload and accessing storage, UB should highlight.
EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
/*icon_visible=*/true, /*protections_on=*/true,
CookieBlocking3pcdStatus::kNotIn3pcd,
/*should_highlight=*/true));
NavigateAndCommit(GURL(kUrl));
page_specific_content_settings()->OnBrowsingDataAccessed(
CreateUnpartitionedStorageKey(GURL("https://thirdparty.com")),
BrowsingDataModel::StorageType::kQuotaStorage,
/*blocked=*/false);
cookie_controls()->OnEntryPointAnimated();
testing::Mock::VerifyAndClearExpectations(mock());
// After the entry point was highlighted, a setting is recorded.
base::Value stored_value = hcsm->GetWebsiteSetting(
GURL(kUrl), GURL(), ContentSettingsType::COOKIE_CONTROLS_METADATA);
EXPECT_TRUE(stored_value.is_dict());
EXPECT_TRUE(stored_value.GetDict().FindBool("entry_point_animated").value());
}
TEST_F(CookieControlsUserBypassTest,
HittingPageReloadThresholdTriggersOnReloadThresholdExceeded) {
// Update initial web contents to ensure the tab observer is set up.
cookie_controls()->Update(web_contents());
// Don't call observer when reload count = 0.
EXPECT_CALL(*mock(), OnReloadThresholdExceeded()).Times(0);
EXPECT_CALL(*mock(),
OnStatusChanged(
/*controls_visible=*/true, /*protections_on=*/true,
CookieControlsEnforcement::kNoEnforcement,
CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration(),
GetThirdPartyCookiesFeatureForEnforcement(
CookieControlsEnforcement::kNoEnforcement,
BlockingStatus::kBlocked)));
EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
/*icon_visible=*/false, /*protections_on=*/true,
CookieBlocking3pcdStatus::kNotIn3pcd,
/*should_highlight=*/false));
NavigateAndCommit(GURL(kUrl));
cookie_controls()->Update(web_contents());
testing::Mock::VerifyAndClearExpectations(mock());
// Don't call observer when reload count = 1.
EXPECT_CALL(*mock(), OnReloadThresholdExceeded()).Times(0);
EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
/*icon_visible=*/false, /*protections_on=*/true,
CookieBlocking3pcdStatus::kNotIn3pcd,
/*should_highlight=*/false));
NavigateAndCommit(GURL(kUrl));
cookie_controls()->Update(web_contents());
testing::Mock::VerifyAndClearExpectations(mock());
// Expect observer call when reload count hits threshold of 2.
EXPECT_CALL(*mock(), OnReloadThresholdExceeded());
// Expect that we attempt to highlight the user bypass icon.
EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
/*icon_visible=*/false, /*protections_on=*/true,
CookieBlocking3pcdStatus::kNotIn3pcd,
/*should_highlight=*/true));
NavigateAndCommit(GURL(kUrl));
cookie_controls()->Update(web_contents());
testing::Mock::VerifyAndClearExpectations(mock());
}
TEST_F(CookieControlsUserBypassTest,
UserBypassDoesNotHighlightIfCookiesAreAllowed) {
// Set cookie blocking pref to allow all cookies.
profile()->GetPrefs()->SetInteger(
prefs::kCookieControlsMode,
static_cast<int>(content_settings::CookieControlsMode::kOff));
// Update initial web contents to ensure the tab observer is set up.
cookie_controls()->Update(web_contents());
EXPECT_CALL(*mock(),
OnStatusChanged(
/*controls_visible=*/false, /*protections_on=*/false,
CookieControlsEnforcement::kNoEnforcement,
CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration(),
GetThirdPartyCookiesFeatureForEnforcement(
CookieControlsEnforcement::kNoEnforcement,
BlockingStatus::kAllowed)));
EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
/*icon_visible=*/false, /*protections_on=*/false,
CookieBlocking3pcdStatus::kNotIn3pcd,
/*should_highlight=*/false));
NavigateAndCommit(GURL(kUrl));
cookie_controls()->Update(web_contents());
testing::Mock::VerifyAndClearExpectations(mock());
// Trigger reload heuristic.
EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
/*icon_visible=*/false, /*protections_on=*/false,
CookieBlocking3pcdStatus::kNotIn3pcd,
/*should_highlight=*/false));
NavigateAndCommit(GURL(kUrl));
cookie_controls()->Update(web_contents());
testing::Mock::VerifyAndClearExpectations(mock());
// Verify we do not attempt to highlight user bypass as 3PCs are allowed.
EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
/*icon_visible=*/false, /*protections_on=*/false,
CookieBlocking3pcdStatus::kNotIn3pcd,
/*should_highlight=*/false));
NavigateAndCommit(GURL(kUrl));
cookie_controls()->Update(web_contents());
testing::Mock::VerifyAndClearExpectations(mock());
}
TEST_F(CookieControlsUserBypassTest, FrequentPageReloadsMetrics) {
base::HistogramTester t;
cookie_controls()->Update(web_contents());
NavigateAndCommit(GURL(kUrl));
EXPECT_CALL(*mock(),
OnStatusChanged(
/*controls_visible=*/true, /*protections_on=*/true,
CookieControlsEnforcement::kNoEnforcement,
CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration(),
GetThirdPartyCookiesFeatureForEnforcement(
CookieControlsEnforcement::kNoEnforcement,
BlockingStatus::kBlocked)));
EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
/*icon_visible=*/false, /*protections_on=*/true,
CookieBlocking3pcdStatus::kNotIn3pcd,
/*should_highlight=*/false));
cookie_controls()->Update(web_contents());
testing::Mock::VerifyAndClearExpectations(mock());
// Accessing cookies should be notified.
EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
/*icon_visible=*/true, /*protections_on=*/true,
CookieBlocking3pcdStatus::kNotIn3pcd,
/*should_highlight=*/false));
page_specific_content_settings()->OnBrowsingDataAccessed(
CreateUnpartitionedStorageKey(GURL("https://thirdparty.com")),
BrowsingDataModel::StorageType::kQuotaStorage,
/*blocked=*/false);
testing::Mock::VerifyAndClearExpectations(mock());
// Reload the page and simulate accessing storage on page load.
EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
/*icon_visible=*/true, /*protections_on=*/true,
CookieBlocking3pcdStatus::kNotIn3pcd,
/*should_highlight=*/false));
NavigateAndCommit(GURL(kUrl));
page_specific_content_settings()->OnBrowsingDataAccessed(
CreateUnpartitionedStorageKey(GURL("https://thirdparty.com")),
BrowsingDataModel::StorageType::kQuotaStorage,
/*blocked=*/false);
testing::Mock::VerifyAndClearExpectations(mock());
// After the second reload and accessing storage, UB should be highlighted.
EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
/*icon_visible=*/true, /*protections_on=*/true,
CookieBlocking3pcdStatus::kNotIn3pcd,
/*should_highlight=*/true));
NavigateAndCommit(GURL(kUrl));
page_specific_content_settings()->OnBrowsingDataAccessed(
CreateUnpartitionedStorageKey(GURL("https://thirdparty.com")),
BrowsingDataModel::StorageType::kQuotaStorage,
/*blocked=*/false);
testing::Mock::VerifyAndClearExpectations(mock());
// Enabling third-party cookies records metrics.
EXPECT_CALL(*mock(), OnStatusChanged(
/*controls_visible=*/true, /*protections_on=*/false,
CookieControlsEnforcement::kNoEnforcement,
CookieBlocking3pcdStatus::kNotIn3pcd, expiration(),
GetThirdPartyCookiesFeatureForEnforcement(
CookieControlsEnforcement::kNoEnforcement,
BlockingStatus::kAllowed)));
EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
/*icon_visible=*/true, /*protections_on=*/false,
CookieBlocking3pcdStatus::kNotIn3pcd,
/*should_highlight=*/false));
cookie_controls()->OnCookieBlockingEnabledForSite(false);
t.ExpectUniqueSample(kCookieControlsActivatedSaaHistogram, false, 1);
t.ExpectUniqueSample(kCookieControlsActivatedRefreshCountHistogram, 2, 1);
t.ExpectUniqueSample(kCookieControlsActivatedSiteEngagementHistogram, 0, 1);
t.ExpectUniqueSample(
kCookieControlsActivatedSiteDataAccessHistogram,
ThirdPartySiteDataAccessType::kAnyAllowedThirdPartySiteAccesses, 1);
ValidateCookieControlsActivatedUKM(
/*fed_cm_initiated=*/false,
/*storage_access_api_requested=*/false,
/*page_refresh_count=*/2, // Count was reset to 0 after timeout.
/*repeated_activation=*/false, blink::mojom::EngagementLevel::NONE,
ThirdPartySiteDataAccessType::kAnyAllowedThirdPartySiteAccesses);
testing::Mock::VerifyAndClearExpectations(mock());
}
TEST_F(CookieControlsUserBypassTest, InfrequentPageReloads) {
base::HistogramTester t;
NavigateAndCommit(GURL(kUrl));
EXPECT_CALL(*mock(),
OnStatusChanged(
/*controls_visible=*/true, /*protections_on=*/true,
CookieControlsEnforcement::kNoEnforcement,
CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration(),
GetThirdPartyCookiesFeatureForEnforcement(
CookieControlsEnforcement::kNoEnforcement,
BlockingStatus::kBlocked)));
EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
/*icon_visible=*/false, /*protections_on=*/true,
CookieBlocking3pcdStatus::kNotIn3pcd,
/*should_highlight=*/false));
cookie_controls()->Update(web_contents());
testing::Mock::VerifyAndClearExpectations(mock());
// Accessing cookies should be notified.
EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
/*icon_visible=*/true, /*protections_on=*/true,
CookieBlocking3pcdStatus::kNotIn3pcd,
/*should_highlight=*/false));
page_specific_content_settings()->OnBrowsingDataAccessed(
CreateUnpartitionedStorageKey(GURL("https://thirdparty.com")),
BrowsingDataModel::StorageType::kQuotaStorage,
/*blocked=*/false);
testing::Mock::VerifyAndClearExpectations(mock());
// Reload the page and simulate accessing storage on page load.
EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
/*icon_visible=*/true, /*protections_on=*/true,
CookieBlocking3pcdStatus::kNotIn3pcd,
/*should_highlight=*/false));
NavigateAndCommit(GURL(kUrl));
page_specific_content_settings()->OnBrowsingDataAccessed(
CreateUnpartitionedStorageKey(GURL("https://thirdparty.com")),
BrowsingDataModel::StorageType::kQuotaStorage,
/*blocked=*/false);
testing::Mock::VerifyAndClearExpectations(mock());
// Wait for 30 seconds.
FastForwardBy(base::Seconds(30));
// The second reload happens with a delay and doesn't highlight.
EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
/*icon_visible=*/true, /*protections_on=*/true,
CookieBlocking3pcdStatus::kNotIn3pcd,
/*should_highlight=*/false));
NavigateAndCommit(GURL(kUrl));
page_specific_content_settings()->OnBrowsingDataAccessed(
CreateUnpartitionedStorageKey(GURL("https://thirdparty.com")),
BrowsingDataModel::StorageType::kQuotaStorage,
/*blocked=*/false);
testing::Mock::VerifyAndClearExpectations(mock());
// Enabling third-party cookies records metrics.
EXPECT_CALL(*mock(), OnStatusChanged(
/*controls_visible=*/true, /*protections_on=*/false,
CookieControlsEnforcement::kNoEnforcement,
CookieBlocking3pcdStatus::kNotIn3pcd, expiration(),
GetThirdPartyCookiesFeatureForEnforcement(
CookieControlsEnforcement::kNoEnforcement,
BlockingStatus::kAllowed)));
EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
/*icon_visible=*/true, /*protections_on=*/false,
CookieBlocking3pcdStatus::kNotIn3pcd,
/*should_highlight=*/false));
cookie_controls()->OnCookieBlockingEnabledForSite(false);
t.ExpectUniqueSample(kCookieControlsActivatedSaaHistogram, false, 1);
t.ExpectUniqueSample(kCookieControlsActivatedRefreshCountHistogram, 1, 1);
t.ExpectUniqueSample(kCookieControlsActivatedSiteEngagementHistogram, 0, 1);
t.ExpectUniqueSample(
kCookieControlsActivatedSiteDataAccessHistogram,
ThirdPartySiteDataAccessType::kAnyAllowedThirdPartySiteAccesses, 1);
ValidateCookieControlsActivatedUKM(
/*fed_cm_initiated=*/false,
/*storage_access_api_requested=*/false,
/*page_refresh_count=*/1, // Count was reset to 0 after timeout.
/*repeated_activation=*/false, blink::mojom::EngagementLevel::NONE,
ThirdPartySiteDataAccessType::kAnyAllowedThirdPartySiteAccesses);
testing::Mock::VerifyAndClearExpectations(mock());
}
TEST_F(CookieControlsUserBypassTest, HighSiteEngagement) {
auto* hcsm = HostContentSettingsMapFactory::GetForProfile(profile());
// An engagement score above HIGH.
const int kHighEngagement = 60;
// An engagement score below MEDIUM.
const int kLowEngagement = 1;
site_engagement::SiteEngagementService::Get(profile())->ResetBaseScoreForURL(
GURL("https://highengagement.com"), kHighEngagement);
site_engagement::SiteEngagementService::Get(profile())->ResetBaseScoreForURL(
GURL("https://somethingelse.com"), kLowEngagement);
NavigateAndCommit(GURL("https://highengagement.com"));
EXPECT_CALL(*mock(),
OnStatusChanged(
/*controls_visible=*/true, /*protections_on=*/true,
CookieControlsEnforcement::kNoEnforcement,
CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration(),
GetThirdPartyCookiesFeatureForEnforcement(
CookieControlsEnforcement::kNoEnforcement,
BlockingStatus::kBlocked)));
EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
/*icon_visible=*/false, /*protections_on=*/true,
CookieBlocking3pcdStatus::kNotIn3pcd,
/*should_highlight=*/true));
cookie_controls()->Update(web_contents());
testing::Mock::VerifyAndClearExpectations(mock());
// Site data access should highlight.
EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
/*icon_visible=*/true, /*protections_on=*/true,
CookieBlocking3pcdStatus::kNotIn3pcd,
/*should_highlight=*/true));
page_specific_content_settings()->OnBrowsingDataAccessed(
CreateUnpartitionedStorageKey(GURL("https://thirdparty.com")),
BrowsingDataModel::StorageType::kQuotaStorage,
/*blocked=*/false);
cookie_controls()->OnEntryPointAnimated();
testing::Mock::VerifyAndClearExpectations(mock());
// After the entry point was highlighted a setting is recorded.
base::Value stored_value =
hcsm->GetWebsiteSetting(GURL("https://highengagement.com"), GURL(),
ContentSettingsType::COOKIE_CONTROLS_METADATA);
EXPECT_TRUE(stored_value.is_dict());
EXPECT_TRUE(stored_value.GetDict().FindBool("entry_point_animated").value());
// Visiting some other site should reset the state.
NavigateAndCommit(GURL("https://somethingelse.com"));
EXPECT_CALL(*mock(),
OnStatusChanged(
/*controls_visible=*/true, /*protections_on=*/true,
CookieControlsEnforcement::kNoEnforcement,
CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration(),
GetThirdPartyCookiesFeatureForEnforcement(
CookieControlsEnforcement::kNoEnforcement,
BlockingStatus::kBlocked)));
EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
/*icon_visible=*/false, /*protections_on=*/true,
CookieBlocking3pcdStatus::kNotIn3pcd,
/*should_highlight=*/false));
cookie_controls()->Update(web_contents());
testing::Mock::VerifyAndClearExpectations(mock());
// Site with medium or low engagement index that has accessed site does not
// highlight UB and only shows the icon.
EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
/*icon_visible=*/true, /*protections_on=*/true,
CookieBlocking3pcdStatus::kNotIn3pcd,
/*should_highlight=*/false));
page_specific_content_settings()->OnBrowsingDataAccessed(
CreateUnpartitionedStorageKey(GURL("https://anotherthirdparty.com")),
BrowsingDataModel::StorageType::kQuotaStorage,
/*blocked=*/false);
testing::Mock::VerifyAndClearExpectations(mock());
// Revisiting high site engagement site doesn't highlight UB
// because the entry point was already highlighted for that site.
EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
/*icon_visible=*/true, /*protections_on=*/true,
CookieBlocking3pcdStatus::kNotIn3pcd,
/*should_highlight=*/false));
NavigateAndCommit(GURL("https://highengagement.com"));
page_specific_content_settings()->OnBrowsingDataAccessed(
CreateUnpartitionedStorageKey(GURL("https://thirdparty.com")),
BrowsingDataModel::StorageType::kQuotaStorage,
/*blocked=*/false);
testing::Mock::VerifyAndClearExpectations(mock());
}
TEST_F(CookieControlsUserBypassTest, StorageAccessApiHighSiteEngagement) {
base::HistogramTester t;
// An engagement score above HIGH.
const int kHighEngagement = 60;
site_engagement::SiteEngagementService::Get(profile())->ResetBaseScoreForURL(
GURL("https://highengagement.com"), kHighEngagement);
// Create storage access exception for https://highengagement.com as top-level
// origin.
auto* hcsm = HostContentSettingsMapFactory::GetForProfile(profile());
hcsm->SetContentSettingCustomScope(
ContentSettingsPattern::FromURL(GURL("https://thirdparty.com")),
ContentSettingsPattern::FromURL(GURL("https://highengagement.com")),
ContentSettingsType::STORAGE_ACCESS, CONTENT_SETTING_ALLOW);
NavigateAndCommit(GURL("https://highengagement.com"));
EXPECT_CALL(*mock(),
OnStatusChanged(
/*controls_visible=*/true, /*protections_on=*/true,
CookieControlsEnforcement::kNoEnforcement,
CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration(),
GetThirdPartyCookiesFeatureForEnforcement(
CookieControlsEnforcement::kNoEnforcement,
BlockingStatus::kBlocked)));
EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
/*icon_visible=*/false, /*protections_on=*/true,
CookieBlocking3pcdStatus::kNotIn3pcd,
/*should_highlight=*/false));
cookie_controls()->Update(web_contents());
testing::Mock::VerifyAndClearExpectations(mock());
// Even though the site has high engagement level, UB does not highlight
// because SAA was requested in the site context.
EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
/*icon_visible=*/true, /*protections_on=*/true,
CookieBlocking3pcdStatus::kNotIn3pcd,
/*should_highlight=*/false));
page_specific_content_settings()->OnBrowsingDataAccessed(
CreateUnpartitionedStorageKey(GURL("https://thirdparty.com")),
BrowsingDataModel::StorageType::kQuotaStorage,
/*blocked=*/false);
testing::Mock::VerifyAndClearExpectations(mock());
// Enabling third-party cookies records metrics.
EXPECT_CALL(*mock(), OnStatusChanged(
/*controls_visible=*/true, /*protections_on=*/false,
CookieControlsEnforcement::kNoEnforcement,
CookieBlocking3pcdStatus::kNotIn3pcd, expiration(),
GetThirdPartyCookiesFeatureForEnforcement(
CookieControlsEnforcement::kNoEnforcement,
BlockingStatus::kAllowed)));
EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
/*icon_visible=*/true, /*protections_on=*/false,
CookieBlocking3pcdStatus::kNotIn3pcd,
/*should_highlight=*/false));
cookie_controls()->OnCookieBlockingEnabledForSite(false);
t.ExpectUniqueSample(kCookieControlsActivatedSaaHistogram, true, 1);
t.ExpectUniqueSample(kCookieControlsActivatedRefreshCountHistogram, 0, 1);
t.ExpectUniqueSample(kCookieControlsActivatedSiteEngagementHistogram,
kHighEngagement, 1);
t.ExpectUniqueSample(
kCookieControlsActivatedSiteDataAccessHistogram,
ThirdPartySiteDataAccessType::kAnyAllowedThirdPartySiteAccesses, 1);
ValidateCookieControlsActivatedUKM(
/*fed_cm_initiated=*/false,
/*storage_access_api_requested=*/true,
/*page_refresh_count=*/0, /*repeated_activation=*/false,
blink::mojom::EngagementLevel::HIGH,
ThirdPartySiteDataAccessType::kAnyAllowedThirdPartySiteAccesses);
testing::Mock::VerifyAndClearExpectations(mock());
}
TEST_F(CookieControlsUserBypassTest, CustomExceptionsNoWildcardMatchingDomain) {
auto* hcsm = HostContentSettingsMapFactory::GetForProfile(profile());
NavigateAndCommit(GURL("https://cool.things.com"));
EXPECT_CALL(*mock(),
OnStatusChanged(
/*controls_visible=*/true, /*protections_on=*/true,
CookieControlsEnforcement::kNoEnforcement,
CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration(),
GetThirdPartyCookiesFeatureForEnforcement(
CookieControlsEnforcement::kNoEnforcement,
BlockingStatus::kBlocked)));
EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
/*icon_visible=*/false, /*protections_on=*/true,
CookieBlocking3pcdStatus::kNotIn3pcd,
/*should_highlight=*/false));
cookie_controls()->Update(web_contents());
testing::Mock::VerifyAndClearExpectations(mock());
// Creating an exception turns protections off. The exception doesn't contain
// wildcards in the domain and isn't enforced.
EXPECT_CALL(*mock(),
OnStatusChanged(
/*controls_visible=*/true, /*protections_on=*/false,
CookieControlsEnforcement::kNoEnforcement,
CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration(),
GetThirdPartyCookiesFeatureForEnforcement(
CookieControlsEnforcement::kNoEnforcement,
BlockingStatus::kAllowed)));
EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
/*icon_visible=*/true, /*protections_on=*/false,
CookieBlocking3pcdStatus::kNotIn3pcd,
/*should_highlight=*/false));
hcsm->SetContentSettingCustomScope(
ContentSettingsPattern::Wildcard(),
ContentSettingsPattern::FromString("cool.things.com"),
ContentSettingsType::COOKIES, CONTENT_SETTING_ALLOW);
testing::Mock::VerifyAndClearExpectations(mock());
}
TEST_F(CookieControlsUserBypassTest, CustomExceptionsWildcardMatchingDomain) {
auto* hcsm = HostContentSettingsMapFactory::GetForProfile(profile());
NavigateAndCommit(GURL("https://cool.things.com"));
EXPECT_CALL(*mock(),
OnStatusChanged(
/*controls_visible=*/true, /*protections_on=*/true,
CookieControlsEnforcement::kNoEnforcement,
CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration(),
GetThirdPartyCookiesFeatureForEnforcement(
CookieControlsEnforcement::kNoEnforcement,
BlockingStatus::kBlocked)));
EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
/*icon_visible=*/false, /*protections_on=*/true,
CookieBlocking3pcdStatus::kNotIn3pcd,
/*should_highlight=*/false));
cookie_controls()->Update(web_contents());
testing::Mock::VerifyAndClearExpectations(mock());
// Creating an exception turns protections off. The exception has wildcard in
// the domain and cannot be reset, it is enforced by cookie setting.
EXPECT_CALL(*mock(),
OnStatusChanged(
/*controls_visible=*/true, /*protections_on=*/false,
CookieControlsEnforcement::kEnforcedByCookieSetting,
CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration(),
GetThirdPartyCookiesFeatureForEnforcement(
CookieControlsEnforcement::kEnforcedByCookieSetting,
BlockingStatus::kAllowed)));
EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
/*icon_visible=*/true, /*protections_on=*/false,
CookieBlocking3pcdStatus::kNotIn3pcd,
/*should_highlight=*/false));
hcsm->SetContentSettingCustomScope(
ContentSettingsPattern::Wildcard(),
ContentSettingsPattern::FromString("[*.]cool.things.com"),
ContentSettingsType::COOKIES, CONTENT_SETTING_ALLOW);
testing::Mock::VerifyAndClearExpectations(mock());
}
TEST_F(CookieControlsUserBypassTest,
CustomExceptionsWildcardLessSpecificDomain) {
auto* hcsm = HostContentSettingsMapFactory::GetForProfile(profile());
NavigateAndCommit(GURL("https://cool.things.com"));
EXPECT_CALL(*mock(),
OnStatusChanged(
/*controls_visible=*/true, /*protections_on=*/true,
CookieControlsEnforcement::kNoEnforcement,
CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration(),
GetThirdPartyCookiesFeatureForEnforcement(
CookieControlsEnforcement::kNoEnforcement,
BlockingStatus::kBlocked)));
EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
/*icon_visible=*/false, /*protections_on=*/true,
CookieBlocking3pcdStatus::kNotIn3pcd,
/*should_highlight=*/false));
cookie_controls()->Update(web_contents());
testing::Mock::VerifyAndClearExpectations(mock());
// Creating an exception changes turns protections off. The exception
// has wildcard in the domain and cannot be reset, it is enforced by cookie
// setting.
EXPECT_CALL(*mock(),
OnStatusChanged(
/*controls_visible=*/true, /*protections_on=*/false,
CookieControlsEnforcement::kEnforcedByCookieSetting,
CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration(),
GetThirdPartyCookiesFeatureForEnforcement(
CookieControlsEnforcement::kEnforcedByCookieSetting,
BlockingStatus::kAllowed)));
EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
/*icon_visible=*/true, /*protections_on=*/false,
CookieBlocking3pcdStatus::kNotIn3pcd,
/*should_highlight=*/false));
hcsm->SetContentSettingCustomScope(
ContentSettingsPattern::Wildcard(),
ContentSettingsPattern::FromString("[*.]things.com"),
ContentSettingsType::COOKIES, CONTENT_SETTING_ALLOW);
testing::Mock::VerifyAndClearExpectations(mock());
}
TEST_F(CookieControlsUserBypassTest, CustomExceptionsDotComWildcard) {
auto* hcsm = HostContentSettingsMapFactory::GetForProfile(profile());
NavigateAndCommit(GURL("https://cool.things.com"));
EXPECT_CALL(*mock(),
OnStatusChanged(
/*controls_visible=*/true, /*protections_on=*/true,
CookieControlsEnforcement::kNoEnforcement,
CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration(),
GetThirdPartyCookiesFeatureForEnforcement(
CookieControlsEnforcement::kNoEnforcement,
BlockingStatus::kBlocked)));
EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
/*icon_visible=*/false, /*protections_on=*/true,
CookieBlocking3pcdStatus::kNotIn3pcd,
/*should_highlight=*/false));
cookie_controls()->Update(web_contents());
testing::Mock::VerifyAndClearExpectations(mock());
// Creating an exception turns protections off. The exception
// is set at the TLD level and cannot be reset, it is enforced by cookie
// setting.
EXPECT_CALL(*mock(),
OnStatusChanged(
/*controls_visible=*/true, /*protections_on=*/false,
CookieControlsEnforcement::kEnforcedByCookieSetting,
CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration(),
GetThirdPartyCookiesFeatureForEnforcement(
CookieControlsEnforcement::kEnforcedByCookieSetting,
BlockingStatus::kAllowed)));
EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
/*icon_visible=*/true, /*protections_on=*/false,
CookieBlocking3pcdStatus::kNotIn3pcd,
/*should_highlight=*/false));
hcsm->SetContentSettingCustomScope(
ContentSettingsPattern::Wildcard(),
ContentSettingsPattern::FromString("[*.]com"),
ContentSettingsType::COOKIES, CONTENT_SETTING_ALLOW);
testing::Mock::VerifyAndClearExpectations(mock());
}
TEST_F(CookieControlsUserBypassTest, FinishedPageReloadWithChangedSettings) {
// Check that when the page is reloaded after settings have changed, that
// the appropriate observer method is fired. Reloading the page without a
// change, should not fire the observer.
EXPECT_CALL(*mock(), OnFinishedPageReloadWithChangedSettings()).Times(0);
cookie_controls()->Update(web_contents());
NavigateAndCommit(GURL(kUrl));
// Loading the same page after not making an effective change should not fire.
cookie_controls()->OnCookieBlockingEnabledForSite(false);
ValidateCookieControlsActivatedUKM(
/*fed_cm_initiated=*/false,
/*storage_access_api_requested=*/false,
/*page_refresh_count=*/0, /*repeated_activation=*/false,
blink::mojom::EngagementLevel::NONE,
ThirdPartySiteDataAccessType::kNoThirdPartySiteAccesses);
cookie_controls()->OnCookieBlockingEnabledForSite(true);
NavigateAndCommit(GURL(kUrl));
// Loading a different page after making an effective change should not fire.
cookie_controls()->SetUserChangedCookieBlockingForSite(true);
cookie_controls()->OnCookieBlockingEnabledForSite(false);
ValidateCookieControlsActivatedUKM(
/*fed_cm_initiated=*/false,
/*storage_access_api_requested=*/false,
/*page_refresh_count=*/1, /*repeated_activation=*/true,
blink::mojom::EngagementLevel::NONE,
ThirdPartySiteDataAccessType::kNoThirdPartySiteAccesses);
NavigateAndCommit(GURL("https://example2.com"));
testing::Mock::VerifyAndClearExpectations(mock());
// Observer should fire when reloaded after change.
EXPECT_CALL(*mock(), OnFinishedPageReloadWithChangedSettings()).Times(2);
cookie_controls()->SetUserChangedCookieBlockingForSite(true);
cookie_controls()->OnCookieBlockingEnabledForSite(false);
ValidateCookieControlsActivatedUKM(
/*fed_cm_initiated=*/false,
/*storage_access_api_requested=*/false,
/*page_refresh_count=*/0, /*repeated_activation=*/false,
blink::mojom::EngagementLevel::NONE,
ThirdPartySiteDataAccessType::kNoThirdPartySiteAccesses);
NavigateAndCommit(GURL("https://example2.com"));
cookie_controls()->SetUserChangedCookieBlockingForSite(true);
cookie_controls()->OnCookieBlockingEnabledForSite(true);
NavigateAndCommit(GURL("https://example2.com"));
}
TEST_F(CookieControlsUserBypassTest,
DoesNotHighlightLabelWhenSettingNotChangedInContext) {
auto* hcsm = HostContentSettingsMapFactory::GetForProfile(profile());
EXPECT_CALL(*mock(), OnFinishedPageReloadWithChangedSettings()).Times(0);
cookie_controls()->Update(web_contents());
NavigateAndCommit(GURL(kUrl));
testing::Mock::VerifyAndClearExpectations(mock());
EXPECT_CALL(*mock(), OnFinishedPageReloadWithChangedSettings()).Times(0);
hcsm->SetContentSettingCustomScope(ContentSettingsPattern::Wildcard(),
ContentSettingsPattern::FromString(kUrl),
ContentSettingsType::COOKIES,
CONTENT_SETTING_ALLOW);
NavigateAndCommit(GURL(kUrl));
testing::Mock::VerifyAndClearExpectations(mock());
EXPECT_CALL(*mock(), OnFinishedPageReloadWithChangedSettings()).Times(0);
hcsm->SetContentSettingCustomScope(ContentSettingsPattern::Wildcard(),
ContentSettingsPattern::FromString(kUrl),
ContentSettingsType::COOKIES,
CONTENT_SETTING_BLOCK);
NavigateAndCommit(GURL(kUrl));
testing::Mock::VerifyAndClearExpectations(mock());
EXPECT_CALL(*mock(), OnFinishedPageReloadWithChangedSettings()).Times(0);
hcsm->SetContentSettingCustomScope(
ContentSettingsPattern::Wildcard(),
ContentSettingsPattern::FromString("[*.]example.com"),
ContentSettingsType::COOKIES, CONTENT_SETTING_ALLOW);
NavigateAndCommit(GURL(kUrl));
testing::Mock::VerifyAndClearExpectations(mock());
}
TEST_F(CookieControlsUserBypassTest, IconHighlightedAfterExceptionExpires) {
NavigateAndCommit(GURL(kUrl));
page_specific_content_settings()->OnBrowsingDataAccessed(
CreateUnpartitionedStorageKey(GURL("https://thirdparty.com")),
BrowsingDataModel::StorageType::kQuotaStorage,
/*blocked=*/true);
EXPECT_CALL(*mock(),
OnStatusChanged(
/*controls_visible=*/true, /*protections_on=*/true,
CookieControlsEnforcement::kNoEnforcement,
CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration(),
GetThirdPartyCookiesFeatureForEnforcement(
CookieControlsEnforcement::kNoEnforcement,
BlockingStatus::kBlocked)));
EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
/*icon_visible=*/true, /*protections_on=*/true,
CookieBlocking3pcdStatus::kNotIn3pcd,
/*should_highlight=*/false));
cookie_controls()->Update(web_contents());
testing::Mock::VerifyAndClearExpectations(mock());
// Enable third-party cookies.
EXPECT_CALL(*mock(), OnStatusChanged(
/*controls_visible=*/true, /*protections_on=*/false,
CookieControlsEnforcement::kNoEnforcement,
CookieBlocking3pcdStatus::kNotIn3pcd, expiration(),
GetThirdPartyCookiesFeatureForEnforcement(
CookieControlsEnforcement::kNoEnforcement,
BlockingStatus::kAllowed)));
EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
/*icon_visible=*/true, /*protections_on=*/false,
CookieBlocking3pcdStatus::kNotIn3pcd,
/*should_highlight=*/false));
cookie_controls()->OnCookieBlockingEnabledForSite(false);
ValidateCookieControlsActivatedUKM(
/*fed_cm_initiated=*/false,
/*storage_access_api_requested=*/false,
/*page_refresh_count=*/0, /*repeated_activation=*/false,
blink::mojom::EngagementLevel::NONE,
ThirdPartySiteDataAccessType::kAnyBlockedThirdPartySiteAccesses);
NavigateAndCommit(GURL(kUrl));
testing::Mock::VerifyAndClearExpectations(mock());
// Wait for exception to expire.
FastForwardTo(expiration() + base::Days(1));
// Visiting the site after exception expires highlights UB.
NavigateAndCommit(GURL(kUrl));
page_specific_content_settings()->OnBrowsingDataAccessed(
CreateUnpartitionedStorageKey(GURL("https://thirdparty.com")),
BrowsingDataModel::StorageType::kQuotaStorage,
/*blocked=*/true);
EXPECT_CALL(*mock(),
OnStatusChanged(
/*controls_visible=*/true, /*protections_on=*/true,
CookieControlsEnforcement::kNoEnforcement,
CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration(),
GetThirdPartyCookiesFeatureForEnforcement(
CookieControlsEnforcement::kNoEnforcement,
BlockingStatus::kBlocked)));
EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
/*icon_visible=*/true, /*protections_on=*/true,
CookieBlocking3pcdStatus::kNotIn3pcd,
/*should_highlight=*/true));
cookie_controls()->Update(web_contents());
testing::Mock::VerifyAndClearExpectations(mock());
// Revisiting the site again after 30 seconds shouldn't highlight UB.
FastForwardBy(base::Seconds(30));
NavigateAndCommit(GURL(kUrl));
page_specific_content_settings()->OnBrowsingDataAccessed(
CreateUnpartitionedStorageKey(GURL("https://thirdparty.com")),
BrowsingDataModel::StorageType::kQuotaStorage,
/*blocked=*/true);
EXPECT_CALL(*mock(),
OnStatusChanged(
/*controls_visible=*/true, /*protections_on=*/true,
CookieControlsEnforcement::kNoEnforcement,
CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration(),
GetThirdPartyCookiesFeatureForEnforcement(
CookieControlsEnforcement::kNoEnforcement,
BlockingStatus::kBlocked)));
EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
/*icon_visible=*/true, /*protections_on=*/true,
CookieBlocking3pcdStatus::kNotIn3pcd,
/*should_highlight=*/false));
cookie_controls()->Update(web_contents());
testing::Mock::VerifyAndClearExpectations(mock());
}
TEST_F(CookieControlsUserBypassTest, StatefulBounce) {
NavigateAndCommit(GURL(kUrl));
page_specific_content_settings()->IncrementStatefulBounceCount();
EXPECT_CALL(*mock(),
OnStatusChanged(
/*controls_visible=*/true, /*protections_on=*/true,
CookieControlsEnforcement::kNoEnforcement,
CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration(),
GetThirdPartyCookiesFeatureForEnforcement(
CookieControlsEnforcement::kNoEnforcement,
BlockingStatus::kBlocked)));
EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
/*icon_visible=*/true, /*protections_on=*/true,
CookieBlocking3pcdStatus::kNotIn3pcd,
/*should_highlight=*/false));
cookie_controls()->Update(web_contents());
}
TEST_F(CookieControlsUserBypassTest, SubresourceBlocked) {
base::test::ScopedFeatureList fingerprinting_protection_feature_list;
fingerprinting_protection_feature_list.InitAndEnableFeature(
fingerprinting_protection_filter::features::
kEnableFingerprintingProtectionFilter);
CreateFingerprintingProtectionWebContentsHelper(
web_contents(), /*pref_service=*/nullptr, /*content_settings=*/nullptr,
/*tracking_protection_settings=*/nullptr, /*is_incognito=*/false);
NavigateAndCommit(GURL(kUrl));
fingerprinting_protection_filter::FingerprintingProtectionWebContentsHelper::
FromWebContents(web_contents())
->NotifyOnBlockedSubresource(
subresource_filter::mojom::ActivationLevel::kEnabled);
EXPECT_CALL(*mock(),
OnStatusChanged(
/*controls_visible=*/true, /*protections_on=*/true,
CookieControlsEnforcement::kNoEnforcement,
CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration(),
GetThirdPartyCookiesFeatureForEnforcement(
CookieControlsEnforcement::kNoEnforcement,
BlockingStatus::kBlocked)));
EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
/*icon_visible=*/true, /*protections_on=*/true,
CookieBlocking3pcdStatus::kNotIn3pcd,
/*should_highlight=*/false));
cookie_controls()->Update(web_contents());
}
TEST_F(CookieControlsUserBypassTest, SubresourceBlockedInIncognito) {
base::test::ScopedFeatureList fingerprinting_protection_feature_list;
fingerprinting_protection_feature_list.InitAndEnableFeature(
fingerprinting_protection_filter::features::
kEnableFingerprintingProtectionFilterInIncognito);
CreateFingerprintingProtectionWebContentsHelper(
web_contents(), /*pref_service=*/nullptr, /*content_settings=*/nullptr,
/*tracking_protection_settings=*/nullptr, /*is_incognito=*/true);
NavigateAndCommit(GURL(kUrl));
fingerprinting_protection_filter::FingerprintingProtectionWebContentsHelper::
FromWebContents(web_contents())
->NotifyOnBlockedSubresource(
subresource_filter::mojom::ActivationLevel::kEnabled);
EXPECT_CALL(*mock(),
OnStatusChanged(
/*controls_visible=*/true, /*protections_on=*/true,
CookieControlsEnforcement::kNoEnforcement,
CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration(),
GetThirdPartyCookiesFeatureForEnforcement(
CookieControlsEnforcement::kNoEnforcement,
BlockingStatus::kBlocked)));
EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
/*icon_visible=*/true, /*protections_on=*/true,
CookieBlocking3pcdStatus::kNotIn3pcd,
/*should_highlight=*/false));
cookie_controls()->Update(web_contents());
}
TEST_F(CookieControlsUserBypassTest, SandboxedTopLevelFrame) {
auto headers = base::MakeRefCounted<net::HttpResponseHeaders>("");
headers->SetHeader("Content-Security-Policy", "sandbox");
auto navigation = content::NavigationSimulator::CreateBrowserInitiated(
GURL(kUrl), web_contents());
navigation->SetResponseHeaders(headers);
navigation->Start();
navigation->Commit();
EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
/*icon_visible=*/true, /*protections_on=*/true,
CookieBlocking3pcdStatus::kNotIn3pcd,
/*should_highlight=*/false));
cookie_controls()->Update(web_contents());
testing::Mock::VerifyAndClearExpectations(mock());
}
TEST_F(CookieControlsUserBypassTest,
FrequentPageReloadsWithoutUpdateBeingCalled) {
NavigateAndCommit(GURL(kUrl));
// Call the entry point animated function without setting up the observer.
cookie_controls()->OnEntryPointAnimated();
auto* hcsm = HostContentSettingsMapFactory::GetForProfile(profile());
// A setting is not recorded.
base::Value stored_value = hcsm->GetWebsiteSetting(
GURL(kUrl), GURL(), ContentSettingsType::COOKIE_CONTROLS_METADATA);
EXPECT_TRUE(stored_value.is_none());
testing::Mock::VerifyAndClearExpectations(mock());
}
TEST_F(CookieControlsUserBypassTest,
BlockingStatusLimitedWhenCookieControlsModePrefIsLimited) {
NavigateAndCommit(GURL("https://cool.things.com"));
EXPECT_CALL(*mock(),
OnStatusChanged(
/*controls_visible=*/true, /*protections_on=*/true,
CookieControlsEnforcement::kNoEnforcement,
CookieBlocking3pcdStatus::kLimited, zero_expiration(),
GetThirdPartyCookiesFeatureForEnforcement(
CookieControlsEnforcement::kNoEnforcement,
BlockingStatus::kLimited)));
EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
/*icon_visible=*/false, /*protections_on=*/true,
CookieBlocking3pcdStatus::kLimited,
/*should_highlight=*/false));
profile()->GetPrefs()->SetInteger(
prefs::kCookieControlsMode,
static_cast<int>(content_settings::CookieControlsMode::kLimited));
cookie_controls()->Update(web_contents());
testing::Mock::VerifyAndClearExpectations(mock());
}
class CookieControlsUserBypassIncognitoTest
: public CookieControlsUserBypassTest {
public:
CookieControlsUserBypassIncognitoTest() = default;
~CookieControlsUserBypassIncognitoTest() override = default;
void SetUp() override {
CookieControlsUserBypassTest::SetUp();
feature_list_.InitAndEnableFeature(
privacy_sandbox::kTrackingProtectionContentSettingUbControl);
incognito_cookie_controls_ =
std::make_unique<content_settings::CookieControlsController>(
CookieSettingsFactory::GetForProfile(incognito_profile()),
CookieSettingsFactory::GetForProfile(profile()),
HostContentSettingsMapFactory::GetForProfile(incognito_profile()),
TrackingProtectionSettingsFactory::GetForProfile(
incognito_profile()),
/*is_incognito_profile=*/true);
incognito_cookie_controls_->AddObserver(mock());
}
void TearDown() override {
incognito_cookie_controls_->RemoveObserver(mock());
incognito_cookie_controls_.reset();
CookieControlsUserBypassTest::TearDown();
}
content_settings::CookieControlsController* incognito_cookie_controls() {
return incognito_cookie_controls_.get();
}
Profile* incognito_profile() {
return profile()->GetPrimaryOTRProfile(/*create_if_needed=*/true);
}
private:
base::test::ScopedFeatureList feature_list_;
std::unique_ptr<content_settings::CookieControlsController>
incognito_cookie_controls_;
};
TEST_F(CookieControlsUserBypassIncognitoTest, AddTrackingProtectionException) {
NavigateAndCommit(GURL(kUrl));
privacy_sandbox::TrackingProtectionSettings* tracking_protection_settings =
TrackingProtectionSettingsFactory::GetForProfile(incognito_profile());
incognito_cookie_controls()->Update(web_contents());
incognito_cookie_controls()->OnCookieBlockingEnabledForSite(false);
EXPECT_TRUE(
tracking_protection_settings->HasTrackingProtectionException(GURL(kUrl)));
testing::Mock::VerifyAndClearExpectations(mock());
}
TEST_F(CookieControlsUserBypassIncognitoTest,
RemoveTrackingProtectionException) {
NavigateAndCommit(GURL(kUrl));
privacy_sandbox::TrackingProtectionSettings* tracking_protection_settings =
TrackingProtectionSettingsFactory::GetForProfile(incognito_profile());
incognito_cookie_controls()->Update(web_contents());
// Ensure tracking protection exception is created.
tracking_protection_settings->AddTrackingProtectionException(GURL(kUrl));
EXPECT_TRUE(
tracking_protection_settings->HasTrackingProtectionException(GURL(kUrl)));
incognito_cookie_controls()->OnCookieBlockingEnabledForSite(true);
EXPECT_FALSE(
tracking_protection_settings->HasTrackingProtectionException(GURL(kUrl)));
testing::Mock::VerifyAndClearExpectations(mock());
}
TEST_F(CookieControlsUserBypassIncognitoTest, ToggleUpdatesUi) {
incognito_cookie_controls()->Update(web_contents());
NavigateAndCommit(GURL(kUrl));
EXPECT_CALL(*mock(),
OnStatusChanged(
/*controls_visible=*/true, /*protections_on=*/false,
CookieControlsEnforcement::kNoEnforcement,
CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration(),
GetThirdPartyCookiesFeatureForEnforcement(
CookieControlsEnforcement::kNoEnforcement,
BlockingStatus::kAllowed)));
EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
/*icon_visible=*/true, /*protections_on=*/false,
CookieBlocking3pcdStatus::kNotIn3pcd,
/*should_highlight=*/false));
incognito_cookie_controls()->OnCookieBlockingEnabledForSite(false);
testing::Mock::VerifyAndClearExpectations(mock());
}
class CookieControlsUserBypassTrackingProtectionUiTest
: public CookieControlsUserBypassTest,
public testing::WithParamInterface<testing::tuple<bool, bool, bool>> {
public:
CookieControlsUserBypassTrackingProtectionUiTest() = default;
~CookieControlsUserBypassTrackingProtectionUiTest() override = default;
void TearDown() override {
incognito_cookie_controls_->RemoveObserver(incognito_mock());
incognito_cookie_controls_.reset();
incognito_web_contents_ = nullptr;
incognito_profile_ = nullptr;
CookieControlsUserBypassTest::TearDown();
}
void SetUp() override {
CookieControlsUserBypassTest::SetUp();
std::vector<base::test::FeatureRef> enabled_features = {
privacy_sandbox::kTrackingProtectionContentSettingUbControl};
if (std::get<1>(GetParam())) {
enabled_features.push_back(privacy_sandbox::kActUserBypassUx);
enabled_features.push_back(privacy_sandbox::kIpProtectionV1);
profile()->GetPrefs()->SetBoolean(prefs::kIpProtectionEnabled, true);
}
if (std::get<2>(GetParam())) {
enabled_features.push_back(privacy_sandbox::kActUserBypassUx);
enabled_features.push_back(privacy_sandbox::kFingerprintingProtectionUx);
profile()->GetPrefs()->SetBoolean(prefs::kFingerprintingProtectionEnabled,
true);
}
feature_list_.InitWithFeatures(enabled_features, {});
incognito_profile_ =
profile()->GetPrimaryOTRProfile(/*create_if_needed=*/true);
incognito_web_contents_ = content::WebContentsTester::CreateTestWebContents(
incognito_profile_, nullptr);
content_settings::PageSpecificContentSettings::CreateForWebContents(
incognito_web_contents_.get(),
std::make_unique<PageSpecificContentSettingsDelegate>(
incognito_web_contents_.get()));
incognito_cookie_controls_ =
std::make_unique<content_settings::CookieControlsController>(
CookieSettingsFactory::GetForProfile(incognito_profile_),
CookieSettingsFactory::GetForProfile(profile()),
HostContentSettingsMapFactory::GetForProfile(incognito_profile_),
TrackingProtectionSettingsFactory::GetForProfile(
incognito_profile_),
/*is_incognito_profile=*/true);
incognito_cookie_controls_->AddObserver(incognito_mock());
}
std::vector<TrackingProtectionFeature> GetFeatureVector(
CookieControlsEnforcement enforcement) {
bool protections_on = std::get<0>(GetParam());
std::vector<TrackingProtectionFeature> features_list;
features_list.push_back(
{FeatureType::kThirdPartyCookies, enforcement,
protections_on &&
enforcement == CookieControlsEnforcement::kNoEnforcement
? BlockingStatus::kBlocked
: BlockingStatus::kAllowed});
// Currently these ACT features do not support different enforcement types.
if (std::get<1>(GetParam())) {
features_list.push_back({FeatureType::kIpProtection,
CookieControlsEnforcement::kNoEnforcement,
protections_on ? BlockingStatus::kHidden
: BlockingStatus::kVisible});
}
if (std::get<2>(GetParam())) {
features_list.push_back({FeatureType::kFingerprintingProtection,
CookieControlsEnforcement::kNoEnforcement,
protections_on ? BlockingStatus::kLimited
: BlockingStatus::kAllowed});
}
return features_list;
}
content::WebContents* incognito_web_contents() {
return incognito_web_contents_.get();
}
Profile* incognito_profile() { return incognito_profile_.get(); }
MockCookieControlsObserver* incognito_mock() { return &incognito_mock_; }
content_settings::CookieControlsController* incognito_cookie_controls() {
return incognito_cookie_controls_.get();
}
private:
base::test::ScopedFeatureList feature_list_;
MockCookieControlsObserver incognito_mock_;
raw_ptr<Profile> incognito_profile_;
std::unique_ptr<content::WebContents> incognito_web_contents_;
std::unique_ptr<content_settings::CookieControlsController>
incognito_cookie_controls_;
};
TEST_P(CookieControlsUserBypassTrackingProtectionUiTest,
AddsActFeaturesToVectorInIncognitoBasedOnFeatureAndExceptionStatus) {
auto* tester = content::WebContentsTester::For(incognito_web_contents());
tester->NavigateAndCommit(GURL(kUrl));
incognito_cookie_controls()->Update(incognito_web_contents());
incognito_cookie_controls()->OnCookieBlockingEnabledForSite(
std::get<0>(GetParam()));
EXPECT_CALL(*incognito_mock(),
OnStatusChanged(
/*controls_visible=*/true, std::get<0>(GetParam()),
CookieControlsEnforcement::kNoEnforcement,
CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration(),
GetFeatureVector(CookieControlsEnforcement::kNoEnforcement)));
incognito_cookie_controls()->Update(incognito_web_contents());
testing::Mock::VerifyAndClearExpectations(incognito_mock());
}
TEST_P(CookieControlsUserBypassTrackingProtectionUiTest,
ProtectionsOnForActFeaturesWhenCookiesAreEnforced) {
NavigateAndCommit(GURL(kUrl));
cookie_controls()->Update(web_contents());
auto* tester = content::WebContentsTester::For(incognito_web_contents());
tester->NavigateAndCommit(GURL(kUrl));
incognito_cookie_controls()->Update(incognito_web_contents());
incognito_cookie_controls()->OnCookieBlockingEnabledForSite(
std::get<0>(GetParam()));
// Allowing 3PCs in regular mode should allow & enforce them in incognito.
// Protections (i.e. the toggle) should still be on iff ACT features are
// enabled.
EXPECT_CALL(*mock(), OnStatusChanged(
/*controls_visible=*/true,
/*protections_on=*/false,
CookieControlsEnforcement::kNoEnforcement,
CookieBlocking3pcdStatus::kNotIn3pcd, expiration(),
GetThirdPartyCookiesFeatureForEnforcement(
CookieControlsEnforcement::kNoEnforcement,
BlockingStatus::kAllowed)));
EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
/*icon_visible=*/true,
/*protections_on=*/false,
CookieBlocking3pcdStatus::kNotIn3pcd,
/*should_highlight=*/false));
bool act_features_enabled =
std::get<1>(GetParam()) || std::get<2>(GetParam());
bool protections_on = act_features_enabled && std::get<0>(GetParam());
EXPECT_CALL(
*incognito_mock(),
OnStatusChanged(
/*controls_visible=*/true, protections_on,
CookieControlsEnforcement::kEnforcedByCookieSetting,
CookieBlocking3pcdStatus::kNotIn3pcd,
std::get<0>(GetParam()) && !act_features_enabled ? expiration()
: zero_expiration(),
GetFeatureVector(
CookieControlsEnforcement::kEnforcedByCookieSetting)));
EXPECT_CALL(*incognito_mock(),
OnCookieControlsIconStatusChanged(
/*icon_visible=*/
!protections_on, // Icon shown when
// protections are off
protections_on, CookieBlocking3pcdStatus::kNotIn3pcd,
/*should_highlight=*/false));
cookie_controls()->OnCookieBlockingEnabledForSite(false);
testing::Mock::VerifyAndClearExpectations(mock());
testing::Mock::VerifyAndClearExpectations(incognito_mock());
}
INSTANTIATE_TEST_SUITE_P(
All,
CookieControlsUserBypassTrackingProtectionUiTest,
testing::Combine(/*protections_on*/ testing::Bool(),
/*kIpProtectionV1*/ testing::Bool(),
/*kFingerprintingProtectionUx*/ testing::Bool()));