blob: c7cedf62dfebfb52a06ebd32fd5c8cb0aea0b164 [file] [log] [blame]
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/browsing_data/browsing_data_remover_browsertest_base.h"
#include <algorithm>
#include <memory>
#include <utility>
#include <vector>
#include "base/files/file_enumerator.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/functional/callback.h"
#include "base/memory/raw_ptr.h"
#include "base/path_service.h"
#include "base/test/bind.h"
#include "base/test/test_future.h"
#include "build/build_config.h"
#include "build/buildflag.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/browsing_data/browsing_data_file_system_util.h"
#include "chrome/browser/browsing_data/chrome_browsing_data_model_delegate.h"
#include "chrome/browser/browsing_data/counters/site_data_counting_helper.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/common/chrome_paths.h"
#include "components/browsing_data/content/browsing_data_model.h"
#include "components/browsing_data/content/browsing_data_test_util.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/download_manager.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/test/browser_test_utils.h"
#include "content/public/test/download_test_observer.h"
#include "google_apis/gaia/gaia_urls.h"
#include "net/base/filename_util.h"
#include "net/dns/mock_host_resolver.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "third_party/leveldatabase/env_chromium.h"
#include "third_party/re2/src/re2/re2.h"
#include "ui/base/models/tree_model.h"
#if !BUILDFLAG(IS_ANDROID)
#include "chrome/browser/download/download_browsertest_utils.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/test/base/ui_test_utils.h"
#endif
namespace {
#if BUILDFLAG(IS_ANDROID)
// TODO(crbug.com/40169678): Move these functions to
// /chrome/test/base/test_utils.{h|cc}.
base::FilePath GetTestFilePath(const char* dir, const char* file) {
base::FilePath path;
base::ScopedAllowBlockingForTesting allow_blocking;
base::PathService::Get(chrome::DIR_TEST_DATA, &path);
if (dir)
path = path.AppendASCII(dir);
return path.AppendASCII(file);
}
GURL GetTestUrl(const char* dir, const char* file) {
return net::FilePathToFileURL(GetTestFilePath(dir, file));
}
#endif
// Class for waiting for download manager to be initiailized.
class DownloadManagerWaiter : public content::DownloadManager::Observer {
public:
explicit DownloadManagerWaiter(content::DownloadManager* download_manager)
: initialized_(false), download_manager_(download_manager) {
download_manager_->AddObserver(this);
}
~DownloadManagerWaiter() override { download_manager_->RemoveObserver(this); }
void WaitForInitialized() {
initialized_ = download_manager_->IsManagerInitialized();
if (initialized_)
return;
base::RunLoop run_loop;
quit_closure_ = run_loop.QuitClosure();
run_loop.Run();
}
void OnManagerInitialized() override {
initialized_ = true;
if (quit_closure_)
std::move(quit_closure_).Run();
}
private:
base::OnceClosure quit_closure_;
bool initialized_;
raw_ptr<content::DownloadManager> download_manager_;
};
// Check if |file| matches any regex in |ignore_file_patterns|.
bool ShouldIgnoreFile(const std::string& file,
const std::vector<std::string>& ignore_file_patterns) {
for (const std::string& pattern : ignore_file_patterns) {
if (RE2::PartialMatch(file, pattern))
return true;
}
return false;
}
} // namespace
BrowsingDataRemoverBrowserTestBase::BrowsingDataRemoverBrowserTestBase() =
default;
BrowsingDataRemoverBrowserTestBase::~BrowsingDataRemoverBrowserTestBase() =
default;
void BrowsingDataRemoverBrowserTestBase::InitFeatureLists(
std::vector<base::test::FeatureRef> enabled_features,
std::vector<base::test::FeatureRef> disabled_features) {
feature_list_.InitWithFeatures(enabled_features, disabled_features);
}
#if !BUILDFLAG(IS_ANDROID)
Browser* BrowsingDataRemoverBrowserTestBase::GetBrowser() const {
return incognito_browser_ ? incognito_browser_.get() : browser();
}
// Call to use an Incognito browser rather than the default.
void BrowsingDataRemoverBrowserTestBase::UseIncognitoBrowser() {
ASSERT_EQ(nullptr, incognito_browser_.get());
incognito_browser_ = CreateIncognitoBrowser();
}
void BrowsingDataRemoverBrowserTestBase::RestartIncognitoBrowser() {
ASSERT_NE(nullptr, incognito_browser_);
CloseBrowserSynchronously(incognito_browser_);
incognito_browser_ = nullptr;
UseIncognitoBrowser();
}
#endif
void BrowsingDataRemoverBrowserTestBase::SetUpOnMainThread() {
base::FilePath path;
base::PathService::Get(content::DIR_TEST_DATA, &path);
embedded_test_server()->ServeFilesFromDirectory(path);
ASSERT_TRUE(embedded_test_server()->Start());
}
void BrowsingDataRemoverBrowserTestBase::RunScriptAndCheckResult(
const std::string& script,
const std::string& result,
content::WebContents* web_contents) {
if (!web_contents)
web_contents = GetActiveWebContents();
ASSERT_EQ(result, content::EvalJs(web_contents, script));
}
bool BrowsingDataRemoverBrowserTestBase::RunScriptAndGetBool(
const std::string& script,
content::WebContents* web_contents) {
EXPECT_TRUE(web_contents);
return content::EvalJs(web_contents, script).ExtractBool();
}
void BrowsingDataRemoverBrowserTestBase::VerifyDownloadCount(size_t expected,
Profile* profile) {
if (!profile)
profile = GetProfile();
content::DownloadManager* download_manager = profile->GetDownloadManager();
DownloadManagerWaiter download_manager_waiter(download_manager);
download_manager_waiter.WaitForInitialized();
std::vector<raw_ptr<download::DownloadItem, VectorExperimental>> downloads;
download_manager->GetAllDownloads(&downloads);
EXPECT_EQ(expected, downloads.size());
}
void BrowsingDataRemoverBrowserTestBase::DownloadAnItem() {
// Start a download.
content::DownloadManager* download_manager =
GetProfile()->GetDownloadManager();
auto observer = std::make_unique<content::DownloadTestObserverTerminal>(
download_manager, 1,
content::DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_ACCEPT);
#if BUILDFLAG(IS_ANDROID)
GURL download_url = GetTestUrl("downloads", "a_zip_file.zip");
ASSERT_TRUE(content::NavigateToURL(GetActiveWebContents(), download_url,
GURL("about:blank")));
#else
GURL download_url =
ui_test_utils::GetTestUrl(base::FilePath().AppendASCII("downloads"),
base::FilePath().AppendASCII("a_zip_file.zip"));
SetPromptForDownload(GetBrowser(), false);
ASSERT_TRUE(ui_test_utils::NavigateToURL(GetBrowser(), download_url));
#endif
observer->WaitForFinished();
VerifyDownloadCount(1u);
}
bool BrowsingDataRemoverBrowserTestBase::HasDataForType(
const std::string& type,
content::WebContents* web_contents) {
if (!web_contents)
web_contents = GetActiveWebContents();
return browsing_data_test_util::HasDataForType(type, web_contents);
}
void BrowsingDataRemoverBrowserTestBase::SetDataForType(
const std::string& type,
content::WebContents* web_contents) {
if (!web_contents)
web_contents = GetActiveWebContents();
browsing_data_test_util::SetDataForType(type, web_contents);
}
int BrowsingDataRemoverBrowserTestBase::GetSiteDataCount(
content::WebContents* web_contents) {
if (!web_contents)
web_contents = GetActiveWebContents();
Profile* profile =
Profile::FromBrowserContext(web_contents->GetBrowserContext());
base::RunLoop run_loop;
int count = -1;
(new SiteDataCountingHelper(profile, base::Time(), base::Time::Max(),
base::BindLambdaForTesting([&](int c) {
count = c;
run_loop.Quit();
})))
->CountAndDestroySelfWhenFinished();
run_loop.Run();
return count;
}
network::mojom::NetworkContext*
BrowsingDataRemoverBrowserTestBase::network_context() {
return GetProfile()->GetDefaultStoragePartition()->GetNetworkContext();
}
// Returns the active WebContents. On desktop this is in the first browser
// window created by tests, more specific behaviour requires other means.
content::WebContents*
BrowsingDataRemoverBrowserTestBase::GetActiveWebContents() {
#if BUILDFLAG(IS_ANDROID)
return chrome_test_utils::GetActiveWebContents(this);
#else
return GetBrowser()->tab_strip_model()->GetActiveWebContents();
#endif
}
#if !BUILDFLAG(IS_ANDROID)
content::WebContents* BrowsingDataRemoverBrowserTestBase::GetActiveWebContents(
Browser* browser) {
return browser->tab_strip_model()->GetActiveWebContents();
}
#endif // !BUILDFLAG(IS_ANDROID)
Profile* BrowsingDataRemoverBrowserTestBase::GetProfile() {
#if BUILDFLAG(IS_ANDROID)
return chrome_test_utils::GetProfile(this);
#else
return GetBrowser()->profile();
#endif
}
bool BrowsingDataRemoverBrowserTestBase::CheckUserDirectoryForString(
const std::string& hostname,
const std::vector<std::string>& ignore_file_patterns,
bool check_leveldb_content) {
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 (ShouldIgnoreFile(file, ignore_file_patterns)) {
LOG(INFO) << "Ignored: " << file;
} else {
found++;
LOG(WARNING) << "Found file name: " << file;
}
}
// Check leveldb content.
if (check_leveldb_content && 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/40784064): Most databases are already open and
// the LOCK prevents us from accessing them.
LOG(INFO) << "Could not open: " << file << " " << status.ToString();
}
}
// TODO(crbug.com/40577815): 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 (ShouldIgnoreFile(file, ignore_file_patterns)) {
LOG(INFO) << "Ignored: " << 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" << found;
}
}
return found;
}
std::unique_ptr<BrowsingDataModel>
BrowsingDataRemoverBrowserTestBase::GetBrowsingDataModel(Profile* profile) {
base::test::TestFuture<std::unique_ptr<BrowsingDataModel>>
browsing_data_model;
BrowsingDataModel::BuildFromDisk(
profile, ChromeBrowsingDataModelDelegate::CreateForProfile(profile),
browsing_data_model.GetCallback());
return browsing_data_model.Take();
}
#if BUILDFLAG(ENABLE_DICE_SUPPORT)
bool BrowsingDataRemoverBrowserTestBase::SetGaiaCookieForProfile(
Profile* profile) {
GURL google_url = GaiaUrls::GetInstance()->secure_google_url();
auto cookie = net::CanonicalCookie::CreateUnsafeCookieForTesting(
"SAPISID", std::string(), "." + google_url.GetHost(), "/", base::Time(),
base::Time(), base::Time(), base::Time(), /*secure=*/true,
/*httponly=*/false, net::CookieSameSite::NO_RESTRICTION,
net::COOKIE_PRIORITY_DEFAULT);
bool success = false;
base::RunLoop loop;
base::OnceCallback<void(net::CookieAccessResult)> callback =
base::BindLambdaForTesting([&success, &loop](net::CookieAccessResult r) {
success = r.status.IsInclude();
loop.Quit();
});
network::mojom::CookieManager* cookie_manager =
profile->GetDefaultStoragePartition()
->GetCookieManagerForBrowserProcess();
cookie_manager->SetCanonicalCookie(*cookie, google_url,
net::CookieOptions::MakeAllInclusive(),
std::move(callback));
loop.Run();
return success;
}
#endif