| Index: content/browser/browsing_data/clear_site_data_throttle_unittest.cc
|
| diff --git a/content/browser/browsing_data/clear_site_data_throttle_unittest.cc b/content/browser/browsing_data/clear_site_data_throttle_unittest.cc
|
| index bdfa8fad3d519996800691f6526b3e3ad320f0f1..7d76da46e1088f5c0f5c16ae69039f3c1a976f3c 100644
|
| --- a/content/browser/browsing_data/clear_site_data_throttle_unittest.cc
|
| +++ b/content/browser/browsing_data/clear_site_data_throttle_unittest.cc
|
| @@ -6,30 +6,167 @@
|
|
|
| #include <memory>
|
|
|
| -#include "base/command_line.h"
|
| -#include "content/public/common/content_switches.h"
|
| +#include "base/memory/ptr_util.h"
|
| +#include "base/memory/ref_counted.h"
|
| +#include "base/run_loop.h"
|
| +#include "base/strings/stringprintf.h"
|
| +#include "base/test/scoped_command_line.h"
|
| +#include "base/test/scoped_task_environment.h"
|
| +#include "content/public/browser/resource_request_info.h"
|
| +#include "content/public/test/test_browser_thread.h"
|
| +#include "content/public/test/test_browser_thread_bundle.h"
|
| +#include "net/base/load_flags.h"
|
| +#include "net/http/http_util.h"
|
| +#include "net/url_request/redirect_info.h"
|
| +#include "net/url_request/url_request_job.h"
|
| +#include "net/url_request/url_request_test_util.h"
|
| #include "testing/gmock/include/gmock/gmock.h"
|
| #include "testing/gtest/include/gtest/gtest.h"
|
|
|
| +using ::testing::_;
|
| +
|
| namespace content {
|
|
|
| -class ClearSiteDataThrottleTest : public testing::Test {
|
| +using ConsoleMessagesDelegate = ClearSiteDataThrottle::ConsoleMessagesDelegate;
|
| +
|
| +namespace {
|
| +
|
| +const char kClearSiteDataHeaderPrefix[] = "Clear-Site-Data: ";
|
| +
|
| +const char kClearCookiesHeader[] =
|
| + "Clear-Site-Data: { \"types\": [ \"cookies\" ] }";
|
| +
|
| +void WaitForUIThread() {
|
| + base::RunLoop run_loop;
|
| + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, run_loop.QuitClosure());
|
| + run_loop.Run();
|
| +}
|
| +
|
| +// Used to verify that resource throttle delegate calls are made.
|
| +class MockResourceThrottleDelegate : public ResourceThrottle::Delegate {
|
| public:
|
| - void SetUp() override {
|
| - base::CommandLine::ForCurrentProcess()->AppendSwitch(
|
| - switches::kEnableExperimentalWebPlatformFeatures);
|
| - throttle_ = ClearSiteDataThrottle::CreateThrottleForNavigation(nullptr);
|
| + MOCK_METHOD0(Cancel, void());
|
| + MOCK_METHOD0(CancelAndIgnore, void());
|
| + MOCK_METHOD1(CancelWithError, void(int));
|
| + MOCK_METHOD0(Resume, void());
|
| +};
|
| +
|
| +// A slightly modified ClearSiteDataThrottle for testing with unconditional
|
| +// construction, injectable response headers, and dummy clearing functionality.
|
| +class TestThrottle : public ClearSiteDataThrottle {
|
| + public:
|
| + TestThrottle(net::URLRequest* request,
|
| + std::unique_ptr<ConsoleMessagesDelegate> delegate)
|
| + : ClearSiteDataThrottle(request, std::move(delegate)) {}
|
| + ~TestThrottle() override {}
|
| +
|
| + void SetResponseHeaders(const std::string& headers) {
|
| + std::string headers_with_status_code = "HTTP/1.1 200\n" + headers;
|
| + headers_ = new net::HttpResponseHeaders(net::HttpUtil::AssembleRawHeaders(
|
| + headers_with_status_code.c_str(), headers_with_status_code.size()));
|
| }
|
|
|
| - ClearSiteDataThrottle* GetThrottle() {
|
| - return static_cast<ClearSiteDataThrottle*>(throttle_.get());
|
| + MOCK_METHOD4(ClearSiteData,
|
| + void(const url::Origin& origin,
|
| + bool clear_cookies,
|
| + bool clear_storage,
|
| + bool clear_cache));
|
| +
|
| + protected:
|
| + const net::HttpResponseHeaders* GetResponseHeaders() const override {
|
| + return headers_.get();
|
| + }
|
| +
|
| + void ExecuteClearingTask(const url::Origin& origin,
|
| + bool clear_cookies,
|
| + bool clear_storage,
|
| + bool clear_cache,
|
| + base::OnceClosure callback) override {
|
| + ClearSiteData(origin, clear_cookies, clear_storage, clear_cache);
|
| +
|
| + // 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();
|
| }
|
|
|
| private:
|
| - std::unique_ptr<NavigationThrottle> throttle_;
|
| + scoped_refptr<net::HttpResponseHeaders> headers_;
|
| };
|
|
|
| -TEST_F(ClearSiteDataThrottleTest, ParseHeader) {
|
| +// A TestThrottle with modifiable current url.
|
| +class RedirectableTestThrottle : public TestThrottle {
|
| + public:
|
| + RedirectableTestThrottle(net::URLRequest* request,
|
| + std::unique_ptr<ConsoleMessagesDelegate> delegate)
|
| + : TestThrottle(request, std::move(delegate)) {}
|
| +
|
| + const GURL& GetCurrentURL() const override {
|
| + return current_url_.is_valid() ? current_url_
|
| + : TestThrottle::GetCurrentURL();
|
| + }
|
| +
|
| + void SetCurrentURLForTesting(const GURL& url) { current_url_ = url; }
|
| +
|
| + private:
|
| + GURL current_url_;
|
| +};
|
| +
|
| +// 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::Bind(&StringConsoleMessagesDelegate::OutputFormattedMessage,
|
| + base::Unretained(output_buffer)));
|
| + }
|
| +
|
| + ~StringConsoleMessagesDelegate() override {}
|
| +
|
| + private:
|
| + static void OutputFormattedMessage(std::string* output_buffer,
|
| + WebContents* web_contents,
|
| + ConsoleMessageLevel level,
|
| + const std::string& formatted_text) {
|
| + *output_buffer += formatted_text + "\n";
|
| + }
|
| +};
|
| +
|
| +} // namespace
|
| +
|
| +class ClearSiteDataThrottleTest : public testing::Test {
|
| + public:
|
| + ClearSiteDataThrottleTest()
|
| + : thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP) {}
|
| +
|
| + private:
|
| + TestBrowserThreadBundle thread_bundle_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(ClearSiteDataThrottleTest);
|
| +};
|
| +
|
| +TEST_F(ClearSiteDataThrottleTest, MaybeCreateThrottleForRequest) {
|
| + // Create a URL request.
|
| + GURL url("https://www.example.com");
|
| + net::TestURLRequestContext context;
|
| + std::unique_ptr<net::URLRequest> request(
|
| + context.CreateRequest(url, net::DEFAULT_PRIORITY, nullptr));
|
| +
|
| + // We will not create the throttle for an empty ResourceRequestInfo.
|
| + EXPECT_FALSE(
|
| + ClearSiteDataThrottle::MaybeCreateThrottleForRequest(request.get()));
|
| +
|
| + // We can create the throttle for a valid ResourceRequestInfo.
|
| + ResourceRequestInfo::AllocateForTesting(request.get(), RESOURCE_TYPE_IMAGE,
|
| + nullptr, 0, 0, 0, false, true, true,
|
| + true, false);
|
| + EXPECT_TRUE(
|
| + ClearSiteDataThrottle::MaybeCreateThrottleForRequest(request.get()));
|
| +}
|
| +
|
| +TEST_F(ClearSiteDataThrottleTest, ParseHeaderAndExecuteClearingTask) {
|
| struct TestCase {
|
| const char* header;
|
| bool cookies;
|
| @@ -72,19 +209,41 @@ TEST_F(ClearSiteDataThrottleTest, ParseHeader) {
|
| for (const TestCase& test_case : test_cases) {
|
| SCOPED_TRACE(test_case.header);
|
|
|
| + // Test that ParseHeader works correctly.
|
| bool actual_cookies;
|
| bool actual_storage;
|
| bool actual_cache;
|
|
|
| - std::vector<ClearSiteDataThrottle::ConsoleMessage> messages;
|
| + GURL url("https://example.com");
|
| + ConsoleMessagesDelegate console_delegate;
|
|
|
| - EXPECT_TRUE(GetThrottle()->ParseHeader(test_case.header, &actual_cookies,
|
| - &actual_storage, &actual_cache,
|
| - &messages));
|
| + EXPECT_TRUE(ClearSiteDataThrottle::ParseHeaderForTesting(
|
| + test_case.header, &actual_cookies, &actual_storage, &actual_cache,
|
| + &console_delegate, url));
|
|
|
| EXPECT_EQ(test_case.cookies, actual_cookies);
|
| EXPECT_EQ(test_case.storage, actual_storage);
|
| EXPECT_EQ(test_case.cache, actual_cache);
|
| +
|
| + // Test that a call with the above parameters actually reaches
|
| + // ExecuteClearingTask().
|
| + net::TestURLRequestContext context;
|
| + std::unique_ptr<net::URLRequest> request(
|
| + context.CreateRequest(url, net::DEFAULT_PRIORITY, nullptr));
|
| + TestThrottle throttle(request.get(),
|
| + base::MakeUnique<ConsoleMessagesDelegate>());
|
| + MockResourceThrottleDelegate delegate;
|
| + throttle.set_delegate_for_testing(&delegate);
|
| + throttle.SetResponseHeaders(std::string(kClearSiteDataHeaderPrefix) +
|
| + test_case.header);
|
| +
|
| + EXPECT_CALL(throttle, ClearSiteData(url::Origin(url), test_case.cookies,
|
| + test_case.storage, test_case.cache));
|
| + bool defer;
|
| + throttle.WillProcessResponse(&defer);
|
| + EXPECT_TRUE(defer);
|
| +
|
| + testing::Mock::VerifyAndClearExpectations(&throttle);
|
| }
|
| }
|
|
|
| @@ -93,17 +252,17 @@ TEST_F(ClearSiteDataThrottleTest, InvalidHeader) {
|
| const char* header;
|
| const char* console_message;
|
| } test_cases[] = {
|
| - {"", "Not a valid JSON.\n"},
|
| - {"\"unclosed quote", "Not a valid JSON.\n"},
|
| - {"\"some text\"", "Expecting a JSON dictionary with a 'types' field.\n"},
|
| + {"", "Expected valid JSON.\n"},
|
| + {"\"unclosed quote", "Expected valid JSON.\n"},
|
| + {"\"some text\"", "Expected a JSON dictionary with a 'types' field.\n"},
|
| {"{ \"field\" : {} }",
|
| - "Expecting a JSON dictionary with a 'types' field.\n"},
|
| + "Expected a JSON dictionary with a 'types' field.\n"},
|
| {"{ \"types\" : [ \"passwords\" ] }",
|
| - "Invalid type: \"passwords\".\n"
|
| - "No valid types specified in the 'types' field.\n"},
|
| + "Unrecognized type: \"passwords\".\n"
|
| + "No recognized types specified in the 'types' field.\n"},
|
| {"{ \"types\" : [ [ \"list in a list\" ] ] }",
|
| - "Invalid type: [\"list in a list\"].\n"
|
| - "No valid types specified in the 'types' field.\n"},
|
| + "Unrecognized type: [\"list in a list\"].\n"
|
| + "No recognized types specified in the 'types' field.\n"},
|
| {"{ \"types\" : [ \"кукис\", \"сторидж\", \"кэш\" ]",
|
| "Must only contain ASCII characters.\n"}};
|
|
|
| @@ -114,14 +273,14 @@ TEST_F(ClearSiteDataThrottleTest, InvalidHeader) {
|
| bool actual_storage;
|
| bool actual_cache;
|
|
|
| - std::vector<ClearSiteDataThrottle::ConsoleMessage> messages;
|
| + ConsoleMessagesDelegate console_delegate;
|
|
|
| - EXPECT_FALSE(GetThrottle()->ParseHeader(test_case.header, &actual_cookies,
|
| - &actual_storage, &actual_cache,
|
| - &messages));
|
| + EXPECT_FALSE(ClearSiteDataThrottle::ParseHeaderForTesting(
|
| + test_case.header, &actual_cookies, &actual_storage, &actual_cache,
|
| + &console_delegate, GURL()));
|
|
|
| std::string multiline_message;
|
| - for (const auto& message : messages) {
|
| + for (const auto& message : console_delegate.messages()) {
|
| EXPECT_EQ(CONSOLE_MESSAGE_LEVEL_ERROR, message.level);
|
| multiline_message += message.text + "\n";
|
| }
|
| @@ -130,4 +289,340 @@ TEST_F(ClearSiteDataThrottleTest, InvalidHeader) {
|
| }
|
| }
|
|
|
| +TEST_F(ClearSiteDataThrottleTest, LoadDoNotSaveCookies) {
|
| + net::TestURLRequestContext context;
|
| + std::unique_ptr<net::URLRequest> request(context.CreateRequest(
|
| + GURL("https://www.example.com"), net::DEFAULT_PRIORITY, nullptr));
|
| + std::unique_ptr<ConsoleMessagesDelegate> scoped_console_delegate(
|
| + new ConsoleMessagesDelegate());
|
| + const ConsoleMessagesDelegate* console_delegate =
|
| + scoped_console_delegate.get();
|
| + TestThrottle throttle(request.get(), std::move(scoped_console_delegate));
|
| + MockResourceThrottleDelegate delegate;
|
| + throttle.set_delegate_for_testing(&delegate);
|
| + throttle.SetResponseHeaders(kClearCookiesHeader);
|
| +
|
| + EXPECT_CALL(throttle, ClearSiteData(_, _, _, _));
|
| + bool defer;
|
| + throttle.WillProcessResponse(&defer);
|
| + EXPECT_TRUE(defer);
|
| + EXPECT_EQ(1u, console_delegate->messages().size());
|
| + EXPECT_EQ("Cleared data types: cookies.",
|
| + console_delegate->messages().front().text);
|
| + EXPECT_EQ(console_delegate->messages().front().level,
|
| + CONSOLE_MESSAGE_LEVEL_INFO);
|
| + testing::Mock::VerifyAndClearExpectations(&throttle);
|
| +
|
| + request->SetLoadFlags(net::LOAD_DO_NOT_SAVE_COOKIES);
|
| + EXPECT_CALL(throttle, ClearSiteData(_, _, _, _)).Times(0);
|
| + throttle.WillProcessResponse(&defer);
|
| + EXPECT_FALSE(defer);
|
| + EXPECT_EQ(2u, console_delegate->messages().size());
|
| + EXPECT_EQ(
|
| + "The request's credentials mode prohibits modifying cookies "
|
| + "and other local data.",
|
| + console_delegate->messages().rbegin()->text);
|
| + EXPECT_EQ(CONSOLE_MESSAGE_LEVEL_ERROR,
|
| + console_delegate->messages().rbegin()->level);
|
| + testing::Mock::VerifyAndClearExpectations(&throttle);
|
| +}
|
| +
|
| +TEST_F(ClearSiteDataThrottleTest, InvalidOrigin) {
|
| + struct TestCase {
|
| + const char* origin;
|
| + bool expect_success;
|
| + std::string error_message; // Tested only if |expect_success| = false.
|
| + } kTestCases[] = {
|
| + // The throttle 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."},
|
| + };
|
| +
|
| + net::TestURLRequestContext context;
|
| +
|
| + for (const TestCase& test_case : kTestCases) {
|
| + std::unique_ptr<net::URLRequest> request(context.CreateRequest(
|
| + GURL(test_case.origin), net::DEFAULT_PRIORITY, nullptr));
|
| + std::unique_ptr<ConsoleMessagesDelegate> scoped_console_delegate(
|
| + new ConsoleMessagesDelegate());
|
| + const ConsoleMessagesDelegate* console_delegate =
|
| + scoped_console_delegate.get();
|
| + TestThrottle throttle(request.get(), std::move(scoped_console_delegate));
|
| + MockResourceThrottleDelegate delegate;
|
| + throttle.set_delegate_for_testing(&delegate);
|
| + throttle.SetResponseHeaders(kClearCookiesHeader);
|
| +
|
| + EXPECT_CALL(throttle, ClearSiteData(_, _, _, _))
|
| + .Times(test_case.expect_success ? 1 : 0);
|
| +
|
| + bool defer;
|
| + throttle.WillProcessResponse(&defer);
|
| +
|
| + EXPECT_EQ(defer, test_case.expect_success);
|
| + EXPECT_EQ(console_delegate->messages().size(), 1u);
|
| + EXPECT_EQ(test_case.expect_success ? CONSOLE_MESSAGE_LEVEL_INFO
|
| + : CONSOLE_MESSAGE_LEVEL_ERROR,
|
| + console_delegate->messages().front().level);
|
| + if (!test_case.expect_success) {
|
| + EXPECT_EQ(test_case.error_message,
|
| + console_delegate->messages().front().text);
|
| + }
|
| + testing::Mock::VerifyAndClearExpectations(&throttle);
|
| + }
|
| +}
|
| +
|
| +TEST_F(ClearSiteDataThrottleTest, DeferAndResume) {
|
| + enum Stage { START, REDIRECT, RESPONSE };
|
| +
|
| + struct TestCase {
|
| + Stage stage;
|
| + std::string response_headers;
|
| + bool should_defer;
|
| + } kTestCases[] = {
|
| + // The throttle never interferes while the request is starting. Response
|
| + // headers are ignored, because URLRequest is not supposed to have any
|
| + // at this stage in the first place.
|
| + {START, "", false},
|
| + {START, kClearCookiesHeader, false},
|
| +
|
| + // The throttle does not defer redirects if there are no interesting
|
| + // response headers.
|
| + {REDIRECT, "", false},
|
| + {REDIRECT, "Set-Cookie: abc=123;", false},
|
| + {REDIRECT, "Content-Type: image/png;", false},
|
| +
|
| + // That includes malformed Clear-Site-Data headers or header values
|
| + // that do not lead to deletion.
|
| + {REDIRECT, "Clear-Site-Data: { types: cookies } ", false},
|
| + {REDIRECT, "Clear-Site-Data: { \"types\": [ \"unknown type\" ] }", false},
|
| +
|
| + // However, redirects are deferred for valid Clear-Site-Data headers.
|
| + {REDIRECT,
|
| + "Clear-Site-Data: { \"types\": [ \"cookies\", \"unknown type\" ] }",
|
| + true},
|
| + {REDIRECT,
|
| + base::StringPrintf("Content-Type: image/png;\n%s", kClearCookiesHeader),
|
| + true},
|
| + {REDIRECT,
|
| + base::StringPrintf("%s\nContent-Type: image/png;", kClearCookiesHeader),
|
| + true},
|
| +
|
| + // We expect at most one instance of the header. Multiple instances
|
| + // will not be parsed currently. This is not an inherent property of
|
| + // Clear-Site-Data, just a documentation of the current behavior.
|
| + {REDIRECT,
|
| + base::StringPrintf("%s\n%s", kClearCookiesHeader, kClearCookiesHeader),
|
| + false},
|
| +
|
| + // Final response headers are treated the same way as in the case
|
| + // of redirect.
|
| + {REDIRECT, "Set-Cookie: abc=123;", false},
|
| + {REDIRECT, "Clear-Site-Data: { types: cookies } ", false},
|
| + {REDIRECT, kClearCookiesHeader, true},
|
| + };
|
| +
|
| + struct TestOrigin {
|
| + const char* origin;
|
| + bool valid;
|
| + } kTestOrigins[] = {
|
| + // The throttle 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},
|
| + {"filesystem:http://insecure-origin.com/temporary/", false},
|
| +
|
| + // Not on unique origins.
|
| + {"data:unique-origin;", false},
|
| + };
|
| +
|
| + net::TestURLRequestContext context;
|
| +
|
| + for (const TestOrigin& test_origin : kTestOrigins) {
|
| + for (const TestCase& test_case : kTestCases) {
|
| + SCOPED_TRACE(base::StringPrintf("Origin=%s\nStage=%d\nHeaders:\n%s",
|
| + test_origin.origin, test_case.stage,
|
| + test_case.response_headers.c_str()));
|
| +
|
| + std::unique_ptr<net::URLRequest> request(context.CreateRequest(
|
| + GURL(test_origin.origin), net::DEFAULT_PRIORITY, nullptr));
|
| + TestThrottle throttle(request.get(),
|
| + base::MakeUnique<ConsoleMessagesDelegate>());
|
| + throttle.SetResponseHeaders(test_case.response_headers);
|
| +
|
| + MockResourceThrottleDelegate delegate;
|
| + throttle.set_delegate_for_testing(&delegate);
|
| +
|
| + // Whether we should defer is always conditional on the origin
|
| + // being valid.
|
| + bool expected_defer = test_case.should_defer && test_origin.valid;
|
| +
|
| + // If we expect loading to be deferred, then we also expect data to be
|
| + // cleared and the load to eventually resume.
|
| + if (expected_defer) {
|
| + testing::Expectation expectation = EXPECT_CALL(
|
| + throttle,
|
| + ClearSiteData(url::Origin(GURL(test_origin.origin)), _, _, _));
|
| + EXPECT_CALL(delegate, Resume()).After(expectation);
|
| + } else {
|
| + EXPECT_CALL(throttle, ClearSiteData(_, _, _, _)).Times(0);
|
| + EXPECT_CALL(delegate, Resume()).Times(0);
|
| + }
|
| +
|
| + bool actual_defer = false;
|
| +
|
| + switch (test_case.stage) {
|
| + case START: {
|
| + throttle.WillStartRequest(&actual_defer);
|
| + break;
|
| + }
|
| + case REDIRECT: {
|
| + net::RedirectInfo redirect_info;
|
| + throttle.WillRedirectRequest(redirect_info, &actual_defer);
|
| + break;
|
| + }
|
| + case RESPONSE: {
|
| + throttle.WillProcessResponse(&actual_defer);
|
| + break;
|
| + }
|
| + }
|
| +
|
| + EXPECT_EQ(expected_defer, actual_defer);
|
| + testing::Mock::VerifyAndClearExpectations(&delegate);
|
| + }
|
| + }
|
| +}
|
| +
|
| +// Verifies that console outputs from various actions on different URLs
|
| +// are correctly pretty-printed to the console.
|
| +TEST_F(ClearSiteDataThrottleTest, FormattedConsoleOutput) {
|
| + struct TestCase {
|
| + const char* header;
|
| + const char* url;
|
| + const char* output;
|
| + } kTestCases[] = {
|
| + // Successful deletion outputs one line.
|
| + {"{ \"types\": [ \"cookies\" ] }", "https://origin1.com/foo",
|
| + "Clear-Site-Data header on 'https://origin1.com/foo': "
|
| + "Cleared data types: cookies.\n"},
|
| +
|
| + // Another successful deletion.
|
| + {"{ \"types\": [ \"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\": \"bar\" }", "https://origin2.com/foo",
|
| + "Clear-Site-Data header on 'https://origin2.com/foo': "
|
| + "Expected a JSON dictionary with a 'types' field.\n"},
|
| +
|
| + // Redirect to another URL. Another unsuccessful deletion.
|
| + {"\"some text\"", "https://origin3.com/bar",
|
| + "Clear-Site-Data header on 'https://origin3.com/bar': "
|
| + "Expected a JSON dictionary with a 'types' field.\n"},
|
| +
|
| + // Yet another on the same URL.
|
| + {"{ \"types\" : [ \"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 in the 'types' field.\n"},
|
| +
|
| + // Successful deletion on the same URL.
|
| + {"{ \"types\": [ \"cache\" ] }", "https://origin3.com/bar",
|
| + "Clear-Site-Data header on 'https://origin3.com/bar': "
|
| + "Cleared data types: cache.\n"},
|
| +
|
| + // Redirect to the original URL.
|
| + // Successful deletion outputs one line.
|
| + {"", "https://origin1.com/foo",
|
| + "Clear-Site-Data header on 'https://origin1.com/foo': "
|
| + "Expected valid JSON.\n"}};
|
| +
|
| + bool kThrottleTypeIsNavigation[] = {true, false};
|
| +
|
| + for (bool navigation : kThrottleTypeIsNavigation) {
|
| + SCOPED_TRACE(navigation ? "Navigation test." : "Subresource test.");
|
| +
|
| + net::TestURLRequestContext context;
|
| + std::unique_ptr<net::URLRequest> request(context.CreateRequest(
|
| + GURL(kTestCases[0].url), net::DEFAULT_PRIORITY, nullptr));
|
| + ResourceRequestInfo::AllocateForTesting(
|
| + request.get(),
|
| + navigation ? RESOURCE_TYPE_SUB_FRAME : RESOURCE_TYPE_IMAGE, nullptr, 0,
|
| + 0, 0, false, true, true, true, false);
|
| +
|
| + std::string output_buffer;
|
| + std::unique_ptr<RedirectableTestThrottle> throttle =
|
| + base::MakeUnique<RedirectableTestThrottle>(
|
| + request.get(),
|
| + base::MakeUnique<StringConsoleMessagesDelegate>(&output_buffer));
|
| +
|
| + MockResourceThrottleDelegate delegate;
|
| + throttle->set_delegate_for_testing(&delegate);
|
| +
|
| + std::string last_seen_console_output;
|
| +
|
| + // Simulate redirecting the throttle through the above origins with the
|
| + // corresponding response headers.
|
| + bool defer;
|
| + throttle->WillStartRequest(&defer);
|
| +
|
| + for (size_t i = 0; i < arraysize(kTestCases); i++) {
|
| + throttle->SetResponseHeaders(std::string(kClearSiteDataHeaderPrefix) +
|
| + kTestCases[i].header);
|
| +
|
| + // TODO(msramek): There is probably a better way to do this inside
|
| + // URLRequest.
|
| + throttle->SetCurrentURLForTesting(GURL(kTestCases[i].url));
|
| +
|
| + net::RedirectInfo redirect_info;
|
| + if (i < arraysize(kTestCases) - 1)
|
| + throttle->WillRedirectRequest(redirect_info, &defer);
|
| + else
|
| + throttle->WillProcessResponse(&defer);
|
| +
|
| + // Wait for any messages to be output.
|
| + WaitForUIThread();
|
| +
|
| + // 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 + kTestCases[i].output,
|
| + output_buffer);
|
| + }
|
| +
|
| + last_seen_console_output = output_buffer;
|
| + }
|
| +
|
| + throttle.reset();
|
| + WaitForUIThread();
|
| +
|
| + // 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);
|
| + }
|
| +}
|
| +
|
| } // namespace content
|
|
|