0

Reland "[DIPS] Move from //chrome to //content."

This reverts commit 5783b19dd0.

Reason for revert: Fix failing bot.

Original change's description:
> Revert "[DIPS] Move from //chrome to //content."
>
> This reverts commit 8915d9509c.
>
> Reason for revert:
> https://ci.chromium.org/ui/p/chromium/builders/ci/linux-cast-x64-rel/6859/overview
>
> Original change's description:
> > [DIPS] Move from //chrome to //content.
> >
> > By moving DIPS (Bounce Tracking Mitigations) into //content, it will be
> > available for all content embedders, such as Android WebView.
> >
> > Aside from the obvious file moves, the main changes are:
> > - DIPS-related WebContentsObservers are created in
> >   WebContentsImpl::Init() instead of TabHelpers::AttachTabHelpers()
> > - No more DIPSServiceFactory: BrowserContextImpl creates and owns the
> >   DIPSServiceImpl directly
> > - No more DIPSCleanupService (nor its factory): BrowserContextImpl
> >   deletes the DIPS database file if necessary
> > - The logic to trigger DIPS data deletion moved from
> >   ChromeBrowsingDataRemoverDelegate to BrowsingDataRemoverImpl
> > - Tests have to override the ContentBrowserClient instead of setting
> >   prefs and modifying the HostContentSettingsMap to test 3PC behavior
> > - The OpenerHeuristicService was split into two pieces: the part that
> >   creates cookie grants was moved into //content as the new method
> >   BrowserContext::BackfillPopupHeuristicGrants(); and the part that
> >   observes the tracking protection settings stays in //chrome and calls
> >   that method.
> > - Many FeatureParams were moved from tpcd_experiment_features.h in
> >   //chrome to //components/content_settings/core/common/features.h
> > - components/content_settings/core/common was added to content/DEPS
> > - Renamed SiteDataAccessType to DIPSDataAccessType
> >
> > In followup CLs, we will
> > (1) Put all of the DIPS classes, functions, etc into the content namespace (crrev.com/c/6039087)
> > (2) Rename DIPS prefixes to Dips to comply with the style guide
> > (3) Change b/ bug references to crbug.com/
> > (4) Delete DipsDelegate, moving its methods to ContentBrowserClient
> >
> > Bug: 40883201
> > Change-Id: I3c07e867ae00e6817ff286a71722473c08ead624
> > Fuchsia-Binary-Size: Size increase is unavoidable - moving feature from //chrome to //content
> > Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6000406
> > Reviewed-by: Avi Drissman <avi@chromium.org>
> > Reviewed-by: Joshua Hood <jdh@chromium.org>
> > Commit-Queue: Ryan Tarpine <rtarpine@chromium.org>
> > Reviewed-by: Adam Langley <agl@chromium.org>
> > Cr-Commit-Position: refs/heads/main@{#1399855}
>
> Bug: 40883201
> Change-Id: I7c74ccf088a40075dd2134d855dc4ad5ea08ae58
> No-Presubmit: true
> No-Tree-Checks: true
> No-Try: true
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6117354
> Owners-Override: Owen Min <zmin@chromium.org>
> Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
> Auto-Submit: Owen Min <zmin@chromium.org>
> Commit-Queue: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
> Cr-Commit-Position: refs/heads/main@{#1399859}

Bug: 40883201
Change-Id: If191568901b803a8c4d1daebab27a295cbe38a0e
Fuchsia-Binary-Size: Size increase is unavoidable - moving feature from //chrome to //content
Cq-Include-Trybots: luci.chromium.try:linux-cast-x64-rel
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6121689
Reviewed-by: Avi Drissman <avi@chromium.org>
Reviewed-by: Joshua Hood <jdh@chromium.org>
Reviewed-by: Adam Langley <agl@chromium.org>
Owners-Override: Ryan Tarpine <rtarpine@chromium.org>
Reviewed-by: Mike Wasserman <msw@chromium.org>
Commit-Queue: Ryan Tarpine <rtarpine@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1400362}
This commit is contained in:
Ryan Tarpine
2024-12-26 13:56:02 -08:00
committed by Chromium LUCI CQ
parent 6d610a2fae
commit 35c4db2b9a
135 changed files with 3092 additions and 2459 deletions
chrome
components
content_settings
ukm
content
DEPS
browser
public
test
ui/views/controls/webview

@ -369,39 +369,10 @@ static_library("browser") {
"digital_credentials/digital_identity_low_risk_origins.h",
"dips/chrome_dips_delegate.cc",
"dips/chrome_dips_delegate.h",
"dips/cookie_access_filter.cc",
"dips/cookie_access_filter.h",
"dips/dips_bounce_detector.cc",
"dips/dips_bounce_detector.h",
"dips/dips_browser_signin_detector.cc",
"dips/dips_browser_signin_detector.h",
"dips/dips_browser_signin_detector_factory.cc",
"dips/dips_browser_signin_detector_factory.h",
"dips/dips_cleanup_service.cc",
"dips/dips_cleanup_service.h",
"dips/dips_cleanup_service_factory.cc",
"dips/dips_cleanup_service_factory.h",
"dips/dips_database.cc",
"dips/dips_database.h",
"dips/dips_database_migrator.cc",
"dips/dips_database_migrator.h",
"dips/dips_navigation_flow_detector.cc",
"dips/dips_navigation_flow_detector.h",
"dips/dips_redirect_info.cc",
"dips/dips_redirect_info.h",
"dips/dips_service.h",
"dips/dips_service_factory.cc",
"dips/dips_service_factory.h",
"dips/dips_service_impl.cc",
"dips/dips_service_impl.h",
"dips/dips_state.cc",
"dips/dips_state.h",
"dips/dips_storage.cc",
"dips/dips_storage.h",
"dips/dips_utils.cc",
"dips/dips_utils.h",
"dips/persistent_repeating_timer.cc",
"dips/persistent_repeating_timer.h",
"dips/stateful_bounce_counter.cc",
"dips/stateful_bounce_counter.h",
"display_capture/captured_surface_control_permission_context.cc",
@ -1586,18 +1557,10 @@ static_library("browser") {
"tpcd/experiment/experiment_manager_impl.h",
"tpcd/experiment/tpcd_experiment_features.cc",
"tpcd/experiment/tpcd_experiment_features.h",
"tpcd/heuristics/opener_heuristic_metrics.cc",
"tpcd/heuristics/opener_heuristic_metrics.h",
"tpcd/heuristics/opener_heuristic_service.cc",
"tpcd/heuristics/opener_heuristic_service.h",
"tpcd/heuristics/opener_heuristic_service_factory.cc",
"tpcd/heuristics/opener_heuristic_service_factory.h",
"tpcd/heuristics/opener_heuristic_tab_helper.cc",
"tpcd/heuristics/opener_heuristic_tab_helper.h",
"tpcd/heuristics/opener_heuristic_utils.cc",
"tpcd/heuristics/opener_heuristic_utils.h",
"tpcd/heuristics/redirect_heuristic_tab_helper.cc",
"tpcd/heuristics/redirect_heuristic_tab_helper.h",
"tpcd/http_error_observer/http_error_tab_helper.cc",
"tpcd/http_error_observer/http_error_tab_helper.h",
"tpcd/metadata/devtools_observer.cc",

@ -3302,56 +3302,75 @@ const FeatureEntry::FeatureParam
kTpcdHeuristicsGrants_CurrentInteraction_ShortRedirect_MainFrameInitiator
[] = {
{content_settings::features::kTpcdReadHeuristicsGrantsName, "true"},
{tpcd::experiment::
{content_settings::features::
kTpcdWritePopupCurrentInteractionHeuristicsGrantsName,
"30d"},
{tpcd::experiment::kTpcdBackfillPopupHeuristicsGrantsName, "30d"},
{tpcd::experiment::kTpcdPopupHeuristicEnableForIframeInitiatorName,
{content_settings::features::kTpcdBackfillPopupHeuristicsGrantsName,
"30d"},
{content_settings::features::
kTpcdPopupHeuristicEnableForIframeInitiatorName,
"none"},
{tpcd::experiment::kTpcdWriteRedirectHeuristicGrantsName, "15m"},
{tpcd::experiment::kTpcdRedirectHeuristicRequireABAFlowName,
{content_settings::features::kTpcdWriteRedirectHeuristicGrantsName,
"15m"},
{content_settings::features::
kTpcdRedirectHeuristicRequireABAFlowName,
"true"},
{tpcd::experiment::
{content_settings::features::
kTpcdRedirectHeuristicRequireCurrentInteractionName,
"true"}};
const FeatureEntry::FeatureParam
kTpcdHeuristicsGrants_CurrentInteraction_LongRedirect_MainFrameInitiator[] =
{{content_settings::features::kTpcdReadHeuristicsGrantsName, "true"},
{tpcd::experiment::
{content_settings::features::
kTpcdWritePopupCurrentInteractionHeuristicsGrantsName,
"30d"},
{tpcd::experiment::kTpcdBackfillPopupHeuristicsGrantsName, "30d"},
{tpcd::experiment::kTpcdPopupHeuristicEnableForIframeInitiatorName,
{content_settings::features::kTpcdBackfillPopupHeuristicsGrantsName,
"30d"},
{content_settings::features::
kTpcdPopupHeuristicEnableForIframeInitiatorName,
"none"},
{tpcd::experiment::kTpcdWriteRedirectHeuristicGrantsName, "30d"},
{tpcd::experiment::kTpcdRedirectHeuristicRequireABAFlowName, "true"},
{tpcd::experiment::kTpcdRedirectHeuristicRequireCurrentInteractionName,
{content_settings::features::kTpcdWriteRedirectHeuristicGrantsName,
"30d"},
{content_settings::features::kTpcdRedirectHeuristicRequireABAFlowName,
"true"},
{content_settings::features::
kTpcdRedirectHeuristicRequireCurrentInteractionName,
"true"}};
const FeatureEntry::FeatureParam
kTpcdHeuristicsGrants_CurrentInteraction_ShortRedirect_AllFrameInitiator[] =
{{content_settings::features::kTpcdReadHeuristicsGrantsName, "true"},
{tpcd::experiment::
{content_settings::features::
kTpcdWritePopupCurrentInteractionHeuristicsGrantsName,
"30d"},
{tpcd::experiment::kTpcdBackfillPopupHeuristicsGrantsName, "30d"},
{tpcd::experiment::kTpcdPopupHeuristicEnableForIframeInitiatorName,
{content_settings::features::kTpcdBackfillPopupHeuristicsGrantsName,
"30d"},
{content_settings::features::
kTpcdPopupHeuristicEnableForIframeInitiatorName,
"all"},
{tpcd::experiment::kTpcdWriteRedirectHeuristicGrantsName, "15m"},
{tpcd::experiment::kTpcdRedirectHeuristicRequireABAFlowName, "true"},
{tpcd::experiment::kTpcdRedirectHeuristicRequireCurrentInteractionName,
{content_settings::features::kTpcdWriteRedirectHeuristicGrantsName,
"15m"},
{content_settings::features::kTpcdRedirectHeuristicRequireABAFlowName,
"true"},
{content_settings::features::
kTpcdRedirectHeuristicRequireCurrentInteractionName,
"true"}};
const FeatureEntry::FeatureParam
kTpcdHeuristicsGrants_CurrentInteraction_LongRedirect_AllFrameInitiator[] =
{{content_settings::features::kTpcdReadHeuristicsGrantsName, "true"},
{tpcd::experiment::
{content_settings::features::
kTpcdWritePopupCurrentInteractionHeuristicsGrantsName,
"30d"},
{tpcd::experiment::kTpcdBackfillPopupHeuristicsGrantsName, "30d"},
{tpcd::experiment::kTpcdPopupHeuristicEnableForIframeInitiatorName,
{content_settings::features::kTpcdBackfillPopupHeuristicsGrantsName,
"30d"},
{content_settings::features::
kTpcdPopupHeuristicEnableForIframeInitiatorName,
"all"},
{tpcd::experiment::kTpcdWriteRedirectHeuristicGrantsName, "30d"},
{tpcd::experiment::kTpcdRedirectHeuristicRequireABAFlowName, "true"},
{tpcd::experiment::kTpcdRedirectHeuristicRequireCurrentInteractionName,
{content_settings::features::kTpcdWriteRedirectHeuristicGrantsName,
"30d"},
{content_settings::features::kTpcdRedirectHeuristicRequireABAFlowName,
"true"},
{content_settings::features::
kTpcdRedirectHeuristicRequireCurrentInteractionName,
"true"}};
const FeatureEntry::FeatureVariation kTpcdHeuristicsGrantsVariations[] = {

@ -42,9 +42,6 @@
#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
#include "chrome/browser/crash_upload_list/crash_upload_list.h"
#include "chrome/browser/custom_handlers/protocol_handler_registry_factory.h"
#include "chrome/browser/dips/chrome_dips_delegate.h"
#include "chrome/browser/dips/dips_service_impl.h"
#include "chrome/browser/dips/dips_utils.h"
#include "chrome/browser/domain_reliability/service_factory.h"
#include "chrome/browser/downgrade/user_data_downgrade.h"
#include "chrome/browser/download/download_prefs.h"
@ -152,7 +149,6 @@
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/browsing_data_filter_builder.h"
#include "content/public/browser/dips_delegate.h"
#include "content/public/browser/host_zoom_map.h"
#include "content/public/browser/origin_trials_controller_delegate.h"
#include "content/public/browser/prefetch_service_delegate.h"
@ -284,8 +280,7 @@ ChromeBrowsingDataRemoverDelegate::ChromeBrowsingDataRemoverDelegate(
webapp_registry_(std::make_unique<WebappRegistry>())
#endif
,
credential_store_(MakeCredentialStore()),
dips_delegate_(ChromeDipsDelegate::Create()) {
credential_store_(MakeCredentialStore()) {
domain_reliability_clearer_ = base::BindRepeating(
[](BrowserContext* browser_context,
content::BrowsingDataFilterBuilder* filter_builder,
@ -909,30 +904,6 @@ void ChromeBrowsingDataRemoverDelegate::RemoveEmbedderData(
->RemoveEmbargoAndResetCounts(filter);
}
// Different types of DIPS events are cleared for DATA_TYPE_HISTORY and
// DATA_TYPE_COOKIES.
DIPSEventRemovalType dips_mask = DIPSEventRemovalType::kNone;
if ((remove_mask & content::BrowsingDataRemover::DATA_TYPE_COOKIES) &&
!filter_builder->PartitionedCookiesOnly()) {
// If there's no delegate, delete everything whenever the user is deleting
// cookies.
dips_mask |= dips_delegate_ ? DIPSEventRemovalType::kStorage
: DIPSEventRemovalType::kAll;
}
// If there's a delegate, ask it whether to delete DIPS history.
if (dips_delegate_ &&
dips_delegate_->ShouldDeleteInteractionRecords(remove_mask)) {
dips_mask |= DIPSEventRemovalType::kHistory;
}
if (dips_mask != DIPSEventRemovalType::kNone) {
if (DIPSServiceImpl* dips_service = DIPSServiceImpl::Get(profile_)) {
dips_service->RemoveEvents(delete_begin_, delete_end_,
filter_builder->BuildNetworkServiceFilter(),
dips_mask);
}
}
//////////////////////////////////////////////////////////////////////////////
// Password manager
if (remove_mask & constants::DATA_TYPE_PASSWORDS) {

@ -39,7 +39,6 @@ class WaitableEvent;
namespace content {
class BrowserContext;
class DipsDelegate;
class StoragePartition;
}
@ -266,8 +265,6 @@ class ChromeBrowsingDataRemoverDelegate
std::unique_ptr<device::fido::PlatformCredentialStore> credential_store_;
std::unique_ptr<content::DipsDelegate> dips_delegate_;
base::WeakPtrFactory<ChromeBrowsingDataRemoverDelegate> weak_ptr_factory_{
this};
};

@ -51,8 +51,6 @@
#include "chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_factory.h"
#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
#include "chrome/browser/custom_handlers/protocol_handler_registry_factory.h"
#include "chrome/browser/dips/dips_service_impl.h"
#include "chrome/browser/dips/dips_storage.h"
#include "chrome/browser/domain_reliability/service_factory.h"
#include "chrome/browser/download/chrome_download_manager_delegate.h"
#include "chrome/browser/download/download_core_service_factory.h"
@ -687,44 +685,6 @@ class ClearDomainReliabilityTester {
base::RepeatingCallback<bool(const GURL&)> last_filter_;
};
class RemoveDIPSEventsTester {
public:
explicit RemoveDIPSEventsTester(Profile* profile) {
storage_ = DIPSServiceImpl::Get(profile)->storage();
}
void WriteEventTimes(GURL url,
std::optional<base::Time> storage_time,
std::optional<base::Time> interaction_time) {
if (storage_time.has_value()) {
storage_->AsyncCall(&DIPSStorage::RecordStorage)
.WithArgs(url, storage_time.value(), DIPSCookieMode::kBlock3PC);
}
if (interaction_time.has_value()) {
storage_->AsyncCall(&DIPSStorage::RecordInteraction)
.WithArgs(url, interaction_time.value(), DIPSCookieMode::kBlock3PC);
}
storage_->FlushPostedTasksForTesting();
}
std::optional<StateValue> ReadStateValue(GURL url) {
std::optional<StateValue> value;
storage_->AsyncCall(&DIPSStorage::Read)
.WithArgs(url)
.Then(base::BindLambdaForTesting([&](const DIPSState& state) {
value = state.was_loaded() ? std::make_optional(state.ToStateValue())
: std::nullopt;
}));
storage_->FlushPostedTasksForTesting();
return value;
}
private:
raw_ptr<base::SequenceBound<DIPSStorage>> storage_;
};
class RemoveSecurePaymentConfirmationCredentialsTester {
public:
using MockWrapper = testing::NiceMock<payments::MockWebDataServiceWrapper>;
@ -3588,125 +3548,6 @@ TEST_F(ChromeBrowsingDataRemoverDelegateTest, RemoveTopicSettings) {
EXPECT_TRUE(privacy_sandbox_settings->IsTopicAllowed(topic_two));
}
TEST_F(ChromeBrowsingDataRemoverDelegateTest, RemoveDIPSEventsForLastHour) {
RemoveDIPSEventsTester tester(GetProfile());
GURL url1("https://example1.com");
GURL url2("https://example2.com");
base::Time two_hours_ago = base::Time::Now() - base::Hours(2);
tester.WriteEventTimes(url1, /*storage_time=*/base::Time::Now(),
/*interaction_time=*/std::nullopt);
tester.WriteEventTimes(url2, /*storage_time=*/std::nullopt,
/*interaction_time=*/two_hours_ago);
{
std::optional<StateValue> state_val1 = tester.ReadStateValue(url1);
std::optional<StateValue> state_val2 = tester.ReadStateValue(url2);
ASSERT_TRUE(state_val1.has_value());
EXPECT_TRUE(state_val1->site_storage_times.has_value());
ASSERT_TRUE(state_val2.has_value());
EXPECT_TRUE(state_val2->user_interaction_times.has_value());
}
uint64_t remove_mask = constants::DATA_TYPE_HISTORY |
content::BrowsingDataRemover::DATA_TYPE_COOKIES;
BlockUntilBrowsingDataRemoved(AnHourAgo(), base::Time::Max(), remove_mask,
false);
{
std::optional<StateValue> state_val1 = tester.ReadStateValue(url1);
std::optional<StateValue> state_val2 = tester.ReadStateValue(url2);
EXPECT_FALSE(state_val1.has_value());
ASSERT_TRUE(state_val2.has_value());
EXPECT_TRUE(state_val2->user_interaction_times.has_value());
}
BlockUntilBrowsingDataRemoved(base::Time(), base::Time::Max(), remove_mask,
false);
{
std::optional<StateValue> state_val1 = tester.ReadStateValue(url1);
std::optional<StateValue> state_val2 = tester.ReadStateValue(url2);
EXPECT_FALSE(state_val1.has_value());
EXPECT_FALSE(state_val2.has_value());
}
}
TEST_F(ChromeBrowsingDataRemoverDelegateTest, RemoveDIPSEventsByType) {
RemoveDIPSEventsTester tester(GetProfile());
GURL url1("https://example1.com");
GURL url2("https://example2.com");
GURL url3("https://example3.com");
base::Time two_hours_ago = base::Time::Now() - base::Hours(2);
tester.WriteEventTimes(url1, /*storage_time=*/base::Time::Now(),
/*interaction_time=*/std::nullopt);
tester.WriteEventTimes(url2, /*storage_time=*/std::nullopt,
/*interaction_time=*/base::Time::Now());
tester.WriteEventTimes(url3, /*storage_time=*/base::Time::Now(),
/*interaction_time=*/two_hours_ago);
{
std::optional<StateValue> state_val1 = tester.ReadStateValue(url1);
std::optional<StateValue> state_val2 = tester.ReadStateValue(url2);
std::optional<StateValue> state_val3 = tester.ReadStateValue(url3);
ASSERT_TRUE(state_val1.has_value());
EXPECT_TRUE(state_val1->site_storage_times.has_value());
ASSERT_TRUE(state_val2.has_value());
EXPECT_TRUE(state_val2->user_interaction_times.has_value());
ASSERT_TRUE(state_val3.has_value());
EXPECT_TRUE(state_val3->site_storage_times.has_value());
EXPECT_TRUE(state_val3->user_interaction_times.has_value());
}
// Remove interaction events from DIPS Storage.
BlockUntilBrowsingDataRemoved(AnHourAgo(), base::Time::Max(),
constants::DATA_TYPE_HISTORY, false);
// Verify the interaction event for url2 has been removed.
{
std::optional<StateValue> state_val1 = tester.ReadStateValue(url1);
std::optional<StateValue> state_val2 = tester.ReadStateValue(url2);
std::optional<StateValue> state_val3 = tester.ReadStateValue(url3);
ASSERT_TRUE(state_val1.has_value());
EXPECT_TRUE(state_val1->site_storage_times.has_value());
EXPECT_FALSE(state_val2.has_value());
ASSERT_TRUE(state_val3.has_value());
EXPECT_TRUE(state_val3->site_storage_times.has_value());
EXPECT_TRUE(state_val3->user_interaction_times.has_value());
}
// Remove storage events from DIPS Storage.
BlockUntilBrowsingDataRemoved(AnHourAgo(), base::Time::Max(),
content::BrowsingDataRemover::DATA_TYPE_COOKIES,
false);
// Verify the storage events for url1 and url3 have been removed.
{
std::optional<StateValue> state_val1 = tester.ReadStateValue(url1);
std::optional<StateValue> state_val2 = tester.ReadStateValue(url2);
std::optional<StateValue> state_val3 = tester.ReadStateValue(url3);
EXPECT_FALSE(state_val1.has_value());
EXPECT_FALSE(state_val2.has_value());
ASSERT_TRUE(state_val3.has_value());
EXPECT_FALSE(state_val3->site_storage_times.has_value());
EXPECT_TRUE(state_val3->user_interaction_times.has_value());
}
}
TEST_F(ChromeBrowsingDataRemoverDelegateTest,
ClearPermissionPromptCounts) {
RemovePermissionPromptCountsTest tester(GetProfile());

@ -3665,18 +3665,6 @@ bool ChromeContentBrowserClient::IsFullCookieAccessAllowed(
content::WebContents* web_contents,
const GURL& url,
const blink::StorageKey& storage_key) {
return dips_move::IsFullCookieAccessAllowed(browser_context, web_contents,
url, storage_key);
}
// TODO: crbug.com/369813097 - Move this implementation into
// ChromeContentBrowserClient::IsFullCookieAccessAllowed() after DIPS migrates
// to //content.
namespace dips_move {
bool IsFullCookieAccessAllowed(content::BrowserContext* browser_context,
content::WebContents* web_contents,
const GURL& url,
const blink::StorageKey& storage_key) {
Profile* profile = Profile::FromBrowserContext(browser_context);
scoped_refptr<content_settings::CookieSettings> cookie_settings =
CookieSettingsFactory::GetForProfile(profile);
@ -3688,7 +3676,6 @@ bool IsFullCookieAccessAllowed(content::BrowserContext* browser_context,
url::Origin::Create(storage_key.top_level_site().GetURL()),
cookie_settings->SettingOverridesForStorage());
}
} // namespace dips_move
void ChromeContentBrowserClient::GrantCookieAccessDueToHeuristic(
content::BrowserContext* browser_context,
@ -3696,19 +3683,6 @@ void ChromeContentBrowserClient::GrantCookieAccessDueToHeuristic(
const net::SchemefulSite& accessing_site,
base::TimeDelta ttl,
bool ignore_schemes) {
dips_move::GrantCookieAccessDueToHeuristic(
browser_context, top_frame_site, accessing_site, ttl, ignore_schemes);
}
// TODO: crbug.com/369813097 - Move this implementation into
// ChromeContentBrowserClient::GrantCookieAccessDueToHeuristic() after DIPS
// migrates to //content.
namespace dips_move {
void GrantCookieAccessDueToHeuristic(content::BrowserContext* browser_context,
const net::SchemefulSite& top_frame_site,
const net::SchemefulSite& accessing_site,
base::TimeDelta ttl,
bool ignore_schemes) {
scoped_refptr<content_settings::CookieSettings> cookie_settings =
CookieSettingsFactory::GetForProfile(
Profile::FromBrowserContext(browser_context));
@ -3720,7 +3694,6 @@ void GrantCookieAccessDueToHeuristic(content::BrowserContext* browser_context,
accessing_site.GetURL(), top_frame_site.GetURL(), ttl,
/*use_schemeless_patterns=*/ignore_schemes);
}
} // namespace dips_move
#if BUILDFLAG(IS_CHROMEOS)
void ChromeContentBrowserClient::OnTrustAnchorUsed(

@ -1363,20 +1363,4 @@ class ChromeContentBrowserClient : public content::ContentBrowserClient {
base::WeakPtrFactory<ChromeContentBrowserClient> weak_factory_{this};
};
// DO NOT USE. Functions in this namespace are only for the migration of DIPS to
// the content/ folder. They will be deleted soon.
//
// TODO: crbug.com/369813097 - Remove this after DIPS migrates to //content.
namespace dips_move {
void GrantCookieAccessDueToHeuristic(content::BrowserContext* browser_context,
const net::SchemefulSite& top_frame_site,
const net::SchemefulSite& accessing_site,
base::TimeDelta ttl,
bool ignore_schemes);
bool IsFullCookieAccessAllowed(content::BrowserContext* browser_context,
content::WebContents* web_contents,
const GURL& url,
const blink::StorageKey& storage_key);
} // namespace dips_move
#endif // CHROME_BROWSER_CHROME_CONTENT_BROWSER_CLIENT_H_

@ -30,7 +30,6 @@
#include "chrome/browser/data_saver/data_saver.h"
#include "chrome/browser/devtools/devtools_window.h"
#include "chrome/browser/devtools/protocol/devtools_protocol_test_support.h"
#include "chrome/browser/dips/dips_test_utils.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/extension_util.h"
#include "chrome/browser/extensions/unpacked_installer.h"
@ -38,6 +37,7 @@
#include "chrome/browser/privacy_sandbox/privacy_sandbox_attestations/privacy_sandbox_attestations_mixin.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ssl/https_upgrades_util.h"
#include "chrome/browser/tpcd/support/trial_test_utils.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/webui_url_constants.h"
@ -53,12 +53,17 @@
#include "components/infobars/core/infobar_delegate.h"
#include "components/privacy_sandbox/privacy_sandbox_attestations/privacy_sandbox_attestations.h"
#include "content/public/browser/devtools_agent_host.h"
#include "content/public/browser/dips_redirect_info.h"
#include "content/public/browser/dips_service.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/page_navigator.h"
#include "content/public/browser/ssl_status.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/content_features.h"
#include "content/public/common/referrer.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/dips_service_test_utils.h"
#include "content/public/test/preloading_test_util.h"
#include "content/public/test/prerender_test_util.h"
#include "extensions/browser/api/extensions_api_client.h"
@ -81,6 +86,8 @@
#include "testing/gmock/include/gmock/gmock.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/boringssl/src/include/openssl/ssl.h"
#include "ui/base/page_transition_types.h"
#include "ui/base/window_open_disposition.h"
#include "ui/gfx/codec/png_codec.h"
#include "url/origin.h"
@ -563,6 +570,76 @@ class DevToolsProtocolTest_BounceTrackingMitigations
base::test::ScopedFeatureList scoped_feature_list_;
};
testing::AssertionResult SimulateDipsBounce(content::WebContents* web_contents,
const GURL& initial_url,
const GURL& bounce_url,
const GURL& final_url) {
web_contents = web_contents->OpenURL(
content::OpenURLParams(initial_url, content::Referrer(),
WindowOpenDisposition::NEW_FOREGROUND_TAB,
ui::PageTransition::PAGE_TRANSITION_TYPED,
/*is_renderer_initiated=*/false),
{});
if (!web_contents) {
return testing::AssertionFailure() << "OpenURL() returned nullptr";
}
if (!content::WaitForLoadStop(web_contents)) {
return testing::AssertionFailure() << "Failed to wait for loading to stop";
}
DIPSService* dips_service =
DIPSService::Get(web_contents->GetBrowserContext());
if (!content::NavigateToURLFromRenderer(web_contents, bounce_url)) {
return testing::AssertionFailure()
<< "Failed to navigate to " << bounce_url;
}
tpcd::trial::URLCookieAccessObserver cookie_observer(
web_contents, bounce_url, tpcd::trial::CookieOperation::kChange);
testing::AssertionResult js_result =
content::ExecJs(web_contents, "document.cookie = 'bounce=stateful';",
content::EXECUTE_SCRIPT_NO_USER_GESTURE);
if (!js_result) {
return js_result;
}
cookie_observer.Wait();
content::DipsRedirectChainObserver final_observer(dips_service, final_url);
if (!content::NavigateToURLFromRendererWithoutUserGesture(web_contents,
final_url)) {
return testing::AssertionFailure() << "Failed to navigate to " << final_url;
}
// End redirect chain by closing the tab.
web_contents->Close();
final_observer.Wait();
if (testing::Test::HasFailure()) {
return testing::AssertionFailure() << "Failure generated while waiting for "
"the redirect chain to be reported";
}
if (final_observer.redirects()->size() != 1) {
return testing::AssertionFailure() << "Expected 1 redirect; found "
<< final_observer.redirects()->size();
}
const DIPSRedirectInfo& redirect = *final_observer.redirects()->at(0);
if (redirect.url.url != bounce_url) {
return testing::AssertionFailure() << "Expected redirect at " << bounce_url
<< "; found " << redirect.url.url;
}
if (redirect.access_type != DIPSDataAccessType::kWrite &&
redirect.access_type != DIPSDataAccessType::kReadWrite) {
return testing::AssertionFailure()
<< "No write access recorded for redirect";
}
return testing::AssertionSuccess();
}
IN_PROC_BROWSER_TEST_F(DevToolsProtocolTest_BounceTrackingMitigations,
RunBounceTrackingMitigations) {
SetBlockThirdPartyCookies(true);
@ -577,8 +654,7 @@ IN_PROC_BROWSER_TEST_F(DevToolsProtocolTest_BounceTrackingMitigations,
// Record a stateful bounce for `bouncer`.
ASSERT_TRUE(SimulateDipsBounce(
web_contents(), embedded_test_server()->GetURL("a.test", "/empty.html"),
bouncer, embedded_test_server()->GetURL("b.test", "/empty.html"),
embedded_test_server()->GetURL("c.test", "/empty.html")));
bouncer, embedded_test_server()->GetURL("b.test", "/empty.html")));
SendCommandSync("Storage.runBounceTrackingMitigations");

@ -3,13 +3,14 @@
// found in the LICENSE file.
#include "chrome/browser/devtools/protocol/storage_handler.h"
#include <memory>
#include "chrome/browser/devtools/protocol/storage.h"
#include "chrome/browser/dips/dips_service.h"
#include "chrome/browser/first_party_sets/first_party_sets_policy_service.h"
#include "chrome/browser/first_party_sets/first_party_sets_policy_service_factory.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/dips_service.h"
#include "content/public/browser/web_contents.h"
#include "net/first_party_sets/first_party_set_entry.h"
@ -21,6 +22,7 @@ StorageHandler::StorageHandler(content::WebContents* web_contents,
StorageHandler::~StorageHandler() = default;
// TODO: crbug.com/380896828 - move CDP support for DIPS to //content.
void StorageHandler::RunBounceTrackingMitigations(
std::unique_ptr<RunBounceTrackingMitigationsCallback> callback) {
DIPSService* dips_service =

@ -4,87 +4,15 @@
source_set("unit_tests") {
testonly = true
sources = [
"cookie_access_filter_unittest.cc",
"dips_bounce_detector_unittest.cc",
"dips_browser_signin_detector_unittest.cc",
"dips_cleanup_service_unittest.cc",
"dips_database_migrator_unittest.cc",
"dips_database_unittest.cc",
"dips_service_unittest.cc",
"dips_storage_unittest.cc",
"dips_utils_unittest.cc",
"persistent_repeating_timer_unittest.cc",
]
sources = [ "dips_browser_signin_detector_unittest.cc" ]
deps = [
":golden_dbs_bundle_data",
"//base",
"//base/test:test_support",
"//chrome/browser",
"//chrome/browser/browsing_data:constants",
"//chrome/browser/content_settings",
"//chrome/browser/content_settings:content_settings_factory",
"//chrome/test:test_support",
"//components/site_engagement/content",
"//components/ukm:test_support",
"//content/test:test_support",
"//sql:test_support",
"//testing/gtest",
"//url",
]
data = [ "//chrome/test/data/dips/v1.sql" ]
}
bundle_data("golden_dbs_bundle_data") {
visibility = [ ":unit_tests" ]
testonly = true
sources = [
"//chrome/test/data/dips/v1.sql",
"//chrome/test/data/dips/v2.sql",
"//chrome/test/data/dips/v3.sql",
"//chrome/test/data/dips/v4.sql",
"//chrome/test/data/dips/v5.sql",
]
outputs = [ "{{bundle_resources_dir}}/" +
"{{source_root_relative_dir}}/{{source_file_part}}" ]
}
source_set("browser_tests") {
testonly = true
sources = [
"dips_bounce_detector_browsertest.cc",
"dips_helper_browsertest.cc",
"dips_navigation_flow_detector_browsertest.cc",
]
defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
deps = [
"//base",
"//base/test:test_support",
"//chrome/browser",
"//chrome/browser/browsing_data:constants",
"//chrome/browser/content_settings",
"//chrome/browser/content_settings:content_settings_factory",
"//chrome/browser/ui",
"//chrome/browser/ui/tabs:tabs_public",
"//chrome/test:test_support",
"//components/privacy_sandbox:tracking_protection_prefs",
"//components/privacy_sandbox/privacy_sandbox_attestations",
"//components/site_engagement/content:content",
"//components/subresource_filter/core/common:test_support",
"//components/ukm:test_support",
"//content/test:test_support",
"//device/fido:test_support",
"//net:test_support",
"//third_party/blink/public/common",
]
if (is_android) {
deps += [ "//chrome/test:test_support_ui_android" ]
} else {
deps += [ "//chrome/test:test_support_ui" ]
}
}

@ -1,15 +0,0 @@
include_rules = [
"+components/keyed_service",
"+content/public/browser",
"+content/public/test",
"+net",
]
specific_include_rules = {
"dips_bounce_detector_browsertest\.cc": [
"+device/fido/virtual_fido_device_factory.h",
],
"dips_navigation_flow_detector_browsertest\.cc": [
"+device/fido/virtual_fido_device_factory.h",
],
}

@ -28,7 +28,7 @@ ProfileSelections GetHumanProfileSelections() {
} // namespace
static_assert(content::DipsDelegate::kDefaultRemoveMask ==
static_assert(DIPSService::kDefaultRemoveMask ==
(chrome_browsing_data_remover::FILTERABLE_DATA_TYPES &
((content::BrowsingDataRemover::DATA_TYPE_CONTENT_END << 1) -
1)),

@ -10,12 +10,12 @@
#include "base/files/file_util.h"
#include "base/strings/strcat.h"
#include "base/time/time.h"
#include "chrome/browser/dips/dips_service.h"
#include "components/signin/public/identity_manager/account_info.h"
#include "components/signin/public/identity_manager/accounts_in_cookie_jar_info.h"
#include "components/signin/public/identity_manager/identity_manager.h"
#include "components/signin/public/identity_manager/signin_constants.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/dips_service.h"
using signin::constants::kNoHostedDomainFound;

@ -6,11 +6,10 @@
#include "chrome/browser/dips/chrome_dips_delegate.h"
#include "chrome/browser/dips/dips_browser_signin_detector.h"
#include "chrome/browser/dips/dips_service.h"
#include "chrome/browser/dips/dips_service_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/signin/identity_manager_factory.h"
#include "components/keyed_service/content/browser_context_dependency_manager.h"
#include "content/public/browser/dips_service.h"
#include "content/public/common/content_features.h"
/*static*/
@ -40,7 +39,6 @@ DIPSBrowserSigninDetectorFactory::DIPSBrowserSigninDetectorFactory(PassKey)
: BrowserContextKeyedServiceFactory(
"DIPSBrowserSigninDetector",
BrowserContextDependencyManager::GetInstance()) {
DependsOn(DIPSServiceFactory::GetInstance());
DependsOn(IdentityManagerFactory::GetInstance());
}

@ -18,8 +18,6 @@
#include "base/test/test_file_util.h"
#include "base/test/test_future.h"
#include "chrome/browser/dips/dips_browser_signin_detector_factory.h"
#include "chrome/browser/dips/dips_service.h"
#include "chrome/browser/dips/dips_test_utils.h"
#include "chrome/browser/signin/chrome_signin_client_factory.h"
#include "chrome/browser/signin/chrome_signin_client_test_util.h"
#include "chrome/browser/signin/identity_test_environment_profile_adaptor.h"
@ -29,6 +27,7 @@
#include "components/signin/public/identity_manager/account_info.h"
#include "components/signin/public/identity_manager/account_managed_status_finder.h"
#include "components/signin/public/identity_manager/identity_test_environment.h"
#include "content/public/browser/dips_service.h"
#include "content/public/common/content_features.h"
#include "content/public/test/browser_task_environment.h"
#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
@ -132,9 +131,6 @@ class BrowserSigninDetectorServiceTest : public testing::Test {
base::StrCat({"foo@", kIdentityProviderDomain}), kIdentityProviderDomain};
private:
ScopedInitFeature feature_{features::kDIPS,
/*enable:*/ true,
/*params:*/ {{"persist_database", "true"}}};
network::TestURLLoaderFactory test_url_loader_factory_;
std::unique_ptr<TestingProfile> profile_;
std::unique_ptr<IdentityTestEnvironmentProfileAdaptor>

@ -1,32 +0,0 @@
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/dips/dips_cleanup_service.h"
#include "chrome/browser/dips/dips_cleanup_service_factory.h"
#include "chrome/browser/dips/dips_storage.h"
#include "content/public/common/content_features.h"
DIPSCleanupService::DIPSCleanupService(content::BrowserContext* context) {
DCHECK(!base::FeatureList::IsEnabled(features::kDIPS));
DIPSStorage::DeleteDatabaseFiles(
GetDIPSFilePath(context),
base::BindOnce(&DIPSCleanupService::OnCleanupFinished,
weak_factory_.GetWeakPtr()));
}
DIPSCleanupService::~DIPSCleanupService() = default;
/* static */
DIPSCleanupService* DIPSCleanupService::Get(content::BrowserContext* context) {
return DIPSCleanupServiceFactory::GetForBrowserContext(context);
}
void DIPSCleanupService::WaitOnCleanupForTesting() {
wait_for_cleanup_.Run();
}
void DIPSCleanupService::OnCleanupFinished() {
wait_for_cleanup_.Quit();
}

@ -1,34 +0,0 @@
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_DIPS_DIPS_CLEANUP_SERVICE_H_
#define CHROME_BROWSER_DIPS_DIPS_CLEANUP_SERVICE_H_
#include "base/memory/raw_ptr.h"
#include "base/run_loop.h"
#include "components/keyed_service/core/keyed_service.h"
namespace content {
class BrowserContext;
}
class DIPSCleanupService : public KeyedService {
public:
// Use DIPSCleanupServiceFactory::BuildServiceInstanceForBrowserContext
// instead.
explicit DIPSCleanupService(content::BrowserContext* context);
~DIPSCleanupService() override;
static DIPSCleanupService* Get(content::BrowserContext* context);
void WaitOnCleanupForTesting();
private:
void OnCleanupFinished();
base::RunLoop wait_for_cleanup_;
base::WeakPtrFactory<DIPSCleanupService> weak_factory_{this};
};
#endif // CHROME_BROWSER_DIPS_DIPS_CLEANUP_SERVICE_H_

@ -1,61 +0,0 @@
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/dips/dips_cleanup_service_factory.h"
#include "base/no_destructor.h"
#include "chrome/browser/dips/chrome_dips_delegate.h"
#include "chrome/browser/dips/dips_cleanup_service.h"
#include "components/keyed_service/content/browser_context_dependency_manager.h"
#include "content/public/browser/browser_context.h"
#include "content/public/common/content_features.h"
// static
DIPSCleanupService* DIPSCleanupServiceFactory::GetForBrowserContext(
content::BrowserContext* context) {
return static_cast<DIPSCleanupService*>(
GetInstance()->GetServiceForBrowserContext(context, /*create=*/true));
}
DIPSCleanupServiceFactory* DIPSCleanupServiceFactory::GetInstance() {
static base::NoDestructor<DIPSCleanupServiceFactory> instance;
return instance.get();
}
DIPSCleanupServiceFactory::DIPSCleanupServiceFactory()
: BrowserContextKeyedServiceFactory(
"DIPSCleanupService",
BrowserContextDependencyManager::GetInstance()) {}
DIPSCleanupServiceFactory::~DIPSCleanupServiceFactory() = default;
content::BrowserContext* DIPSCleanupServiceFactory::GetBrowserContextToUse(
content::BrowserContext* context) const {
// Only enable when DIPS is turned off.
if (base::FeatureList::IsEnabled(features::kDIPS)) {
return nullptr;
}
// Only enable for profiles where DIPS is normally enabled.
if (!ChromeDipsDelegate::Create()->ShouldEnableDips(context)) {
return nullptr;
}
// Only enable for profiles where the DIPS DB is written to disk.
if (context->IsOffTheRecord()) {
return nullptr;
}
return context;
}
std::unique_ptr<KeyedService>
DIPSCleanupServiceFactory::BuildServiceInstanceForBrowserContext(
content::BrowserContext* context) const {
return std::make_unique<DIPSCleanupService>(context);
}
bool DIPSCleanupServiceFactory::ServiceIsCreatedWithBrowserContext() const {
return true;
}

@ -1,36 +0,0 @@
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_DIPS_DIPS_CLEANUP_SERVICE_FACTORY_H_
#define CHROME_BROWSER_DIPS_DIPS_CLEANUP_SERVICE_FACTORY_H_
#include "base/no_destructor.h"
#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
namespace content {
class BrowserContext;
}
class DIPSCleanupService;
class DIPSCleanupServiceFactory : public BrowserContextKeyedServiceFactory {
public:
static DIPSCleanupServiceFactory* GetInstance();
static DIPSCleanupService* GetForBrowserContext(
content::BrowserContext* context);
private:
friend base::NoDestructor<DIPSCleanupServiceFactory>;
DIPSCleanupServiceFactory();
~DIPSCleanupServiceFactory() override;
// BrowserContextKeyedServiceFactory:
content::BrowserContext* GetBrowserContextToUse(
content::BrowserContext* context) const override;
std::unique_ptr<KeyedService> BuildServiceInstanceForBrowserContext(
content::BrowserContext* context) const override;
bool ServiceIsCreatedWithBrowserContext() const override;
};
#endif // CHROME_BROWSER_DIPS_DIPS_CLEANUP_SERVICE_FACTORY_H_

@ -1,81 +0,0 @@
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/dips/dips_cleanup_service.h"
#include "base/files/file_util.h"
#include "base/memory/raw_ptr.h"
#include "base/test/test_file_util.h"
#include "chrome/browser/dips/dips_service_impl.h"
#include "chrome/browser/dips/dips_test_utils.h"
#include "chrome/browser/dips/dips_utils.h"
#include "chrome/test/base/testing_profile.h"
#include "content/public/test/browser_task_environment.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
class DIPSCleanupServiceTest : public testing::Test {
protected:
void WaitOnStorage(DIPSServiceImpl* service) {
service->storage()->FlushPostedTasksForTesting();
}
private:
content::BrowserTaskEnvironment task_environment_;
};
TEST_F(DIPSCleanupServiceTest, DontCreateServiceIfFeatureEnabled) {
ScopedInitDIPSFeature init_dips(true);
TestingProfile profile;
EXPECT_EQ(DIPSCleanupService::Get(&profile), nullptr);
}
TEST_F(DIPSCleanupServiceTest, CreateServiceIfFeatureDisabled) {
ScopedInitDIPSFeature init_dips(false);
TestingProfile profile;
EXPECT_NE(DIPSCleanupService::Get(&profile), nullptr);
}
TEST_F(DIPSCleanupServiceTest, DeleteDbFilesIfFeatureDisabled) {
base::FilePath data_path = base::CreateUniqueTempDirectoryScopedToTest();
{
// Ensure the DIPS feature is enabled and the database is set to be
// persisted.
ScopedInitDIPSFeature enable_dips(true, {{"persist_database", "true"}});
std::unique_ptr<TestingProfile> profile =
TestingProfile::Builder().SetPath(data_path).Build();
DIPSServiceImpl* dips_service = DIPSServiceImpl::Get(profile.get());
ASSERT_NE(dips_service, nullptr);
ASSERT_EQ(DIPSCleanupService::Get(profile.get()), nullptr);
WaitOnStorage(dips_service);
dips_service->WaitForFileDeletionCompleteForTesting();
ASSERT_TRUE(base::PathExists(GetDIPSFilePath(profile.get())));
}
// This should be equivalent to the DIPS db filepath in |data_path|.
ASSERT_TRUE(base::PathExists(data_path.Append(kDIPSFilename)));
{
// Disable the DIPS feature.
ScopedInitDIPSFeature disable_dips(false);
std::unique_ptr<TestingProfile> profile =
TestingProfile::Builder().SetPath(data_path).Build();
DIPSCleanupService* cleanup_service =
DIPSCleanupService::Get(profile.get());
ASSERT_NE(cleanup_service, nullptr);
ASSERT_EQ(DIPSServiceImpl::Get(profile.get()), nullptr);
cleanup_service->WaitOnCleanupForTesting();
EXPECT_FALSE(base::PathExists(GetDIPSFilePath(profile.get())));
}
}

@ -0,0 +1,156 @@
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <string>
#include <vector>
#include "base/functional/bind.h"
#include "base/values.h"
#include "chrome/test/base/chrome_test_utils.h"
#include "chrome/test/base/platform_browser_test.h"
#include "content/public/browser/cookie_access_details.h"
#include "content/public/browser/global_routing_id.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/test_devtools_protocol_client.h"
#include "net/dns/mock_host_resolver.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
using CookieOperation = content::CookieAccessDetails::Type;
class FrameCookieAccessObserver : public content::WebContentsObserver {
public:
explicit FrameCookieAccessObserver(
content::WebContents* web_contents,
content::RenderFrameHost* render_frame_host,
CookieOperation access_type)
: WebContentsObserver(web_contents),
rfh_token_(render_frame_host->GetGlobalFrameToken()),
access_type_(access_type) {}
// Wait until the frame accesses cookies.
void Wait() { run_loop_.Run(); }
// WebContentsObserver override
void OnCookiesAccessed(content::RenderFrameHost* render_frame_host,
const content::CookieAccessDetails& details) override {
if (details.type == access_type_ &&
render_frame_host->GetGlobalFrameToken() == rfh_token_) {
run_loop_.Quit();
}
}
private:
const content::GlobalRenderFrameHostToken rfh_token_;
const CookieOperation access_type_;
base::RunLoop run_loop_;
};
} // namespace
class DIPSBounceTrackingDevToolsIssueTest
: public content::TestDevToolsProtocolClient,
public PlatformBrowserTest {
protected:
void SetUpOnMainThread() override {
PlatformBrowserTest::SetUpOnMainThread();
host_resolver()->AddRule("*", "127.0.0.1");
embedded_test_server()->AddDefaultHandlers(GetChromeTestDataDir());
ASSERT_TRUE(embedded_test_server()->Start());
}
void WaitForIssueAndCheckTrackingSites(
const std::vector<std::string>& sites) {
auto is_dips_issue = [](const base::Value::Dict& params) {
return *(params.FindStringByDottedPath("issue.code")) ==
"BounceTrackingIssue";
};
// Wait for notification of a Bounce Tracking Issue.
base::Value::Dict params = WaitForMatchingNotification(
"Audits.issueAdded", base::BindRepeating(is_dips_issue));
ASSERT_EQ(*params.FindStringByDottedPath("issue.code"),
"BounceTrackingIssue");
base::Value::Dict* bounce_tracking_issue_details =
params.FindDictByDottedPath("issue.details.bounceTrackingIssueDetails");
ASSERT_TRUE(bounce_tracking_issue_details);
std::vector<std::string> tracking_sites;
base::Value::List* tracking_sites_list =
bounce_tracking_issue_details->FindList("trackingSites");
if (tracking_sites_list) {
for (const auto& val : *tracking_sites_list) {
tracking_sites.push_back(val.GetString());
}
}
// Verify the reported tracking sites match the expected sites.
EXPECT_THAT(tracking_sites, testing::ElementsAreArray(sites));
// Clear existing notifications so subsequent calls don't fail by checking
// `sites` against old notifications.
ClearNotifications();
}
void TearDownOnMainThread() override {
DetachProtocolClient();
PlatformBrowserTest::TearDownOnMainThread();
}
};
IN_PROC_BROWSER_TEST_F(DIPSBounceTrackingDevToolsIssueTest,
BounceTrackingDevToolsIssue) {
content::WebContents* web_contents =
chrome_test_utils::GetActiveWebContents(this);
// Visit initial page on a.test.
ASSERT_TRUE(content::NavigateToURL(
web_contents, embedded_test_server()->GetURL("a.test", "/title1.html")));
// Open DevTools and enable Audit domain.
AttachToWebContents(web_contents);
SendCommandSync("Audits.enable");
ClearNotifications();
// Navigate with a click (not a redirect) to b.test, which S-redirects to
// c.test.
ASSERT_TRUE(content::NavigateToURLFromRenderer(
web_contents,
embedded_test_server()->GetURL(
"b.test", "/cross-site-with-cookie/c.test/title1.html"),
embedded_test_server()->GetURL("c.test", "/title1.html")));
WaitForIssueAndCheckTrackingSites({"b.test"});
// Write a cookie via JS on c.test.
content::RenderFrameHost* frame = web_contents->GetPrimaryMainFrame();
FrameCookieAccessObserver cookie_observer(web_contents, frame,
CookieOperation::kChange);
ASSERT_TRUE(content::ExecJs(frame, "document.cookie = 'foo=bar';",
content::EXECUTE_SCRIPT_NO_USER_GESTURE));
cookie_observer.Wait();
// Navigate without a click (i.e. by C-redirecting) to d.test.
ASSERT_TRUE(content::NavigateToURLFromRendererWithoutUserGesture(
web_contents, embedded_test_server()->GetURL("d.test", "/title1.html")));
WaitForIssueAndCheckTrackingSites({"c.test"});
// Navigate without a click (i.e. by C-redirecting) to e.test, which
// S-redirects to f.test, which S-redirects to g.test.
ASSERT_TRUE(content::NavigateToURLFromRendererWithoutUserGesture(
web_contents,
embedded_test_server()->GetURL(
"e.test",
"/cross-site-with-cookie/f.test/cross-site-with-cookie/g.test/"
"title1.html"),
embedded_test_server()->GetURL("g.test", "/title1.html")));
// Note d.test is not listed as a potentially tracking site since it did not
// write cookies before bouncing the user.
WaitForIssueAndCheckTrackingSites({"e.test", "f.test"});
}

@ -1,56 +0,0 @@
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_DIPS_DIPS_SERVICE_H_
#define CHROME_BROWSER_DIPS_DIPS_SERVICE_H_
#include <string>
#include <vector>
#include "base/functional/callback_forward.h"
#include "base/observer_list_types.h"
#include "base/supports_user_data.h"
#include "base/time/time.h"
#include "chrome/browser/dips/dips_redirect_info.h"
class GURL;
namespace content {
class BrowserContext;
class WebContents;
} // namespace content
// When DIPS moves to //content, DIPSService will be exposed in the Content API,
// available to embedders such as Chrome.
class DIPSService : public base::SupportsUserData {
public:
using DeletedSitesCallback =
base::OnceCallback<void(const std::vector<std::string>& sites)>;
using CheckInteractionCallback = base::OnceCallback<void(bool)>;
class Observer : public base::CheckedObserver {
public:
virtual void OnStatefulBounce(content::WebContents* web_contents) {}
virtual void OnChainHandled(const DIPSRedirectChainInfoPtr& chain) {}
};
static DIPSService* Get(content::BrowserContext* context);
virtual void RecordBrowserSignIn(std::string_view domain) = 0;
virtual void DeleteEligibleSitesImmediately(
DeletedSitesCallback callback) = 0;
virtual void RecordInteractionForTesting(const GURL& url) = 0;
virtual void DidSiteHaveInteractionSince(
const GURL& url,
base::Time bound,
CheckInteractionCallback callback) const = 0;
virtual void AddObserver(Observer* observer) = 0;
virtual void RemoveObserver(const Observer* observer) = 0;
};
#endif // CHROME_BROWSER_DIPS_DIPS_SERVICE_H_

@ -1,54 +0,0 @@
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/dips/dips_service_factory.h"
#include "base/no_destructor.h"
#include "chrome/browser/dips/chrome_dips_delegate.h"
#include "chrome/browser/dips/dips_service_impl.h"
#include "components/keyed_service/content/browser_context_dependency_manager.h"
using PassKey = base::PassKey<DIPSServiceFactory>;
/* static */
DIPSServiceImpl* DIPSServiceFactory::GetForBrowserContext(
content::BrowserContext* context) {
auto* dips_service = static_cast<DIPSServiceImpl*>(
GetInstance()->GetServiceForBrowserContext(context, /*create=*/true));
if (dips_service) {
dips_service->MaybeNotifyCreated(PassKey());
}
return dips_service;
}
DIPSServiceFactory* DIPSServiceFactory::GetInstance() {
static base::NoDestructor<DIPSServiceFactory> instance;
return instance.get();
}
DIPSServiceFactory::DIPSServiceFactory()
: BrowserContextKeyedServiceFactory(
"DIPSServiceImpl",
BrowserContextDependencyManager::GetInstance()) {}
DIPSServiceFactory::~DIPSServiceFactory() = default;
content::BrowserContext* DIPSServiceFactory::GetBrowserContextToUse(
content::BrowserContext* context) const {
if (!base::FeatureList::IsEnabled(features::kDIPS)) {
return nullptr;
}
if (!ChromeDipsDelegate::Create()->ShouldEnableDips(context)) {
return nullptr;
}
return context;
}
std::unique_ptr<KeyedService>
DIPSServiceFactory::BuildServiceInstanceForBrowserContext(
content::BrowserContext* context) const {
return std::make_unique<DIPSServiceImpl>(PassKey(), context);
}

@ -1,36 +0,0 @@
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_DIPS_DIPS_SERVICE_FACTORY_H_
#define CHROME_BROWSER_DIPS_DIPS_SERVICE_FACTORY_H_
#include "base/no_destructor.h"
#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
namespace content {
class BrowserContext;
}
class DIPSServiceImpl;
class DIPSServiceFactory : public BrowserContextKeyedServiceFactory {
public:
static DIPSServiceFactory* GetInstance();
static DIPSServiceImpl* GetForBrowserContext(
content::BrowserContext* context);
private:
friend base::NoDestructor<DIPSServiceFactory>;
DIPSServiceFactory();
~DIPSServiceFactory() override;
// BrowserContextKeyedServiceFactory:
content::BrowserContext* GetBrowserContextToUse(
content::BrowserContext* context) const override;
std::unique_ptr<KeyedService> BuildServiceInstanceForBrowserContext(
content::BrowserContext* context) const override;
};
#endif // CHROME_BROWSER_DIPS_DIPS_SERVICE_FACTORY_H_

@ -6,8 +6,8 @@
#include <memory>
#include "chrome/browser/dips/dips_bounce_detector.h"
#include "components/content_settings/browser/page_specific_content_settings.h"
#include "content/public/browser/dips_service.h"
#include "content/public/browser/web_contents_user_data.h"
namespace dips {

@ -8,7 +8,7 @@
#include "base/memory/raw_ptr.h"
#include "base/supports_user_data.h"
#include "base/types/pass_key.h"
#include "chrome/browser/dips/dips_service.h"
#include "content/public/browser/dips_service.h"
namespace dips {

@ -52,8 +52,6 @@
#include "chrome/browser/device_api/managed_configuration_api_factory.h"
#include "chrome/browser/device_reauth/chrome_device_authenticator_factory.h"
#include "chrome/browser/dips/dips_browser_signin_detector_factory.h"
#include "chrome/browser/dips/dips_cleanup_service_factory.h"
#include "chrome/browser/dips/dips_service_factory.h"
#include "chrome/browser/dom_distiller/dom_distiller_service_factory.h"
#include "chrome/browser/domain_reliability/service_factory.h"
#include "chrome/browser/download/background_download_service_factory.h"
@ -814,8 +812,6 @@ void ChromeBrowserMainExtraPartsProfiles::
DiceWebSigninInterceptorFactory::GetInstance();
#endif
DIPSBrowserSigninDetectorFactory::GetInstance();
DIPSCleanupServiceFactory::GetInstance();
DIPSServiceFactory::GetInstance();
DocumentSuggestionsServiceFactory::GetInstance();
dom_distiller::DomDistillerServiceFactory::GetInstance();
DomainDiversityReporterFactory::GetInstance();

@ -447,7 +447,6 @@ IN_PROC_BROWSER_TEST_F(ProfileKeyedServiceGuestBrowserTest,
"ConnectorsService",
"CupsPrintersManagerFactory",
"DIPSBrowserSigninDetector",
"DIPSServiceImpl",
"DataControlsRulesService",
"DownloadCoreService",
"EnterpriseManagementService",

@ -15,7 +15,6 @@
#include "base/time/time.h"
#include "chrome/browser/content_settings/cookie_settings_factory.h"
#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
#include "chrome/browser/dips/dips_service.h"
#include "chrome/browser/first_party_sets/first_party_sets_policy_service.h"
#include "chrome/browser/first_party_sets/first_party_sets_policy_service_factory.h"
#include "chrome/browser/profiles/profile.h"
@ -35,6 +34,7 @@
#include "components/permissions/permission_request_id.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/dips_service.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/runtime_feature_state/runtime_feature_state_document_data.h"
#include "content/public/browser/storage_partition.h"

@ -16,8 +16,6 @@
#include "base/version.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/dips/dips_bounce_detector.h"
#include "chrome/browser/dips/dips_service.h"
#include "chrome/browser/first_party_sets/scoped_mock_first_party_sets_handler.h"
#include "chrome/browser/webid/federated_identity_permission_context.h"
#include "chrome/browser/webid/federated_identity_permission_context_factory.h"
@ -37,6 +35,7 @@
#include "components/permissions/permission_util.h"
#include "components/permissions/test/mock_permission_prompt_factory.h"
#include "components/prefs/pref_service.h"
#include "content/public/browser/dips_service.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/content_features.h"
#include "content/public/test/browser_test_utils.h"

@ -110,57 +110,4 @@ const base::FeatureParam<bool> kExcludePwaOrTwaInstalled{
/*default_value=*/true};
#endif
const char kTpcdWritePopupCurrentInteractionHeuristicsGrantsName[] =
"TpcdWritePopupCurrentInteractionHeuristicsGrants";
const char kTpcdWritePopupPastInteractionHeuristicsGrantsName[] =
"TpcdWritePopupPastInteractionHeuristicsGrants";
const char kTpcdBackfillPopupHeuristicsGrantsName[] =
"TpcdBackfillPopupHeuristicsGrants";
const char kTpcdPopupHeuristicDisableForAdTaggedPopupsName[] =
"TpcdPopupHeuristicDisableForAdTaggedPopups";
const char kTpcdPopupHeuristicEnableForIframeInitiatorName[] =
"TpcdPopupHeuristicEnableForIframeInitiator";
const char kTpcdWriteRedirectHeuristicGrantsName[] =
"TpcdWriteRedirectHeuristicGrants";
const char kTpcdRedirectHeuristicRequireABAFlowName[] =
"TpcdRedirectHeuristicRequireABAFlow";
const char kTpcdRedirectHeuristicRequireCurrentInteractionName[] =
"TpcdRedirectHeuristicRequireCurrentInteraction";
const base::FeatureParam<base::TimeDelta>
kTpcdWritePopupCurrentInteractionHeuristicsGrants{
&content_settings::features::kTpcdHeuristicsGrants,
kTpcdWritePopupCurrentInteractionHeuristicsGrantsName, base::Days(30)};
const base::FeatureParam<base::TimeDelta>
kTpcdWritePopupPastInteractionHeuristicsGrants{
&content_settings::features::kTpcdHeuristicsGrants,
kTpcdWritePopupPastInteractionHeuristicsGrantsName, base::TimeDelta()};
const base::FeatureParam<base::TimeDelta> kTpcdBackfillPopupHeuristicsGrants{
&content_settings::features::kTpcdHeuristicsGrants,
kTpcdBackfillPopupHeuristicsGrantsName, base::Days(30)};
const base::FeatureParam<bool> kTpcdPopupHeuristicDisableForAdTaggedPopups{
&content_settings::features::kTpcdHeuristicsGrants,
kTpcdPopupHeuristicDisableForAdTaggedPopupsName, false};
const base::FeatureParam<EnableForIframeTypes>
kTpcdPopupHeuristicEnableForIframeInitiator{
&content_settings::features::kTpcdHeuristicsGrants,
kTpcdPopupHeuristicEnableForIframeInitiatorName,
EnableForIframeTypes::kAll, &kEnableForIframeTypesOptions};
const base::FeatureParam<base::TimeDelta> kTpcdWriteRedirectHeuristicGrants{
&content_settings::features::kTpcdHeuristicsGrants,
kTpcdWriteRedirectHeuristicGrantsName, base::Minutes(15)};
const base::FeatureParam<bool> kTpcdRedirectHeuristicRequireABAFlow{
&content_settings::features::kTpcdHeuristicsGrants,
kTpcdRedirectHeuristicRequireABAFlowName, true};
const base::FeatureParam<bool> kTpcdRedirectHeuristicRequireCurrentInteraction{
&content_settings::features::kTpcdHeuristicsGrants,
kTpcdRedirectHeuristicRequireCurrentInteractionName, true};
} // namespace tpcd::experiment

@ -57,72 +57,6 @@ extern const base::FeatureParam<base::TimeDelta> kInstallTimeForNewUser;
extern const base::FeatureParam<bool> kExcludePwaOrTwaInstalled;
#endif
extern const char kTpcdWritePopupCurrentInteractionHeuristicsGrantsName[];
extern const char kTpcdWritePopupPastInteractionHeuristicsGrantsName[];
extern const char kTpcdBackfillPopupHeuristicsGrantsName[];
extern const char kTpcdPopupHeuristicDisableForAdTaggedPopupsName[];
extern const char kTpcdPopupHeuristicEnableForIframeInitiatorName[];
extern const char kTpcdWriteRedirectHeuristicGrantsName[];
extern const char kTpcdRedirectHeuristicRequireABAFlowName[];
extern const char kTpcdRedirectHeuristicRequireCurrentInteractionName[];
// The duration of the storage access grant created when observing the Popup
// With Current Interaction scenario. If set to zero duration, do not create a
// grant.
extern const base::FeatureParam<base::TimeDelta>
kTpcdWritePopupCurrentInteractionHeuristicsGrants;
// The duration of the storage access grant created when observing the Popup
// With Past Interaction scenario. If set to zero duration, do not create a
// grant.
extern const base::FeatureParam<base::TimeDelta>
kTpcdWritePopupPastInteractionHeuristicsGrants;
// The lookback and duration of the storage access grants created when
// backfilling the Popup With Current Interaction scenario on onboarding to
// 3PCD. If set to zero duration, to not create backfill grants.
extern const base::FeatureParam<base::TimeDelta>
kTpcdBackfillPopupHeuristicsGrants;
// Whether to disable writing Popup heuristic grants when the popup is opened
// via an ad-tagged frame.
extern const base::FeatureParam<bool>
kTpcdPopupHeuristicDisableForAdTaggedPopups;
enum class EnableForIframeTypes { kNone = 0, kFirstParty = 1, kAll = 2 };
// Whether to enable writing Popup heuristic grants when the popup is opened via
// an iframe initiator.
// * kNone: Ignore popups initiated from iframes.
// * kFirstPartyIframes: Only write grants for popups initiated from 1P iframes,
// or nested tree of all 1P iframes.
// * kAllIframes: Write grants for popups initiated from any frame.
constexpr base::FeatureParam<EnableForIframeTypes>::Option
kEnableForIframeTypesOptions[] = {
{EnableForIframeTypes::kNone, "none"},
{EnableForIframeTypes::kFirstParty, "first-party"},
{EnableForIframeTypes::kAll, "all"},
};
extern const base::FeatureParam<EnableForIframeTypes>
kTpcdPopupHeuristicEnableForIframeInitiator;
// The duration of the storage access grant created when observing the Redirect
// With Current Interaction scenario. If set to zero duration, do not create a
// grant.
extern const base::FeatureParam<base::TimeDelta>
kTpcdWriteRedirectHeuristicGrants;
// Whether to require an A-B-A flow (where the first party preceded the
// third-party redirect in the tab history) when applying the Redirect
// heuristic.
extern const base::FeatureParam<bool> kTpcdRedirectHeuristicRequireABAFlow;
// Whether to require the third-party interaction to be in the current
// navigation when applying the Redirect heuristic.
extern const base::FeatureParam<bool>
kTpcdRedirectHeuristicRequireCurrentInteraction;
} // namespace tpcd::experiment
#endif // CHROME_BROWSER_TPCD_EXPERIMENT_TPCD_EXPERIMENT_FEATURES_H_

@ -1,14 +0,0 @@
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_TPCD_HEURISTICS_OPENER_HEURISTIC_METRICS_H_
#define CHROME_BROWSER_TPCD_HEURISTICS_OPENER_HEURISTIC_METRICS_H_
#include <stdint.h>
// Bucketize `sample` into 50 buckets, capped at maximum and distributed
// non-linearly similarly to base::Histogram::InitializeBucketRanges.
int32_t Bucketize3PCDHeuristicSample(int64_t sample, int64_t maximum);
#endif // CHROME_BROWSER_TPCD_HEURISTICS_OPENER_HEURISTIC_METRICS_H_

@ -4,13 +4,11 @@
#include "chrome/browser/tpcd/heuristics/opener_heuristic_service.h"
#include "chrome/browser/chrome_content_browser_client.h"
#include "chrome/browser/dips/dips_service_impl.h"
#include "base/functional/bind.h"
#include "chrome/browser/privacy_sandbox/tracking_protection_settings_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/tpcd/experiment/tpcd_experiment_features.h"
#include "chrome/browser/tpcd/heuristics/opener_heuristic_service_factory.h"
#include "components/content_settings/core/common/features.h"
#include "components/privacy_sandbox/tracking_protection_settings.h"
#include "url/origin.h"
@ -18,7 +16,6 @@ OpenerHeuristicService::OpenerHeuristicService(
base::PassKey<OpenerHeuristicServiceFactory>,
content::BrowserContext* context)
: browser_context_(context),
dips_(DIPSServiceImpl::Get(context)),
tracking_protection_settings_(
TrackingProtectionSettingsFactory::GetForProfile(
Profile::FromBrowserContext(context))) {
@ -37,65 +34,32 @@ OpenerHeuristicService* OpenerHeuristicService::Get(
}
void OpenerHeuristicService::Shutdown() {
dips_ = nullptr;
tracking_protection_settings_ = nullptr;
tracking_protection_settings_observation_.Reset();
}
void OpenerHeuristicService::OnTrackingProtection3pcdChanged() {
if (IsShuttingDown()) {
return;
}
if (!base::FeatureList::IsEnabled(
content_settings::features::kTpcdHeuristicsGrants) ||
!tpcd::experiment::kTpcdBackfillPopupHeuristicsGrants.Get()
.is_positive()) {
return;
}
if (!tracking_protection_settings_ ||
!tracking_protection_settings_->IsTrackingProtection3pcdEnabled()) {
NotifyBackfillPopupHeuristicGrants(false);
return;
}
// TODO: crbug.com/1502264 - ensure backfill is completed if interrupted
dips_->storage()
->AsyncCall(&DIPSStorage::ReadRecentPopupsWithInteraction)
.WithArgs(tpcd::experiment::kTpcdBackfillPopupHeuristicsGrants.Get())
.Then(
base::BindOnce(&OpenerHeuristicService::BackfillPopupHeuristicGrants,
weak_factory_.GetWeakPtr()));
browser_context_->BackfillPopupHeuristicGrants(base::BindOnce(
&OpenerHeuristicService::NotifyBackfillPopupHeuristicGrants,
weak_factory_.GetWeakPtr()));
}
void OpenerHeuristicService::BackfillPopupHeuristicGrants(
std::vector<PopupWithTime> recent_popups) {
if (IsShuttingDown()) {
return;
}
void OpenerHeuristicService::AddObserver(Observer* observer) {
observers_.AddObserver(observer);
}
for (const auto& popup : recent_popups) {
base::TimeDelta grant_duration =
tpcd::experiment::kTpcdBackfillPopupHeuristicsGrants.Get() -
(base::Time::Now() - popup.last_popup_time);
if (!grant_duration.is_positive()) {
continue;
}
void OpenerHeuristicService::RemoveObserver(Observer* observer) {
observers_.RemoveObserver(observer);
}
// `popup_site` and `opener_site` were read from the DIPS database,
// and were originally computed by calling GetSiteForDIPS().
// GrantCookieAccessDueToHeuristic() takes SchemefulSites, so we create some
// here, but since we pass ignore_schemes=true the scheme doesn't matter
// (and port never matters for SchemefulSites), so we hardcode http and 80.
net::SchemefulSite popup_site(
url::Origin::CreateFromNormalizedTuple("http", popup.popup_site, 80));
net::SchemefulSite opener_site(
url::Origin::CreateFromNormalizedTuple("http", popup.opener_site, 80));
// TODO: crbug.com/40883201 - When we move to //content, we will call
// this via ContentBrowserClient instead of as a standalone function.
dips_move::GrantCookieAccessDueToHeuristic(browser_context_, opener_site,
popup_site, grant_duration,
/*ignore_schemes=*/true);
void OpenerHeuristicService::NotifyBackfillPopupHeuristicGrants(bool success) {
for (auto& observer : observers_) {
observer.OnBackfillPopupHeuristicGrants(browser_context_.get(), success);
}
}

@ -5,12 +5,11 @@
#ifndef CHROME_BROWSER_TPCD_HEURISTICS_OPENER_HEURISTIC_SERVICE_H_
#define CHROME_BROWSER_TPCD_HEURISTICS_OPENER_HEURISTIC_SERVICE_H_
#include <vector>
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "base/observer_list_types.h"
#include "base/scoped_observation.h"
#include "base/types/pass_key.h"
#include "chrome/browser/dips/dips_utils.h"
#include "components/keyed_service/core/keyed_service.h"
#include "components/privacy_sandbox/tracking_protection_settings_observer.h"
@ -22,37 +21,44 @@ namespace privacy_sandbox {
class TrackingProtectionSettings;
}
class DIPSServiceImpl;
class OpenerHeuristicServiceFactory;
class OpenerHeuristicService
: public KeyedService,
privacy_sandbox::TrackingProtectionSettingsObserver {
public:
class Observer : public base::CheckedObserver {
public:
virtual void OnBackfillPopupHeuristicGrants(
content::BrowserContext* browser_context,
bool success) = 0;
};
OpenerHeuristicService(base::PassKey<OpenerHeuristicServiceFactory>,
content::BrowserContext* context);
~OpenerHeuristicService() override;
static OpenerHeuristicService* Get(content::BrowserContext* context);
void AddObserver(Observer* observer);
void RemoveObserver(Observer* observer);
private:
// KeyedService overrides:
void Shutdown() override;
bool IsShuttingDown() const { return !dips_; }
// Create backfill storage access grants for the provided recent popups.
void BackfillPopupHeuristicGrants(std::vector<PopupWithTime> recent_popups);
// TrackingProtectionSettingsObserver overrides:
void OnTrackingProtection3pcdChanged() override;
void NotifyBackfillPopupHeuristicGrants(bool success);
raw_ptr<content::BrowserContext> browser_context_;
raw_ptr<DIPSServiceImpl> dips_;
raw_ptr<privacy_sandbox::TrackingProtectionSettings>
tracking_protection_settings_;
base::ScopedObservation<privacy_sandbox::TrackingProtectionSettings,
privacy_sandbox::TrackingProtectionSettingsObserver>
tracking_protection_settings_observation_{this};
base::ObserverList<Observer, /*check_empty=*/true> observers_;
base::WeakPtrFactory<OpenerHeuristicService> weak_factory_{this};
};

@ -0,0 +1,84 @@
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/tpcd/heuristics/opener_heuristic_service.h"
#include <optional>
#include "base/run_loop.h"
#include "base/test/scoped_feature_list.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/test/base/chrome_test_utils.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/platform_browser_test.h"
#include "components/content_settings/core/common/features.h"
#include "components/prefs/pref_service.h"
#include "components/privacy_sandbox/tracking_protection_prefs.h"
#include "content/public/test/browser_test.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
class OpenerHeuristicServiceTest : public PlatformBrowserTest,
public testing::WithParamInterface<bool> {
public:
void SetUp() override {
feature_list_.InitWithFeaturesAndParameters(
{{content_settings::features::kTpcdHeuristicsGrants,
base::FieldTrialParams{{"TpcdBackfillPopupHeuristicsGrants",
backfill_enabled() ? "1us" : "0"}}}},
// Disable the tracking protection feature so that its pref takes
// precedence.
{content_settings::features::kTrackingProtection3pcd});
PlatformBrowserTest::SetUp();
}
bool backfill_enabled() const { return GetParam(); }
private:
base::test::ScopedFeatureList feature_list_;
};
class BackfillObserver : public OpenerHeuristicService::Observer {
public:
explicit BackfillObserver(OpenerHeuristicService* service) {
observation_.Observe(service);
}
std::optional<bool> status() const { return status_; }
void Wait() { run_loop_.Run(); }
private:
void OnBackfillPopupHeuristicGrants(content::BrowserContext* browser_context,
bool success) override {
status_ = success;
run_loop_.Quit();
}
base::RunLoop run_loop_;
std::optional<bool> status_;
base::ScopedObservation<OpenerHeuristicService,
OpenerHeuristicService::Observer>
observation_{this};
};
IN_PROC_BROWSER_TEST_P(OpenerHeuristicServiceTest,
BackfillWhenTrackingProtection3pcdEnabled) {
Profile* profile = chrome_test_utils::GetProfile(this);
BackfillObserver observer(OpenerHeuristicService::Get(profile));
profile->GetPrefs()->SetBoolean(prefs::kTrackingProtection3pcdEnabled, true);
observer.Wait();
ASSERT_EQ(observer.status(), backfill_enabled());
}
IN_PROC_BROWSER_TEST_P(OpenerHeuristicServiceTest,
DontRequestBackfillWhenTrackingProtection3pcdDisabled) {
Profile* profile = chrome_test_utils::GetProfile(this);
BackfillObserver observer(OpenerHeuristicService::Get(profile));
profile->GetPrefs()->SetBoolean(prefs::kTrackingProtection3pcdEnabled, false);
observer.Wait();
ASSERT_THAT(observer.status(), testing::Optional(false));
}
INSTANTIATE_TEST_SUITE_P(All, OpenerHeuristicServiceTest, testing::Bool());

@ -7,8 +7,6 @@
#include "base/no_destructor.h"
#include "base/types/pass_key.h"
#include "chrome/browser/dips/chrome_dips_delegate.h"
#include "chrome/browser/dips/dips_service_factory.h"
#include "chrome/browser/dips/dips_utils.h"
#include "chrome/browser/privacy_sandbox/tracking_protection_settings_factory.h"
#include "chrome/browser/tpcd/heuristics/opener_heuristic_service.h"
#include "components/content_settings/core/common/features.h"
@ -30,7 +28,6 @@ OpenerHeuristicServiceFactory::OpenerHeuristicServiceFactory()
: BrowserContextKeyedServiceFactory(
"OpenerHeuristicService",
BrowserContextDependencyManager::GetInstance()) {
DependsOn(DIPSServiceFactory::GetInstance());
DependsOn(TrackingProtectionSettingsFactory::GetInstance());
}

@ -1,17 +0,0 @@
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_TPCD_HEURISTICS_OPENER_HEURISTIC_UTILS_H_
#define CHROME_BROWSER_TPCD_HEURISTICS_OPENER_HEURISTIC_UTILS_H_
class GURL;
enum class PopupProvider {
kUnknown = 0,
kGoogle = 1,
};
PopupProvider GetPopupProvider(const GURL& popup_url);
#endif // CHROME_BROWSER_TPCD_HEURISTICS_OPENER_HEURISTIC_UTILS_H_

@ -8,7 +8,6 @@
#include "base/values.h"
#include "chrome/browser/content_settings/cookie_settings_factory.h"
#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
#include "chrome/browser/dips/dips_test_utils.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/subresource_filter/subresource_filter_browser_test_harness.h"
#include "chrome/browser/ui/browser.h"
@ -34,6 +33,93 @@ namespace {
using ::chrome_test_utils::GetActiveWebContents;
class URLCookieAccessObserver : public content::WebContentsObserver {
public:
URLCookieAccessObserver(content::WebContents* web_contents,
GURL url,
content::CookieAccessDetails::Type access_type);
void Wait();
private:
// WebContentsObserver overrides
void OnCookiesAccessed(content::RenderFrameHost* render_frame_host,
const content::CookieAccessDetails& details) override;
void OnCookiesAccessed(content::NavigationHandle* navigation_handle,
const content::CookieAccessDetails& details) override;
GURL url_;
content::CookieAccessDetails::Type access_type_;
base::RunLoop run_loop_;
};
URLCookieAccessObserver::URLCookieAccessObserver(
content::WebContents* web_contents,
GURL url,
content::CookieAccessDetails::Type access_type)
: WebContentsObserver(web_contents),
url_(std::move(url)),
access_type_(access_type) {}
void URLCookieAccessObserver::Wait() {
run_loop_.Run();
}
void URLCookieAccessObserver::OnCookiesAccessed(
content::RenderFrameHost* render_frame_host,
const content::CookieAccessDetails& details) {
if (details.type == access_type_ && details.url == url_) {
run_loop_.Quit();
}
}
void URLCookieAccessObserver::OnCookiesAccessed(
content::NavigationHandle* navigation_handle,
const content::CookieAccessDetails& details) {
if (details.type == access_type_ && details.url == url_) {
run_loop_.Quit();
}
}
bool NavigateToSetCookie(content::WebContents* web_contents,
const net::EmbeddedTestServer* server,
std::string_view host,
bool is_secure_cookie_set,
bool is_ad_tagged) {
std::string relative_url = "/set-cookie?name=value";
if (is_secure_cookie_set) {
relative_url += ";Secure;SameSite=None";
}
if (is_ad_tagged) {
relative_url += "&isad=1";
}
const auto url = server->GetURL(host, relative_url);
URLCookieAccessObserver observer(web_contents, url,
content::CookieAccessDetails::Type::kChange);
bool success = content::NavigateToURL(web_contents, url);
if (success) {
observer.Wait();
}
return success;
}
void CreateImageAndWaitForCookieAccess(content::WebContents* web_contents,
const GURL& image_url) {
URLCookieAccessObserver observer(web_contents, image_url,
content::CookieAccessDetails::Type::kRead);
ASSERT_TRUE(content::ExecJs(web_contents,
content::JsReplace(
R"(
let img = document.createElement('img');
img.src = $1;
document.body.appendChild(img);)",
image_url),
content::EXECUTE_SCRIPT_NO_USER_GESTURE));
// The image must cause a cookie access, or else this will hang.
observer.Wait();
}
} // namespace
class TpcdMetadataDevtoolsObserverBrowserTest

@ -27,8 +27,6 @@
#include "chrome/browser/content_settings/mixed_content_settings_tab_helper.h"
#include "chrome/browser/content_settings/page_specific_content_settings_delegate.h"
#include "chrome/browser/content_settings/sound_content_setting_observer.h"
#include "chrome/browser/dips/dips_bounce_detector.h"
#include "chrome/browser/dips/dips_navigation_flow_detector.h"
#include "chrome/browser/external_protocol/external_protocol_observer.h"
#include "chrome/browser/favicon/favicon_utils.h"
#include "chrome/browser/file_system_access/file_system_access_features.h"
@ -84,8 +82,7 @@
#include "chrome/browser/sync/sessions/sync_sessions_web_contents_router_factory.h"
#include "chrome/browser/tab_contents/navigation_metrics_recorder.h"
#include "chrome/browser/task_manager/web_contents_tags.h"
#include "chrome/browser/tpcd/heuristics/opener_heuristic_tab_helper.h"
#include "chrome/browser/tpcd/heuristics/redirect_heuristic_tab_helper.h"
#include "chrome/browser/tpcd/heuristics/opener_heuristic_service.h"
#include "chrome/browser/tpcd/http_error_observer/http_error_tab_helper.h"
#include "chrome/browser/tpcd/metadata/devtools_observer.h"
#include "chrome/browser/tpcd/support/validity_service.h"
@ -332,17 +329,6 @@ void TabHelpers::AttachTabHelpers(WebContents* web_contents) {
// ChromeSubresourceFilterClient has it as a dependency.
infobars::ContentInfoBarManager::CreateForWebContents(web_contents);
// `PageSpecificContentSettings` (PSCS) needs to come before
// `DIPSWebContentsObserver` for this latter to be correctly added to the PSCS
// observer list.
content_settings::PageSpecificContentSettings::CreateForWebContents(
web_contents,
std::make_unique<PageSpecificContentSettingsDelegate>(web_contents));
// RedirectChainDetector comes before common tab helpers since
// DIPSWebContentsObserver has it as a dependency.
RedirectChainDetector::CreateForWebContents(web_contents);
Profile* profile =
Profile::FromBrowserContext(web_contents->GetBrowserContext());
@ -402,9 +388,10 @@ void TabHelpers::AttachTabHelpers(WebContents* web_contents) {
commerce::ShoppingServiceFactory::GetForBrowserContext(profile),
ISOLATED_WORLD_ID_CHROME_INTERNAL);
ConnectionHelpTabHelper::CreateForWebContents(web_contents);
content_settings::PageSpecificContentSettings::CreateForWebContents(
web_contents,
std::make_unique<PageSpecificContentSettingsDelegate>(web_contents));
CoreTabHelper::CreateForWebContents(web_contents);
DipsNavigationFlowDetector::CreateForWebContents(web_contents);
DIPSWebContentsObserver::MaybeCreateForWebContents(web_contents);
#if BUILDFLAG(ENABLE_REPORTING)
if (base::FeatureList::IsEnabled(
net::features::kReportingApiEnableEnterpriseCookieIssues)) {
@ -442,7 +429,7 @@ void TabHelpers::AttachTabHelpers(WebContents* web_contents) {
MixedContentSettingsTabHelper::CreateForWebContents(web_contents);
NavigationMetricsRecorder::CreateForWebContents(web_contents);
NavigationPredictorPreconnectClient::CreateForWebContents(web_contents);
OpenerHeuristicTabHelper::CreateForWebContents(web_contents);
OpenerHeuristicService::Get(web_contents->GetBrowserContext());
if (optimization_guide::features::IsOptimizationHintsEnabled()) {
OptimizationGuideWebContentsObserver::CreateForWebContents(web_contents);
}
@ -498,7 +485,6 @@ void TabHelpers::AttachTabHelpers(WebContents* web_contents) {
PrefsTabHelper::CreateForWebContents(web_contents);
prerender::NoStatePrefetchTabHelper::CreateForWebContents(web_contents);
RecentlyAudibleHelper::CreateForWebContents(web_contents);
RedirectHeuristicTabHelper::CreateForWebContents(web_contents);
#if BUILDFLAG(IS_ANDROID)
RequestDesktopSiteWebContentsObserverAndroid::CreateForWebContents(
web_contents);

@ -158,8 +158,6 @@ static_library("test_support") {
"../browser/autofill/automated_tests/cache_replayer.h",
"../browser/component_updater/privacy_sandbox_attestations_component_installer_test_util.cc",
"../browser/component_updater/privacy_sandbox_attestations_component_installer_test_util.h",
"../browser/dips/dips_test_utils.cc",
"../browser/dips/dips_test_utils.h",
"../browser/fingerprinting_protection/fingerprinting_protection_filter_browser_test_harness.cc",
"../browser/fingerprinting_protection/fingerprinting_protection_filter_browser_test_harness.h",
"../browser/notifications/notification_display_service_tester.cc",
@ -1540,7 +1538,6 @@ if (is_android) {
"//chrome/browser/autofill",
"//chrome/browser/browsing_data:constants",
"//chrome/browser/content_settings:content_settings_factory",
"//chrome/browser/dips:browser_tests",
"//chrome/browser/enterprise/data_controls:data_controls",
"//chrome/browser/fast_checkout:test_support",
"//chrome/browser/flags:flags_android",
@ -1963,7 +1960,6 @@ if (!is_android) {
"//chrome/browser/data_saver",
"//chrome/browser/devtools",
"//chrome/browser/devtools:test_support",
"//chrome/browser/dips:browser_tests",
"//chrome/browser/enterprise/watermark:watermark_view_lib",
"//chrome/browser/error_reporting:test_support",
"//chrome/browser/favicon",
@ -2780,6 +2776,7 @@ if (!is_android) {
"../browser/devtools/protocol/devtools_protocol_test_support.h",
"../browser/devtools/protocol/devtools_pwa_browsertest.cc",
"../browser/devtools/protocol/form_devtools_issues_browsertest.cc",
"../browser/dips/dips_devtools_browsertest.cc",
"../browser/direct_sockets/direct_sockets_apitest.cc",
"../browser/dom_distiller/distillable_page_utils_browsertest.cc",
"../browser/dom_distiller/dom_distiller_viewer_source_browsertest.cc",
@ -3126,7 +3123,7 @@ if (!is_android) {
"../browser/tpcd/experiment/eligibility_service_browsertest.cc",
"../browser/tpcd/experiment/experiment_manager_impl_browsertest.cc",
"../browser/tpcd/experiment/tpcd_mitigations_browsertest.cc",
"../browser/tpcd/heuristics/opener_heuristic_browsertest.cc",
"../browser/tpcd/heuristics/opener_heuristic_service_browsertest.cc",
"../browser/tpcd/metadata/devtools_observer_browsertest.cc",
"../browser/tpcd/metadata/manager_browsertest.cc",
"../browser/tpcd/support/top_level_trial_service_browsertest.cc",
@ -6372,7 +6369,6 @@ test("unit_tests") {
"//chrome/browser/sync_file_system/drive_backend:sync_file_system_drive_proto",
"//chrome/browser/top_level_storage_access_api:permissions",
"//chrome/browser/tpcd/experiment:unit_tests",
"//chrome/browser/tpcd/heuristics:unit_tests",
"//chrome/browser/ui:browser_element_identifiers",
"//chrome/browser/ui:test_support",
"//chrome/browser/ui:ui_features",

@ -102,6 +102,24 @@ BASE_FEATURE(kNativeUnpartitionedStoragePermittedWhen3PCOff,
const char kTpcdReadHeuristicsGrantsName[] = "TpcdReadHeuristicsGrants";
const char kTpcdWriteRedirectHeuristicGrantsName[] =
"TpcdWriteRedirectHeuristicGrants";
const char kTpcdRedirectHeuristicRequireABAFlowName[] =
"TpcdRedirectHeuristicRequireABAFlow";
const char kTpcdRedirectHeuristicRequireCurrentInteractionName[] =
"TpcdRedirectHeuristicRequireCurrentInteraction";
const char kTpcdWritePopupCurrentInteractionHeuristicsGrantsName[] =
"TpcdWritePopupCurrentInteractionHeuristicsGrants";
const char kTpcdWritePopupPastInteractionHeuristicsGrantsName[] =
"TpcdWritePopupPastInteractionHeuristicsGrants";
const char kTpcdBackfillPopupHeuristicsGrantsName[] =
"TpcdBackfillPopupHeuristicsGrants";
const char kTpcdPopupHeuristicDisableForAdTaggedPopupsName[] =
"TpcdPopupHeuristicDisableForAdTaggedPopups";
const char kTpcdPopupHeuristicEnableForIframeInitiatorName[] =
"TpcdPopupHeuristicEnableForIframeInitiator";
BASE_FEATURE(kTpcdHeuristicsGrants,
"TpcdHeuristicsGrants",
base::FEATURE_ENABLED_BY_DEFAULT);
@ -109,6 +127,42 @@ BASE_FEATURE(kTpcdHeuristicsGrants,
const base::FeatureParam<bool> kTpcdReadHeuristicsGrants{
&kTpcdHeuristicsGrants, kTpcdReadHeuristicsGrantsName, true};
const base::FeatureParam<base::TimeDelta> kTpcdWriteRedirectHeuristicGrants{
&content_settings::features::kTpcdHeuristicsGrants,
kTpcdWriteRedirectHeuristicGrantsName, base::Minutes(15)};
const base::FeatureParam<bool> kTpcdRedirectHeuristicRequireABAFlow{
&content_settings::features::kTpcdHeuristicsGrants,
kTpcdRedirectHeuristicRequireABAFlowName, true};
const base::FeatureParam<bool> kTpcdRedirectHeuristicRequireCurrentInteraction{
&content_settings::features::kTpcdHeuristicsGrants,
kTpcdRedirectHeuristicRequireCurrentInteractionName, true};
const base::FeatureParam<base::TimeDelta>
kTpcdWritePopupCurrentInteractionHeuristicsGrants{
&content_settings::features::kTpcdHeuristicsGrants,
kTpcdWritePopupCurrentInteractionHeuristicsGrantsName, base::Days(30)};
const base::FeatureParam<base::TimeDelta>
kTpcdWritePopupPastInteractionHeuristicsGrants{
&content_settings::features::kTpcdHeuristicsGrants,
kTpcdWritePopupPastInteractionHeuristicsGrantsName, base::TimeDelta()};
const base::FeatureParam<base::TimeDelta> kTpcdBackfillPopupHeuristicsGrants{
&content_settings::features::kTpcdHeuristicsGrants,
kTpcdBackfillPopupHeuristicsGrantsName, base::Days(30)};
const base::FeatureParam<bool> kTpcdPopupHeuristicDisableForAdTaggedPopups{
&content_settings::features::kTpcdHeuristicsGrants,
kTpcdPopupHeuristicDisableForAdTaggedPopupsName, false};
const base::FeatureParam<EnableForIframeTypes>
kTpcdPopupHeuristicEnableForIframeInitiator{
&content_settings::features::kTpcdHeuristicsGrants,
kTpcdPopupHeuristicEnableForIframeInitiatorName,
EnableForIframeTypes::kAll, &kEnableForIframeTypesOptions};
BASE_FEATURE(kContentSettingsPartitioning,
"ContentSettingsPartitioning",
base::FEATURE_DISABLED_BY_DEFAULT);

@ -121,6 +121,27 @@ BASE_DECLARE_FEATURE(kTrackingProtection3pcd);
COMPONENT_EXPORT(CONTENT_SETTINGS_FEATURES)
BASE_DECLARE_FEATURE(kNativeUnpartitionedStoragePermittedWhen3PCOff);
////////////////////////////////////////////////////////////
// Start of third-party cookie access heuristics features //
////////////////////////////////////////////////////////////
// The content module implements the third-party cookie (3PC or TPC) access
// heuristics described here:
// https://github.com/amaliev/3pcd-exemption-heuristics/blob/main/explainer.md
//
// At a high level, the heuristics are enabled/disabled by the
// kTpcdHeuristicsGrants Feature.
//
// The heuristics can be tweaked through the FeatureParams declared below. They
// affect when the heuristics apply and how long the temporary cookie access
// lasts.
//
// The heuristics grant third-party cookie access via calls to
// ContentBrowserClient::GrantCookieAccessDueToHeuristic(). Embedders should
// take these calls, kTpcdHeuristicsGrants, and kTpcdReadHeuristicsGrants into
// account in their implementation of
// ContentBrowserClient::IsFullCookieAccessAllowed().
COMPONENT_EXPORT(CONTENT_SETTINGS_FEATURES)
extern const char kTpcdReadHeuristicsGrantsName[];
@ -134,6 +155,93 @@ BASE_DECLARE_FEATURE(kTpcdHeuristicsGrants);
COMPONENT_EXPORT(CONTENT_SETTINGS_FEATURES)
extern const base::FeatureParam<bool> kTpcdReadHeuristicsGrants;
COMPONENT_EXPORT(CONTENT_SETTINGS_FEATURES)
extern const char kTpcdWriteRedirectHeuristicGrantsName[];
COMPONENT_EXPORT(CONTENT_SETTINGS_FEATURES)
extern const char kTpcdRedirectHeuristicRequireABAFlowName[];
COMPONENT_EXPORT(CONTENT_SETTINGS_FEATURES)
extern const char kTpcdRedirectHeuristicRequireCurrentInteractionName[];
// The duration of the storage access grant created when observing the Redirect
// With Current Interaction scenario. If set to zero duration, do not create a
// grant.
COMPONENT_EXPORT(CONTENT_SETTINGS_FEATURES)
extern const base::FeatureParam<base::TimeDelta>
kTpcdWriteRedirectHeuristicGrants;
// Whether to require an A-B-A flow (where the first party preceded the
// third-party redirect in the tab history) when applying the Redirect
// heuristic.
COMPONENT_EXPORT(CONTENT_SETTINGS_FEATURES)
extern const base::FeatureParam<bool> kTpcdRedirectHeuristicRequireABAFlow;
// Whether to require the third-party interaction to be in the current
// navigation when applying the Redirect heuristic.
COMPONENT_EXPORT(CONTENT_SETTINGS_FEATURES)
extern const base::FeatureParam<bool>
kTpcdRedirectHeuristicRequireCurrentInteraction;
COMPONENT_EXPORT(CONTENT_SETTINGS_FEATURES)
extern const char kTpcdPopupHeuristicEnableForIframeInitiatorName[];
COMPONENT_EXPORT(CONTENT_SETTINGS_FEATURES)
extern const char kTpcdWritePopupCurrentInteractionHeuristicsGrantsName[];
COMPONENT_EXPORT(CONTENT_SETTINGS_FEATURES)
extern const char kTpcdWritePopupPastInteractionHeuristicsGrantsName[];
COMPONENT_EXPORT(CONTENT_SETTINGS_FEATURES)
extern const char kTpcdBackfillPopupHeuristicsGrantsName[];
COMPONENT_EXPORT(CONTENT_SETTINGS_FEATURES)
extern const char kTpcdPopupHeuristicDisableForAdTaggedPopupsName[];
enum class EnableForIframeTypes { kNone = 0, kFirstParty = 1, kAll = 2 };
// Whether to enable writing Popup heuristic grants when the popup is opened via
// an iframe initiator.
// * kNone: Ignore popups initiated from iframes.
// * kFirstPartyIframes: Only write grants for popups initiated from 1P iframes,
// or nested tree of all 1P iframes.
// * kAllIframes: Write grants for popups initiated from any frame.
constexpr base::FeatureParam<EnableForIframeTypes>::Option
kEnableForIframeTypesOptions[] = {
{EnableForIframeTypes::kNone, "none"},
{EnableForIframeTypes::kFirstParty, "first-party"},
{EnableForIframeTypes::kAll, "all"},
};
COMPONENT_EXPORT(CONTENT_SETTINGS_FEATURES)
extern const base::FeatureParam<EnableForIframeTypes>
kTpcdPopupHeuristicEnableForIframeInitiator;
// The duration of the storage access grant created when observing the Popup
// With Current Interaction scenario. If set to zero duration, do not create a
// grant.
COMPONENT_EXPORT(CONTENT_SETTINGS_FEATURES)
extern const base::FeatureParam<base::TimeDelta>
kTpcdWritePopupCurrentInteractionHeuristicsGrants;
// The duration of the storage access grant created when observing the Popup
// With Past Interaction scenario. If set to zero duration, do not create a
// grant.
COMPONENT_EXPORT(CONTENT_SETTINGS_FEATURES)
extern const base::FeatureParam<base::TimeDelta>
kTpcdWritePopupPastInteractionHeuristicsGrants;
// The lookback and duration of the storage access grants created when
// backfilling the Popup With Current Interaction scenario on onboarding to
// 3PCD. If set to zero duration, to not create backfill grants.
COMPONENT_EXPORT(CONTENT_SETTINGS_FEATURES)
extern const base::FeatureParam<base::TimeDelta>
kTpcdBackfillPopupHeuristicsGrants;
// Whether to disable writing Popup heuristic grants when the popup is opened
// via an ad-tagged frame.
COMPONENT_EXPORT(CONTENT_SETTINGS_FEATURES)
extern const base::FeatureParam<bool>
kTpcdPopupHeuristicDisableForAdTaggedPopups;
//////////////////////////////////////////////////////////
// End of third-party cookie access heuristics features //
//////////////////////////////////////////////////////////
// Whether we should partition content settings (by StoragePartitions for
// non-ios platforms).
COMPONENT_EXPORT(CONTENT_SETTINGS_FEATURES)

@ -3,6 +3,7 @@
// found in the LICENSE file.
#include "components/ukm/content/source_url_recorder.h"
#include "base/test/scoped_feature_list.h"
#include "components/ukm/test_ukm_recorder.h"
#include "content/public/browser/web_contents.h"
@ -11,6 +12,7 @@
#include "services/metrics/public/cpp/ukm_recorder.h"
#include "services/metrics/public/cpp/ukm_source.h"
#include "services/metrics/public/cpp/ukm_source_id.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/metrics_proto/ukm/source.pb.h"
#include "url/gurl.h"
@ -61,26 +63,33 @@ TEST_F(SourceUrlRecorderWebContentsObserverTest, InitialUrl) {
const auto& sources = test_ukm_recorder_.GetSources();
// Expect two sources, one for navigation, one for document.
EXPECT_EQ(2ul, sources.size());
// Expect three sources, one each for navigation, redirect, and document.
EXPECT_EQ(3ul, sources.size());
size_t navigation_type_source_count = 0;
size_t redirect_type_source_count = 0;
size_t document_type_source_count = 0;
for (const auto& kv : sources) {
if (ukm::GetSourceIdType(kv.first) ==
ukm::SourceIdObj::Type::NAVIGATION_ID) {
// The navigation source has both URLs.
EXPECT_EQ(initial_url, kv.second->urls().front());
EXPECT_EQ(final_url, kv.second->urls().back());
EXPECT_THAT(kv.second->urls(),
testing::ElementsAre(initial_url, final_url));
navigation_type_source_count++;
}
if (ukm::GetSourceIdType(kv.first) == ukm::SourceIdObj::Type::REDIRECT_ID) {
// The redirect source has the initial URL which the navigation requested.
EXPECT_THAT(kv.second->urls(), testing::ElementsAre(initial_url));
redirect_type_source_count++;
}
if (ukm::GetSourceIdType(kv.first) == ukm::SourceIdObj::Type::DEFAULT) {
// The document source has the final URL which is one set on the
// committed document.
EXPECT_EQ(final_url, kv.second->urls().front());
EXPECT_THAT(kv.second->urls(), testing::ElementsAre(final_url));
document_type_source_count++;
}
}
EXPECT_EQ(1u, navigation_type_source_count);
EXPECT_EQ(1u, redirect_type_source_count);
EXPECT_EQ(1u, document_type_source_count);
}

@ -27,6 +27,7 @@ include_rules = [
"+components/app_launch_prefetch",
"+components/attribution_reporting",
"+components/browsing_topics/common",
"+components/content_settings/core/common",
"+components/input",
"+components/memory_pressure",
# components/performance_manager sits between chrome/ and content/, except for

@ -955,6 +955,26 @@ source_set("browser") {
"devtools/worker_or_worklet_devtools_agent_host.h",
"devtools/worklet_devtools_agent_host.cc",
"devtools/worklet_devtools_agent_host.h",
"dips/cookie_access_filter.cc",
"dips/cookie_access_filter.h",
"dips/dips_bounce_detector.cc",
"dips/dips_bounce_detector.h",
"dips/dips_database.cc",
"dips/dips_database.h",
"dips/dips_database_migrator.cc",
"dips/dips_database_migrator.h",
"dips/dips_navigation_flow_detector.cc",
"dips/dips_navigation_flow_detector.h",
"dips/dips_service_impl.cc",
"dips/dips_service_impl.h",
"dips/dips_state.cc",
"dips/dips_state.h",
"dips/dips_storage.cc",
"dips/dips_storage.h",
"dips/dips_utils.cc",
"dips/dips_utils.h",
"dips/persistent_repeating_timer.cc",
"dips/persistent_repeating_timer.h",
"display_cutout/display_cutout_constants.h",
"display_cutout/display_cutout_host_impl.cc",
"display_cutout/display_cutout_host_impl.h",
@ -2195,6 +2215,14 @@ source_set("browser") {
"synthetic_trial_syncer.cc",
"theme_helper.cc",
"theme_helper.h",
"tpcd_heuristics/opener_heuristic_metrics.cc",
"tpcd_heuristics/opener_heuristic_metrics.h",
"tpcd_heuristics/opener_heuristic_tab_helper.cc",
"tpcd_heuristics/opener_heuristic_tab_helper.h",
"tpcd_heuristics/opener_heuristic_utils.cc",
"tpcd_heuristics/opener_heuristic_utils.h",
"tpcd_heuristics/redirect_heuristic_tab_helper.cc",
"tpcd_heuristics/redirect_heuristic_tab_helper.h",
"tracing/background_tracing_agent_client_impl.cc",
"tracing/background_tracing_agent_client_impl.h",
"tracing/background_tracing_manager_impl.cc",
@ -3594,7 +3622,9 @@ if (is_android) {
# See comment at the top of //content/BUILD.gn for how this works.
group("for_content_tests") {
visibility = [
"//content/browser/dips:unit_tests",
"//content/browser/indexed_db:unit_tests",
"//content/browser/tpcd_heuristics:unit_tests",
"//content/public/test/android/*",
"//content/test/*",
"//content/web_test:web_test_browser",

@ -353,6 +353,11 @@ ResourceContext* BrowserContext::GetResourceContext() const {
return impl()->GetResourceContext();
}
void BrowserContext::BackfillPopupHeuristicGrants(
base::OnceCallback<void(bool)> callback) {
return impl_->BackfillPopupHeuristicGrants(std::move(callback));
}
base::WeakPtr<BrowserContext> BrowserContext::GetWeakPtr() {
return weak_factory_.GetWeakPtr();
}

@ -6,12 +6,18 @@
#include <utility>
#include "base/check_is_test.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/location.h"
#include "base/memory/ref_counted.h"
#include "base/task/sequenced_task_runner.h"
#include "base/trace_event/trace_event.h"
#include "build/build_config.h"
#include "components/content_settings/core/common/features.h"
#include "content/browser/background_sync/background_sync_scheduler.h"
#include "content/browser/browsing_data/browsing_data_remover_impl.h"
#include "content/browser/dips/dips_service_impl.h"
#include "content/browser/download/download_manager_impl.h"
#include "content/browser/in_memory_federated_permission_context.h"
#include "content/browser/permissions/permission_controller_impl.h"
@ -25,8 +31,11 @@
#include "content/browser/storage_partition_impl_map.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/browser/dips_delegate.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/shared_worker_service.h"
#include "content/public/common/content_client.h"
#include "media/capabilities/webrtc_video_stats_db_impl.h"
#include "media/learning/common/media_learning_tasks.h"
#include "media/learning/impl/learning_session_impl.h"
@ -68,10 +77,61 @@ BrowserContextImpl* BrowserContextImpl::From(BrowserContext* self) {
return self->impl();
}
void BrowserContextImpl::MaybeCleanupDips() {
base::ScopedClosureRunner quit_runner(dips_cleanup_loop_.QuitClosure());
// Don't attempt to delete the database if the DIPS feature is enabled; we
// need it.
if (base::FeatureList::IsEnabled(features::kDIPS)) {
return;
}
// Don't attempt to delete the database if this browser context should never
// have DIPS enabled. (This is important for embedders like ChromeOS, which
// have internal non-user-facing browser contexts. We don't want to touch
// them.)
if (dips_delegate_ && !dips_delegate_->ShouldEnableDips(self_)) {
return;
}
// Don't attempt to delete the database if this browser context doesn't write
// to disk. (This is important for embedders like Chrome, which can make OTR
// browser contexts share the same data directory as a non-OTR context.)
if (self_->IsOffTheRecord()) {
return;
}
DIPSStorage::DeleteDatabaseFiles(GetDIPSFilePath(self_),
quit_runner.Release());
}
void BrowserContextImpl::WaitForDipsCleanupForTesting() {
dips_cleanup_loop_.Run();
}
BrowserContextImpl::BrowserContextImpl(BrowserContext* self) : self_(self) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
background_sync_scheduler_ = base::MakeRefCounted<BackgroundSyncScheduler>();
// TODO: crbug.com/382509288 - don't allow null clients here, even in tests.
if (GetContentClient()) {
if (GetContentClient()->browser()) {
dips_delegate_ = GetContentClient()->browser()->CreateDipsDelegate();
} else {
CHECK_IS_TEST() << "Attempted to create BrowserContext without a "
"ContentBrowserClient";
}
} else {
CHECK_IS_TEST()
<< "Attempted to create BrowserContext without a ContentClient";
}
// Run MaybeCleanupDips() very soon. We can't call it right now because it
// calls a virtual function (BrowserContext::IsOffTheRecord()), which causes
// undefined behavior since we're called by the BrowserContext constructor
// and the method is not implemented by that class.
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(&BrowserContextImpl::MaybeCleanupDips,
weak_factory_.GetWeakPtr()));
}
BrowserContextImpl::~BrowserContextImpl() {
@ -245,6 +305,11 @@ void BrowserContextImpl::ShutdownStoragePartitions() {
background_sync_scheduler_.reset();
storage_partition_map_.reset();
// Delete the DIPSService, causing its SQLite database file to be closed. This
// is necessary for TestBrowserContext to be able to delete its temporary
// directory.
dips_service_.reset();
}
DownloadManager* BrowserContextImpl::GetDownloadManager() {
@ -346,4 +411,89 @@ void BrowserContextImpl::WriteIntoTrace(
proto->set_id(UniqueId());
}
namespace {
bool ShouldEnableDips(BrowserContext* browser_context,
DipsDelegate* dips_delegate) {
if (!base::FeatureList::IsEnabled(features::kDIPS)) {
return false;
}
if (dips_delegate && !dips_delegate->ShouldEnableDips(browser_context)) {
return false;
}
return true;
}
} // namespace
DIPSServiceImpl* BrowserContextImpl::GetDipsService() {
if (!dips_service_) {
if (!ShouldEnableDips(self_, dips_delegate_.get())) {
return nullptr;
}
dips_service_ = std::make_unique<DIPSServiceImpl>(
base::PassKey<BrowserContextImpl>(), self_);
if (dips_delegate_) {
dips_delegate_->OnDipsServiceCreated(self_, dips_service_.get());
}
}
return dips_service_.get();
}
namespace {
void CreatePopupHeuristicGrants(base::WeakPtr<BrowserContext> browser_context,
base::OnceCallback<void(bool)> callback,
std::vector<PopupWithTime> recent_popups) {
if (!browser_context) {
std::move(callback).Run(false);
return;
}
for (const PopupWithTime& popup : recent_popups) {
base::TimeDelta grant_duration =
content_settings::features::kTpcdBackfillPopupHeuristicsGrants.Get() -
(base::Time::Now() - popup.last_popup_time);
if (!grant_duration.is_positive()) {
continue;
}
// `popup_site` and `opener_site` were read from the DIPS database,
// and were originally computed by calling GetSiteForDIPS().
// GrantCookieAccessDueToHeuristic() takes SchemefulSites, so we create some
// here, but since we pass ignore_schemes=true the scheme doesn't matter
// (and port never matters for SchemefulSites), so we hardcode http and 80.
net::SchemefulSite popup_site(
url::Origin::CreateFromNormalizedTuple("http", popup.popup_site, 80));
net::SchemefulSite opener_site(
url::Origin::CreateFromNormalizedTuple("http", popup.opener_site, 80));
GetContentClient()->browser()->GrantCookieAccessDueToHeuristic(
browser_context.get(), opener_site, popup_site, grant_duration,
/*ignore_schemes=*/true);
}
std::move(callback).Run(true);
}
} // namespace
void BrowserContextImpl::BackfillPopupHeuristicGrants(
base::OnceCallback<void(bool)> callback) {
if (!base::FeatureList::IsEnabled(
content_settings::features::kTpcdHeuristicsGrants) ||
!content_settings::features::kTpcdBackfillPopupHeuristicsGrants.Get()
.is_positive()) {
std::move(callback).Run(false);
return;
}
// TODO: crbug.com/1502264 - ensure backfill is completed if Chrome is
// shutdown or crashes.
GetDipsService()
->storage()
->AsyncCall(&DIPSStorage::ReadRecentPopupsWithInteraction)
.WithArgs(
content_settings::features::kTpcdBackfillPopupHeuristicsGrants.Get())
.Then(base::BindOnce(&CreatePopupHeuristicGrants, self_->GetWeakPtr(),
std::move(callback)));
}
} // namespace content

@ -10,8 +10,12 @@
#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/run_loop.h"
#include "build/build_config.h"
#include "content/browser/dips/dips_service_impl.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/dips_delegate.h"
#include "content/public/browser/resource_context.h"
#include "content/public/browser/shared_cors_origin_access_list.h"
@ -114,6 +118,17 @@ class CONTENT_EXPORT BrowserContextImpl {
return resource_context_.get();
}
DIPSServiceImpl* GetDipsService();
// If the DIPS database file should be deleted, wait for it. Otherwise, return
// immediately.
//
// TODO: crbug.com/356624038 - delete this method when the DIPS feature flag
// is removed.
void WaitForDipsCleanupForTesting();
// (See BrowserContext::BackfillPopupHeuristicGrants().)
void BackfillPopupHeuristicGrants(base::OnceCallback<void(bool)> callback);
private:
// Creates the media service for storing/retrieving WebRTC encoding and
// decoding performance stats. Exposed here rather than StoragePartition
@ -121,6 +136,13 @@ class CONTENT_EXPORT BrowserContextImpl {
// exposed to the web directly, so privacy is not compromised.
std::unique_ptr<media::WebrtcVideoPerfHistory> CreateWebrtcVideoPerfHistory();
// Delete any existing DIPS database file if DIPS is disabled (because it's
// not possible for the user to clear it through the browser UI).
//
// TODO: crbug.com/356624038 - delete this method when the DIPS feature flag
// is removed.
void MaybeCleanupDips();
// BrowserContextImpl is owned and build from BrowserContext constructor.
// TODO(crbug.com/40169693): Invert the dependency. Make BrowserContext
// a pure interface and BrowserContextImpl implements it. Remove the `self_`
@ -149,6 +171,18 @@ class CONTENT_EXPORT BrowserContextImpl {
std::unique_ptr<media::VideoDecodePerfHistory> video_decode_perf_history_;
std::unique_ptr<media::WebrtcVideoPerfHistory> webrtc_video_perf_history_;
// A cached instance of the DIPS delegate created by the browser client.
std::unique_ptr<DipsDelegate> dips_delegate_;
// Manages DIPS for all WebContentses using this browser context.
std::unique_ptr<DIPSServiceImpl> dips_service_;
// If DIPS is disabled, any existing database file is asynchronously deleted
// when the BrowserContextImpl is created. This RunLoop allows tests to wait
// for the deletion to complete.
//
// TODO: crbug.com/356624038 - delete this when the DIPS feature flag is
// removed.
base::RunLoop dips_cleanup_loop_;
// TODO(crbug.com/40604019): Get rid of ResourceContext.
// Created on the UI thread, otherwise lives on and is destroyed on the IO
// thread.
@ -158,6 +192,10 @@ class CONTENT_EXPORT BrowserContextImpl {
#if BUILDFLAG(IS_CHROMEOS)
scoped_refptr<storage::ExternalMountPoints> external_mount_points_;
#endif
// TODO: crbug.com/40169693 - BrowserContext and BrowserContextImpl both have
// WeakPtrFactories. Remove one once the inheritance is sorted out.
base::WeakPtrFactory<BrowserContextImpl> weak_factory_{this};
};
} // namespace content

@ -29,6 +29,8 @@
#include "base/trace_event/trace_event.h"
#include "components/browsing_data/core/cookie_or_cache_deletion_choice.h"
#include "content/browser/browsing_data/browsing_data_filter_builder_impl.h"
#include "content/browser/dips/dips_service_impl.h"
#include "content/browser/dips/dips_utils.h"
#include "content/browser/renderer_host/render_frame_host_impl.h"
#include "content/browser/renderer_host/render_process_host_impl.h"
#include "content/browser/web_contents/web_contents_impl.h"
@ -40,9 +42,11 @@
#include "content/public/browser/browsing_data_remover_delegate.h"
#include "content/public/browser/client_hints_controller_delegate.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/browser/dips_delegate.h"
#include "content/public/browser/download_manager.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/browser/storage_partition_config.h"
#include "content/public/common/content_client.h"
#include "mojo/public/cpp/bindings/callback_helpers.h"
#include "services/network/public/cpp/features.h"
#include "services/network/public/mojom/clear_data_filter.mojom.h"
@ -124,6 +128,7 @@ BrowsingDataRemoverImpl::BrowsingDataRemoverImpl(
storage_partition_config_(std::nullopt),
is_removing_(false) {
DCHECK(browser_context_);
dips_delegate_ = GetContentClient()->browser()->CreateDipsDelegate();
}
BrowsingDataRemoverImpl::~BrowsingDataRemoverImpl() {
@ -678,6 +683,31 @@ void BrowsingDataRemoverImpl::RemoveImpl(
}
}
// Different types of DIPS events are cleared for DATA_TYPE_HISTORY and
// DATA_TYPE_COOKIES.
DIPSEventRemovalType dips_mask = DIPSEventRemovalType::kNone;
if ((remove_mask & DATA_TYPE_COOKIES) &&
!filter_builder->PartitionedCookiesOnly()) {
// If there's no delegate, delete everything whenever the user is deleting
// cookies.
dips_mask |= dips_delegate_ ? DIPSEventRemovalType::kStorage
: DIPSEventRemovalType::kAll;
}
// If there's a delegate, ask it whether to delete DIPS history.
if (dips_delegate_ &&
dips_delegate_->ShouldDeleteInteractionRecords(remove_mask)) {
dips_mask |= DIPSEventRemovalType::kHistory;
}
if (dips_mask != DIPSEventRemovalType::kNone) {
if (DIPSServiceImpl* dips_service =
DIPSServiceImpl::Get(browser_context_)) {
dips_service->RemoveEvents(delete_begin_, delete_end_,
filter_builder->BuildNetworkServiceFilter(),
dips_mask);
}
}
//////////////////////////////////////////////////////////////////////////////
// Embedder data.
if (embedder_delegate_) {

@ -8,6 +8,7 @@
#include <stdint.h>
#include <map>
#include <memory>
#include <optional>
#include <set>
@ -31,6 +32,7 @@ namespace content {
class BrowserContext;
class BrowsingDataFilterBuilder;
class DipsDelegate;
class StoragePartition;
class CONTENT_EXPORT BrowsingDataRemoverImpl
@ -245,6 +247,8 @@ class CONTENT_EXPORT BrowsingDataRemoverImpl
// A delegate to delete the embedder-specific data. Owned by the embedder.
raw_ptr<BrowsingDataRemoverDelegate, DanglingUntriaged> embedder_delegate_;
std::unique_ptr<DipsDelegate> dips_delegate_;
// Start time to delete from.
base::Time delete_begin_;

@ -29,9 +29,16 @@
#include "base/strings/utf_string_conversions.h"
#include "base/task/cancelable_task_tracker.h"
#include "base/task/single_thread_task_runner.h"
#include "base/test/bind.h"
#include "base/test/gmock_callback_support.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/test_future.h"
#include "base/threading/sequence_bound.h"
#include "base/time/time.h"
#include "content/browser/dips/dips_service_impl.h"
#include "content/browser/dips/dips_storage.h"
#include "content/browser/dips/dips_test_utils.h"
#include "content/browser/dips/dips_utils.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
@ -43,6 +50,7 @@
#include "content/public/browser/storage_partition.h"
#include "content/public/browser/storage_partition_config.h"
#include "content/public/browser/storage_usage_info.h"
#include "content/public/common/content_client.h"
#include "content/public/test/browser_task_environment.h"
#include "content/public/test/browsing_data_remover_test_util.h"
#include "content/public/test/mock_download_manager.h"
@ -366,7 +374,15 @@ class RemoveDownloadsTester {
class BrowsingDataRemoverImplTest : public testing::Test {
public:
BrowsingDataRemoverImplTest() : browser_context_(new TestBrowserContext()) {
BrowsingDataRemoverImplTest() : BrowsingDataRemoverImplTest(nullptr) {}
explicit BrowsingDataRemoverImplTest(
std::unique_ptr<ContentBrowserClient> browser_client)
: browser_client_(std::move(browser_client)) {
if (browser_client_) {
browser_client_setting_.emplace(browser_client_.get());
}
browser_context_ = std::make_unique<TestBrowserContext>();
remover_ = static_cast<BrowsingDataRemoverImpl*>(
browser_context_->GetBrowsingDataRemover());
@ -495,6 +511,8 @@ class BrowsingDataRemoverImplTest : public testing::Test {
}
private:
std::unique_ptr<ContentBrowserClient> browser_client_;
std::optional<ScopedContentBrowserClientSetting> browser_client_setting_;
std::unique_ptr<StoragePartitionRemovalTestStoragePartition>
storage_partition_;
@ -2114,4 +2132,168 @@ TEST_F(BrowsingDataRemoverImplSharedStorageTest,
mock_policy()));
}
class RemoveDIPSEventsTester {
public:
explicit RemoveDIPSEventsTester(BrowserContext* browser_context) {
storage_ = DIPSServiceImpl::Get(browser_context)->storage();
}
void WriteEventTimes(GURL url,
std::optional<base::Time> storage_time,
std::optional<base::Time> interaction_time) {
if (storage_time.has_value()) {
storage_->AsyncCall(&DIPSStorage::RecordStorage)
.WithArgs(url, storage_time.value(), DIPSCookieMode::kBlock3PC);
}
if (interaction_time.has_value()) {
storage_->AsyncCall(&DIPSStorage::RecordInteraction)
.WithArgs(url, interaction_time.value(), DIPSCookieMode::kBlock3PC);
}
storage_->FlushPostedTasksForTesting();
}
std::optional<StateValue> ReadStateValue(GURL url) {
base::test::TestFuture<DIPSState> dips_state;
storage_->AsyncCall(&DIPSStorage::Read)
.WithArgs(url)
.Then(dips_state.GetCallback());
const DIPSState& state = dips_state.Get();
if (!state.was_loaded()) {
return {};
}
return state.ToStateValue();
}
private:
raw_ptr<base::SequenceBound<DIPSStorage>> storage_;
};
class BrowsingDataRemoverImplDipsTest : public BrowsingDataRemoverImplTest {
public:
BrowsingDataRemoverImplDipsTest()
: BrowsingDataRemoverImplTest(
std::make_unique<TpcBlockingBrowserClient>()) {}
};
TEST_F(BrowsingDataRemoverImplDipsTest, RemoveDIPSEventsForLastHour) {
RemoveDIPSEventsTester tester(GetBrowserContext());
GURL url1("https://example1.com");
GURL url2("https://example2.com");
base::Time two_hours_ago = base::Time::Now() - base::Hours(2);
tester.WriteEventTimes(url1, /*storage_time=*/base::Time::Now(),
/*interaction_time=*/std::nullopt);
tester.WriteEventTimes(url2, /*storage_time=*/std::nullopt,
/*interaction_time=*/two_hours_ago);
{
std::optional<StateValue> state_val1 = tester.ReadStateValue(url1);
std::optional<StateValue> state_val2 = tester.ReadStateValue(url2);
ASSERT_TRUE(state_val1.has_value());
EXPECT_TRUE(state_val1->site_storage_times.has_value());
ASSERT_TRUE(state_val2.has_value());
EXPECT_TRUE(state_val2->user_interaction_times.has_value());
}
uint64_t remove_mask = TpcBlockingBrowserClient::DATA_TYPE_HISTORY |
content::BrowsingDataRemover::DATA_TYPE_COOKIES;
BlockUntilBrowsingDataRemoved(AnHourAgo(), base::Time::Max(), remove_mask,
false);
{
std::optional<StateValue> state_val1 = tester.ReadStateValue(url1);
std::optional<StateValue> state_val2 = tester.ReadStateValue(url2);
EXPECT_FALSE(state_val1.has_value());
ASSERT_TRUE(state_val2.has_value());
EXPECT_TRUE(state_val2->user_interaction_times.has_value());
}
BlockUntilBrowsingDataRemoved(base::Time(), base::Time::Max(), remove_mask,
false);
{
std::optional<StateValue> state_val1 = tester.ReadStateValue(url1);
std::optional<StateValue> state_val2 = tester.ReadStateValue(url2);
EXPECT_FALSE(state_val1.has_value());
EXPECT_FALSE(state_val2.has_value());
}
}
TEST_F(BrowsingDataRemoverImplDipsTest, RemoveDIPSEventsByType) {
RemoveDIPSEventsTester tester(GetBrowserContext());
GURL url1("https://example1.com");
GURL url2("https://example2.com");
GURL url3("https://example3.com");
base::Time two_hours_ago = base::Time::Now() - base::Hours(2);
tester.WriteEventTimes(url1, /*storage_time=*/base::Time::Now(),
/*interaction_time=*/std::nullopt);
tester.WriteEventTimes(url2, /*storage_time=*/std::nullopt,
/*interaction_time=*/base::Time::Now());
tester.WriteEventTimes(url3, /*storage_time=*/base::Time::Now(),
/*interaction_time=*/two_hours_ago);
{
std::optional<StateValue> state_val1 = tester.ReadStateValue(url1);
std::optional<StateValue> state_val2 = tester.ReadStateValue(url2);
std::optional<StateValue> state_val3 = tester.ReadStateValue(url3);
ASSERT_TRUE(state_val1.has_value());
EXPECT_TRUE(state_val1->site_storage_times.has_value());
ASSERT_TRUE(state_val2.has_value());
EXPECT_TRUE(state_val2->user_interaction_times.has_value());
ASSERT_TRUE(state_val3.has_value());
EXPECT_TRUE(state_val3->site_storage_times.has_value());
EXPECT_TRUE(state_val3->user_interaction_times.has_value());
}
// Remove interaction events from DIPS Storage.
BlockUntilBrowsingDataRemoved(AnHourAgo(), base::Time::Max(),
TpcBlockingBrowserClient::DATA_TYPE_HISTORY,
false);
// Verify the interaction event for url2 has been removed.
{
std::optional<StateValue> state_val1 = tester.ReadStateValue(url1);
std::optional<StateValue> state_val2 = tester.ReadStateValue(url2);
std::optional<StateValue> state_val3 = tester.ReadStateValue(url3);
ASSERT_TRUE(state_val1.has_value());
EXPECT_TRUE(state_val1->site_storage_times.has_value());
EXPECT_FALSE(state_val2.has_value());
ASSERT_TRUE(state_val3.has_value());
EXPECT_TRUE(state_val3->site_storage_times.has_value());
EXPECT_TRUE(state_val3->user_interaction_times.has_value());
}
// Remove storage events from DIPS Storage.
BlockUntilBrowsingDataRemoved(AnHourAgo(), base::Time::Max(),
content::BrowsingDataRemover::DATA_TYPE_COOKIES,
false);
// Verify the storage events for url1 and url3 have been removed.
{
std::optional<StateValue> state_val1 = tester.ReadStateValue(url1);
std::optional<StateValue> state_val2 = tester.ReadStateValue(url2);
std::optional<StateValue> state_val3 = tester.ReadStateValue(url3);
EXPECT_FALSE(state_val1.has_value());
EXPECT_FALSE(state_val2.has_value());
ASSERT_TRUE(state_val3.has_value());
EXPECT_FALSE(state_val3->site_storage_times.has_value());
EXPECT_TRUE(state_val3->user_interaction_times.has_value());
}
}
} // namespace content

@ -0,0 +1,53 @@
# Copyright 2024 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
source_set("unit_tests") {
testonly = true
# See content_unittests for justification.
if (is_component_build) {
check_includes = false
}
sources = [
"cookie_access_filter_unittest.cc",
"dips_bounce_detector_unittest.cc",
"dips_database_migrator_unittest.cc",
"dips_database_unittest.cc",
"dips_service_unittest.cc",
"dips_storage_unittest.cc",
"dips_utils_unittest.cc",
"persistent_repeating_timer_unittest.cc",
]
deps = [
":golden_dbs_bundle_data",
"//base",
"//base/test:test_support",
"//components/site_engagement/content",
"//components/ukm:test_support",
"//content/browser:for_content_tests",
"//content/test:browsertest_support",
"//content/test:test_support",
"//sql:test_support",
"//testing/gtest",
"//url",
]
data = [ "//content/test/data/dips/v1.sql" ]
}
bundle_data("golden_dbs_bundle_data") {
visibility = [ ":unit_tests" ]
testonly = true
sources = [
"//content/test/data/dips/v1.sql",
"//content/test/data/dips/v2.sql",
"//content/test/data/dips/v3.sql",
"//content/test/data/dips/v4.sql",
"//content/test/data/dips/v5.sql",
]
outputs = [ "{{bundle_resources_dir}}/" +
"{{source_root_relative_dir}}/{{source_file_part}}" ]
}

@ -0,0 +1,5 @@
specific_include_rules = {
"dips_bounce_detector_browsertest\.cc": [
"+device/fido",
],
}

@ -0,0 +1,7 @@
monorail: {
component: "Privacy>NavTracking"
}
team_email: "dc-komics@google.com"
buganizer_public: {
component_id: 1456144
}

@ -0,0 +1,7 @@
amaliev@chromium.org
bcl@chromium.org
jdh@chromium.org
njeunje@chromium.org
rtarpine@chromium.org
svend@chromium.org
wanderview@chromium.org

@ -0,0 +1,33 @@
## **DIPS (Detect/Delete Incidental Party State)**
This directory contains the code for Chromium's DIPS (Detect/Delete Incidental Party State) feature, known externally as Bounce Tracking Mitigations (BTM). DIPS aims to mitigate the privacy impact of "bounce tracking", a technique used to track users across websites without relying on third-party cookies.
**What is bounce tracking?**
Bounce tracking involves redirecting users through a tracker website, often without their knowledge or interaction. This allows the tracker to set or access first-party cookies, effectively circumventing third-party cookie restrictions and user privacy preferences.
**How does DIPS work?**
DIPS detects potential bounce tracking by analyzing website behavior, such as:
- Short dwell times on a website before redirecting.
- Programmatic redirects (as opposed to user-initiated ones).
- Writing to storage (cookies, etc.) before redirecting.
If DIPS determines that a website is likely involved in bounce tracking, and there's no indication of legitimate user interaction with the site, it automatically deletes the site's storage (eTLD+1).
**Goals of DIPS:**
- **Reduce cross-site tracking:*- Limit the ability of bounce trackers to identify and track users across different contexts.
- **Protect user privacy:*- Prevent bounce tracking from circumventing third-party cookie restrictions.
- **Maintain compatibility:*- Avoid disrupting legitimate use cases like federated logins and payment flows that rely on redirects.
- **Adaptability:*- Mitigate tracking by short-lived domains that may evade traditional blocklist-based approaches.
**Non-Goals:**
- **Replacing third-party cookie blocking:*- DIPS is primarily designed for environments where third-party cookies are already restricted.
- **Mitigating tracking by sites with significant first-party activity:*- DIPS focuses on incidental parties (sites without meaningful user interaction) and may not be effective against sites with substantial first-party engagement.
**Further Reading:**
- Explainer: https://github.com/privacycg/nav-tracking-mitigations/blob/main/bounce-tracking-explainer.md

@ -2,15 +2,15 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/dips/cookie_access_filter.h"
#include "content/browser/dips/cookie_access_filter.h"
#include "chrome/browser/dips/dips_utils.h"
#include "content/browser/dips/dips_utils.h"
CookieAccessFilter::CookieAccessFilter() = default;
CookieAccessFilter::~CookieAccessFilter() = default;
void CookieAccessFilter::AddAccess(const GURL& url, CookieOperation op) {
SiteDataAccessType t = ToSiteDataAccessType(op);
DIPSDataAccessType t = ToDIPSDataAccessType(op);
if (!accesses_.empty() && accesses_.back().url == url) {
// Coalesce accesses for the same URL. They may have come from separate
// visits, but we can't distinguish them from redundant calls, which are
@ -34,11 +34,11 @@ void CookieAccessFilter::AddAccess(const GURL& url, CookieOperation op) {
// multiple times, even consecutively, in a single redirect chain.
//
// To handle that corner case (imperfectly), if the same URL appears multiple
// times in a row, it will get the same SiteDataAccessType for all of them.
// times in a row, it will get the same DIPSDataAccessType for all of them.
bool CookieAccessFilter::Filter(const std::vector<GURL>& urls,
std::vector<SiteDataAccessType>* result) const {
std::vector<DIPSDataAccessType>* result) const {
result->clear();
result->resize(urls.size(), SiteDataAccessType::kNone);
result->resize(urls.size(), DIPSDataAccessType::kNone);
size_t url_idx = 0;
size_t access_idx = 0;
@ -82,6 +82,6 @@ bool CookieAccessFilter::Filter(const std::vector<GURL>& urls,
}
// Otherwise, fill the entire result vector with kUnknown and return false.
std::fill(result->begin(), result->end(), SiteDataAccessType::kUnknown);
std::fill(result->begin(), result->end(), DIPSDataAccessType::kUnknown);
return false;
}

@ -2,19 +2,20 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_DIPS_COOKIE_ACCESS_FILTER_H_
#define CHROME_BROWSER_DIPS_COOKIE_ACCESS_FILTER_H_
#ifndef CONTENT_BROWSER_DIPS_COOKIE_ACCESS_FILTER_H_
#define CONTENT_BROWSER_DIPS_COOKIE_ACCESS_FILTER_H_
#include <vector>
#include "chrome/browser/dips/dips_utils.h"
#include "content/browser/dips/dips_utils.h"
#include "content/common/content_export.h"
#include "url/gurl.h"
// Filters a chain of URLs to the ones which accessed cookies.
//
// Intended for use by a WebContentsObserver which overrides OnCookiesAccessed
// and DidFinishNavigation.
class CookieAccessFilter {
class CONTENT_EXPORT CookieAccessFilter {
public:
CookieAccessFilter();
~CookieAccessFilter();
@ -29,7 +30,7 @@ class CookieAccessFilter {
// kUnknown. (Note: this depends on the order of previous calls to
// AddAccess()).
bool Filter(const std::vector<GURL>& urls,
std::vector<SiteDataAccessType>* result) const;
std::vector<DIPSDataAccessType>* result) const;
// Returns true iff AddAccess() has never been called.
bool is_empty() const { return accesses_.empty(); }
@ -37,7 +38,7 @@ class CookieAccessFilter {
private:
struct CookieAccess {
GURL url;
SiteDataAccessType type = SiteDataAccessType::kUnknown;
DIPSDataAccessType type = DIPSDataAccessType::kUnknown;
};
// We use a vector rather than a set of URLs because order can matter. If the
@ -46,4 +47,4 @@ class CookieAccessFilter {
std::vector<CookieAccess> accesses_;
};
#endif // CHROME_BROWSER_DIPS_COOKIE_ACCESS_FILTER_H_
#endif // CONTENT_BROWSER_DIPS_COOKIE_ACCESS_FILTER_H_

@ -2,29 +2,29 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/dips/cookie_access_filter.h"
#include "content/browser/dips/cookie_access_filter.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
TEST(CookieAccessType, BitwiseOrOperator) {
ASSERT_EQ(SiteDataAccessType::kRead,
SiteDataAccessType::kNone | SiteDataAccessType::kRead);
ASSERT_EQ(DIPSDataAccessType::kRead,
DIPSDataAccessType::kNone | DIPSDataAccessType::kRead);
ASSERT_EQ(SiteDataAccessType::kWrite,
SiteDataAccessType::kNone | SiteDataAccessType::kWrite);
ASSERT_EQ(DIPSDataAccessType::kWrite,
DIPSDataAccessType::kNone | DIPSDataAccessType::kWrite);
ASSERT_EQ(SiteDataAccessType::kReadWrite,
SiteDataAccessType::kRead | SiteDataAccessType::kWrite);
ASSERT_EQ(DIPSDataAccessType::kReadWrite,
DIPSDataAccessType::kRead | DIPSDataAccessType::kWrite);
ASSERT_EQ(SiteDataAccessType::kUnknown,
SiteDataAccessType::kUnknown | SiteDataAccessType::kNone);
ASSERT_EQ(DIPSDataAccessType::kUnknown,
DIPSDataAccessType::kUnknown | DIPSDataAccessType::kNone);
ASSERT_EQ(SiteDataAccessType::kUnknown,
SiteDataAccessType::kUnknown | SiteDataAccessType::kRead);
ASSERT_EQ(DIPSDataAccessType::kUnknown,
DIPSDataAccessType::kUnknown | DIPSDataAccessType::kRead);
ASSERT_EQ(SiteDataAccessType::kUnknown,
SiteDataAccessType::kUnknown | SiteDataAccessType::kWrite);
ASSERT_EQ(DIPSDataAccessType::kUnknown,
DIPSDataAccessType::kUnknown | DIPSDataAccessType::kWrite);
}
TEST(CookieAccessFilter, NoAccesses) {
@ -32,10 +32,10 @@ TEST(CookieAccessFilter, NoAccesses) {
GURL url2("http://google.com");
CookieAccessFilter filter;
std::vector<SiteDataAccessType> result;
std::vector<DIPSDataAccessType> result;
ASSERT_TRUE(filter.Filter({url1, url2}, &result));
EXPECT_THAT(result, testing::ElementsAre(SiteDataAccessType::kNone,
SiteDataAccessType::kNone));
EXPECT_THAT(result, testing::ElementsAre(DIPSDataAccessType::kNone,
DIPSDataAccessType::kNone));
}
TEST(CookieAccessFilter, OneRead_Former) {
@ -44,10 +44,10 @@ TEST(CookieAccessFilter, OneRead_Former) {
CookieAccessFilter filter;
filter.AddAccess(url1, CookieOperation::kRead);
std::vector<SiteDataAccessType> result;
std::vector<DIPSDataAccessType> result;
ASSERT_TRUE(filter.Filter({url1, url2}, &result));
EXPECT_THAT(result, testing::ElementsAre(SiteDataAccessType::kRead,
SiteDataAccessType::kNone));
EXPECT_THAT(result, testing::ElementsAre(DIPSDataAccessType::kRead,
DIPSDataAccessType::kNone));
}
TEST(CookieAccessFilter, OneRead_Latter) {
@ -56,10 +56,10 @@ TEST(CookieAccessFilter, OneRead_Latter) {
CookieAccessFilter filter;
filter.AddAccess(url2, CookieOperation::kRead);
std::vector<SiteDataAccessType> result;
std::vector<DIPSDataAccessType> result;
ASSERT_TRUE(filter.Filter({url1, url2}, &result));
EXPECT_THAT(result, testing::ElementsAre(SiteDataAccessType::kNone,
SiteDataAccessType::kRead));
EXPECT_THAT(result, testing::ElementsAre(DIPSDataAccessType::kNone,
DIPSDataAccessType::kRead));
}
TEST(CookieAccessFilter, OneWrite) {
@ -68,10 +68,10 @@ TEST(CookieAccessFilter, OneWrite) {
CookieAccessFilter filter;
filter.AddAccess(url2, CookieOperation::kChange);
std::vector<SiteDataAccessType> result;
std::vector<DIPSDataAccessType> result;
ASSERT_TRUE(filter.Filter({url1, url2}, &result));
EXPECT_THAT(result, testing::ElementsAre(SiteDataAccessType::kNone,
SiteDataAccessType::kWrite));
EXPECT_THAT(result, testing::ElementsAre(DIPSDataAccessType::kNone,
DIPSDataAccessType::kWrite));
}
TEST(CookieAccessFilter, UnexpectedURL) {
@ -80,10 +80,10 @@ TEST(CookieAccessFilter, UnexpectedURL) {
CookieAccessFilter filter;
filter.AddAccess(GURL("http://other.com"), CookieOperation::kRead);
std::vector<SiteDataAccessType> result;
std::vector<DIPSDataAccessType> result;
ASSERT_FALSE(filter.Filter({url1, url2}, &result));
EXPECT_THAT(result, testing::ElementsAre(SiteDataAccessType::kUnknown,
SiteDataAccessType::kUnknown));
EXPECT_THAT(result, testing::ElementsAre(DIPSDataAccessType::kUnknown,
DIPSDataAccessType::kUnknown));
}
TEST(CookieAccessFilter, TwoReads) {
@ -93,10 +93,10 @@ TEST(CookieAccessFilter, TwoReads) {
filter.AddAccess(url1, CookieOperation::kRead);
filter.AddAccess(url2, CookieOperation::kRead);
std::vector<SiteDataAccessType> result;
std::vector<DIPSDataAccessType> result;
ASSERT_TRUE(filter.Filter({url1, url2}, &result));
EXPECT_THAT(result, testing::ElementsAre(SiteDataAccessType::kRead,
SiteDataAccessType::kRead));
EXPECT_THAT(result, testing::ElementsAre(DIPSDataAccessType::kRead,
DIPSDataAccessType::kRead));
}
TEST(CookieAccessFilter, CoalesceReadBeforeWrite) {
@ -107,10 +107,10 @@ TEST(CookieAccessFilter, CoalesceReadBeforeWrite) {
filter.AddAccess(url1, CookieOperation::kChange);
filter.AddAccess(url2, CookieOperation::kRead);
std::vector<SiteDataAccessType> result;
std::vector<DIPSDataAccessType> result;
ASSERT_TRUE(filter.Filter({url1, url2}, &result));
EXPECT_THAT(result, testing::ElementsAre(SiteDataAccessType::kReadWrite,
SiteDataAccessType::kRead));
EXPECT_THAT(result, testing::ElementsAre(DIPSDataAccessType::kReadWrite,
DIPSDataAccessType::kRead));
}
TEST(CookieAccessFilter, CoalesceReadBeforeWrite_Repeated) {
@ -121,11 +121,11 @@ TEST(CookieAccessFilter, CoalesceReadBeforeWrite_Repeated) {
filter.AddAccess(url1, CookieOperation::kChange);
filter.AddAccess(url2, CookieOperation::kRead);
std::vector<SiteDataAccessType> result;
std::vector<DIPSDataAccessType> result;
ASSERT_TRUE(filter.Filter({url1, url1, url2}, &result));
EXPECT_THAT(result, testing::ElementsAre(SiteDataAccessType::kReadWrite,
SiteDataAccessType::kReadWrite,
SiteDataAccessType::kRead));
EXPECT_THAT(result, testing::ElementsAre(DIPSDataAccessType::kReadWrite,
DIPSDataAccessType::kReadWrite,
DIPSDataAccessType::kRead));
}
TEST(CookieAccessFilter, CoalesceWrites) {
@ -136,10 +136,10 @@ TEST(CookieAccessFilter, CoalesceWrites) {
filter.AddAccess(url1, CookieOperation::kChange);
filter.AddAccess(url2, CookieOperation::kRead);
std::vector<SiteDataAccessType> result;
std::vector<DIPSDataAccessType> result;
ASSERT_TRUE(filter.Filter({url1, url2}, &result));
EXPECT_THAT(result, testing::ElementsAre(SiteDataAccessType::kWrite,
SiteDataAccessType::kRead));
EXPECT_THAT(result, testing::ElementsAre(DIPSDataAccessType::kWrite,
DIPSDataAccessType::kRead));
}
TEST(CookieAccessFilter, CoalesceWrites_Repeated) {
@ -150,11 +150,11 @@ TEST(CookieAccessFilter, CoalesceWrites_Repeated) {
filter.AddAccess(url1, CookieOperation::kChange);
filter.AddAccess(url2, CookieOperation::kRead);
std::vector<SiteDataAccessType> result;
std::vector<DIPSDataAccessType> result;
ASSERT_TRUE(filter.Filter({url1, url1, url2}, &result));
EXPECT_THAT(result, testing::ElementsAre(SiteDataAccessType::kWrite,
SiteDataAccessType::kWrite,
SiteDataAccessType::kRead));
EXPECT_THAT(result, testing::ElementsAre(DIPSDataAccessType::kWrite,
DIPSDataAccessType::kWrite,
DIPSDataAccessType::kRead));
}
TEST(CookieAccessFilter, CoalesceReads) {
@ -165,10 +165,10 @@ TEST(CookieAccessFilter, CoalesceReads) {
filter.AddAccess(url1, CookieOperation::kRead);
filter.AddAccess(url2, CookieOperation::kRead);
std::vector<SiteDataAccessType> result;
std::vector<DIPSDataAccessType> result;
ASSERT_TRUE(filter.Filter({url1, url2}, &result));
EXPECT_THAT(result, testing::ElementsAre(SiteDataAccessType::kRead,
SiteDataAccessType::kRead));
EXPECT_THAT(result, testing::ElementsAre(DIPSDataAccessType::kRead,
DIPSDataAccessType::kRead));
}
TEST(CookieAccessFilter, CoalesceReads_Repeated) {
@ -179,11 +179,11 @@ TEST(CookieAccessFilter, CoalesceReads_Repeated) {
filter.AddAccess(url1, CookieOperation::kRead);
filter.AddAccess(url2, CookieOperation::kRead);
std::vector<SiteDataAccessType> result;
std::vector<DIPSDataAccessType> result;
ASSERT_TRUE(filter.Filter({url1, url1, url2}, &result));
EXPECT_THAT(result, testing::ElementsAre(SiteDataAccessType::kRead,
SiteDataAccessType::kRead,
SiteDataAccessType::kRead));
EXPECT_THAT(result, testing::ElementsAre(DIPSDataAccessType::kRead,
DIPSDataAccessType::kRead,
DIPSDataAccessType::kRead));
}
TEST(CookieAccessFilter, CoalesceWriteBeforeRead) {
@ -194,10 +194,10 @@ TEST(CookieAccessFilter, CoalesceWriteBeforeRead) {
filter.AddAccess(url1, CookieOperation::kRead);
filter.AddAccess(url2, CookieOperation::kRead);
std::vector<SiteDataAccessType> result;
std::vector<DIPSDataAccessType> result;
ASSERT_TRUE(filter.Filter({url1, url2}, &result));
EXPECT_THAT(result, testing::ElementsAre(SiteDataAccessType::kReadWrite,
SiteDataAccessType::kRead));
EXPECT_THAT(result, testing::ElementsAre(DIPSDataAccessType::kReadWrite,
DIPSDataAccessType::kRead));
}
TEST(CookieAccessFilter, CoalesceWriteBeforeRead_Repeated) {
@ -208,11 +208,11 @@ TEST(CookieAccessFilter, CoalesceWriteBeforeRead_Repeated) {
filter.AddAccess(url1, CookieOperation::kRead);
filter.AddAccess(url2, CookieOperation::kRead);
std::vector<SiteDataAccessType> result;
std::vector<DIPSDataAccessType> result;
ASSERT_TRUE(filter.Filter({url1, url1, url2}, &result));
EXPECT_THAT(result, testing::ElementsAre(SiteDataAccessType::kReadWrite,
SiteDataAccessType::kReadWrite,
SiteDataAccessType::kRead));
EXPECT_THAT(result, testing::ElementsAre(DIPSDataAccessType::kReadWrite,
DIPSDataAccessType::kReadWrite,
DIPSDataAccessType::kRead));
}
TEST(CookieAccessFilter, SameURLTwiceWithDifferentAccessTypes) {
@ -224,9 +224,9 @@ TEST(CookieAccessFilter, SameURLTwiceWithDifferentAccessTypes) {
filter.AddAccess(url2, CookieOperation::kChange);
filter.AddAccess(url1, CookieOperation::kRead);
std::vector<SiteDataAccessType> result;
std::vector<DIPSDataAccessType> result;
ASSERT_TRUE(filter.Filter({url1, url2, url1}, &result));
EXPECT_THAT(result, testing::ElementsAre(SiteDataAccessType::kWrite,
SiteDataAccessType::kReadWrite,
SiteDataAccessType::kRead));
EXPECT_THAT(result, testing::ElementsAre(DIPSDataAccessType::kWrite,
DIPSDataAccessType::kReadWrite,
DIPSDataAccessType::kRead));
}

@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/dips/dips_bounce_detector.h"
#include "content/browser/dips/dips_bounce_detector.h"
#include <cmath>
#include <cstddef>
@ -25,14 +25,14 @@
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "base/types/optional_ref.h"
#include "chrome/browser/chrome_content_browser_client.h"
#include "chrome/browser/dips/cookie_access_filter.h"
#include "chrome/browser/dips/dips_redirect_info.h"
#include "chrome/browser/dips/dips_service_impl.h"
#include "chrome/browser/dips/dips_storage.h"
#include "chrome/browser/dips/dips_utils.h"
#include "content/browser/dips/cookie_access_filter.h"
#include "content/browser/dips/dips_service_impl.h"
#include "content/browser/dips/dips_storage.h"
#include "content/browser/dips/dips_utils.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/browser/cookie_access_details.h"
#include "content/public/browser/dips_redirect_info.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/page.h"
#include "content/public/browser/page_user_data.h"
@ -193,7 +193,7 @@ DIPSRedirectContext::~DIPSRedirectContext() = default;
void DIPSRedirectContext::AppendClientRedirect(
DIPSRedirectInfoPtr client_redirect) {
DCHECK_EQ(client_redirect->redirect_type, DIPSRedirectType::kClient);
if (client_redirect->access_type > SiteDataAccessType::kRead) {
if (client_redirect->access_type > DIPSDataAccessType::kRead) {
redirectors_.insert(client_redirect->site);
}
redirects_.push_back(std::move(client_redirect));
@ -204,7 +204,7 @@ void DIPSRedirectContext::AppendServerRedirects(
std::vector<DIPSRedirectInfoPtr> server_redirects) {
for (auto& redirect : server_redirects) {
DCHECK_EQ(redirect->redirect_type, DIPSRedirectType::kServer);
if (redirect->access_type > SiteDataAccessType::kRead) {
if (redirect->access_type > DIPSDataAccessType::kRead) {
redirectors_.insert(redirect->site);
}
redirects_.push_back(std::move(redirect));
@ -462,8 +462,7 @@ bool AddLateCookieAccess(const GURL& url,
for (size_t i = 1; i <= lookback; i++) {
const size_t offset = redirects.size() - i;
if (redirects[offset]->url.url == url) {
redirects[offset]->access_type =
redirects[offset]->access_type | ToSiteDataAccessType(op);
redirects[offset]->access_type |= ToDIPSDataAccessType(op);
// This cookie access might indicate a stateful bounce and ideally we'd
// report an issue to notify the user, but the navigation already
@ -570,16 +569,15 @@ void Populate3PcExceptions(content::BrowserContext* browser_context,
blink::StorageKey::CreateFirstParty(url::Origin::Create(initial_url));
const blink::StorageKey final_url_key =
blink::StorageKey::CreateFirstParty(url::Origin::Create(final_url));
// TODO: crbug.com/40883201 - When we move to //content, we will call
// IsFullCookieAccessAllowed() via ContentBrowserClient instead of as a
// standalone function.
content::ContentBrowserClient* browser_client =
content::GetContentClient()->browser();
for (DIPSRedirectInfoPtr& redirect : redirects) {
redirect->has_3pc_exception =
dips_move::IsFullCookieAccessAllowed(browser_context, web_contents,
redirect->url.url,
initial_url_key) ||
dips_move::IsFullCookieAccessAllowed(browser_context, web_contents,
redirect->url.url, final_url_key);
browser_client->IsFullCookieAccessAllowed(browser_context, web_contents,
redirect->url.url,
initial_url_key) ||
browser_client->IsFullCookieAccessAllowed(
browser_context, web_contents, redirect->url.url, final_url_key);
}
}
} // namespace dips
@ -715,7 +713,7 @@ void DIPSBounceDetector::DidStartNavigation(
// The delay between the previous navigation commit and the current
// client-redirect is only tracked for stateful bounces.
if (client_detection_state_->site_data_access_type >
SiteDataAccessType::kNone) {
DIPSDataAccessType::kNone) {
UmaHistogramTimeToBounce(client_bounce_delay);
}
@ -885,7 +883,7 @@ void DIPSBounceDetector::OnClientSiteDataAccessed(const GURL& url,
GetSiteForDIPS(url) == client_detection_state_->current_site) {
client_detection_state_->site_data_access_type =
client_detection_state_->site_data_access_type |
ToSiteDataAccessType(op);
ToDIPSDataAccessType(op);
if (op == CookieOperation::kChange) {
client_detection_state_->last_storage_time = now;
@ -1040,7 +1038,7 @@ void DIPSBounceDetector::DidFinishNavigation(
// Before we replace the ClientBounceDetectionState, keep a copy of whether
// the previous page accessed storage or not.
std::optional<SiteDataAccessType> prev_page_access_type;
std::optional<DIPSDataAccessType> prev_page_access_type;
if (client_detection_state_) {
prev_page_access_type = client_detection_state_->site_data_access_type;
}
@ -1072,7 +1070,7 @@ void DIPSBounceDetector::DidFinishNavigation(
}
std::vector<DIPSRedirectInfoPtr> redirects;
std::vector<SiteDataAccessType> access_types;
std::vector<DIPSDataAccessType> access_types;
server_state->filter.Filter(navigation_handle->GetRedirectChain(),
&access_types);

@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_DIPS_DIPS_BOUNCE_DETECTOR_H_
#define CHROME_BROWSER_DIPS_DIPS_BOUNCE_DETECTOR_H_
#ifndef CONTENT_BROWSER_DIPS_DIPS_BOUNCE_DETECTOR_H_
#define CONTENT_BROWSER_DIPS_DIPS_BOUNCE_DETECTOR_H_
#include <memory>
#include <string>
@ -19,13 +19,14 @@
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "base/types/optional_ref.h"
#include "chrome/browser/dips/cookie_access_filter.h"
#include "chrome/browser/dips/dips_redirect_info.h"
#include "chrome/browser/dips/dips_service_impl.h"
#include "chrome/browser/dips/dips_utils.h"
#include "content/browser/dips/cookie_access_filter.h"
#include "content/browser/dips/dips_service_impl.h"
#include "content/browser/dips/dips_utils.h"
#include "content/common/content_export.h"
#include "content/public/browser/allow_service_worker_result.h"
#include "content/public/browser/cookie_access_details.h"
#include "content/public/browser/dedicated_worker_service.h"
#include "content/public/browser/dips_redirect_info.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/navigation_handle_timing.h"
#include "content/public/browser/navigation_handle_user_data.h"
@ -49,6 +50,9 @@ namespace url {
class Origin;
}
using DIPSRedirectChainHandler =
base::RepeatingCallback<void(std::vector<DIPSRedirectInfoPtr>,
DIPSRedirectChainInfoPtr)>;
using DIPSIssueHandler =
base::RepeatingCallback<void(std::set<std::string> sites)>;
using DIPSIssueReportingCallback =
@ -72,7 +76,7 @@ class ClientBounceDetectionState {
std::optional<base::Time> last_activation_time;
std::optional<base::Time> last_storage_time;
std::optional<base::Time> last_successful_web_authn_assertion_time;
SiteDataAccessType site_data_access_type = SiteDataAccessType::kUnknown;
DIPSDataAccessType site_data_access_type = DIPSDataAccessType::kUnknown;
};
// Either the URL navigated away from (starting a new chain), or the client-side
@ -92,7 +96,7 @@ inline constexpr int kAllSitesFollowingFirstPartyLookbackLength = 10;
// A redirect-chain-in-progress. It grows by calls to Append() and restarts by
// calls to EndChain().
// TODO: crbug.com/324573484 - rename to remove association with DIPS.
class DIPSRedirectContext {
class CONTENT_EXPORT DIPSRedirectContext {
public:
DIPSRedirectContext(DIPSRedirectChainHandler handler,
DIPSIssueHandler issue_handler,
@ -199,7 +203,7 @@ class DIPSRedirectContext {
// A simplified interface to WebContents and DIPSServiceImpl that can be faked
// in tests. Needed to allow unit testing DIPSBounceDetector.
// TODO: crbug.com/324573484 - rename to remove association with DIPS.
class DIPSBounceDetectorDelegate {
class CONTENT_EXPORT DIPSBounceDetectorDelegate {
public:
virtual ~DIPSBounceDetectorDelegate();
virtual UrlAndSourceId GetLastCommittedURL() const = 0;
@ -214,7 +218,7 @@ class DIPSBounceDetectorDelegate {
// ServerBounceDetectionState gets attached to NavigationHandle (which is a
// SupportsUserData subclass) to store data needed to detect stateful
// server-side redirects.
class ServerBounceDetectionState
class CONTENT_EXPORT ServerBounceDetectionState
: public content::NavigationHandleUserData<ServerBounceDetectionState> {
public:
ServerBounceDetectionState();
@ -243,7 +247,7 @@ class ServerBounceDetectionState
// A simplified interface to content::NavigationHandle that can be faked in
// tests. Needed to allow unit testing DIPSBounceDetector.
// TODO: crbug.com/324573484 - rename to remove association with DIPS.
class DIPSNavigationHandle {
class CONTENT_EXPORT DIPSNavigationHandle {
public:
virtual ~DIPSNavigationHandle();
@ -281,7 +285,7 @@ class DIPSNavigationHandle {
// metrics and storing them in the DIPSDatabase).
// TODO: crbug.com/324573484 - rename this to avoid confusion with
// RedirectChainDetector and remove its association with DIPS.
class DIPSBounceDetector {
class CONTENT_EXPORT DIPSBounceDetector {
public:
explicit DIPSBounceDetector(DIPSBounceDetectorDelegate* delegate,
const base::TickClock* tick_clock,
@ -371,7 +375,7 @@ class DelayedChainHandler {
// Detects chains of server- and client redirects, and notifies observers.
// TODO: crbug.com/324573485 - move to separate file.
class RedirectChainDetector
class CONTENT_EXPORT RedirectChainDetector
: public content::WebContentsObserver,
public content::WebContentsUserData<RedirectChainDetector>,
public DIPSBounceDetectorDelegate {
@ -466,7 +470,7 @@ class RedirectChainDetector
// Populates the DIPS Database with site metadata, for the DIPS Service to
// recognize and delete the storage of sites that perform bounce tracking.
class DIPSWebContentsObserver
class CONTENT_EXPORT DIPSWebContentsObserver
: public content::WebContentsObserver,
public content::WebContentsUserData<DIPSWebContentsObserver>,
public content::SharedWorkerService::Observer,
@ -585,15 +589,17 @@ namespace dips {
ukm::SourceId GetInitialRedirectSourceId(
content::NavigationHandle* navigation_handle);
bool IsOrWasInPrimaryPage(content::RenderFrameHost* render_frame_host);
CONTENT_EXPORT bool IsOrWasInPrimaryPage(
content::RenderFrameHost* render_frame_host);
// Sets the `has_3pc_exception` field of each element of `redirects`.
void Populate3PcExceptions(content::BrowserContext* browser_context,
content::WebContents* web_contents,
const GURL& initial_url,
const GURL& final_url,
base::span<DIPSRedirectInfoPtr> redirects);
CONTENT_EXPORT void Populate3PcExceptions(
content::BrowserContext* browser_context,
content::WebContents* web_contents,
const GURL& initial_url,
const GURL& final_url,
base::span<DIPSRedirectInfoPtr> redirects);
} // namespace dips
#endif // CHROME_BROWSER_DIPS_DIPS_BOUNCE_DETECTOR_H_
#endif // CONTENT_BROWSER_DIPS_DIPS_BOUNCE_DETECTOR_H_

@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/dips/dips_bounce_detector.h"
#include "content/browser/dips/dips_bounce_detector.h"
#include <string_view>
#include <tuple>
@ -19,11 +19,11 @@
#include "base/test/task_environment.h"
#include "base/time/time.h"
#include "base/types/pass_key.h"
#include "chrome/browser/dips/dips_service_impl.h"
#include "chrome/browser/dips/dips_test_utils.h"
#include "chrome/browser/dips/dips_utils.h"
#include "components/content_settings/core/common/features.h"
#include "components/ukm/test_ukm_recorder.h"
#include "content/browser/dips/dips_service_impl.h"
#include "content/browser/dips/dips_test_utils.h"
#include "content/browser/dips/dips_utils.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/navigation_handle_timing.h"
#include "content/public/common/content_features.h"
@ -68,7 +68,7 @@ void AppendRedirect(std::vector<std::string>* redirects,
"[%zu/%zu] %s -> %s (%s) -> %s", redirect.chain_index.value() + 1,
chain.length, FormatURL(chain.initial_url.url).c_str(),
FormatURL(redirect.url.url).c_str(),
SiteDataAccessTypeToString(redirect.access_type).data(),
DIPSDataAccessTypeToString(redirect.access_type).data(),
FormatURL(chain.final_url.url).c_str()));
}
@ -96,7 +96,7 @@ class TestBounceDetectorDelegate : public DIPSBounceDetectorDelegate {
redirect->chain_id = chain->chain_id;
redirect->chain_index = redirect_index;
redirect->has_3pc_exception = false;
DCHECK(redirect->access_type != SiteDataAccessType::kUnknown);
DCHECK(redirect->access_type != DIPSDataAccessType::kUnknown);
AppendRedirect(&redirects_, *redirect, *chain);
DIPSServiceImpl::HandleRedirectForTesting(
@ -933,12 +933,12 @@ TEST_F(DIPSBounceDetectorTest, Histograms_UMA) {
histograms.GetAllSamples("Privacy.DIPS.BounceCategoryClient.Block3PC"),
testing::ElementsAre(
// b.test
Bucket((int)RedirectCategory::kReadCookies_HasEngagement, 1)));
Bucket((int)DIPSRedirectCategory::kReadCookies_HasEngagement, 1)));
EXPECT_THAT(
histograms.GetAllSamples("Privacy.DIPS.BounceCategoryServer.Block3PC"),
testing::ElementsAre(
// c.test
Bucket((int)RedirectCategory::kWriteCookies_NoEngagement, 1)));
Bucket((int)DIPSRedirectCategory::kWriteCookies_NoEngagement, 1)));
// Verify the time-to-bounce metric was recorded for the client bounce.
histograms.ExpectBucketCount(
@ -974,7 +974,7 @@ TEST_F(DIPSBounceDetectorTest, Histograms_UKM) {
EXPECT_THAT(
ukm_entries[0].metrics,
ElementsAre(Pair("ClientBounceDelay", 2),
Pair("CookieAccessType", (int)SiteDataAccessType::kRead),
Pair("CookieAccessType", (int)DIPSDataAccessType::kRead),
Pair("HasStickyActivation", false),
Pair("InitialAndFinalSitesSame", false),
Pair("RedirectAndFinalSiteSame", false),
@ -989,7 +989,7 @@ TEST_F(DIPSBounceDetectorTest, Histograms_UKM) {
EXPECT_THAT(
ukm_entries[1].metrics,
ElementsAre(Pair("ClientBounceDelay", 0),
Pair("CookieAccessType", (int)SiteDataAccessType::kWrite),
Pair("CookieAccessType", (int)DIPSDataAccessType::kWrite),
Pair("HasStickyActivation", false),
Pair("InitialAndFinalSitesSame", false),
Pair("RedirectAndFinalSiteSame", false),
@ -1086,7 +1086,7 @@ void AppendChainPair(std::vector<ChainPair>& vec,
std::vector<DIPSRedirectInfoPtr> MakeServerRedirects(
std::vector<std::string> urls,
SiteDataAccessType access_type = SiteDataAccessType::kReadWrite) {
DIPSDataAccessType access_type = DIPSDataAccessType::kReadWrite) {
std::vector<DIPSRedirectInfoPtr> redirects;
for (const auto& url : urls) {
redirects.push_back(std::make_unique<DIPSRedirectInfo>(
@ -1103,7 +1103,7 @@ std::vector<DIPSRedirectInfoPtr> MakeServerRedirects(
DIPSRedirectInfoPtr MakeClientRedirect(
std::string url,
SiteDataAccessType access_type = SiteDataAccessType::kReadWrite,
DIPSDataAccessType access_type = DIPSDataAccessType::kReadWrite,
bool has_sticky_activation = false,
bool has_web_authn_assertion = false) {
return std::make_unique<DIPSRedirectInfo>(
@ -1128,9 +1128,9 @@ MATCHER_P(HasRedirectType, redirect_type, "") {
result_listener);
}
MATCHER_P(HasSiteDataAccessType, access_type, "") {
MATCHER_P(HasDIPSDataAccessType, access_type, "") {
*result_listener << "whose access_type is "
<< SiteDataAccessTypeToString(arg->access_type);
<< DIPSDataAccessTypeToString(arg->access_type);
return ExplainMatchResult(Eq(access_type), arg->access_type, result_listener);
}
@ -1443,7 +1443,7 @@ TEST(DIPSRedirectContextTest, AddLateCookieAccess) {
MakeUrlAndId("http://a.test/"),
MakeServerRedirects(
{"http://b.test/", "http://c.test/", "http://d.test/"},
SiteDataAccessType::kNone),
DIPSDataAccessType::kNone),
MakeUrlAndId("http://e.test/"), false);
EXPECT_TRUE(context.AddLateCookieAccess(GURL("http://d.test/"),
@ -1453,14 +1453,14 @@ TEST(DIPSRedirectContextTest, AddLateCookieAccess) {
CookieOperation::kRead));
context.AppendCommitted(
MakeClientRedirect("http://e.test/", SiteDataAccessType::kNone),
MakeClientRedirect("http://e.test/", DIPSDataAccessType::kNone),
MakeServerRedirects({"http://f.test/", "http://g.test/"},
SiteDataAccessType::kRead),
DIPSDataAccessType::kRead),
MakeUrlAndId("http://h.test/"), false);
context.AppendCommitted(
MakeClientRedirect("http://h.test/", SiteDataAccessType::kNone),
MakeServerRedirects({"http://i.test/"}, SiteDataAccessType::kRead),
MakeClientRedirect("http://h.test/", DIPSDataAccessType::kNone),
MakeServerRedirects({"http://i.test/"}, DIPSDataAccessType::kRead),
MakeUrlAndId("http://j.test/"), false);
// Since kMaxLookback=5, AddLateCookieAccess() can attribute late accesses to
@ -1488,21 +1488,21 @@ TEST(DIPSRedirectContextTest, AddLateCookieAccess) {
EXPECT_THAT(
chains[0].second,
ElementsAre(AllOf(HasUrl("http://b.test/"),
HasSiteDataAccessType(SiteDataAccessType::kNone)),
HasDIPSDataAccessType(DIPSDataAccessType::kNone)),
AllOf(HasUrl("http://c.test/"),
HasSiteDataAccessType(SiteDataAccessType::kRead)),
HasDIPSDataAccessType(DIPSDataAccessType::kRead)),
AllOf(HasUrl("http://d.test/"),
HasSiteDataAccessType(SiteDataAccessType::kWrite)),
HasDIPSDataAccessType(DIPSDataAccessType::kWrite)),
AllOf(HasUrl("http://e.test/"),
HasSiteDataAccessType(SiteDataAccessType::kRead)),
HasDIPSDataAccessType(DIPSDataAccessType::kRead)),
AllOf(HasUrl("http://f.test/"),
HasSiteDataAccessType(SiteDataAccessType::kRead)),
HasDIPSDataAccessType(DIPSDataAccessType::kRead)),
AllOf(HasUrl("http://g.test/"),
HasSiteDataAccessType(SiteDataAccessType::kReadWrite)),
HasDIPSDataAccessType(DIPSDataAccessType::kReadWrite)),
AllOf(HasUrl("http://h.test/"),
HasSiteDataAccessType(SiteDataAccessType::kRead)),
HasDIPSDataAccessType(DIPSDataAccessType::kRead)),
AllOf(HasUrl("http://i.test/"),
HasSiteDataAccessType(SiteDataAccessType::kRead))));
HasDIPSDataAccessType(DIPSDataAccessType::kRead))));
}
TEST(DIPSRedirectContextTest, GetRedirectHeuristicURLs_NoRequirements) {
@ -1525,7 +1525,7 @@ TEST(DIPSRedirectContextTest, GetRedirectHeuristicURLs_NoRequirements) {
{MakeServerRedirects({"http://c.test"})},
current_interaction_url, false);
context.AppendCommitted(
MakeClientRedirect("http://b.test/", SiteDataAccessType::kNone,
MakeClientRedirect("http://b.test/", DIPSDataAccessType::kNone,
/*has_sticky_activation=*/true),
{}, first_party_url, false);
@ -1600,7 +1600,7 @@ TEST(DIPSRedirectContextTest,
{MakeServerRedirects({"http://c.test"})},
current_interaction_url, false);
context.AppendCommitted(
MakeClientRedirect("http://b.test/", SiteDataAccessType::kNone,
MakeClientRedirect("http://b.test/", DIPSDataAccessType::kNone,
/*has_sticky_activation=*/false, true),
{}, first_party_url, false);
@ -1640,7 +1640,7 @@ TEST(DIPSRedirectContextTest,
UrlAndSourceId(),
/*redirect_prefix_count=*/0);
context.AppendCommitted(
MakeClientRedirect("http://a.test/", SiteDataAccessType::kNone, false,
MakeClientRedirect("http://a.test/", DIPSDataAccessType::kNone, false,
true),
{}, MakeUrlAndId("http://b.test/"), false);
ASSERT_EQ(context.size(), 1u);
@ -1686,7 +1686,7 @@ TEST(
{MakeServerRedirects({"http://b.test"})},
MakeUrlAndId("http://c.test/"), false);
context.AppendCommitted(
MakeClientRedirect("http://b.test/", SiteDataAccessType::kNone, false,
MakeClientRedirect("http://b.test/", DIPSDataAccessType::kNone, false,
true),
{}, MakeUrlAndId("http://d.test/"), false);
ASSERT_EQ(context.size(), 2u);
@ -1709,7 +1709,7 @@ TEST(
{MakeServerRedirects({"http://b.test/"})},
MakeUrlAndId("http://c.test/"), false);
context.AppendCommitted(
MakeClientRedirect("http://b.test/", SiteDataAccessType::kNone, false,
MakeClientRedirect("http://b.test/", DIPSDataAccessType::kNone, false,
true),
MakeServerRedirects({"http://a.test/server-redirect/"}),
MakeUrlAndId("http://d.test/"), false);

@ -0,0 +1,52 @@
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/dips/dips_browsertest_utils.h"
#include "content/public/browser/dips_delegate.h"
namespace content {
bool ContentBrowserTestTpcBlockingBrowserClient::IsFullCookieAccessAllowed(
content::BrowserContext* browser_context,
content::WebContents* web_contents,
const GURL& url,
const blink::StorageKey& storage_key) {
return impl_.IsFullCookieAccessAllowed(browser_context, web_contents, url,
storage_key);
}
void ContentBrowserTestTpcBlockingBrowserClient::
GrantCookieAccessDueToHeuristic(content::BrowserContext* browser_context,
const net::SchemefulSite& top_frame_site,
const net::SchemefulSite& accessing_site,
base::TimeDelta ttl,
bool ignore_schemes) {
impl_.GrantCookieAccessDueToHeuristic(browser_context, top_frame_site,
accessing_site, ttl, ignore_schemes);
}
std::unique_ptr<content::DipsDelegate>
ContentBrowserTestTpcBlockingBrowserClient::CreateDipsDelegate() {
return impl_.CreateDipsDelegate();
}
bool ContentBrowserTestTpcBlockingBrowserClient::
IsPrivacySandboxReportingDestinationAttested(
content::BrowserContext* browser_context,
const url::Origin& destination_origin,
content::PrivacySandboxInvokingAPI invoking_api) {
return true;
}
bool ContentBrowserTestTpcBlockingBrowserClient::IsInterestGroupAPIAllowed(
content::BrowserContext* browser_context,
content::RenderFrameHost* render_frame_host,
InterestGroupApiOperation operation,
const url::Origin& top_frame_origin,
const url::Origin& api_origin) {
return true;
}
} // namespace content

@ -0,0 +1,70 @@
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CONTENT_BROWSER_DIPS_DIPS_BROWSERTEST_UTILS_H_
#define CONTENT_BROWSER_DIPS_DIPS_BROWSERTEST_UTILS_H_
#include <memory>
#include "base/time/time.h"
#include "content/browser/dips/dips_test_utils.h"
#include "content/public/test/content_browser_test_content_browser_client.h"
class GURL;
namespace blink {
class StorageKey;
}
namespace net {
class SchemefulSite;
}
namespace url {
class Origin;
}
namespace content {
class DipsDelegate;
class BrowserContext;
class RenderFrameHost;
class WebContents;
// Wraps TpcBlockingBrowserClient for use in content browser tests (which
// require subclasses of ContentBrowserTestContentBrowserClient).
class ContentBrowserTestTpcBlockingBrowserClient
: public ContentBrowserTestContentBrowserClient {
public:
bool IsFullCookieAccessAllowed(BrowserContext* browser_context,
WebContents* web_contents,
const GURL& url,
const blink::StorageKey& storage_key) override;
void GrantCookieAccessDueToHeuristic(BrowserContext* browser_context,
const net::SchemefulSite& top_frame_site,
const net::SchemefulSite& accessing_site,
base::TimeDelta ttl,
bool ignore_schemes) override;
std::unique_ptr<DipsDelegate> CreateDipsDelegate() override;
bool IsPrivacySandboxReportingDestinationAttested(
BrowserContext* browser_context,
const url::Origin& destination_origin,
PrivacySandboxInvokingAPI invoking_api) override;
bool IsInterestGroupAPIAllowed(BrowserContext* browser_context,
RenderFrameHost* render_frame_host,
InterestGroupApiOperation operation,
const url::Origin& top_frame_origin,
const url::Origin& api_origin) override;
TpcBlockingBrowserClient& impl() { return impl_; }
private:
TpcBlockingBrowserClient impl_;
};
} // namespace content
#endif // CONTENT_BROWSER_DIPS_DIPS_BROWSERTEST_UTILS_H_

@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/dips/dips_database.h"
#include "content/browser/dips/dips_database.h"
#include <cstddef>
#include <limits>
@ -19,8 +19,8 @@
#include "base/strings/stringprintf.h"
#include "base/threading/thread_restrictions.h"
#include "base/time/time.h"
#include "chrome/browser/dips/dips_database_migrator.h"
#include "chrome/browser/dips/dips_utils.h"
#include "content/browser/dips/dips_database_migrator.h"
#include "content/browser/dips/dips_utils.h"
#include "content/public/common/content_features.h"
#include "sql/database.h"
#include "sql/error_delegate_util.h"
@ -36,9 +36,10 @@ BASE_FEATURE(kSqlWALModeOnDipsDatabase,
base::FEATURE_ENABLED_BY_DEFAULT);
// NOTE: This is flag is intended for local testing and debugging only.
// TODO: crbug.com/380903149 - re-enable exclusive locking.
BASE_FEATURE(kDisableExclusiveLockingOnDipsDatabase,
"DisableExclusiveLockingOnDipsDatabase",
base::FEATURE_DISABLED_BY_DEFAULT);
base::FEATURE_ENABLED_BY_DEFAULT);
constexpr char kTimerLastFiredKey[] = "timer_last_fired";
@ -93,11 +94,6 @@ void BindTimesOrNull(sql::Statement& statement,
} // namespace
// See comments at declaration of these variables in dips_database.h
// for details.
const base::TimeDelta DIPSDatabase::kMetricsInterval = base::Hours(24);
const base::TimeDelta DIPSDatabase::kPopupTtl = base::Days(60);
DIPSDatabase::DIPSDatabase(const std::optional<base::FilePath>& db_path)
: db_path_(db_path.value_or(base::FilePath())) {
DCHECK(base::FeatureList::IsEnabled(features::kDIPS));

@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_DIPS_DIPS_DATABASE_H_
#define CHROME_BROWSER_DIPS_DIPS_DATABASE_H_
#ifndef CONTENT_BROWSER_DIPS_DIPS_DATABASE_H_
#define CONTENT_BROWSER_DIPS_DIPS_DATABASE_H_
#include <stddef.h>
@ -13,7 +13,8 @@
#include "base/strings/cstring_view.h"
#include "base/time/clock.h"
#include "base/time/default_clock.h"
#include "chrome/browser/dips/dips_utils.h"
#include "content/browser/dips/dips_utils.h"
#include "content/common/content_export.h"
#include "content/public/common/content_features.h"
#include "sql/database.h"
#include "sql/init_status.h"
@ -21,7 +22,7 @@
#include "sql/statement.h"
// Encapsulates an SQL database that holds DIPS info.
class DIPSDatabase {
class CONTENT_EXPORT DIPSDatabase {
public:
// Version number of the database schema.
// NOTE: When changing the version, add a new golden file for the new version
@ -34,11 +35,11 @@ class DIPSDatabase {
static constexpr char kPrepopulatedKey[] = "prepopulated";
// The length of time that will be waited between emitting db health metrics.
static const base::TimeDelta kMetricsInterval;
static constexpr base::TimeDelta kMetricsInterval = base::Hours(24);
// How long DIPS maintains popups in storage (for recording Popup Heuristic
// storage accesses).
static const base::TimeDelta kPopupTtl;
static constexpr base::TimeDelta kPopupTtl = base::Days(60);
// Passing in an std::nullopt `db_path` causes the db to be created in
// memory. Init() must be called before using the DIPSDatabase to make sure it
@ -287,4 +288,4 @@ class DIPSDatabase {
SEQUENCE_CHECKER(sequence_checker_);
};
#endif // CHROME_BROWSER_DIPS_DIPS_DATABASE_H_
#endif // CONTENT_BROWSER_DIPS_DIPS_DATABASE_H_

@ -2,11 +2,11 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/dips/dips_database_migrator.h"
#include "content/browser/dips/dips_database_migrator.h"
#include "base/check_deref.h"
#include "base/strings/stringprintf.h"
#include "chrome/browser/dips/dips_database.h"
#include "content/browser/dips/dips_database.h"
#include "sql/meta_table.h"
#include "sql/statement.h"

@ -2,19 +2,21 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_DIPS_DIPS_DATABASE_MIGRATOR_H_
#define CHROME_BROWSER_DIPS_DIPS_DATABASE_MIGRATOR_H_
#ifndef CONTENT_BROWSER_DIPS_DIPS_DATABASE_MIGRATOR_H_
#define CONTENT_BROWSER_DIPS_DIPS_DATABASE_MIGRATOR_H_
#include "content/common/content_export.h"
#include "sql/database.h"
#include "sql/meta_table.h"
// Returns whether the migration was successful.
bool MigrateDIPSSchemaToLatestVersion(sql::Database& db,
sql::MetaTable& meta_table);
CONTENT_EXPORT bool MigrateDIPSSchemaToLatestVersion(
sql::Database& db,
sql::MetaTable& meta_table);
namespace internal {
class DIPSDatabaseMigrator {
class CONTENT_EXPORT DIPSDatabaseMigrator {
public:
// `db` and `meta_table` must be non-null and must outlive this
// `DIPSDatabaseMigrator` instance.
@ -66,4 +68,4 @@ class DIPSDatabaseMigrator {
} // namespace internal
#endif // CHROME_BROWSER_DIPS_DIPS_DATABASE_MIGRATOR_H_
#endif // CONTENT_BROWSER_DIPS_DIPS_DATABASE_MIGRATOR_H_

@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/dips/dips_database_migrator.h"
#include "content/browser/dips/dips_database_migrator.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
@ -10,7 +10,7 @@
#include "base/path_service.h"
#include "base/strings/string_split.h"
#include "base/strings/stringprintf.h"
#include "chrome/browser/dips/dips_test_utils.h"
#include "content/browser/dips/dips_test_utils.h"
#include "sql/database.h"
#include "sql/test/test_helpers.h"
#include "sql/transaction.h"
@ -31,7 +31,7 @@ class DIPSDatabaseMigrationTest : public testing::Test {
if (!base::PathService::Get(base::DIR_SRC_TEST_DATA_ROOT, &root)) {
return AssertionFailure() << "Could not get test data directory root";
}
base::FilePath file_path = root.AppendASCII("chrome")
base::FilePath file_path = root.AppendASCII("content")
.AppendASCII("test")
.AppendASCII("data")
.AppendASCII("dips")

@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/dips/dips_database.h"
#include "content/browser/dips/dips_database.h"
#include <cstdio>
#include <optional>
@ -21,8 +21,8 @@
#include "base/test/scoped_feature_list.h"
#include "base/test/simple_test_clock.h"
#include "base/time/time.h"
#include "chrome/browser/dips/dips_test_utils.h"
#include "chrome/browser/dips/dips_utils.h"
#include "content/browser/dips/dips_test_utils.h"
#include "content/browser/dips/dips_utils.h"
#include "content/public/common/content_features.h"
#include "content/public/common/dips_utils.h"
#include "sql/database.h"
@ -1619,7 +1619,7 @@ class DIPSDatabaseInitializationTest : public testing::Test {
void LoadDatabase(const char* file_name) {
base::FilePath root;
ASSERT_TRUE(base::PathService::Get(base::DIR_SRC_TEST_DATA_ROOT, &root));
base::FilePath file_path = root.AppendASCII("chrome")
base::FilePath file_path = root.AppendASCII("content")
.AppendASCII("test")
.AppendASCII("data")
.AppendASCII("dips")

@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <memory>
#include "base/memory/raw_ptr.h"
#include "base/rand_util.h"
#include "base/run_loop.h"
@ -14,39 +16,35 @@
#include "base/test/test_file_util.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/browsing_data/browsing_data_important_sites_util.h"
#include "chrome/browser/browsing_data/chrome_browsing_data_remover_constants.h"
#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
#include "chrome/browser/dips/dips_bounce_detector.h"
#include "chrome/browser/dips/dips_service_impl.h"
#include "chrome/browser/dips/dips_storage.h"
#include "chrome/browser/dips/dips_test_utils.h"
#include "chrome/browser/dips/dips_utils.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/test/base/chrome_test_utils.h"
#include "chrome/test/base/platform_browser_test.h"
#include "chrome/test/base/testing_profile.h"
#include "components/content_settings/core/browser/host_content_settings_map.h"
#include "components/content_settings/core/common/content_settings_pattern.h"
#include "components/ukm/test_ukm_recorder.h"
#include "content/browser/dips/dips_bounce_detector.h"
#include "content/browser/dips/dips_browsertest_utils.h"
#include "content/browser/dips/dips_service_impl.h"
#include "content/browser/dips/dips_storage.h"
#include "content/browser/dips/dips_test_utils.h"
#include "content/browser/dips/dips_utils.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/browsing_data_remover.h"
#include "content/public/browser/dips_delegate.h"
#include "content/public/browser/dips_service.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/common/content_features.h"
#include "content/public/test/browser_task_environment.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/content_browser_test.h"
#include "content/public/test/content_browser_test_content_browser_client.h"
#include "content/public/test/dips_service_test_utils.h"
#include "content/public/test/hit_test_region_observer.h"
#include "content/public/test/test_browser_context.h"
#include "content/public/test/test_launcher.h"
#include "content/shell/browser/shell.h"
#include "net/dns/mock_host_resolver.h"
#include "third_party/blink/public/common/switches.h"
#if !BUILDFLAG(IS_ANDROID)
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_commands.h"
#include "chrome/test/base/ui_test_utils.h"
#endif // !BUILDFLAG(IS_ANDROID)
#include "ui/gfx/geometry/size.h"
#include "url/url_constants.h"
using content::CookieAccessDetails;
using content::NavigationHandle;
@ -58,7 +56,7 @@ using testing::Pair;
using testing::Optional;
using testing::Pair;
class DIPSTabHelperBrowserTest : public PlatformBrowserTest,
class DIPSTabHelperBrowserTest : public content::ContentBrowserTest,
public testing::WithParamInterface<bool> {
protected:
void SetUp() override {
@ -74,7 +72,7 @@ class DIPSTabHelperBrowserTest : public PlatformBrowserTest,
}
scoped_feature_list_.InitWithFeaturesAndParameters(enabled_features,
disabled_features);
PlatformBrowserTest::SetUp();
content::ContentBrowserTest::SetUp();
}
void SetUpCommandLine(base::CommandLine* command_line) override {
@ -90,26 +88,29 @@ class DIPSTabHelperBrowserTest : public PlatformBrowserTest,
host_resolver()->AddRule("d.test", "127.0.0.1");
DIPSWebContentsObserver::FromWebContents(GetActiveWebContents())
->SetClockForTesting(&test_clock_);
browser_client_.emplace();
// Initialize exceptions for 1P sites with embedded 3P cookies. Block 3PC by
// default on a.test and d.test, since those are used as the initial and
// final URL in the redirect chains. This avoids trimming bounces due to 1P
// exceptions (e.g. Chrome Guard).
map_ = HostContentSettingsMapFactory::GetForProfile(
chrome_test_utils::GetActiveWebContents(this)->GetBrowserContext());
map_->SetContentSettingCustomScope(
ContentSettingsPattern::Wildcard(),
ContentSettingsPattern::FromString("[*.]a.test"),
ContentSettingsType::COOKIES, ContentSetting::CONTENT_SETTING_BLOCK);
map_->SetContentSettingCustomScope(
ContentSettingsPattern::Wildcard(),
ContentSettingsPattern::FromString("[*.]d.test"),
ContentSettingsType::COOKIES, ContentSetting::CONTENT_SETTING_BLOCK);
browser_client_->impl().SetBlockThirdPartyCookiesByDefault(false);
browser_client_->impl().BlockThirdPartyCookiesOnSite(GURL("http://a.test"));
browser_client_->impl().BlockThirdPartyCookiesOnSite(GURL("http://d.test"));
// We can only create extra browser contexts while single-threaded.
extra_browser_context_ = CreateTestBrowserContext();
}
void TearDown() override {
content::GetUIThreadTaskRunner({})->DeleteSoon(
FROM_HERE, std::move(extra_browser_context_));
content::ContentBrowserTest::TearDown();
}
WebContents* GetActiveWebContents() {
if (!web_contents_) {
web_contents_ = chrome_test_utils::GetActiveWebContents(this);
web_contents_ = shell()->web_contents();
}
return web_contents_;
}
@ -142,7 +143,8 @@ class DIPSTabHelperBrowserTest : public PlatformBrowserTest,
DIPSServiceImpl::Get(web_contents->GetBrowserContext());
GURL expected_url = web_contents->GetLastCommittedURL();
RedirectChainObserver chain_observer(dips_service, expected_url);
content::DipsRedirectChainObserver chain_observer(dips_service,
expected_url);
// Performing a browser-based navigation terminates the current redirect
// chain.
ASSERT_TRUE(content::NavigateToURL(
@ -151,18 +153,28 @@ class DIPSTabHelperBrowserTest : public PlatformBrowserTest,
chain_observer.Wait();
}
content::BrowserContext* extra_browser_context() {
return extra_browser_context_.get();
}
private:
raw_ptr<WebContents, AcrossTasksDanglingUntriaged> web_contents_ = nullptr;
// browser_client_ is wrapped in optional<> to delay construction -- it won't
// be registered properly if it's created too early.
std::optional<content::ContentBrowserTestTpcBlockingBrowserClient>
browser_client_;
base::SimpleTestClock test_clock_;
base::test::ScopedFeatureList scoped_feature_list_;
raw_ptr<HostContentSettingsMap> map_;
std::unique_ptr<content::TestBrowserContext> extra_browser_context_;
};
IN_PROC_BROWSER_TEST_P(DIPSTabHelperBrowserTest,
InteractionsRecordedInAncestorFrames) {
GURL url_a = embedded_test_server()->GetURL("a.test", "/iframe_blank.html");
GURL url_a =
embedded_test_server()->GetURL("a.test", "/page_with_blank_iframe.html");
GURL url_b = embedded_test_server()->GetURL("b.test", "/title1.html");
const std::string kIframeId = "test"; // defined in iframe_blank.html
const std::string kIframeId =
"test_iframe"; // defined in page_with_blank_iframe.html
base::Time time = base::Time::FromSecondsSinceUnixEpoch(1);
content::WebContents* web_contents = GetActiveWebContents();
@ -273,13 +285,14 @@ IN_PROC_BROWSER_TEST_P(DIPSTabHelperBrowserTest, StorageRecordedInSingleFrame) {
// cookie, the cookie needs to be SameSite=None and Secure.
net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
https_server.SetSSLConfig(net::EmbeddedTestServer::CERT_TEST_NAMES);
https_server.AddDefaultHandlers(
base::FilePath(FILE_PATH_LITERAL("chrome/test/data")));
https_server.AddDefaultHandlers(GetTestDataFilePath());
ASSERT_TRUE(https_server.Start());
GURL url_a = embedded_test_server()->GetURL("a.test", "/iframe_blank.html");
GURL url_a =
embedded_test_server()->GetURL("a.test", "/page_with_blank_iframe.html");
GURL url_b = https_server.GetURL("b.test", "/title1.html");
const std::string kIframeId = "test"; // defined in iframe_blank.html
const std::string kIframeId =
"test_iframe"; // defined in page_with_blank_iframe.html
base::Time time = base::Time::FromSecondsSinceUnixEpoch(1);
content::WebContents* web_contents = GetActiveWebContents();
@ -321,8 +334,7 @@ IN_PROC_BROWSER_TEST_P(DIPSTabHelperBrowserTest,
// cookie, the cookie needs to be SameSite=None and Secure.
net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
https_server.SetSSLConfig(net::EmbeddedTestServer::CERT_TEST_NAMES);
https_server.AddDefaultHandlers(
base::FilePath(FILE_PATH_LITERAL("chrome/test/data")));
https_server.AddDefaultHandlers(GetTestDataFilePath());
ASSERT_TRUE(https_server.Start());
GURL page_url = embedded_test_server()->GetURL("a.test", "/title1.html");
@ -409,6 +421,38 @@ IN_PROC_BROWSER_TEST_P(DIPSTabHelperBrowserTest, MultipleSiteStoragesRecorded) {
state_2->site_storage_times->second);
}
namespace {
class BrowsingDataRemovalObserver
: public content::BrowsingDataRemover::Observer {
public:
explicit BrowsingDataRemovalObserver(base::OnceClosure callback)
: callback_(std::move(callback)) {}
void OnBrowsingDataRemoverDone(uint64_t failed_data_types) override {
std::move(callback_).Run();
}
private:
base::OnceClosure callback_;
};
bool ClearBrowsingData(content::BrowsingDataRemover* remover,
base::TimeDelta time_period) {
base::RunLoop run_loop;
BrowsingDataRemovalObserver observer(run_loop.QuitClosure());
remover->AddObserver(&observer);
const base::Time now = base::Time::Now();
remover->RemoveAndReply(
now - time_period, now,
DIPSService::kDefaultRemoveMask |
TpcBlockingBrowserClient::DATA_TYPE_HISTORY,
content::BrowsingDataRemover::ORIGIN_TYPE_UNPROTECTED_WEB, &observer);
run_loop.Run();
remover->RemoveObserver(&observer);
return !testing::Test::HasFailure();
}
} // namespace
IN_PROC_BROWSER_TEST_P(DIPSTabHelperBrowserTest,
ChromeBrowsingDataRemover_Basic) {
content::WebContents* web_contents = GetActiveWebContents();
@ -431,20 +475,9 @@ IN_PROC_BROWSER_TEST_P(DIPSTabHelperBrowserTest,
EXPECT_EQ(state_initial->user_interaction_times->first, interaction_time);
// Remove browsing data for the past day.
uint64_t remove_mask = chrome_browsing_data_remover::DATA_TYPE_HISTORY |
chrome_browsing_data_remover::DATA_TYPE_SITE_DATA;
std::unique_ptr<content::BrowsingDataFilterBuilder> filter_builder(
content::BrowsingDataFilterBuilder::Create(
content::BrowsingDataFilterBuilder::Mode::kPreserve));
content::BrowsingDataRemover* remover =
GetActiveWebContents()->GetBrowserContext()->GetBrowsingDataRemover();
base::RunLoop run_loop;
browsing_data_important_sites_util::Remove(
remove_mask, content::BrowsingDataRemover::ORIGIN_TYPE_UNPROTECTED_WEB,
browsing_data::TimePeriod::LAST_DAY, std::move(filter_builder), remover,
base::IgnoreArgs<uint64_t>(run_loop.QuitClosure()));
run_loop.Run();
ASSERT_TRUE(ClearBrowsingData(
GetActiveWebContents()->GetBrowserContext()->GetBrowsingDataRemover(),
base::Days(1)));
// Verify that the user interaction has been cleared from the DIPS DB.
std::optional<StateValue> state_final =
@ -547,11 +580,14 @@ IN_PROC_BROWSER_TEST_P(DIPSTabHelperBrowserTest,
// Make b.test statefully bounce to d.test.
ASSERT_TRUE(content::NavigateToURL(
web_contents, embedded_test_server()->GetURL("a.test", "/title1.html")));
const GURL bounce_url1 = embedded_test_server()->GetURL(
"b.test", "/cross-site-with-cookie/d.test/title1.html");
URLCookieAccessObserver observer1(web_contents, bounce_url1,
CookieOperation::kChange);
ASSERT_TRUE(content::NavigateToURLFromRenderer(
web_contents,
embedded_test_server()->GetURL(
"b.test", "/cross-site-with-cookie/d.test/title1.html"),
web_contents, bounce_url1,
embedded_test_server()->GetURL("d.test", "/title1.html")));
observer1.Wait();
// End the chain so the bounce is recorded.
ASSERT_TRUE(content::NavigateToURL(
web_contents, embedded_test_server()->GetURL("a.test", "/title1.html")));
@ -560,11 +596,14 @@ IN_PROC_BROWSER_TEST_P(DIPSTabHelperBrowserTest,
// Make c.test statefully bounce to d.test.
ASSERT_TRUE(content::NavigateToURL(
web_contents, embedded_test_server()->GetURL("a.test", "/title1.html")));
const GURL bounce_url2 = embedded_test_server()->GetURL(
"c.test", "/cross-site-with-cookie/d.test/title1.html");
URLCookieAccessObserver observer2(web_contents, bounce_url2,
CookieOperation::kChange);
ASSERT_TRUE(content::NavigateToURLFromRenderer(
web_contents,
embedded_test_server()->GetURL(
"c.test", "/cross-site-with-cookie/d.test/title1.html"),
web_contents, bounce_url2,
embedded_test_server()->GetURL("d.test", "/title1.html")));
observer2.Wait();
EndRedirectChain();
// Verify the bounces were recorded. b.test:
@ -583,17 +622,9 @@ IN_PROC_BROWSER_TEST_P(DIPSTabHelperBrowserTest,
// Remove browsing data for the past hour. This should include c.test but not
// b.test.
base::RunLoop run_loop;
browsing_data_important_sites_util::Remove(
chrome_browsing_data_remover::DATA_TYPE_HISTORY |
chrome_browsing_data_remover::DATA_TYPE_SITE_DATA,
content::BrowsingDataRemover::ORIGIN_TYPE_UNPROTECTED_WEB,
browsing_data::TimePeriod::LAST_HOUR,
content::BrowsingDataFilterBuilder::Create(
content::BrowsingDataFilterBuilder::Mode::kPreserve),
ASSERT_TRUE(ClearBrowsingData(
web_contents->GetBrowserContext()->GetBrowsingDataRemover(),
base::IgnoreArgs<uint64_t>(run_loop.QuitClosure()));
run_loop.Run();
base::Hours(1)));
// Verify only the DIPS record for c.test was deleted.
ASSERT_TRUE(GetDIPSState(GetDipsService(web_contents), GURL("http://b.test"))
@ -769,12 +800,11 @@ IN_PROC_BROWSER_TEST_P(DIPSTabHelperBrowserTest,
ASSERT_EQ(c_state->user_interaction_times, std::nullopt);
// Open c.test on a new tab in a new window/profile.
ProfileManager* profile_manager = g_browser_process->profile_manager();
base::FilePath profile_path = profile_manager->user_data_dir().Append(
FILE_PATH_LITERAL("OtherProfile"));
Browser* new_browser = chrome::OpenEmptyWindow(
&profiles::testing::CreateProfileSync(profile_manager, profile_path));
ASSERT_TRUE(ui_test_utils::NavigateToURL(new_browser, GURL("http://c.test")));
content::Shell* another_window = content::Shell::CreateNewWindow(
extra_browser_context(), GURL(url::kAboutBlankURL), nullptr, gfx::Size());
ASSERT_TRUE(content::NavigateToURL(
another_window->web_contents(),
embedded_test_server()->GetURL("c.test", "/title1.html")));
// Trigger the DIPS timer which would delete tracker data.
SetDIPSTime(bounce_time + features::kDIPSGracePeriod.Get() +

@ -2,10 +2,10 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/dips/dips_navigation_flow_detector.h"
#include "content/browser/dips/dips_navigation_flow_detector.h"
#include "base/rand_util.h"
#include "chrome/browser/dips/dips_utils.h"
#include "content/browser/dips/dips_utils.h"
#include "content/public/browser/cookie_access_details.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/web_contents.h"
@ -161,8 +161,8 @@ EntrypointInfo::EntrypointInfo(const DIPSRedirectInfo& server_redirect_info,
: site(server_redirect_info.site),
source_id(server_redirect_info.url.source_id),
had_triggering_storage_access(
server_redirect_info.access_type == SiteDataAccessType::kWrite ||
server_redirect_info.access_type == SiteDataAccessType::kReadWrite),
server_redirect_info.access_type == DIPSDataAccessType::kWrite ||
server_redirect_info.access_type == DIPSDataAccessType::kReadWrite),
was_referral_client_redirect(
exit_page_info.WasNavigationToPageClientRedirect()) {}

@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_DIPS_DIPS_NAVIGATION_FLOW_DETECTOR_H_
#define CHROME_BROWSER_DIPS_DIPS_NAVIGATION_FLOW_DETECTOR_H_
#ifndef CONTENT_BROWSER_DIPS_DIPS_NAVIGATION_FLOW_DETECTOR_H_
#define CONTENT_BROWSER_DIPS_DIPS_NAVIGATION_FLOW_DETECTOR_H_
#include <optional>
#include <string>
@ -15,7 +15,7 @@
#include "base/time/clock.h"
#include "base/time/default_clock.h"
#include "base/time/time.h"
#include "chrome/browser/dips/dips_bounce_detector.h"
#include "content/browser/dips/dips_bounce_detector.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/browser/web_contents_user_data.h"
#include "services/metrics/public/cpp/ukm_source_id.h"
@ -205,4 +205,4 @@ class DipsNavigationFlowDetector
WEB_CONTENTS_USER_DATA_KEY_DECL();
};
#endif // CHROME_BROWSER_DIPS_DIPS_NAVIGATION_FLOW_DETECTOR_H_
#endif // CONTENT_BROWSER_DIPS_DIPS_NAVIGATION_FLOW_DETECTOR_H_

@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/dips/dips_navigation_flow_detector.h"
#include "content/browser/dips/dips_navigation_flow_detector.h"
#include "base/base64.h"
#include "base/test/bind.h"
@ -12,16 +12,8 @@
#include "base/test/test_future.h"
#include "base/test/test_timeouts.h"
#include "base/types/expected.h"
#include "chrome/browser/content_settings/cookie_settings_factory.h"
#include "chrome/browser/dips/dips_test_utils.h"
#include "chrome/browser/privacy_sandbox/privacy_sandbox_settings_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/test/base/chrome_test_utils.h"
#include "chrome/test/base/platform_browser_test.h"
#include "components/content_settings/core/browser/cookie_settings.h"
#include "components/content_settings/core/common/pref_names.h"
#include "components/privacy_sandbox/privacy_sandbox_attestations/privacy_sandbox_attestations.h"
#include "components/ukm/test_ukm_recorder.h"
#include "content/browser/dips/dips_test_utils.h"
#include "content/public/browser/attribution_data_model.h"
#include "content/public/browser/network_service_instance.h"
#include "content/public/test/browser_test.h"
@ -38,16 +30,6 @@
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/switches.h"
#if !BUILDFLAG(IS_ANDROID)
#include "chrome/browser/ssl/cert_verifier_browser_test.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/tabs/public/tab_features.h"
#include "components/network_session_configurator/common/network_switches.h"
#include "content/public/browser/scoped_authenticator_environment_for_testing.h"
#include "content/public/common/content_switches.h"
#include "device/fido/virtual_fido_device_factory.h"
#endif // !BUILDFLAG(IS_ANDROID)
namespace {
using AttributionData = std::set<content::AttributionDataModel::DataKey>;

@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/dips/dips_service_impl.h"
#include "content/browser/dips/dips_service_impl.h"
#include <memory>
#include <optional>
@ -25,19 +25,20 @@
#include "base/task/thread_pool.h"
#include "base/time/time.h"
#include "base/types/pass_key.h"
#include "chrome/browser/chrome_content_browser_client.h"
#include "chrome/browser/dips/chrome_dips_delegate.h"
#include "chrome/browser/dips/dips_redirect_info.h"
#include "chrome/browser/dips/dips_service_factory.h"
#include "chrome/browser/dips/dips_storage.h"
#include "chrome/browser/dips/dips_utils.h"
#include "chrome/browser/dips/persistent_repeating_timer.h"
#include "content/browser/browser_context_impl.h"
#include "content/browser/dips/dips_storage.h"
#include "content/browser/dips/dips_utils.h"
#include "content/browser/dips/persistent_repeating_timer.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/browsing_data_filter_builder.h"
#include "content/public/browser/browsing_data_remover.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/browser/dips_delegate.h"
#include "content/public/browser/dips_redirect_info.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/content_client.h"
#include "content/public/common/content_features.h"
#include "content/public/common/dips_utils.h"
#include "net/base/schemeful_site.h"
@ -57,28 +58,30 @@ BASE_FEATURE(kDipsOnForegroundSequence,
"DipsOnForegroundSequence",
base::FEATURE_DISABLED_BY_DEFAULT);
RedirectCategory ClassifyRedirect(SiteDataAccessType access,
bool has_interaction) {
DIPSRedirectCategory ClassifyRedirect(DIPSDataAccessType access,
bool has_interaction) {
using enum DIPSRedirectCategory;
switch (access) {
case SiteDataAccessType::kUnknown:
return has_interaction ? RedirectCategory::kUnknownCookies_HasEngagement
: RedirectCategory::kUnknownCookies_NoEngagement;
case SiteDataAccessType::kNone:
return has_interaction ? RedirectCategory::kNoCookies_HasEngagement
: RedirectCategory::kNoCookies_NoEngagement;
case SiteDataAccessType::kRead:
return has_interaction ? RedirectCategory::kReadCookies_HasEngagement
: RedirectCategory::kReadCookies_NoEngagement;
case SiteDataAccessType::kWrite:
return has_interaction ? RedirectCategory::kWriteCookies_HasEngagement
: RedirectCategory::kWriteCookies_NoEngagement;
case SiteDataAccessType::kReadWrite:
return has_interaction ? RedirectCategory::kReadWriteCookies_HasEngagement
: RedirectCategory::kReadWriteCookies_NoEngagement;
case DIPSDataAccessType::kUnknown:
return has_interaction ? kUnknownCookies_HasEngagement
: kUnknownCookies_NoEngagement;
case DIPSDataAccessType::kNone:
return has_interaction ? kNoCookies_HasEngagement
: kNoCookies_NoEngagement;
case DIPSDataAccessType::kRead:
return has_interaction ? kReadCookies_HasEngagement
: kReadCookies_NoEngagement;
case DIPSDataAccessType::kWrite:
return has_interaction ? kWriteCookies_HasEngagement
: kWriteCookies_NoEngagement;
case DIPSDataAccessType::kReadWrite:
return has_interaction ? kReadWriteCookies_HasEngagement
: kReadWriteCookies_NoEngagement;
}
}
inline void UmaHistogramBounceCategory(RedirectCategory category,
inline void UmaHistogramBounceCategory(DIPSRedirectCategory category,
DIPSCookieMode mode,
DIPSRedirectType type) {
const std::string histogram_name =
@ -272,9 +275,11 @@ DIPSService* DIPSService::Get(content::BrowserContext* context) {
return DIPSServiceImpl::Get(context);
}
DIPSServiceImpl::DIPSServiceImpl(base::PassKey<DIPSServiceFactory>,
DIPSServiceImpl::DIPSServiceImpl(base::PassKey<content::BrowserContextImpl>,
content::BrowserContext* context)
: browser_context_(context), dips_delegate_(ChromeDipsDelegate::Create()) {
: browser_context_(context),
dips_delegate_(
content::GetContentClient()->browser()->CreateDipsDelegate()) {
DCHECK(base::FeatureList::IsEnabled(features::kDIPS));
std::optional<base::FilePath> path_to_use;
base::FilePath dips_path = GetDIPSFilePath(browser_context_);
@ -329,7 +334,7 @@ DIPSServiceImpl::~DIPSServiceImpl() {
/* static */
DIPSServiceImpl* DIPSServiceImpl::Get(content::BrowserContext* context) {
return DIPSServiceFactory::GetForBrowserContext(context);
return content::BrowserContextImpl::From(context)->GetDipsService();
}
scoped_refptr<base::SequencedTaskRunner> DIPSServiceImpl::CreateTaskRunner() {
@ -363,7 +368,7 @@ void DIPSServiceImpl::HandleRedirectChain(
if (redirects.empty()) {
DCHECK(!chain->is_partial_chain);
for (auto& observer : observers_) {
observer.OnChainHandled(chain);
observer.OnChainHandled(redirects, chain);
}
return;
}
@ -444,7 +449,7 @@ void DIPSServiceImpl::GotState(
// All redirects handled.
if (!chain->is_partial_chain) {
for (auto& observer : observers_) {
observer.OnChainHandled(chain);
observer.OnChainHandled(redirects, chain);
}
}
return;
@ -543,7 +548,8 @@ void DIPSServiceImpl::HandleRedirect(
.SetRedirectChainIndex(redirect.chain_index.value())
.SetRedirectChainLength(chain.length)
.SetIsPartialRedirectChain(chain.is_partial_chain)
.SetClientBounceDelay(BucketizeBounceDelay(redirect.client_bounce_delay))
.SetClientBounceDelay(
BucketizeDIPSBounceDelay(redirect.client_bounce_delay))
.SetHasStickyActivation(redirect.has_sticky_activation)
.SetWebAuthnAssertionRequestSucceeded(
redirect.web_authn_assertion_request_succeeded)
@ -556,15 +562,15 @@ void DIPSServiceImpl::HandleRedirect(
}
// Record this bounce in the DIPS database.
if (redirect.access_type != SiteDataAccessType::kUnknown) {
if (redirect.access_type != DIPSDataAccessType::kUnknown) {
record_bounce.Run(
redirect.url.url, redirect.has_3pc_exception.value(),
chain.final_url.url, redirect.time,
/*stateful=*/redirect.access_type > SiteDataAccessType::kRead,
/*stateful=*/redirect.access_type > DIPSDataAccessType::kRead,
stateful_bounce_callback);
}
RedirectCategory category =
DIPSRedirectCategory category =
ClassifyRedirect(redirect.access_type, redirect.has_interaction.value());
UmaHistogramBounceCategory(category, chain.cookie_mode.value(),
redirect.redirect_type);
@ -682,9 +688,8 @@ void DIPSServiceImpl::RunDeletionTaskOnUIThread(std::vector<std::string> sites,
base::OnceClosure callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
uint64_t remove_mask = dips_delegate_
? dips_delegate_->GetRemoveMask()
: content::DipsDelegate::kDefaultRemoveMask;
uint64_t remove_mask =
dips_delegate_ ? dips_delegate_->GetRemoveMask() : kDefaultRemoveMask;
StateClearer::DeleteState(browser_context_->GetBrowsingDataRemover(),
std::move(sites), remove_mask, std::move(callback));
@ -705,15 +710,6 @@ void DIPSServiceImpl::RecordBrowserSignIn(std::string_view domain) {
base::Time::Now(), GetCookieMode());
}
void DIPSServiceImpl::MaybeNotifyCreated(base::PassKey<DIPSServiceFactory>) {
if (delegate_notified_) {
return;
}
delegate_notified_ = true; // Set this first to prevent infinite recursion.
ChromeDipsDelegate::Create()->OnDipsServiceCreated(browser_context_, this);
}
void DIPSServiceImpl::NotifyStatefulBounce(content::WebContents* web_contents) {
for (auto& observer : observers_) {
observer.OnStatefulBounce(web_contents);

@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_DIPS_DIPS_SERVICE_IMPL_H_
#define CHROME_BROWSER_DIPS_DIPS_SERVICE_IMPL_H_
#ifndef CONTENT_BROWSER_DIPS_DIPS_SERVICE_IMPL_H_
#define CONTENT_BROWSER_DIPS_DIPS_SERVICE_IMPL_H_
#include "base/functional/bind.h"
#include "base/functional/callback_forward.h"
@ -15,17 +15,16 @@
#include "base/task/sequenced_task_runner.h"
#include "base/threading/sequence_bound.h"
#include "base/types/pass_key.h"
#include "chrome/browser/dips/dips_redirect_info.h"
#include "chrome/browser/dips/dips_service.h"
#include "chrome/browser/dips/dips_storage.h"
#include "chrome/browser/dips/dips_utils.h"
#include "components/keyed_service/core/keyed_service.h"
#include "content/browser/dips/dips_storage.h"
#include "content/browser/dips/dips_utils.h"
#include "content/common/content_export.h"
#include "content/public/browser/browsing_data_filter_builder.h"
class DIPSServiceFactory;
#include "content/public/browser/dips_redirect_info.h"
#include "content/public/browser/dips_service.h"
namespace content {
class BrowserContext;
class BrowserContextImpl;
class DipsDelegate;
} // namespace content
@ -36,7 +35,7 @@ class PersistentRepeatingTimer;
// When DIPS moves to //content, DIPSServiceImpl will *not* be exposed in the
// Content API. Only other code in //content (such as the DIPS implementation)
// will be allowed to access it.
class DIPSServiceImpl : public DIPSService, public KeyedService {
class CONTENT_EXPORT DIPSServiceImpl : public DIPSService {
public:
using RecordBounceCallback = base::RepeatingCallback<void(
const GURL& url,
@ -46,13 +45,14 @@ class DIPSServiceImpl : public DIPSService, public KeyedService {
bool stateful,
base::RepeatingCallback<void(const GURL&)> stateful_bounce_callback)>;
DIPSServiceImpl(base::PassKey<DIPSServiceFactory>,
DIPSServiceImpl(base::PassKey<content::BrowserContextImpl>,
content::BrowserContext* context);
~DIPSServiceImpl() override;
static DIPSServiceImpl* Get(content::BrowserContext* context);
base::SequenceBound<DIPSStorage>* storage() { return &storage_; }
void RecordBounceForTesting(
const GURL& url,
bool has_3pc_exception,
@ -127,10 +127,6 @@ class DIPSServiceImpl : public DIPSService, public KeyedService {
}
}
// The first time this method is called, it calls
// DipsDelegate::OnDipsServiceCreated(). On subsequent calls, it does nothing.
void MaybeNotifyCreated(base::PassKey<DIPSServiceFactory>);
// Notify Observers that a stateful bounce took place in `web_contents`.
void NotifyStatefulBounce(content::WebContents* web_contents);
@ -177,11 +173,10 @@ class DIPSServiceImpl : public DIPSService, public KeyedService {
base::SequenceBound<DIPSStorage> storage_;
base::ObserverList<Observer> observers_;
std::unique_ptr<content::DipsDelegate> dips_delegate_;
bool delegate_notified_ = false;
std::map<std::string, int> open_sites_;
base::WeakPtrFactory<DIPSServiceImpl> weak_factory_{this};
};
#endif // CHROME_BROWSER_DIPS_DIPS_SERVICE_IMPL_H_
#endif // CONTENT_BROWSER_DIPS_DIPS_SERVICE_IMPL_H_

@ -2,9 +2,13 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/public/browser/dips_service.h"
#include <optional>
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/functional/callback_forward.h"
#include "base/memory/raw_ptr.h"
#include "base/run_loop.h"
@ -16,28 +20,22 @@
#include "base/time/default_clock.h"
#include "base/time/time.h"
#include "base/types/pass_key.h"
#include "chrome/browser/browsing_data/chrome_browsing_data_remover_constants.h"
#include "chrome/browser/content_settings/cookie_settings_factory.h"
#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
#include "chrome/browser/dips/dips_bounce_detector.h"
#include "chrome/browser/dips/dips_redirect_info.h"
#include "chrome/browser/dips/dips_service_factory.h"
#include "chrome/browser/dips/dips_service_impl.h"
#include "chrome/browser/dips/dips_state.h"
#include "chrome/browser/dips/dips_test_utils.h"
#include "chrome/browser/dips/dips_utils.h"
#include "chrome/test/base/testing_profile.h"
#include "components/content_settings/core/browser/cookie_settings.h"
#include "components/content_settings/core/browser/host_content_settings_map.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/pref_names.h"
#include "components/prefs/pref_service.h"
#include "components/ukm/test_ukm_recorder.h"
#include "content/browser/browser_context_impl.h"
#include "content/browser/dips/dips_bounce_detector.h"
#include "content/browser/dips/dips_service_impl.h"
#include "content/browser/dips/dips_state.h"
#include "content/browser/dips/dips_test_utils.h"
#include "content/browser/dips/dips_utils.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/dips_delegate.h"
#include "content/public/browser/dips_redirect_info.h"
#include "content/public/common/content_client.h"
#include "content/public/common/content_features.h"
#include "content/public/test/browser_task_environment.h"
#include "content/public/test/dips_service_test_utils.h"
#include "content/public/test/mock_browsing_data_remover_delegate.h"
#include "content/public/test/test_browser_context.h"
#include "net/base/schemeful_site.h"
#include "net/cookies/cookie_partition_key.h"
#include "net/http/http_status_code.h"
@ -45,8 +43,10 @@
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/features_generated.h"
#include "third_party/blink/public/common/storage_key/storage_key.h"
#include "url/gurl.h"
using content::DipsRedirectChainObserver;
using testing::AllOf;
using testing::ElementsAre;
using testing::IsEmpty;
@ -59,7 +59,7 @@ bool Has3pcException(content::BrowserContext* browser_context,
const GURL& final_url) {
auto redirect = std::make_unique<DIPSRedirectInfo>(
UrlAndSourceId(url, ukm::kInvalidSourceId), DIPSRedirectType::kServer,
SiteDataAccessType::kWrite, base::Time::Now(), false, net::HTTP_FOUND,
DIPSDataAccessType::kWrite, base::Time::Now(), false, net::HTTP_FOUND,
base::TimeDelta());
dips::Populate3PcExceptions(browser_context, web_contents, initial_url,
final_url, base::span_from_ref(redirect));
@ -93,14 +93,14 @@ class DIPSServiceTest : public testing::Test {
TEST_F(DIPSServiceTest, CreateServiceIfFeatureEnabled) {
ScopedInitDIPSFeature init_dips(true);
TestingProfile profile;
content::TestBrowserContext profile;
EXPECT_NE(DIPSServiceImpl::Get(&profile), nullptr);
}
TEST_F(DIPSServiceTest, DontCreateServiceIfFeatureDisabled) {
ScopedInitDIPSFeature init_dips(false);
TestingProfile profile;
content::TestBrowserContext profile;
EXPECT_EQ(DIPSServiceImpl::Get(&profile), nullptr);
}
@ -110,14 +110,14 @@ TEST_F(DIPSServiceTest, DontCreateServiceIfFeatureDisabled) {
TEST_F(DIPSServiceTest, DeleteDbFilesIfPersistenceDisabled) {
base::FilePath data_path = base::CreateUniqueTempDirectoryScopedToTest();
DIPSServiceImpl* service;
std::unique_ptr<TestingProfile> profile;
std::unique_ptr<content::TestBrowserContext> profile;
// Ensure the DIPS feature is enabled and the database is set to be persisted.
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeatureWithParameters(
features::kDIPS, {{"persist_database", "true"}});
profile = TestingProfile::Builder().SetPath(data_path).Build();
profile = std::make_unique<content::TestBrowserContext>(data_path);
service = DIPSServiceImpl::Get(profile.get());
ASSERT_NE(service, nullptr);
@ -132,10 +132,10 @@ TEST_F(DIPSServiceTest, DeleteDbFilesIfPersistenceDisabled) {
feature_list.InitAndEnableFeatureWithParameters(
features::kDIPS, {{"persist_database", "false"}});
// Reset the TestingProfile, then create a new instance with the same user
// Reset the TestBrowserContext, then create a new instance with the same user
// data path.
profile.reset();
profile = TestingProfile::Builder().SetPath(data_path).Build();
profile = std::make_unique<content::TestBrowserContext>(data_path);
service = DIPSServiceImpl::Get(profile.get());
ASSERT_NE(service, nullptr);
@ -157,8 +157,8 @@ TEST_F(DIPSServiceTest, PreserveRegularProfileDbFiles) {
features::kDIPS, {{"persist_database", "true"}});
// Build a regular profile.
std::unique_ptr<TestingProfile> profile =
TestingProfile::Builder().SetPath(data_path).Build();
std::unique_ptr<content::TestBrowserContext> profile =
std::make_unique<content::TestBrowserContext>(data_path);
DIPSServiceImpl* service = DIPSServiceImpl::Get(profile.get());
ASSERT_NE(service, nullptr);
@ -169,10 +169,10 @@ TEST_F(DIPSServiceTest, PreserveRegularProfileDbFiles) {
ASSERT_TRUE(base::PathExists(GetDIPSFilePath(profile.get())));
// Build an off-the-record profile based on `profile`.
TestingProfile* otr_profile =
TestingProfile::Builder().SetPath(data_path).BuildIncognito(
profile.get());
DIPSServiceImpl* otr_service = DIPSServiceImpl::Get(otr_profile);
std::unique_ptr<content::TestBrowserContext> otr_profile =
std::make_unique<content::TestBrowserContext>(profile->GetPath());
otr_profile->set_is_off_the_record(true);
DIPSServiceImpl* otr_service = DIPSServiceImpl::Get(otr_profile.get());
ASSERT_NE(otr_service, nullptr);
// Ensure the OTR profile's database has been initialized and any file
@ -182,12 +182,18 @@ TEST_F(DIPSServiceTest, PreserveRegularProfileDbFiles) {
// Ensure the regular profile's database files were NOT deleted.
EXPECT_TRUE(base::PathExists(GetDIPSFilePath(profile.get())));
// Every TestBrowserContext normally deletes its folder when it's destroyed.
// But since `otr_profile` is sharing `profile`'s directory, we don't want it
// to delete that folder (`profile` will).
otr_profile->TakePath();
}
TEST_F(DIPSServiceTest, EmptySiteEventsIgnored) {
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeature(features::kDIPS);
std::unique_ptr<TestingProfile> profile = std::make_unique<TestingProfile>();
std::unique_ptr<content::TestBrowserContext> profile =
std::make_unique<content::TestBrowserContext>();
DIPSServiceImpl* service = DIPSServiceImpl::Get(profile.get());
// Record a bounce for an empty URL.
@ -211,30 +217,20 @@ TEST_F(DIPSServiceTest, EmptySiteEventsIgnored) {
class DIPSServiceStateRemovalTest : public testing::Test {
public:
DIPSServiceStateRemovalTest()
: profile_(std::make_unique<TestingProfile>()),
service_(DIPSServiceImpl::Get(GetProfile())),
cookie_settings_(
CookieSettingsFactory::GetForProfile(GetProfile()).get()) {}
: profile_(std::make_unique<content::TestBrowserContext>()),
service_(DIPSServiceImpl::Get(GetProfile())) {
content::SetBrowserClientForTesting(&browser_client_);
}
base::TimeDelta grace_period;
base::TimeDelta interaction_ttl;
base::TimeDelta tiny_delta = base::Milliseconds(1);
void SetBlockThirdPartyCookies(bool value) {
GetProfile()->GetPrefs()->SetInteger(
prefs::kCookieControlsMode,
static_cast<int>(
value ? content_settings::CookieControlsMode::kBlockThirdParty
: content_settings::CookieControlsMode::kOff));
}
Profile* GetProfile() { return profile_.get(); }
content::BrowserContext* GetProfile() { return profile_.get(); }
DIPSServiceImpl* GetService() { return service_; }
content_settings::CookieSettings* GetCookieSettings() {
return cookie_settings_;
}
protected:
TpcBlockingBrowserClient browser_client_;
content::BrowserTaskEnvironment task_environment_;
content::MockBrowsingDataRemoverDelegate delegate_;
@ -245,8 +241,7 @@ class DIPSServiceStateRemovalTest : public testing::Test {
ASSERT_LT(tiny_delta, grace_period);
GetProfile()->GetBrowsingDataRemover()->SetEmbedderDelegate(&delegate_);
SetBlockThirdPartyCookies(true);
ASSERT_TRUE(GetCookieSettings()->ShouldBlockThirdPartyCookies());
browser_client_.SetBlockThirdPartyCookiesByDefault(true);
DCHECK(service_);
service_->SetStorageClockForTesting(&clock_);
@ -274,39 +269,23 @@ class DIPSServiceStateRemovalTest : public testing::Test {
}
// Add an exception to the third-party cookie blocking rule for
// |third_party_url| embedded by |first_party_url|. If |first_party_url| is
// not provided, |third_party_url| is allowed when embedded by any site. If
// |third_party_url| is not provided, any sites embedded under
// |first_party_url| are excepted.
void Add3PCException(const std::optional<GURL>& first_party_url,
const std::optional<GURL>& third_party_url) {
HostContentSettingsMap* map =
HostContentSettingsMapFactory::GetForProfile(GetProfile());
// |third_party_url| embedded by |first_party_url|.
void Add3PCException(const GURL& first_party_url,
const GURL& third_party_url) {
browser_client_.GrantCookieAccessDueToHeuristic(
profile_.get(), net::SchemefulSite(first_party_url),
net::SchemefulSite(third_party_url), base::Days(1),
/*ignore_schemas=*/false);
ContentSettingsPattern first_party_pattern =
first_party_url.has_value() ? ContentSettingsPattern::FromString(
"[*.]" + first_party_url->host())
: ContentSettingsPattern::Wildcard();
ContentSettingsPattern third_party_pattern =
third_party_url.has_value() ? ContentSettingsPattern::FromString(
"[*.]" + third_party_url->host())
: ContentSettingsPattern::Wildcard();
map->SetContentSettingCustomScope(third_party_pattern, first_party_pattern,
ContentSettingsType::COOKIES,
ContentSetting::CONTENT_SETTING_ALLOW);
EXPECT_EQ(CONTENT_SETTING_BLOCK,
GetCookieSettings()->GetCookieSetting(
first_party_url.value_or(GURL()), net::SiteForCookies(),
third_party_url.value_or(GURL()),
net::CookieSettingOverrides(), nullptr));
EXPECT_EQ(CONTENT_SETTING_ALLOW,
GetCookieSettings()->GetCookieSetting(
third_party_url.value_or(GURL()), net::SiteForCookies(),
first_party_url.value_or(GURL()),
net::CookieSettingOverrides(), nullptr));
auto* client = content::GetContentClientForTesting()->browser();
EXPECT_TRUE(client->IsFullCookieAccessAllowed(
profile_.get(), nullptr, third_party_url,
blink::StorageKey::CreateFirstParty(
url::Origin::Create(first_party_url))));
EXPECT_FALSE(client->IsFullCookieAccessAllowed(
profile_.get(), nullptr, first_party_url,
blink::StorageKey::CreateFirstParty(
url::Origin::Create(third_party_url))));
}
void RecordBounce(
@ -325,23 +304,38 @@ class DIPSServiceStateRemovalTest : public testing::Test {
private:
base::SimpleTestClock clock_;
std::unique_ptr<TestingProfile> profile_;
std::unique_ptr<content::TestBrowserContext> profile_;
raw_ptr<DIPSServiceImpl, DanglingUntriaged> service_ = nullptr;
raw_ptr<content_settings::CookieSettings, DanglingUntriaged>
cookie_settings_ = nullptr;
};
namespace {
class RedirectChainCounter : public DIPSService::Observer {
public:
explicit RedirectChainCounter(DIPSService* service) { obs_.Observe(service); }
size_t count() const { return count_; }
private:
void OnChainHandled(const std::vector<DIPSRedirectInfoPtr>& redirects,
const DIPSRedirectChainInfoPtr& chain) override {
count_++;
}
size_t count_ = 0;
base::ScopedObservation<DIPSService, Observer> obs_{this};
};
} // namespace
TEST_F(DIPSServiceStateRemovalTest,
CompleteChain_NotifiesRedirectChainObservers) {
CompleteChain_NotifiesDipsRedirectChainObservers) {
GetService()->SetStorageClockForTesting(base::DefaultClock::GetInstance());
auto observer = std::make_unique<RedirectChainObserver>(
GetService(), /*final_url=*/GURL("http://c.test/"));
RedirectChainCounter chain_counter(GetService());
std::vector<DIPSRedirectInfoPtr> complete_redirects;
complete_redirects.push_back(std::make_unique<DIPSRedirectInfo>(
/*url=*/MakeUrlAndId("http://b.test/"),
/*redirect_type=*/DIPSRedirectType::kServer,
/*access_type=*/SiteDataAccessType::kNone,
/*access_type=*/DIPSDataAccessType::kNone,
/*time=*/Now(),
/*was_response_cached=*/false,
/*response_code=*/net::HTTP_FOUND,
@ -359,20 +353,19 @@ TEST_F(DIPSServiceStateRemovalTest,
base::BindRepeating([](const GURL& final_url) {}));
WaitOnStorage(GetService());
// Expect one call to Observer.OnChainHandled when handling a complete chain.
EXPECT_EQ(observer->handle_call_count, 1u);
EXPECT_EQ(chain_counter.count(), 1u);
}
TEST_F(DIPSServiceStateRemovalTest,
PartialChain_DoesNotNotifyRedirectChainObservers) {
PartialChain_DoesNotNotifyDipsRedirectChainObservers) {
GetService()->SetStorageClockForTesting(base::DefaultClock::GetInstance());
auto observer = std::make_unique<RedirectChainObserver>(
GetService(), /*final_url=*/GURL("http://c.test/"));
RedirectChainCounter chain_counter(GetService());
std::vector<DIPSRedirectInfoPtr> partial_redirects;
partial_redirects.push_back(std::make_unique<DIPSRedirectInfo>(
/*url=*/MakeUrlAndId("http://b.test/"),
/*redirect_type=*/DIPSRedirectType::kServer,
/*access_type=*/SiteDataAccessType::kNone,
/*access_type=*/DIPSDataAccessType::kNone,
/*time=*/Now(),
/*was_response_cached=*/false,
/*response_code=*/net::HTTP_FOUND,
@ -390,14 +383,14 @@ TEST_F(DIPSServiceStateRemovalTest,
base::BindRepeating([](const GURL& final_url) {}));
WaitOnStorage(GetService());
// Expect no calls to Observer.OnChainHandled when handling a partial chain.
EXPECT_EQ(observer->handle_call_count, 0u);
EXPECT_EQ(chain_counter.count(), 0u);
}
// NOTE: The use of a MockBrowsingDataRemoverDelegate in this test fixture
// means that when DIPS deletion is enabled, the row for 'url' is not actually
// removed from the DIPS db since 'delegate_' doesn't actually carryout the
// removal task.
TEST_F(DIPSServiceStateRemovalTest, BrowsingDataDeletion_Enabled) {
TEST_F(DIPSServiceStateRemovalTest, DISABLED_BrowsingDataDeletion_Enabled) {
ukm::TestAutoSetUkmRecorder ukm_recorder;
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeatureWithParameters(
@ -427,7 +420,7 @@ TEST_F(DIPSServiceStateRemovalTest, BrowsingDataDeletion_Enabled) {
net::CookiePartitionKeyCollection());
delegate_.ExpectCall(
base::Time::Min(), base::Time::Max(),
(chrome_browsing_data_remover::FILTERABLE_DATA_TYPES &
(DIPSService::kDefaultRemoveMask &
~content::BrowsingDataRemover::DATA_TYPE_PRIVACY_SANDBOX) |
content::BrowsingDataRemover::DATA_TYPE_AVOID_CLOSING_CONNECTIONS,
content::BrowsingDataRemover::ORIGIN_TYPE_UNPROTECTED_WEB |
@ -507,7 +500,7 @@ TEST_F(DIPSServiceStateRemovalTest,
GURL excepted_3p_url("https://excepted-as-3p.com");
GURL non_excepted_url("https://not-excepted.com");
Add3PCException(std::nullopt, excepted_3p_url);
browser_client_.GrantCookieAccessTo3pSite(excepted_3p_url);
int stateful_bounce_count = 0;
base::RepeatingCallback<void(const GURL&)> increment_bounce =
@ -554,7 +547,7 @@ TEST_F(DIPSServiceStateRemovalTest,
GURL redirect_url_2("https://redirect-2.com");
GURL redirect_url_3("https://redirect-3.com");
Add3PCException(excepted_1p_url, std::nullopt);
browser_client_.AllowThirdPartyCookiesOnSite(excepted_1p_url);
Add3PCException(scoped_excepted_1p_url, redirect_url_1);
int stateful_bounce_count = 0;
@ -613,8 +606,11 @@ TEST_F(DIPSServiceStateRemovalTest,
EXPECT_EQ(stateful_bounce_count, 2);
}
// TODO: crbug.com/376625002 - temporarily disabled for the move to //content,
// where there's no HostContentSettingsMap. Find an appropriate way to implement
// this test in //content or move it back to //chrome.
TEST_F(DIPSServiceStateRemovalTest,
BrowsingDataDeletion_RespectsStorageAccessGrantExceptions) {
DISABLED_BrowsingDataDeletion_RespectsStorageAccessGrantExceptions) {
ukm::TestAutoSetUkmRecorder ukm_recorder;
std::vector<base::test::FeatureRefAndParams> enabled_features;
enabled_features.push_back(
@ -631,6 +627,7 @@ TEST_F(DIPSServiceStateRemovalTest,
GURL redirect_url_3("https://redirect-3.com");
// Create Storage Access grants for the required sites.
/*
HostContentSettingsMap* map =
HostContentSettingsMapFactory::GetForProfile(GetProfile());
map->SetContentSettingCustomScope(
@ -643,7 +640,7 @@ TEST_F(DIPSServiceStateRemovalTest,
ContentSettingsPattern::FromString(
"[*.]" + top_level_storage_access_grant_url.host()),
ContentSettingsType::TOP_LEVEL_STORAGE_ACCESS, CONTENT_SETTING_ALLOW);
*/
int stateful_bounce_count = 0;
base::RepeatingCallback<void(const GURL&)> increment_bounce =
base::BindLambdaForTesting(
@ -701,7 +698,7 @@ TEST_F(DIPSServiceStateRemovalTest,
TEST_F(
DIPSServiceStateRemovalTest,
BrowsingDataDeletion_Respects1PExceptionsForBlocking3PCWhenDefaultAllowed) {
SetBlockThirdPartyCookies(false);
browser_client_.SetBlockThirdPartyCookiesByDefault(false);
ukm::TestAutoSetUkmRecorder ukm_recorder;
base::test::ScopedFeatureList feature_list;
@ -716,17 +713,9 @@ TEST_F(
GURL redirect_url_3("https://redirect-3.com");
GURL redirect_url_4("https://redirect-4.com");
// Exceptions to block third-party cookies.
HostContentSettingsMap* map =
HostContentSettingsMapFactory::GetForProfile(GetProfile());
map->SetContentSettingCustomScope(
ContentSettingsPattern::Wildcard(),
ContentSettingsPattern::FromString("[*.]" + blocked_1p_url.host()),
ContentSettingsType::COOKIES, ContentSetting::CONTENT_SETTING_BLOCK);
map->SetContentSettingCustomScope(
ContentSettingsPattern::FromString("[*.]" + redirect_url_1.host()),
ContentSettingsPattern::FromString("[*.]" + scoped_blocked_1p_url.host()),
ContentSettingsType::COOKIES, ContentSetting::CONTENT_SETTING_BLOCK);
// // Exceptions to block third-party cookies.
browser_client_.BlockThirdPartyCookiesOnSite(blocked_1p_url);
browser_client_.BlockThirdPartyCookies(redirect_url_1, scoped_blocked_1p_url);
int stateful_bounce_count = 0;
base::RepeatingCallback<void(const GURL&)> increment_bounce =
@ -812,7 +801,7 @@ TEST_F(DIPSServiceStateRemovalTest, ImmediateEnforcement) {
net::CookiePartitionKeyCollection());
delegate_.ExpectCall(
base::Time::Min(), base::Time::Max(),
(chrome_browsing_data_remover::FILTERABLE_DATA_TYPES &
(DIPSService::kDefaultRemoveMask &
~content::BrowsingDataRemover::DATA_TYPE_PRIVACY_SANDBOX) |
content::BrowsingDataRemover::DATA_TYPE_AVOID_CLOSING_CONNECTIONS,
content::BrowsingDataRemover::ORIGIN_TYPE_UNPROTECTED_WEB |
@ -953,7 +942,7 @@ TEST_F(DIPSServiceHistogramTest, Deletion_ExceptedAs1P) {
// Record a bounce.
GURL url("https://example.com");
GURL excepted_1p_url("https://initial.com");
Add3PCException(excepted_1p_url, std::nullopt);
browser_client_.AllowThirdPartyCookiesOnSite(excepted_1p_url);
base::Time bounce_time = base::Time::FromSecondsSinceUnixEpoch(2);
RecordBounce(url, excepted_1p_url, GURL("https://final.com"), bounce_time,
true, base::BindRepeating([](const GURL& final_url) {}));
@ -987,7 +976,7 @@ TEST_F(DIPSServiceHistogramTest, Deletion_ExceptedAs3P) {
// Record a bounce.
GURL excepted_3p_url("https://example.com");
Add3PCException(std::nullopt, excepted_3p_url);
browser_client_.GrantCookieAccessTo3pSite(excepted_3p_url);
base::Time bounce_time = base::Time::FromSecondsSinceUnixEpoch(2);
RecordBounce(excepted_3p_url, GURL("https://initial.com"),
GURL("https://final.com"), bounce_time, true,
@ -1009,7 +998,7 @@ TEST_F(DIPSServiceHistogramTest, Deletion_ExceptedAs3P) {
EXPECT_FALSE(GetDIPSState(GetService(), excepted_3p_url).has_value());
}
TEST_F(DIPSServiceHistogramTest, Deletion_Enforced) {
TEST_F(DIPSServiceHistogramTest, DISABLED_Deletion_Enforced) {
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeatureWithParameters(
features::kDIPS,
@ -1055,19 +1044,19 @@ TEST_F(DIPSServiceHistogramTest, ServerBounceDelay) {
.GetTotalCountsForPrefix(kServerRedirectsStatusCodePrefix)
.empty());
TestingProfile profile;
content::TestBrowserContext profile;
DIPSServiceImpl* service = DIPSServiceImpl::Get(&profile);
UrlAndSourceId initial_url = MakeUrlAndId("http://a.test/");
UrlAndSourceId first_redirect_url = MakeUrlAndId("http://b.test/");
UrlAndSourceId second_redirect_url = MakeUrlAndId("http://c.test/");
RedirectChainObserver observer(service, GURL());
content::DipsRedirectChainObserver observer(service, GURL());
std::vector<DIPSRedirectInfoPtr> redirects;
redirects.push_back(std::make_unique<DIPSRedirectInfo>(
first_redirect_url,
/*redirect_type=*/DIPSRedirectType::kServer,
/*access_type=*/SiteDataAccessType::kNone,
/*access_type=*/DIPSDataAccessType::kNone,
/*time=*/base::Time::Now(),
/*was_response_cached=*/true,
/*response_code=*/net::HTTP_MOVED_PERMANENTLY,
@ -1075,7 +1064,7 @@ TEST_F(DIPSServiceHistogramTest, ServerBounceDelay) {
redirects.push_back(std::make_unique<DIPSRedirectInfo>(
second_redirect_url,
/*redirect_type=*/DIPSRedirectType::kServer,
/*access_type=*/SiteDataAccessType::kNone,
/*access_type=*/DIPSDataAccessType::kNone,
/*time=*/base::Time::Now(),
/*was_response_cached=*/false,
/*response_code=*/net::HTTP_FOUND,
@ -1121,7 +1110,7 @@ using DIPSServiceUkmTest = DIPSServiceTest;
TEST_F(DIPSServiceUkmTest, BothChainBeginAndChainEnd) {
ukm::TestAutoSetUkmRecorder ukm_recorder;
TestingProfile profile;
content::TestBrowserContext profile;
DIPSServiceImpl* service = DIPSServiceImpl::Get(&profile);
UrlAndSourceId initial_url = MakeUrlAndId("http://a.test/");
@ -1129,12 +1118,12 @@ TEST_F(DIPSServiceUkmTest, BothChainBeginAndChainEnd) {
UrlAndSourceId redirect_url2 = MakeUrlAndId("http://c.test/first");
UrlAndSourceId final_url = MakeUrlAndId("http://c.test/second");
RedirectChainObserver observer(service, final_url.url);
DipsRedirectChainObserver observer(service, final_url.url);
std::vector<DIPSRedirectInfoPtr> redirects;
redirects.push_back(std::make_unique<DIPSRedirectInfo>(
redirect_url1,
/*redirect_type=*/DIPSRedirectType::kServer,
/*access_type=*/SiteDataAccessType::kNone,
/*access_type=*/DIPSDataAccessType::kNone,
/*time=*/base::Time::Now(),
/*was_response_cached=*/false,
/*response_code=*/net::HTTP_FOUND,
@ -1142,7 +1131,7 @@ TEST_F(DIPSServiceUkmTest, BothChainBeginAndChainEnd) {
redirects.push_back(std::make_unique<DIPSRedirectInfo>(
redirect_url2,
/*redirect_type=*/DIPSRedirectType::kServer,
/*access_type=*/SiteDataAccessType::kNone,
/*access_type=*/DIPSDataAccessType::kNone,
/*time=*/base::Time::Now(),
/*was_response_cached=*/false,
/*response_code=*/net::HTTP_FOUND,
@ -1185,19 +1174,19 @@ TEST_F(DIPSServiceUkmTest, BothChainBeginAndChainEnd) {
TEST_F(DIPSServiceUkmTest, InitialAndFinalSitesSame_True) {
ukm::TestAutoSetUkmRecorder ukm_recorder;
TestingProfile profile;
content::TestBrowserContext profile;
DIPSServiceImpl* service = DIPSServiceImpl::Get(&profile);
UrlAndSourceId initial_url = MakeUrlAndId("http://a.test/");
UrlAndSourceId redirect_url = MakeUrlAndId("http://b.test/");
UrlAndSourceId final_url = MakeUrlAndId("http://a.test/different-path");
RedirectChainObserver observer(service, final_url.url);
DipsRedirectChainObserver observer(service, final_url.url);
std::vector<DIPSRedirectInfoPtr> redirects;
redirects.push_back(std::make_unique<DIPSRedirectInfo>(
redirect_url,
/*redirect_type=*/DIPSRedirectType::kServer,
/*access_type=*/SiteDataAccessType::kNone,
/*access_type=*/DIPSDataAccessType::kNone,
/*time=*/base::Time::Now(),
/*was_response_cached=*/false,
/*response_code=*/net::HTTP_FOUND,
@ -1233,13 +1222,13 @@ TEST_F(DIPSServiceUkmTest, InitialAndFinalSitesSame_True) {
TEST_F(DIPSServiceUkmTest, DontReportEmptyChainsAtAll) {
ukm::TestAutoSetUkmRecorder ukm_recorder;
TestingProfile profile;
content::TestBrowserContext profile;
DIPSServiceImpl* service = DIPSServiceImpl::Get(&profile);
UrlAndSourceId initial_url = MakeUrlAndId("http://a.test/");
UrlAndSourceId final_url = MakeUrlAndId("http://b.test/");
RedirectChainObserver observer(service, final_url.url);
DipsRedirectChainObserver observer(service, final_url.url);
DIPSRedirectChainInfoPtr chain = std::make_unique<DIPSRedirectChainInfo>(
initial_url, final_url,
/*length=*/0, /*is_partial_chain=*/false);
@ -1254,18 +1243,18 @@ TEST_F(DIPSServiceUkmTest, DontReportEmptyChainsAtAll) {
TEST_F(DIPSServiceUkmTest, DontReportChainBeginIfInvalidSourceId) {
ukm::TestAutoSetUkmRecorder ukm_recorder;
TestingProfile profile;
content::TestBrowserContext profile;
DIPSServiceImpl* service = DIPSServiceImpl::Get(&profile);
UrlAndSourceId redirect_url = MakeUrlAndId("http://b.test/");
UrlAndSourceId final_url = MakeUrlAndId("http://c.test/");
RedirectChainObserver observer(service, final_url.url);
DipsRedirectChainObserver observer(service, final_url.url);
std::vector<DIPSRedirectInfoPtr> redirects;
redirects.push_back(std::make_unique<DIPSRedirectInfo>(
redirect_url,
/*redirect_type=*/DIPSRedirectType::kServer,
/*access_type=*/SiteDataAccessType::kNone,
/*access_type=*/DIPSDataAccessType::kNone,
/*time=*/base::Time::Now(),
/*was_response_cached=*/false,
/*response_code=*/net::HTTP_FOUND,
@ -1291,18 +1280,18 @@ TEST_F(DIPSServiceUkmTest, DontReportChainBeginIfInvalidSourceId) {
TEST_F(DIPSServiceUkmTest, DontReportChainEndIfInvalidSourceId) {
ukm::TestAutoSetUkmRecorder ukm_recorder;
TestingProfile profile;
content::TestBrowserContext profile;
DIPSServiceImpl* service = DIPSServiceImpl::Get(&profile);
UrlAndSourceId initial_url = MakeUrlAndId("http://a.test/");
UrlAndSourceId redirect_url = MakeUrlAndId("http://b.test/");
RedirectChainObserver observer(service, GURL());
DipsRedirectChainObserver observer(service, GURL());
std::vector<DIPSRedirectInfoPtr> redirects;
redirects.push_back(std::make_unique<DIPSRedirectInfo>(
redirect_url,
/*redirect_type=*/DIPSRedirectType::kServer,
/*access_type=*/SiteDataAccessType::kNone,
/*access_type=*/DIPSDataAccessType::kNone,
/*time=*/base::Time::Now(),
/*was_response_cached=*/false,
/*response_code=*/net::HTTP_FOUND,
@ -1325,3 +1314,47 @@ TEST_F(DIPSServiceUkmTest, DontReportChainEndIfInvalidSourceId) {
EXPECT_THAT(ukm_recorder.GetEntries("DIPS.ChainEnd", {}), IsEmpty());
}
TEST(DIPSCleanupTest, DatabaseFileIsDeletedIfFeatureIsDisabled) {
content::BrowserTaskEnvironment task_environment;
base::FilePath user_data_dir;
base::FilePath db_path;
// First, create a browser context while DIPS is enabled, and confirm a
// database file is created.
{
content::TestBrowserContext browser_context;
db_path = GetDIPSFilePath(&browser_context);
// Wait for the database to be created.
content::BrowserContextImpl::From(&browser_context)
->GetDipsService()
->storage()
->FlushPostedTasksForTesting();
ASSERT_TRUE(base::PathExists(db_path));
// Take ownership of the browser context's directory so we can reuse it.
user_data_dir = browser_context.TakePath();
// Confirm that WaitForDipsCleanupForTesting() returns even if the file is
// not deleted.
content::BrowserContextImpl::From(&browser_context)
->WaitForDipsCleanupForTesting();
ASSERT_TRUE(base::PathExists(db_path));
}
// Confirm the file still exists after the browser context is destroyed.
ASSERT_TRUE(base::PathExists(db_path));
// Create another browser context for the same directory, while DIPS is
// disabled. Confirm the database file is deleted.
{
ScopedInitDIPSFeature disable_dips(false);
content::TestBrowserContext browser_context(user_data_dir);
ASSERT_FALSE(
content::BrowserContextImpl::From(&browser_context)->GetDipsService());
content::BrowserContextImpl::From(&browser_context)
->WaitForDipsCleanupForTesting();
ASSERT_FALSE(base::PathExists(db_path));
}
}

@ -2,10 +2,10 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/dips/dips_state.h"
#include "content/browser/dips/dips_state.h"
#include "chrome/browser/dips/dips_storage.h"
#include "chrome/browser/dips/dips_utils.h"
#include "content/browser/dips/dips_storage.h"
#include "content/browser/dips/dips_utils.h"
DIPSState::DIPSState(DIPSStorage* storage, std::string site)
: storage_(storage), site_(std::move(site)), was_loaded_(false) {}
@ -19,6 +19,7 @@ DIPSState::DIPSState(DIPSStorage* storage,
state_(state) {}
DIPSState::DIPSState(DIPSState&&) = default;
DIPSState& DIPSState::operator=(DIPSState&&) = default;
DIPSState::~DIPSState() {
if (dirty_) {

@ -2,14 +2,15 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_DIPS_DIPS_STATE_H_
#define CHROME_BROWSER_DIPS_DIPS_STATE_H_
#ifndef CONTENT_BROWSER_DIPS_DIPS_STATE_H_
#define CONTENT_BROWSER_DIPS_DIPS_STATE_H_
#include <string>
#include "base/memory/raw_ptr.h"
#include "base/time/time.h"
#include "chrome/browser/dips/dips_utils.h"
#include "content/browser/dips/dips_utils.h"
#include "content/common/content_export.h"
class DIPSStorage;
@ -17,7 +18,11 @@ class DIPSStorage;
class DirtyBit {
public:
explicit DirtyBit(bool value = false) : value_(value) {}
DirtyBit(DirtyBit&& old) : value_(std::exchange(old.value_, false)) {}
DirtyBit(DirtyBit&& old) { *this = std::move(old); }
DirtyBit& operator=(DirtyBit&& old) {
value_ = std::exchange(old.value_, false);
return *this;
}
explicit operator bool() const { return value_; }
@ -37,13 +42,14 @@ class DirtyBit {
// Not to be confused with state stored by sites (e.g. cookies, local storage),
// DIPSState represents the state recorded by DIPSService itself.
class DIPSState {
class CONTENT_EXPORT DIPSState {
public:
DIPSState(DIPSStorage* storage, std::string site);
// For loaded DIPSState.
DIPSState(DIPSStorage* storage, std::string site, const StateValue& state);
DIPSState(DIPSState&&);
DIPSState& operator=(DIPSState&&);
// Flushes changes to storage_.
~DIPSState();
@ -81,4 +87,4 @@ class DIPSState {
StateValue state_;
};
#endif // CHROME_BROWSER_DIPS_DIPS_STATE_H_
#endif // CONTENT_BROWSER_DIPS_DIPS_STATE_H_

@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/dips/dips_storage.h"
#include "content/browser/dips/dips_storage.h"
#include <memory>
@ -13,7 +13,7 @@
#include "base/task/sequenced_task_runner.h"
#include "base/task/thread_pool.h"
#include "base/threading/thread_restrictions.h"
#include "chrome/browser/dips/dips_utils.h"
#include "content/browser/dips/dips_utils.h"
#include "content/public/common/content_features.h"
#include "content/public/common/dips_utils.h"
#include "services/network/public/mojom/clear_data_filter.mojom.h"

@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_DIPS_DIPS_STORAGE_H_
#define CHROME_BROWSER_DIPS_DIPS_STORAGE_H_
#ifndef CONTENT_BROWSER_DIPS_DIPS_STORAGE_H_
#define CONTENT_BROWSER_DIPS_DIPS_STORAGE_H_
#include <cstddef>
#include <map>
@ -14,9 +14,10 @@
#include "base/memory/weak_ptr.h"
#include "base/sequence_checker.h"
#include "base/time/time.h"
#include "chrome/browser/dips/dips_database.h"
#include "chrome/browser/dips/dips_state.h"
#include "chrome/browser/dips/dips_utils.h"
#include "content/browser/dips/dips_database.h"
#include "content/browser/dips/dips_state.h"
#include "content/browser/dips/dips_utils.h"
#include "content/common/content_export.h"
#include "services/network/public/mojom/network_context.mojom.h"
class GURL;
@ -24,7 +25,7 @@ class GURL;
using UrlPredicate = base::RepeatingCallback<bool(const GURL&)>;
// Manages the storage of DIPSState values.
class DIPSStorage {
class CONTENT_EXPORT DIPSStorage {
public:
explicit DIPSStorage(const std::optional<base::FilePath>& path);
~DIPSStorage();
@ -147,4 +148,4 @@ class DIPSStorage {
base::WeakPtrFactory<DIPSStorage> weak_factory_{this};
};
#endif // CHROME_BROWSER_DIPS_DIPS_STORAGE_H_
#endif // CONTENT_BROWSER_DIPS_DIPS_STORAGE_H_

@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/dips/dips_storage.h"
#include "content/browser/dips/dips_storage.h"
#include <optional>
@ -16,8 +16,8 @@
#include "base/test/task_environment.h"
#include "base/threading/sequence_bound.h"
#include "base/time/time.h"
#include "chrome/browser/dips/dips_state.h"
#include "chrome/browser/dips/dips_utils.h"
#include "content/browser/dips/dips_state.h"
#include "content/browser/dips/dips_utils.h"
#include "content/public/browser/browsing_data_filter_builder.h"
#include "content/public/common/content_features.h"
#include "services/network/public/mojom/clear_data_filter.mojom.h"
@ -799,9 +799,9 @@ TEST_F(DIPSStorageTest, RemoveBySite) {
EXPECT_FALSE(state1.site_storage_times().has_value()); // removed
EXPECT_EQ(state1.user_interaction_times()->first,
std::make_optional(
base::Time::FromSecondsSinceUnixEpoch(2))); // no change
EXPECT_FALSE(state1.stateful_bounce_times().has_value()); // removed
EXPECT_FALSE(state1.bounce_times().has_value()); // removed
base::Time::FromSecondsSinceUnixEpoch(2))); // no change
EXPECT_FALSE(state1.stateful_bounce_times().has_value()); // removed
EXPECT_FALSE(state1.bounce_times().has_value()); // removed
DIPSState state2 = storage_.Read(url2);
EXPECT_EQ(state2.site_storage_times()->first,

@ -2,21 +2,22 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/dips/dips_test_utils.h"
#include "content/browser/dips/dips_test_utils.h"
#include <string_view>
#include "base/test/bind.h"
#include "chrome/browser/dips/dips_cleanup_service_factory.h"
#include "chrome/browser/dips/dips_service.h"
#include "chrome/browser/dips/dips_service_factory.h"
#include "chrome/browser/dips/dips_service_impl.h"
#include "content/browser/dips/dips_service_impl.h"
#include "content/public/browser/browsing_data_remover.h"
#include "content/public/browser/dips_delegate.h"
#include "content/public/browser/dips_service.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/content_features.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/hit_test_region_observer.h"
#include "content/public/test/test_frame_navigation_observer.h"
#include "content/public/test/test_utils.h"
#include "net/base/schemeful_site.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "testing/gmock/include/gmock/gmock.h"
@ -226,29 +227,6 @@ void FrameCookieAccessObserver::OnCookiesAccessed(
}
}
RedirectChainObserver::RedirectChainObserver(DIPSService* service,
GURL final_url,
size_t expected_match_count)
: final_url_(std::move(final_url)),
expected_match_count_(expected_match_count) {
obs_.Observe(service);
}
RedirectChainObserver::~RedirectChainObserver() = default;
void RedirectChainObserver::OnChainHandled(
const DIPSRedirectChainInfoPtr& chain) {
handle_call_count++;
if (chain->final_url.url == final_url_ &&
++match_count_ == expected_match_count_) {
run_loop_.Quit();
}
}
void RedirectChainObserver::Wait() {
run_loop_.Run();
}
UserActivationObserver::UserActivationObserver(
WebContents* web_contents,
RenderFrameHost* render_frame_host)
@ -360,59 +338,112 @@ UrlAndSourceId MakeUrlAndId(std::string_view url) {
return UrlAndSourceId(GURL(url), ukm::AssignNewSourceId());
}
testing::AssertionResult SimulateDipsBounce(content::WebContents* web_contents,
const GURL& initial_url,
const GURL& bounce_url,
const GURL& final_url,
const GURL& next_initial_url) {
if (web_contents->GetLastCommittedURL() == initial_url) {
return testing::AssertionFailure() << "Already on " << initial_url;
TpcBlockingBrowserClient::TpcBlockingBrowserClient() = default;
TpcBlockingBrowserClient::~TpcBlockingBrowserClient() = default;
bool TpcBlockingBrowserClient::IsFullCookieAccessAllowed(
content::BrowserContext* browser_context,
content::WebContents* web_contents,
const GURL& url,
const blink::StorageKey& storage_key) {
// TODO: crbug.com/384531044 - implement this method by subclassing
// `content_settings::CookieSettingsBase` and calling its
// `IsFullCookieAccessAllowed()`.
const net::SchemefulSite top_level_site = storage_key.top_level_site();
const net::SchemefulSite url_site(url);
if (base::Contains(tpc_1p_blocks_, top_level_site)) {
return false;
}
DIPSService* dips_service =
DIPSService::Get(web_contents->GetBrowserContext());
RedirectChainObserver initial_observer(dips_service, initial_url);
if (!content::NavigateToURL(web_contents, initial_url)) {
return testing::AssertionFailure()
<< "Failed to navigate to " << initial_url;
}
initial_observer.Wait();
if (testing::Test::HasFailure()) {
return testing::AssertionFailure()
<< "Failure generated while waiting for the previous redirect chain "
"to be reported";
if (base::Contains(tpc_blocks_, std::make_pair(top_level_site, url_site))) {
return false;
}
if (!content::NavigateToURLFromRenderer(web_contents, bounce_url)) {
return testing::AssertionFailure()
<< "Failed to navigate to " << bounce_url;
if (!block_3pcs_) {
return true;
}
testing::AssertionResult js_result =
content::ExecJs(web_contents, "document.cookie = 'bounce=stateful';",
content::EXECUTE_SCRIPT_NO_USER_GESTURE);
if (!js_result) {
return js_result;
if (storage_key.ToNetSiteForCookies().IsFirstParty(url)) {
return true;
}
RedirectChainObserver final_observer(dips_service, final_url);
if (!content::NavigateToURLFromRendererWithoutUserGesture(web_contents,
final_url)) {
return testing::AssertionFailure() << "Failed to navigate to " << final_url;
// XXX how should we use storage_key.origin() ?
if (base::Contains(tpc_1p_site_exceptions_, top_level_site)) {
return true;
}
// End redirect chain by navigating with a user gesture.
if (!content::NavigateToURLFromRenderer(web_contents, next_initial_url)) {
return testing::AssertionFailure()
<< "Failed to navigate to " << next_initial_url;
}
final_observer.Wait();
if (testing::Test::HasFailure()) {
return testing::AssertionFailure() << "Failure generated while waiting for "
"the redirect chain to be reported";
if (base::Contains(tpc_3p_site_exceptions_, url_site)) {
return true;
}
return testing::AssertionSuccess();
if (base::Contains(
schemeless_tpc_exceptions_,
std::make_pair(
top_level_site.registrable_domain_or_host_for_testing(),
url_site.registrable_domain_or_host_for_testing()))) {
return true;
}
bool b =
base::Contains(tpc_exceptions_, std::make_pair(top_level_site, url_site));
return b;
}
void TpcBlockingBrowserClient::GrantCookieAccessDueToHeuristic(
content::BrowserContext* browser_context,
const net::SchemefulSite& top_frame_site,
const net::SchemefulSite& accessing_site,
base::TimeDelta ttl,
bool ignore_schemes) {
if (ignore_schemes) {
schemeless_tpc_exceptions_.emplace(
top_frame_site.registrable_domain_or_host_for_testing(),
accessing_site.registrable_domain_or_host_for_testing());
} else {
tpc_exceptions_.emplace(top_frame_site, accessing_site);
}
}
// A DipsDelegate that only differs from the default (i.e., no delegate)
// behavior in one way: ShouldDeleteInteractionRecords() checks for the
// DATA_TYPE_HISTORY bit.
class SimpleDipsDelegate : public content::DipsDelegate {
public:
bool ShouldEnableDips(content::BrowserContext* browser_context) override {
return true;
}
void OnDipsServiceCreated(content::BrowserContext* browser_context,
DIPSService* dips_service) override {}
uint64_t GetRemoveMask() override { return DIPSService::kDefaultRemoveMask; }
bool ShouldDeleteInteractionRecords(uint64_t remove_mask) override {
return remove_mask & TpcBlockingBrowserClient::DATA_TYPE_HISTORY;
}
};
std::unique_ptr<content::DipsDelegate>
TpcBlockingBrowserClient::CreateDipsDelegate() {
return std::make_unique<SimpleDipsDelegate>();
}
void TpcBlockingBrowserClient::AllowThirdPartyCookiesOnSite(const GURL& url) {
tpc_1p_site_exceptions_.emplace(url);
}
void TpcBlockingBrowserClient::GrantCookieAccessTo3pSite(const GURL& url) {
tpc_3p_site_exceptions_.emplace(url);
}
void TpcBlockingBrowserClient::BlockThirdPartyCookiesOnSite(const GURL& url) {
tpc_1p_blocks_.emplace(url);
}
void TpcBlockingBrowserClient::BlockThirdPartyCookies(
const GURL& url,
const GURL& first_party_url) {
tpc_blocks_.emplace(net::SchemefulSite(first_party_url),
net::SchemefulSite(url));
}

@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_DIPS_DIPS_TEST_UTILS_H_
#define CHROME_BROWSER_DIPS_DIPS_TEST_UTILS_H_
#ifndef CONTENT_BROWSER_DIPS_DIPS_TEST_UTILS_H_
#define CONTENT_BROWSER_DIPS_DIPS_TEST_UTILS_H_
#include <iosfwd>
#include <string>
@ -14,13 +14,12 @@
#include "base/scoped_observation.h"
#include "base/test/scoped_feature_list.h"
#include "base/types/expected.h"
#include "chrome/browser/dips/dips_redirect_info.h"
#include "chrome/browser/dips/dips_service.h"
#include "chrome/browser/dips/dips_service_impl.h"
#include "chrome/browser/dips/dips_utils.h"
#include "chrome/browser/profiles/profile_test_util.h"
#include "components/ukm/test_ukm_recorder.h"
#include "content/browser/dips/dips_service_impl.h"
#include "content/browser/dips/dips_utils.h"
#include "content/public/browser/cookie_access_details.h"
#include "content/public/browser/dips_redirect_info.h"
#include "content/public/browser/dips_service.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/test/browser_test_utils.h"
@ -187,27 +186,6 @@ class FrameCookieAccessObserver : public content::WebContentsObserver {
base::RunLoop run_loop_;
};
class RedirectChainObserver : public DIPSService::Observer {
public:
explicit RedirectChainObserver(DIPSService* service,
GURL final_url,
size_t expected_match_count = 1);
~RedirectChainObserver() override;
void OnChainHandled(const DIPSRedirectChainInfoPtr& chain) override;
void Wait();
size_t handle_call_count = 0;
private:
GURL final_url_;
size_t match_count_ = 0;
size_t expected_match_count_;
base::RunLoop run_loop_;
base::ScopedObservation<DIPSService, Observer> obs_{this};
};
class UserActivationObserver : public content::WebContentsObserver {
public:
explicit UserActivationObserver(content::WebContents* web_contents,
@ -305,14 +283,46 @@ void SimulateMouseClickAndWait(content::WebContents*);
// Make a UrlAndSourceId with a randomly-generated UKM source id.
UrlAndSourceId MakeUrlAndId(std::string_view url);
// Cause DIPS to record a stateful client bounce on `bounce_url` to `final_url`.
// The redirect chain will be started by performing a browser-initiated
// navigation to `initial_url`, and terminated by another such navigation to
// `next_url`.
testing::AssertionResult SimulateDipsBounce(content::WebContents*,
const GURL& initial_url,
const GURL& bounce_url,
const GURL& final_url,
const GURL& next_url);
// A ContentBrowserClient that supports third-party cookie blocking. Note that
// this can only be used directly by unit tests; browser tests must use
// ContentBrowserTestTpcBlockingBrowserClient instead.
class TpcBlockingBrowserClient : public content::ContentBrowserClient {
public:
static constexpr uint64_t DATA_TYPE_HISTORY =
content::BrowsingDataRemover::DATA_TYPE_CONTENT_END << 1;
#endif // CHROME_BROWSER_DIPS_DIPS_TEST_UTILS_H_
TpcBlockingBrowserClient();
~TpcBlockingBrowserClient() override;
void SetBlockThirdPartyCookiesByDefault(bool block) { block_3pcs_ = block; }
bool IsFullCookieAccessAllowed(content::BrowserContext* browser_context,
content::WebContents* web_contents,
const GURL& url,
const blink::StorageKey& storage_key) override;
void GrantCookieAccessDueToHeuristic(content::BrowserContext* browser_context,
const net::SchemefulSite& top_frame_site,
const net::SchemefulSite& accessing_site,
base::TimeDelta ttl,
bool ignore_schemes) override;
std::unique_ptr<content::DipsDelegate> CreateDipsDelegate() override;
void AllowThirdPartyCookiesOnSite(const GURL& url);
void GrantCookieAccessTo3pSite(const GURL& url);
void BlockThirdPartyCookiesOnSite(const GURL& url);
void BlockThirdPartyCookies(const GURL& url, const GURL& first_party_url);
private:
bool block_3pcs_ = false;
std::set<net::SchemefulSite> tpc_1p_site_exceptions_;
std::set<net::SchemefulSite> tpc_3p_site_exceptions_;
std::set<std::pair<std::string, std::string>> schemeless_tpc_exceptions_;
std::set<std::pair<net::SchemefulSite, net::SchemefulSite>> tpc_exceptions_;
std::set<net::SchemefulSite> tpc_1p_blocks_;
std::set<std::pair<net::SchemefulSite, net::SchemefulSite>> tpc_blocks_;
};
#endif // CONTENT_BROWSER_DIPS_DIPS_TEST_UTILS_H_

@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/dips/dips_utils.h"
#include "content/browser/dips/dips_utils.h"
#include <algorithm>
#include <string_view>
@ -17,6 +17,39 @@
#include "url/gurl.h"
#include "url/origin.h"
base::cstring_view DIPSCookieModeToString(DIPSCookieMode mode) {
switch (mode) {
case DIPSCookieMode::kBlock3PC:
return "Block3PC";
case DIPSCookieMode::kOffTheRecord_Block3PC:
return "OffTheRecord_Block3PC";
}
}
base::cstring_view DIPSRedirectTypeToString(DIPSRedirectType type) {
switch (type) {
case DIPSRedirectType::kClient:
return "Client";
case DIPSRedirectType::kServer:
return "Server";
}
}
base::cstring_view DIPSDataAccessTypeToString(DIPSDataAccessType type) {
switch (type) {
case DIPSDataAccessType::kUnknown:
return "Unknown";
case DIPSDataAccessType::kNone:
return "None";
case DIPSDataAccessType::kRead:
return "Read";
case DIPSDataAccessType::kWrite:
return "Write";
case DIPSDataAccessType::kReadWrite:
return "ReadWrite";
}
}
base::FilePath GetDIPSFilePath(content::BrowserContext* context) {
return context->GetPath().Append(kDIPSFilename);
}
@ -59,9 +92,9 @@ std::ostream& operator<<(std::ostream& os, TimestampRange range) {
return os << "[" << range->first << ", " << range->second << "]";
}
// SiteDataAccessType:
std::ostream& operator<<(std::ostream& os, SiteDataAccessType access_type) {
return os << SiteDataAccessTypeToString(access_type);
// DIPSDataAccessType:
std::ostream& operator<<(std::ostream& os, DIPSDataAccessType access_type) {
return os << DIPSDataAccessTypeToString(access_type);
}
// DIPSCookieMode:
@ -105,7 +138,7 @@ std::ostream& operator<<(std::ostream& os, DIPSRedirectType type) {
return os << DIPSRedirectTypeToString(type);
}
int64_t BucketizeBounceDelay(base::TimeDelta delta) {
int64_t BucketizeDIPSBounceDelay(base::TimeDelta delta) {
return std::clamp(delta.InSeconds(), INT64_C(0), INT64_C(10));
}
@ -149,8 +182,6 @@ bool HasSameSiteIframe(content::WebContents* web_contents, const GURL& url) {
return found;
}
const base::TimeDelta kDIPSTimestampUpdateInterval = base::Minutes(1);
bool UpdateTimestamp(std::optional<base::Time>& last_time, base::Time now) {
if (!last_time.has_value() ||
(now - last_time.value()) >= kDIPSTimestampUpdateInterval) {

@ -2,17 +2,19 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_DIPS_DIPS_UTILS_H_
#define CHROME_BROWSER_DIPS_DIPS_UTILS_H_
#ifndef CONTENT_BROWSER_DIPS_DIPS_UTILS_H_
#define CONTENT_BROWSER_DIPS_DIPS_UTILS_H_
#include <optional>
#include <ostream>
#include <string_view>
#include "base/files/file_path.h"
#include "base/strings/cstring_view.h"
#include "base/time/time.h"
#include "chrome/browser/dips/dips_redirect_info.h"
#include "content/common/content_export.h"
#include "content/public/browser/cookie_access_details.h"
#include "content/public/browser/dips_redirect_info.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/page.h"
#include "content/public/browser/render_frame_host.h"
@ -31,8 +33,15 @@ namespace url {
class Origin;
}
// For use in tests/debugging.
CONTENT_EXPORT base::cstring_view DIPSCookieModeToString(DIPSCookieMode mode);
CONTENT_EXPORT base::cstring_view DIPSRedirectTypeToString(
DIPSRedirectType type);
CONTENT_EXPORT base::cstring_view DIPSDataAccessTypeToString(
DIPSDataAccessType type);
// A single cookie-accessing operation (either read or write). Not to be
// confused with SiteDataAccessType, which can also represent no access or both
// confused with DIPSDataAccessType, which can also represent no access or both
// read+write.
using CookieOperation = network::mojom::CookieAccessDetails::Type;
@ -43,19 +52,24 @@ const base::FilePath::CharType kDIPSFilename[] = FILE_PATH_LITERAL("DIPS");
// if one exists.
// NOTE: This returns the same value regardless of if there is actually a
// persisted DIPSDatabase for the BrowserContext or not.
base::FilePath GetDIPSFilePath(content::BrowserContext* context);
CONTENT_EXPORT base::FilePath GetDIPSFilePath(content::BrowserContext* context);
inline SiteDataAccessType ToSiteDataAccessType(CookieOperation op) {
return (op == CookieOperation::kChange ? SiteDataAccessType::kWrite
: SiteDataAccessType::kRead);
inline DIPSDataAccessType ToDIPSDataAccessType(CookieOperation op) {
return (op == CookieOperation::kChange ? DIPSDataAccessType::kWrite
: DIPSDataAccessType::kRead);
}
std::ostream& operator<<(std::ostream& os, SiteDataAccessType access_type);
CONTENT_EXPORT std::ostream& operator<<(std::ostream& os,
DIPSDataAccessType access_type);
constexpr SiteDataAccessType operator|(SiteDataAccessType lhs,
SiteDataAccessType rhs) {
return static_cast<SiteDataAccessType>(static_cast<int>(lhs) |
constexpr DIPSDataAccessType operator|(DIPSDataAccessType lhs,
DIPSDataAccessType rhs) {
return static_cast<DIPSDataAccessType>(static_cast<int>(lhs) |
static_cast<int>(rhs));
}
inline DIPSDataAccessType& operator|=(DIPSDataAccessType& lhs,
DIPSDataAccessType rhs) {
return (lhs = lhs | rhs);
}
DIPSCookieMode GetDIPSCookieMode(bool is_otr);
std::string_view GetHistogramSuffix(DIPSCookieMode mode);
@ -95,14 +109,17 @@ constexpr DIPSEventRemovalType& operator&=(DIPSEventRemovalType& lhs,
}
std::string_view GetHistogramPiece(DIPSRedirectType type);
std::ostream& operator<<(std::ostream& os, DIPSRedirectType type);
CONTENT_EXPORT std::ostream& operator<<(std::ostream& os,
DIPSRedirectType type);
using TimestampRange = std::optional<std::pair<base::Time, base::Time>>;
// Expand the range to include `time` if necessary. Returns true iff the range
// was modified.
bool UpdateTimestampRange(TimestampRange& range, base::Time time);
CONTENT_EXPORT bool UpdateTimestampRange(TimestampRange& range,
base::Time time);
// Checks that `this` range is either null or falls within `other`.
bool IsNullOrWithin(const TimestampRange& inner, const TimestampRange& outer);
CONTENT_EXPORT bool IsNullOrWithin(const TimestampRange& inner,
const TimestampRange& outer);
std::ostream& operator<<(std::ostream& os, TimestampRange type);
@ -129,6 +146,8 @@ struct PopupWithTime {
base::Time last_popup_time;
};
// These values are emitted in metrics and should not be renumbered. This one
// type is used for both of the IsAdTagged and HasSameSiteIframe UKM enums.
enum class OptionalBool {
kUnknown = 0,
kFalse = 1,
@ -148,15 +167,15 @@ inline bool operator==(const StateValue& lhs, const StateValue& rhs) {
rhs.web_authn_assertion_times);
}
// Return the number of seconds in `td`, clamped to [0, 10].
// Return the number of seconds in `delta`, clamped to [0, 10].
// i.e. 11 linearly-sized buckets.
int64_t BucketizeBounceDelay(base::TimeDelta delta);
CONTENT_EXPORT int64_t BucketizeDIPSBounceDelay(base::TimeDelta delta);
// Returns an opaque value representing the "privacy boundary" that the URL
// belongs to. Currently returns eTLD+1, but this is an implementation detail
// and may change.
std::string GetSiteForDIPS(const GURL& url);
std::string GetSiteForDIPS(const url::Origin& origin);
CONTENT_EXPORT std::string GetSiteForDIPS(const GURL& url);
CONTENT_EXPORT std::string GetSiteForDIPS(const url::Origin& origin);
// Returns true iff `web_contents` contains an iframe whose committed URL
// belongs to the same site as `url`.
@ -165,10 +184,11 @@ bool HasSameSiteIframe(content::WebContents* web_contents, const GURL& url);
// Returns whether the provided cookie access was ad-tagged, based on the cookie
// settings overrides. Returns Unknown if kSkipTpcdMitigationsForAdsHeuristics
// is false and the override is not set regardless.
OptionalBool IsAdTaggedCookieForHeuristics(
const content::CookieAccessDetails& details);
CONTENT_EXPORT OptionalBool
IsAdTaggedCookieForHeuristics(const content::CookieAccessDetails& details);
bool HasCHIPS(const net::CookieAccessResultList& cookie_access_result_list);
CONTENT_EXPORT bool HasCHIPS(
const net::CookieAccessResultList& cookie_access_result_list);
// Returns `True` iff the `navigation_handle` represents a navigation
// happening in an iframe of the primary frame tree.
@ -224,10 +244,12 @@ inline std::optional<GURL> GetFirstPartyURL(content::RenderFrameHost* rfh) {
// The amount of time since a page last received user interaction before a
// subsequent user interaction event may be recorded to DIPS Storage for the
// same page.
extern const base::TimeDelta kDIPSTimestampUpdateInterval;
inline constexpr base::TimeDelta kDIPSTimestampUpdateInterval =
base::Minutes(1);
[[nodiscard]] bool UpdateTimestamp(std::optional<base::Time>& last_time,
base::Time now);
[[nodiscard]] CONTENT_EXPORT bool UpdateTimestamp(
std::optional<base::Time>& last_time,
base::Time now);
// DIPSInteractionType is used in UKM to record the way the user interacted with
// the site. It should match CookieHeuristicInteractionType in
@ -244,12 +266,12 @@ enum class DIPSRecordedEvent {
kWebAuthnAssertion,
};
// RedirectCategory is basically the cross-product of SiteDataAccessType and a
// boolean value indicating site engagement. It's used in UMA enum histograms.
// DIPSRedirectCategory is basically the cross-product of DIPSDataAccessType and
// a boolean value indicating site engagement. It's used in UMA enum histograms.
//
// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
enum class RedirectCategory {
enum class DIPSRedirectCategory {
kNoCookies_NoEngagement = 0,
kReadCookies_NoEngagement = 1,
kWriteCookies_NoEngagement = 2,
@ -307,4 +329,4 @@ enum class DIPSDatabaseTable {
kMaxValue = kPopups,
};
#endif // CHROME_BROWSER_DIPS_DIPS_UTILS_H_
#endif // CONTENT_BROWSER_DIPS_DIPS_UTILS_H_

@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/dips/dips_utils.h"
#include "content/browser/dips/dips_utils.h"
#include "base/test/scoped_feature_list.h"
#include "base/time/time.h"
@ -101,23 +101,23 @@ TEST(TimestampRangeTest, IsNullOrWithin_AllowsEquals) {
EXPECT_TRUE(IsNullOrWithin(range, range));
}
TEST(BucketizeBounceDelayTest, BucketizeBounceDelay) {
TEST(BucketizeDIPSBounceDelayTest, BucketizeDIPSBounceDelay) {
// any TimeDelta in (-inf, 1s) should return 0
EXPECT_EQ(0, BucketizeBounceDelay(base::Days(-1)));
EXPECT_EQ(0, BucketizeBounceDelay(base::Milliseconds(0)));
EXPECT_EQ(0, BucketizeBounceDelay(base::Milliseconds(999)));
EXPECT_EQ(0, BucketizeDIPSBounceDelay(base::Days(-1)));
EXPECT_EQ(0, BucketizeDIPSBounceDelay(base::Milliseconds(0)));
EXPECT_EQ(0, BucketizeDIPSBounceDelay(base::Milliseconds(999)));
// anything in [1s, 2s) should return 1
EXPECT_EQ(1, BucketizeBounceDelay(base::Milliseconds(1000)));
EXPECT_EQ(1, BucketizeBounceDelay(base::Milliseconds(1999)));
EXPECT_EQ(1, BucketizeDIPSBounceDelay(base::Milliseconds(1000)));
EXPECT_EQ(1, BucketizeDIPSBounceDelay(base::Milliseconds(1999)));
// similarly for [2s, 3s)
EXPECT_EQ(2, BucketizeBounceDelay(base::Milliseconds(2000)));
EXPECT_EQ(2, BucketizeBounceDelay(base::Milliseconds(2999)));
EXPECT_EQ(2, BucketizeDIPSBounceDelay(base::Milliseconds(2000)));
EXPECT_EQ(2, BucketizeDIPSBounceDelay(base::Milliseconds(2999)));
// ...
EXPECT_EQ(9, BucketizeBounceDelay(base::Milliseconds(9999)));
EXPECT_EQ(9, BucketizeDIPSBounceDelay(base::Milliseconds(9999)));
// anything in [10s, inf) should return 10
EXPECT_EQ(10, BucketizeBounceDelay(base::Milliseconds(10000)));
EXPECT_EQ(10, BucketizeBounceDelay(base::Milliseconds(10001)));
EXPECT_EQ(10, BucketizeBounceDelay(base::Days(1)));
EXPECT_EQ(10, BucketizeDIPSBounceDelay(base::Milliseconds(10000)));
EXPECT_EQ(10, BucketizeDIPSBounceDelay(base::Milliseconds(10001)));
EXPECT_EQ(10, BucketizeDIPSBounceDelay(base::Days(1)));
}
TEST(UpdateTimestampTest, AlwaysReplaceNullOpt) {

@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/dips/persistent_repeating_timer.h"
#include "content/browser/dips/persistent_repeating_timer.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"

@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_DIPS_PERSISTENT_REPEATING_TIMER_H_
#define CHROME_BROWSER_DIPS_PERSISTENT_REPEATING_TIMER_H_
#ifndef CONTENT_BROWSER_DIPS_PERSISTENT_REPEATING_TIMER_H_
#define CONTENT_BROWSER_DIPS_PERSISTENT_REPEATING_TIMER_H_
#include <memory>
#include <optional>
@ -13,6 +13,7 @@
#include "base/memory/weak_ptr.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "content/common/content_export.h"
// We copied this class from
// //components/signin/public/base/persistent_repeating_timer.h in order to
@ -24,9 +25,9 @@ namespace dips {
// This class fires a task repeatedly, across application restarts. The timer
// stores the date of the last invocation in a preference, which is persisted
// to disk.
class PersistentRepeatingTimer {
class CONTENT_EXPORT PersistentRepeatingTimer {
public:
class Storage {
class CONTENT_EXPORT Storage {
public:
using TimeCallback = base::OnceCallback<void(std::optional<base::Time>)>;
virtual ~Storage();
@ -60,4 +61,4 @@ class PersistentRepeatingTimer {
} // namespace dips
#endif // CHROME_BROWSER_DIPS_PERSISTENT_REPEATING_TIMER_H_
#endif // CONTENT_BROWSER_DIPS_PERSISTENT_REPEATING_TIMER_H_

@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/dips/persistent_repeating_timer.h"
#include "content/browser/dips/persistent_repeating_timer.h"
#include <memory>

@ -4,6 +4,12 @@
source_set("unit_tests") {
testonly = true
# See content_unittests for justification.
if (is_component_build) {
check_includes = false
}
sources = [
"opener_heuristic_metrics_unittest.cc",
"opener_heuristic_utils_unittest.cc",
@ -11,7 +17,8 @@ source_set("unit_tests") {
deps = [
"//base",
"//chrome/browser",
"//content/browser:for_content_tests",
"//testing/gtest",
"//url",
]
}

@ -0,0 +1,5 @@
amaliev@chromium.org
jdh@chromium.org
njeunje@chromium.org
rtarpine@chromium.org
wanderview@chromium.org

@ -0,0 +1,5 @@
# Third-Party Cookie Blocking Breakage Mitigation Heuristics
This directory contains code specific to efforts intended to mitigate
site breakage caused by the blocking of third-party cookies, while
prioritizing user privacy.

@ -7,66 +7,67 @@
#include "base/functional/callback_forward.h"
#include "base/memory/raw_ptr.h"
#include "base/metrics/field_trial_params.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/test/bind.h"
#include "base/test/gmock_expected_support.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/simple_test_clock.h"
#include "base/test/test_future.h"
#include "base/test/values_test_util.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "chrome/browser/content_settings/cookie_settings_factory.h"
#include "chrome/browser/dips/dips_service_impl.h"
#include "chrome/browser/dips/dips_storage.h"
#include "chrome/browser/dips/dips_test_utils.h"
#include "chrome/browser/dips/dips_utils.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/subresource_filter/subresource_filter_browser_test_harness.h"
#include "chrome/browser/tpcd/experiment/tpcd_experiment_features.h"
#include "chrome/browser/tpcd/heuristics/opener_heuristic_metrics.h"
#include "chrome/browser/tpcd/heuristics/opener_heuristic_tab_helper.h"
#include "chrome/browser/tpcd/heuristics/opener_heuristic_utils.h"
#include "chrome/test/base/chrome_test_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/features.h"
#include "components/content_settings/core/common/pref_names.h"
#include "components/prefs/pref_service.h"
#include "components/subresource_filter/content/browser/ad_tagging_browser_test_utils.h"
#include "components/subresource_filter/core/common/test_ruleset_utils.h"
#include "components/ukm/content/source_url_recorder.h"
#include "components/ukm/test_ukm_recorder.h"
#include "content/browser/dips/dips_browsertest_utils.h"
#include "content/browser/dips/dips_service_impl.h"
#include "content/browser/dips/dips_storage.h"
#include "content/browser/dips/dips_test_utils.h"
#include "content/browser/dips/dips_utils.h"
#include "content/browser/tpcd_heuristics/opener_heuristic_metrics.h"
#include "content/browser/tpcd_heuristics/opener_heuristic_tab_helper.h"
#include "content/browser/tpcd_heuristics/opener_heuristic_utils.h"
#include "content/common/features.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/browser/dips_delegate.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/common/content_client.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/content_browser_test.h"
#include "content/public/test/content_browser_test_content_browser_client.h"
#include "content/public/test/hit_test_region_observer.h"
#include "content/public/test/render_frame_host_test_support.h"
#include "content/public/test/test_devtools_protocol_client.h"
#include "content/public/test/test_navigation_observer.h"
#include "content/shell/browser/shell.h"
#include "net/cookies/site_for_cookies.h"
#include "net/dns/mock_host_resolver.h"
#include "services/metrics/public/cpp/ukm_source.h"
#include "services/metrics/public/cpp/ukm_source_id.h"
#include "services/network/public/cpp/features.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/storage_key/storage_key.h"
#include "third_party/blink/public/common/switches.h"
#include "ui/base/window_open_disposition.h"
#if !BUILDFLAG(IS_ANDROID)
#include "chrome/browser/ui/browser.h"
#endif // !BUILDFLAG(IS_ANDROID)
using base::test::HasValue;
using base::test::ValueIs;
using content::NavigationHandle;
using content::RenderFrameHost;
using content::WebContents;
using content::WebContentsObserver;
using content_settings::features::EnableForIframeTypes;
using testing::ElementsAre;
using testing::Field;
using testing::Optional;
using testing::Pair;
using tpcd::experiment::EnableForIframeTypes;
namespace {
@ -75,7 +76,7 @@ struct AccessGrantTestCase {
bool disable_for_ad_tagged_popups = false;
};
const AccessGrantTestCase kAccessGrantTestCases[] = {
[[maybe_unused]] const AccessGrantTestCase kAccessGrantTestCases[] = {
{.write_grant_enabled = false, .disable_for_ad_tagged_popups = false},
{.write_grant_enabled = true, .disable_for_ad_tagged_popups = false},
{.write_grant_enabled = true, .disable_for_ad_tagged_popups = true}};
@ -160,32 +161,29 @@ class NavigationFinishObserver : public WebContentsObserver {
// SubresourceFilterBrowserTest is necessary to test ad-tagging related
// behaviors.
class OpenerHeuristicBrowserTest
: public subresource_filter::SubresourceFilterBrowserTest,
public content::TestDevToolsProtocolClient {
class OpenerHeuristicBrowserTest : public content::ContentBrowserTest,
public content::TestDevToolsProtocolClient {
public:
OpenerHeuristicBrowserTest()
: https_server_(net::EmbeddedTestServer::TYPE_HTTPS) {
// We host the "images" on an HTTPS server, because for it to write a
// cookie, the cookie needs to be SameSite=None and Secure.
https_server_.SetSSLConfig(net::EmbeddedTestServer::CERT_TEST_NAMES);
https_server_.AddDefaultHandlers(
base::FilePath(FILE_PATH_LITERAL("chrome/test/data")));
https_server_.AddDefaultHandlers(GetTestDataFilePath());
}
void SetUp() override {
tpcd_heuristics_grants_params_["TpcdReadHeuristicsGrants"] = "true";
feature_list_.InitWithFeaturesAndParameters(
{{content_settings::features::kTpcdHeuristicsGrants,
tpcd_heuristics_grants_params_},
{network::features::kSkipTpcdMitigationsForAds,
{{"SkipTpcdMitigationsForAdsHeuristics", "true"}}},
{content_settings::features::kTrackingProtection3pcd, {}}},
{
{content_settings::features::kTpcdHeuristicsGrants,
tpcd_heuristics_grants_params_},
{network::features::kSkipTpcdMitigationsForAds,
{{"SkipTpcdMitigationsForAdsHeuristics", "true"}}},
},
{});
OpenerHeuristicTabHelper::SetClockForTesting(&clock_);
PlatformBrowserTest::SetUp();
content::ContentBrowserTest::SetUp();
}
void SetUpCommandLine(base::CommandLine* command_line) override {
@ -194,21 +192,31 @@ class OpenerHeuristicBrowserTest
}
void SetUpOnMainThread() override {
SubresourceFilterBrowserTest::SetUpOnMainThread();
content::ContentBrowserTest::SetUpOnMainThread();
ASSERT_TRUE(https_server_.Start());
ASSERT_TRUE(embedded_test_server()->Start());
host_resolver()->AddRule("*", "127.0.0.1");
// These rules apply an ad-tagging param to scripts in ad_script.js,
// and to cookies marked with the `isad=1` param value.
SetRulesetWithRules(
{subresource_filter::testing::CreateSuffixRule("ad_script.js"),
subresource_filter::testing::CreateSuffixRule("isad=1")});
// TODO: crbug.com/376625002 - disabled for the move to //content since
// SubresourceFilterBrowserTest is unavailable. Either find a way to
// implement this test in //content or move it back to //chrome.
//
// // These rules apply an ad-tagging param to scripts in ad_script.js,
// // and to cookies marked with the `isad=1` param value.
// SetRulesetWithRules(
// {subresource_filter::testing::CreateSuffixRule("ad_script.js"),
// subresource_filter::testing::CreateSuffixRule("isad=1")});
DIPSServiceImpl::Get(GetActiveWebContents()->GetBrowserContext())
->SetStorageClockForTesting(&clock_);
ukm::InitializeSourceUrlRecorderForWebContents(GetActiveWebContents());
ASSERT_TRUE(
content::NavigateToURL(GetActiveWebContents(), GURL("about:blank")));
// Open and reset DevTools.
AttachToWebContents(chrome_test_utils::GetActiveWebContents(this));
AttachToWebContents(GetActiveWebContents());
SendCommandSync("Audits.enable");
ClearNotifications();
}
@ -220,7 +228,7 @@ class OpenerHeuristicBrowserTest
}
content::WebContents* GetActiveWebContents() {
return chrome_test_utils::GetActiveWebContents(this);
return shell()->web_contents();
}
OpenerHeuristicTabHelper* GetTabHelper() {
@ -250,6 +258,12 @@ class OpenerHeuristicBrowserTest
// Open a popup window with the given URL and return its WebContents.
base::expected<WebContents*, std::string> OpenPopup(const GURL& url) {
auto* web_contents = GetActiveWebContents();
if (web_contents->GetLastCommittedURL().is_empty()) {
// We can't call window.open() if we're not on a page. Go to about:blank.
if (!content::NavigateToURL(web_contents, GURL("about:blank"))) {
return base::unexpected("failed to navigate to about:blank");
}
}
PopupObserver observer(web_contents);
if (!content::ExecJs(
web_contents,
@ -293,7 +307,7 @@ class OpenerHeuristicBrowserTest
const GURL& url) {
content::TestNavigationObserver load_observer(GetActiveWebContents());
std::string script = base::StringPrintf(
"var iframe = document.getElementById('test');iframe.src='%s';",
"var iframe = document.getElementById('test_iframe');iframe.src='%s';",
url.spec().c_str());
if (!content::ExecJs(parent_frame, script,
content::EXECUTE_SCRIPT_NO_USER_GESTURE)) {
@ -479,7 +493,7 @@ class OpenerHeuristicIframeInitiatorBrowserTest
: iframe_types_flag_(std::get<0>(GetParam())),
is_nested_iframe_(std::get<1>(GetParam())) {
for (const auto [val, str] :
tpcd::experiment::kEnableForIframeTypesOptions) {
content_settings::features::kEnableForIframeTypesOptions) {
if (val == iframe_types_flag_) {
tpcd_heuristics_grants_params_
["TpcdPopupHeuristicEnableForIframeInitiator"] = str;
@ -496,7 +510,7 @@ IN_PROC_BROWSER_TEST_P(
URLsInitiatedByFirstPartyIframes_HavePopupStateWithFlag) {
WebContents* web_contents = GetActiveWebContents();
const GURL opener_primary_frame_url =
embedded_test_server()->GetURL("a.test", "/iframe_blank.html");
embedded_test_server()->GetURL("a.test", "/page_with_blank_iframe.html");
const GURL opener_iframe_url =
embedded_test_server()->GetURL("a.test", "/title1.html");
GURL popup_url = embedded_test_server()->GetURL("b.test", "/title1.html");
@ -532,9 +546,9 @@ IN_PROC_BROWSER_TEST_P(
URLsInitiatedByThirdPartyIframes_HavePopupStateWithFlag) {
WebContents* web_contents = GetActiveWebContents();
const GURL opener_1p_frame_url =
embedded_test_server()->GetURL("a.test", "/iframe_blank.html");
embedded_test_server()->GetURL("a.test", "/page_with_blank_iframe.html");
const GURL opener_3p_frame_url =
embedded_test_server()->GetURL("b.test", "/iframe_blank.html");
embedded_test_server()->GetURL("b.test", "/page_with_blank_iframe.html");
GURL popup_url = embedded_test_server()->GetURL("b.test", "/title1.html");
ASSERT_TRUE(content::NavigateToURL(web_contents, opener_1p_frame_url));
@ -654,13 +668,19 @@ class OpenerHeuristicMultiplePastInteractionTypesBrowserTest
void SetUpOnMainThread() override {
OpenerHeuristicBrowserTest::SetUpOnMainThread();
browser()->profile()->GetPrefs()->SetInteger(
prefs::kCookieControlsMode,
static_cast<int>(
content_settings::CookieControlsMode::kBlockThirdParty));
browser()->profile()->GetPrefs()->SetBoolean(
prefs::kTrackingProtection3pcdEnabled, true);
browser_client_.emplace();
browser_client()->SetBlockThirdPartyCookiesByDefault(true);
}
TpcBlockingBrowserClient* browser_client() {
return &browser_client_->impl();
}
private:
// browser_client_ is wrapped in optional<> to delay construction -- it won't
// be registered properly if it's created too early.
std::optional<content::ContentBrowserTestTpcBlockingBrowserClient>
browser_client_;
};
IN_PROC_BROWSER_TEST_P(OpenerHeuristicMultiplePastInteractionTypesBrowserTest,
@ -712,15 +732,32 @@ class OpenerHeuristicPastInteractionGrantBrowserTest
void SetUpOnMainThread() override {
OpenerHeuristicBrowserTest::SetUpOnMainThread();
browser()->profile()->GetPrefs()->SetInteger(
prefs::kCookieControlsMode,
static_cast<int>(
content_settings::CookieControlsMode::kBlockThirdParty));
browser()->profile()->GetPrefs()->SetBoolean(
prefs::kTrackingProtection3pcdEnabled, true);
browser_client_.emplace();
browser_client()->SetBlockThirdPartyCookiesByDefault(true);
}
TpcBlockingBrowserClient* browser_client() {
return &browser_client_->impl();
}
private:
std::optional<content::ContentBrowserTestTpcBlockingBrowserClient>
browser_client_;
};
namespace {
bool IsFullCookieAccessAllowed(WebContents* web_contents,
const GURL& url,
const GURL& first_party_url) {
return content::GetContentClientForTesting()
->browser()
->IsFullCookieAccessAllowed(web_contents->GetBrowserContext(),
web_contents, url,
blink::StorageKey::CreateFirstParty(
url::Origin::Create(first_party_url)));
}
} // namespace
IN_PROC_BROWSER_TEST_P(OpenerHeuristicPastInteractionGrantBrowserTest,
PopupPastInteractionIsReported_WithStorageAccessGrant) {
GURL opener_url = embedded_test_server()->GetURL("a.test", "/title1.html");
@ -734,29 +771,18 @@ IN_PROC_BROWSER_TEST_P(OpenerHeuristicPastInteractionGrantBrowserTest,
// Expect that cookie access was granted for the Popup With Past Interaction
// heuristic, if the feature is enabled.
auto cookie_settings = CookieSettingsFactory::GetForProfile(
Profile::FromBrowserContext(GetActiveWebContents()->GetBrowserContext()));
EXPECT_EQ(cookie_settings->GetCookieSetting(
initial_url, net::SiteForCookies(), opener_url,
net::CookieSettingOverrides(), nullptr),
GetParam().write_grant_enabled ? CONTENT_SETTING_ALLOW
: CONTENT_SETTING_BLOCK);
EXPECT_EQ(cookie_settings->GetThirdPartyCookieAllowMechanism(
initial_url, net::SiteForCookies::FromUrl(opener_url),
opener_url, net::CookieSettingOverrides(), nullptr),
GetParam().write_grant_enabled
? content_settings::CookieSettingsBase::
ThirdPartyCookieAllowMechanism::kAllowBy3PCDHeuristics
: content_settings::CookieSettingsBase::
ThirdPartyCookieAllowMechanism::kNone);
EXPECT_EQ(IsFullCookieAccessAllowed(GetActiveWebContents(), initial_url,
opener_url),
GetParam().write_grant_enabled);
// Cookie access was NOT granted for the site that the popup redirected to.
EXPECT_EQ(cookie_settings->GetCookieSetting(
final_url, net::SiteForCookies(), opener_url,
net::CookieSettingOverrides(), nullptr),
CONTENT_SETTING_BLOCK);
EXPECT_FALSE(
IsFullCookieAccessAllowed(GetActiveWebContents(), final_url, opener_url));
}
// TODO: crbug.com/376625002 - disabled for the move to //content since
// SubresourceFilterBrowserTest is unavailable. Either find a way to implement
// this test in //content or move it back to //chrome.
//
// TODO(crbug.com/40947612) Flaky on mac.
#if BUILDFLAG(IS_MAC)
#define MAYBE_AdTaggedPopupPastInteractionIsReported_WithStorageAccessGrant \
@ -767,7 +793,7 @@ IN_PROC_BROWSER_TEST_P(OpenerHeuristicPastInteractionGrantBrowserTest,
#endif
IN_PROC_BROWSER_TEST_P(
OpenerHeuristicPastInteractionGrantBrowserTest,
MAYBE_AdTaggedPopupPastInteractionIsReported_WithStorageAccessGrant) {
DISABLED_AdTaggedPopupPastInteractionIsReported_WithStorageAccessGrant) {
GURL opener_url =
embedded_test_server()->GetURL("a.com", "/ad_tagging/frame_factory.html");
GURL popup_url = embedded_test_server()->GetURL("c.com", "/title1.html");
@ -779,13 +805,9 @@ IN_PROC_BROWSER_TEST_P(
// Interaction heuristic, only if the flag is *off*.
bool should_cookies_be_blocked = !GetParam().write_grant_enabled ||
GetParam().disable_for_ad_tagged_popups;
auto cookie_settings = CookieSettingsFactory::GetForProfile(
Profile::FromBrowserContext(GetActiveWebContents()->GetBrowserContext()));
EXPECT_EQ(cookie_settings->GetCookieSetting(
popup_url, net::SiteForCookies(), opener_url,
net::CookieSettingOverrides(), nullptr),
should_cookies_be_blocked ? CONTENT_SETTING_BLOCK
: CONTENT_SETTING_ALLOW);
EXPECT_EQ(
IsFullCookieAccessAllowed(GetActiveWebContents(), popup_url, opener_url),
!should_cookies_be_blocked);
}
INSTANTIATE_TEST_SUITE_P(All,
@ -913,15 +935,20 @@ class OpenerHeuristicInteractionTypesBrowserTest
void SetUpOnMainThread() override {
OpenerHeuristicBrowserTest::SetUpOnMainThread();
browser()->profile()->GetPrefs()->SetInteger(
prefs::kCookieControlsMode,
static_cast<int>(
content_settings::CookieControlsMode::kBlockThirdParty));
browser()->profile()->GetPrefs()->SetBoolean(
prefs::kTrackingProtection3pcdEnabled, true);
browser_client_.emplace();
browser_client()->SetBlockThirdPartyCookiesByDefault(true);
}
TpcBlockingBrowserClient* browser_client() {
return &browser_client_->impl();
}
base::Time Hours;
base::Time UserAuthenticationTime;
private:
std::optional<content::ContentBrowserTestTpcBlockingBrowserClient>
browser_client_;
};
INSTANTIATE_TEST_SUITE_P(All,
@ -933,8 +960,7 @@ IN_PROC_BROWSER_TEST_P(OpenerHeuristicInteractionTypesBrowserTest,
ukm::TestAutoSetUkmRecorder ukm_recorder;
GURL opener_url = embedded_test_server()->GetURL("a.test", "/title1.html");
GURL popup_url = embedded_test_server()->GetURL("b.test", "/title1.html");
CookieSettingsFactory::GetForProfile(browser()->profile())
->SetThirdPartyCookieSetting(opener_url, CONTENT_SETTING_ALLOW);
browser_client()->AllowThirdPartyCookiesOnSite(opener_url);
// Initialize interaction and popup.
RecordPastInteraction(popup_url, clock_.Now() - base::Hours(3));
@ -989,8 +1015,11 @@ IN_PROC_BROWSER_TEST_P(OpenerHeuristicInteractionTypesBrowserTest,
opener_url);
EXPECT_EQ(access_entries[0].metrics["AccessId"], access_id);
EXPECT_EQ(access_entries[0].metrics["AccessSucceeded"], true);
EXPECT_EQ(access_entries[0].metrics["IsAdTagged"],
static_cast<int32_t>(OptionalBool::kTrue));
// TODO: crbug.com/376625002 - disabled for the move to //content since
// SubresourceFilterBrowserTest is unavailable. Either find a way to implement
// this test in //content or move it back to //chrome.
// EXPECT_EQ(access_entries[0].metrics["IsAdTagged"],
// static_cast<int32_t>(OptionalBool::kTrue));
EXPECT_EQ(access_entries[0].metrics["HoursSincePopupOpened"], 0);
}
@ -1054,13 +1083,17 @@ class OpenerHeuristicCurrentInteractionGrantBrowserTest
void SetUpOnMainThread() override {
OpenerHeuristicBrowserTest::SetUpOnMainThread();
browser()->profile()->GetPrefs()->SetInteger(
prefs::kCookieControlsMode,
static_cast<int>(
content_settings::CookieControlsMode::kBlockThirdParty));
browser()->profile()->GetPrefs()->SetBoolean(
prefs::kTrackingProtection3pcdEnabled, true);
browser_client_.emplace();
browser_client()->SetBlockThirdPartyCookiesByDefault(true);
}
TpcBlockingBrowserClient* browser_client() {
return &browser_client_->impl();
}
private:
std::optional<content::ContentBrowserTestTpcBlockingBrowserClient>
browser_client_;
};
IN_PROC_BROWSER_TEST_P(OpenerHeuristicCurrentInteractionGrantBrowserTest,
@ -1075,25 +1108,23 @@ IN_PROC_BROWSER_TEST_P(OpenerHeuristicCurrentInteractionGrantBrowserTest,
clock_.Advance(base::Minutes(1));
SimulateMouseClick(popup);
auto cookie_settings = CookieSettingsFactory::GetForProfile(
Profile::FromBrowserContext(GetActiveWebContents()->GetBrowserContext()));
// Cookie access was NOT granted for the initial URL (that the user didn't
// interact with).
EXPECT_EQ(cookie_settings->GetCookieSetting(
initial_url, net::SiteForCookies(), opener_url,
net::CookieSettingOverrides(), nullptr),
CONTENT_SETTING_BLOCK);
EXPECT_FALSE(IsFullCookieAccessAllowed(GetActiveWebContents(), initial_url,
opener_url));
// Cookie access WAS granted for the interacted-with URL (if the feature was
// enabled).
EXPECT_EQ(cookie_settings->GetCookieSetting(
final_url, net::SiteForCookies(), opener_url,
net::CookieSettingOverrides(), nullptr),
GetParam().write_grant_enabled ? CONTENT_SETTING_ALLOW
: CONTENT_SETTING_BLOCK);
EXPECT_EQ(
IsFullCookieAccessAllowed(GetActiveWebContents(), final_url, opener_url),
GetParam().write_grant_enabled);
}
IN_PROC_BROWSER_TEST_P(OpenerHeuristicCurrentInteractionGrantBrowserTest,
AdTaggedPopupInteractionWithStorageAccessGrant) {
// TODO: crbug.com/376625002 - disabled for the move to //content since
// SubresourceFilterBrowserTest is unavailable. Either find a way to implement
// this test in //content or move it back to //chrome.
IN_PROC_BROWSER_TEST_P(
OpenerHeuristicCurrentInteractionGrantBrowserTest,
DISABLED_AdTaggedPopupInteractionWithStorageAccessGrant) {
GURL opener_url =
embedded_test_server()->GetURL("a.com", "/ad_tagging/frame_factory.html");
GURL popup_url = embedded_test_server()->GetURL("c.com", "/title1.html");
@ -1107,13 +1138,9 @@ IN_PROC_BROWSER_TEST_P(OpenerHeuristicCurrentInteractionGrantBrowserTest,
// Interaction heuristic, only if the flag is *off*.
bool should_cookies_be_blocked = !GetParam().write_grant_enabled ||
GetParam().disable_for_ad_tagged_popups;
auto cookie_settings = CookieSettingsFactory::GetForProfile(
Profile::FromBrowserContext(GetActiveWebContents()->GetBrowserContext()));
EXPECT_EQ(cookie_settings->GetCookieSetting(
popup_url, net::SiteForCookies(), opener_url,
net::CookieSettingOverrides(), nullptr),
should_cookies_be_blocked ? CONTENT_SETTING_BLOCK
: CONTENT_SETTING_ALLOW);
EXPECT_EQ(
IsFullCookieAccessAllowed(GetActiveWebContents(), popup_url, opener_url),
!should_cookies_be_blocked);
}
INSTANTIATE_TEST_SUITE_P(All,
@ -1203,8 +1230,7 @@ IN_PROC_BROWSER_TEST_P(
GURL popup_url_2 =
embedded_test_server()->GetURL("b.test", "/server-redirect?title1.html");
GURL popup_url_3 = embedded_test_server()->GetURL("b.test", "/title1.html");
CookieSettingsFactory::GetForProfile(browser()->profile())
->SetThirdPartyCookieSetting(opener_url, CONTENT_SETTING_ALLOW);
browser_client()->AllowThirdPartyCookiesOnSite(opener_url);
// Initialize popup and interaction.
ASSERT_TRUE(content::NavigateToURL(GetActiveWebContents(), opener_url));
@ -1268,6 +1294,10 @@ IN_PROC_BROWSER_TEST_P(
}
// TODO(https://crbug.com/40933721): flaky on Mac.
//
// TODO: crbug.com/376625002 - disabled for the move to //content since the
// DevTools integration is still only in //chrome. Either find a way to
// implement this test in //content or move it back to //chrome.
#if BUILDFLAG(IS_MAC)
#define MAYBE_PopupInteraction_CookieAccessEmitsDevtoolsWarning \
DISABLED_PopupInteraction_CookieAccessEmitsDevtoolsWarning
@ -1277,7 +1307,7 @@ IN_PROC_BROWSER_TEST_P(
#endif
IN_PROC_BROWSER_TEST_F(
OpenerHeuristicBrowserTest,
MAYBE_PopupInteraction_CookieAccessEmitsDevtoolsWarning) {
DISABLED_PopupInteraction_CookieAccessEmitsDevtoolsWarning) {
GURL opener_url = https_server_.GetURL("a.test", "/title1.html");
GURL popup_url_1 = https_server_.GetURL("c.test", "/title1.html");
GURL popup_url_2 =
@ -1343,11 +1373,11 @@ IN_PROC_BROWSER_TEST_F(OpenerHeuristicBrowserTest,
TopLevelIsReported_HasSameSiteIframe) {
ukm::TestAutoSetUkmRecorder ukm_recorder;
GURL toplevel_url =
embedded_test_server()->GetURL("a.test", "/iframe_blank.html");
embedded_test_server()->GetURL("a.test", "/page_with_blank_iframe.html");
GURL iframe_url =
embedded_test_server()->GetURL("sub.b.test", "/title1.html");
GURL popup_url = embedded_test_server()->GetURL("b.test", "/title1.html");
const std::string iframe_id = "test";
const std::string iframe_id = "test_iframe";
WebContents* web_contents = GetActiveWebContents();
RecordUserActivationInteraction(GURL("https://b.test"),
@ -1441,6 +1471,10 @@ IN_PROC_BROWSER_TEST_F(OpenerHeuristicBrowserTest, TopLevel_PopupId) {
EXPECT_NE(popup_id, popup_id2);
}
// TODO: crbug.com/376625002 - disabled for the move to //content since
// SubresourceFilterBrowserTest is unavailable. Either find a way to implement
// this test in //content or move it back to //chrome.
//
// TODO(crbug.com/41484288): Flaky on mac.
#if BUILDFLAG(IS_MAC)
#define MAYBE_TopLevel_PastInteraction_AdTagged \
@ -1450,7 +1484,7 @@ IN_PROC_BROWSER_TEST_F(OpenerHeuristicBrowserTest, TopLevel_PopupId) {
TopLevel_PastInteraction_AdTagged
#endif
IN_PROC_BROWSER_TEST_F(OpenerHeuristicBrowserTest,
MAYBE_TopLevel_PastInteraction_AdTagged) {
DISABLED_TopLevel_PastInteraction_AdTagged) {
ukm::TestAutoSetUkmRecorder ukm_recorder;
GURL toplevel_url =
embedded_test_server()->GetURL("a.com", "/ad_tagging/frame_factory.html");
@ -1473,8 +1507,11 @@ IN_PROC_BROWSER_TEST_F(OpenerHeuristicBrowserTest,
EXPECT_EQ(entries[0].metrics["IsAdTaggedPopupClick"], true);
}
// TODO: crbug.com/376625002 - disabled for the move to //content since
// SubresourceFilterBrowserTest is unavailable. Either find a way to implement
// this test in //content or move it back to //chrome.
IN_PROC_BROWSER_TEST_F(OpenerHeuristicBrowserTest,
TopLevel_CurrentInteraction_AdTagged) {
DISABLED_TopLevel_CurrentInteraction_AdTagged) {
ukm::TestAutoSetUkmRecorder ukm_recorder;
GURL toplevel_url =
embedded_test_server()->GetURL("a.com", "/ad_tagging/frame_factory.html");
@ -1558,7 +1595,7 @@ class OpenerHeuristicBackfillGrantBrowserTest
public:
OpenerHeuristicBackfillGrantBrowserTest() {
tpcd_heuristics_grants_params_["TpcdBackfillPopupHeuristicsGrants"] =
GetParam() ? "10m" : "0s";
GetParam() ? "1us" : "0s";
tpcd_heuristics_grants_params_
["TpcdWritePopupCurrentInteractionHeuristicsGrants"] = "0s";
}
@ -1568,11 +1605,17 @@ class OpenerHeuristicBackfillGrantBrowserTest
clock_.SetNow(base::Time::Now());
browser()->profile()->GetPrefs()->SetInteger(
prefs::kCookieControlsMode,
static_cast<int>(
content_settings::CookieControlsMode::kBlockThirdParty));
browser_client_.emplace();
browser_client()->SetBlockThirdPartyCookiesByDefault(true);
}
TpcBlockingBrowserClient* browser_client() {
return &browser_client_->impl();
}
private:
std::optional<content::ContentBrowserTestTpcBlockingBrowserClient>
browser_client_;
};
// Test the backfill grants created by OpenerHeuristicService when tracking
@ -1604,46 +1647,36 @@ IN_PROC_BROWSER_TEST_P(OpenerHeuristicBackfillGrantBrowserTest,
clock_.Advance(base::Minutes(1));
SimulateMouseClick(popup);
// The pref is updated when the user in onboarded to 3PCD tracking protection,
// and a PrefChangeRegistrar updates the TrackingProtectionSettingsObservers.
browser()->profile()->GetPrefs()->SetBoolean(
prefs::kTrackingProtection3pcdEnabled, true);
GetDipsService()->storage()->FlushPostedTasksForTesting();
base::test::TestFuture<bool> backfill_done;
GetActiveWebContents()->GetBrowserContext()->BackfillPopupHeuristicGrants(
backfill_done.GetCallback());
ASSERT_EQ(backfill_done.Get(), GetParam());
// Expect that a cookie access grant is not backfilled for popup_url_1 or
// popup_url_2.
auto cookie_settings = CookieSettingsFactory::GetForProfile(
Profile::FromBrowserContext(GetActiveWebContents()->GetBrowserContext()));
EXPECT_EQ(cookie_settings->GetCookieSetting(
popup_url_1, net::SiteForCookies(), opener_url,
net::CookieSettingOverrides(), nullptr),
CONTENT_SETTING_BLOCK);
EXPECT_EQ(cookie_settings->GetCookieSetting(
popup_url_2, net::SiteForCookies(), opener_url,
net::CookieSettingOverrides(), nullptr),
CONTENT_SETTING_BLOCK);
EXPECT_FALSE(IsFullCookieAccessAllowed(GetActiveWebContents(), popup_url_1,
opener_url));
EXPECT_FALSE(IsFullCookieAccessAllowed(GetActiveWebContents(), popup_url_2,
opener_url));
// Expect that a cookie access grant is backfilled for popup_url_3 when the
// experiment is enabled.
EXPECT_EQ(cookie_settings->GetCookieSetting(
popup_url_3, net::SiteForCookies(), opener_url,
net::CookieSettingOverrides(), nullptr),
GetParam() ? CONTENT_SETTING_ALLOW : CONTENT_SETTING_BLOCK);
EXPECT_EQ(IsFullCookieAccessAllowed(GetActiveWebContents(), popup_url_3,
opener_url),
GetParam());
// Expect that the cookie access grant applies to other URLs with the same
// eTLD+1.
GURL popup_url_3a =
embedded_test_server()->GetURL("www.d.test", "/favicon.png");
EXPECT_EQ(cookie_settings->GetCookieSetting(
popup_url_3a, net::SiteForCookies(), opener_url,
net::CookieSettingOverrides(), nullptr),
GetParam() ? CONTENT_SETTING_ALLOW : CONTENT_SETTING_BLOCK);
EXPECT_EQ(IsFullCookieAccessAllowed(GetActiveWebContents(), popup_url_3a,
opener_url),
GetParam());
GURL popup_url_3b =
embedded_test_server()->GetURL("corp.d.test", "/title1.html");
EXPECT_EQ(cookie_settings->GetCookieSetting(
popup_url_3b, net::SiteForCookies(), opener_url,
net::CookieSettingOverrides(), nullptr),
GetParam() ? CONTENT_SETTING_ALLOW : CONTENT_SETTING_BLOCK);
EXPECT_EQ(IsFullCookieAccessAllowed(GetActiveWebContents(), popup_url_3b,
opener_url),
GetParam());
}
INSTANTIATE_TEST_SUITE_P(All,

@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/tpcd/heuristics/opener_heuristic_metrics.h"
#include "content/browser/tpcd_heuristics/opener_heuristic_metrics.h"
#include <algorithm>
#include <cstdint>

@ -0,0 +1,17 @@
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CONTENT_BROWSER_TPCD_HEURISTICS_OPENER_HEURISTIC_METRICS_H_
#define CONTENT_BROWSER_TPCD_HEURISTICS_OPENER_HEURISTIC_METRICS_H_
#include <stdint.h>
#include "content/common/content_export.h"
// Bucketize `sample` into 50 buckets, capped at maximum and distributed
// non-linearly similarly to base::Histogram::InitializeBucketRanges.
CONTENT_EXPORT int32_t Bucketize3PCDHeuristicSample(int64_t sample,
int64_t maximum);
#endif // CONTENT_BROWSER_TPCD_HEURISTICS_OPENER_HEURISTIC_METRICS_H_

@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/tpcd/heuristics/opener_heuristic_metrics.h"
#include "content/browser/tpcd_heuristics/opener_heuristic_metrics.h"
#include "base/functional/bind.h"
#include "base/functional/callback_forward.h"

Some files were not shown because too many files have changed in this diff Show More