Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1041)

Unified Diff: content/browser/browsing_data/clear_site_data_throttle_unittest.cc

Issue 2368923003: Support the Clear-Site-Data header on resource requests (Closed)
Patch Set: Addressed comments, formatted. Created 3 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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

Powered by Google App Engine
This is Rietveld 408576698