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 |