#include <algorithm>
#include <array>
#include <memory>
#include <optional>
#include <vector>
#include "base/command_line.h"
#include "base/feature_list.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/run_loop.h"
#include "base/strings/escape.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/synchronization/lock.h"
#include "base/test/bind.h"
#include "base/test/scoped_feature_list.h"
#include "base/thread_annotations.h"
#include "base/types/optional_ref.h"
#include "build/build_config.h"
#include "content/browser/browsing_data/browsing_data_browsertest_utils.h"
#include "content/browser/browsing_data/browsing_data_filter_builder_impl.h"
#include "content/browser/browsing_data/shared_storage_clear_site_data_tester.h"
#include "content/browser/browsing_data/storage_bucket_clear_site_data_tester.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/browsing_data_remover.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/browser/network_service_util.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/browser/storage_partition_config.h"
#include "content/public/browser/storage_usage_info.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/content_browser_test.h"
#include "content/public/test/content_browser_test_utils.h"
#include "content/public/test/mock_browsing_data_remover_delegate.h"
#include "content/public/test/test_navigation_observer.h"
#include "content/shell/browser/shell.h"
#include "net/base/features.h"
#include "net/base/net_errors.h"
#include "net/base/schemeful_site.h"
#include "net/base/url_util.h"
#include "net/cookies/cookie_access_result.h"
#include "net/cookies/cookie_partition_key.h"
#include "net/cookies/cookie_partition_key_collection.h"
#include "net/cookies/cookie_store.h"
#include "net/dns/mock_host_resolver.h"
#include "net/test/embedded_test_server/http_request.h"
#include "net/test/embedded_test_server/http_response.h"
#include "services/network/public/cpp/features.h"
#include "storage/browser/quota/quota_settings.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/public/common/features_generated.h"
#include "third_party/blink/public/common/storage_key/storage_key.h"
#include "third_party/blink/public/mojom/storage_key/ancestor_chain_bit.mojom.h"
#include "url/origin.h"
#include "url/url_constants.h"
using testing::_;
namespace content {
namespace {
void AddQuery(GURL* url, const std::string& key, const std::string& value) {
*url = GURL(url->spec() + (url->has_query() ? "&" : "?") + key + "=" +
base::EscapeQueryParamValue(value, false));
}
void WaitForTitle(const Shell* shell, const char* expected_title) {
std::u16string expected_title_16 = base::ASCIIToUTF16(expected_title);
TitleWatcher title_watcher(shell->web_contents(), expected_title_16);
ASSERT_EQ(expected_title_16, title_watcher.WaitAndGetTitle());
}
static const char* kClearCookiesHeader = "\"cookies\"";
enum class SetStorageKey { kYes, kNo };
class TestBrowsingDataRemoverDelegate : public MockBrowsingDataRemoverDelegate {
public:
TestBrowsingDataRemoverDelegate() = default;
void ExpectClearSiteDataCall(
const StoragePartitionConfig& storage_partition_config,
const url::Origin& origin,
const net::SchemefulSite& top_level_site,
bool cookies,
bool storage,
bool cache,
bool override_partition_key_cross_site = false,
SetStorageKey set_storage_key = SetStorageKey::kYes) {
const uint64_t kOriginTypeMask =
BrowsingDataRemover::ORIGIN_TYPE_UNPROTECTED_WEB |
BrowsingDataRemover::ORIGIN_TYPE_PROTECTED_WEB;
bool partition_key_cross_site =
override_partition_key_cross_site ||
net::SchemefulSite(origin) != top_level_site;
if (cookies) {
uint64_t data_type_mask =
BrowsingDataRemover::DATA_TYPE_COOKIES |
BrowsingDataRemover::DATA_TYPE_AVOID_CLOSING_CONNECTIONS |
BrowsingDataRemover::DATA_TYPE_DEVICE_BOUND_SESSIONS;
net::CookiePartitionKey::AncestorChainBit ancestor_chain_bit =
net::CookiePartitionKey::BoolToAncestorChainBit(
partition_key_cross_site);
BrowsingDataFilterBuilderImpl filter_builder(
BrowsingDataFilterBuilder::Mode::kDelete);
filter_builder.AddRegisterableDomain(origin.host());
filter_builder.SetStoragePartitionConfig(storage_partition_config);
filter_builder.SetCookiePartitionKeyCollection(
net::CookiePartitionKeyCollection(
net::CookiePartitionKey::FromStorageKeyComponents(
top_level_site, ancestor_chain_bit, std::nullopt)));
ExpectCall(base::Time(), base::Time::Max(), data_type_mask,
kOriginTypeMask, &filter_builder);
}
if (storage || cache) {
uint64_t data_type_mask =
(storage ? BrowsingDataRemover::DATA_TYPE_DOM_STORAGE |
BrowsingDataRemover::DATA_TYPE_PRIVACY_SANDBOX |
BrowsingDataRemover::DATA_TYPE_DEVICE_BOUND_SESSIONS
: 0) |
(cache ? BrowsingDataRemover::DATA_TYPE_CACHE : 0);
data_type_mask &=
~BrowsingDataRemover::DATA_TYPE_PRIVACY_SANDBOX_INTERNAL;
data_type_mask &=
~BrowsingDataRemover::DATA_TYPE_INTEREST_GROUPS_USER_CLEAR;
BrowsingDataFilterBuilderImpl filter_builder(
BrowsingDataFilterBuilder::Mode::kDelete);
filter_builder.AddOrigin(origin);
filter_builder.SetStoragePartitionConfig(storage_partition_config);
if (set_storage_key == SetStorageKey::kYes) {
filter_builder.SetStorageKey(blink::StorageKey::Create(
origin, top_level_site,
partition_key_cross_site
? blink::mojom::AncestorChainBit::kCrossSite
: blink::mojom::AncestorChainBit::kSameSite));
}
ExpectCall(base::Time(), base::Time::Max(), data_type_mask,
kOriginTypeMask, &filter_builder);
}
}
void ExpectClearSiteDataCookiesCall(
const StoragePartitionConfig& storage_partition_config,
const url::Origin& origin,
bool override_partition_key_cross_site = false,
base::optional_ref<const net::SchemefulSite> top_level_site =
base::optional_ref<const net::SchemefulSite>()) {
ExpectClearSiteDataCall(storage_partition_config, origin,
top_level_site.has_value()
? top_level_site.value()
: net::SchemefulSite(origin),
true,
false,
false, override_partition_key_cross_site);
}
};
}
class ClearSiteDataHandlerBrowserTest : public ContentBrowserTest {
public:
void SetUpCommandLine(base::CommandLine* command_line) override {
browsing_data_browsertest_utils::SetIgnoreCertificateErrors(command_line);
}
void SetUpOnMainThread() override {
ContentBrowserTest::SetUpOnMainThread();
browser_context()->GetBrowsingDataRemover()->SetEmbedderDelegate(
&embedder_delegate_);
host_resolver()->AddRule("*", "127.0.0.1");
if (IsOutOfProcessNetworkService())
browsing_data_browsertest_utils::SetUpMockCertVerifier(net::OK);
embedded_test_server()->RegisterRequestHandler(
base::BindRepeating(&ClearSiteDataHandlerBrowserTest::HandleRequest,
base::Unretained(this)));
ASSERT_TRUE(embedded_test_server()->Start());
https_server_ = std::make_unique<net::EmbeddedTestServer>(
net::test_server::EmbeddedTestServer::TYPE_HTTPS);
https_server_->SetSSLConfig(net::EmbeddedTestServer::CERT_OK);
https_server_->RegisterRequestHandler(
base::BindRepeating(&ClearSiteDataHandlerBrowserTest::HandleRequest,
base::Unretained(this)));
ASSERT_TRUE(https_server_->Start());
}
BrowserContext* browser_context() {
return shell()->web_contents()->GetBrowserContext();
}
StoragePartition* storage_partition() {
return browser_context()->GetDefaultStoragePartition();
}
const StoragePartitionConfig& storage_partition_config() {
return storage_partition()->GetConfig();
}
void AddCookie(const GURL& url,
const std::optional<net::CookiePartitionKey>&
cookie_partition_key = std::nullopt) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
network::mojom::CookieManager* cookie_manager =
storage_partition()->GetCookieManagerForBrowserProcess();
std::string cookie_line = "A=1";
if (cookie_partition_key) {
cookie_line += "; Secure; Partitioned";
}
std::unique_ptr<net::CanonicalCookie> cookie(
net::CanonicalCookie::CreateForTesting(
url, cookie_line, base::Time::Now(), std::nullopt,
cookie_partition_key));
base::RunLoop run_loop;
cookie_manager->SetCanonicalCookie(
*cookie, url, net::CookieOptions::MakeAllInclusive(),
base::BindOnce(&ClearSiteDataHandlerBrowserTest::AddCookieCallback,
run_loop.QuitClosure()));
run_loop.Run();
}
net::CookieList GetCookies() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
network::mojom::CookieManager* cookie_manager =
storage_partition()->GetCookieManagerForBrowserProcess();
base::RunLoop run_loop;
net::CookieList cookie_list;
cookie_manager->GetAllCookies(
base::BindOnce(&ClearSiteDataHandlerBrowserTest::GetCookiesCallback,
run_loop.QuitClosure(), base::Unretained(&cookie_list)));
run_loop.Run();
return cookie_list;
}
void CreateCacheEntry(const GURL& url) {
ASSERT_EQ(net::OK,
LoadBasicRequest(storage_partition()->GetNetworkContext(), url));
}
bool TestCacheEntry(const GURL& url) {
return LoadBasicRequest(storage_partition()->GetNetworkContext(), url,
net::LOAD_ONLY_FROM_CACHE) == net::OK;
}
GURL GetURLForHTTPSHost1(const std::string& relative_url) {
return https_server_->GetURL("origin1.com", relative_url);
}
GURL GetURLForHTTPSHost2(const std::string& relative_url) {
return https_server_->GetURL("origin2.com", relative_url);
}
TestBrowsingDataRemoverDelegate* delegate() { return &embedder_delegate_; }
net::EmbeddedTestServer* https_server() { return https_server_.get(); }
void SetClearSiteDataHeader(const std::string& header) {
base::AutoLock lock(clear_site_data_header_lock_);
clear_site_data_header_ = header;
}
bool RunScriptAndGetBool(const std::string& script) {
return EvalJs(shell()->web_contents(), script).ExtractBool();
}
private:
std::unique_ptr<net::test_server::HttpResponse> HandleRequest(
const net::test_server::HttpRequest& request) {
std::unique_ptr<net::test_server::BasicHttpResponse> response(
new net::test_server::BasicHttpResponse());
{
base::AutoLock lock(clear_site_data_header_lock_);
if (!clear_site_data_header_.empty())
response->AddCustomHeader("Clear-Site-Data", clear_site_data_header_);
}
std::string value;
if (net::GetValueForKeyInQuery(request.GetURL(), "header", &value))
response->AddCustomHeader("Clear-Site-Data", value);
if (net::GetValueForKeyInQuery(request.GetURL(), "redirect", &value)) {
response->set_code(net::HTTP_FOUND);
response->AddCustomHeader("Location", value);
} else {
response->set_code(net::HTTP_OK);
}
if (net::GetValueForKeyInQuery(request.GetURL(), "html", &value)) {
response->set_content_type("text/html");
response->set_content(value);
response->AddCustomHeader("X-XSS-Protection", "0");
}
if (net::GetValueForKeyInQuery(request.GetURL(),
"access-control-allow-origin", &value)) {
response->AddCustomHeader("Access-Control-Allow-Origin", value);
response->AddCustomHeader("Access-Control-Allow-Credentials", "true");
}
browsing_data_browsertest_utils::SetResponseContent(request.GetURL(),
&value, response.get());
if (base::StartsWith(request.relative_url, "/cachetime",
base::CompareCase::SENSITIVE)) {
response->set_content(
"<html><head><title>Cache: max-age=60</title></head></html>");
response->set_content_type("text/html");
response->AddCustomHeader("Cache-Control", "max-age=60");
}
return std::move(response);
}
static void AddCookieCallback(base::OnceClosure callback,
net::CookieAccessResult result) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
ASSERT_TRUE(result.status.IsInclude());
std::move(callback).Run();
}
static void GetCookiesCallback(base::OnceClosure callback,
net::CookieList* out_cookie_list,
const net::CookieList& cookie_list) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
*out_cookie_list = cookie_list;
std::move(callback).Run();
}
base::Lock clear_site_data_header_lock_;
std::string clear_site_data_header_ GUARDED_BY(clear_site_data_header_lock_);
std::unique_ptr<net::EmbeddedTestServer> https_server_;
TestBrowsingDataRemoverDelegate embedder_delegate_;
};
#if BUILDFLAG(IS_WIN)
#define MAYBE_RedirectNavigation DISABLED_RedirectNavigation
#else
#define MAYBE_RedirectNavigation RedirectNavigation
#endif
IN_PROC_BROWSER_TEST_F(ClearSiteDataHandlerBrowserTest,
MAYBE_RedirectNavigation) {
std::array<GURL, 3> page_urls = {
https_server()->GetURL("origin1.com", "/"),
https_server()->GetURL("origin2.com", "/foo/bar"),
https_server()->GetURL("origin3.com", "/index.html"),
};
for (int mask = 0; mask < (1 << 3); ++mask) {
std::array<GURL, 3> urls;
for (int i = 0; i < 3; ++i) {
urls[i] = page_urls[i];
if (mask & (1 << i))
AddQuery(&urls[i], "header", kClearCookiesHeader);
if (mask & (1 << i))
delegate()->ExpectClearSiteDataCookiesCall(
storage_partition_config(), url::Origin::Create(urls[i]),
false);
}
AddQuery(&urls[1], "redirect", urls[2].spec());
AddQuery(&urls[0], "redirect", urls[1].spec());
EXPECT_TRUE(
NavigateToURL(shell(), urls[0], urls[2] ));
EXPECT_EQ(urls[2], shell()->web_contents()->GetLastCommittedURL());
delegate()->VerifyAndClearExpectations();
}
}
#if BUILDFLAG(IS_WIN)
#define MAYBE_RedirectResourceLoad DISABLED_RedirectResourceLoad
#else
#define MAYBE_RedirectResourceLoad RedirectResourceLoad
#endif
IN_PROC_BROWSER_TEST_F(ClearSiteDataHandlerBrowserTest,
MAYBE_RedirectResourceLoad) {
std::array<GURL, 3> resource_urls = {
https_server()->GetURL("origin1.com", "/redirect-start"),
https_server()->GetURL("origin2.com", "/redirect-middle"),
https_server()->GetURL("origin3.com", "/redirect-end"),
};
for (int mask = 0; mask < (1 << 3); ++mask) {
std::array<GURL, 3> urls;
GURL page_with_image = https_server()->GetURL("origin4.com", "/index.html");
for (int i = 0; i < 3; ++i) {
urls[i] = resource_urls[i];
if (mask & (1 << i))
AddQuery(&urls[i], "header", kClearCookiesHeader);
if (mask & (1 << i))
delegate()->ExpectClearSiteDataCookiesCall(
storage_partition_config(), url::Origin::Create(urls[i]),
true,
net::SchemefulSite(page_with_image));
}
AddQuery(&urls[1], "redirect", urls[2].spec());
AddQuery(&urls[0], "redirect", urls[1].spec());
std::string content_with_image =
"<html><head></head><body>"
"<img src=\"" +
urls[0].spec() +
"\" />"
"</body></html>";
AddQuery(&page_with_image, "html", content_with_image);
EXPECT_TRUE(NavigateToURL(shell(), page_with_image));
delegate()->VerifyAndClearExpectations();
}
}
IN_PROC_BROWSER_TEST_F(ClearSiteDataHandlerBrowserTest, InsecureNavigation) {
GURL url = embedded_test_server()->GetURL("example.com", "/");
AddQuery(&url, "header", kClearCookiesHeader);
ASSERT_FALSE(url.SchemeIsCryptographic());
EXPECT_TRUE(NavigateToURL(shell(), url));
delegate()->VerifyAndClearExpectations();
}
class ClearSiteDataHandlerBrowserTestWithAutoupgradesDisabled
: public ClearSiteDataHandlerBrowserTest {
public:
void SetUpCommandLine(base::CommandLine* command_line) override {
ClearSiteDataHandlerBrowserTest::SetUpCommandLine(command_line);
feature_list.InitAndDisableFeature(
blink::features::kMixedContentAutoupgrade);
}
private:
base::test::ScopedFeatureList feature_list;
};
IN_PROC_BROWSER_TEST_F(ClearSiteDataHandlerBrowserTestWithAutoupgradesDisabled,
SecureAndInsecureResourceLoad) {
GURL insecure_image =
embedded_test_server()->GetURL("example.com", "/image.png");
GURL secure_image = https_server()->GetURL("example.com", "/image.png");
ASSERT_TRUE(secure_image.SchemeIsCryptographic());
ASSERT_FALSE(insecure_image.SchemeIsCryptographic());
AddQuery(&secure_image, "header", kClearCookiesHeader);
AddQuery(&insecure_image, "header", kClearCookiesHeader);
std::string content_with_insecure_image =
"<html><head></head><body>"
"<img src=\"" +
insecure_image.spec() +
"\" />"
"</body></html>";
std::string content_with_secure_image =
"<html><head></head><body>"
"<img src=\"" +
secure_image.spec() +
"\" />"
"</body></html>";
GURL insecure_page = embedded_test_server()->GetURL("example.com", "/");
GURL secure_page = https_server()->GetURL("example.com", "/");
AddQuery(&insecure_page, "html", content_with_insecure_image);
AddQuery(&secure_page, "html", content_with_insecure_image);
EXPECT_TRUE(NavigateToURL(shell(), insecure_page));
EXPECT_TRUE(NavigateToURL(shell(), secure_page));
delegate()->VerifyAndClearExpectations();
insecure_page = embedded_test_server()->GetURL("example.com", "/");
secure_page = https_server()->GetURL("example.com", "/");
AddQuery(&insecure_page, "html", content_with_secure_image);
AddQuery(&secure_page, "html", content_with_secure_image);
delegate()->ExpectClearSiteDataCookiesCall(storage_partition_config(),
url::Origin::Create(secure_image));
EXPECT_TRUE(NavigateToURL(shell(), secure_page));
delegate()->VerifyAndClearExpectations();
delegate()->ExpectClearSiteDataCookiesCall(storage_partition_config(),
url::Origin::Create(secure_image));
EXPECT_TRUE(NavigateToURL(shell(), secure_page));
delegate()->VerifyAndClearExpectations();
}
IN_PROC_BROWSER_TEST_F(ClearSiteDataHandlerBrowserTest, ServiceWorker) {
GURL origin1 = https_server()->GetURL("origin1.com", "/");
GURL origin2 = https_server()->GetURL("origin2.com", "/");
GURL origin3 = https_server()->GetURL("origin3.com", "/");
GURL origin4 = https_server()->GetURL("origin4.com", "/");
GURL url = origin1;
AddQuery(&url, "file", "worker_setup.html");
EXPECT_TRUE(NavigateToURL(shell(), url));
WaitForTitle(shell(), "service worker is ready");
delegate()->ExpectClearSiteDataCookiesCall(
storage_partition_config(), url::Origin::Create(origin1),
false, net::SchemefulSite(url));
delegate()->ExpectClearSiteDataCookiesCall(
storage_partition_config(), url::Origin::Create(origin4),
true, net::SchemefulSite(url));
delegate()->ExpectClearSiteDataCookiesCall(
storage_partition_config(), url::Origin::Create(origin2),
true, net::SchemefulSite(url));
delegate()->ExpectClearSiteDataCookiesCall(
storage_partition_config(), url::Origin::Create(origin4),
true, net::SchemefulSite(url));
url = https_server()->GetURL("origin1.com", "/anything-in-workers-scope");
AddQuery(&url, "origin1", origin1.spec());
AddQuery(&url, "origin2", origin2.spec());
AddQuery(&url, "origin3", origin3.spec());
AddQuery(&url, "origin4", origin4.spec());
EXPECT_TRUE(NavigateToURL(shell(), url));
WaitForTitle(shell(), "done");
delegate()->VerifyAndClearExpectations();
}
#if BUILDFLAG(IS_WIN)
#define MAYBE_Credentials DISABLED_Credentials
#else
#define MAYBE_Credentials Credentials
#endif
IN_PROC_BROWSER_TEST_F(ClearSiteDataHandlerBrowserTest, MAYBE_Credentials) {
GURL page_template = https_server()->GetURL("origin1.com", "/");
GURL same_origin_resource =
https_server()->GetURL("origin1.com", "/resource");
GURL different_origin_resource =
https_server()->GetURL("origin2.com", "/resource");
AddQuery(&same_origin_resource, "header", kClearCookiesHeader);
AddQuery(&different_origin_resource, "header", kClearCookiesHeader);
const struct TestCase {
bool same_origin;
std::string credentials;
bool should_run;
bool override_partition_key_cross_site;
} kTestCases[] = {
{true, "", true, false},
{true, "omit", false, false},
{true, "same-origin", true, false},
{true, "include", true, false},
{false, "", false, false},
{false, "omit", false, false},
{false, "same-origin", false, false},
{false, "include", true, true},
};
for (const TestCase& test_case : kTestCases) {
const GURL& resource = test_case.same_origin ? same_origin_resource
: different_origin_resource;
std::string credentials =
test_case.credentials.empty()
? ""
: "credentials: '" + test_case.credentials + "'";
std::string content = base::StringPrintf(
"<html><head></head><body><script>"
"fetch('%s', {%s})"
".then(function() { document.title = 'done'; })"
".catch(function() { document.title = 'done'; })"
"</script></body></html>",
resource.spec().c_str(), credentials.c_str());
GURL page = page_template;
AddQuery(&page, "html", content);
if (test_case.should_run)
delegate()->ExpectClearSiteDataCookiesCall(
storage_partition_config(), url::Origin::Create(resource),
test_case.override_partition_key_cross_site,
net::SchemefulSite(page));
EXPECT_TRUE(NavigateToURL(shell(), page));
WaitForTitle(shell(), "done");
delegate()->VerifyAndClearExpectations();
}
}
IN_PROC_BROWSER_TEST_F(ClearSiteDataHandlerBrowserTest, CredentialsOnRedirect) {
auto urls = std::to_array<GURL, 2>({
https_server()->GetURL("origin1.com", "/image.png"),
https_server()->GetURL("origin2.com", "/image.png"),
});
AddQuery(&urls[0], "header", kClearCookiesHeader);
AddQuery(&urls[1], "header", kClearCookiesHeader);
AddQuery(&urls[0], "redirect", urls[1].spec());
std::string content = base::StringPrintf(
"<html><head></head><body><script>"
"fetch('%s', {'credentials': 'same-origin'})"
".then(function() { document.title = 'done'; })"
".catch(function() { document.title = 'done'; })"
"</script></body></html>",
urls[0].spec().c_str());
delegate()->ExpectClearSiteDataCookiesCall(storage_partition_config(),
url::Origin::Create(urls[0]));
GURL page = https_server()->GetURL("origin1.com", "/");
AddQuery(&page, "html", content);
EXPECT_TRUE(NavigateToURL(shell(), page));
WaitForTitle(shell(), "done");
delegate()->VerifyAndClearExpectations();
}
IN_PROC_BROWSER_TEST_F(ClearSiteDataHandlerBrowserTest, Types) {
GURL base_url = https_server()->GetURL("example.com", "/");
const struct TestCase {
const char* value;
bool remove_cookies;
bool remove_storage;
bool remove_cache;
} kTestCases[] = {
{"\"cookies\"", true, false, false},
{"\"storage\"", false, true, false},
{"\"cache\"", false, false, true},
{"\"cookies\", \"storage\"", true, true, false},
{"\"cookies\", \"cache\"", true, false, true},
{"\"storage\", \"cache\"", false, true, true},
{"\"cookies\", \"storage\", \"cache\"", true, true, true},
};
for (const TestCase& test_case : kTestCases) {
GURL url = base_url;
AddQuery(&url, "header", test_case.value);
delegate()->ExpectClearSiteDataCall(
storage_partition_config(), url::Origin::Create(url),
net::SchemefulSite(url), test_case.remove_cookies,
test_case.remove_storage, test_case.remove_cache);
EXPECT_TRUE(NavigateToURL(shell(), url));
delegate()->VerifyAndClearExpectations();
}
}
IN_PROC_BROWSER_TEST_F(ClearSiteDataHandlerBrowserTest,
CookiesIntegrationTest) {
AddCookie(https_server()->GetURL("origin1.com", "/abc"));
AddCookie(https_server()->GetURL("subdomain.origin1.com", "/"));
AddCookie(https_server()->GetURL("origin2.com", "/def"));
AddCookie(https_server()->GetURL("subdomain.origin2.com", "/"));
net::CookieList cookies = GetCookies();
EXPECT_EQ(4u, cookies.size());
GURL url = https_server()->GetURL("origin1.com", "/clear-site-data");
AddQuery(&url, "header", kClearCookiesHeader);
EXPECT_TRUE(NavigateToURL(shell(), url));
cookies = GetCookies();
ASSERT_EQ(2u, cookies.size());
EXPECT_EQ(cookies[0].Domain(), "origin2.com");
EXPECT_EQ(cookies[1].Domain(), "subdomain.origin2.com");
}
IN_PROC_BROWSER_TEST_F(ClearSiteDataHandlerBrowserTest,
ThirdPartyCookieBlocking) {
network::mojom::CookieManager* cookie_manager =
storage_partition()->GetCookieManagerForBrowserProcess();
cookie_manager->BlockThirdPartyCookies(false);
AddCookie(https_server()->GetURL("origin1.com", "/"));
AddCookie(https_server()->GetURL("origin1.com", "/"),
net::CookiePartitionKey::FromURLForTesting(
GURL("https://origin2.com"),
net::CookiePartitionKey::AncestorChainBit::kCrossSite));
GURL url = https_server()->GetURL("origin2.com", "/");
EXPECT_TRUE(NavigateToURL(shell(), url));
GURL csd_url = https_server()->GetURL("origin1.com", "/clear-site-data");
AddQuery(&csd_url, "header", kClearCookiesHeader);
std::string origin = url.spec();
origin.erase(origin.size() - 1);
AddQuery(&csd_url, "access-control-allow-origin", origin);
std::string script =
"fetch('" + csd_url.spec() + "', {credentials: 'include'})";
script += ".then(resp => resp.ok)";
script += ".catch(err => { console.error(err); return false; });";
EXPECT_EQ(true, EvalJs(shell()->web_contents(), script));
auto cookies = GetCookies();
ASSERT_EQ(0u, cookies.size());
cookie_manager->BlockThirdPartyCookies(true);
AddCookie(https_server()->GetURL("origin1.com", "/"));
AddCookie(https_server()->GetURL("origin1.com", "/"),
net::CookiePartitionKey::FromURLForTesting(
GURL("https://origin2.com"),
net::CookiePartitionKey::AncestorChainBit::kCrossSite));
EXPECT_EQ(true, EvalJs(shell()->web_contents(), script));
cookies = GetCookies();
ASSERT_EQ(1u, cookies.size());
EXPECT_FALSE(cookies[0].IsPartitioned());
}
IN_PROC_BROWSER_TEST_F(ClearSiteDataHandlerBrowserTest,
StorageServiceWorkersIntegrationTest) {
StoragePartition* partition = storage_partition();
net::EmbeddedTestServer* server = https_server();
browsing_data_browsertest_utils::AddServiceWorker("origin1.com", partition,
server);
browsing_data_browsertest_utils::AddServiceWorker("origin2.com", partition,
server);
std::vector<StorageUsageInfo> service_workers =
browsing_data_browsertest_utils::GetServiceWorkers(partition);
EXPECT_EQ(2u, service_workers.size());
GURL url = server->GetURL("origin1.com", "/anything-in-the-scope");
AddQuery(&url, "header", "\"storage\"");
EXPECT_TRUE(NavigateToURL(shell(), url));
service_workers =
browsing_data_browsertest_utils::GetServiceWorkers(partition);
EXPECT_EQ(2u, service_workers.size());
url = server->GetURL("origin1.com", "/resource");
AddQuery(&url, "header", "\"storage\"");
EXPECT_TRUE(NavigateToURL(shell(), url));
service_workers =
browsing_data_browsertest_utils::GetServiceWorkers(partition);
ASSERT_EQ(1u, service_workers.size());
EXPECT_EQ(service_workers[0].storage_key.origin().GetURL(),
server->GetURL("origin2.com", "/"));
}
IN_PROC_BROWSER_TEST_F(ClearSiteDataHandlerBrowserTest,
DISABLED_CacheIntegrationTest) {
GURL url1 = GetURLForHTTPSHost1("/cachetime/foo");
GURL url2 = GetURLForHTTPSHost1("/cachetime/bar");
GURL url3 = GetURLForHTTPSHost2("/cachetime/foo");
GURL url4 = GetURLForHTTPSHost2("/cachetime/bar");
CreateCacheEntry(url1);
CreateCacheEntry(url2);
CreateCacheEntry(url3);
CreateCacheEntry(url4);
EXPECT_TRUE(TestCacheEntry(url1));
EXPECT_TRUE(TestCacheEntry(url2));
EXPECT_TRUE(TestCacheEntry(url3));
EXPECT_TRUE(TestCacheEntry(url4));
GURL url = GetURLForHTTPSHost2("/clear-site-data");
AddQuery(&url, "header", "\"cache\"");
EXPECT_TRUE(NavigateToURL(shell(), url));
EXPECT_TRUE(TestCacheEntry(url1));
EXPECT_TRUE(TestCacheEntry(url2));
EXPECT_FALSE(TestCacheEntry(url3));
EXPECT_FALSE(TestCacheEntry(url4));
}
IN_PROC_BROWSER_TEST_F(ClearSiteDataHandlerBrowserTest, ClosedTab) {
GURL url = https_server()->GetURL("example.com", "/");
AddQuery(&url, "header", kClearCookiesHeader);
shell()->LoadURL(url);
shell()->Close();
}
IN_PROC_BROWSER_TEST_F(ClearSiteDataHandlerBrowserTest,
ClearSiteDataDuringServiceWorkerInstall) {
GURL url = embedded_test_server()->GetURL("127.0.0.1", "/");
AddQuery(&url, "file", "worker_test.html");
EXPECT_TRUE(NavigateToURL(shell(), url));
delegate()->ExpectClearSiteDataCall(
storage_partition_config(), url::Origin::Create(url),
net::SchemefulSite(url), false, true, false, false, SetStorageKey::kNo);
SetClearSiteDataHeader("\"storage\"");
EXPECT_FALSE(RunScriptAndGetBool("installServiceWorker()"));
delegate()->VerifyAndClearExpectations();
EXPECT_FALSE(RunScriptAndGetBool("hasServiceWorker()"));
SetClearSiteDataHeader("");
EXPECT_TRUE(RunScriptAndGetBool("installServiceWorker()"));
delegate()->VerifyAndClearExpectations();
EXPECT_TRUE(RunScriptAndGetBool("hasServiceWorker()"));
}
IN_PROC_BROWSER_TEST_F(ClearSiteDataHandlerBrowserTest,
ClearSiteDataDuringServiceWorkerUpdate) {
GURL url = embedded_test_server()->GetURL("127.0.0.1", "/");
AddQuery(&url, "file", "worker_test.html");
EXPECT_TRUE(NavigateToURL(shell(), url));
EXPECT_TRUE(RunScriptAndGetBool("installServiceWorker()"));
delegate()->VerifyAndClearExpectations();
delegate()->ExpectClearSiteDataCall(
storage_partition_config(), url::Origin::Create(url),
net::SchemefulSite(url), false, true, false, false, SetStorageKey::kNo);
base::RunLoop loop;
auto* remover = browser_context()->GetBrowsingDataRemover();
remover->SetWouldCompleteCallbackForTesting(
base::BindLambdaForTesting([&](base::OnceClosure callback) {
std::move(callback).Run();
loop.Quit();
}));
SetClearSiteDataHeader("\"storage\"");
EXPECT_FALSE(RunScriptAndGetBool("updateServiceWorker()"));
delegate()->VerifyAndClearExpectations();
loop.Run();
EXPECT_FALSE(RunScriptAndGetBool("hasServiceWorker()"));
}
enum TestScenario {
NoFeaturesActivated,
StorageBucketsActivated,
ThirdPartyStoragePartitioningActivated,
AllFeaturesActivated,
};
class ClearSiteDataHandlerStorageBucketsBrowserTest
: public ClearSiteDataHandlerBrowserTest,
public testing::WithParamInterface<TestScenario> {
public:
ClearSiteDataHandlerStorageBucketsBrowserTest() {
enum TestScenario test_scenario = GetParam();
std::vector<base::test::FeatureRef> activated_features = {};
switch (test_scenario) {
case NoFeaturesActivated:
break;
case StorageBucketsActivated:
activated_features.push_back(blink::features::kStorageBuckets);
break;
case ThirdPartyStoragePartitioningActivated:
activated_features.push_back(
net::features::kThirdPartyStoragePartitioning);
break;
case AllFeaturesActivated:
activated_features.push_back(blink::features::kStorageBuckets);
activated_features.push_back(
net::features::kThirdPartyStoragePartitioning);
break;
}
feature_list_.InitWithFeatures(activated_features, {});
}
private:
base::test::ScopedFeatureList feature_list_;
};
INSTANTIATE_TEST_SUITE_P(StorageBucketsIntegrationTestSuite,
ClearSiteDataHandlerStorageBucketsBrowserTest,
testing::Values(NoFeaturesActivated,
StorageBucketsActivated,
ThirdPartyStoragePartitioningActivated,
AllFeaturesActivated));
IN_PROC_BROWSER_TEST_P(ClearSiteDataHandlerStorageBucketsBrowserTest,
StorageBucketsIntegrationTest) {
GURL url = https_server()->GetURL("127.0.0.1", "/");
const auto storage_key =
blink::StorageKey::CreateFirstParty(url::Origin::Create(url));
StorageBucketClearSiteDataTester tester(storage_partition());
tester.CreateBucketForTesting(
storage_key, "drafts",
base::BindOnce(
[](storage::QuotaErrorOr<storage::BucketInfo> error_or_bucket_info) {
}));
tester.CreateBucketForTesting(
storage_key, "inbox",
base::BindOnce(
[](storage::QuotaErrorOr<storage::BucketInfo> error_or_bucket_info) {
}));
tester.CreateBucketForTesting(
storage_key, "attachments",
base::BindOnce(
[](storage::QuotaErrorOr<storage::BucketInfo> error_or_bucket_info) {
}));
AddQuery(&url, "header", "\"storage:drafts\", \"storage:attachments\"");
EXPECT_TRUE(NavigateToURL(shell(), url));
tester.GetBucketsForStorageKey(
storage_key,
base::BindOnce([](storage::QuotaErrorOr<std::set<storage::BucketInfo>>
error_or_buckets) {
EXPECT_EQ(base::FeatureList::IsEnabled(blink::features::kStorageBuckets)
? 1u
: 3u,
error_or_buckets.value().size());
}));
delegate()->VerifyAndClearExpectations();
}
class ClearSiteDataHandlerSharedStorageBrowserTest
: public ClearSiteDataHandlerBrowserTest {
public:
ClearSiteDataHandlerSharedStorageBrowserTest() {
feature_list_.InitAndEnableFeature(network::features::kSharedStorageAPI);
}
private:
base::test::ScopedFeatureList feature_list_;
};
IN_PROC_BROWSER_TEST_F(ClearSiteDataHandlerSharedStorageBrowserTest,
SharedStorageIntegrationTest) {
SharedStorageClearSiteDataTester tester(storage_partition());
GURL url1 = https_server()->GetURL("origin1.com", "/");
const url::Origin kOrigin1 = url::Origin::Create(url1);
tester.AddConsecutiveSharedStorageEntries(kOrigin1, u"key", u"value", 10);
GURL url2 = https_server()->GetURL("origin2.com", "/");
const url::Origin kOrigin2 = url::Origin::Create(url2);
tester.AddConsecutiveSharedStorageEntries(kOrigin2, u"key", u"value", 5);
EXPECT_THAT(tester.GetSharedStorageOrigins(),
testing::UnorderedElementsAre(kOrigin1, kOrigin2));
const int kNumBytesPerEntry = 20;
EXPECT_EQ(10 * kNumBytesPerEntry,
tester.GetSharedStorageNumBytesForOrigin(kOrigin1));
EXPECT_EQ(5 * kNumBytesPerEntry,
tester.GetSharedStorageNumBytesForOrigin(kOrigin2));
EXPECT_EQ(15 * kNumBytesPerEntry, tester.GetSharedStorageTotalBytes());
delegate()->ExpectClearSiteDataCall(storage_partition_config(), kOrigin1,
net::SchemefulSite(kOrigin1),
false,
true, false);
AddQuery(&url1, "header", "\"storage\"");
EXPECT_TRUE(NavigateToURL(shell(), url1));
delegate()->VerifyAndClearExpectations();
EXPECT_THAT(tester.GetSharedStorageOrigins(),
testing::UnorderedElementsAre(kOrigin2));
EXPECT_EQ(0, tester.GetSharedStorageNumBytesForOrigin(kOrigin1));
EXPECT_EQ(5 * kNumBytesPerEntry,
tester.GetSharedStorageNumBytesForOrigin(kOrigin2));
EXPECT_EQ(5 * kNumBytesPerEntry, tester.GetSharedStorageTotalBytes());
}
}