#include <memory>
#include "base/containers/contains.h"
#include "base/files/file_path.h"
#include "base/functional/bind.h"
#include "base/location.h"
#include "base/memory/raw_ptr.h"
#include "base/strings/stringprintf.h"
#include "base/test/bind.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/test_future.h"
#include "components/ukm/test_ukm_recorder.h"
#include "content/browser/back_forward_cache_test_util.h"
#include "content/browser/browsing_data/shared_storage_clear_site_data_tester.h"
#include "content/browser/preloading/prefetch/prefetch_document_manager.h"
#include "content/browser/preloading/prefetch/prefetch_features.h"
#include "content/browser/preloading/prefetch/prefetch_status.h"
#include "content/browser/preloading/prerender/prerender_final_status.h"
#include "content/browser/renderer_host/render_frame_host_impl.h"
#include "content/public/browser/back_forward_cache.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browsing_data_filter_builder.h"
#include "content/public/browser/browsing_data_remover.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/content_client.h"
#include "content/public/common/content_features.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 "content/public/test/content_browser_test.h"
#include "content/public/test/content_browser_test_utils.h"
#include "content/public/test/navigation_simulator.h"
#include "content/public/test/prefetch_test_util.h"
#include "content/public/test/prerender_test_util.h"
#include "content/public/test/simple_url_loader_test_helper.h"
#include "content/public/test/test_navigation_observer.h"
#include "content/shell/browser/shell.h"
#include "content/shell/browser/shell_content_browser_client.h"
#include "net/base/features.h"
#include "net/base/net_errors.h"
#include "net/cookies/cookie_base.h"
#include "net/cookies/cookie_partition_key.h"
#include "net/cookies/cookie_partition_key_collection.h"
#include "net/cookies/cookie_util.h"
#include "net/dns/mock_host_resolver.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "net/test/embedded_test_server/http_request.h"
#include "net/test/embedded_test_server/http_response.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 "services/network/public/mojom/network_context.mojom.h"
#include "services/network/public/mojom/network_service.mojom.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/features.h"
#include "url/gurl.h"
namespace {
const char kHstsPath[] = "/hsts";
const char kHttpAuthPath[] = "/http_auth";
const char kHstsResponseBody[] = "HSTS set";
const char kHstsHostname[] = "a.test";
std::unique_ptr<net::test_server::HttpResponse> HandleHstsRequest(
const net::test_server::HttpRequest& request) {
if (request.relative_url == kHstsPath) {
std::unique_ptr<net::test_server::BasicHttpResponse> hsts_response =
std::make_unique<net::test_server::BasicHttpResponse>();
hsts_response->AddCustomHeader("Strict-Transport-Security",
"max-age=1000000");
hsts_response->set_content(kHstsResponseBody);
return hsts_response;
}
return nullptr;
}
std::unique_ptr<net::test_server::HttpResponse> HandleHttpAuthRequest(
const net::test_server::HttpRequest& request) {
if (request.relative_url != kHttpAuthPath) {
return nullptr;
}
auto http_response = std::make_unique<net::test_server::BasicHttpResponse>();
if (base::Contains(request.headers, "Authorization")) {
http_response->set_code(net::HTTP_OK);
http_response->set_content("Success!");
} else {
http_response->set_code(net::HTTP_UNAUTHORIZED);
http_response->AddCustomHeader("WWW-Authenticate",
"Basic realm=\"test realm\"");
}
return http_response;
}
}
namespace content {
class BrowsingDataRemoverImplBrowserTest
: public ContentBrowserTest,
public BackForwardCacheMetricsTestMatcher {
public:
BrowsingDataRemoverImplBrowserTest()
: ssl_server_(net::test_server::EmbeddedTestServer::TYPE_HTTPS) {
ssl_server_.SetCertHostnames({kHstsHostname, "localhost"});
ssl_server_.AddDefaultHandlers(GetTestDataFilePath());
ssl_server_.RegisterRequestHandler(base::BindRepeating(&HandleHstsRequest));
ssl_server_.RegisterRequestHandler(
base::BindRepeating(&HandleHttpAuthRequest));
EXPECT_TRUE(ssl_server_.Start());
}
void SetUpOnMainThread() override {
ukm_recorder_ = std::make_unique<ukm::TestAutoSetUkmRecorder>();
histogram_tester_ = std::make_unique<base::HistogramTester>();
host_resolver()->AddRule(kHstsHostname, "127.0.0.1");
}
void RemoveAndWait(uint64_t remove_mask) {
content::BrowsingDataRemover* remover =
shell()->web_contents()->GetBrowserContext()->GetBrowsingDataRemover();
content::BrowsingDataRemoverCompletionObserver completion_observer(remover);
remover->RemoveAndReply(
base::Time(), base::Time::Max(), remove_mask,
content::BrowsingDataRemover::ORIGIN_TYPE_UNPROTECTED_WEB,
&completion_observer);
completion_observer.BlockUntilCompletion();
}
void RemoveWithFilterAndWait(
uint64_t remove_mask,
std::unique_ptr<BrowsingDataFilterBuilder> filter) {
content::BrowsingDataRemover* remover =
shell()->web_contents()->GetBrowserContext()->GetBrowsingDataRemover();
content::BrowsingDataRemoverCompletionObserver completion_observer(remover);
remover->RemoveWithFilterAndReply(
base::Time(), base::Time::Max(), remove_mask,
content::BrowsingDataRemover::ORIGIN_TYPE_UNPROTECTED_WEB,
std::move(filter), &completion_observer);
completion_observer.BlockUntilCompletion();
}
void IssueRequestThatSetsHsts() {
std::unique_ptr<network::ResourceRequest> request =
std::make_unique<network::ResourceRequest>();
request->url = ssl_server_.GetURL(kHstsHostname, kHstsPath);
SimpleURLLoaderTestHelper loader_helper;
std::unique_ptr<network::SimpleURLLoader> loader =
network::SimpleURLLoader::Create(std::move(request),
TRAFFIC_ANNOTATION_FOR_TESTS);
loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
url_loader_factory(), loader_helper.GetCallback());
loader_helper.WaitForCallback();
ASSERT_TRUE(loader_helper.response_body());
EXPECT_EQ(kHstsResponseBody, *loader_helper.response_body());
EXPECT_TRUE(IsHstsSet());
}
bool IsHstsSet() {
GURL url = ssl_server_.GetURL(kHstsHostname, "/echo");
GURL::Replacements replacements;
replacements.SetSchemeStr("http");
url = url.ReplaceComponents(replacements);
url::Origin origin = url::Origin::Create(url);
std::unique_ptr<network::ResourceRequest> request =
std::make_unique<network::ResourceRequest>();
request->url = url;
request->site_for_cookies = net::SiteForCookies::FromOrigin(origin);
request->update_first_party_url_on_redirect = true;
request->trusted_params.emplace();
request->trusted_params->isolation_info = net::IsolationInfo::Create(
net::IsolationInfo::RequestType::kMainFrame, origin, origin,
net::SiteForCookies::FromOrigin(origin));
std::unique_ptr<network::SimpleURLLoader> loader =
network::SimpleURLLoader::Create(std::move(request),
TRAFFIC_ANNOTATION_FOR_TESTS);
SimpleURLLoaderTestHelper loader_helper;
loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
url_loader_factory(), loader_helper.GetCallback());
loader_helper.WaitForCallback();
if (loader_helper.response_body()) {
EXPECT_EQ("Echo", *loader_helper.response_body());
return true;
}
EXPECT_EQ(net::ERR_EMPTY_RESPONSE, loader->NetError());
return false;
}
void IssueRequestThatSetsHttpAuthCache() {
GURL url = ssl_server_.GetURL(kHttpAuthPath);
GURL::Replacements replacements;
replacements.SetUsernameStr("user");
replacements.SetPasswordStr("password");
GURL url_with_creds = url.ReplaceComponents(replacements);
ASSERT_TRUE(NavigateToURL(shell(), url_with_creds));
ASSERT_TRUE(IsHttpAuthCacheSet());
}
bool IsHttpAuthCacheSet() {
bool login_requested = false;
ShellContentBrowserClient::Get()->set_login_request_callback(
base::BindLambdaForTesting(
[&](bool is_primary_main_frame_navigation ,
bool is_navigation ) { login_requested = true; }));
GURL url = ssl_server_.GetURL(kHttpAuthPath);
bool navigation_suceeded = NavigateToURL(shell(), url);
EXPECT_NE(navigation_suceeded, login_requested);
return !login_requested && navigation_suceeded;
}
network::mojom::URLLoaderFactory* url_loader_factory() {
return shell()
->web_contents()
->GetBrowserContext()
->GetDefaultStoragePartition()
->GetURLLoaderFactoryForBrowserProcess()
.get();
}
network::mojom::NetworkContext* network_context() {
return shell()
->web_contents()
->GetBrowserContext()
->GetDefaultStoragePartition()
->GetNetworkContext();
}
const ukm::TestAutoSetUkmRecorder& ukm_recorder() override {
return *ukm_recorder_;
}
const base::HistogramTester& histogram_tester() override {
return *histogram_tester_;
}
const net::test_server::EmbeddedTestServer& ssl_server() {
return ssl_server_;
}
private:
net::test_server::EmbeddedTestServer ssl_server_;
std::unique_ptr<ukm::TestAutoSetUkmRecorder> ukm_recorder_;
std::unique_ptr<base::HistogramTester> histogram_tester_;
};
IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverImplBrowserTest,
ClearTransportSecurityState) {
IssueRequestThatSetsHsts();
RemoveAndWait(BrowsingDataRemover::DATA_TYPE_CACHE);
EXPECT_FALSE(IsHstsSet());
}
IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverImplBrowserTest,
PreserveTransportSecurityState) {
IssueRequestThatSetsHsts();
RemoveAndWait(BrowsingDataRemover::DATA_TYPE_DOWNLOADS);
EXPECT_TRUE(IsHstsSet());
auto filter = BrowsingDataFilterBuilder::Create(
BrowsingDataFilterBuilder::Mode::kDelete);
filter->AddRegisterableDomain("foobar.com");
RemoveWithFilterAndWait(BrowsingDataRemover::DATA_TYPE_CACHE,
std::move(filter));
EXPECT_TRUE(IsHstsSet());
}
IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverImplBrowserTest, ClearHttpAuthCache) {
ASSERT_FALSE(IsHttpAuthCacheSet());
IssueRequestThatSetsHttpAuthCache();
RemoveAndWait(BrowsingDataRemover::DATA_TYPE_COOKIES);
EXPECT_FALSE(IsHttpAuthCacheSet());
}
IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverImplBrowserTest,
PreserveHttpAuthCache) {
ASSERT_FALSE(IsHttpAuthCacheSet());
IssueRequestThatSetsHttpAuthCache();
RemoveAndWait(BrowsingDataRemover::DATA_TYPE_DOWNLOADS);
EXPECT_TRUE(IsHttpAuthCacheSet());
}
IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverImplBrowserTest,
ClearHttpAuthCacheWhenEmpty) {
ASSERT_FALSE(IsHttpAuthCacheSet());
RemoveAndWait(BrowsingDataRemover::DATA_TYPE_COOKIES);
EXPECT_FALSE(IsHttpAuthCacheSet());
}
IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverImplBrowserTest,
ClearBackForwardCacheEntries) {
if (!content::BackForwardCache::IsBackForwardCacheFeatureEnabled()) {
return;
}
GURL url_1 = ssl_server().GetURL("/title1.html");
GURL url_2 = ssl_server().GetURL("/title2.html");
ASSERT_TRUE(NavigateToURL(shell(), url_1));
ASSERT_TRUE(NavigateToURL(shell(), url_2));
ASSERT_TRUE(HistoryGoBack(shell()->web_contents()));
ExpectRestored(FROM_HERE);
ASSERT_TRUE(NavigateToURL(shell(), url_2));
RemoveAndWait(BrowsingDataRemover::DATA_TYPE_CACHE);
ASSERT_TRUE(HistoryGoBack(shell()->web_contents()));
ExpectNotRestored({BackForwardCacheMetrics::NotRestoredReason::kCacheFlushed},
{}, {}, {}, {}, FROM_HERE);
}
IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverImplBrowserTest,
ClearBackForwardCacheEntriesWithOrigin_DataTypeCache) {
if (!content::BackForwardCache::IsBackForwardCacheFeatureEnabled()) {
return;
}
GURL url_1 = ssl_server().GetURL("/title1.html");
GURL url_2 = ssl_server().GetURL("/title2.html");
ASSERT_TRUE(NavigateToURL(shell(), url_1));
ASSERT_TRUE(NavigateToURL(shell(), url_2));
ASSERT_TRUE(HistoryGoBack(shell()->web_contents()));
ExpectRestored(FROM_HERE);
ASSERT_TRUE(NavigateToURL(shell(), url_2));
auto filter = BrowsingDataFilterBuilder::Create(
BrowsingDataFilterBuilder::Mode::kDelete);
filter->AddRegisterableDomain("foobar.com");
RemoveWithFilterAndWait(BrowsingDataRemover::DATA_TYPE_CACHE,
std::move(filter));
ASSERT_TRUE(HistoryGoBack(shell()->web_contents()));
ExpectRestored(FROM_HERE);
ASSERT_TRUE(NavigateToURL(shell(), url_2));
filter = BrowsingDataFilterBuilder::Create(
BrowsingDataFilterBuilder::Mode::kDelete);
filter->AddRegisterableDomain(ssl_server().base_url().GetHost());
RemoveWithFilterAndWait(BrowsingDataRemover::DATA_TYPE_CACHE,
std::move(filter));
ASSERT_TRUE(HistoryGoBack(shell()->web_contents()));
ExpectNotRestored({BackForwardCacheMetrics::NotRestoredReason::kCacheFlushed},
{}, {}, {}, {}, FROM_HERE);
}
class BrowsingDataRemoverImplForCacheControlNoStorePageBrowserTest
: public BrowsingDataRemoverImplBrowserTest {
protected:
void SetUpCommandLine(base::CommandLine* command_line) override {
feature_list_.InitAndEnableFeatureWithParameters(
features::kCacheControlNoStoreEnterBackForwardCache,
{{"level", "store-and-evict"}});
BrowsingDataRemoverImplBrowserTest::SetUpCommandLine(command_line);
}
private:
base::test::ScopedFeatureList feature_list_;
};
IN_PROC_BROWSER_TEST_F(
BrowsingDataRemoverImplForCacheControlNoStorePageBrowserTest,
DoesClearNonCacheControlNoStoreBackForwardCacheEntries_DataTypeCookie) {
if (!content::BackForwardCache::IsBackForwardCacheFeatureEnabled()) {
return;
}
GURL url_1 = ssl_server().GetURL("/title1.html");
GURL url_2 = ssl_server().GetURL("/title2.html");
ASSERT_TRUE(NavigateToURL(shell(), url_1));
ASSERT_TRUE(NavigateToURL(shell(), url_2));
ASSERT_TRUE(HistoryGoBack(shell()->web_contents()));
ExpectRestored(FROM_HERE);
ASSERT_TRUE(NavigateToURL(shell(), url_2));
auto filter = BrowsingDataFilterBuilder::Create(
BrowsingDataFilterBuilder::Mode::kDelete);
filter->AddRegisterableDomain(ssl_server().base_url().GetHost());
RemoveWithFilterAndWait(BrowsingDataRemover::DATA_TYPE_COOKIES,
std::move(filter));
ASSERT_TRUE(HistoryGoBack(shell()->web_contents()));
ExpectRestored(FROM_HERE);
}
IN_PROC_BROWSER_TEST_F(
BrowsingDataRemoverImplForCacheControlNoStorePageBrowserTest,
ClearCacheControlNoStoreBackForwardCacheEntries_DataTypeCookie) {
if (!content::BackForwardCache::IsBackForwardCacheFeatureEnabled()) {
return;
}
GURL url_1 = ssl_server().GetURL("/echoall/nocache");
GURL url_2 = ssl_server().GetURL("/title1.html");
ASSERT_TRUE(NavigateToURL(shell(), url_1));
ASSERT_TRUE(NavigateToURL(shell(), url_2));
auto filter = BrowsingDataFilterBuilder::Create(
BrowsingDataFilterBuilder::Mode::kDelete);
filter->AddRegisterableDomain(ssl_server().base_url().GetHost());
RemoveWithFilterAndWait(BrowsingDataRemover::DATA_TYPE_COOKIES,
std::move(filter));
ASSERT_TRUE(HistoryGoBack(shell()->web_contents()));
ExpectNotRestored(
{BackForwardCacheMetrics::NotRestoredReason::kCookieFlushed,
BackForwardCacheMetrics::NotRestoredReason::kCacheControlNoStore},
{}, {}, {}, {}, FROM_HERE);
}
class CookiesBrowsingDataRemoverImplBrowserTest
: public BrowsingDataRemoverImplBrowserTest {
public:
void SetUpOnMainThread() override {
network_context()->GetCookieManager(
cookie_manager_.BindNewPipeAndPassReceiver());
}
bool SetCookie(
const GURL& url,
const std::string& cookie_line,
const std::optional<net::CookiePartitionKey>& cookie_partition_key) {
auto cookie_obj = net::CanonicalCookie::CreateForTesting(
url, cookie_line, base::Time::Now(), std::nullopt,
cookie_partition_key);
base::test::TestFuture<net::CookieAccessResult> future;
cookie_manager_->SetCanonicalCookie(*cookie_obj, url,
net::CookieOptions::MakeAllInclusive(),
future.GetCallback());
return future.Take().status.IsInclude();
}
net::CookieList GetAllCookies() {
base::test::TestFuture<const net::CookieList&> future;
cookie_manager_->GetAllCookies(future.GetCallback());
return future.Take();
}
private:
mojo::Remote<network::mojom::CookieManager> cookie_manager_;
};
IN_PROC_BROWSER_TEST_F(CookiesBrowsingDataRemoverImplBrowserTest,
ClearsAllCookiesByDefault) {
ASSERT_TRUE(SetCookie(GURL("http://a.com"), "A=0", std::nullopt));
ASSERT_TRUE(SetCookie(GURL("https://a.com"), "B=1; secure; samesite=none",
std::nullopt));
ASSERT_TRUE(SetCookie(GURL("https://b.com"),
"C=2; secure; samesite=none; max-age=10000",
std::nullopt));
ASSERT_EQ(3u, GetAllCookies().size());
ASSERT_TRUE(SetCookie(
GURL("https://c.com"), "A=0; secure; partitioned",
net::CookiePartitionKey::FromURLForTesting(GURL("https://d.com"))));
ASSERT_TRUE(SetCookie(
GURL("https://c.com"), "A=0; secure; samesite=none; partitioned",
net::CookiePartitionKey::FromURLForTesting(GURL("http://e.com"))));
ASSERT_TRUE(SetCookie(
GURL("https://f.com"),
"B=1; secure; samesite=none; max-age=10000; partitioned",
net::CookiePartitionKey::FromURLForTesting(GURL("https://g.com"))));
ASSERT_TRUE(
SetCookie(GURL("https://f.com"), "C=2; secure; samesite=none",
net::CookiePartitionKey::FromURLForTesting(
GURL("https://g.com"),
net::CookiePartitionKey::AncestorChainBit::kCrossSite,
base::UnguessableToken::Create())));
ASSERT_EQ(7u, GetAllCookies().size());
RemoveAndWait(BrowsingDataRemover::DATA_TYPE_COOKIES);
EXPECT_EQ(0u, GetAllCookies().size());
}
IN_PROC_BROWSER_TEST_F(CookiesBrowsingDataRemoverImplBrowserTest,
ClearingCookiesByHostKey) {
ASSERT_TRUE(SetCookie(GURL("https://a.com"), "A=0; secure; partitioned",
std::nullopt));
ASSERT_TRUE(SetCookie(
GURL("https://a.com"), "B=1; secure; partitioned",
net::CookiePartitionKey::FromURLForTesting(GURL("https://a.com"))));
ASSERT_TRUE(SetCookie(
GURL("https://a.com"), "C=2; secure; partitioned",
net::CookiePartitionKey::FromURLForTesting(GURL("https://b.com"))));
ASSERT_TRUE(SetCookie(GURL("https://b.com"), "D=3; secure; partitioned",
std::nullopt));
ASSERT_TRUE(SetCookie(
GURL("https://b.com"), "E=4; secure; partitioned",
net::CookiePartitionKey::FromURLForTesting(GURL("https://a.com"))));
ASSERT_TRUE(SetCookie(
GURL("https://b.com"), "F=5; secure; partitioned",
net::CookiePartitionKey::FromURLForTesting(GURL("https://b.com"))));
ASSERT_EQ(6u, GetAllCookies().size());
std::unique_ptr<BrowsingDataFilterBuilder> builder(
BrowsingDataFilterBuilder::Create(
BrowsingDataFilterBuilder::Mode::kDelete));
builder->AddRegisterableDomain("a.com");
RemoveWithFilterAndWait(BrowsingDataRemover::DATA_TYPE_COOKIES,
std::move(builder));
auto cookies = GetAllCookies();
EXPECT_EQ(3u, cookies.size());
EXPECT_EQ("D", cookies[0].Name());
EXPECT_EQ("E", cookies[1].Name());
EXPECT_EQ("F", cookies[2].Name());
}
IN_PROC_BROWSER_TEST_F(CookiesBrowsingDataRemoverImplBrowserTest,
ClearCookiesWithEmptyFilter) {
ASSERT_TRUE(SetCookie(GURL("https://a.com"), "A=0; secure",
std::nullopt));
ASSERT_EQ(1u, GetAllCookies().size());
std::unique_ptr<BrowsingDataFilterBuilder> builder(
BrowsingDataFilterBuilder::Create(
BrowsingDataFilterBuilder::Mode::kDelete));
EXPECT_TRUE(builder->MatchesNothing());
EXPECT_DEATH_IF_SUPPORTED(
RemoveWithFilterAndWait(BrowsingDataRemover::DATA_TYPE_COOKIES,
std::move(builder)),
"");
EXPECT_EQ(1u, GetAllCookies().size());
}
IN_PROC_BROWSER_TEST_F(CookiesBrowsingDataRemoverImplBrowserTest,
ClearSiteData_PartitionedCookiesOnly) {
ASSERT_TRUE(SetCookie(GURL("https://a.com"), "A=0; secure;",
std::nullopt));
ASSERT_TRUE(SetCookie(
GURL("https://a.com"), "B=1; secure; partitioned",
net::CookiePartitionKey::FromURLForTesting(GURL("https://b.com"))));
ASSERT_TRUE(
SetCookie(GURL("https://a.com"), "C=2; secure;",
net::CookiePartitionKey::FromURLForTesting(
GURL("https://b.com"),
net::CookiePartitionKey::AncestorChainBit::kCrossSite,
base::UnguessableToken::Create())));
std::unique_ptr<BrowsingDataFilterBuilder> builder(
BrowsingDataFilterBuilder::Create(
BrowsingDataFilterBuilder::Mode::kDelete));
builder->AddRegisterableDomain("a.com");
builder->SetPartitionedCookiesOnly(true);
RemoveWithFilterAndWait(BrowsingDataRemover::DATA_TYPE_COOKIES,
std::move(builder));
auto cookies = GetAllCookies();
EXPECT_EQ(1u, cookies.size());
EXPECT_EQ("A", cookies[0].Name());
}
IN_PROC_BROWSER_TEST_F(CookiesBrowsingDataRemoverImplBrowserTest,
ClearSiteData_AllDomainsPartitionedCookiesOnly) {
ASSERT_TRUE(SetCookie(GURL("https://a.com"), "A=0; secure;",
std::nullopt));
ASSERT_TRUE(SetCookie(
GURL("https://a.com"), "B=1; secure; partitioned",
net::CookiePartitionKey::FromURLForTesting(GURL("https://b.com"))));
std::unique_ptr<BrowsingDataFilterBuilder> builder(
BrowsingDataFilterBuilder::Create(
BrowsingDataFilterBuilder::Mode::kPreserve));
builder->SetPartitionedCookiesOnly(true);
RemoveWithFilterAndWait(BrowsingDataRemover::DATA_TYPE_COOKIES,
std::move(builder));
EXPECT_THAT(GetAllCookies(), testing::ElementsAre(testing::Property(
&net::CookieBase::Name, "A")));
}
IN_PROC_BROWSER_TEST_F(CookiesBrowsingDataRemoverImplBrowserTest,
ClearSiteData_AllDomainsCookiePartitionKeyCollection) {
ASSERT_TRUE(SetCookie(GURL("https://a.com"), "A=0; secure;",
std::nullopt));
ASSERT_TRUE(SetCookie(
GURL("https://a.com"), "B=1; secure; partitioned",
net::CookiePartitionKey::FromURLForTesting(GURL("https://b.com"))));
ASSERT_TRUE(SetCookie(
GURL("https://a.com"), "C=2; secure; partitioned",
net::CookiePartitionKey::FromURLForTesting(GURL("https://c.com"))));
std::unique_ptr<BrowsingDataFilterBuilder> builder(
BrowsingDataFilterBuilder::Create(
BrowsingDataFilterBuilder::Mode::kPreserve));
builder->SetCookiePartitionKeyCollection(net::CookiePartitionKeyCollection(
net::CookiePartitionKey::FromURLForTesting(GURL("https://b.com"))));
RemoveWithFilterAndWait(BrowsingDataRemover::DATA_TYPE_COOKIES,
std::move(builder));
EXPECT_THAT(GetAllCookies(), testing::ElementsAre(testing::Property(
&net::CookieBase::Name, "C")));
}
namespace {
class TrustTokensTester {
public:
explicit TrustTokensTester(network::mojom::NetworkContext* network_context)
: network_context_(network_context) {}
void AddOrigin(const url::Origin& origin) {
mojo::Remote<network::mojom::TrustTokenQueryAnswerer> answerer;
network_context_->GetTrustTokenQueryAnswerer(
answerer.BindNewPipeAndPassReceiver(), origin);
for (int i = 0; i < 2; ++i) {
base::RunLoop run_loop;
answerer->HasTrustTokens(
url::Origin::Create(
GURL(base::StringPrintf("https://prime%d.example", i))),
base::BindLambdaForTesting(
[&](network::mojom::HasTrustTokensResultPtr) {
run_loop.Quit();
}));
run_loop.Run();
}
}
bool HasOrigin(const url::Origin& origin) {
mojo::Remote<network::mojom::TrustTokenQueryAnswerer> answerer;
network_context_->GetTrustTokenQueryAnswerer(
answerer.BindNewPipeAndPassReceiver(), origin);
base::RunLoop run_loop;
bool has_origin = false;
answerer->HasTrustTokens(
url::Origin::Create(GURL("https://probe.example")),
base::BindLambdaForTesting(
[&](network::mojom::HasTrustTokensResultPtr result) {
if (result->status ==
network::mojom::TrustTokenOperationStatus::kSiteIssuerLimit) {
has_origin = true;
}
run_loop.Quit();
}));
run_loop.Run();
return has_origin;
}
private:
raw_ptr<network::mojom::NetworkContext> network_context_ = nullptr;
};
}
using BrowsingDataRemoverImplTrustTokenTest =
BrowsingDataRemoverImplBrowserTest;
IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverImplTrustTokenTest, Remove) {
TrustTokensTester tester(network_context());
auto origin = url::Origin::Create(GURL("https://topframe.example"));
tester.AddOrigin(origin);
ASSERT_TRUE(tester.HasOrigin(origin));
RemoveAndWait(BrowsingDataRemover::DATA_TYPE_TRUST_TOKENS);
EXPECT_FALSE(tester.HasOrigin(origin));
}
IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverImplTrustTokenTest, RemoveByDomain) {
TrustTokensTester tester(network_context());
auto origin = url::Origin::Create(GURL("https://topframe.example"));
auto sub_origin = url::Origin::Create(GURL("https://sub.topframe.example"));
auto another_origin =
url::Origin::Create(GURL("https://another-topframe.example"));
tester.AddOrigin(origin);
tester.AddOrigin(sub_origin);
tester.AddOrigin(another_origin);
ASSERT_TRUE(tester.HasOrigin(origin));
ASSERT_TRUE(tester.HasOrigin(sub_origin));
ASSERT_TRUE(tester.HasOrigin(another_origin));
std::unique_ptr<BrowsingDataFilterBuilder> builder(
BrowsingDataFilterBuilder::Create(
BrowsingDataFilterBuilder::Mode::kDelete));
builder->AddRegisterableDomain("topframe.example");
RemoveWithFilterAndWait(BrowsingDataRemover::DATA_TYPE_TRUST_TOKENS,
std::move(builder));
EXPECT_FALSE(tester.HasOrigin(origin));
EXPECT_FALSE(tester.HasOrigin(sub_origin));
EXPECT_TRUE(tester.HasOrigin(another_origin));
}
IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverImplTrustTokenTest,
PreserveByDomain) {
TrustTokensTester tester(network_context());
auto origin = url::Origin::Create(GURL("https://topframe.example"));
auto sub_origin = url::Origin::Create(GURL("https://sub.topframe.example"));
auto another_origin =
url::Origin::Create(GURL("https://another-topframe.example"));
tester.AddOrigin(origin);
tester.AddOrigin(sub_origin);
tester.AddOrigin(another_origin);
ASSERT_TRUE(tester.HasOrigin(origin));
ASSERT_TRUE(tester.HasOrigin(sub_origin));
ASSERT_TRUE(tester.HasOrigin(another_origin));
std::unique_ptr<BrowsingDataFilterBuilder> builder(
BrowsingDataFilterBuilder::Create(
BrowsingDataFilterBuilder::Mode::kPreserve));
builder->AddRegisterableDomain("topframe.example");
RemoveWithFilterAndWait(BrowsingDataRemover::DATA_TYPE_TRUST_TOKENS,
std::move(builder));
EXPECT_TRUE(tester.HasOrigin(origin));
EXPECT_TRUE(tester.HasOrigin(sub_origin));
EXPECT_FALSE(tester.HasOrigin(another_origin));
}
class BrowsingDataRemoverImplSharedStorageBrowserTest
: public BrowsingDataRemoverImplBrowserTest {
public:
BrowsingDataRemoverImplSharedStorageBrowserTest() {
feature_list_.InitAndEnableFeature(network::features::kSharedStorageAPI);
}
StoragePartition* storage_partition() {
return shell()
->web_contents()
->GetBrowserContext()
->GetDefaultStoragePartition();
}
private:
base::test::ScopedFeatureList feature_list_;
};
IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverImplSharedStorageBrowserTest,
Remove) {
SharedStorageClearSiteDataTester tester(storage_partition());
auto origin = url::Origin::Create(GURL("https://topframe.example"));
tester.AddConsecutiveSharedStorageEntries(origin, u"key", u"value", 10);
EXPECT_THAT(tester.GetSharedStorageOrigins(),
testing::UnorderedElementsAre(origin));
const int kNumBytesPerEntry = 20;
EXPECT_EQ(10 * kNumBytesPerEntry, tester.GetSharedStorageTotalBytes());
RemoveAndWait(BrowsingDataRemover::DATA_TYPE_SHARED_STORAGE);
EXPECT_TRUE(tester.GetSharedStorageOrigins().empty());
EXPECT_EQ(0, tester.GetSharedStorageTotalBytes());
}
IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverImplSharedStorageBrowserTest,
RemoveByDomain) {
SharedStorageClearSiteDataTester tester(storage_partition());
auto origin = url::Origin::Create(GURL("https://topframe.example"));
auto sub_origin = url::Origin::Create(GURL("https://sub.topframe.example"));
auto another_origin =
url::Origin::Create(GURL("https://another-topframe.example"));
tester.AddConsecutiveSharedStorageEntries(origin, u"key", u"value", 5);
tester.AddConsecutiveSharedStorageEntries(sub_origin, u"key", u"value", 10);
tester.AddConsecutiveSharedStorageEntries(another_origin, u"key", u"value",
1);
EXPECT_THAT(
tester.GetSharedStorageOrigins(),
testing::UnorderedElementsAre(origin, sub_origin, another_origin));
const int kNumBytesPerEntry = 20;
EXPECT_EQ(16 * kNumBytesPerEntry, tester.GetSharedStorageTotalBytes());
std::unique_ptr<BrowsingDataFilterBuilder> builder(
BrowsingDataFilterBuilder::Create(
BrowsingDataFilterBuilder::Mode::kDelete));
builder->AddRegisterableDomain("topframe.example");
RemoveWithFilterAndWait(BrowsingDataRemover::DATA_TYPE_SHARED_STORAGE,
std::move(builder));
EXPECT_THAT(tester.GetSharedStorageOrigins(),
testing::UnorderedElementsAre(another_origin));
EXPECT_EQ(1 * kNumBytesPerEntry, tester.GetSharedStorageTotalBytes());
}
IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverImplSharedStorageBrowserTest,
PreserveByDomain) {
SharedStorageClearSiteDataTester tester(storage_partition());
auto origin = url::Origin::Create(GURL("https://topframe.example"));
auto sub_origin = url::Origin::Create(GURL("https://sub.topframe.example"));
auto another_origin =
url::Origin::Create(GURL("https://another-topframe.example"));
tester.AddConsecutiveSharedStorageEntries(origin, u"key", u"value", 5);
tester.AddConsecutiveSharedStorageEntries(sub_origin, u"key", u"value", 10);
tester.AddConsecutiveSharedStorageEntries(another_origin, u"key", u"value",
1);
EXPECT_THAT(
tester.GetSharedStorageOrigins(),
testing::UnorderedElementsAre(origin, sub_origin, another_origin));
const int kNumBytesPerEntry = 20;
EXPECT_EQ(16 * kNumBytesPerEntry, tester.GetSharedStorageTotalBytes());
std::unique_ptr<BrowsingDataFilterBuilder> builder(
BrowsingDataFilterBuilder::Create(
BrowsingDataFilterBuilder::Mode::kPreserve));
builder->AddRegisterableDomain("topframe.example");
RemoveWithFilterAndWait(BrowsingDataRemover::DATA_TYPE_SHARED_STORAGE,
std::move(builder));
EXPECT_THAT(tester.GetSharedStorageOrigins(),
testing::UnorderedElementsAre(origin, sub_origin));
EXPECT_EQ(15 * kNumBytesPerEntry, tester.GetSharedStorageTotalBytes());
}
class BrowsingDataRemoverImplPrerenderingBrowserTest
: public BrowsingDataRemoverImplBrowserTest {
public:
BrowsingDataRemoverImplPrerenderingBrowserTest() {
prerender_helper_ =
std::make_unique<test::PrerenderTestHelper>(base::BindRepeating(
&BrowsingDataRemoverImplPrerenderingBrowserTest::web_contents,
base::Unretained(this)));
}
~BrowsingDataRemoverImplPrerenderingBrowserTest() override = default;
protected:
test::PrerenderTestHelper& prerender_helper() { return *prerender_helper_; }
WebContents* web_contents() { return shell()->web_contents(); }
std::unique_ptr<test::PrerenderTestHelper> prerender_helper_;
};
IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverImplPrerenderingBrowserTest,
ClearCacheCancelsPrerendering) {
GURL initial_url = ssl_server().GetURL("/title1.html");
GURL prerendering_url = ssl_server().GetURL("/empty.html");
ASSERT_TRUE(NavigateToURL(shell(), initial_url));
FrameTreeNodeId host_id = prerender_helper().AddPrerender(prerendering_url);
content::test::PrerenderHostObserver host_observer(*web_contents(), host_id);
auto filter = BrowsingDataFilterBuilder::Create(
BrowsingDataFilterBuilder::Mode::kDelete);
filter->AddRegisterableDomain(ssl_server().base_url().GetHost());
RemoveWithFilterAndWait(BrowsingDataRemover::DATA_TYPE_CACHE,
std::move(filter));
host_observer.WaitForDestroyed();
histogram_tester().ExpectUniqueSample(
"Prerender.Experimental.PrerenderHostFinalStatus.SpeculationRule",
PrerenderFinalStatus::kBrowsingDataRemoved, 1);
}
class BrowsingDataRemoverImplPrefetchBrowserTest
: public BrowsingDataRemoverImplBrowserTest {
public:
void StartPrefetch(const GURL& url, Shell* shell) {
auto* prefetch_document_manager =
PrefetchDocumentManager::GetOrCreateForCurrentDocument(
shell->web_contents()->GetPrimaryMainFrame());
auto candidate = blink::mojom::SpeculationCandidate::New();
candidate->url = url;
candidate->action = blink::mojom::SpeculationAction::kPrefetch;
candidate->eagerness = blink::mojom::SpeculationEagerness::kImmediate;
candidate->referrer = Referrer::SanitizeForRequest(
url, blink::mojom::Referrer(
shell->web_contents()->GetURL(),
network::mojom::ReferrerPolicy::kStrictOriginWhenCrossOrigin));
std::vector<blink::mojom::SpeculationCandidatePtr> candidates;
candidates.push_back(std::move(candidate));
prefetch_document_manager->ProcessCandidates(candidates);
}
~BrowsingDataRemoverImplPrefetchBrowserTest() override = default;
};
IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverImplPrefetchBrowserTest,
ClearCacheCancelsPrefetch) {
GURL initial_url = ssl_server().GetURL("/empty.html");
GURL prefetch_url = ssl_server().GetURL("/title1.html");
ASSERT_TRUE(NavigateToURL(shell(), initial_url));
test::TestPrefetchWatcher test_prefetch_watcher;
StartPrefetch(prefetch_url, shell());
test_prefetch_watcher.WaitUntilPrefetchResponseCompleted(
static_cast<RenderFrameHostImpl*>(
shell()->web_contents()->GetPrimaryMainFrame())
->GetDocumentToken(),
prefetch_url);
auto filter = BrowsingDataFilterBuilder::Create(
BrowsingDataFilterBuilder::Mode::kDelete);
filter->AddRegisterableDomain(ssl_server().base_url().GetHost());
RemoveWithFilterAndWait(BrowsingDataRemover::DATA_TYPE_CACHE,
std::move(filter));
histogram_tester().ExpectUniqueSample(
"Preloading.Prefetch.PrefetchStatus",
PrefetchStatus::kPrefetchEvictedAfterBrowsingDataRemoved, 1);
}
IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverImplPrefetchBrowserTest,
ClearCacheCancelsPrefetchMultipleOrigins) {
GURL referral_url1 = ssl_server().GetURL("/empty.html");
GURL prefetch_url1 = ssl_server().GetURL("/title1.html");
GURL referral_url2 = ssl_server().GetURL("a.test", "/title1.html");
GURL prefetch_url2 = ssl_server().GetURL("a.test", "/empty.html");
ASSERT_TRUE(NavigateToURL(shell(), referral_url1));
Shell* new_shell =
Shell::CreateNewWindow(shell()->web_contents()->GetBrowserContext(),
GURL(url::kAboutBlankURL), nullptr, gfx::Size());
ASSERT_TRUE(NavigateToURL(new_shell, referral_url2));
test::TestPrefetchWatcher test_prefetch_watcher;
StartPrefetch(prefetch_url1, shell());
test_prefetch_watcher.WaitUntilPrefetchResponseCompleted(
static_cast<RenderFrameHostImpl*>(
shell()->web_contents()->GetPrimaryMainFrame())
->GetDocumentToken(),
prefetch_url1);
StartPrefetch(prefetch_url2, new_shell);
test_prefetch_watcher.WaitUntilPrefetchResponseCompleted(
static_cast<RenderFrameHostImpl*>(
new_shell->web_contents()->GetPrimaryMainFrame())
->GetDocumentToken(),
prefetch_url2);
auto filter = BrowsingDataFilterBuilder::Create(
BrowsingDataFilterBuilder::Mode::kPreserve);
RemoveWithFilterAndWait(BrowsingDataRemover::DATA_TYPE_CACHE,
std::move(filter));
histogram_tester().ExpectUniqueSample(
"Preloading.Prefetch.PrefetchStatus",
PrefetchStatus::kPrefetchEvictedAfterBrowsingDataRemoved, 2);
}
class BrowsingDataRemoverImplPrefetchHoldbackBrowserTest
: public BrowsingDataRemoverImplPrefetchBrowserTest {
public:
BrowsingDataRemoverImplPrefetchHoldbackBrowserTest() {
feature_list_.InitAndEnableFeatureWithParameters(
features::kPreloadingConfig, {{"preloading_config", R"(
[{
"preloading_type": "Prefetch",
"preloading_predictor": "SpeculationRules",
"holdback": true,
"sampling_likelihood": 1
}])"}});
}
~BrowsingDataRemoverImplPrefetchHoldbackBrowserTest() override = default;
private:
base::test::ScopedFeatureList feature_list_;
};
IN_PROC_BROWSER_TEST_F(BrowsingDataRemoverImplPrefetchHoldbackBrowserTest,
ClearCacheCancelsHeldbackPrefetch) {
GURL initial_url = ssl_server().GetURL("/empty.html");
GURL prefetch_url = ssl_server().GetURL("/title1.html");
ASSERT_TRUE(NavigateToURL(shell(), initial_url));
StartPrefetch(prefetch_url, shell());
auto filter = BrowsingDataFilterBuilder::Create(
BrowsingDataFilterBuilder::Mode::kDelete);
filter->AddRegisterableDomain(ssl_server().base_url().GetHost());
RemoveWithFilterAndWait(BrowsingDataRemover::DATA_TYPE_CACHE,
std::move(filter));
histogram_tester().ExpectUniqueSample(
"Preloading.Prefetch.PrefetchStatus",
PrefetchStatus::kPrefetchEvictedAfterBrowsingDataRemoved, 1);
}
}