Commit 7a9374b4 authored by Yao Xiao's avatar Yao Xiao Committed by Chromium LUCI CQ

[floc] Integrate floc-accessible-since for floc computation and invalidation

Use floc-accessible-since to bound the history query time range (in
addition to 7-days-ago-from-now). floc-accessible-since represents the
point in time from which history is eligible to be used when
calculating a user's floc id. It will be reset to base::Time::Now()
when a user clears all cookies, or when the browser restarts with
"Clear on exit" enabled.

When the floc-accessible-since is updated to be greater than the
begin time of the history used to compute the current floc, invalidate
the current floc. This could happen at the browser startup, or during a
browser session when getting an explicit notification. We only
invalidate the floc rather than recomputing, because we don't want the
floc to change more frequently than the scheduled update rate.

Bug: 1157383
Change-Id: I60708820b353ae3c30be6eb34fe0596a6080003f
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2617622
Commit-Queue: Yao Xiao <yaoxia@chromium.org>
Reviewed-by: default avatarCharlie Harrison <csharrison@chromium.org>
Cr-Commit-Position: refs/heads/master@{#842632}
parent 8905ee14
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include "base/test/scoped_feature_list.h" #include "base/test/scoped_feature_list.h"
#include "base/threading/thread_restrictions.h" #include "base/threading/thread_restrictions.h"
#include "chrome/browser/browser_process.h" #include "chrome/browser/browser_process.h"
#include "chrome/browser/browsing_data/chrome_browsing_data_remover_constants.h"
#include "chrome/browser/chrome_content_browser_client.h" #include "chrome/browser/chrome_content_browser_client.h"
#include "chrome/browser/content_settings/host_content_settings_map_factory.h" #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
#include "chrome/browser/federated_learning/floc_event_logger.h" #include "chrome/browser/federated_learning/floc_event_logger.h"
...@@ -35,6 +36,7 @@ ...@@ -35,6 +36,7 @@
#include "content/public/browser/storage_partition.h" #include "content/public/browser/storage_partition.h"
#include "content/public/common/content_switches.h" #include "content/public/common/content_switches.h"
#include "content/public/test/browser_test.h" #include "content/public/test/browser_test.h"
#include "content/public/test/browsing_data_remover_test_util.h"
#include "net/dns/mock_host_resolver.h" #include "net/dns/mock_host_resolver.h"
#include "net/test/embedded_test_server/http_request.h" #include "net/test/embedded_test_server/http_request.h"
#include "net/test/embedded_test_server/http_response.h" #include "net/test/embedded_test_server/http_response.h"
...@@ -296,6 +298,17 @@ class FlocIdProviderWithCustomizedServicesBrowserTest ...@@ -296,6 +298,17 @@ class FlocIdProviderWithCustomizedServicesBrowserTest
run_loop.Run(); run_loop.Run();
} }
void ClearCookiesBrowsingData() {
content::BrowsingDataRemover* remover =
content::BrowserContext::GetBrowsingDataRemover(browser()->profile());
content::BrowsingDataRemoverCompletionObserver observer(remover);
remover->RemoveAndReply(
base::Time(), base::Time::Max(),
content::BrowsingDataRemover::DATA_TYPE_COOKIES,
content::BrowsingDataRemover::ORIGIN_TYPE_UNPROTECTED_WEB, &observer);
observer.BlockUntilCompletion();
}
base::FilePath GetUniqueTemporaryPath() { base::FilePath GetUniqueTemporaryPath() {
CHECK(scoped_temp_dir_.IsValid() || scoped_temp_dir_.CreateUniqueTempDir()); CHECK(scoped_temp_dir_.IsValid() || scoped_temp_dir_.CreateUniqueTempDir());
return scoped_temp_dir_.GetPath().AppendASCII( return scoped_temp_dir_.GetPath().AppendASCII(
...@@ -507,7 +520,24 @@ IN_PROC_BROWSER_TEST_F(FlocIdProviderWithCustomizedServicesBrowserTest, ...@@ -507,7 +520,24 @@ IN_PROC_BROWSER_TEST_F(FlocIdProviderWithCustomizedServicesBrowserTest,
} }
IN_PROC_BROWSER_TEST_F(FlocIdProviderWithCustomizedServicesBrowserTest, IN_PROC_BROWSER_TEST_F(FlocIdProviderWithCustomizedServicesBrowserTest,
HistoryDeleteRecomputeFloc) { ClearCookiesInvalidateFloc) {
ConfigureReplacementHostAndPortForRemotePermissionService();
FinishOutstandingAsyncQueries();
EXPECT_TRUE(GetFlocId().IsValid());
EXPECT_EQ(1u, floc_event_logger_->NumberOfLogAttemptsQueued());
ClearCookiesBrowsingData();
FinishOutstandingAsyncQueries();
// The floc has been invalidated. Expect no additional event logging.
EXPECT_FALSE(GetFlocId().IsValid());
EXPECT_EQ(1u, floc_event_logger_->NumberOfLogAttemptsQueued());
}
IN_PROC_BROWSER_TEST_F(FlocIdProviderWithCustomizedServicesBrowserTest,
HistoryDeleteInvalidateFloc) {
ConfigureReplacementHostAndPortForRemotePermissionService(); ConfigureReplacementHostAndPortForRemotePermissionService();
FinishOutstandingAsyncQueries(); FinishOutstandingAsyncQueries();
......
...@@ -23,16 +23,24 @@ constexpr int kQueryHistoryWindowInDays = 7; ...@@ -23,16 +23,24 @@ constexpr int kQueryHistoryWindowInDays = 7;
// The placeholder sorting-lsh version when the sorting-lsh feature is disabled. // The placeholder sorting-lsh version when the sorting-lsh feature is disabled.
constexpr uint32_t kSortingLshVersionPlaceholder = 0; constexpr uint32_t kSortingLshVersionPlaceholder = 0;
// Checks whether we can keep using the previous floc. If so, write to struct StartupComputeDecision {
// |next_compute_delay| the time period we should wait until the floc needs to bool invalidate_existing_floc = true;
// be recomputed. // Will be base::nullopt if should recompute immediately.
bool ShouldKeepUsingPreviousFloc(const FlocId& last_floc, base::Optional<base::TimeDelta> next_compute_delay;
base::TimeDelta* next_compute_delay) { };
// Determine whether we can keep using the previous floc and/or when should the
// next floc computation occur.
StartupComputeDecision GetStartupComputeDecision(
const FlocId& last_floc,
base::Time floc_accessible_since) {
// The floc has never been computed. This could happen with a fresh profile, // The floc has never been computed. This could happen with a fresh profile,
// or some early trigger conditions were never met (e.g. sorting-lsh file has // or some early trigger conditions were never met (e.g. sorting-lsh file has
// never been ready). // never been ready).
if (last_floc.compute_time().is_null()) if (last_floc.compute_time().is_null()) {
return false; return StartupComputeDecision{.invalidate_existing_floc = true,
.next_compute_delay = base::nullopt};
}
// The browser started with a kFlocIdFinchConfigVersion param different from // The browser started with a kFlocIdFinchConfigVersion param different from
// the param when floc was computed last time. // the param when floc was computed last time.
...@@ -44,7 +52,8 @@ bool ShouldKeepUsingPreviousFloc(const FlocId& last_floc, ...@@ -44,7 +52,8 @@ bool ShouldKeepUsingPreviousFloc(const FlocId& last_floc,
// wouldn't arrive due to e.g. component updater issue. // wouldn't arrive due to e.g. component updater issue.
if (last_floc.finch_config_version() != if (last_floc.finch_config_version() !=
static_cast<uint32_t>(kFlocIdFinchConfigVersion.Get())) { static_cast<uint32_t>(kFlocIdFinchConfigVersion.Get())) {
return false; return StartupComputeDecision{.invalidate_existing_floc = true,
.next_compute_delay = base::nullopt};
} }
base::TimeDelta presumed_next_compute_delay = base::TimeDelta presumed_next_compute_delay =
...@@ -52,19 +61,33 @@ bool ShouldKeepUsingPreviousFloc(const FlocId& last_floc, ...@@ -52,19 +61,33 @@ bool ShouldKeepUsingPreviousFloc(const FlocId& last_floc,
base::Time::Now(); base::Time::Now();
// The last floc has expired. // The last floc has expired.
if (presumed_next_compute_delay <= base::TimeDelta()) if (presumed_next_compute_delay <= base::TimeDelta()) {
return false; return StartupComputeDecision{.invalidate_existing_floc = true,
.next_compute_delay = base::nullopt};
}
// This could happen if the machine time has changed since the last // This could happen if the machine time has changed since the last
// computation. Return false in order to keep computing the floc at the // computation. Recompute immediately to align with the expected schedule
// anticipated schedule rather than potentially stop computing for a very long // rather than potentially stop computing for a very long time.
// time. if (presumed_next_compute_delay >= 2 * kFlocIdScheduledUpdateInterval.Get()) {
if (presumed_next_compute_delay >= 2 * kFlocIdScheduledUpdateInterval.Get()) return StartupComputeDecision{.invalidate_existing_floc = true,
return false; .next_compute_delay = base::nullopt};
}
*next_compute_delay = presumed_next_compute_delay; // Normally "floc_accessible_since <= last_floc.history_begin_time()" is an
// invariant, because we monitor its update and reset the floc accordingly.
// But "Clear on exit" may cause a cookie deletion on shutdown (practically on
// startup) that will reset floc_accessible_since to base::Time::Now and
// break the invariant on startup.
if (floc_accessible_since > last_floc.history_begin_time()) {
return StartupComputeDecision{
.invalidate_existing_floc = true,
.next_compute_delay = presumed_next_compute_delay};
}
return true; return StartupComputeDecision{
.invalidate_existing_floc = false,
.next_compute_delay = presumed_next_compute_delay};
} }
} // namespace } // namespace
...@@ -79,19 +102,23 @@ FlocIdProviderImpl::FlocIdProviderImpl( ...@@ -79,19 +102,23 @@ FlocIdProviderImpl::FlocIdProviderImpl(
history_service_(history_service), history_service_(history_service),
floc_event_logger_(std::move(floc_event_logger)), floc_event_logger_(std::move(floc_event_logger)),
floc_id_(FlocId::ReadFromPrefs(prefs_)) { floc_id_(FlocId::ReadFromPrefs(prefs_)) {
privacy_sandbox_settings->AddObserver(this);
history_service->AddObserver(this); history_service->AddObserver(this);
g_browser_process->floc_sorting_lsh_clusters_service()->AddObserver(this); g_browser_process->floc_sorting_lsh_clusters_service()->AddObserver(this);
// If the previous floc has expired, invalidate it. The next computation will StartupComputeDecision decision = GetStartupComputeDecision(
// be "immediate", i.e. will occur after we first observe that the SortingLSH floc_id_, privacy_sandbox_settings->FlocDataAccessibleSince());
// file is loaded; otherwise, keep using the last floc (which may still have
// be invalid), and schedule a recompute event with the desired delay. // If the previous floc has expired, invalidate it; otherwise, keep using the
base::TimeDelta next_compute_delay; // previous floc though it may already be invalid.
if (ShouldKeepUsingPreviousFloc(floc_id_, &next_compute_delay)) { if (decision.invalidate_existing_floc)
ScheduleFlocComputation(next_compute_delay);
} else {
floc_id_.InvalidateIdAndSaveToPrefs(prefs_); floc_id_.InvalidateIdAndSaveToPrefs(prefs_);
}
// Schedule the next floc computation if a delay is needed; otherwise, the
// next computation will occur immediately, or as soon as the sorting-lsh file
// is loaded when the sorting-lsh feature is enabled.
if (decision.next_compute_delay.has_value())
ScheduleFlocComputation(decision.next_compute_delay.value());
if (g_browser_process->floc_sorting_lsh_clusters_service() if (g_browser_process->floc_sorting_lsh_clusters_service()
->IsSortingLshClustersFileReady()) { ->IsSortingLshClustersFileReady()) {
...@@ -147,13 +174,35 @@ void FlocIdProviderImpl::LogFlocComputedEvent(const ComputeFlocResult& result) { ...@@ -147,13 +174,35 @@ void FlocIdProviderImpl::LogFlocComputedEvent(const ComputeFlocResult& result) {
} }
void FlocIdProviderImpl::Shutdown() { void FlocIdProviderImpl::Shutdown() {
if (history_service_) privacy_sandbox_settings_->RemoveObserver(this);
history_service_->RemoveObserver(this); history_service_->RemoveObserver(this);
history_service_ = nullptr;
g_browser_process->floc_sorting_lsh_clusters_service()->RemoveObserver(this); g_browser_process->floc_sorting_lsh_clusters_service()->RemoveObserver(this);
} }
void FlocIdProviderImpl::OnFlocDataAccessibleSinceUpdated() {
// Set the |need_recompute_| flag so that we will recompute the floc
// immediately after the in-progress one finishes, so as to avoid potential
// data races.
if (floc_computation_in_progress_) {
need_recompute_ = true;
return;
}
// Note: we only invalidate the floc rather than recomputing, because we don't
// want the floc to change more frequently than the scheduled update rate.
// No-op if the floc is already invalid.
if (!floc_id_.IsValid())
return;
// Invalidate the floc if the new floc-accessible-since time is greater than
// the begin time of the history used to compute the current floc.
if (privacy_sandbox_settings_->FlocDataAccessibleSince() >
floc_id_.history_begin_time()) {
floc_id_.InvalidateIdAndSaveToPrefs(prefs_);
}
}
void FlocIdProviderImpl::OnURLsDeleted( void FlocIdProviderImpl::OnURLsDeleted(
history::HistoryService* history_service, history::HistoryService* history_service,
const history::DeletionInfo& deletion_info) { const history::DeletionInfo& deletion_info) {
...@@ -198,8 +247,8 @@ void FlocIdProviderImpl::OnSortingLshClustersFileReady() { ...@@ -198,8 +247,8 @@ void FlocIdProviderImpl::OnSortingLshClustersFileReady() {
void FlocIdProviderImpl::MaybeTriggerImmediateComputation() { void FlocIdProviderImpl::MaybeTriggerImmediateComputation() {
// If the floc computation is neither in progress nor scheduled, it means we // If the floc computation is neither in progress nor scheduled, it means we
// want to trigger an immediate computation as soon as the sorting-lsh file is // want to trigger an immediate computation, or as soon as the sorting-lsh
// loaded. // file is loaded when the sorting-lsh feature is enabled.
if (floc_computation_in_progress_ || compute_floc_timer_.IsRunning()) if (floc_computation_in_progress_ || compute_floc_timer_.IsRunning())
return; return;
...@@ -254,8 +303,13 @@ bool FlocIdProviderImpl::IsPrivacySandboxAllowed() const { ...@@ -254,8 +303,13 @@ bool FlocIdProviderImpl::IsPrivacySandboxAllowed() const {
void FlocIdProviderImpl::GetRecentlyVisitedURLs( void FlocIdProviderImpl::GetRecentlyVisitedURLs(
GetRecentlyVisitedURLsCallback callback) { GetRecentlyVisitedURLsCallback callback) {
base::Time now = base::Time::Now();
history::QueryOptions options; history::QueryOptions options;
options.SetRecentDayRange(kQueryHistoryWindowInDays); options.begin_time =
std::max(privacy_sandbox_settings_->FlocDataAccessibleSince(),
now - base::TimeDelta::FromDays(kQueryHistoryWindowInDays));
options.end_time = now;
options.duplicate_policy = history::QueryOptions::KEEP_ALL_DUPLICATES; options.duplicate_policy = history::QueryOptions::KEEP_ALL_DUPLICATES;
history_service_->QueryHistory(base::string16(), options, std::move(callback), history_service_->QueryHistory(base::string16(), options, std::move(callback),
......
...@@ -9,12 +9,11 @@ ...@@ -9,12 +9,11 @@
#include "base/task/cancelable_task_tracker.h" #include "base/task/cancelable_task_tracker.h"
#include "base/timer/timer.h" #include "base/timer/timer.h"
#include "chrome/browser/federated_learning/floc_id_provider.h" #include "chrome/browser/federated_learning/floc_id_provider.h"
#include "chrome/browser/privacy_sandbox/privacy_sandbox_settings.h"
#include "components/federated_learning/floc_sorting_lsh_clusters_service.h" #include "components/federated_learning/floc_sorting_lsh_clusters_service.h"
#include "components/history/core/browser/history_service.h" #include "components/history/core/browser/history_service.h"
#include "components/history/core/browser/history_service_observer.h" #include "components/history/core/browser/history_service_observer.h"
class PrivacySandboxSettings;
namespace federated_learning { namespace federated_learning {
class FlocEventLogger; class FlocEventLogger;
...@@ -48,9 +47,14 @@ class FlocEventLogger; ...@@ -48,9 +47,14 @@ class FlocEventLogger;
// //
// In the event of history deletion, the floc will be invalidated immediately if // In the event of history deletion, the floc will be invalidated immediately if
// the time range of the deletion overlaps with the time range used to compute // the time range of the deletion overlaps with the time range used to compute
// the existing floc. // the existing floc. In the event of cookie deletion, the floc will always be
// invalidated. Note that we only invalidate the floc rather than recomputing,
// because we don't want the floc to change more frequently than the scheduled
// update rate (% rare cases such as when the finch version param has changed
// indicating a new algorithm / experiment, a recompute will be needed).
class FlocIdProviderImpl : public FlocIdProvider, class FlocIdProviderImpl : public FlocIdProvider,
public FlocSortingLshClustersService::Observer, public FlocSortingLshClustersService::Observer,
public PrivacySandboxSettings::Observer,
public history::HistoryServiceObserver { public history::HistoryServiceObserver {
public: public:
struct ComputeFlocResult { struct ComputeFlocResult {
...@@ -101,8 +105,16 @@ class FlocIdProviderImpl : public FlocIdProvider, ...@@ -101,8 +105,16 @@ class FlocIdProviderImpl : public FlocIdProvider,
// KeyedService: // KeyedService:
void Shutdown() override; void Shutdown() override;
// PrivacySandboxSettings::Observer
// When the floc-accessible-since time is updated (due to e.g. cookies
// deletion), we'll either invalidate or keep using the floc. This will
// depend on the updated time and the begin time of the history used to
// compute the current floc.
void OnFlocDataAccessibleSinceUpdated() override;
// history::HistoryServiceObserver // history::HistoryServiceObserver
//
// On history deletion, we'll either invalidate or keep using the floc. This // On history deletion, we'll either invalidate or keep using the floc. This
// will depend on the deletion type and the time range. // will depend on the deletion type and the time range.
void OnURLsDeleted(history::HistoryService* history_service, void OnURLsDeleted(history::HistoryService* history_service,
......
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