| OLD | NEW |
| 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/memory/ptr_util.h" | 9 #include "base/memory/ptr_util.h" |
| 10 #include "base/memory/ref_counted.h" | 10 #include "base/memory/ref_counted.h" |
| (...skipping 15 matching lines...) Expand all Loading... |
| 26 using ::testing::_; | 26 using ::testing::_; |
| 27 | 27 |
| 28 namespace content { | 28 namespace content { |
| 29 | 29 |
| 30 using ConsoleMessagesDelegate = ClearSiteDataThrottle::ConsoleMessagesDelegate; | 30 using ConsoleMessagesDelegate = ClearSiteDataThrottle::ConsoleMessagesDelegate; |
| 31 | 31 |
| 32 namespace { | 32 namespace { |
| 33 | 33 |
| 34 const char kClearSiteDataHeaderPrefix[] = "Clear-Site-Data: "; | 34 const char kClearSiteDataHeaderPrefix[] = "Clear-Site-Data: "; |
| 35 | 35 |
| 36 const char kClearCookiesHeader[] = "Clear-Site-Data: \"cookies\""; | 36 const char kClearCookiesHeader[] = |
| 37 "Clear-Site-Data: { \"types\": [ \"cookies\" ] }"; |
| 37 | 38 |
| 38 void WaitForUIThread() { | 39 void WaitForUIThread() { |
| 39 base::RunLoop run_loop; | 40 base::RunLoop run_loop; |
| 40 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, run_loop.QuitClosure()); | 41 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, run_loop.QuitClosure()); |
| 41 run_loop.Run(); | 42 run_loop.Run(); |
| 42 } | 43 } |
| 43 | 44 |
| 44 // Used to verify that resource throttle delegate calls are made. | 45 // Used to verify that resource throttle delegate calls are made. |
| 45 class MockResourceThrottleDelegate : public ResourceThrottle::Delegate { | 46 class MockResourceThrottleDelegate : public ResourceThrottle::Delegate { |
| 46 public: | 47 public: |
| (...skipping 119 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 166 } | 167 } |
| 167 | 168 |
| 168 TEST_F(ClearSiteDataThrottleTest, ParseHeaderAndExecuteClearingTask) { | 169 TEST_F(ClearSiteDataThrottleTest, ParseHeaderAndExecuteClearingTask) { |
| 169 struct TestCase { | 170 struct TestCase { |
| 170 const char* header; | 171 const char* header; |
| 171 bool cookies; | 172 bool cookies; |
| 172 bool storage; | 173 bool storage; |
| 173 bool cache; | 174 bool cache; |
| 174 } test_cases[] = { | 175 } test_cases[] = { |
| 175 // One data type. | 176 // One data type. |
| 176 {"\"cookies\"", true, false, false}, | 177 {"{ \"types\": [\"cookies\"] }", true, false, false}, |
| 177 {"\"storage\"", false, true, false}, | 178 {"{ \"types\": [\"storage\"] }", false, true, false}, |
| 178 {"\"cache\"", false, false, true}, | 179 {"{ \"types\": [\"cache\"] }", false, false, true}, |
| 179 | 180 |
| 180 // Two data types. | 181 // Two data types. |
| 181 {"\"cookies\", \"storage\"", true, true, false}, | 182 {"{ \"types\": [\"cookies\", \"storage\"] }", true, true, false}, |
| 182 {"\"cookies\", \"cache\"", true, false, true}, | 183 {"{ \"types\": [\"cookies\", \"cache\"] }", true, false, true}, |
| 183 {"\"storage\", \"cache\"", false, true, true}, | 184 {"{ \"types\": [\"storage\", \"cache\"] }", false, true, true}, |
| 184 | 185 |
| 185 // Three data types. | 186 // Three data types. |
| 186 {"\"storage\", \"cache\", \"cookies\"", true, true, true}, | 187 {"{ \"types\": [\"storage\", \"cache\", \"cookies\"] }", true, true, |
| 187 {"\"cache\", \"cookies\", \"storage\"", true, true, true}, | 188 true}, |
| 188 {"\"cookies\", \"storage\", \"cache\"", true, true, true}, | 189 {"{ \"types\": [\"cache\", \"cookies\", \"storage\"] }", true, true, |
| 190 true}, |
| 191 {"{ \"types\": [\"cookies\", \"storage\", \"cache\"] }", true, true, |
| 192 true}, |
| 189 | 193 |
| 190 // Different formatting. | 194 // Different formatting. |
| 191 {"\"cookies\"", true, false, false}, | 195 {" { \"types\": [\"cookies\" ]}", true, false, false}, |
| 192 | 196 |
| 193 // Duplicates. | 197 // Duplicates. |
| 194 {"\"cookies\", \"cookies\"", true, false, false}, | 198 {"{ \"types\": [\"cookies\", \"cookies\"] }", true, false, false}, |
| 195 | 199 |
| 196 // Other JSON-formatted items in the list. | 200 // Other entries in the dictionary. |
| 197 {"\"storage\", { \"other_params\": {} }", false, true, false}, | 201 {"{ \"types\": [\"storage\"], \"other_params\": {} }", false, true, |
| 202 false}, |
| 198 | 203 |
| 199 // Unknown types are ignored, but we still proceed with the deletion for | 204 // Unknown types are ignored, but we still proceed with the deletion for |
| 200 // those that we recognize. | 205 // those that we recognize. |
| 201 {"\"cache\", \"foo\"", false, false, true}, | 206 {"{ \"types\": [\"cache\", \"foo\"] }", false, false, true}, |
| 202 }; | 207 }; |
| 203 | 208 |
| 204 for (const TestCase& test_case : test_cases) { | 209 for (const TestCase& test_case : test_cases) { |
| 205 SCOPED_TRACE(test_case.header); | 210 SCOPED_TRACE(test_case.header); |
| 206 | 211 |
| 207 // Test that ParseHeader works correctly. | 212 // Test that ParseHeader works correctly. |
| 208 bool actual_cookies; | 213 bool actual_cookies; |
| 209 bool actual_storage; | 214 bool actual_storage; |
| 210 bool actual_cache; | 215 bool actual_cache; |
| 211 | 216 |
| (...skipping 28 matching lines...) Expand all Loading... |
| 240 | 245 |
| 241 testing::Mock::VerifyAndClearExpectations(&throttle); | 246 testing::Mock::VerifyAndClearExpectations(&throttle); |
| 242 } | 247 } |
| 243 } | 248 } |
| 244 | 249 |
| 245 TEST_F(ClearSiteDataThrottleTest, InvalidHeader) { | 250 TEST_F(ClearSiteDataThrottleTest, InvalidHeader) { |
| 246 struct TestCase { | 251 struct TestCase { |
| 247 const char* header; | 252 const char* header; |
| 248 const char* console_message; | 253 const char* console_message; |
| 249 } test_cases[] = { | 254 } test_cases[] = { |
| 250 {"", "No recognized types specified.\n"}, | 255 {"", "Expected valid JSON.\n"}, |
| 251 {"\"unclosed", "The header's value does not parse as valid JSON.\n"}, | 256 {"\"unclosed quote", "Expected valid JSON.\n"}, |
| 252 {"\"passwords\"", | 257 {"\"some text\"", "Expected a JSON dictionary with a 'types' field.\n"}, |
| 258 {"{ \"field\" : {} }", |
| 259 "Expected a JSON dictionary with a 'types' field.\n"}, |
| 260 {"{ \"types\" : [ \"passwords\" ] }", |
| 253 "Unrecognized type: \"passwords\".\n" | 261 "Unrecognized type: \"passwords\".\n" |
| 254 "No recognized types specified.\n"}, | 262 "No recognized types specified in the 'types' field.\n"}, |
| 255 {"[ \"list\" ]", | 263 {"{ \"types\" : [ [ \"list in a list\" ] ] }", |
| 256 "Unrecognized type: [\"list\"].\n" | 264 "Unrecognized type: [\"list in a list\"].\n" |
| 257 "No recognized types specified.\n"}, | 265 "No recognized types specified in the 'types' field.\n"}, |
| 258 {"[ \"list\" ]", | 266 {"{ \"types\" : [ \"кукис\", \"сторидж\", \"кэш\" ]", |
| 259 "Unrecognized type: [\"list\"].\n" | |
| 260 "No recognized types specified.\n"}, | |
| 261 {"{ \"cookies\": [ \"a\" ] }", | |
| 262 "Unrecognized type: {\"cookies\":[\"a\"]}.\n" | |
| 263 "No recognized types specified.\n"}, | |
| 264 {"\"кукис\", \"сторидж\", \"кэш\"", | |
| 265 "Must only contain ASCII characters.\n"}}; | 267 "Must only contain ASCII characters.\n"}}; |
| 266 | 268 |
| 267 for (const TestCase& test_case : test_cases) { | 269 for (const TestCase& test_case : test_cases) { |
| 268 SCOPED_TRACE(test_case.header); | 270 SCOPED_TRACE(test_case.header); |
| 269 | 271 |
| 270 bool actual_cookies; | 272 bool actual_cookies; |
| 271 bool actual_storage; | 273 bool actual_storage; |
| 272 bool actual_cache; | 274 bool actual_cache; |
| 273 | 275 |
| 274 ConsoleMessagesDelegate console_delegate; | 276 ConsoleMessagesDelegate console_delegate; |
| (...skipping 121 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 396 {START, kClearCookiesHeader, false}, | 398 {START, kClearCookiesHeader, false}, |
| 397 | 399 |
| 398 // The throttle does not defer redirects if there are no interesting | 400 // The throttle does not defer redirects if there are no interesting |
| 399 // response headers. | 401 // response headers. |
| 400 {REDIRECT, "", false}, | 402 {REDIRECT, "", false}, |
| 401 {REDIRECT, "Set-Cookie: abc=123;", false}, | 403 {REDIRECT, "Set-Cookie: abc=123;", false}, |
| 402 {REDIRECT, "Content-Type: image/png;", false}, | 404 {REDIRECT, "Content-Type: image/png;", false}, |
| 403 | 405 |
| 404 // That includes malformed Clear-Site-Data headers or header values | 406 // That includes malformed Clear-Site-Data headers or header values |
| 405 // that do not lead to deletion. | 407 // that do not lead to deletion. |
| 406 {REDIRECT, "Clear-Site-Data: cookies", false}, | 408 {REDIRECT, "Clear-Site-Data: { types: cookies } ", false}, |
| 407 {REDIRECT, "Clear-Site-Data: \"unknown type\"", false}, | 409 {REDIRECT, "Clear-Site-Data: { \"types\": [ \"unknown type\" ] }", false}, |
| 408 | 410 |
| 409 // However, redirects are deferred for valid Clear-Site-Data headers. | 411 // However, redirects are deferred for valid Clear-Site-Data headers. |
| 410 {REDIRECT, "Clear-Site-Data: \"cookies\", \"unknown type\"", true}, | 412 {REDIRECT, |
| 413 "Clear-Site-Data: { \"types\": [ \"cookies\", \"unknown type\" ] }", |
| 414 true}, |
| 411 {REDIRECT, | 415 {REDIRECT, |
| 412 base::StringPrintf("Content-Type: image/png;\n%s", kClearCookiesHeader), | 416 base::StringPrintf("Content-Type: image/png;\n%s", kClearCookiesHeader), |
| 413 true}, | 417 true}, |
| 414 {REDIRECT, | 418 {REDIRECT, |
| 415 base::StringPrintf("%s\nContent-Type: image/png;", kClearCookiesHeader), | 419 base::StringPrintf("%s\nContent-Type: image/png;", kClearCookiesHeader), |
| 416 true}, | 420 true}, |
| 417 | 421 |
| 418 // Multiple instances of the header will be parsed correctly. | 422 // We expect at most one instance of the header. Multiple instances |
| 423 // will not be parsed currently. This is not an inherent property of |
| 424 // Clear-Site-Data, just a documentation of the current behavior. |
| 419 {REDIRECT, | 425 {REDIRECT, |
| 420 base::StringPrintf("%s\n%s", kClearCookiesHeader, kClearCookiesHeader), | 426 base::StringPrintf("%s\n%s", kClearCookiesHeader, kClearCookiesHeader), |
| 421 true}, | 427 false}, |
| 422 | 428 |
| 423 // Final response headers are treated the same way as in the case | 429 // Final response headers are treated the same way as in the case |
| 424 // of redirect. | 430 // of redirect. |
| 425 {REDIRECT, "Set-Cookie: abc=123;", false}, | 431 {REDIRECT, "Set-Cookie: abc=123;", false}, |
| 426 {REDIRECT, "Clear-Site-Data: cookies", false}, | 432 {REDIRECT, "Clear-Site-Data: { types: cookies } ", false}, |
| 427 {REDIRECT, kClearCookiesHeader, true}, | 433 {REDIRECT, kClearCookiesHeader, true}, |
| 428 }; | 434 }; |
| 429 | 435 |
| 430 struct TestOrigin { | 436 struct TestOrigin { |
| 431 const char* origin; | 437 const char* origin; |
| 432 bool valid; | 438 bool valid; |
| 433 } kTestOrigins[] = { | 439 } kTestOrigins[] = { |
| 434 // The throttle only works on secure origins. | 440 // The throttle only works on secure origins. |
| 435 {"https://secure-origin.com", true}, | 441 {"https://secure-origin.com", true}, |
| 436 {"filesystem:https://secure-origin.com/temporary/", true}, | 442 {"filesystem:https://secure-origin.com/temporary/", true}, |
| (...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 505 | 511 |
| 506 // Verifies that console outputs from various actions on different URLs | 512 // Verifies that console outputs from various actions on different URLs |
| 507 // are correctly pretty-printed to the console. | 513 // are correctly pretty-printed to the console. |
| 508 TEST_F(ClearSiteDataThrottleTest, FormattedConsoleOutput) { | 514 TEST_F(ClearSiteDataThrottleTest, FormattedConsoleOutput) { |
| 509 struct TestCase { | 515 struct TestCase { |
| 510 const char* header; | 516 const char* header; |
| 511 const char* url; | 517 const char* url; |
| 512 const char* output; | 518 const char* output; |
| 513 } kTestCases[] = { | 519 } kTestCases[] = { |
| 514 // Successful deletion outputs one line. | 520 // Successful deletion outputs one line. |
| 515 {"\"cookies\"", "https://origin1.com/foo", | 521 {"{ \"types\": [ \"cookies\" ] }", "https://origin1.com/foo", |
| 516 "Clear-Site-Data header on 'https://origin1.com/foo': " | 522 "Clear-Site-Data header on 'https://origin1.com/foo': " |
| 517 "Cleared data types: cookies.\n"}, | 523 "Cleared data types: cookies.\n"}, |
| 518 | 524 |
| 519 // Another successful deletion. | 525 // Another successful deletion. |
| 520 {"\"storage\"", "https://origin2.com/foo", | 526 {"{ \"types\": [ \"storage\" ] }", "https://origin2.com/foo", |
| 521 "Clear-Site-Data header on 'https://origin2.com/foo': " | 527 "Clear-Site-Data header on 'https://origin2.com/foo': " |
| 522 "Cleared data types: storage.\n"}, | 528 "Cleared data types: storage.\n"}, |
| 523 | 529 |
| 524 // Redirect to the same URL. Unsuccessful deletion outputs two lines. | 530 // Redirect to the same URL. Unsuccessful deletion outputs two lines. |
| 525 {"\"foo\"", "https://origin2.com/foo", | 531 {"{ \"foo\": \"bar\" }", "https://origin2.com/foo", |
| 526 "Clear-Site-Data header on 'https://origin2.com/foo': " | 532 "Clear-Site-Data header on 'https://origin2.com/foo': " |
| 527 "Unrecognized type: \"foo\".\n" | 533 "Expected a JSON dictionary with a 'types' field.\n"}, |
| 528 "Clear-Site-Data header on 'https://origin2.com/foo': " | |
| 529 "No recognized types specified.\n"}, | |
| 530 | 534 |
| 531 // Redirect to another URL. Another unsuccessful deletion. | 535 // Redirect to another URL. Another unsuccessful deletion. |
| 532 {"\"some text\"", "https://origin3.com/bar", | 536 {"\"some text\"", "https://origin3.com/bar", |
| 533 "Clear-Site-Data header on 'https://origin3.com/bar': " | 537 "Clear-Site-Data header on 'https://origin3.com/bar': " |
| 534 "Unrecognized type: \"some text\".\n" | 538 "Expected a JSON dictionary with a 'types' field.\n"}, |
| 535 "Clear-Site-Data header on 'https://origin3.com/bar': " | |
| 536 "No recognized types specified.\n"}, | |
| 537 | 539 |
| 538 // Yet another on the same URL. | 540 // Yet another on the same URL. |
| 539 {"\"passwords\"", "https://origin3.com/bar", | 541 {"{ \"types\" : [ \"passwords\" ] }", "https://origin3.com/bar", |
| 540 "Clear-Site-Data header on 'https://origin3.com/bar': " | 542 "Clear-Site-Data header on 'https://origin3.com/bar': " |
| 541 "Unrecognized type: \"passwords\".\n" | 543 "Unrecognized type: \"passwords\".\n" |
| 542 "Clear-Site-Data header on 'https://origin3.com/bar': " | 544 "Clear-Site-Data header on 'https://origin3.com/bar': " |
| 543 "No recognized types specified.\n"}, | 545 "No recognized types specified in the 'types' field.\n"}, |
| 544 | 546 |
| 545 // Successful deletion on the same URL. | 547 // Successful deletion on the same URL. |
| 546 {"\"cache\"", "https://origin3.com/bar", | 548 {"{ \"types\": [ \"cache\" ] }", "https://origin3.com/bar", |
| 547 "Clear-Site-Data header on 'https://origin3.com/bar': " | 549 "Clear-Site-Data header on 'https://origin3.com/bar': " |
| 548 "Cleared data types: cache.\n"}, | 550 "Cleared data types: cache.\n"}, |
| 549 | 551 |
| 550 // Redirect to the original URL. | 552 // Redirect to the original URL. |
| 551 // Successful deletion outputs one line. | 553 // Successful deletion outputs one line. |
| 552 {"", "https://origin1.com/foo", | 554 {"", "https://origin1.com/foo", |
| 553 "Clear-Site-Data header on 'https://origin1.com/foo': " | 555 "Clear-Site-Data header on 'https://origin1.com/foo': " |
| 554 "No recognized types specified.\n"}}; | 556 "Expected valid JSON.\n"}}; |
| 555 | 557 |
| 556 bool kThrottleTypeIsNavigation[] = {true, false}; | 558 bool kThrottleTypeIsNavigation[] = {true, false}; |
| 557 | 559 |
| 558 for (bool navigation : kThrottleTypeIsNavigation) { | 560 for (bool navigation : kThrottleTypeIsNavigation) { |
| 559 SCOPED_TRACE(navigation ? "Navigation test." : "Subresource test."); | 561 SCOPED_TRACE(navigation ? "Navigation test." : "Subresource test."); |
| 560 | 562 |
| 561 net::TestURLRequestContext context; | 563 net::TestURLRequestContext context; |
| 562 std::unique_ptr<net::URLRequest> request(context.CreateRequest( | 564 std::unique_ptr<net::URLRequest> request(context.CreateRequest( |
| 563 GURL(kTestCases[0].url), net::DEFAULT_PRIORITY, nullptr)); | 565 GURL(kTestCases[0].url), net::DEFAULT_PRIORITY, nullptr)); |
| 564 ResourceRequestInfo::AllocateForTesting( | 566 ResourceRequestInfo::AllocateForTesting( |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 617 // At the end, the console must contain all messages regardless of whether | 619 // At the end, the console must contain all messages regardless of whether |
| 618 // it was a navigation or a subresource request. | 620 // it was a navigation or a subresource request. |
| 619 std::string expected_output; | 621 std::string expected_output; |
| 620 for (struct TestCase& test_case : kTestCases) | 622 for (struct TestCase& test_case : kTestCases) |
| 621 expected_output += test_case.output; | 623 expected_output += test_case.output; |
| 622 EXPECT_EQ(expected_output, output_buffer); | 624 EXPECT_EQ(expected_output, output_buffer); |
| 623 } | 625 } |
| 624 } | 626 } |
| 625 | 627 |
| 626 } // namespace content | 628 } // namespace content |
| OLD | NEW |