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

Side by Side 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. 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 unified diff | Download patch
OLDNEW
1 // Copyright 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "content/browser/browsing_data/clear_site_data_throttle.h" 5 #include "content/browser/browsing_data/clear_site_data_throttle.h"
6 6
7 #include <memory> 7 #include <memory>
8 8
9 #include "base/command_line.h" 9 #include "base/command_line.h"
10 #include "base/memory/ptr_util.h"
11 #include "base/memory/ref_counted.h"
12 #include "base/run_loop.h"
13 #include "base/strings/stringprintf.h"
14 #include "base/test/scoped_command_line.h"
15 #include "base/test/scoped_task_environment.h"
16 #include "content/public/browser/resource_request_info.h"
10 #include "content/public/common/content_switches.h" 17 #include "content/public/common/content_switches.h"
18 #include "content/public/test/test_browser_thread.h"
19 #include "content/public/test/test_browser_thread_bundle.h"
20 #include "net/base/load_flags.h"
21 #include "net/http/http_util.h"
22 #include "net/url_request/redirect_info.h"
23 #include "net/url_request/url_request_job.h"
24 #include "net/url_request/url_request_test_util.h"
11 #include "testing/gmock/include/gmock/gmock.h" 25 #include "testing/gmock/include/gmock/gmock.h"
12 #include "testing/gtest/include/gtest/gtest.h" 26 #include "testing/gtest/include/gtest/gtest.h"
13 27
28 using ::testing::_;
29
14 namespace content { 30 namespace content {
15 31
32 using ConsoleMessagesDelegate = ClearSiteDataThrottle::ConsoleMessagesDelegate;
33
34 namespace {
35
36 const char kClearSiteDataHeaderPrefix[] = "Clear-Site-Data: ";
37
38 const char kClearCookiesHeader[] =
39 "Clear-Site-Data: { \"types\": [ \"cookies\" ] }";
40
41 void WaitForUIThread() {
42 base::RunLoop run_loop;
43 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, run_loop.QuitClosure());
44 run_loop.Run();
45 }
46
47 // Used to verify that resource throttle delegate calls are made.
48 class MockResourceThrottleDelegate : public ResourceThrottle::Delegate {
49 public:
50 MOCK_METHOD0(Cancel, void());
51 MOCK_METHOD0(CancelAndIgnore, void());
52 MOCK_METHOD1(CancelWithError, void(int));
53 MOCK_METHOD0(Resume, void());
54 };
55
56 // A slightly modified ClearSiteDataThrottle for testing with unconditional
57 // construction, injectable response headers, and dummy clearing functionality.
58 class TestThrottle : public ClearSiteDataThrottle {
59 public:
60 TestThrottle(net::URLRequest* request,
61 std::unique_ptr<ConsoleMessagesDelegate> delegate)
62 : ClearSiteDataThrottle(request, std::move(delegate)) {}
63 ~TestThrottle() override {}
64
65 void SetResponseHeaders(const std::string& headers) {
66 std::string headers_with_status_code = "HTTP/1.1 200\n" + headers;
67 headers_ = new net::HttpResponseHeaders(net::HttpUtil::AssembleRawHeaders(
68 headers_with_status_code.c_str(), headers_with_status_code.size()));
69 }
70
71 MOCK_METHOD4(ClearSiteData,
72 void(const url::Origin& origin,
73 bool clear_cookies,
74 bool clear_storage,
75 bool clear_cache));
76
77 protected:
78 const net::HttpResponseHeaders* GetResponseHeaders() const override {
79 return headers_.get();
80 }
81
82 void ExecuteClearingTask(const url::Origin& origin,
83 bool clear_cookies,
84 bool clear_storage,
85 bool clear_cache,
86 base::OnceClosure callback) override {
87 ClearSiteData(origin, clear_cookies, clear_storage, clear_cache);
88
89 std::move(callback).Run();
mmenke 2017/06/05 22:39:36 This violates the ResourceThrottle spec (Throttles
msramek 2017/06/06 18:20:57 Acknowledged. I documented this fact, but didn't f
90 }
91
92 private:
93 scoped_refptr<net::HttpResponseHeaders> headers_;
94 };
95
96 // A TestThrottle with modifiable current url.
97 class RedirectableTestThrottle : public TestThrottle {
98 public:
99 RedirectableTestThrottle(net::URLRequest* request,
100 std::unique_ptr<ConsoleMessagesDelegate> delegate)
101 : TestThrottle(request, std::move(delegate)) {}
102
103 const GURL& GetCurrentURL() const override {
104 return current_url_.is_valid() ? current_url_
105 : TestThrottle::GetCurrentURL();
106 }
107
108 void SetCurrentURLForTesting(const GURL& url) { current_url_ = url; }
109
110 private:
111 GURL current_url_;
112 };
113
114 // A ConsoleDelegate that outputs messages to a string |output_buffer| owned
115 // by the caller instead of to the console (losing the level information).
116 class StringConsoleMessagesDelegate : public ConsoleMessagesDelegate {
117 public:
118 StringConsoleMessagesDelegate(std::string* output_buffer) {
119 SetOutputFormattedMessageFunctionForTesting(
120 base::Bind(&StringConsoleMessagesDelegate::OutputFormattedMessage,
121 base::Unretained(output_buffer)));
122 }
123
124 ~StringConsoleMessagesDelegate() override {}
125
126 private:
127 static void OutputFormattedMessage(std::string* output_buffer,
128 WebContents* web_contents,
129 ConsoleMessageLevel level,
130 const std::string& formatted_text) {
131 *output_buffer += formatted_text + "\n";
132 }
133 };
134
135 } // namespace
136
16 class ClearSiteDataThrottleTest : public testing::Test { 137 class ClearSiteDataThrottleTest : public testing::Test {
17 public: 138 public:
18 void SetUp() override { 139 ClearSiteDataThrottleTest()
19 base::CommandLine::ForCurrentProcess()->AppendSwitch( 140 : thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP) {}
20 switches::kEnableExperimentalWebPlatformFeatures);
21 throttle_ = ClearSiteDataThrottle::CreateThrottleForNavigation(nullptr);
22 }
23
24 ClearSiteDataThrottle* GetThrottle() {
25 return static_cast<ClearSiteDataThrottle*>(throttle_.get());
26 }
27 141
28 private: 142 private:
29 std::unique_ptr<NavigationThrottle> throttle_; 143 TestBrowserThreadBundle thread_bundle_;
144
145 DISALLOW_COPY_AND_ASSIGN(ClearSiteDataThrottleTest);
30 }; 146 };
31 147
32 TEST_F(ClearSiteDataThrottleTest, ParseHeader) { 148 TEST_F(ClearSiteDataThrottleTest, MaybeCreateThrottleForRequest) {
149 // Enable experimental features.
150 std::unique_ptr<base::test::ScopedCommandLine> command_line(
151 new base::test::ScopedCommandLine());
152 command_line->GetProcessCommandLine()->AppendSwitch(
153 switches::kEnableExperimentalWebPlatformFeatures);
154
155 // Create a URL request.
156 GURL url("https://www.example.com");
157 net::TestURLRequestContext context;
158 std::unique_ptr<net::URLRequest> request(
159 context.CreateRequest(url, net::DEFAULT_PRIORITY, nullptr));
160
161 // We will not create the throttle for an empty ResourceRequestInfo.
162 EXPECT_FALSE(
163 ClearSiteDataThrottle::MaybeCreateThrottleForRequest(request.get()));
164
165 // We can create the throttle for a valid ResourceRequestInfo.
166 ResourceRequestInfo::AllocateForTesting(request.get(), RESOURCE_TYPE_IMAGE,
167 nullptr, 0, 0, 0, false, true, true,
168 true, false);
169 EXPECT_TRUE(
170 ClearSiteDataThrottle::MaybeCreateThrottleForRequest(request.get()));
171
172 // But not if experimental web features are disabled again.
173 request->SetLoadFlags(net::LOAD_NORMAL);
174 command_line.reset();
175 EXPECT_FALSE(
176 ClearSiteDataThrottle::MaybeCreateThrottleForRequest(request.get()));
177 }
178
179 TEST_F(ClearSiteDataThrottleTest, ParseHeaderAndExecuteClearingTask) {
33 struct TestCase { 180 struct TestCase {
34 const char* header; 181 const char* header;
35 bool cookies; 182 bool cookies;
36 bool storage; 183 bool storage;
37 bool cache; 184 bool cache;
38 } test_cases[] = { 185 } test_cases[] = {
39 // One data type. 186 // One data type.
40 {"{ \"types\": [\"cookies\"] }", true, false, false}, 187 {"{ \"types\": [\"cookies\"] }", true, false, false},
41 {"{ \"types\": [\"storage\"] }", false, true, false}, 188 {"{ \"types\": [\"storage\"] }", false, true, false},
42 {"{ \"types\": [\"cache\"] }", false, false, true}, 189 {"{ \"types\": [\"cache\"] }", false, false, true},
(...skipping 22 matching lines...) Expand all
65 false}, 212 false},
66 213
67 // Unknown types are ignored, but we still proceed with the deletion for 214 // Unknown types are ignored, but we still proceed with the deletion for
68 // those that we recognize. 215 // those that we recognize.
69 {"{ \"types\": [\"cache\", \"foo\"] }", false, false, true}, 216 {"{ \"types\": [\"cache\", \"foo\"] }", false, false, true},
70 }; 217 };
71 218
72 for (const TestCase& test_case : test_cases) { 219 for (const TestCase& test_case : test_cases) {
73 SCOPED_TRACE(test_case.header); 220 SCOPED_TRACE(test_case.header);
74 221
222 // Test that ParseHeader works correctly.
75 bool actual_cookies; 223 bool actual_cookies;
76 bool actual_storage; 224 bool actual_storage;
77 bool actual_cache; 225 bool actual_cache;
78 226
79 std::vector<ClearSiteDataThrottle::ConsoleMessage> messages; 227 GURL url("https://example.com");
228 ConsoleMessagesDelegate console_delegate;
80 229
81 EXPECT_TRUE(GetThrottle()->ParseHeader(test_case.header, &actual_cookies, 230 EXPECT_TRUE(ClearSiteDataThrottle::ParseHeaderForTesting(
82 &actual_storage, &actual_cache, 231 test_case.header, &actual_cookies, &actual_storage, &actual_cache,
83 &messages)); 232 &console_delegate, url));
84 233
85 EXPECT_EQ(test_case.cookies, actual_cookies); 234 EXPECT_EQ(test_case.cookies, actual_cookies);
86 EXPECT_EQ(test_case.storage, actual_storage); 235 EXPECT_EQ(test_case.storage, actual_storage);
87 EXPECT_EQ(test_case.cache, actual_cache); 236 EXPECT_EQ(test_case.cache, actual_cache);
237
238 // Test that a call with the above parameters actually reaches
239 // ExecuteClearingTask().
240 net::TestURLRequestContext context;
241 std::unique_ptr<net::URLRequest> request(
242 context.CreateRequest(url, net::DEFAULT_PRIORITY, nullptr));
243 TestThrottle throttle(request.get(),
244 base::MakeUnique<ConsoleMessagesDelegate>());
245 MockResourceThrottleDelegate delegate;
246 throttle.set_delegate_for_testing(&delegate);
247 throttle.SetResponseHeaders(std::string(kClearSiteDataHeaderPrefix) +
248 test_case.header);
249
250 EXPECT_CALL(throttle, ClearSiteData(url::Origin(url), test_case.cookies,
251 test_case.storage, test_case.cache));
252 bool defer;
253 throttle.WillProcessResponse(&defer);
254 EXPECT_TRUE(defer);
255
256 testing::Mock::VerifyAndClearExpectations(&throttle);
88 } 257 }
89 } 258 }
90 259
91 TEST_F(ClearSiteDataThrottleTest, InvalidHeader) { 260 TEST_F(ClearSiteDataThrottleTest, InvalidHeader) {
92 struct TestCase { 261 struct TestCase {
93 const char* header; 262 const char* header;
94 const char* console_message; 263 const char* console_message;
95 } test_cases[] = { 264 } test_cases[] = {
96 {"", "Not a valid JSON.\n"}, 265 {"", "Not a valid JSON.\n"},
97 {"\"unclosed quote", "Not a valid JSON.\n"}, 266 {"\"unclosed quote", "Not a valid JSON.\n"},
98 {"\"some text\"", "Expecting a JSON dictionary with a 'types' field.\n"}, 267 {"\"some text\"", "Expecting a JSON dictionary with a 'types' field.\n"},
99 {"{ \"field\" : {} }", 268 {"{ \"field\" : {} }",
100 "Expecting a JSON dictionary with a 'types' field.\n"}, 269 "Expecting a JSON dictionary with a 'types' field.\n"},
101 {"{ \"types\" : [ \"passwords\" ] }", 270 {"{ \"types\" : [ \"passwords\" ] }",
102 "Invalid type: \"passwords\".\n" 271 "Unrecognized type: \"passwords\".\n"
103 "No valid types specified in the 'types' field.\n"}, 272 "No recognized types specified in the 'types' field.\n"},
104 {"{ \"types\" : [ [ \"list in a list\" ] ] }", 273 {"{ \"types\" : [ [ \"list in a list\" ] ] }",
105 "Invalid type: [\"list in a list\"].\n" 274 "Unrecognized type: [\"list in a list\"].\n"
106 "No valid types specified in the 'types' field.\n"}, 275 "No recognized types specified in the 'types' field.\n"},
107 {"{ \"types\" : [ \"кукис\", \"сторидж\", \"кэш\" ]", 276 {"{ \"types\" : [ \"кукис\", \"сторидж\", \"кэш\" ]",
108 "Must only contain ASCII characters.\n"}}; 277 "Must only contain ASCII characters.\n"}};
109 278
110 for (const TestCase& test_case : test_cases) { 279 for (const TestCase& test_case : test_cases) {
111 SCOPED_TRACE(test_case.header); 280 SCOPED_TRACE(test_case.header);
112 281
113 bool actual_cookies; 282 bool actual_cookies;
114 bool actual_storage; 283 bool actual_storage;
115 bool actual_cache; 284 bool actual_cache;
116 285
117 std::vector<ClearSiteDataThrottle::ConsoleMessage> messages; 286 ConsoleMessagesDelegate console_delegate;
118 287
119 EXPECT_FALSE(GetThrottle()->ParseHeader(test_case.header, &actual_cookies, 288 EXPECT_FALSE(ClearSiteDataThrottle::ParseHeaderForTesting(
120 &actual_storage, &actual_cache, 289 test_case.header, &actual_cookies, &actual_storage, &actual_cache,
121 &messages)); 290 &console_delegate, GURL()));
122 291
123 std::string multiline_message; 292 std::string multiline_message;
124 for (const auto& message : messages) { 293 for (const auto& message : console_delegate.messages()) {
125 EXPECT_EQ(CONSOLE_MESSAGE_LEVEL_ERROR, message.level); 294 EXPECT_EQ(CONSOLE_MESSAGE_LEVEL_ERROR, message.level);
126 multiline_message += message.text + "\n"; 295 multiline_message += message.text + "\n";
127 } 296 }
128 297
129 EXPECT_EQ(test_case.console_message, multiline_message); 298 EXPECT_EQ(test_case.console_message, multiline_message);
130 } 299 }
131 } 300 }
132 301
302 TEST_F(ClearSiteDataThrottleTest, LoadDoNotSaveCookies) {
303 net::TestURLRequestContext context;
304 std::unique_ptr<net::URLRequest> request(context.CreateRequest(
305 GURL("https://www.example.com"), net::DEFAULT_PRIORITY, nullptr));
306 std::unique_ptr<ConsoleMessagesDelegate> scoped_console_delegate(
307 new ConsoleMessagesDelegate());
308 const ConsoleMessagesDelegate* console_delegate =
309 scoped_console_delegate.get();
310 TestThrottle throttle(request.get(), std::move(scoped_console_delegate));
311 MockResourceThrottleDelegate delegate;
312 throttle.set_delegate_for_testing(&delegate);
313 throttle.SetResponseHeaders(kClearCookiesHeader);
314
315 bool defer;
316 throttle.WillProcessResponse(&defer);
317 EXPECT_TRUE(defer);
318 EXPECT_EQ(1u, console_delegate->messages().size());
319 EXPECT_EQ("Cleared datatypes: cookies.",
320 console_delegate->messages().front().text);
321 EXPECT_EQ(console_delegate->messages().front().level,
322 CONSOLE_MESSAGE_LEVEL_INFO);
323
324 request->SetLoadFlags(net::LOAD_DO_NOT_SAVE_COOKIES);
325 throttle.WillProcessResponse(&defer);
326 EXPECT_FALSE(defer);
327 EXPECT_EQ(2u, console_delegate->messages().size());
328 EXPECT_EQ(
329 "The request's credentials mode prohibits modifying cookies "
330 "and other local data.",
331 console_delegate->messages().rbegin()->text);
332 EXPECT_EQ(CONSOLE_MESSAGE_LEVEL_ERROR,
333 console_delegate->messages().rbegin()->level);
mmenke 2017/06/05 22:39:36 EXPECT that the throttle's ClearSiteData isn't cal
msramek 2017/06/06 18:20:57 Done. Also added the positive call expectation abo
334 }
335
336 TEST_F(ClearSiteDataThrottleTest, InvalidOrigin) {
337 struct TestCase {
338 const char* origin;
339 bool expect_success;
340 std::string error_message; // Tested only if |expect_success| = false.
341 } kTestCases[] = {
342 // The throttle only works on secure origins.
343 {"https://secure-origin.com", true, ""},
344 {"filesystem:https://secure-origin.com/temporary/", true, ""},
345
346 // That includes localhost.
347 {"http://localhost", true, ""},
348
349 // Not on insecure origins.
350 {"http://insecure-origin.com", false,
351 "Not supported for insecure origins."},
352 {"filesystem:http://insecure-origin.com/temporary/", false,
353 "Not supported for insecure origins."},
354
355 // Not on unique origins.
356 {"data:unique-origin;", false, "Not supported for unique origins."},
357 };
358
359 net::TestURLRequestContext context;
360
361 for (const TestCase& test_case : kTestCases) {
362 std::unique_ptr<net::URLRequest> request(context.CreateRequest(
363 GURL(test_case.origin), net::DEFAULT_PRIORITY, nullptr));
364 std::unique_ptr<ConsoleMessagesDelegate> scoped_console_delegate(
365 new ConsoleMessagesDelegate());
366 const ConsoleMessagesDelegate* console_delegate =
367 scoped_console_delegate.get();
368 TestThrottle throttle(request.get(), std::move(scoped_console_delegate));
369 MockResourceThrottleDelegate delegate;
370 throttle.set_delegate_for_testing(&delegate);
371 throttle.SetResponseHeaders(kClearCookiesHeader);
372
373 bool defer;
374 throttle.WillProcessResponse(&defer);
375
376 EXPECT_EQ(defer, test_case.expect_success);
377 EXPECT_EQ(console_delegate->messages().size(), 1u);
378 EXPECT_EQ(test_case.expect_success ? CONSOLE_MESSAGE_LEVEL_INFO
379 : CONSOLE_MESSAGE_LEVEL_ERROR,
380 console_delegate->messages().front().level);
381 if (!test_case.expect_success) {
382 EXPECT_EQ(test_case.error_message,
383 console_delegate->messages().front().text);
384 }
mmenke 2017/06/05 22:39:36 Again, EXPECT_CALLed / not calleds as appropriate?
msramek 2017/06/06 18:20:57 Done.
385 }
386 }
387
388 TEST_F(ClearSiteDataThrottleTest, DeferAndResume) {
389 enum Stage { START, REDIRECT, RESPONSE };
390
391 struct TestCase {
392 Stage stage;
393 std::string response_headers;
394 bool should_defer;
395 } kTestCases[] = {
396 // The throttle never interferes while the request is starting. Response
397 // headers are ignored, because URLRequest is not supposed to have any
398 // at this stage in the first place.
399 {START, "", false},
400 {START, kClearCookiesHeader, false},
401
402 // The throttle does not defer redirects if there are no interesting
403 // response headers.
404 {REDIRECT, "", false},
405 {REDIRECT, "Set-Cookie: abc=123;", false},
406 {REDIRECT, "Content-Type: image/png;", false},
407
408 // That includes malformed Clear-Site-Data headers or header values
409 // that do not lead to deletion.
410 {REDIRECT, "Clear-Site-Data: { types: cookies } ", false},
411 {REDIRECT, "Clear-Site-Data: { \"types\": [ \"unknown type\" ] }", false},
412
413 // However, redirects are deferred for valid Clear-Site-Data headers.
414 {REDIRECT,
415 "Clear-Site-Data: { \"types\": [ \"cookies\", \"unknown type\" ] }",
416 true},
417 {REDIRECT,
418 base::StringPrintf("Content-Type: image/png;\n%s", kClearCookiesHeader),
419 true},
420 {REDIRECT,
421 base::StringPrintf("%s\nContent-Type: image/png;", kClearCookiesHeader),
422 true},
423
424 // We expect at most one instance of the header. Multiple instances
425 // will not be parsed currently. This is not an inherent property of
426 // Clear-Site-Data, just a documentation of the current behavior.
427 {REDIRECT,
428 base::StringPrintf("%s\n%s", kClearCookiesHeader, kClearCookiesHeader),
429 false},
430
431 // Final response headers are treated the same way as in the case
432 // of redirect.
433 {REDIRECT, "Set-Cookie: abc=123;", false},
434 {REDIRECT, "Clear-Site-Data: { types: cookies } ", false},
435 {REDIRECT, kClearCookiesHeader, true},
436 };
437
438 struct TestOrigin {
439 const char* origin;
440 bool valid;
441 } kTestOrigins[] = {
442 // The throttle only works on secure origins.
443 {"https://secure-origin.com", true},
444 {"filesystem:https://secure-origin.com/temporary/", true},
445
446 // That includes localhost.
447 {"http://localhost", true},
448
449 // Not on insecure origins.
450 {"http://insecure-origin.com", false},
451 {"filesystem:http://insecure-origin.com/temporary/", false},
452
453 // Not on unique origins.
454 {"data:unique-origin;", false},
455 };
456
457 net::TestURLRequestContext context;
458
459 for (const TestOrigin& test_origin : kTestOrigins) {
460 for (const TestCase& test_case : kTestCases) {
461 SCOPED_TRACE(base::StringPrintf("Origin=%s\nStage=%d\nHeaders:\n%s",
462 test_origin.origin, test_case.stage,
463 test_case.response_headers.c_str()));
464
465 std::unique_ptr<net::URLRequest> request(context.CreateRequest(
466 GURL(test_origin.origin), net::DEFAULT_PRIORITY, nullptr));
467 TestThrottle throttle(request.get(),
468 base::MakeUnique<ConsoleMessagesDelegate>());
469 throttle.SetResponseHeaders(test_case.response_headers);
470
471 MockResourceThrottleDelegate delegate;
472 throttle.set_delegate_for_testing(&delegate);
473
474 // Whether we should defer is always conditional on the origin
475 // being valid.
476 bool expected_defer = test_case.should_defer && test_origin.valid;
477
478 // If we expect loading to be deferred, then we also expect data to be
479 // cleared and the load to eventually resume.
480 if (expected_defer) {
481 testing::Expectation expectation = EXPECT_CALL(
482 throttle,
483 ClearSiteData(url::Origin(GURL(test_origin.origin)), _, _, _));
484 EXPECT_CALL(delegate, Resume()).After(expectation);
485 } else {
486 EXPECT_CALL(throttle, ClearSiteData(_, _, _, _)).Times(0);
487 EXPECT_CALL(delegate, Resume()).Times(0);
488 }
489
490 bool actual_defer = false;
491
492 switch (test_case.stage) {
493 case START: {
494 throttle.WillStartRequest(&actual_defer);
495 break;
496 }
497 case REDIRECT: {
498 net::RedirectInfo redirect_info;
499 throttle.WillRedirectRequest(redirect_info, &actual_defer);
500 break;
501 }
502 case RESPONSE: {
503 throttle.WillProcessResponse(&actual_defer);
504 break;
505 }
506 }
507
508 EXPECT_EQ(expected_defer, actual_defer);
509 testing::Mock::VerifyAndClearExpectations(&delegate);
510 }
511 }
512 }
513
514 // Verifies that console outputs from various actions on different URLs
515 // are correctly pretty-printed to the console.
516 TEST_F(ClearSiteDataThrottleTest, FormattedConsoleOutput) {
517 struct TestCase {
518 const char* header;
519 const char* url;
520 const char* output;
521 } kTestCases[] = {
522 // Successful deletion outputs one line.
523 {"{ \"types\": [ \"cookies\" ] }", "https://origin1.com/foo",
524 "Clear-Site-Data header on 'https://origin1.com/foo': "
525 "Cleared datatypes: cookies.\n"},
526
527 // Another successful deletion.
528 {"{ \"types\": [ \"storage\" ] }", "https://origin2.com/foo",
529 "Clear-Site-Data header on 'https://origin2.com/foo': "
530 "Cleared datatypes: storage.\n"},
531
532 // Redirect to the same URL. Unsuccessful deletion outputs two lines.
533 {"{ \"foo\": \"bar\" }", "https://origin2.com/foo",
534 "Clear-Site-Data header on 'https://origin2.com/foo': "
535 "Expecting a JSON dictionary with a 'types' field.\n"},
536
537 // Redirect to another URL. Another unsuccessful deletion.
538 {"\"some text\"", "https://origin3.com/bar",
539 "Clear-Site-Data header on 'https://origin3.com/bar': "
540 "Expecting a JSON dictionary with a 'types' field.\n"},
541
542 // Yet another on the same URL.
543 {"{ \"types\" : [ \"passwords\" ] }", "https://origin3.com/bar",
544 "Clear-Site-Data header on 'https://origin3.com/bar': "
545 "Unrecognized type: \"passwords\".\n"
546 "Clear-Site-Data header on 'https://origin3.com/bar': "
547 "No recognized types specified in the 'types' field.\n"},
548
549 // Successful deletion on the same URL.
550 {"{ \"types\": [ \"cache\" ] }", "https://origin3.com/bar",
551 "Clear-Site-Data header on 'https://origin3.com/bar': "
552 "Cleared datatypes: cache.\n"},
553
554 // Redirect to the original URL.
555 // Successful deletion outputs one line.
556 {"", "https://origin1.com/foo",
557 "Clear-Site-Data header on 'https://origin1.com/foo': "
558 "Not a valid JSON.\n"}};
559
560 bool kThrottleTypeIsNavigation[] = {true, false};
561
562 for (bool throttle_type : kThrottleTypeIsNavigation) {
563 bool navigation = kThrottleTypeIsNavigation[throttle_type];
mmenke 2017/06/05 22:39:37 This is a bit funky. throttle_type is not an inde
msramek 2017/06/06 18:20:57 But it works, doesn't it? :-D Yeah, I was trying t
564 SCOPED_TRACE(navigation ? "Navigation test." : "Subresource test.");
565
566 net::TestURLRequestContext context;
567 std::unique_ptr<net::URLRequest> request(context.CreateRequest(
568 GURL(kTestCases[0].url), net::DEFAULT_PRIORITY, nullptr));
569 ResourceRequestInfo::AllocateForTesting(
570 request.get(),
571 navigation ? RESOURCE_TYPE_SUB_FRAME : RESOURCE_TYPE_IMAGE, nullptr, 0,
572 0, 0, false, true, true, true, false);
573
574 std::string output_buffer;
575 std::unique_ptr<RedirectableTestThrottle> throttle =
576 base::MakeUnique<RedirectableTestThrottle>(
577 request.get(),
578 base::MakeUnique<StringConsoleMessagesDelegate>(&output_buffer));
579
580 MockResourceThrottleDelegate delegate;
581 throttle->set_delegate_for_testing(&delegate);
582
583 std::string last_seen_console_output;
584
585 // Simulate redirecting the throttle through the above origins with the
586 // corresponding response headers.
587 bool defer;
588 throttle->WillStartRequest(&defer);
589
590 for (size_t i = 0; i < arraysize(kTestCases); i++) {
591 throttle->SetResponseHeaders(std::string(kClearSiteDataHeaderPrefix) +
592 kTestCases[i].header);
593
594 // TODO(msramek): There is probably a better way to do this inside
595 // URLRequest.
596 throttle->SetCurrentURLForTesting(GURL(kTestCases[i].url));
597
598 net::RedirectInfo redirect_info;
599 if (i < arraysize(kTestCases) - 1)
600 throttle->WillRedirectRequest(redirect_info, &defer);
601 else
602 throttle->WillProcessResponse(&defer);
603
604 // Wait for any messages to be output.
605 WaitForUIThread();
606
607 // For navigations, the console should be still empty. For subresource
608 // requests, messages should be added progressively.
609 if (navigation) {
610 EXPECT_TRUE(output_buffer.empty());
611 } else {
612 EXPECT_EQ(last_seen_console_output + kTestCases[i].output,
613 output_buffer);
614 }
615
616 last_seen_console_output = output_buffer;
617 }
618
619 throttle.reset();
620 WaitForUIThread();
621
622 // At the end, the console must contain all messages regardless of whether
623 // it was a navigation or a subresource request.
624 std::string expected_output;
625 for (struct TestCase& test_case : kTestCases)
626 expected_output += test_case.output;
627 EXPECT_EQ(expected_output, output_buffer);
628 }
629 }
630
133 } // namespace content 631 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698