Commit 4f9f833e authored by Yao Xiao's avatar Yao Xiao Committed by Commit Bot

Add the floc blocklist filtering function behind a feature flag

When the feature is enabled: the 1st floc computation condition will
not only be first-sync-enabled-seen, but would also require
first-blocklist-loaded-seen, as both conditions are likely to occur
after a while after browser start. When floc is computed from history,
invalidate it if it's in the blocklist; invalidate any floc when the
blocklist is not yet loaded / has error in loading.


Bug: 1062736
Change-Id: I81dcfc27b0d2a7023ba1559d30131de41d5842ae
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2365396Reviewed-by: default avatarJosh Karlin <jkarlin@chromium.org>
Commit-Queue: Yao Xiao <yaoxia@chromium.org>
Cr-Commit-Position: refs/heads/master@{#800295}
parent f7844638
......@@ -7,6 +7,7 @@
#include "base/strings/strcat.h"
#include "base/test/bind_test_util.h"
#include "base/test/scoped_feature_list.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chrome_content_browser_client.h"
#include "chrome/browser/content_settings/cookie_settings_factory.h"
#include "chrome/browser/federated_learning/floc_id_provider_factory.h"
......@@ -115,7 +116,9 @@ class FlocIdProviderWithCustomizedServicesBrowserTest
public:
FlocIdProviderWithCustomizedServicesBrowserTest() {
scoped_feature_list_.InitWithFeatures(
{features::kFlocIdComputedEventLogging}, {});
{features::kFlocIdComputedEventLogging,
features::kFlocIdBlocklistFiltering},
{});
}
// BrowserTestBase::SetUpInProcessBrowserTestFixture
......@@ -204,6 +207,11 @@ class FlocIdProviderWithCustomizedServicesBrowserTest
run_loop.Run();
}
void OnBlocklistLoaded(const std::unordered_set<uint64_t>& blocklist) {
g_browser_process->floc_blocklist_service()->OnBlocklistLoadResult(
blocklist);
}
history::HistoryService* history_service() {
return HistoryServiceFactory::GetForProfile(
browser()->profile(), ServiceAccessType::IMPLICIT_ACCESS);
......@@ -289,10 +297,13 @@ IN_PROC_BROWSER_TEST_F(FlocIdProviderWithCustomizedServicesBrowserTest,
EXPECT_EQ(GetFlocId().ToDebugHeaderValue(), FlocId().ToDebugHeaderValue());
// Turn on sync-history to trigger the 1st floc computation.
// Turn on sync-history and load the blocklist to trigger the 1st floc
// computation.
sync_service()->SetActiveDataTypes(syncer::ModelTypeSet::All());
sync_service()->FireStateChanged();
OnBlocklistLoaded({});
FinishOutstandingRemotePermissionQueries();
FinishOutstandingHistoryQueries();
......@@ -324,17 +335,19 @@ IN_PROC_BROWSER_TEST_F(FlocIdProviderWithCustomizedServicesBrowserTest,
EXPECT_EQ(GetFlocId().ToDebugHeaderValue(), FlocId().ToDebugHeaderValue());
// Turn on sync-history to trigger the 1st floc computation.
// Turn on sync-history and load the blocklist to trigger the 1st floc
// computation.
sync_service()->SetActiveDataTypes(syncer::ModelTypeSet::All());
sync_service()->FireStateChanged();
OnBlocklistLoaded({});
FinishOutstandingRemotePermissionQueries();
FinishOutstandingHistoryQueries();
// Expect that the FlocIdComputed user event is not recorded.
ASSERT_EQ(0u, user_event_service()->GetRecordedUserEvents().size());
}
IN_PROC_BROWSER_TEST_F(FlocIdProviderWithCustomizedServicesBrowserTest,
HistoryDeleteRecomputeFloc) {
net::IPAddress::ConsiderLoopbackIPToBePubliclyRoutableForTesting();
......@@ -349,10 +362,13 @@ IN_PROC_BROWSER_TEST_F(FlocIdProviderWithCustomizedServicesBrowserTest,
EXPECT_EQ(GetFlocId().ToDebugHeaderValue(), FlocId().ToDebugHeaderValue());
// Turn on sync-history to trigger the 1st floc computation.
// Turn on sync-history and load the blocklist to trigger the 1st floc
// computation.
sync_service()->SetActiveDataTypes(syncer::ModelTypeSet::All());
sync_service()->FireStateChanged();
OnBlocklistLoaded({});
FinishOutstandingRemotePermissionQueries();
FinishOutstandingHistoryQueries();
......@@ -377,4 +393,62 @@ IN_PROC_BROWSER_TEST_F(FlocIdProviderWithCustomizedServicesBrowserTest,
EXPECT_FALSE(event.has_floc_id());
}
IN_PROC_BROWSER_TEST_F(FlocIdProviderWithCustomizedServicesBrowserTest,
Blocked_FlocInBlocklist) {
net::IPAddress::ConsiderLoopbackIPToBePubliclyRoutableForTesting();
ConfigureReplacementHostAndPortForRemotePermissionService();
std::string cookies_to_set = "/set-cookie?user_id=123";
ui_test_utils::NavigateToURL(
browser(), https_server_.GetURL(test_host(), cookies_to_set));
EXPECT_EQ(1u, GetHistoryUrls().size());
EXPECT_EQ(GetFlocId().ToDebugHeaderValue(), FlocId().ToDebugHeaderValue());
// Turn on sync-history and load the blocklist to trigger the 1st floc
// computation.
sync_service()->SetActiveDataTypes(syncer::ModelTypeSet::All());
sync_service()->FireStateChanged();
// Load a blocklist that would block the upcoming floc.
OnBlocklistLoaded({FlocId::CreateFromHistory({test_host()}).ToUint64()});
FinishOutstandingRemotePermissionQueries();
FinishOutstandingHistoryQueries();
// Expect that the FlocIdComputed user event is not recorded.
ASSERT_EQ(0u, user_event_service()->GetRecordedUserEvents().size());
}
IN_PROC_BROWSER_TEST_F(FlocIdProviderWithCustomizedServicesBrowserTest,
NotBlocked_FlocNotInBlocklist) {
net::IPAddress::ConsiderLoopbackIPToBePubliclyRoutableForTesting();
ConfigureReplacementHostAndPortForRemotePermissionService();
std::string cookies_to_set = "/set-cookie?user_id=123";
ui_test_utils::NavigateToURL(
browser(), https_server_.GetURL(test_host(), cookies_to_set));
EXPECT_EQ(1u, GetHistoryUrls().size());
EXPECT_EQ(GetFlocId().ToDebugHeaderValue(), FlocId().ToDebugHeaderValue());
// Turn on sync-history and load the blocklist to trigger the 1st floc
// computation.
sync_service()->SetActiveDataTypes(syncer::ModelTypeSet::All());
sync_service()->FireStateChanged();
// Load a blocklist that would block a floc different from the upcoming floc.
OnBlocklistLoaded({FlocId::CreateFromHistory({"b.test"}).ToUint64()});
FinishOutstandingRemotePermissionQueries();
FinishOutstandingHistoryQueries();
// Expect that the FlocIdComputed user event is recorded.
ASSERT_EQ(1u, user_event_service()->GetRecordedUserEvents().size());
}
} // namespace federated_learning
......@@ -6,6 +6,7 @@
#include <unordered_set>
#include "chrome/browser/browser_process.h"
#include "chrome/browser/content_settings/cookie_settings_factory.h"
#include "chrome/browser/federated_learning/floc_remote_permission_service.h"
#include "chrome/browser/history/history_service_factory.h"
......@@ -15,6 +16,7 @@
#include "chrome/browser/sync/user_event_service_factory.h"
#include "chrome/common/chrome_features.h"
#include "components/content_settings/core/browser/cookie_settings.h"
#include "components/federated_learning/floc_blocklist_service.h"
#include "components/history/core/browser/history_service.h"
#include "components/sync/driver/profile_sync_service.h"
#include "components/sync_user_events/user_event_service.h"
......@@ -46,7 +48,12 @@ FlocIdProviderImpl::FlocIdProviderImpl(
user_event_service_(user_event_service) {
history_service->AddObserver(this);
sync_service_->AddObserver(this);
g_browser_process->floc_blocklist_service()->AddObserver(this);
OnStateChanged(sync_service);
if (g_browser_process->floc_blocklist_service()->BlocklistLoaded())
OnBlocklistLoaded();
}
FlocIdProviderImpl::~FlocIdProviderImpl() = default;
......@@ -152,6 +159,8 @@ void FlocIdProviderImpl::Shutdown() {
if (history_service_)
history_service_->RemoveObserver(this);
history_service_ = nullptr;
g_browser_process->floc_blocklist_service()->RemoveObserver(this);
}
void FlocIdProviderImpl::OnURLsDeleted(
......@@ -163,13 +172,37 @@ void FlocIdProviderImpl::OnURLsDeleted(
ComputeFloc(ComputeFlocTrigger::kHistoryDelete);
}
void FlocIdProviderImpl::OnBlocklistLoaded() {
if (first_blocklist_loaded_seen_)
return;
first_blocklist_loaded_seen_ = true;
MaybeTriggerFirstFlocComputation();
}
void FlocIdProviderImpl::OnStateChanged(syncer::SyncService* sync_service) {
if (first_floc_computation_triggered_)
if (first_sync_history_enabled_seen_)
return;
if (!IsSyncHistoryEnabled())
return;
first_sync_history_enabled_seen_ = true;
MaybeTriggerFirstFlocComputation();
}
void FlocIdProviderImpl::MaybeTriggerFirstFlocComputation() {
if (first_floc_computation_triggered_)
return;
if (!first_sync_history_enabled_seen_ ||
(base::FeatureList::IsEnabled(features::kFlocIdBlocklistFiltering) &&
!first_blocklist_loaded_seen_)) {
return;
}
ComputeFloc(ComputeFlocTrigger::kBrowserStart);
}
......@@ -281,6 +314,13 @@ void FlocIdProviderImpl::OnGetRecentlyVisitedURLsCompleted(
? FlocId::CreateFromHistory(domains)
: FlocId();
if (floc_id.IsValid() &&
base::FeatureList::IsEnabled(features::kFlocIdBlocklistFiltering) &&
g_browser_process->floc_blocklist_service()->ShouldBlockFloc(
floc_id.ToUint64())) {
floc_id = FlocId();
}
std::move(callback).Run(floc_id);
}
......
......@@ -9,6 +9,7 @@
#include "base/task/cancelable_task_tracker.h"
#include "base/timer/timer.h"
#include "chrome/browser/federated_learning/floc_id_provider.h"
#include "components/federated_learning/floc_blocklist_service.h"
#include "components/history/core/browser/history_service.h"
#include "components/history/core/browser/history_service_observer.h"
#include "components/sync/driver/sync_service_observer.h"
......@@ -36,13 +37,14 @@ class FlocRemotePermissionService;
//
// When all the prerequisites are met, the floc will be computed by sim-hashing
// navigation URL domains in the last 7 days; otherwise, an invalid floc will be
// given.
// given. However, the floc can be invalidated if it's in a blocklist.
//
// The floc will be first computed after sync & sync-history are enabled. After
// each computation, another computation will be scheduled 24 hours later. In
// the event of history deletion, the floc will be recomputed immediately and
// reset the timer of any currently scheduled computation to be 24 hours later.
class FlocIdProviderImpl : public FlocIdProvider,
public FlocBlocklistService::Observer,
public history::HistoryServiceObserver,
public syncer::SyncServiceObserver {
public:
......@@ -88,9 +90,14 @@ class FlocIdProviderImpl : public FlocIdProvider,
void OnURLsDeleted(history::HistoryService* history_service,
const history::DeletionInfo& deletion_info) override;
// FlocBlocklistService::Observer
void OnBlocklistLoaded() override;
// syncer::SyncServiceObserver:
void OnStateChanged(syncer::SyncService* sync_service) override;
void MaybeTriggerFirstFlocComputation();
void ComputeFloc(ComputeFlocTrigger trigger);
void OnComputeFlocCompleted(ComputeFlocTrigger trigger, FlocId floc_id);
......@@ -109,6 +116,9 @@ class FlocIdProviderImpl : public FlocIdProvider,
bool floc_computation_in_progress_ = false;
bool first_floc_computation_triggered_ = false;
bool first_blocklist_loaded_seen_ = false;
bool first_sync_history_enabled_seen_ = false;
// For the swaa/nac/account_type permission, we will use a cached status to
// avoid querying too often.
bool cached_swaa_nac_account_enabled_ = false;
......
......@@ -8,8 +8,10 @@
#include "base/strings/strcat.h"
#include "base/test/bind_test_util.h"
#include "base/test/scoped_feature_list.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/federated_learning/floc_remote_permission_service.h"
#include "chrome/common/chrome_features.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chrome/test/base/testing_profile.h"
#include "components/content_settings/core/browser/cookie_settings.h"
#include "components/content_settings/core/common/pref_names.h"
......@@ -83,6 +85,9 @@ class FlocIdProviderUnitTest : public testing::Test {
void SetUp() override {
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
TestingBrowserProcess::GetGlobal()->SetFlocBlocklistService(
std::make_unique<FlocBlocklistService>());
history_service_ = std::make_unique<history::HistoryService>();
history_service_->Init(
history::TestHistoryDatabaseParamsForPath(temp_dir_.GetPath()));
......@@ -170,6 +175,11 @@ class FlocIdProviderUnitTest : public testing::Test {
fake_floc_remote_permission_service_->set_swaa_nac_account_enabled(enabled);
}
void OnBlocklistLoaded(const std::unordered_set<uint64_t>& blocklist) {
g_browser_process->floc_blocklist_service()->OnBlocklistLoadResult(
blocklist);
}
protected:
content::BrowserTaskEnvironment task_environment_;
......@@ -644,6 +654,103 @@ TEST_F(FlocIdProviderUnitTest, MultipleHistoryEntries) {
floc_id().ToDebugHeaderValue());
}
TEST_F(FlocIdProviderUnitTest,
BlocklistFilteringEnabled_SyncHistoryEnabledFollowedByBlocklistLoaded) {
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeature(features::kFlocIdBlocklistFiltering);
// Turn on sync & sync-history. The 1st floc computation should not be
// triggered as the blocklist hasn't been loaded yet.
test_sync_service_->SetTransportState(
syncer::SyncService::TransportState::ACTIVE);
test_sync_service_->FireStateChanged();
EXPECT_FALSE(first_floc_computation_triggered());
// Load the blocklist. The 1st floc computation should be triggered now as
// sync & sync-history are enabled the blocklist is loaded.
OnBlocklistLoaded({});
EXPECT_TRUE(first_floc_computation_triggered());
}
TEST_F(FlocIdProviderUnitTest,
BlocklistFilteringEnabled_BlocklistLoadedFollowedBySyncHistoryEnabled) {
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeature(features::kFlocIdBlocklistFiltering);
// Load the blocklist. The 1st floc computation should not be
// triggered as sync & sync-history are not enabled yet.
OnBlocklistLoaded({});
EXPECT_FALSE(first_floc_computation_triggered());
// Turn on sync & sync-history. The 1st floc computation should be triggered
// now as sync & sync-history are enabled the blocklist is loaded.
test_sync_service_->SetTransportState(
syncer::SyncService::TransportState::ACTIVE);
test_sync_service_->FireStateChanged();
EXPECT_TRUE(first_floc_computation_triggered());
}
TEST_F(FlocIdProviderUnitTest, BlocklistFilteringEnabled_BlockedFloc) {
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeature(features::kFlocIdBlocklistFiltering);
std::string domain = "foo.com";
history::HistoryAddPageArgs add_page_args;
add_page_args.url = GURL(base::StrCat({"https://www.", domain}));
add_page_args.time = base::Time::Now() - base::TimeDelta::FromDays(1);
add_page_args.publicly_routable = true;
history_service_->AddPage(add_page_args);
task_environment_.RunUntilIdle();
// Load the blocklist and turn on sync & sync-history to trigger the 1st floc
// computation.
std::unordered_set<uint64_t> blocklist;
OnBlocklistLoaded(blocklist);
test_sync_service_->SetTransportState(
syncer::SyncService::TransportState::ACTIVE);
test_sync_service_->FireStateChanged();
EXPECT_TRUE(first_floc_computation_triggered());
task_environment_.RunUntilIdle();
// Expect a floc id update notification. The floc should be equal to the
// sim-hash of the history.
ASSERT_EQ(1u, floc_id_provider_->floc_update_notification_count());
ASSERT_EQ(FlocId::CreateFromHistory({domain}).ToDebugHeaderValue(),
floc_id().ToDebugHeaderValue());
// Insert the current floc to blocklist and reload it.
blocklist.insert(FlocId::CreateFromHistory({domain}).ToUint64());
OnBlocklistLoaded(blocklist);
task_environment_.FastForwardBy(base::TimeDelta::FromDays(1));
// Expect a floc id update notification, with an invalid floc because was
// blocked.
ASSERT_EQ(2u, floc_id_provider_->floc_update_notification_count());
ASSERT_EQ(FlocId().ToDebugHeaderValue(), floc_id().ToDebugHeaderValue());
// Reset and reload the blocklist.
blocklist.clear();
OnBlocklistLoaded(blocklist);
task_environment_.FastForwardBy(base::TimeDelta::FromDays(1));
// Expect a floc id update notification. The floc should be equal to the
// sim-hash of the history.
ASSERT_EQ(3u, floc_id_provider_->floc_update_notification_count());
ASSERT_EQ(FlocId::CreateFromHistory({domain}).ToDebugHeaderValue(),
floc_id().ToDebugHeaderValue());
}
TEST_F(FlocIdProviderUnitTest, TurnSyncOffAndOn) {
std::string domain = "foo.com";
......
......@@ -340,6 +340,10 @@ const base::Feature kFlashDeprecationWarning{"FlashDeprecationWarning",
const base::Feature kFlocIdComputedEventLogging{
"FlocIdComputedEventLogging", base::FEATURE_DISABLED_BY_DEFAULT};
// If enabled, a computed floc will be invalidated if it appears in a blocklist.
const base::Feature kFlocIdBlocklistFiltering{
"FlocIdBlocklistFiltering", base::FEATURE_DISABLED_BY_DEFAULT};
// Enables Focus Mode which brings up a PWA-like window look.
const base::Feature kFocusMode{"FocusMode", base::FEATURE_DISABLED_BY_DEFAULT};
......
......@@ -228,6 +228,9 @@ extern const base::Feature kFlashDeprecationWarning;
COMPONENT_EXPORT(CHROME_FEATURES)
extern const base::Feature kFlocIdComputedEventLogging;
COMPONENT_EXPORT(CHROME_FEATURES)
extern const base::Feature kFlocIdBlocklistFiltering;
COMPONENT_EXPORT(CHROME_FEATURES)
extern const base::Feature kFocusMode;
......
......@@ -67,6 +67,14 @@ FlocBlocklistService::FlocBlocklistService()
FlocBlocklistService::~FlocBlocklistService() = default;
void FlocBlocklistService::AddObserver(Observer* observer) {
observers_.AddObserver(observer);
}
void FlocBlocklistService::RemoveObserver(Observer* observer) {
observers_.RemoveObserver(observer);
}
void FlocBlocklistService::OnBlocklistFileReady(
const base::FilePath& file_path) {
base::PostTaskAndReplyWithResult(
......@@ -81,8 +89,26 @@ void FlocBlocklistService::SetBackgroundTaskRunnerForTesting(
background_task_runner_ = background_task_runner;
}
bool FlocBlocklistService::BlocklistLoaded() const {
return loaded_blocklist_.has_value();
}
bool FlocBlocklistService::ShouldBlockFloc(uint64_t floc_id) const {
// If the blocklist hasn't been loaded or if there was a load failure, we
// block all flocs.
if (!loaded_blocklist_)
return true;
return loaded_blocklist_->find(floc_id) != loaded_blocklist_->end();
}
void FlocBlocklistService::OnBlocklistLoadResult(LoadedBlocklist blocklist) {
loaded_blocklist_ = std::move(blocklist);
if (loaded_blocklist_) {
for (auto& observer : observers_)
observer.OnBlocklistLoaded();
}
}
} // namespace federated_learning
......@@ -11,6 +11,7 @@
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "base/optional.h"
namespace base {
......@@ -27,6 +28,11 @@ namespace federated_learning {
class FlocBlocklistService
: public base::SupportsWeakPtr<FlocBlocklistService> {
public:
class Observer {
public:
virtual void OnBlocklistLoaded() = 0;
};
using LoadedBlocklist = base::Optional<std::unordered_set<uint64_t>>;
FlocBlocklistService();
......@@ -35,22 +41,33 @@ class FlocBlocklistService
FlocBlocklistService(const FlocBlocklistService&) = delete;
FlocBlocklistService& operator=(const FlocBlocklistService&) = delete;
// Adds/Removes an Observer.
void AddObserver(Observer* observer);
void RemoveObserver(Observer* observer);
// Virtual for testing.
virtual void OnBlocklistFileReady(const base::FilePath& file_path);
void SetBackgroundTaskRunnerForTesting(
scoped_refptr<base::SequencedTaskRunner> background_task_runner);
bool BlocklistLoaded() const;
bool ShouldBlockFloc(uint64_t floc_id) const;
protected:
// Virtual for testing.
virtual void OnBlocklistLoadResult(LoadedBlocklist blocklist);
private:
friend class MockFlocBlocklistService;
friend class FlocIdProviderUnitTest;
friend class FlocIdProviderWithCustomizedServicesBrowserTest;
// Runner for tasks that do not influence user experience.
scoped_refptr<base::SequencedTaskRunner> background_task_runner_;
base::ObserverList<Observer>::Unchecked observers_;
LoadedBlocklist loaded_blocklist_;
};
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment