blob: 8edaadcb2bf8a9f7f8be0079a3b61d2b4c6e1d8f [file] [log] [blame]
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <stddef.h>
#include <array>
#include <memory>
#include <string>
#include "base/feature_list.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/metrics/histogram_samples.h"
#include "base/path_service.h"
#include "base/run_loop.h"
#include "base/strings/stringprintf.h"
#include "base/task/sequenced_task_runner.h"
#include "base/test/bind.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/simple_test_clock.h"
#include "base/test/test_future.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "chrome/browser/browsing_data/browsing_data_remover_browsertest_base.h"
#include "chrome/browser/browsing_data/chrome_browsing_data_remover_constants.h"
#include "chrome/browser/browsing_data/counters/cache_counter.h"
#include "chrome/browser/browsing_data/counters/site_data_counting_helper.h"
#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
#include "chrome/browser/external_protocol/external_protocol_handler.h"
#include "chrome/browser/media/clear_key_cdm_test_helper.h"
#include "chrome/browser/prefs/session_startup_pref.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/signin/account_reconcilor_factory.h"
#include "chrome/browser/signin/identity_manager_factory.h"
#include "chrome/browser/sync/sync_service_factory.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/browsing_data/content/browsing_data_model.h"
#include "components/browsing_data/core/features.h"
#include "components/content_settings/core/browser/host_content_settings_map.h"
#include "components/content_settings/core/common/content_settings_utils.h"
#include "components/history/core/common/pref_names.h"
#include "components/metrics/content/subprocess_metrics_provider.h"
#include "components/password_manager/core/browser/features/password_features.h"
#include "components/password_manager/core/browser/features/password_manager_features_util.h"
#include "components/prefs/pref_service.h"
#include "components/signin/core/browser/account_reconcilor.h"
#include "components/signin/public/base/signin_switches.h"
#include "components/signin/public/identity_manager/identity_manager.h"
#include "components/signin/public/identity_manager/identity_test_utils.h"
#include "components/sync/base/user_selectable_type.h"
#include "components/sync/service/sync_user_settings.h"
#include "components/sync/test/test_sync_service.h"
#include "content/public/browser/browsing_data_filter_builder.h"
#include "content/public/browser/clear_site_data_utils.h"
#include "content/public/browser/network_service_util.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/browser/storage_usage_info.h"
#include "content/public/common/content_client.h"
#include "content/public/common/content_features.h"
#include "content/public/common/content_paths.h"
#include "content/public/common/content_switches.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/browsing_data_remover_test_util.h"
#include "google_apis/gaia/google_service_auth_error.h"
#include "media/base/media_switches.h"
#include "media/mojo/mojom/media_types.mojom.h"
#include "media/mojo/services/video_decode_perf_history.h"
#include "media/mojo/services/webrtc_video_perf_history.h"
#include "net/base/features.h"
#include "net/cookies/cookie_partition_key.h"
#include "net/dns/mock_host_resolver.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
#include "storage/browser/quota/quota_manager.h"
#include "storage/browser/quota/quota_manager_proxy.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/public/common/features_generated.h"
#include "url/gurl.h"
#include "url/origin.h"
#if BUILDFLAG(ENABLE_LIBRARY_CDMS)
#if BUILDFLAG(IS_MAC)
#include "base/threading/platform_thread.h"
#endif
#include "base/memory/scoped_refptr.h"
#endif // BUILDFLAG(ENABLE_LIBRARY_CDMS)
#if BUILDFLAG(IS_CHROMEOS)
#include "chrome/browser/ash/net/system_proxy_manager.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/browser_process_platform_part.h"
#include "chromeos/ash/components/dbus/system_proxy/system_proxy_client.h"
#endif // BUILDFLAG(IS_CHROMEOS)
using content::BrowserThread;
using content::BrowsingDataFilterBuilder;
using testing::UnorderedElementsAre;
using testing::UnorderedElementsAreArray;
namespace {
static const char* kExampleHost = "example.com";
static const char* kLocalHost = "localhost";
static const base::Time kStartTime = base::Time::Now();
static const base::Time kLastHourTime = kStartTime - base::Hours(1);
// This enum is used in the place of base::Time because using base::Time
// as a test param causes problems on Fuchsia. See https://crbug.com/1308948 for
// details.
enum TimeEnum {
kDefault,
kStart,
kLastHour,
kMax,
};
base::Time TimeEnumToTime(TimeEnum time) {
switch (time) {
case TimeEnum::kStart:
return kStartTime;
case TimeEnum::kLastHour:
return kLastHourTime;
case TimeEnum::kMax:
return base::Time::Max();
default:
return base::Time();
}
}
std::vector<std::string> GetHistogramSuffixes(
const base::HistogramTester& tester,
const std::string& prefix) {
std::vector<std::string> types;
for (const auto& entry : tester.GetTotalCountsForPrefix(prefix)) {
types.push_back(entry.first.substr(prefix.length()));
}
return types;
}
void AppendRange(std::vector<std::string>& target,
const std::vector<std::string_view>& append) {
// Use std append_range() when c++23 is available.
target.insert(target.end(), append.begin(), append.end());
}
} // namespace
class BrowsingDataRemoverBrowserTest
: public BrowsingDataRemoverBrowserTestBase {
public:
BrowsingDataRemoverBrowserTest() {
std::vector<base::test::FeatureRef> enabled_features = {};
std::vector<base::test::FeatureRef> disabled_features = {};
#if BUILDFLAG(ENABLE_LIBRARY_CDMS)
enabled_features.push_back(media::kExternalClearKeyForTesting);
#endif
#if !BUILDFLAG(IS_ANDROID)
enabled_features.push_back(browsing_data::features::kDbdRevampDesktop);
#endif // !BUILDFLAG(IS_ANDROID)
InitFeatureLists(std::move(enabled_features), std::move(disabled_features));
}
void SetUpOnMainThread() override {
BrowsingDataRemoverBrowserTestBase::SetUpOnMainThread();
host_resolver()->AddRule(kExampleHost, "127.0.0.1");
// Explicitly disable session restore. Otherwise tests that restart the
// browser can get tab data persisted across sessions when we thought we
// deleted it.
SessionStartupPref::SetStartupPref(
GetProfile()->GetPrefs(),
SessionStartupPref(SessionStartupPref::DEFAULT));
}
void RemoveAndWait(uint64_t remove_mask) {
RemoveAndWait(remove_mask, TimeEnum::kDefault, TimeEnum::kMax);
}
void RemoveAndWait(uint64_t remove_mask, TimeEnum delete_begin) {
RemoveAndWait(remove_mask, delete_begin, TimeEnum::kMax);
}
void RemoveAndWait(uint64_t remove_mask,
TimeEnum delete_begin,
TimeEnum delete_end) {
content::BrowsingDataRemover* remover =
GetBrowser()->profile()->GetBrowsingDataRemover();
content::BrowsingDataRemoverCompletionObserver completion_observer(remover);
remover->RemoveAndReply(
TimeEnumToTime(delete_begin), TimeEnumToTime(delete_end), remove_mask,
content::BrowsingDataRemover::ORIGIN_TYPE_UNPROTECTED_WEB,
&completion_observer);
completion_observer.BlockUntilCompletion();
}
void RemoveWithFilterAndWait(
uint64_t remove_mask,
std::unique_ptr<BrowsingDataFilterBuilder> filter_builder) {
content::BrowsingDataRemover* remover =
GetBrowser()->profile()->GetBrowsingDataRemover();
content::BrowsingDataRemoverCompletionObserver completion_observer(remover);
remover->RemoveWithFilterAndReply(
base::Time(), base::Time::Max(), remove_mask,
content::BrowsingDataRemover::ORIGIN_TYPE_UNPROTECTED_WEB,
std::move(filter_builder), &completion_observer);
completion_observer.BlockUntilCompletion();
}
// Test a data type by creating a value and checking it is counted by the
// cookie counter. Then it deletes the value and checks that it has been
// deleted and the cookie counter is back to zero.
void TestSiteData(const std::string& type, TimeEnum delete_begin) {
EXPECT_EQ(0, GetSiteDataCount());
GURL url = embedded_test_server()->GetURL("/browsing_data/site_data.html");
ASSERT_TRUE(ui_test_utils::NavigateToURL(GetBrowser(), url));
EXPECT_EQ(0, GetSiteDataCount());
ExpectTotalModelCount(0);
EXPECT_FALSE(HasDataForType(type));
SetDataForType(type);
EXPECT_EQ(1, GetSiteDataCount());
// TODO(crbug.com/40218898): Use a different approach to determine presence
// of data that does not depend on UI code and has a better resolution when
// 3PSP is fully enabled. ExpectTotalModelCount(1) is not always true
// here.
EXPECT_TRUE(HasDataForType(type));
RemoveAndWait(chrome_browsing_data_remover::DATA_TYPE_SITE_DATA,
delete_begin);
EXPECT_EQ(0, GetSiteDataCount());
ExpectTotalModelCount(0);
EXPECT_FALSE(HasDataForType(type));
}
// Test that storage systems like filesystem, where just an access
// creates an empty store, are counted and deleted correctly.
void TestEmptySiteData(const std::string& type, TimeEnum delete_begin) {
EXPECT_EQ(0, GetSiteDataCount());
ExpectTotalModelCount(0);
GURL url = embedded_test_server()->GetURL("/browsing_data/site_data.html");
ASSERT_TRUE(ui_test_utils::NavigateToURL(GetBrowser(), url));
EXPECT_EQ(0, GetSiteDataCount());
ExpectTotalModelCount(0);
// Opening a store of this type creates a site data entry.
EXPECT_FALSE(HasDataForType(type));
EXPECT_EQ(1, GetSiteDataCount());
// TODO(crbug.com/40218898): Use a different approach to determine presence
// of data that does not depend on UI code and has a better resolution when
// 3PSP is fully enabled. ExpectTotalModelCount(1) is not always true
// here.
RemoveAndWait(chrome_browsing_data_remover::DATA_TYPE_SITE_DATA,
delete_begin);
EXPECT_EQ(0, GetSiteDataCount());
ExpectTotalModelCount(0);
}
inline void ExpectTotalModelCount(size_t expected) {
std::unique_ptr<BrowsingDataModel> browsing_data_model =
GetBrowsingDataModel(GetProfile());
EXPECT_EQ(expected, browsing_data_model->size());
}
void OnVideoDecodePerfInfo(base::RunLoop* run_loop,
bool* out_is_smooth,
bool* out_is_power_efficient,
bool is_smooth,
bool is_power_efficient) {
*out_is_smooth = is_smooth;
*out_is_power_efficient = is_power_efficient;
run_loop->QuitWhenIdle();
}
network::mojom::NetworkContext* network_context() const {
return GetBrowser()
->profile()
->GetDefaultStoragePartition()
->GetNetworkContext();
}
void ClearSiteDataAndWait(
const url::Origin& origin,
const std::optional<net::CookiePartitionKey>& cookie_partition_key,
const std::optional<blink::StorageKey>& storage_key,
const std::set<std::string>& storage_buckets_to_remove) {
bool partitioned_state_allowed_only =
cookie_partition_key.has_value() &&
!origin.DomainIs(cookie_partition_key->site()
.registrable_domain_or_host_for_testing());
base::RunLoop loop;
content::ClearSiteData(
GetBrowser()->profile()->GetWeakPtr(),
/*storage_partition_config=*/std::nullopt,
/*origin=*/origin, content::ClearSiteDataTypeSet::All(),
/*storage_buckets_to_remove=*/storage_buckets_to_remove,
/*avoid_closing_connections=*/true,
/*cookie_partition_key=*/cookie_partition_key,
/*storage_key=*/storage_key,
/*partitioned_state_allowed_only=*/partitioned_state_allowed_only,
/*callback=*/loop.QuitClosure());
loop.Run();
}
private:
void OnCacheSizeResult(
base::RunLoop* run_loop,
browsing_data::BrowsingDataCounter::ResultInt* out_size,
std::unique_ptr<browsing_data::BrowsingDataCounter::Result> result) {
if (!result->Finished())
return;
*out_size =
static_cast<browsing_data::BrowsingDataCounter::FinishedResult*>(
result.get())
->Value();
run_loop->Quit();
}
void SetUpCommandLine(base::CommandLine* command_line) override {
#if BUILDFLAG(ENABLE_LIBRARY_CDMS)
// Testing MediaLicenses requires additional command line parameters as
// it uses the External Clear Key CDM.
RegisterClearKeyCdm(command_line);
#endif
}
};
#if BUILDFLAG(ENABLE_DICE_SUPPORT)
// Same as BrowsingDataRemoverBrowserTest, but forces Dice to be enabled.
class DiceBrowsingDataRemoverBrowserTest
: public BrowsingDataRemoverBrowserTest {
public:
AccountInfo AddAccountToProfile(const std::string& account_id,
Profile* profile,
bool is_primary) {
auto* identity_manager = IdentityManagerFactory::GetForProfile(profile);
if (is_primary) {
DCHECK(!identity_manager->HasPrimaryAccount(signin::ConsentLevel::kSync));
return signin::MakePrimaryAccountAvailable(identity_manager,
account_id + "@gmail.com",
signin::ConsentLevel::kSync);
}
auto account_info =
signin::MakeAccountAvailable(identity_manager, account_id);
DCHECK(
identity_manager->HasAccountWithRefreshToken(account_info.account_id));
return account_info;
}
};
#endif
// Test BrowsingDataRemover for downloads.
IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverBrowserTest, Download) {
DownloadAnItem();
RemoveAndWait(content::BrowsingDataRemover::DATA_TYPE_DOWNLOADS);
VerifyDownloadCount(0u);
}
// Test that the salt for media device IDs is reset when cookies are cleared.
IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverBrowserTest, MediaDeviceIdSalt) {
content::RenderFrameHost* frame_host = GetBrowser()
->tab_strip_model()
->GetActiveWebContents()
->GetPrimaryMainFrame();
url::Origin origin = frame_host->GetLastCommittedOrigin();
net::SiteForCookies site_for_cookies =
net::SiteForCookies::FromOrigin(origin);
blink::StorageKey storage_key = blink::StorageKey::CreateFirstParty(origin);
base::test::TestFuture<bool, const std::string&> future;
content::GetContentClientForTesting()->browser()->GetMediaDeviceIDSalt(
frame_host, site_for_cookies, storage_key, future.GetCallback());
std::string original_salt = future.Get<1>();
RemoveAndWait(content::BrowsingDataRemover::DATA_TYPE_COOKIES);
future.Clear();
content::GetContentClientForTesting()->browser()->GetMediaDeviceIDSalt(
frame_host, site_for_cookies, storage_key, future.GetCallback());
std::string new_salt = future.Get<1>();
EXPECT_NE(original_salt, new_salt);
}
#if BUILDFLAG(ENABLE_DICE_SUPPORT)
// Test that Sync is not paused when cookies are cleared.
IN_PROC_BROWSER_TEST_F(DiceBrowsingDataRemoverBrowserTest, SyncToken) {
Profile* profile = browser()->profile();
// Set a Gaia cookie.
ASSERT_TRUE(SetGaiaCookieForProfile(profile));
// Set a Sync account and a secondary account.
const char kPrimaryAccountId[] = "primary_account_id";
AccountInfo primary_account =
AddAccountToProfile(kPrimaryAccountId, profile, /*is_primary=*/true);
const char kSecondaryAccountId[] = "secondary_account_id";
AccountInfo secondary_account =
AddAccountToProfile(kSecondaryAccountId, profile, /*is_primary=*/false);
// Clear cookies.
RemoveAndWait(content::BrowsingDataRemover::DATA_TYPE_COOKIES);
// Check that the primary account was not removed and has valid auth.
signin::IdentityManager* identity_manager =
IdentityManagerFactory::GetForProfile(profile);
EXPECT_TRUE(
identity_manager->HasAccountWithRefreshToken(primary_account.account_id));
EXPECT_FALSE(
identity_manager->HasAccountWithRefreshTokenInPersistentErrorState(
primary_account.account_id));
// Check that the secondary token was revoked.
EXPECT_FALSE(identity_manager->HasAccountWithRefreshToken(
secondary_account.account_id));
}
// Test that Sync is not paused when cookies are cleared.
IN_PROC_BROWSER_TEST_F(DiceBrowsingDataRemoverBrowserTest,
SyncTokenScopedDeletion) {
Profile* profile = browser()->profile();
// Set a Gaia cookie.
ASSERT_TRUE(SetGaiaCookieForProfile(profile));
// Set a Sync account and a secondary account.
const char kPrimaryAccountId[] = "primary_account_id";
AccountInfo primary_account =
AddAccountToProfile(kPrimaryAccountId, profile, /*is_primary=*/true);
const char kSecondaryAccountId[] = "secondary_account_id";
AccountInfo secondary_account =
AddAccountToProfile(kSecondaryAccountId, profile, /*is_primary=*/false);
// Clear cookies.
RemoveAndWait(content::BrowsingDataRemover::DATA_TYPE_COOKIES);
// Check that the Sync token was not revoked.
auto* identity_manager = IdentityManagerFactory::GetForProfile(profile);
EXPECT_TRUE(
identity_manager->HasAccountWithRefreshToken(primary_account.account_id));
EXPECT_FALSE(
identity_manager->HasAccountWithRefreshTokenInPersistentErrorState(
primary_account.account_id));
// Check that the secondary token was revoked.
EXPECT_FALSE(identity_manager->HasAccountWithRefreshToken(
secondary_account.account_id));
}
// Test that Sync is left in error when cookies are cleared.
IN_PROC_BROWSER_TEST_F(DiceBrowsingDataRemoverBrowserTest, SyncTokenError) {
Profile* profile = browser()->profile();
// Set a Gaia cookie.
ASSERT_TRUE(SetGaiaCookieForProfile(profile));
// Set a Sync account with authentication error.
const char kAccountId[] = "account_id";
AccountInfo primary_account =
AddAccountToProfile(kAccountId, profile, /*is_primary=*/true);
auto* identity_manager = IdentityManagerFactory::GetForProfile(profile);
signin::UpdatePersistentErrorOfRefreshTokenForAccount(
identity_manager, primary_account.account_id,
GoogleServiceAuthError::FromInvalidGaiaCredentialsReason(
GoogleServiceAuthError::InvalidGaiaCredentialsReason::
CREDENTIALS_REJECTED_BY_SERVER));
// Clear cookies.
RemoveAndWait(content::BrowsingDataRemover::DATA_TYPE_COOKIES);
// Check that the account was not removed and Sync was paused.
EXPECT_TRUE(
identity_manager->HasAccountWithRefreshToken(primary_account.account_id));
EXPECT_EQ(
GoogleServiceAuthError::InvalidGaiaCredentialsReason::
CREDENTIALS_REJECTED_BY_SERVER,
identity_manager
->GetErrorStateOfRefreshTokenForAccount(primary_account.account_id)
.GetInvalidGaiaCredentialsReason());
}
// Test that the tokens are revoked when cookies are cleared when there is no
// primary account.
IN_PROC_BROWSER_TEST_F(DiceBrowsingDataRemoverBrowserTest, NoSync) {
Profile* profile = browser()->profile();
// Set a Gaia cookie.
ASSERT_TRUE(SetGaiaCookieForProfile(profile));
// Set a non-Sync account.
const char kAccountId[] = "account_id";
AccountInfo secondary_account =
AddAccountToProfile(kAccountId, profile, /*is_primary=*/false);
// Clear cookies.
RemoveAndWait(content::BrowsingDataRemover::DATA_TYPE_COOKIES);
// Check that the account was removed.
auto* identity_manager = IdentityManagerFactory::GetForProfile(profile);
EXPECT_FALSE(identity_manager->HasAccountWithRefreshToken(
secondary_account.account_id));
}
#endif
// The call to Remove() should crash in debug (DCHECK), but the browser-test
// process model prevents using a death test.
#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON)
// Test BrowsingDataRemover for prohibited downloads. Note that this only
// really exercises the code in a Release build.
IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverBrowserTest, DownloadProhibited) {
PrefService* prefs = GetBrowser()->profile()->GetPrefs();
prefs->SetBoolean(prefs::kAllowDeletingBrowserHistory, false);
DownloadAnItem();
RemoveAndWait(content::BrowsingDataRemover::DATA_TYPE_DOWNLOADS);
VerifyDownloadCount(1u);
}
#endif
// Verify VideoDecodePerfHistory is cleared when deleting all history from
// beginning of time.
IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverBrowserTest, VideoDecodePerfHistory) {
media::VideoDecodePerfHistory* video_decode_perf_history =
GetBrowser()->profile()->GetVideoDecodePerfHistory();
// Save a video decode record. Note: we avoid using a web page to generate the
// stats as this takes at least 5 seconds and even then is not a guarantee
// depending on scheduler. Manual injection is quick and non-flaky.
const media::VideoCodecProfile kProfile = media::VP9PROFILE_PROFILE0;
const gfx::Size kSize(100, 200);
const int kFrameRate = 30;
const int kFramesDecoded = 1000;
const int kFramesDropped = .9 * kFramesDecoded;
const int kFramesPowerEfficient = 0;
const url::Origin kOrigin = url::Origin::Create(GURL("http://example.com"));
const bool kIsTopFrame = true;
const uint64_t kPlayerId = 1234u;
media::mojom::PredictionFeatures prediction_features;
prediction_features.profile = kProfile;
prediction_features.video_size = kSize;
prediction_features.frames_per_sec = kFrameRate;
media::mojom::PredictionTargets prediction_targets;
prediction_targets.frames_decoded = kFramesDecoded;
prediction_targets.frames_dropped = kFramesDropped;
prediction_targets.frames_power_efficient = kFramesPowerEfficient;
{
base::RunLoop run_loop;
video_decode_perf_history->GetSaveCallback().Run(
ukm::kInvalidSourceId, kIsTopFrame, prediction_features,
prediction_targets, kPlayerId, run_loop.QuitWhenIdleClosure());
run_loop.Run();
}
// Verify history exists.
// Expect |is_smooth| = false and |is_power_efficient| = false given that 90%
// of recorded frames were dropped and 0 were power efficient.
bool is_smooth = true;
bool is_power_efficient = true;
{
base::RunLoop run_loop;
video_decode_perf_history->GetPerfInfo(
media::mojom::PredictionFeatures::New(prediction_features),
base::BindOnce(&BrowsingDataRemoverBrowserTest::OnVideoDecodePerfInfo,
base::Unretained(this), &run_loop, &is_smooth,
&is_power_efficient));
run_loop.Run();
}
EXPECT_FALSE(is_smooth);
EXPECT_FALSE(is_power_efficient);
// Clear history.
RemoveAndWait(chrome_browsing_data_remover::DATA_TYPE_HISTORY);
// Verify history no longer exists. Both |is_smooth| and |is_power_efficient|
// should now report true because the VideoDecodePerfHistory optimistically
// returns true when it has no data.
{
base::RunLoop run_loop;
video_decode_perf_history->GetPerfInfo(
media::mojom::PredictionFeatures::New(prediction_features),
base::BindOnce(&BrowsingDataRemoverBrowserTest::OnVideoDecodePerfInfo,
base::Unretained(this), &run_loop, &is_smooth,
&is_power_efficient));
run_loop.Run();
}
EXPECT_TRUE(is_smooth);
EXPECT_TRUE(is_power_efficient);
}
// Verify WebrtcVideoPerfHistory is cleared when deleting all history from
// beginning of time.
IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverBrowserTest, WebrtcVideoPerfHistory) {
media::WebrtcVideoPerfHistory* webrtc_video_perf_history =
GetBrowser()->profile()->GetWebrtcVideoPerfHistory();
// Save a video decode record. Note: we avoid using a web page to generate the
// stats as this takes at least 5 seconds and even then is not a guarantee
// depending on scheduler. Manual injection is quick and non-flaky.
const media::VideoCodecProfile kProfile = media::VP9PROFILE_PROFILE0;
const int kVideoPixels = 1920 * 1080;
const int kFrameRate = 30;
const int kFramesProcessed = 1000;
const int kKeyFramesProcessed = 11;
const float kP99ProcessingTimeMs = 100.0;
media::mojom::WebrtcPredictionFeatures features;
features.is_decode_stats = true;
features.profile = kProfile;
features.video_pixels = kVideoPixels;
features.hardware_accelerated = false;
media::mojom::WebrtcVideoStats video_stats;
video_stats.frames_processed = kFramesProcessed;
video_stats.key_frames_processed = kKeyFramesProcessed;
video_stats.p99_processing_time_ms = kP99ProcessingTimeMs;
{
base::RunLoop run_loop;
webrtc_video_perf_history->GetSaveCallback().Run(features, video_stats,
run_loop.QuitClosure());
run_loop.Run();
}
// Verify history exists.
// Expect |is_smooth| = false given that the 99th percentile processing time
// is 100 ms.
{
base::RunLoop run_loop;
webrtc_video_perf_history->GetPerfInfo(
media::mojom::WebrtcPredictionFeatures::New(features), kFrameRate,
base::BindLambdaForTesting([&](bool smooth) {
EXPECT_FALSE(smooth);
run_loop.Quit();
}));
run_loop.Run();
}
auto filter_builder = BrowsingDataFilterBuilder::Create(
BrowsingDataFilterBuilder::Mode::kDelete);
filter_builder->AddRegisterableDomain("example.com");
RemoveWithFilterAndWait(chrome_browsing_data_remover::FILTERABLE_DATA_TYPES,
std::move(filter_builder));
// This data type doesn't implement per-origin deletion so just test that
// nothing got removed.
{
base::RunLoop run_loop;
webrtc_video_perf_history->GetPerfInfo(
media::mojom::WebrtcPredictionFeatures::New(features), kFrameRate,
base::BindLambdaForTesting([&](bool smooth) {
EXPECT_FALSE(smooth);
run_loop.Quit();
}));
run_loop.Run();
}
// Clear history.
RemoveAndWait(chrome_browsing_data_remover::DATA_TYPE_HISTORY);
// Verify history no longer exists. |is_smooth| should now report true because
// the WebrtcVideoPerfHistory optimistically returns true when it has no data.
{
base::RunLoop run_loop;
webrtc_video_perf_history->GetPerfInfo(
media::mojom::WebrtcPredictionFeatures::New(features), kFrameRate,
base::BindLambdaForTesting([&](bool smooth) {
EXPECT_TRUE(smooth);
run_loop.Quit();
}));
run_loop.Run();
}
}
// Verifies that cache deletion finishes successfully. Completes deletion of
// cache should leave it empty, and partial deletion should leave nonzero
// amount of data. Note that this tests the integration of BrowsingDataRemover
// with ConditionalCacheDeletionHelper. Whether ConditionalCacheDeletionHelper
// actually deletes the correct entries is tested
// in ConditionalCacheDeletionHelperBrowsertest.
// TODO(crbug.com/41373874): check the cache size instead of stopping the server
// and loading the request again.
IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverBrowserTest, Cache) {
// Load several resources.
GURL url1 = embedded_test_server()->GetURL("/cachetime");
GURL url2 = embedded_test_server()->GetURL(kExampleHost, "/cachetime");
ASSERT_FALSE(url::IsSameOriginWith(url1, url2));
EXPECT_EQ(net::OK, content::LoadBasicRequest(network_context(), url1));
EXPECT_EQ(net::OK, content::LoadBasicRequest(network_context(), url2));
// Check that the cache has been populated by revisiting these pages with the
// server stopped.
ASSERT_TRUE(embedded_test_server()->ShutdownAndWaitUntilComplete());
EXPECT_EQ(net::OK, content::LoadBasicRequest(network_context(), url1));
EXPECT_EQ(net::OK, content::LoadBasicRequest(network_context(), url2));
// Partially delete cache data. Delete data for localhost, which is the origin
// of |url1|, but not for |kExampleHost|, which is the origin of |url2|.
std::unique_ptr<BrowsingDataFilterBuilder> filter_builder =
BrowsingDataFilterBuilder::Create(
BrowsingDataFilterBuilder::Mode::kDelete);
filter_builder->AddOrigin(url::Origin::Create(url1));
RemoveWithFilterAndWait(content::BrowsingDataRemover::DATA_TYPE_CACHE,
std::move(filter_builder));
// After the partial deletion, the cache should be smaller but still nonempty.
EXPECT_NE(net::OK, content::LoadBasicRequest(network_context(), url1));
EXPECT_EQ(net::OK, content::LoadBasicRequest(network_context(), url2));
// Another partial deletion with the same filter should have no effect.
filter_builder = BrowsingDataFilterBuilder::Create(
BrowsingDataFilterBuilder::Mode::kDelete);
filter_builder->AddOrigin(url::Origin::Create(url1));
RemoveWithFilterAndWait(content::BrowsingDataRemover::DATA_TYPE_CACHE,
std::move(filter_builder));
EXPECT_NE(net::OK, content::LoadBasicRequest(network_context(), url1));
EXPECT_EQ(net::OK, content::LoadBasicRequest(network_context(), url2));
// Delete the remaining data.
RemoveAndWait(content::BrowsingDataRemover::DATA_TYPE_CACHE);
// The cache should be empty.
EXPECT_NE(net::OK, content::LoadBasicRequest(network_context(), url1));
EXPECT_NE(net::OK, content::LoadBasicRequest(network_context(), url2));
}
// Crashes the network service while clearing the HTTP cache to make sure the
// clear operation does complete.
// Note that there is a race between crashing the network service and clearing
// the cache, so the test might flakily fail if the tested behavior does not
// work.
// TODO(crbug.com/40563720): test retry behavior by validating the cache is
// empty after the crash.
IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverBrowserTest,
ClearCacheAndNetworkServiceCrashes) {
if (!content::IsOutOfProcessNetworkService())
return;
// Clear the cached data with a task posted to crash the network service.
// The task should be run while waiting for the cache clearing operation to
// complete, hopefully it happens before the cache has been cleared.
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE,
base::BindOnce(&content::BrowserTestBase::SimulateNetworkServiceCrash,
base::Unretained(this)));
RemoveAndWait(content::BrowsingDataRemover::DATA_TYPE_CACHE);
}
// Verifies that the network quality prefs are cleared.
IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverBrowserTest, VerifyNQECacheCleared) {
base::HistogramTester histogram_tester;
RemoveAndWait(content::BrowsingDataRemover::DATA_TYPE_CACHE);
// Wait until there is at least one sample in NQE.PrefsSizeOnClearing.
bool histogram_populated = false;
for (size_t attempt = 0; attempt < 3; ++attempt) {
const std::vector<base::Bucket> buckets =
histogram_tester.GetAllSamples("NQE.PrefsSizeOnClearing");
for (const auto& bucket : buckets) {
if (bucket.count > 0) {
histogram_populated = true;
break;
}
}
if (histogram_populated)
break;
// Retry fetching the histogram since it's not populated yet.
content::FetchHistogramsFromChildProcesses();
metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
base::RunLoop().RunUntilIdle();
}
histogram_tester.ExpectTotalCount("NQE.PrefsSizeOnClearing", 1);
}
IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverBrowserTest,
ExternalProtocolHandlerPerOriginPrefs) {
Profile* profile = GetBrowser()->profile();
url::Origin test_origin = url::Origin::Create(GURL("https://example.test/"));
const std::string serialized_test_origin = test_origin.Serialize();
base::Value::Dict allowed_protocols_for_origin;
allowed_protocols_for_origin.Set("tel", true);
base::Value::Dict origin_pref;
origin_pref.Set(serialized_test_origin,
std::move(allowed_protocols_for_origin));
profile->GetPrefs()->SetDict(prefs::kProtocolHandlerPerOriginAllowedProtocols,
std::move(origin_pref));
ExternalProtocolHandler::BlockState block_state =
ExternalProtocolHandler::GetBlockState("tel", &test_origin, profile);
ASSERT_EQ(ExternalProtocolHandler::DONT_BLOCK, block_state);
RemoveAndWait(chrome_browsing_data_remover::DATA_TYPE_SITE_DATA);
block_state =
ExternalProtocolHandler::GetBlockState("tel", &test_origin, profile);
ASSERT_EQ(ExternalProtocolHandler::UNKNOWN, block_state);
}
IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverBrowserTest, HistoryDeletion) {
const std::string kType = "History";
GURL url = embedded_test_server()->GetURL("/browsing_data/site_data.html");
// Create a new tab to avoid confusion from having a NTP navigation entry.
ui_test_utils::NavigateToURLWithDisposition(
GetBrowser(), url, WindowOpenDisposition::NEW_FOREGROUND_TAB,
ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP);
EXPECT_FALSE(HasDataForType(kType));
SetDataForType(kType);
EXPECT_TRUE(HasDataForType(kType));
// Remove history from navigation to site_data.html.
RemoveAndWait(chrome_browsing_data_remover::DATA_TYPE_HISTORY);
EXPECT_FALSE(HasDataForType(kType));
SetDataForType(kType);
EXPECT_TRUE(HasDataForType(kType));
// Remove history from previous pushState() call in setHistory().
RemoveAndWait(chrome_browsing_data_remover::DATA_TYPE_HISTORY);
EXPECT_FALSE(HasDataForType(kType));
}
// Storage Buckets
class BrowsingDataRemoverStorageBucketsBrowserTest
: public BrowsingDataRemoverBrowserTest {
public:
BrowsingDataRemoverStorageBucketsBrowserTest() {
features_.InitWithFeatures({blink::features::kStorageBuckets,
net::features::kThirdPartyStoragePartitioning},
{});
}
void ClearSiteDataAndWait(
const url::Origin& origin,
const std::optional<blink::StorageKey>& storage_key,
const std::set<std::string>& storage_buckets_to_remove) {
base::RunLoop loop;
content::ClearSiteDataTypeSet clear_site_data_types =
content::ClearSiteDataTypeSet::All();
// We're clearing some storage buckets and not all of them.
clear_site_data_types.Remove(content::ClearSiteDataType::kStorage);
content::ClearSiteData(
GetBrowser()->profile()->GetWeakPtr(),
/*storage_partition_config=*/std::nullopt,
/*origin=*/origin, clear_site_data_types,
/*storage_buckets_to_remove=*/storage_buckets_to_remove,
/*avoid_closing_connections=*/true,
/*cookie_partition_key=*/std::nullopt,
/*storage_key=*/storage_key,
/*partitioned_state_allowed_only=*/false,
/*callback=*/loop.QuitClosure());
loop.Run();
}
private:
base::test::ScopedFeatureList features_;
};
IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverStorageBucketsBrowserTest,
ClearSiteDataStorageBuckets) {
GURL url("https://example.com");
url::Origin origin = url::Origin::Create(url);
const auto storage_key = blink::StorageKey::CreateFirstParty(origin);
storage::QuotaManager* quota_manager =
GetBrowser()->profile()->GetDefaultStoragePartition()->GetQuotaManager();
auto* quota_manager_proxy = quota_manager->proxy();
quota_manager_proxy->CreateBucketForTesting(
storage_key, "drafts", base::SequencedTaskRunner::GetCurrentDefault(),
base::BindOnce(
[](storage::QuotaErrorOr<storage::BucketInfo> error_or_bucket_info) {
}));
quota_manager_proxy->CreateBucketForTesting(
storage_key, "inbox", base::SequencedTaskRunner::GetCurrentDefault(),
base::BindOnce(
[](storage::QuotaErrorOr<storage::BucketInfo> error_or_bucket_info) {
}));
quota_manager_proxy->CreateBucketForTesting(
storage_key, "attachments",
base::SequencedTaskRunner::GetCurrentDefault(),
base::BindOnce(
[](storage::QuotaErrorOr<storage::BucketInfo> error_or_bucket_info) {
}));
ClearSiteDataAndWait(origin, storage_key, {"drafts", "attachments"});
quota_manager_proxy->GetBucketsForStorageKey(
storage_key,
/*delete_expired*/ false, base::SequencedTaskRunner::GetCurrentDefault(),
base::BindOnce([](storage::QuotaErrorOr<std::set<storage::BucketInfo>>
error_or_buckets) {
EXPECT_EQ(1u, error_or_buckets.value().size());
}));
}
const char kDelegateHistogramPrefix[] =
"History.ClearBrowsingData.Duration.ChromeTask.";
const char kImplHistogramPrefix[] = "History.ClearBrowsingData.Duration.Task.";
// Add data types here that support filtering and only delete data that matches
// the BrowsingDataFilterBuilder.
const std::vector<std::string_view> kSupportsOriginFilteringImpl{
"AuthCache", "EmbedderData", "HttpCache",
"NetworkErrorLogging", "PrefetchCache", "PreflightCache",
"PrerenderCache", "ReportingCache", "SharedDictionary",
"StoragePartition", "Synchronous", "TrustTokens",
};
const std::vector<std::string_view> kSupportsOriginFilteringDelegate{
#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_WIN)
"CdmLicenses",
#endif
"Cookies", "DisableAutoSigninForProfilePasswords",
"DomainReliability", "MediaDeviceSalts",
"Synchronous",
};
// This test ensures that all deletions that are part of FILTERABLE_DATA_TYPES
// fully support the BrowsingDataFilterBuilder if they are running for
// origin-specific deletions. Ideally, every web-visible data type should
// support filtering. If you implemented filtering already, just add your type
// to kSupportsOriginFiltering above.
//
// If it is not important that your data is cleared with per-origin deletions,
// you can add your type to kDoesNotSupportOriginFiltering and ensure that
// deletions are only performed when the filter builder
// MatchesMostOriginsAndDomains().
IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverBrowserTest, FullyFilteredDataTypes) {
base::HistogramTester tester;
auto filter_builder = BrowsingDataFilterBuilder::Create(
BrowsingDataFilterBuilder::Mode::kDelete);
filter_builder->AddRegisterableDomain("example.com");
RemoveWithFilterAndWait(chrome_browsing_data_remover::FILTERABLE_DATA_TYPES,
std::move(filter_builder));
EXPECT_THAT(GetHistogramSuffixes(tester, kImplHistogramPrefix),
UnorderedElementsAreArray(kSupportsOriginFilteringImpl));
EXPECT_THAT(GetHistogramSuffixes(tester, kDelegateHistogramPrefix),
UnorderedElementsAreArray(kSupportsOriginFilteringDelegate));
}
// Add data types here that do not support the BrowsingDataFilterBuilder.
// These deletions should only run when the mode of the filter builder is
// "kPreserve" and MatchesMostOriginsAndDomains() is true. Otherwise data for
// these types will be cleared when per-origin deletions like those from the
// Clear-Site-Data header are performed.
const std::vector<std::string_view> kDoesNotSupportOriginFilteringImpl{
"CodeCaches",
"NetworkHistory",
};
const std::vector<std::string_view> kDoesNotSupportOriginFilteringDelegate{
"FaviconCacheExpiration",
#if BUILDFLAG(ENABLE_DOWNGRADE_PROCESSING)
"UserDataSnapshot",
#endif
"WebrtcEventLogs",
#if BUILDFLAG(IS_CHROMEOS)
"TpmAttestationKeys",
#endif
};
// See comment on FullyFilteredDataTypes test for advice when this test fails.
IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverBrowserTest, AllFilterableDataTypes) {
base::HistogramTester tester;
auto filter_builder = BrowsingDataFilterBuilder::Create(
BrowsingDataFilterBuilder::Mode::kPreserve);
filter_builder->AddRegisterableDomain("example.com");
RemoveWithFilterAndWait(chrome_browsing_data_remover::FILTERABLE_DATA_TYPES,
std::move(filter_builder));
std::vector<std::string> all_impl_types;
AppendRange(all_impl_types, kSupportsOriginFilteringImpl);
AppendRange(all_impl_types, kDoesNotSupportOriginFilteringImpl);
EXPECT_THAT(GetHistogramSuffixes(tester, kImplHistogramPrefix),
UnorderedElementsAreArray(all_impl_types));
std::vector<std::string> all_delegate_types;
AppendRange(all_delegate_types, kSupportsOriginFilteringDelegate);
AppendRange(all_delegate_types, kDoesNotSupportOriginFilteringDelegate);
EXPECT_THAT(GetHistogramSuffixes(tester, kDelegateHistogramPrefix),
UnorderedElementsAreArray(all_delegate_types));
}
// Parameterized to run tests for different deletion time ranges.
class BrowsingDataRemoverBrowserTestP
: public BrowsingDataRemoverBrowserTest,
public testing::WithParamInterface<TimeEnum> {};
IN_PROC_BROWSER_TEST_P(BrowsingDataRemoverBrowserTestP, CookieDeletion) {
TestSiteData("Cookie", GetParam());
}
IN_PROC_BROWSER_TEST_P(BrowsingDataRemoverBrowserTestP,
CookieIncognitoDeletion) {
UseIncognitoBrowser();
TestSiteData("Cookie", GetParam());
}
// Regression test for https://crbug.com/1216406.
// TODO(crbug.com/413259587): Re-enable this test once the flakiness is fixed.
#if BUILDFLAG(IS_WIN)
#define MAYBE_BrowserContextDestructionVsCookieRemoval \
DISABLED_BrowserContextDestructionVsCookieRemoval
#else
#define MAYBE_BrowserContextDestructionVsCookieRemoval \
BrowserContextDestructionVsCookieRemoval
#endif
IN_PROC_BROWSER_TEST_P(BrowsingDataRemoverBrowserTestP,
MAYBE_BrowserContextDestructionVsCookieRemoval) {
// Open an incognito browser.
UseIncognitoBrowser();
// Set a cookie.
const char kDataType[] = "Cookie";
GURL url = embedded_test_server()->GetURL("/browsing_data/site_data.html");
ASSERT_TRUE(ui_test_utils::NavigateToURL(GetBrowser(), url));
SetDataForType(kDataType);
EXPECT_EQ(1, GetSiteDataCount());
ExpectTotalModelCount(1);
EXPECT_TRUE(HasDataForType(kDataType));
// Start data removal. This will CreateTaskCompletionClosureForMojo and
// register it as a completion callback for mojo calls to NetworkContext
// and other StorageParition-owned mojo::Remote(s).
//
// kRemoveMask contains:
// - DATA_TYPE_SITE_DATA - cargo-culted default from other tests
// - DEFERRED_COOKIE_DELETION_DATA_TYPES - to get non-empty result from
// ChromeBrowsingDataRemoverDelegate::GetDomainsForDeferredCookieDeletion
// (which is needed to touch StoragePartition in
// BrowsingDataRemoverImpl::OnTaskComplete when it is called later,
// after starting destruction of the BrowserContext - see the description
// of the next test step below).
constexpr uint64_t kRemoveMask =
chrome_browsing_data_remover::DATA_TYPE_SITE_DATA |
chrome_browsing_data_remover::DEFERRED_COOKIE_DELETION_DATA_TYPES;
content::BrowserContext* browser_context = GetBrowser()->profile();
content::BrowsingDataRemover* remover =
browser_context->GetBrowsingDataRemover();
content::BrowsingDataRemoverCompletionObserver completion_observer(remover);
remover->RemoveAndReply(
base::Time(), // delete_begin
base::Time::Max(), // delete_end
kRemoveMask, content::BrowsingDataRemover::ORIGIN_TYPE_UNPROTECTED_WEB,
&completion_observer);
// Close the incognito browser. This will tear down its
// Profile/BrowserContext, which will tear down the StoragePartition, which
// will tear down some mojo::Remote(s), which will end up running the closures
// returned from CreateTaskCompletionClosureForMojo (see the previous test
// step), which will run BrowsingDataRemoverImpl::OnTaskComplete. In
// https://crbug.com/1216406 OnTaskComplete would attempt to use its
// `browser_context_` (half-way destructed at this point) to get a
// StoragePartition and this would lead to DumpWithoutCrashing initially (and
// potentially crashes down the line).
CloseBrowserSynchronously(GetBrowser());
// Verify that the completion observer will get notified, even if there might
// have been a failure with the removal.
completion_observer.BlockUntilCompletion();
// Expect that removing the cookies failed, because the StoragePartition has
// been already gone by the time BrowsingDataRemoverImpl::OnTaskComplete run.
EXPECT_TRUE(content::StoragePartition::REMOVE_DATA_MASK_COOKIES &
completion_observer.failed_data_types());
}
IN_PROC_BROWSER_TEST_P(BrowsingDataRemoverBrowserTestP, SessionCookieDeletion) {
TestSiteData("SessionCookie", GetParam());
}
IN_PROC_BROWSER_TEST_P(BrowsingDataRemoverBrowserTestP, LocalStorageDeletion) {
TestSiteData("LocalStorage", GetParam());
}
IN_PROC_BROWSER_TEST_P(BrowsingDataRemoverBrowserTestP,
LocalStorageIncognitoDeletion) {
UseIncognitoBrowser();
TestSiteData("LocalStorage", GetParam());
}
// TODO(crbug.com/41348517): DISABLED until session storage is working
// correctly. Add Incognito variant when this is re-enabled.
IN_PROC_BROWSER_TEST_P(BrowsingDataRemoverBrowserTestP,
DISABLED_SessionStorageDeletion) {
TestSiteData("SessionStorage", GetParam());
}
// SessionStorage is not supported by site data counting and the cookie tree
// model but we can test the web visible behavior.
IN_PROC_BROWSER_TEST_P(BrowsingDataRemoverBrowserTestP,
SessionStorageDeletionWebOnly) {
GURL url = embedded_test_server()->GetURL("/browsing_data/site_data.html");
ASSERT_TRUE(ui_test_utils::NavigateToURL(GetBrowser(), url));
const std::string type = "SessionStorage";
EXPECT_FALSE(HasDataForType(type));
SetDataForType(type);
EXPECT_TRUE(HasDataForType(type));
RemoveAndWait(chrome_browsing_data_remover::DATA_TYPE_SITE_DATA, GetParam());
EXPECT_FALSE(HasDataForType(type));
}
// Test that session storage is not counted until crbug.com/772337 is fixed.
IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverBrowserTest, SessionStorageCounting) {
EXPECT_EQ(0, GetSiteDataCount());
ExpectTotalModelCount(0);
GURL url = embedded_test_server()->GetURL("/browsing_data/site_data.html");
ASSERT_TRUE(ui_test_utils::NavigateToURL(GetBrowser(), url));
EXPECT_EQ(0, GetSiteDataCount());
ExpectTotalModelCount(0);
SetDataForType("SessionStorage");
EXPECT_EQ(0, GetSiteDataCount());
ExpectTotalModelCount(0);
EXPECT_TRUE(HasDataForType("SessionStorage"));
}
IN_PROC_BROWSER_TEST_P(BrowsingDataRemoverBrowserTestP, ServiceWorkerDeletion) {
TestSiteData("ServiceWorker", GetParam());
}
IN_PROC_BROWSER_TEST_P(BrowsingDataRemoverBrowserTestP,
ServiceWorkerIncognitoDeletion) {
UseIncognitoBrowser();
TestSiteData("ServiceWorker", GetParam());
}
IN_PROC_BROWSER_TEST_P(BrowsingDataRemoverBrowserTestP, CacheStorageDeletion) {
TestSiteData("CacheStorage", GetParam());
}
IN_PROC_BROWSER_TEST_P(BrowsingDataRemoverBrowserTestP,
CacheStorageIncognitoDeletion) {
UseIncognitoBrowser();
TestSiteData("CacheStorage", GetParam());
}
IN_PROC_BROWSER_TEST_P(BrowsingDataRemoverBrowserTestP, FileSystemDeletion) {
TestSiteData("FileSystem", GetParam());
}
IN_PROC_BROWSER_TEST_P(BrowsingDataRemoverBrowserTestP,
FileSystemIncognitoDeletion) {
UseIncognitoBrowser();
TestSiteData("FileSystem", GetParam());
}
// Test that empty filesystems are deleted correctly.
IN_PROC_BROWSER_TEST_P(BrowsingDataRemoverBrowserTestP,
EmptyFileSystemDeletion) {
TestEmptySiteData("FileSystem", GetParam());
}
// Test that empty filesystems are deleted correctly in incognito mode.
IN_PROC_BROWSER_TEST_P(BrowsingDataRemoverBrowserTestP,
EmptyFileSystemIncognitoDeletion) {
UseIncognitoBrowser();
TestEmptySiteData("FileSystem", GetParam());
}
IN_PROC_BROWSER_TEST_P(BrowsingDataRemoverBrowserTestP, IndexedDbDeletion) {
TestSiteData("IndexedDb", GetParam());
}
IN_PROC_BROWSER_TEST_P(BrowsingDataRemoverBrowserTestP,
IndexedDbIncognitoDeletion) {
UseIncognitoBrowser();
TestSiteData("IndexedDb", GetParam());
}
// Test that empty indexed dbs are deleted correctly.
IN_PROC_BROWSER_TEST_P(BrowsingDataRemoverBrowserTestP, EmptyIndexedDb) {
TestEmptySiteData("IndexedDb", GetParam());
}
#if BUILDFLAG(ENABLE_LIBRARY_CDMS)
// Test Media Licenses by creating one and checking it is counted by the
// cookie counter. Then delete it and check that the cookie counter is back
// to zero.
IN_PROC_BROWSER_TEST_P(BrowsingDataRemoverBrowserTestP, MediaLicenseDeletion) {
const std::string kMediaLicenseType = "MediaLicense";
const TimeEnum delete_begin = GetParam();
EXPECT_EQ(0, GetSiteDataCount());
GURL url =
embedded_test_server()->GetURL("/browsing_data/media_license.html");
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
EXPECT_EQ(0, GetSiteDataCount());
ExpectTotalModelCount(0);
EXPECT_FALSE(HasDataForType(kMediaLicenseType));
SetDataForType(kMediaLicenseType);
EXPECT_EQ(1, GetSiteDataCount());
ExpectTotalModelCount(1);
EXPECT_TRUE(HasDataForType(kMediaLicenseType));
// Try to remove the Media Licenses using a time frame up until an hour ago,
// which should not remove the recently created Media License.
RemoveAndWait(chrome_browsing_data_remover::DATA_TYPE_SITE_DATA, delete_begin,
TimeEnum::kLastHour);
EXPECT_EQ(1, GetSiteDataCount());
ExpectTotalModelCount(1);
EXPECT_TRUE(HasDataForType(kMediaLicenseType));
// Now try with a time range that includes the current time, which should
// clear the Media License created for this test.
RemoveAndWait(chrome_browsing_data_remover::DATA_TYPE_SITE_DATA, delete_begin,
TimeEnum::kMax);
EXPECT_EQ(0, GetSiteDataCount());
ExpectTotalModelCount(0);
EXPECT_FALSE(HasDataForType(kMediaLicenseType));
}
// Create and save a media license (which will be deleted in the following
// test).
IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverBrowserTest,
PRE_MediaLicenseTimedDeletion) {
const std::string kMediaLicenseType = "MediaLicense";
EXPECT_EQ(0, GetSiteDataCount());
GURL url =
embedded_test_server()->GetURL("/browsing_data/media_license.html");
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
EXPECT_EQ(0, GetSiteDataCount());
ExpectTotalModelCount(0);
EXPECT_FALSE(HasDataForType(kMediaLicenseType));
SetDataForType(kMediaLicenseType);
EXPECT_EQ(1, GetSiteDataCount());
ExpectTotalModelCount(1);
EXPECT_TRUE(HasDataForType(kMediaLicenseType));
}
// Create and save a second media license, and then verify that timed deletion
// selects the correct license to delete.
IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverBrowserTest,
MediaLicenseTimedDeletion) {
const std::string kMediaLicenseType = "MediaLicense";
// As the PRE_ test should run first, there should be one media license
// still stored. The time of it's creation should be sometime before
// this test starts. This license will be for a different origin, and so wont
// affect HasDataForType, but it still exists.
LOG(INFO) << "MediaLicenseTimedDeletion starting @ " << kStartTime;
EXPECT_EQ(1, GetSiteDataCount());
GURL url =
embedded_test_server()->GetURL("/browsing_data/media_license.html");
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
#if BUILDFLAG(IS_MAC)
// On some Macs the file system uses second granularity. So before
// creating the second license, delay for 1 second so that the new
// license's time is not the same second as |kStartTime|.
base::PlatformThread::Sleep(base::Seconds(1));
#endif
// This test should use a different domain than the PRE_ test, so there
// should be no existing media license for it.
// Note that checking HasDataForType() may result in an empty file being
// created. Deleting licenses checks for any file within the time range
// specified in order to delete all the files for the domain, so this may
// cause problems (especially with Macs that use second granularity).
// http://crbug.com/909829.
EXPECT_FALSE(HasDataForType(kMediaLicenseType));
SetDataForType(kMediaLicenseType);
ExpectTotalModelCount(1);
EXPECT_TRUE(HasDataForType(kMediaLicenseType));
// As Clear Browsing Data typically deletes recent data (e.g. last hour,
// last day, etc.), try to remove the Media Licenses created since the
// the start of this test, which should only delete the just created
// media license, and leave the one created by the PRE_ test.
RemoveAndWait(chrome_browsing_data_remover::DATA_TYPE_SITE_DATA,
TimeEnum::kStart);
EXPECT_EQ(1, GetSiteDataCount());
ExpectTotalModelCount(1);
EXPECT_FALSE(HasDataForType(kMediaLicenseType));
// Now try with a time range that includes all time, which should
// clear the media license created by the PRE_ test.
RemoveAndWait(chrome_browsing_data_remover::DATA_TYPE_SITE_DATA);
EXPECT_EQ(0, GetSiteDataCount());
ExpectTotalModelCount(0);
}
IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverBrowserTest,
MediaLicenseDeletionWithFilter) {
const std::string kMediaLicenseType = "MediaLicense";
GURL url =
embedded_test_server()->GetURL("/browsing_data/media_license.html");
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
ExpectTotalModelCount(0);
EXPECT_FALSE(HasDataForType(kMediaLicenseType));
SetDataForType(kMediaLicenseType);
ExpectTotalModelCount(1);
EXPECT_TRUE(HasDataForType(kMediaLicenseType));
// Try to remove the Media Licenses using a deletelist that doesn't include
// the current URL. Media License should not be deleted.
std::unique_ptr<BrowsingDataFilterBuilder> filter_builder =
BrowsingDataFilterBuilder::Create(
BrowsingDataFilterBuilder::Mode::kDelete);
filter_builder->AddOrigin(
url::Origin::CreateFromNormalizedTuple("https", "test-origin", 443));
RemoveWithFilterAndWait(
content::BrowsingDataRemover::DATA_TYPE_MEDIA_LICENSES,
std::move(filter_builder));
ExpectTotalModelCount(1);
// Now try with a preservelist that includes the current URL. Media License
// should not be deleted.
filter_builder = BrowsingDataFilterBuilder::Create(
BrowsingDataFilterBuilder::Mode::kPreserve);
filter_builder->AddOrigin(url::Origin::Create(url));
RemoveWithFilterAndWait(
content::BrowsingDataRemover::DATA_TYPE_MEDIA_LICENSES,
std::move(filter_builder));
ExpectTotalModelCount(1);
// Now try with a deletelist that includes the current URL. Media License
// should be deleted this time.
filter_builder = BrowsingDataFilterBuilder::Create(
BrowsingDataFilterBuilder::Mode::kDelete);
filter_builder->AddOrigin(url::Origin::Create(url));
RemoveWithFilterAndWait(
content::BrowsingDataRemover::DATA_TYPE_MEDIA_LICENSES,
std::move(filter_builder));
ExpectTotalModelCount(0);
}
#endif // BUILDFLAG(ENABLE_LIBRARY_CDMS)
const std::vector<std::string> kStorageTypes{
"Cookie", "LocalStorage", "FileSystem", "SessionStorage",
"IndexedDb", "ServiceWorker", "CacheStorage", "MediaLicense",
};
// Test that storage doesn't leave any traces on disk.
IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverBrowserTest,
PRE_PRE_StorageRemovedFromDisk) {
// Checking leveldb content fails in most cases. See
// https://crbug.com/1238325.
ASSERT_EQ(0, CheckUserDirectoryForString(kLocalHost, {},
/*check_leveldb_content=*/false));
ASSERT_EQ(0, GetSiteDataCount());
ExpectTotalModelCount(0);
// To use secure-only features on a host name, we need an https server.
net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
https_server.SetSSLConfig(
net::EmbeddedTestServer::CERT_COMMON_NAME_IS_DOMAIN);
base::FilePath path;
base::PathService::Get(content::DIR_TEST_DATA, &path);
https_server.ServeFilesFromDirectory(path);
ASSERT_TRUE(https_server.Start());
GURL url = https_server.GetURL(kLocalHost, "/browsing_data/site_data.html");
ASSERT_TRUE(ui_test_utils::NavigateToURL(GetBrowser(), url));
for (const std::string& type : kStorageTypes) {
SetDataForType(type);
EXPECT_TRUE(HasDataForType(type));
}
// TODO(crbug.com/40577815): Add more datatypes for testing. E.g.
// notifications, payment handler, content settings, autofill, ...?
}
// Restart after creating the data to ensure that everything was written to
// disk.
//
// This depends on session restore being explicitly disabled by the test harness
// above. Otherwise, we'll restore the tabs, delete the data on disk, and the
// still-open tabs can get re-persisted.
IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverBrowserTest,
PRE_StorageRemovedFromDisk) {
EXPECT_EQ(1, GetSiteDataCount());
ExpectTotalModelCount(1);
RemoveAndWait(chrome_browsing_data_remover::DATA_TYPE_SITE_DATA |
content::BrowsingDataRemover::DATA_TYPE_CACHE |
chrome_browsing_data_remover::DATA_TYPE_HISTORY |
chrome_browsing_data_remover::DATA_TYPE_CONTENT_SETTINGS);
EXPECT_EQ(0, GetSiteDataCount());
ExpectTotalModelCount(0);
}
// Check if any data remains after a deletion and a Chrome restart to force
// all writes to be finished.
IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverBrowserTest, StorageRemovedFromDisk) {
// Deletions should remove all traces of browsing data from disk
// but there are a few bugs that need to be fixed.
// Any addition to this list must have an associated TODO.
static const std::vector<std::string> ignore_file_patterns = {
#if BUILDFLAG(IS_CHROMEOS)
// TODO(crbug.com/40577815): Many leveldb files remain on ChromeOS. I
// couldn't reproduce this in manual testing, so it might be a timing
// issue when Chrome is closed after the second test?
"[0-9]{6}",
#endif
};
int found = CheckUserDirectoryForString(kLocalHost, ignore_file_patterns,
/*check_leveldb_content=*/false);
EXPECT_EQ(0, found) << "A non-ignored file contains the hostname.";
}
const std::vector<std::string> kSessionOnlyStorageTestTypes{
"Cookie", "LocalStorage", "FileSystem", "SessionStorage",
"IndexedDb", "ServiceWorker", "CacheStorage", "MediaLicense",
};
// Test that storage gets deleted if marked as SessionOnly.
IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverBrowserTest,
PRE_SessionOnlyStorageRemoved) {
ExpectTotalModelCount(0);
GURL url = embedded_test_server()->GetURL("/browsing_data/site_data.html");
ASSERT_TRUE(ui_test_utils::NavigateToURL(GetBrowser(), url));
for (const std::string& type : kSessionOnlyStorageTestTypes) {
SetDataForType(type);
EXPECT_TRUE(HasDataForType(type));
}
ExpectTotalModelCount(1);
HostContentSettingsMapFactory::GetForProfile(GetBrowser()->profile())
->SetDefaultContentSetting(ContentSettingsType::COOKIES,
CONTENT_SETTING_SESSION_ONLY);
}
// TODO(crbug.com/40925336): Test is flaky on Mac.
#if BUILDFLAG(IS_MAC)
#define MAYBE_SessionOnlyStorageRemoved DISABLED_SessionOnlyStorageRemoved
#else
#define MAYBE_SessionOnlyStorageRemoved SessionOnlyStorageRemoved
#endif
// Restart to delete session only storage.
IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverBrowserTest,
MAYBE_SessionOnlyStorageRemoved) {
// All cookies should have been deleted.
ExpectTotalModelCount(0);
GURL url = embedded_test_server()->GetURL("/browsing_data/site_data.html");
ASSERT_TRUE(ui_test_utils::NavigateToURL(GetBrowser(), url));
for (const std::string& type : kSessionOnlyStorageTestTypes) {
EXPECT_FALSE(HasDataForType(type));
}
}
#if BUILDFLAG(IS_CHROMEOS)
// Test that removing passwords, when System-proxy is enabled on Chrome OS,
// sends a request to System-proxy to clear the cached user credentials.
IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverBrowserTest,
SystemProxyClearsUserCredentials_RemovePasswords) {
ash::SystemProxyManager::Get()->SetSystemProxyEnabledForTest(true);
EXPECT_EQ(0, ash::SystemProxyClient::Get()
->GetTestInterface()
->GetClearUserCredentialsCount());
RemoveAndWait(chrome_browsing_data_remover::DATA_TYPE_PASSWORDS);
EXPECT_EQ(1, ash::SystemProxyClient::Get()
->GetTestInterface()
->GetClearUserCredentialsCount());
}
// Test that removing cookies, when System-proxy is enabled on Chrome OS and
// kDbdRevampDesktop is enabled, sends a request to System-proxy to clear the
// cached user credentials.
IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverBrowserTest,
SystemProxyClearsUserCredentials_RemoveCookies) {
ash::SystemProxyManager::Get()->SetSystemProxyEnabledForTest(true);
EXPECT_EQ(0, ash::SystemProxyClient::Get()
->GetTestInterface()
->GetClearUserCredentialsCount());
RemoveAndWait(content::BrowsingDataRemover::DATA_TYPE_COOKIES);
EXPECT_EQ(1, ash::SystemProxyClient::Get()
->GetTestInterface()
->GetClearUserCredentialsCount());
}
#endif // BUILDFLAG(IS_CHROMEOS)
IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverBrowserTest,
RelatedWebsiteSetsDeletion) {
const GURL kPrimaryUrl("https://subdomain.example.com:112");
const GURL kSecondaryUrl("https://subidubi.testsite.com:55");
HostContentSettingsMap* settings_map =
HostContentSettingsMapFactory::GetForProfile(GetProfile());
// Setting the HostContentSettingsMap's clock to test last_modified of
// RuleMetaData below.
base::SimpleTestClock test_clock;
settings_map->SetClockForTesting(&test_clock);
test_clock.SetNow(base::Time::Now());
// Expected setting for the default grant.
const ContentSettingPatternSource kExpectedSettingDefault(
ContentSettingsPattern::Wildcard(), ContentSettingsPattern::Wildcard(),
content_settings::ContentSettingToValue(CONTENT_SETTING_ASK),
content_settings::ProviderType::kDefaultProvider,
/*incognito=*/false);
// Check that there are only default grants.
ASSERT_THAT(
settings_map->GetSettingsForOneType(ContentSettingsType::STORAGE_ACCESS),
UnorderedElementsAre(kExpectedSettingDefault));
ASSERT_THAT(settings_map->GetSettingsForOneType(
ContentSettingsType::TOP_LEVEL_STORAGE_ACCESS),
UnorderedElementsAre(kExpectedSettingDefault));
// Set RWS grants.
content_settings::ContentSettingConstraints constraints;
constraints.set_session_model(content_settings::mojom::SessionModel::DURABLE);
constraints.set_decided_by_related_website_sets(true);
settings_map->SetContentSettingDefaultScope(
kPrimaryUrl, kSecondaryUrl, ContentSettingsType::STORAGE_ACCESS,
CONTENT_SETTING_ALLOW, constraints);
settings_map->SetContentSettingDefaultScope(
kPrimaryUrl, kSecondaryUrl, ContentSettingsType::TOP_LEVEL_STORAGE_ACCESS,
CONTENT_SETTING_ALLOW, constraints);
// Check that the grants were set.
content_settings::RuleMetaData expected_metadata;
expected_metadata.SetFromConstraints(constraints);
expected_metadata.set_last_modified(test_clock.Now());
EXPECT_THAT(
settings_map->GetSettingsForOneType(ContentSettingsType::STORAGE_ACCESS),
UnorderedElementsAre(
kExpectedSettingDefault,
ContentSettingPatternSource(
ContentSettingsPattern::FromURLToSchemefulSitePattern(
GURL(kPrimaryUrl)), // https://[*.]example.com
ContentSettingsPattern::FromURLToSchemefulSitePattern(
GURL(kSecondaryUrl)), // https://[*.]testsite.com
content_settings::ContentSettingToValue(CONTENT_SETTING_ALLOW),
content_settings::ProviderType::kPrefProvider,
/*incognito=*/false, expected_metadata.Clone())));
EXPECT_THAT(
settings_map->GetSettingsForOneType(
ContentSettingsType::TOP_LEVEL_STORAGE_ACCESS),
UnorderedElementsAre(
kExpectedSettingDefault,
ContentSettingPatternSource(
ContentSettingsPattern::FromURLNoWildcard(
GURL(kPrimaryUrl)), // https://subdomain.example.com:112
ContentSettingsPattern::FromURLToSchemefulSitePattern(
GURL(kSecondaryUrl)),
content_settings::ContentSettingToValue(CONTENT_SETTING_ALLOW),
content_settings::ProviderType::kPrefProvider,
/*incognito=*/false, expected_metadata.Clone())));
// Remove Related Website Sets storage grants.
std::unique_ptr<BrowsingDataFilterBuilder> filter_builder =
BrowsingDataFilterBuilder::Create(
BrowsingDataFilterBuilder::Mode::kDelete);
filter_builder->AddOrigin(url::Origin::Create(kPrimaryUrl));
RemoveWithFilterAndWait(
content::BrowsingDataRemover::DATA_TYPE_RELATED_WEBSITE_SETS_PERMISSIONS,
std::move(filter_builder));
// Check that there's only the default grant left.
EXPECT_THAT(
settings_map->GetSettingsForOneType(ContentSettingsType::STORAGE_ACCESS),
UnorderedElementsAre(kExpectedSettingDefault));
EXPECT_THAT(settings_map->GetSettingsForOneType(
ContentSettingsType::TOP_LEVEL_STORAGE_ACCESS),
UnorderedElementsAre(kExpectedSettingDefault));
}
// Some storage backend use a different code path for full deletions and
// partial deletions, so we need to test both.
INSTANTIATE_TEST_SUITE_P(
All,
BrowsingDataRemoverBrowserTestP,
::testing::Values(TimeEnum::kDefault, TimeEnum::kLastHour),
[](const ::testing::TestParamInfo<
BrowsingDataRemoverBrowserTestP::ParamType>& info) {
switch (info.param) {
case TimeEnum::kDefault:
return "kDefault";
case TimeEnum::kLastHour:
return "kLastHour";
case TimeEnum::kStart:
return "kStart";
case TimeEnum::kMax:
return "kMax";
}
});