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

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, 7 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"
10 #include "content/public/common/content_switches.h" 16 #include "content/public/common/content_switches.h"
17 #include "net/base/load_flags.h"
18 #include "net/http/http_util.h"
19 #include "net/url_request/redirect_info.h"
20 #include "net/url_request/url_request_job.h"
21 #include "net/url_request/url_request_test_util.h"
11 #include "testing/gmock/include/gmock/gmock.h" 22 #include "testing/gmock/include/gmock/gmock.h"
12 #include "testing/gtest/include/gtest/gtest.h" 23 #include "testing/gtest/include/gtest/gtest.h"
13 24
25 using ::testing::_;
26
14 namespace content { 27 namespace content {
15 28
29 using ConsoleMessagesDelegate = ClearSiteDataThrottle::ConsoleMessagesDelegate;
30
31 namespace {
32
33 const char kClearSiteDataHeaderPrefix[] = "Clear-Site-Data: ";
34
35 const char kClearCookiesHeader[] =
36 "Clear-Site-Data: { \"types\": [ \"cookies\" ] }";
37
38 // Used to verify that resource throttle delegate calls are made.
39 class MockResourceThrottleDelegate : public ResourceThrottle::Delegate {
40 public:
41 MOCK_METHOD0(Cancel, void());
42 MOCK_METHOD0(CancelAndIgnore, void());
43 MOCK_METHOD1(CancelWithError, void(int));
44 MOCK_METHOD0(Resume, void());
45 };
46
47 // A slightly modified ClearSiteDataThrottle for testing with unconditional
48 // construction, injectable response headers, and dummy clearing functionality.
49 class TestThrottle : public ClearSiteDataThrottle {
50 public:
51 TestThrottle(net::URLRequest* request,
52 std::unique_ptr<ConsoleMessagesDelegate> delegate)
53 : ClearSiteDataThrottle(request, std::move(delegate)) {}
54 ~TestThrottle() override {}
55
56 void SetResponseHeaders(const std::string& headers) {
57 std::string headers_with_status_code = "HTTP/1.1 200\n" + headers;
58 headers_ = new net::HttpResponseHeaders(net::HttpUtil::AssembleRawHeaders(
59 headers_with_status_code.c_str(), headers_with_status_code.size()));
60 }
61
62 MOCK_METHOD4(ClearSiteData,
63 void(const url::Origin& origin,
64 bool clear_cookies,
65 bool clear_storage,
66 bool clear_cache));
67
68 protected:
69 const net::HttpResponseHeaders* GetResponseHeaders() const override {
70 return headers_.get();
71 }
72
73 void ExecuteClearingTask(const url::Origin& origin,
74 bool clear_cookies,
75 bool clear_storage,
76 bool clear_cache,
77 base::OnceClosure callback) override {
78 ClearSiteData(origin, clear_cookies, clear_storage, clear_cache);
79
80 std::move(callback).Run();
81 }
82
83 private:
84 scoped_refptr<net::HttpResponseHeaders> headers_;
85 };
86
87 } // namespace
88
16 class ClearSiteDataThrottleTest : public testing::Test { 89 class ClearSiteDataThrottleTest : public testing::Test {
17 public: 90 public:
18 void SetUp() override { 91 ClearSiteDataThrottleTest()
19 base::CommandLine::ForCurrentProcess()->AppendSwitch( 92 : scoped_task_environment_(
20 switches::kEnableExperimentalWebPlatformFeatures); 93 base::test::ScopedTaskEnvironment::MainThreadType::IO) {}
21 throttle_ = ClearSiteDataThrottle::CreateThrottleForNavigation(nullptr);
22 }
23
24 ClearSiteDataThrottle* GetThrottle() {
25 return static_cast<ClearSiteDataThrottle*>(throttle_.get());
26 }
27 94
28 private: 95 private:
29 std::unique_ptr<NavigationThrottle> throttle_; 96 base::test::ScopedTaskEnvironment scoped_task_environment_;
97
98 DISALLOW_COPY_AND_ASSIGN(ClearSiteDataThrottleTest);
30 }; 99 };
31 100
32 TEST_F(ClearSiteDataThrottleTest, ParseHeader) { 101 TEST_F(ClearSiteDataThrottleTest, MaybeCreateThrottleForRequest) {
102 // Enable experimental features.
103 std::unique_ptr<base::test::ScopedCommandLine> command_line(
104 new base::test::ScopedCommandLine());
105 command_line->GetProcessCommandLine()->AppendSwitch(
106 switches::kEnableExperimentalWebPlatformFeatures);
107
108 // Create a URL request.
109 GURL url("https://www.example.com");
110 net::TestURLRequestContext context;
111 std::unique_ptr<net::URLRequest> request(
112 context.CreateRequest(url, net::DEFAULT_PRIORITY, nullptr));
113
114 // We will not create the throttle for an empty ResourceRequestInfo.
115 EXPECT_FALSE(
116 ClearSiteDataThrottle::MaybeCreateThrottleForRequest(request.get()));
117
118 // We can create the throttle for a valid ResourceRequestInfo.
119 ResourceRequestInfo::AllocateForTesting(request.get(), RESOURCE_TYPE_IMAGE,
120 nullptr, 0, 0, 0, false, true, true,
121 true, false);
122 EXPECT_TRUE(
123 ClearSiteDataThrottle::MaybeCreateThrottleForRequest(request.get()));
124
125 // But not if experimental web features are disabled again.
126 request->SetLoadFlags(net::LOAD_NORMAL);
127 command_line.reset();
128 EXPECT_FALSE(
129 ClearSiteDataThrottle::MaybeCreateThrottleForRequest(request.get()));
130 }
131
132 TEST_F(ClearSiteDataThrottleTest, ParseHeaderAndExecuteClearingTask) {
33 struct TestCase { 133 struct TestCase {
34 const char* header; 134 const char* header;
35 bool cookies; 135 bool cookies;
36 bool storage; 136 bool storage;
37 bool cache; 137 bool cache;
38 } test_cases[] = { 138 } test_cases[] = {
39 // One data type. 139 // One data type.
40 {"{ \"types\": [\"cookies\"] }", true, false, false}, 140 {"{ \"types\": [\"cookies\"] }", true, false, false},
41 {"{ \"types\": [\"storage\"] }", false, true, false}, 141 {"{ \"types\": [\"storage\"] }", false, true, false},
42 {"{ \"types\": [\"cache\"] }", false, false, true}, 142 {"{ \"types\": [\"cache\"] }", false, false, true},
(...skipping 22 matching lines...) Expand all
65 false}, 165 false},
66 166
67 // Unknown types are ignored, but we still proceed with the deletion for 167 // Unknown types are ignored, but we still proceed with the deletion for
68 // those that we recognize. 168 // those that we recognize.
69 {"{ \"types\": [\"cache\", \"foo\"] }", false, false, true}, 169 {"{ \"types\": [\"cache\", \"foo\"] }", false, false, true},
70 }; 170 };
71 171
72 for (const TestCase& test_case : test_cases) { 172 for (const TestCase& test_case : test_cases) {
73 SCOPED_TRACE(test_case.header); 173 SCOPED_TRACE(test_case.header);
74 174
175 // Test that ParseHeader works correctly.
75 bool actual_cookies; 176 bool actual_cookies;
76 bool actual_storage; 177 bool actual_storage;
77 bool actual_cache; 178 bool actual_cache;
78 179
79 std::vector<ClearSiteDataThrottle::ConsoleMessage> messages; 180 GURL url("https://example.com");
181 ConsoleMessagesDelegate console_delegate;
80 182
81 EXPECT_TRUE(GetThrottle()->ParseHeader(test_case.header, &actual_cookies, 183 EXPECT_TRUE(ClearSiteDataThrottle::ParseHeaderForTesting(
82 &actual_storage, &actual_cache, 184 test_case.header, &actual_cookies, &actual_storage, &actual_cache,
83 &messages)); 185 &console_delegate, url));
84 186
85 EXPECT_EQ(test_case.cookies, actual_cookies); 187 EXPECT_EQ(test_case.cookies, actual_cookies);
86 EXPECT_EQ(test_case.storage, actual_storage); 188 EXPECT_EQ(test_case.storage, actual_storage);
87 EXPECT_EQ(test_case.cache, actual_cache); 189 EXPECT_EQ(test_case.cache, actual_cache);
190
191 // Test that a call with the above parameters actually reaches
192 // ExecuteClearingTask().
193 net::TestURLRequestContext context;
194 std::unique_ptr<net::URLRequest> request(
195 context.CreateRequest(url, net::DEFAULT_PRIORITY, nullptr));
196 TestThrottle throttle(request.get(),
197 base::MakeUnique<ConsoleMessagesDelegate>());
198 MockResourceThrottleDelegate delegate;
199 throttle.set_delegate_for_testing(&delegate);
200 throttle.SetResponseHeaders(std::string(kClearSiteDataHeaderPrefix) +
201 test_case.header);
202
203 EXPECT_CALL(throttle, ClearSiteData(url::Origin(url), test_case.cookies,
204 test_case.storage, test_case.cache));
205 bool defer;
206 throttle.WillProcessResponse(&defer);
207 EXPECT_TRUE(defer);
208
209 testing::Mock::VerifyAndClearExpectations(&throttle);
88 } 210 }
89 } 211 }
90 212
91 TEST_F(ClearSiteDataThrottleTest, InvalidHeader) { 213 TEST_F(ClearSiteDataThrottleTest, InvalidHeader) {
92 struct TestCase { 214 struct TestCase {
93 const char* header; 215 const char* header;
94 const char* console_message; 216 const char* console_message;
95 } test_cases[] = { 217 } test_cases[] = {
96 {"", "Not a valid JSON.\n"}, 218 {"", "Not a valid JSON.\n"},
97 {"\"unclosed quote", "Not a valid JSON.\n"}, 219 {"\"unclosed quote", "Not a valid JSON.\n"},
98 {"\"some text\"", "Expecting a JSON dictionary with a 'types' field.\n"}, 220 {"\"some text\"", "Expecting a JSON dictionary with a 'types' field.\n"},
99 {"{ \"field\" : {} }", 221 {"{ \"field\" : {} }",
100 "Expecting a JSON dictionary with a 'types' field.\n"}, 222 "Expecting a JSON dictionary with a 'types' field.\n"},
101 {"{ \"types\" : [ \"passwords\" ] }", 223 {"{ \"types\" : [ \"passwords\" ] }",
102 "Invalid type: \"passwords\".\n" 224 "Unrecognized type: \"passwords\".\n"
103 "No valid types specified in the 'types' field.\n"}, 225 "No recognized types specified in the 'types' field.\n"},
104 {"{ \"types\" : [ [ \"list in a list\" ] ] }", 226 {"{ \"types\" : [ [ \"list in a list\" ] ] }",
105 "Invalid type: [\"list in a list\"].\n" 227 "Unrecognized type: [\"list in a list\"].\n"
106 "No valid types specified in the 'types' field.\n"}, 228 "No recognized types specified in the 'types' field.\n"},
107 {"{ \"types\" : [ \"кукис\", \"сторидж\", \"кэш\" ]", 229 {"{ \"types\" : [ \"кукис\", \"сторидж\", \"кэш\" ]",
108 "Must only contain ASCII characters.\n"}}; 230 "Must only contain ASCII characters.\n"}};
109 231
110 for (const TestCase& test_case : test_cases) { 232 for (const TestCase& test_case : test_cases) {
111 SCOPED_TRACE(test_case.header); 233 SCOPED_TRACE(test_case.header);
112 234
113 bool actual_cookies; 235 bool actual_cookies;
114 bool actual_storage; 236 bool actual_storage;
115 bool actual_cache; 237 bool actual_cache;
116 238
117 std::vector<ClearSiteDataThrottle::ConsoleMessage> messages; 239 ConsoleMessagesDelegate console_delegate;
118 240
119 EXPECT_FALSE(GetThrottle()->ParseHeader(test_case.header, &actual_cookies, 241 EXPECT_FALSE(ClearSiteDataThrottle::ParseHeaderForTesting(
120 &actual_storage, &actual_cache, 242 test_case.header, &actual_cookies, &actual_storage, &actual_cache,
121 &messages)); 243 &console_delegate, GURL()));
122 244
123 std::string multiline_message; 245 std::string multiline_message;
124 for (const auto& message : messages) { 246 for (const auto& message : console_delegate.messages()) {
125 EXPECT_EQ(CONSOLE_MESSAGE_LEVEL_ERROR, message.level); 247 EXPECT_EQ(CONSOLE_MESSAGE_LEVEL_ERROR, message.level);
126 multiline_message += message.text + "\n"; 248 multiline_message += message.text + "\n";
127 } 249 }
128 250
129 EXPECT_EQ(test_case.console_message, multiline_message); 251 EXPECT_EQ(test_case.console_message, multiline_message);
130 } 252 }
131 } 253 }
132 254
255 TEST_F(ClearSiteDataThrottleTest, LoadDoNotSaveCookies) {
256 net::TestURLRequestContext context;
257 std::unique_ptr<net::URLRequest> request(context.CreateRequest(
258 GURL("https://www.example.com"), net::DEFAULT_PRIORITY, nullptr));
259 std::unique_ptr<ConsoleMessagesDelegate> scoped_console_delegate(
260 new ConsoleMessagesDelegate());
261 const ConsoleMessagesDelegate* console_delegate =
262 scoped_console_delegate.get();
263 TestThrottle throttle(request.get(), std::move(scoped_console_delegate));
264 MockResourceThrottleDelegate delegate;
265 throttle.set_delegate_for_testing(&delegate);
266 throttle.SetResponseHeaders(kClearCookiesHeader);
267
268 bool defer;
269 throttle.WillProcessResponse(&defer);
270 EXPECT_TRUE(defer);
271 EXPECT_EQ(1u, console_delegate->messages().size());
272 EXPECT_EQ("Cleared datatypes: cookies.",
273 console_delegate->messages().front().text);
274 EXPECT_EQ(console_delegate->messages().front().level,
275 CONSOLE_MESSAGE_LEVEL_INFO);
276
277 request->SetLoadFlags(net::LOAD_DO_NOT_SAVE_COOKIES);
278 throttle.WillProcessResponse(&defer);
279 EXPECT_FALSE(defer);
280 EXPECT_EQ(2u, console_delegate->messages().size());
281 EXPECT_EQ(
282 "The request's credentials mode prohibits modifying cookies "
283 "and other local data.",
284 console_delegate->messages().rbegin()->text);
285 EXPECT_EQ(CONSOLE_MESSAGE_LEVEL_ERROR,
286 console_delegate->messages().rbegin()->level);
287 }
288
289 TEST_F(ClearSiteDataThrottleTest, InvalidOrigin) {
290 struct TestCase {
291 const char* origin;
292 bool expect_success;
293 std::string error_message; // Tested only if |expect_success| = false.
294 } kTestCases[] = {
295 // The throttle only works on secure origins.
296 {"https://secure-origin.com", true, ""},
297 {"filesystem:https://secure-origin.com/temporary/", true, ""},
298
299 // That includes localhost.
300 {"http://localhost", true, ""},
301
302 // Not on insecure origins.
303 {"http://insecure-origin.com", false,
304 "Not supported for insecure origins."},
305 {"filesystem:http://insecure-origin.com/temporary/", false,
306 "Not supported for insecure origins."},
307
308 // Not on unique origins.
309 {"data:unique-origin;", false, "Not supported for unique origins."},
msramek 2017/05/24 22:59:53 The improvement of the test below actually reveale
310 };
311
312 net::TestURLRequestContext context;
313
314 for (const TestCase& test_case : kTestCases) {
315 std::unique_ptr<net::URLRequest> request(context.CreateRequest(
316 GURL(test_case.origin), net::DEFAULT_PRIORITY, nullptr));
317 std::unique_ptr<ConsoleMessagesDelegate> scoped_console_delegate(
318 new ConsoleMessagesDelegate());
319 const ConsoleMessagesDelegate* console_delegate =
320 scoped_console_delegate.get();
321 TestThrottle throttle(request.get(), std::move(scoped_console_delegate));
322 MockResourceThrottleDelegate delegate;
323 throttle.set_delegate_for_testing(&delegate);
324 throttle.SetResponseHeaders(kClearCookiesHeader);
325
326 bool defer;
327 throttle.WillProcessResponse(&defer);
328
329 EXPECT_EQ(defer, test_case.expect_success);
330 EXPECT_EQ(console_delegate->messages().size(), 1u);
331 EXPECT_EQ(test_case.expect_success ? CONSOLE_MESSAGE_LEVEL_INFO
332 : CONSOLE_MESSAGE_LEVEL_ERROR,
333 console_delegate->messages().front().level);
334 if (!test_case.expect_success) {
335 EXPECT_EQ(test_case.error_message,
336 console_delegate->messages().front().text);
337 }
338 }
339 }
340
341 TEST_F(ClearSiteDataThrottleTest, DeferAndResume) {
342 enum Stage { START, REDIRECT, RESPONSE };
343
344 struct TestCase {
345 Stage stage;
346 std::string response_headers;
347 bool should_defer;
348 } kTestCases[] = {
349 // The throttle never interferes while the request is starting. Response
350 // headers are ignored, because URLRequest is not supposed to have any
351 // at this stage in the first place.
352 {START, "", false},
353 {START, kClearCookiesHeader, false},
354
355 // The throttle does not defer redirects if there are no interesting
356 // response headers.
357 {REDIRECT, "", false},
358 {REDIRECT, "Set-Cookie: abc=123;", false},
359 {REDIRECT, "Content-Type: image/png;", false},
360
361 // That includes malformed Clear-Site-Data headers or header values
362 // that do not lead to deletion.
363 {REDIRECT, "Clear-Site-Data: { types: cookies } ", false},
364 {REDIRECT, "Clear-Site-Data: { \"types\": [ \"unknown type\" ] }", false},
365
366 // However, redirects are deferred for valid Clear-Site-Data headers.
367 {REDIRECT,
368 "Clear-Site-Data: { \"types\": [ \"cookies\", \"unknown type\" ] }",
369 true},
370 {REDIRECT,
371 base::StringPrintf("Content-Type: image/png;\n%s", kClearCookiesHeader),
372 true},
373 {REDIRECT,
374 base::StringPrintf("%s\nContent-Type: image/png;", kClearCookiesHeader),
375 true},
376
377 // We expect at most one instance of the header. Multiple instances
378 // will not be parsed currently. This is not an inherent property of
379 // Clear-Site-Data, just a documentation of the current behavior.
380 {REDIRECT,
381 base::StringPrintf("%s\n%s", kClearCookiesHeader, kClearCookiesHeader),
382 false},
383
384 // Final response headers are treated the same way as in the case
385 // of redirect.
386 {REDIRECT, "Set-Cookie: abc=123;", false},
387 {REDIRECT, "Clear-Site-Data: { types: cookies } ", false},
388 {REDIRECT, kClearCookiesHeader, true},
389 };
390
391 struct TestOrigin {
392 const char* origin;
393 bool valid;
394 } kTestOrigins[] = {
395 // The throttle only works on secure origins.
396 {"https://secure-origin.com", true},
397 {"filesystem:https://secure-origin.com/temporary/", true},
398
399 // That includes localhost.
400 {"http://localhost", true},
401
402 // Not on insecure origins.
403 {"http://insecure-origin.com", false},
404 {"filesystem:http://insecure-origin.com/temporary/", false},
405
406 // Not on unique origins.
407 {"data:unique-origin;", false},
408 };
409
410 net::TestURLRequestContext context;
411
412 for (const TestOrigin& test_origin : kTestOrigins) {
413 for (const TestCase& test_case : kTestCases) {
414 SCOPED_TRACE(base::StringPrintf("Origin=%s\nStage=%d\nHeaders:\n%s",
415 test_origin.origin, test_case.stage,
416 test_case.response_headers.c_str()));
417
418 std::unique_ptr<net::URLRequest> request(context.CreateRequest(
419 GURL(test_origin.origin), net::DEFAULT_PRIORITY, nullptr));
420 TestThrottle throttle(request.get(),
421 base::MakeUnique<ConsoleMessagesDelegate>());
422 throttle.SetResponseHeaders(test_case.response_headers);
423
424 MockResourceThrottleDelegate delegate;
425 throttle.set_delegate_for_testing(&delegate);
426
427 // Whether we should defer is always conditional on the origin
428 // being valid.
429 bool expected_defer = test_case.should_defer && test_origin.valid;
430
431 // If we expect loading to be deferred, then we also expect data to be
432 // cleared and the load to eventually resume.
433 if (expected_defer) {
434 testing::Expectation expectation = EXPECT_CALL(
435 throttle,
436 ClearSiteData(url::Origin(GURL(test_origin.origin)), _, _, _));
437 EXPECT_CALL(delegate, Resume()).After(expectation);
438 } else {
439 EXPECT_CALL(throttle, ClearSiteData(_, _, _, _)).Times(0);
440 EXPECT_CALL(delegate, Resume()).Times(0);
441 }
442
443 bool actual_defer = false;
444
445 switch (test_case.stage) {
446 case START: {
447 throttle.WillStartRequest(&actual_defer);
448 break;
449 }
450 case REDIRECT: {
451 net::RedirectInfo redirect_info;
452 throttle.WillRedirectRequest(redirect_info, &actual_defer);
453 break;
454 }
455 case RESPONSE: {
456 throttle.WillProcessResponse(&actual_defer);
457 break;
458 }
459 }
460
461 EXPECT_EQ(expected_defer, actual_defer);
462 testing::Mock::VerifyAndClearExpectations(&delegate);
463 }
464 }
465 }
466
133 } // namespace content 467 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698