blob: e8d8bdd178e4ba4d48502dc6b63092d13afaaee4 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <stddef.h>
#include <memory>
#include <string>
#include "base/bind.h"
#include "base/callback.h"
#include "base/files/file_enumerator.h"
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/path_service.h"
#include "base/run_loop.h"
#include "base/strings/string_util.h"
#include "base/test/bind_test_util.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/threading/thread_restrictions.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/browsing_data/browsing_data_flash_lso_helper.h"
#include "chrome/browser/browsing_data/browsing_data_helper.h"
#include "chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.h"
#include "chrome/browser/browsing_data/cookies_tree_model.h"
#include "chrome/browser/browsing_data/counters/cache_counter.h"
#include "chrome/browser/browsing_data/counters/site_data_counting_helper.h"
#include "chrome/browser/browsing_data/local_data_container.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
#include "chrome/browser/external_protocol/external_protocol_handler.h"
#include "chrome/browser/metrics/subprocess_metrics_provider.h"
#include "chrome/browser/net/system_network_context_manager.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/signin/account_reconcilor_factory.h"
#include "chrome/browser/signin/identity_manager_factory.h"
#include "chrome/browser/signin/scoped_account_consistency.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/tabs/tab_strip_model.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/core/browsing_data_utils.h"
#include "components/content_settings/core/browser/host_content_settings_map.h"
#include "components/prefs/pref_service.h"
#include "components/signin/core/browser/account_reconcilor.h"
#include "components/signin/public/base/signin_buildflags.h"
#include "components/signin/public/identity_manager/identity_manager.h"
#include "components/signin/public/identity_manager/identity_test_utils.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/browsing_data_filter_builder.h"
#include "content/public/browser/browsing_data_remover.h"
#include "content/public/browser/download_manager.h"
#include "content/public/browser/network_service_instance.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/content_paths.h"
#include "content/public/common/network_service_util.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/browsing_data_remover_test_util.h"
#include "content/public/test/download_test_observer.h"
#include "content/public/test/simple_url_loader_test_helper.h"
#include "google_apis/gaia/gaia_urls.h"
#include "google_apis/gaia/google_service_auth_error.h"
#include "media/base/media_switches.h"
#include "media/mojo/interfaces/media_types.mojom.h"
#include "media/mojo/services/video_decode_perf_history.h"
#include "net/cookies/canonical_cookie.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 "services/network/public/cpp/features.h"
#include "services/network/public/cpp/simple_url_loader.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/leveldatabase/env_chromium.h"
#include "third_party/leveldatabase/leveldb_features.h"
#include "third_party/re2/src/re2/re2.h"
#include "url/gurl.h"
#if BUILDFLAG(ENABLE_LIBRARY_CDMS)
#if defined(OS_MACOSX)
#include "base/threading/platform_thread.h"
#endif
#include "base/memory/scoped_refptr.h"
#include "chrome/browser/browsing_data/browsing_data_media_license_helper.h"
#include "chrome/browser/media/library_cdm_test_helper.h"
#endif // BUILDFLAG(ENABLE_LIBRARY_CDMS)
using content::BrowserThread;
using content::BrowsingDataFilterBuilder;
namespace {
static const char* kExampleHost = "example.com";
static const char* kLocalHost = "localhost";
static const base::Time kLastHour =
base::Time::Now() - base::TimeDelta::FromHours(1);
// Check if |file| matches any regex in |whitelist|.
bool IsFileWhitelisted(const std::string& file,
const std::vector<std::string>& whitelist) {
for (const std::string& pattern : whitelist) {
if (RE2::PartialMatch(file, pattern))
return true;
}
return false;
}
// Searches the user data directory for files that contain |hostname| in the
// filename or as part of the content. Returns the number of files that
// do not match any regex in |whitelist|.
bool CheckUserDirectoryForString(const std::string& hostname,
const std::vector<std::string>& whitelist) {
base::FilePath user_data_dir =
g_browser_process->profile_manager()->user_data_dir();
base::ScopedAllowBlockingForTesting allow_blocking;
base::FileEnumerator enumerator(
user_data_dir, true /* recursive */,
base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES);
int found = 0;
for (base::FilePath path = enumerator.Next(); !path.empty();
path = enumerator.Next()) {
// Remove |user_data_dir| part from path.
std::string file =
path.NormalizePathSeparatorsTo('/').AsUTF8Unsafe().substr(
user_data_dir.AsUTF8Unsafe().length());
// Check file name.
if (file.find(hostname) != std::string::npos) {
if (IsFileWhitelisted(file, whitelist)) {
LOG(INFO) << "Whitelisted: " << file;
} else {
found++;
LOG(WARNING) << "Found file name: " << file;
}
}
// Check leveldb content.
if (path.BaseName().AsUTF8Unsafe() == "CURRENT") {
// LevelDB instances consist of a folder where most files have variable
// names that contain a revision number.
// All leveldb folders have a "CURRENT" file that points to the current
// manifest. We consider all folders with a CURRENT file to be leveldb
// instances and try to open them.
std::unique_ptr<leveldb::DB> db;
std::string db_file = path.DirName().AsUTF8Unsafe();
auto status = leveldb_env::OpenDB(leveldb_env::Options(), db_file, &db);
if (status.ok()) {
std::unique_ptr<leveldb::Iterator> it(
db->NewIterator(leveldb::ReadOptions()));
for (it->SeekToFirst(); it->Valid(); it->Next()) {
std::string entry =
it->key().ToString() + ":" + it->value().ToString();
if (entry.find(hostname) != std::string::npos) {
LOG(WARNING) << "Found leveldb entry: " << file << " " << entry;
found++;
}
}
} else {
// TODO(crbug.com/846297): Some databases are already open and the LOCK
// prevents us from accessing them.
LOG(INFO) << "Could not open: " << file << " " << status.ToString();
}
}
// TODO(crbug.com/846297): Add support for sqlite and other formats that
// possibly contain non-plaintext data.
// Check file content.
if (enumerator.GetInfo().IsDirectory())
continue;
std::string content;
if (!base::ReadFileToString(path, &content)) {
LOG(INFO) << "Could not read: " << file;
continue;
}
size_t pos = content.find(hostname);
if (pos != std::string::npos) {
if (IsFileWhitelisted(file, whitelist)) {
LOG(INFO) << "Whitelisted: " << file;
continue;
}
found++;
// Print surrounding text of the match.
std::string partial_content = content.substr(
pos < 30 ? 0 : pos - 30,
std::min(content.size() - 1, pos + hostname.size() + 30));
LOG(WARNING) << "Found file content: " << file << "\n"
<< partial_content << "\n";
}
}
return found;
}
class CookiesTreeObserver : public CookiesTreeModel::Observer {
public:
explicit CookiesTreeObserver(base::OnceClosure quit_closure)
: quit_closure_(std::move(quit_closure)) {}
void TreeModelBeginBatch(CookiesTreeModel* model) override {}
void TreeModelEndBatch(CookiesTreeModel* model) override {
std::move(quit_closure_).Run();
}
void TreeNodesAdded(ui::TreeModel* model,
ui::TreeModelNode* parent,
size_t start,
size_t count) override {}
void TreeNodesRemoved(ui::TreeModel* model,
ui::TreeModelNode* parent,
size_t start,
size_t count) override {}
void TreeNodeChanged(ui::TreeModel* model, ui::TreeModelNode* node) override {
}
private:
base::OnceClosure quit_closure_;
};
// Returns the sum of the number of datatypes per host.
int GetCookiesTreeModelCount(const CookieTreeNode* root) {
int count = 0;
for (const auto& node : root->children()) {
EXPECT_GE(node->children().size(), 1u);
count += std::count_if(node->children().cbegin(), node->children().cend(),
[](const auto& child) {
// TODO(crbug.com/642955): Include quota nodes.
return child->GetDetailedInfo().node_type !=
CookieTreeNode::DetailedInfo::TYPE_QUOTA;
});
}
return count;
}
// Returns a string with information about the content of the
// cookie tree model.
std::string GetCookiesTreeModelInfo(const CookieTreeNode* root) {
std::stringstream info;
info << "CookieTreeModel: " << std::endl;
for (const auto& node : root->children()) {
info << node->GetTitle() << std::endl;
for (const auto& child : node->children()) {
// Quota nodes are not included in the UI due to crbug.com/642955.
const auto node_type = child->GetDetailedInfo().node_type;
if (node_type != CookieTreeNode::DetailedInfo::TYPE_QUOTA)
info << " " << child->GetTitle() << " " << node_type << std::endl;
}
}
return info.str();
}
#if BUILDFLAG(ENABLE_DICE_SUPPORT)
// Sets the APISID Gaia cookie, which is monitored by the AccountReconcilor.
bool SetGaiaCookieForProfile(Profile* profile) {
GURL google_url = GaiaUrls::GetInstance()->google_url();
net::CanonicalCookie cookie("APISID", std::string(), "." + google_url.host(),
"/", base::Time(), base::Time(), base::Time(),
false, false, net::CookieSameSite::NO_RESTRICTION,
net::COOKIE_PRIORITY_DEFAULT);
bool success = false;
base::RunLoop loop;
base::OnceCallback<void(net::CanonicalCookie::CookieInclusionStatus)>
callback = base::BindLambdaForTesting(
[&success, &loop](net::CanonicalCookie::CookieInclusionStatus s) {
success =
(s == net::CanonicalCookie::CookieInclusionStatus::INCLUDE);
loop.Quit();
});
network::mojom::CookieManager* cookie_manager =
content::BrowserContext::GetDefaultStoragePartition(profile)
->GetCookieManagerForBrowserProcess();
net::CookieOptions options;
options.set_include_httponly();
cookie_manager->SetCanonicalCookie(cookie, "https", options,
std::move(callback));
loop.Run();
return success;
}
#endif
} // namespace
class BrowsingDataRemoverBrowserTest : public InProcessBrowserTest {
public:
BrowsingDataRemoverBrowserTest() {
std::vector<base::Feature> enabled_features = {
leveldb::kLevelDBRewriteFeature};
#if BUILDFLAG(ENABLE_LIBRARY_CDMS)
enabled_features.push_back(media::kExternalClearKeyForTesting);
#endif
feature_list_.InitWithFeatures(enabled_features, {});
}
// Call to use an Incognito browser rather than the default.
void UseIncognitoBrowser() {
ASSERT_EQ(nullptr, incognito_browser_);
incognito_browser_ = CreateIncognitoBrowser();
}
Browser* GetBrowser() const {
return incognito_browser_ ? incognito_browser_ : browser();
}
void SetUpOnMainThread() override {
base::FilePath path;
base::PathService::Get(content::DIR_TEST_DATA, &path);
host_resolver()->AddRule(kExampleHost, "127.0.0.1");
embedded_test_server()->ServeFilesFromDirectory(path);
ASSERT_TRUE(embedded_test_server()->Start());
}
void RunScriptAndCheckResult(const std::string& script,
const std::string& result) {
std::string data;
ASSERT_TRUE(content::ExecuteScriptAndExtractString(
GetBrowser()->tab_strip_model()->GetActiveWebContents(), script,
&data));
ASSERT_EQ(data, result);
}
bool RunScriptAndGetBool(const std::string& script) {
bool data;
EXPECT_TRUE(content::ExecuteScriptAndExtractBool(
GetBrowser()->tab_strip_model()->GetActiveWebContents(), script,
&data));
return data;
}
void VerifyDownloadCount(size_t expected) {
content::DownloadManager* download_manager =
content::BrowserContext::GetDownloadManager(GetBrowser()->profile());
std::vector<download::DownloadItem*> downloads;
download_manager->GetAllDownloads(&downloads);
EXPECT_EQ(expected, downloads.size());
}
void DownloadAnItem() {
// Start a download.
content::DownloadManager* download_manager =
content::BrowserContext::GetDownloadManager(GetBrowser()->profile());
std::unique_ptr<content::DownloadTestObserver> observer(
new content::DownloadTestObserverTerminal(
download_manager, 1,
content::DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_ACCEPT));
GURL download_url = ui_test_utils::GetTestUrl(
base::FilePath().AppendASCII("downloads"),
base::FilePath().AppendASCII("a_zip_file.zip"));
ui_test_utils::NavigateToURL(GetBrowser(), download_url);
observer->WaitForFinished();
VerifyDownloadCount(1u);
}
void RemoveAndWait(int remove_mask) {
RemoveAndWait(remove_mask, base::Time(), base::Time::Max());
}
void RemoveAndWait(int remove_mask, base::Time delete_begin) {
RemoveAndWait(remove_mask, delete_begin, base::Time::Max());
}
void RemoveAndWait(int remove_mask,
base::Time delete_begin,
base::Time delete_end) {
content::BrowsingDataRemover* remover =
content::BrowserContext::GetBrowsingDataRemover(
GetBrowser()->profile());
content::BrowsingDataRemoverCompletionObserver completion_observer(remover);
remover->RemoveAndReply(
delete_begin, delete_end, remove_mask,
content::BrowsingDataRemover::ORIGIN_TYPE_UNPROTECTED_WEB,
&completion_observer);
completion_observer.BlockUntilCompletion();
}
void RemoveWithFilterAndWait(
int remove_mask,
std::unique_ptr<BrowsingDataFilterBuilder> filter_builder) {
content::BrowsingDataRemover* remover =
content::BrowserContext::GetBrowsingDataRemover(
GetBrowser()->profile());
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, base::Time delete_begin) {
EXPECT_EQ(0, GetSiteDataCount());
GURL url = embedded_test_server()->GetURL("/browsing_data/site_data.html");
ui_test_utils::NavigateToURL(GetBrowser(), url);
EXPECT_EQ(0, GetSiteDataCount());
ExpectCookieTreeModelCount(0);
EXPECT_FALSE(HasDataForType(type));
SetDataForType(type);
EXPECT_EQ(1, GetSiteDataCount());
ExpectCookieTreeModelCount(1);
EXPECT_TRUE(HasDataForType(type));
RemoveAndWait(ChromeBrowsingDataRemoverDelegate::DATA_TYPE_SITE_DATA,
delete_begin);
EXPECT_EQ(0, GetSiteDataCount());
ExpectCookieTreeModelCount(0);
EXPECT_FALSE(HasDataForType(type));
}
// Test that storage systems like filesystem and websql, where just an access
// creates an empty store, are counted and deleted correctly.
void TestEmptySiteData(const std::string& type, base::Time delete_begin) {
EXPECT_EQ(0, GetSiteDataCount());
ExpectCookieTreeModelCount(0);
GURL url = embedded_test_server()->GetURL("/browsing_data/site_data.html");
ui_test_utils::NavigateToURL(GetBrowser(), url);
EXPECT_EQ(0, GetSiteDataCount());
ExpectCookieTreeModelCount(0);
// Opening a store of this type creates a site data entry.
EXPECT_FALSE(HasDataForType(type));
EXPECT_EQ(1, GetSiteDataCount());
ExpectCookieTreeModelCount(1);
RemoveAndWait(ChromeBrowsingDataRemoverDelegate::DATA_TYPE_SITE_DATA,
delete_begin);
EXPECT_EQ(0, GetSiteDataCount());
ExpectCookieTreeModelCount(0);
}
bool HasDataForType(const std::string& type) {
return RunScriptAndGetBool("has" + type + "()");
}
void SetDataForType(const std::string& type) {
ASSERT_TRUE(RunScriptAndGetBool("set" + type + "()"))
<< "Couldn't create data for: " << type;
}
int GetSiteDataCount() {
base::RunLoop run_loop;
int count = -1;
(new SiteDataCountingHelper(GetBrowser()->profile(), base::Time(),
base::BindLambdaForTesting([&](int c) {
count = c;
run_loop.Quit();
})))
->CountAndDestroySelfWhenFinished();
run_loop.Run();
return count;
}
#if BUILDFLAG(ENABLE_LIBRARY_CDMS)
int GetMediaLicenseCount() {
base::RunLoop run_loop;
int count = -1;
content::StoragePartition* partition =
content::BrowserContext::GetDefaultStoragePartition(
browser()->profile());
scoped_refptr<BrowsingDataMediaLicenseHelper> media_license_helper =
BrowsingDataMediaLicenseHelper::Create(
partition->GetFileSystemContext());
media_license_helper->StartFetching(base::BindLambdaForTesting(
[&](const std::list<BrowsingDataMediaLicenseHelper::MediaLicenseInfo>&
licenses) {
count = licenses.size();
LOG(INFO) << "Found " << count << " licenses.";
for (const auto& license : licenses)
LOG(INFO) << license.last_modified_time;
run_loop.Quit();
}));
run_loop.Run();
return count;
}
#endif
inline void ExpectCookieTreeModelCount(int expected) {
std::unique_ptr<CookiesTreeModel> model = GetCookiesTreeModel();
EXPECT_EQ(expected, GetCookiesTreeModelCount(model->GetRoot()))
<< GetCookiesTreeModelInfo(model->GetRoot());
}
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 content::BrowserContext::GetDefaultStoragePartition(
GetBrowser()->profile())
->GetNetworkContext();
}
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();
}
std::unique_ptr<CookiesTreeModel> GetCookiesTreeModel() {
Profile* profile = GetBrowser()->profile();
content::StoragePartition* storage_partition =
content::BrowserContext::GetDefaultStoragePartition(profile);
content::IndexedDBContext* indexed_db_context =
storage_partition->GetIndexedDBContext();
content::ServiceWorkerContext* service_worker_context =
storage_partition->GetServiceWorkerContext();
content::CacheStorageContext* cache_storage_context =
storage_partition->GetCacheStorageContext();
storage::FileSystemContext* file_system_context =
storage_partition->GetFileSystemContext();
auto container = std::make_unique<LocalDataContainer>(
new BrowsingDataCookieHelper(storage_partition),
new BrowsingDataDatabaseHelper(profile),
new BrowsingDataLocalStorageHelper(profile),
/*session_storage_helper=*/nullptr,
new BrowsingDataAppCacheHelper(storage_partition->GetAppCacheService()),
new BrowsingDataIndexedDBHelper(indexed_db_context),
BrowsingDataFileSystemHelper::Create(file_system_context),
BrowsingDataQuotaHelper::Create(profile),
new BrowsingDataServiceWorkerHelper(service_worker_context),
new BrowsingDataSharedWorkerHelper(storage_partition,
profile->GetResourceContext()),
new BrowsingDataCacheStorageHelper(cache_storage_context),
BrowsingDataFlashLSOHelper::Create(profile),
BrowsingDataMediaLicenseHelper::Create(file_system_context));
base::RunLoop run_loop;
CookiesTreeObserver observer(run_loop.QuitClosure());
auto model = std::make_unique<CookiesTreeModel>(
std::move(container), profile->GetExtensionSpecialStoragePolicy());
model->AddCookiesTreeObserver(&observer);
run_loop.Run();
return model;
}
void SetUpCommandLine(base::CommandLine* command_line) override {
InProcessBrowserTest::SetUpCommandLine(command_line);
#if BUILDFLAG(ENABLE_LIBRARY_CDMS)
// Testing MediaLicenses requires additional command line parameters as
// it uses the External Clear Key CDM.
RegisterClearKeyCdm(command_line);
#endif
}
base::test::ScopedFeatureList feature_list_;
Browser* incognito_browser_ = nullptr;
};
#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());
return signin::MakePrimaryAccountAvailable(identity_manager,
account_id + "@gmail.com");
}
auto account_info =
signin::MakeAccountAvailable(identity_manager, account_id);
DCHECK(
identity_manager->HasAccountWithRefreshToken(account_info.account_id));
return account_info;
}
private:
ScopedAccountConsistencyDice dice_;
};
#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) {
std::string original_salt = GetBrowser()->profile()->GetMediaDeviceIDSalt();
RemoveAndWait(content::BrowsingDataRemover::DATA_TYPE_COOKIES);
std::string new_salt = GetBrowser()->profile()->GetMediaDeviceIDSalt();
EXPECT_NE(original_salt, new_salt);
}
#if BUILDFLAG(ENABLE_DICE_SUPPORT)
// Test that Sync is 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 Sync account was not removed and Sync was paused.
signin::IdentityManager* identity_manager =
IdentityManagerFactory::GetForProfile(profile);
EXPECT_TRUE(
identity_manager->HasAccountWithRefreshToken(primary_account.account_id));
EXPECT_EQ(
GoogleServiceAuthError::InvalidGaiaCredentialsReason::
CREDENTIALS_REJECTED_BY_CLIENT,
identity_manager
->GetErrorStateOfRefreshTokenForAccount(primary_account.account_id)
.GetInvalidGaiaCredentialsReason());
// 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, if synced data is
// being deleted.
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);
// Sync data is being deleted.
std::unique_ptr<AccountReconcilor::ScopedSyncedDataDeletion> deletion =
AccountReconcilorFactory::GetForProfile(profile)
->GetScopedSyncDataDeletion();
// 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 paused when cookies are cleared if Sync was in error, even
// if synced data is being deleted.
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));
// Sync data is being deleted.
std::unique_ptr<AccountReconcilor::ScopedSyncedDataDeletion> deletion =
AccountReconcilorFactory::GetForProfile(profile)
->GetScopedSyncDataDeletion();
// 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_CLIENT,
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, media::learning::FeatureValue(0), 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(ChromeBrowsingDataRemoverDelegate::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 can modify database after deleting it.
IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverBrowserTest, Database) {
GURL url = embedded_test_server()->GetURL("/simple_database.html");
ui_test_utils::NavigateToURL(GetBrowser(), url);
RunScriptAndCheckResult("createTable()", "done");
RunScriptAndCheckResult("insertRecord('text')", "done");
RunScriptAndCheckResult("getRecords()", "text");
RemoveAndWait(ChromeBrowsingDataRemoverDelegate::DATA_TYPE_SITE_DATA);
ui_test_utils::NavigateToURL(GetBrowser(), url);
RunScriptAndCheckResult("createTable()", "done");
RunScriptAndCheckResult("insertRecord('text2')", "done");
RunScriptAndCheckResult("getRecords()", "text2");
}
// 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/817417): 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::WHITELIST);
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::WHITELIST);
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/813882): 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::SequencedTaskRunnerHandle::Get()->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();
SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
base::RunLoop().RunUntilIdle();
}
histogram_tester.ExpectTotalCount("NQE.PrefsSizeOnClearing", 1);
}
IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverBrowserTest,
ExternalProtocolHandlerPrefs) {
Profile* profile = GetBrowser()->profile();
base::DictionaryValue prefs;
prefs.SetBoolean("tel", false);
profile->GetPrefs()->Set(prefs::kExcludedSchemes, prefs);
ExternalProtocolHandler::BlockState block_state =
ExternalProtocolHandler::GetBlockState("tel", profile);
ASSERT_EQ(ExternalProtocolHandler::DONT_BLOCK, block_state);
RemoveAndWait(ChromeBrowsingDataRemoverDelegate::DATA_TYPE_SITE_DATA);
block_state = ExternalProtocolHandler::GetBlockState("tel", 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_NAVIGATION);
EXPECT_FALSE(HasDataForType(kType));
SetDataForType(kType);
EXPECT_TRUE(HasDataForType(kType));
// Remove history from navigation to site_data.html.
RemoveAndWait(ChromeBrowsingDataRemoverDelegate::DATA_TYPE_HISTORY);
EXPECT_FALSE(HasDataForType(kType));
SetDataForType(kType);
EXPECT_TRUE(HasDataForType(kType));
// Remove history from previous pushState() call in setHistory().
RemoveAndWait(ChromeBrowsingDataRemoverDelegate::DATA_TYPE_HISTORY);
EXPECT_FALSE(HasDataForType(kType));
}
// Parameterized to run tests for different deletion time ranges.
class BrowsingDataRemoverBrowserTestP
: public BrowsingDataRemoverBrowserTest,
public testing::WithParamInterface<base::Time> {};
IN_PROC_BROWSER_TEST_P(BrowsingDataRemoverBrowserTestP, CookieDeletion) {
TestSiteData("Cookie", GetParam());
}
IN_PROC_BROWSER_TEST_P(BrowsingDataRemoverBrowserTestP,
CookieIncognitoDeletion) {
UseIncognitoBrowser();
TestSiteData("Cookie", GetParam());
}
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/772337): 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");
ui_test_utils::NavigateToURL(GetBrowser(), url);
const std::string type = "SessionStorage";
EXPECT_FALSE(HasDataForType(type));
SetDataForType(type);
EXPECT_TRUE(HasDataForType(type));
RemoveAndWait(ChromeBrowsingDataRemoverDelegate::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());
ExpectCookieTreeModelCount(0);
GURL url = embedded_test_server()->GetURL("/browsing_data/site_data.html");
ui_test_utils::NavigateToURL(GetBrowser(), url);
EXPECT_EQ(0, GetSiteDataCount());
ExpectCookieTreeModelCount(0);
SetDataForType("SessionStorage");
EXPECT_EQ(0, GetSiteDataCount());
ExpectCookieTreeModelCount(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, WebSqlDeletion) {
TestSiteData("WebSql", GetParam());
}
IN_PROC_BROWSER_TEST_P(BrowsingDataRemoverBrowserTestP,
WebSqlIncognitoDeletion) {
UseIncognitoBrowser();
TestSiteData("WebSql", GetParam());
}
// Test that empty websql dbs are deleted correctly.
IN_PROC_BROWSER_TEST_P(BrowsingDataRemoverBrowserTestP, EmptyWebSqlDeletion) {
TestEmptySiteData("WebSql", 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 base::Time delete_begin = GetParam();
EXPECT_EQ(0, GetSiteDataCount());
EXPECT_EQ(0, GetMediaLicenseCount());
GURL url =
embedded_test_server()->GetURL("/browsing_data/media_license.html");
ui_test_utils::NavigateToURL(browser(), url);
EXPECT_EQ(0, GetSiteDataCount());
EXPECT_EQ(0, GetMediaLicenseCount());
ExpectCookieTreeModelCount(0);
EXPECT_FALSE(HasDataForType(kMediaLicenseType));
SetDataForType(kMediaLicenseType);
EXPECT_EQ(1, GetSiteDataCount());
EXPECT_EQ(1, GetMediaLicenseCount());
ExpectCookieTreeModelCount(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(content::BrowsingDataRemover::DATA_TYPE_MEDIA_LICENSES,
delete_begin, kLastHour);
EXPECT_EQ(1, GetSiteDataCount());
EXPECT_EQ(1, GetMediaLicenseCount());
ExpectCookieTreeModelCount(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(content::BrowsingDataRemover::DATA_TYPE_MEDIA_LICENSES,
delete_begin, base::Time::Max());
EXPECT_EQ(0, GetSiteDataCount());
EXPECT_EQ(0, GetMediaLicenseCount());
ExpectCookieTreeModelCount(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());
EXPECT_EQ(0, GetMediaLicenseCount());
GURL url =
embedded_test_server()->GetURL("/browsing_data/media_license.html");
ui_test_utils::NavigateToURL(browser(), url);
EXPECT_EQ(0, GetSiteDataCount());
EXPECT_EQ(0, GetMediaLicenseCount());
ExpectCookieTreeModelCount(0);
EXPECT_FALSE(HasDataForType(kMediaLicenseType));
SetDataForType(kMediaLicenseType);
EXPECT_EQ(1, GetSiteDataCount());
EXPECT_EQ(1, GetMediaLicenseCount());
ExpectCookieTreeModelCount(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. We can't see the license, since it's stored for a
// different origin (but we can delete it).
const base::Time start = base::Time::Now();
LOG(INFO) << "MediaLicenseTimedDeletion starting @ " << start;
EXPECT_EQ(1, GetMediaLicenseCount());
GURL url =
embedded_test_server()->GetURL("/browsing_data/media_license.html");
ui_test_utils::NavigateToURL(browser(), url);
#if defined(OS_MACOSX)
// 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 |start|.
base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(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));
// Create a media license for this domain.
SetDataForType(kMediaLicenseType);
EXPECT_EQ(2, GetMediaLicenseCount());
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(content::BrowsingDataRemover::DATA_TYPE_MEDIA_LICENSES, start);
EXPECT_EQ(1, GetMediaLicenseCount());
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(content::BrowsingDataRemover::DATA_TYPE_MEDIA_LICENSES);
EXPECT_EQ(0, GetMediaLicenseCount());
ExpectCookieTreeModelCount(0);
}
IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverBrowserTest,
MediaLicenseDeletionWithFilter) {
const std::string kMediaLicenseType = "MediaLicense";
GURL url =
embedded_test_server()->GetURL("/browsing_data/media_license.html");
ui_test_utils::NavigateToURL(browser(), url);
EXPECT_EQ(0, GetMediaLicenseCount());
EXPECT_FALSE(HasDataForType(kMediaLicenseType));
SetDataForType(kMediaLicenseType);
EXPECT_EQ(1, GetMediaLicenseCount());
EXPECT_TRUE(HasDataForType(kMediaLicenseType));
// Try to remove the Media Licenses using a whitelist that doesn't include
// the current URL. Media License should not be deleted.
std::unique_ptr<BrowsingDataFilterBuilder> filter_builder =
BrowsingDataFilterBuilder::Create(BrowsingDataFilterBuilder::WHITELIST);
filter_builder->AddOrigin(
url::Origin::CreateFromNormalizedTuple("https", "test-origin", 443));
RemoveWithFilterAndWait(
content::BrowsingDataRemover::DATA_TYPE_MEDIA_LICENSES,
std::move(filter_builder));
EXPECT_EQ(1, GetMediaLicenseCount());
// Now try with a blacklist that includes the current URL. Media License
// should not be deleted.
filter_builder =
BrowsingDataFilterBuilder::Create(BrowsingDataFilterBuilder::BLACKLIST);
filter_builder->AddOrigin(url::Origin::Create(url));
RemoveWithFilterAndWait(
content::BrowsingDataRemover::DATA_TYPE_MEDIA_LICENSES,
std::move(filter_builder));
EXPECT_EQ(1, GetMediaLicenseCount());
// Now try with a whitelist that includes the current URL. Media License
// should be deleted this time.
filter_builder =
BrowsingDataFilterBuilder::Create(BrowsingDataFilterBuilder::WHITELIST);
filter_builder->AddOrigin(url::Origin::Create(url));
RemoveWithFilterAndWait(
content::BrowsingDataRemover::DATA_TYPE_MEDIA_LICENSES,
std::move(filter_builder));
EXPECT_EQ(0, GetMediaLicenseCount());
}
#endif // BUILDFLAG(ENABLE_LIBRARY_CDMS)
const std::vector<std::string> kStorageTypes{
"Cookie", "LocalStorage", "FileSystem", "SessionStorage",
"IndexedDb", "WebSql", "ServiceWorker", "CacheStorage",
};
// Test that storage doesn't leave any traces on disk.
IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverBrowserTest,
PRE_PRE_StorageRemovedFromDisk) {
ASSERT_EQ(0, CheckUserDirectoryForString(kLocalHost, {}));
ASSERT_EQ(0, GetSiteDataCount());
ExpectCookieTreeModelCount(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");
ui_test_utils::NavigateToURL(GetBrowser(), url);
for (const std::string& type : kStorageTypes) {
SetDataForType(type);
EXPECT_TRUE(HasDataForType(type));
}
// TODO(crbug.com/846297): 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.
IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverBrowserTest,
PRE_StorageRemovedFromDisk) {
EXPECT_EQ(1, GetSiteDataCount());
// Expect all datatypes from above except SessionStorage. SessionStorage is
// not supported by the CookieTreeModel yet.
ExpectCookieTreeModelCount(kStorageTypes.size() - 1);
RemoveAndWait(ChromeBrowsingDataRemoverDelegate::DATA_TYPE_SITE_DATA |
content::BrowsingDataRemover::DATA_TYPE_CACHE |
ChromeBrowsingDataRemoverDelegate::DATA_TYPE_HISTORY |
ChromeBrowsingDataRemoverDelegate::DATA_TYPE_CONTENT_SETTINGS);
EXPECT_EQ(0, GetSiteDataCount());
ExpectCookieTreeModelCount(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> whitelist = {
#if defined(OS_CHROMEOS)
// TODO(crbug.com/846297): 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, whitelist);
EXPECT_EQ(0, found) << "A non-whitelisted file contains the hostname.";
}
// TODO(crbug.com/840080): Filesystem can't be deleted on exit correctly at the
// moment.
// TODO(crbug.com/927312): LocalStorage deletion is flaky.
const std::vector<std::string> kSessionOnlyStorageTestTypes{
"Cookie",
// "LocalStorage",
// "FileSystem",
"SessionStorage",
"IndexedDb",
"WebSql",
"ServiceWorker",
"CacheStorage",
};
// Test that storage gets deleted if marked as SessionOnly.
IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverBrowserTest,
PRE_SessionOnlyStorageRemoved) {
ExpectCookieTreeModelCount(0);
GURL url = embedded_test_server()->GetURL("/browsing_data/site_data.html");
ui_test_utils::NavigateToURL(GetBrowser(), url);
for (const std::string& type : kSessionOnlyStorageTestTypes) {
SetDataForType(type);
EXPECT_TRUE(HasDataForType(type));
}
// Expect the datatypes from above except SessionStorage. SessionStorage is
// not supported by the CookieTreeModel yet.
ExpectCookieTreeModelCount(kSessionOnlyStorageTestTypes.size() - 1);
HostContentSettingsMapFactory::GetForProfile(GetBrowser()->profile())
->SetDefaultContentSetting(CONTENT_SETTINGS_TYPE_COOKIES,
CONTENT_SETTING_SESSION_ONLY);
}
// Restart to delete session only storage.
IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverBrowserTest,
SessionOnlyStorageRemoved) {
// All cookies should have been deleted.
ExpectCookieTreeModelCount(0);
GURL url = embedded_test_server()->GetURL("/browsing_data/site_data.html");
ui_test_utils::NavigateToURL(GetBrowser(), url);
for (const std::string& type : kSessionOnlyStorageTestTypes) {
EXPECT_FALSE(HasDataForType(type));
}
}
// Some storage backend use a different code path for full deletions and
// partial deletions, so we need to test both.
INSTANTIATE_TEST_SUITE_P(/* no prefix */,
BrowsingDataRemoverBrowserTestP,
::testing::Values(base::Time(), kLastHour));