blob: d8aa8b8fc81fb138330f1befeae38c177e63ade8 [file] [log] [blame]
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/browsing_data/clear_site_data_handler.h"
#include <memory>
#include <optional>
#include "base/feature_list.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/ref_counted.h"
#include "base/run_loop.h"
#include "base/strings/stringprintf.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
#include "content/public/browser/storage_partition_config.h"
#include "content/public/test/browser_task_environment.h"
#include "content/public/test/test_browser_context.h"
#include "net/base/load_flags.h"
#include "net/cookies/cookie_partition_key.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/public/common/features_generated.h"
using ::testing::_;
namespace content {
using ConsoleMessagesDelegate = ClearSiteDataHandler::ConsoleMessagesDelegate;
using Message = ClearSiteDataHandler::ConsoleMessagesDelegate::Message;
namespace {
const char kClearCookiesHeader[] = "\"cookies\"";
const char kClearPrefetchCacheHeader[] = "\"prefetchCache\"";
const char kClearPrerenderCacheHeader[] = "\"prerenderCache\"";
const char kClearPrefetchAndPrerenderCacheHeader[] =
"\"prefetchCache\", \"prerenderCache\"";
const StoragePartitionConfig kTestStoragePartitionConfig;
// A slightly modified ClearSiteDataHandler for testing with dummy clearing
// functionality.
class TestHandler : public ClearSiteDataHandler {
public:
TestHandler(base::WeakPtr<BrowserContext> browser_context,
base::WeakPtr<WebContents> web_contents,
const StoragePartitionConfig& storage_partition_config,
const GURL& url,
const std::string& header_value,
int load_flags,
const std::optional<net::CookiePartitionKey> cookie_partition_key,
const std::optional<blink::StorageKey> storage_key,
bool partitioned_state_allowed_only,
base::OnceClosure callback,
std::unique_ptr<ConsoleMessagesDelegate> delegate)
: ClearSiteDataHandler(browser_context,
web_contents,
storage_partition_config,
url,
header_value,
load_flags,
cookie_partition_key,
storage_key,
partitioned_state_allowed_only,
std::move(callback),
std::move(delegate)) {}
~TestHandler() override = default;
// |HandleHeaderAndOutputConsoleMessages()| is protected and not visible in
// test cases.
bool DoHandleHeader() { return HandleHeaderAndOutputConsoleMessages(); }
MOCK_METHOD8(
ClearSiteData,
void(const StoragePartitionConfig& storage_partition_config,
const url::Origin& origin,
const ClearSiteDataTypeSet clear_site_data_types,
const std::set<std::string>& storage_buckets_to_remove,
bool avoid_closing_connections,
const std::optional<net::CookiePartitionKey> cookie_partition_key,
const std::optional<blink::StorageKey> storage_key,
bool partitioned_state_allowed_only));
protected:
void ExecuteClearingTask(
const url::Origin& origin,
const ClearSiteDataTypeSet clear_site_data_types,
const std::set<std::string>& storage_buckets_to_remove,
base::OnceClosure callback) override {
ClearSiteData(StoragePartitionConfigForTesting(), origin,
clear_site_data_types, storage_buckets_to_remove, false,
CookiePartitionKeyForTesting(), StorageKeyForTesting(),
PartitionedStateOnlyForTesting());
// NOTE: ResourceThrottle expects Resume() to be called asynchronously.
// For the purposes of this test, synchronous call works correctly, and
// is preferable for simplicity, so that we don't have to synchronize
// between triggering Clear-Site-Data and verifying test expectations.
std::move(callback).Run();
}
};
// A ConsoleDelegate that copies message to a vector |message_buffer| owned by
// the caller instead of outputs to the console.
// We need this override because otherwise messages are outputted as soon as
// request finished, and we don't have a chance to check them.
class VectorConsoleMessagesDelegate : public ConsoleMessagesDelegate {
public:
VectorConsoleMessagesDelegate(std::vector<Message>* message_buffer)
: message_buffer_(message_buffer) {}
~VectorConsoleMessagesDelegate() override = default;
void OutputMessages(base::WeakPtr<WebContents> web_contents) override {
*message_buffer_ = GetMessagesForTesting();
}
private:
raw_ptr<std::vector<Message>> message_buffer_;
};
// A ConsoleDelegate that outputs messages to a string |output_buffer| owned
// by the caller instead of to the console (losing the level information).
class StringConsoleMessagesDelegate : public ConsoleMessagesDelegate {
public:
StringConsoleMessagesDelegate(std::string* output_buffer) {
SetOutputFormattedMessageFunctionForTesting(base::BindRepeating(
&StringConsoleMessagesDelegate::OutputFormattedMessage,
base::Unretained(output_buffer)));
}
~StringConsoleMessagesDelegate() override {}
private:
static void OutputFormattedMessage(std::string* output_buffer,
WebContents* web_contents,
blink::mojom::ConsoleMessageLevel level,
const std::string& formatted_text) {
*output_buffer += formatted_text + "\n";
}
};
} // namespace
class ClearSiteDataHandlerTest : public testing::Test,
public testing::WithParamInterface<bool> {
public:
ClearSiteDataHandlerTest()
: task_environment_(BrowserTaskEnvironment::IO_MAINLOOP) {}
ClearSiteDataHandlerTest(const ClearSiteDataHandlerTest&) = delete;
ClearSiteDataHandlerTest& operator=(const ClearSiteDataHandlerTest&) = delete;
bool IsStorageBucketSupportEnabled() { return GetParam(); }
private:
BrowserTaskEnvironment task_environment_;
};
INSTANTIATE_TEST_SUITE_P(
ParseHeaderAndExecuteClearingTaskWithFeaturesEnabledTestSuite,
ClearSiteDataHandlerTest,
testing::Bool());
TEST_P(ClearSiteDataHandlerTest, ParseHeaderAndExecuteClearingTask) {
std::vector<base::test::FeatureRef> features_to_enable;
std::vector<base::test::FeatureRef> features_to_disable;
if (IsStorageBucketSupportEnabled()) {
features_to_enable.push_back(blink::features::kStorageBuckets);
} else {
features_to_disable.push_back(blink::features::kStorageBuckets);
}
base::test::ScopedFeatureList features;
features.InitWithFeatures(features_to_enable, features_to_disable);
struct TestCase {
const char* header;
bool cookies;
bool storage;
bool cache;
bool client_hints;
std::set<std::string> storage_buckets_to_remove;
};
std::set<std::string> storage_buckets_test_case_expectation = {"drafts",
"inbox"};
std::vector<TestCase> test_cases = {
// One data type.
{"\"cookies\"", true, false, false, false},
{"\"storage\"", false, true, false, false},
{"\"cache\"", false, false, true, false},
{"\"clientHints\"", false, false, false, true},
// Two data types.
{"\"cookies\", \"storage\"", true, true, false, false},
{"\"cookies\", \"cache\"", true, false, true, false},
{"\"storage\", \"cache\"", false, true, true, false},
{"\"cookies\", \"clientHints\"", true, false, false, true},
{"\"storage\", \"clientHints\"", false, true, false, true},
{"\"cache\", \"clientHints\"", false, false, true, true},
// Three data types.
{"\"cookies\", \"storage\", \"cache\"", true, true, true, false},
{"\"clientHints\", \"storage\", \"cache\"", false, true, true, true},
{"\"cookies\", \"clientHints\", \"cache\"", true, false, true, true},
{"\"cookies\", \"storage\", \"clientHints\"", true, true, false, true},
// Four data types.
{"\"cookies\", \"storage\", \"cache\", \"clientHints\"", true, true, true,
true},
// Wildcard.
{"\"*\"", true, true, true, true},
{"\"*\", \"storage\"", true, true, true, true},
{"\"cookies\", \"*\", \"storage\"", true, true, true, true},
{"\"*\", \"cookies\", \"*\"", true, true, true, true},
{"\"*\", \"clientHints\"", true, true, true, true},
// Different formatting.
{"\"cookies\"", true, false, false, false},
// Duplicates.
{"\"cookies\", \"cookies\"", true, false, false, false},
// Other JSON-formatted items in the list.
{"\"storage\", { \"other_params\": {} }", false, true, false, false},
// Unknown types are ignored, but we still proceed with the deletion for
// those that we recognize.
{"\"cache\", \"foo\"", false, false, true, false},
// Storage Buckets
{"\"storage\", \"storage:drafts\"", false, true, false, false},
{"\"*\", \"storage:drafts\", \"storage:inbox\"", true, true, true, true,
std::set<std::string>()},
{"\"cookies\", \"storage:drafts", true, false, false,
false}, // Invalid header, should end with '"'
{"\"cookies\", \"storage:invalid_name$#$\"", true, false, false,
false}, // Invalid bucket name
{"\"cookies\", \"storage:drafts\", \"storage:inbox\"", true, false, false,
false,
IsStorageBucketSupportEnabled() ? storage_buckets_test_case_expectation
: std::set<std::string>()},
};
if (!base::FeatureList::IsEnabled(blink::features::kStorageBuckets)) {
storage_buckets_test_case_expectation.clear();
}
for (const TestCase& test_case : test_cases) {
SCOPED_TRACE(test_case.header);
// Test that ParseHeader works correctly.
ClearSiteDataTypeSet clear_site_data_types;
std::set<std::string> storage_buckets_to_remove = {};
GURL url("https://example.com");
ConsoleMessagesDelegate console_delegate;
base::HistogramTester histogram_tester;
bool success = ClearSiteDataHandler::ParseHeaderForTesting(
test_case.header, &clear_site_data_types, &storage_buckets_to_remove,
&console_delegate, url);
if (!test_case.cookies && !test_case.storage && !test_case.cache &&
!test_case.client_hints &&
test_case.storage_buckets_to_remove.empty()) {
EXPECT_FALSE(success);
continue;
}
EXPECT_TRUE(success);
EXPECT_EQ(test_case.cookies,
clear_site_data_types.Has(ClearSiteDataType::kCookies));
EXPECT_EQ(test_case.storage,
clear_site_data_types.Has(ClearSiteDataType::kStorage));
EXPECT_EQ(test_case.cache,
clear_site_data_types.Has(ClearSiteDataType::kCache));
EXPECT_EQ(test_case.client_hints,
clear_site_data_types.Has(ClearSiteDataType::kClientHints));
EXPECT_EQ(test_case.storage_buckets_to_remove, storage_buckets_to_remove);
// Count the number of bits in a mask that are 1.
auto count_ones_in_mask = [](int mask) {
int count = 0;
for (size_t i = 0; i < sizeof(mask) * 8; ++i) {
count += (mask >> i) & 1;
}
return count;
};
histogram_tester.ExpectTotalCount("Storage.ClearSiteDataHeader.Parameters",
1);
int sample =
histogram_tester.GetTotalSum("Storage.ClearSiteDataHeader.Parameters");
// There should be one bit set to one for each data type seen.
EXPECT_EQ(count_ones_in_mask(sample),
static_cast<int>(test_case.cookies) +
static_cast<int>(test_case.storage) +
static_cast<int>(test_case.cache) +
static_cast<int>(!storage_buckets_to_remove.empty()) +
static_cast<int>(test_case.client_hints));
// Test that a call with the above parameters actually reaches
// ExecuteClearingTask().
TestHandler handler(
nullptr, nullptr, kTestStoragePartitionConfig, url, test_case.header,
net::LOAD_NORMAL, /*cookie_partition_key=*/std::nullopt,
/*storage_key=*/std::nullopt, /*partitioned_state_allowed_only=*/false,
base::DoNothing(), std::make_unique<ConsoleMessagesDelegate>());
EXPECT_CALL(handler, ClearSiteData(
_, url::Origin::Create(url), clear_site_data_types,
test_case.storage_buckets_to_remove, _, _, _, _));
bool defer = handler.DoHandleHeader();
EXPECT_TRUE(defer);
testing::Mock::VerifyAndClearExpectations(&handler);
}
}
TEST_F(ClearSiteDataHandlerTest, InvalidHeader) {
struct TestCase {
const char* header;
const char* console_message;
} test_cases[] = {{"", "No recognized types specified.\n"},
{"\"unclosed",
"Unrecognized type: \"unclosed.\n"
"No recognized types specified.\n"},
{"\"passwords\"",
"Unrecognized type: \"passwords\".\n"
"No recognized types specified.\n"},
// The wildcard datatype is not yet shipped.
{"[ \"*\" ]",
"Unrecognized type: [ \"*\" ].\n"
"No recognized types specified.\n"},
{"[ \"list\" ]",
"Unrecognized type: [ \"list\" ].\n"
"No recognized types specified.\n"},
{"{ \"cookies\": [ \"a\" ] }",
"Unrecognized type: { \"cookies\": [ \"a\" ] }.\n"
"No recognized types specified.\n"},
{"\"кукис\", \"сторидж\", \"кэш\"",
"Must only contain ASCII characters.\n"}};
for (const TestCase& test_case : test_cases) {
SCOPED_TRACE(test_case.header);
ClearSiteDataTypeSet clear_site_data_types;
std::set<std::string> actual_storage_buckets_to_remove;
ConsoleMessagesDelegate console_delegate;
EXPECT_FALSE(ClearSiteDataHandler::ParseHeaderForTesting(
test_case.header, &clear_site_data_types,
&actual_storage_buckets_to_remove, &console_delegate, GURL()));
std::string multiline_message;
for (const auto& message : console_delegate.GetMessagesForTesting()) {
EXPECT_EQ(blink::mojom::ConsoleMessageLevel::kWarning, message.level);
multiline_message += message.text + "\n";
}
EXPECT_EQ(test_case.console_message, multiline_message);
}
}
TEST_F(ClearSiteDataHandlerTest, ClearCookieSuccess) {
std::vector<Message> message_buffer;
TestHandler handler(
nullptr, nullptr, kTestStoragePartitionConfig,
GURL("https://example.com"), kClearCookiesHeader, net::LOAD_NORMAL,
/*cookie_partition_key=*/std::nullopt, /*storage_key=*/std::nullopt,
/*partitioned_state_allowed_only=*/false, base::DoNothing(),
std::make_unique<VectorConsoleMessagesDelegate>(&message_buffer));
EXPECT_CALL(handler, ClearSiteData(_, _, _, _, _, _, _, _));
bool defer = handler.DoHandleHeader();
EXPECT_TRUE(defer);
EXPECT_EQ(1u, message_buffer.size());
EXPECT_EQ(
"Cleared data types: \"cookies\". "
"Clearing channel IDs and HTTP authentication cache is currently "
"not supported, as it breaks active network connections.",
message_buffer.front().text);
EXPECT_EQ(message_buffer.front().level,
blink::mojom::ConsoleMessageLevel::kInfo);
testing::Mock::VerifyAndClearExpectations(&handler);
}
TEST_F(ClearSiteDataHandlerTest, LoadDoNotSaveCookies) {
std::vector<Message> message_buffer;
TestHandler handler(
nullptr, nullptr, kTestStoragePartitionConfig,
GURL("https://example.com"), kClearCookiesHeader,
net::LOAD_DO_NOT_SAVE_COOKIES, /*cookie_partition_key=*/std::nullopt,
/*storage_key=*/std::nullopt, /*partitioned_state_allowed_only=*/false,
base::DoNothing(),
std::make_unique<VectorConsoleMessagesDelegate>(&message_buffer));
EXPECT_CALL(handler, ClearSiteData(_, _, _, _, _, _, _, _)).Times(0);
bool defer = handler.DoHandleHeader();
EXPECT_FALSE(defer);
EXPECT_EQ(1u, message_buffer.size());
EXPECT_EQ(
"The request's credentials mode prohibits modifying cookies "
"and other local data.",
message_buffer.front().text);
EXPECT_EQ(blink::mojom::ConsoleMessageLevel::kError,
message_buffer.front().level);
testing::Mock::VerifyAndClearExpectations(&handler);
}
TEST_F(ClearSiteDataHandlerTest, InvalidOrigin) {
struct TestCase {
const char* origin;
bool expect_success;
std::string error_message; // Tested only if |expect_success| = false.
} kTestCases[] = {
// The handler only works on secure origins.
{"https://secure-origin.com", true, ""},
{"filesystem:https://secure-origin.com/temporary/", true, ""},
// That includes localhost.
{"http://localhost", true, ""},
// Not on insecure origins.
{"http://insecure-origin.com", false,
"Not supported for insecure origins."},
{"filesystem:http://insecure-origin.com/temporary/", false,
"Not supported for insecure origins."},
// Not on unique origins.
{"data:unique-origin;", false, "Not supported for unique origins."},
};
for (const TestCase& test_case : kTestCases) {
std::vector<Message> message_buffer;
TestHandler handler(
nullptr, nullptr, kTestStoragePartitionConfig, GURL(test_case.origin),
kClearCookiesHeader, net::LOAD_NORMAL,
/*cookie_partition_key=*/std::nullopt, /*storage_key=*/std::nullopt,
/*partitioned_state_allowed_only=*/false, base::DoNothing(),
std::make_unique<VectorConsoleMessagesDelegate>(&message_buffer));
EXPECT_CALL(handler, ClearSiteData(_, _, _, _, _, _, _, _))
.Times(test_case.expect_success ? 1 : 0);
bool defer = handler.DoHandleHeader();
EXPECT_EQ(defer, test_case.expect_success);
EXPECT_EQ(message_buffer.size(), 1u);
EXPECT_EQ(test_case.expect_success
? blink::mojom::ConsoleMessageLevel::kInfo
: blink::mojom::ConsoleMessageLevel::kError,
message_buffer.front().level);
if (!test_case.expect_success) {
EXPECT_EQ(test_case.error_message, message_buffer.front().text);
}
testing::Mock::VerifyAndClearExpectations(&handler);
}
}
// Verifies that console outputs from various actions on different URLs
// are correctly pretty-printed to the console.
TEST_F(ClearSiteDataHandlerTest, FormattedConsoleOutput) {
struct TestCase {
const char* header;
const char* url;
const char* output;
} kTestCases[] = {
// Successful deletion outputs one line, and in case of cookies, also
// a disclaimer about omitted data (https://crbug.com/798760).
{"\"cookies\"", "https://origin1.com/foo",
"Clear-Site-Data header on 'https://origin1.com/foo': "
"Cleared data types: \"cookies\". "
"Clearing channel IDs and HTTP authentication cache is currently "
"not supported, as it breaks active network connections.\n"},
// Another successful deletion.
{"\"storage\"", "https://origin2.com/foo",
"Clear-Site-Data header on 'https://origin2.com/foo': "
"Cleared data types: \"storage\".\n"},
// Redirect to the same URL. Unsuccessful deletion outputs two lines.
{"\"foo\"", "https://origin2.com/foo",
"Clear-Site-Data header on 'https://origin2.com/foo': "
"Unrecognized type: \"foo\".\n"
"Clear-Site-Data header on 'https://origin2.com/foo': "
"No recognized types specified.\n"},
// Redirect to another URL. Another unsuccessful deletion.
{"\"some text\"", "https://origin3.com/bar",
"Clear-Site-Data header on 'https://origin3.com/bar': "
"Unrecognized type: \"some text\".\n"
"Clear-Site-Data header on 'https://origin3.com/bar': "
"No recognized types specified.\n"},
// Yet another on the same URL.
{"\"passwords\"", "https://origin3.com/bar",
"Clear-Site-Data header on 'https://origin3.com/bar': "
"Unrecognized type: \"passwords\".\n"
"Clear-Site-Data header on 'https://origin3.com/bar': "
"No recognized types specified.\n"},
// Successful deletion on the same URL.
{"\"cache\"", "https://origin3.com/bar",
"Clear-Site-Data header on 'https://origin3.com/bar': "
"Cleared data types: \"cache\".\n"},
// Successful deletion for client hints.
{"\"clientHints\"", "https://origin3.com/bar",
"Clear-Site-Data header on 'https://origin3.com/bar': "
"Cleared data types: \"clientHints\".\n"},
// Successful deletion for *.
{"\"*\"", "https://origin3.com/bar",
"Clear-Site-Data header on 'https://origin3.com/bar': Cleared data "
"types: \"cookies\", \"storage\", \"cache\", \"clientHints\". Clearing "
"channel IDs and HTTP authentication cache is currently not supported, "
"as it breaks active network connections.\n"},
// Redirect to the original URL.
// Successful deletion outputs one line.
{"", "https://origin1.com/foo",
"Clear-Site-Data header on 'https://origin1.com/foo': "
"No recognized types specified.\n"},
};
// TODO(crbug.com/41409604): Delay output until next frame for navigations.
bool kHandlerTypeIsNavigation[] = {false};
for (bool navigation : kHandlerTypeIsNavigation) {
SCOPED_TRACE(navigation ? "Navigation test." : "Subresource test.");
std::string output_buffer;
std::string last_seen_console_output;
// |NetworkServiceClient| creates a new |ClearSiteDataHandler| for each
// navigation, redirect, or subresource header responses.
for (const auto& test : kTestCases) {
TestHandler handler(
nullptr, nullptr, kTestStoragePartitionConfig, GURL(test.url),
test.header, net::LOAD_NORMAL, /*cookie_partition_key=*/std::nullopt,
/*storage_key=*/std::nullopt,
/*partitioned_state_allowed_only=*/false, base::DoNothing(),
std::make_unique<StringConsoleMessagesDelegate>(&output_buffer));
handler.DoHandleHeader();
// For navigations, the console should be still empty. For subresource
// requests, messages should be added progressively.
if (navigation) {
EXPECT_TRUE(output_buffer.empty());
} else {
EXPECT_EQ(last_seen_console_output + test.output, output_buffer);
}
last_seen_console_output = output_buffer;
}
// At the end, the console must contain all messages regardless of whether
// it was a navigation or a subresource request.
std::string expected_output;
for (struct TestCase& test_case : kTestCases)
expected_output += test_case.output;
EXPECT_EQ(expected_output, output_buffer);
}
}
TEST_F(ClearSiteDataHandlerTest, CookiePartitionKey) {
std::optional<net::CookiePartitionKey> cookie_partition_keys[] = {
std::nullopt,
net::CookiePartitionKey::FromURLForTesting(GURL("https://www.foo.com")),
};
const GURL kTestURL("https://www.bar.com");
for (const auto& cookie_partition_key : cookie_partition_keys) {
std::string output_buffer;
TestHandler handler(
nullptr, nullptr, kTestStoragePartitionConfig, kTestURL, "\"cookies\"",
net::LOAD_NORMAL, cookie_partition_key, /*storage_key=*/std::nullopt,
/*partitioned_state_allowed_only=*/false, base::DoNothing(),
std::make_unique<StringConsoleMessagesDelegate>(&output_buffer));
EXPECT_CALL(handler,
ClearSiteData(_, _, _, _, _, cookie_partition_key, _, _));
EXPECT_TRUE(handler.DoHandleHeader());
}
}
TEST_F(ClearSiteDataHandlerTest, StorageKey) {
std::optional<blink::StorageKey> storage_keys[] = {
std::nullopt,
blink::StorageKey::CreateFromStringForTesting("https://example.com")};
const GURL kTestURL("https://example.com");
for (const auto& storage_key : storage_keys) {
std::string output_buffer;
TestHandler handler(
nullptr, nullptr, kTestStoragePartitionConfig, kTestURL, "\"storage\"",
net::LOAD_NORMAL, /*cookie_partition_key=*/std::nullopt, storage_key,
/*partitioned_state_allowed_only=*/false, base::DoNothing(),
std::make_unique<StringConsoleMessagesDelegate>(&output_buffer));
EXPECT_CALL(handler, ClearSiteData(_, _, _, _, _, _, storage_key, _));
EXPECT_TRUE(handler.DoHandleHeader());
}
}
TEST_F(ClearSiteDataHandlerTest, ThirdPartyCookieBlockingEnabled) {
bool test_cases[] = {true, false};
const GURL kTestURL("https://example.com");
for (const auto partitioned_state_allowed_only : test_cases) {
SCOPED_TRACE(base::StringPrintf("partitioned_state_allowed_only: %d",
partitioned_state_allowed_only));
std::string output_buffer;
TestHandler handler(
nullptr, nullptr, kTestStoragePartitionConfig, kTestURL, "\"storage\"",
net::LOAD_NORMAL, /*cookie_partition_key=*/std::nullopt,
/*storage_key=*/std::nullopt, partitioned_state_allowed_only,
base::DoNothing(),
std::make_unique<StringConsoleMessagesDelegate>(&output_buffer));
EXPECT_CALL(handler, ClearSiteData(_, _, _, _, _, _, _,
partitioned_state_allowed_only));
EXPECT_TRUE(handler.DoHandleHeader());
}
}
TEST_F(ClearSiteDataHandlerTest, CorrectStoragePartition) {
const GURL kTestURL("https://example.com");
TestBrowserContext test_browser_context;
test_browser_context.set_is_off_the_record(false);
std::vector<StoragePartitionConfig> test_cases{
StoragePartitionConfig::Create(&test_browser_context, "test_domain_0",
"test name 1", true),
StoragePartitionConfig::Create(&test_browser_context, "test_domain_1",
"test name 2", false),
StoragePartitionConfig::Create(&test_browser_context, "test_domain_1",
"test name 3", false),
StoragePartitionConfig::Create(&test_browser_context, "test_domain_1", "",
false),
};
for (const StoragePartitionConfig& storage_partition_config : test_cases) {
std::string output_buffer;
TestHandler handler(
nullptr, nullptr, storage_partition_config, kTestURL, "\"storage\"",
net::LOAD_NORMAL, /*cookie_partition_key=*/std::nullopt,
/*storage_key=*/std::nullopt, /*partitioned_state_allowed_only=*/false,
base::DoNothing(),
std::make_unique<StringConsoleMessagesDelegate>(&output_buffer));
EXPECT_CALL(handler,
ClearSiteData(storage_partition_config, _, _, _, _, _, _, _));
EXPECT_TRUE(handler.DoHandleHeader());
}
}
TEST_F(ClearSiteDataHandlerTest, ClearPrefetchCacheSuccess) {
base::test::ScopedFeatureList features;
features.InitAndEnableFeature(
blink::features::kClearSiteDataPrefetchPrerenderCache);
std::vector<Message> message_buffer;
TestHandler handler(
nullptr, nullptr, kTestStoragePartitionConfig,
GURL("https://example.com"), kClearPrefetchCacheHeader, net::LOAD_NORMAL,
/*cookie_partition_key=*/std::nullopt, /*storage_key=*/std::nullopt,
/*partitioned_state_allowed_only=*/false, base::DoNothing(),
std::make_unique<VectorConsoleMessagesDelegate>(&message_buffer));
EXPECT_CALL(handler, ClearSiteData(_, _, _, _, _, _, _, _));
bool defer = handler.DoHandleHeader();
EXPECT_TRUE(defer);
EXPECT_EQ(1u, message_buffer.size());
EXPECT_EQ("Cleared data types: \"prefetchCache\".",
message_buffer.front().text);
EXPECT_EQ(message_buffer.front().level,
blink::mojom::ConsoleMessageLevel::kInfo);
testing::Mock::VerifyAndClearExpectations(&handler);
}
TEST_F(ClearSiteDataHandlerTest, ClearPrerenderCacheSuccess) {
base::test::ScopedFeatureList features;
features.InitAndEnableFeature(
blink::features::kClearSiteDataPrefetchPrerenderCache);
std::vector<Message> message_buffer;
TestHandler handler(
nullptr, nullptr, kTestStoragePartitionConfig,
GURL("https://example.com"), kClearPrerenderCacheHeader, net::LOAD_NORMAL,
/*cookie_partition_key=*/std::nullopt, /*storage_key=*/std::nullopt,
/*partitioned_state_allowed_only=*/false, base::DoNothing(),
std::make_unique<VectorConsoleMessagesDelegate>(&message_buffer));
EXPECT_CALL(handler, ClearSiteData(_, _, _, _, _, _, _, _));
bool defer = handler.DoHandleHeader();
EXPECT_TRUE(defer);
EXPECT_EQ(1u, message_buffer.size());
EXPECT_EQ("Cleared data types: \"prerenderCache\".",
message_buffer.front().text);
EXPECT_EQ(message_buffer.front().level,
blink::mojom::ConsoleMessageLevel::kInfo);
testing::Mock::VerifyAndClearExpectations(&handler);
}
TEST_F(ClearSiteDataHandlerTest, ClearPrefetchAndPrerenderCacheSuccess) {
base::test::ScopedFeatureList features;
features.InitAndEnableFeature(
blink::features::kClearSiteDataPrefetchPrerenderCache);
std::vector<Message> message_buffer;
TestHandler handler(
nullptr, nullptr, kTestStoragePartitionConfig,
GURL("https://example.com"), kClearPrefetchAndPrerenderCacheHeader,
net::LOAD_NORMAL,
/*cookie_partition_key=*/std::nullopt, /*storage_key=*/std::nullopt,
/*partitioned_state_allowed_only=*/false, base::DoNothing(),
std::make_unique<VectorConsoleMessagesDelegate>(&message_buffer));
EXPECT_CALL(handler, ClearSiteData(_, _, _, _, _, _, _, _));
bool defer = handler.DoHandleHeader();
EXPECT_TRUE(defer);
EXPECT_EQ(1u, message_buffer.size());
EXPECT_EQ("Cleared data types: \"prefetchCache\", \"prerenderCache\".",
message_buffer.front().text);
EXPECT_EQ(message_buffer.front().level,
blink::mojom::ConsoleMessageLevel::kInfo);
testing::Mock::VerifyAndClearExpectations(&handler);
}
} // namespace content