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
3 // found in the LICENSE file.
4
5 #include <algorithm>
6 #include <memory>
7
8 #include "base/bind.h"
9 #include "base/callback.h"
10 #include "base/command_line.h"
11 #include "base/run_loop.h"
12 #include "base/scoped_observer.h"
13 #include "base/stl_util.h"
14 #include "base/strings/stringprintf.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "base/synchronization/lock.h"
17 #include "base/test/bind_test_util.h"
18 #include "base/test/scoped_feature_list.h"
19 #include "base/thread_annotations.h"
20 #include "build/build_config.h"
21 #include "content/browser/browsing_data/browsing_data_browsertest_utils.h"
22 #include "content/browser/browsing_data/browsing_data_filter_builder_impl.h"
23 #include "content/public/browser/browser_context.h"
24 #include "content/public/browser/browser_task_traits.h"
25 #include "content/public/browser/browser_thread.h"
26 #include "content/public/browser/browsing_data_remover.h"
27 #include "content/public/browser/content_browser_client.h"
28 #include "content/public/browser/storage_partition.h"
29 #include "content/public/browser/storage_usage_info.h"
30 #include "content/public/browser/web_contents.h"
31 #include "content/public/common/network_service_util.h"
32 #include "content/public/test/content_browser_test.h"
33 #include "content/public/test/content_browser_test_utils.h"
34 #include "content/public/test/mock_browsing_data_remover_delegate.h"
35 #include "content/public/test/test_navigation_observer.h"
36 #include "content/shell/browser/shell.h"
37 #include "net/base/escape.h"
38 #include "net/base/net_errors.h"
39 #include "net/base/url_util.h"
40 #include "net/cookies/cookie_store.h"
41 #include "net/dns/mock_host_resolver.h"
42 #include "net/test/embedded_test_server/http_request.h"
43 #include "net/test/embedded_test_server/http_response.h"
44 #include "net/url_request/url_request_context.h"
45 #include "net/url_request/url_request_context_getter.h"
46 #include "services/service_manager/public/cpp/connector.h"
47 #include "storage/browser/quota/quota_settings.h"
48 #include "testing/gmock/include/gmock/gmock.h"
49 #include "third_party/blink/public/common/features.h"
50 #include "url/origin.h"
51 #include "url/url_constants.h"
52
53 using testing::_;
54
55 namespace content {
56
57 namespace {
58
59 // Adds a key=value pair to the url's query.
AddQuery(GURL * url,const std::string & key,const std::string & value)60 void AddQuery(GURL* url, const std::string& key, const std::string& value) {
61 *url = GURL(url->spec() + (url->has_query() ? "&" : "?") + key + "=" +
62 net::EscapeQueryParamValue(value, false));
63 }
64
65 // A helper function to synchronize with JS side of the tests. JS can append
66 // information to the loaded website's title and C++ will wait until that
67 // happens.
WaitForTitle(const Shell * shell,const char * expected_title)68 void WaitForTitle(const Shell* shell, const char* expected_title) {
69 base::string16 expected_title_16 = base::ASCIIToUTF16(expected_title);
70 TitleWatcher title_watcher(shell->web_contents(), expected_title_16);
71 ASSERT_EQ(expected_title_16, title_watcher.WaitAndGetTitle());
72 }
73
74 // A value of the Clear-Site-Data header that requests cookie deletion. Reused
75 // in tests that need a valid header but do not depend on its value.
76 static const char* kClearCookiesHeader = "\"cookies\"";
77
78 // A helper class to observe BrowsingDataRemover deletion tasks coming from
79 // ClearSiteData.
80 class TestBrowsingDataRemoverDelegate : public MockBrowsingDataRemoverDelegate {
81 public:
82 // Sets a test expectation that a Clear-Site-Data header call from |origin|,
83 // instructing to delete |cookies|, |storage|, and |cache|, will schedule
84 // the corresponding BrowsingDataRemover deletion tasks.
ExpectClearSiteDataCall(const url::Origin & origin,bool cookies,bool storage,bool cache)85 void ExpectClearSiteDataCall(const url::Origin& origin,
86 bool cookies,
87 bool storage,
88 bool cache) {
89 const int kOriginTypeMask =
90 BrowsingDataRemover::ORIGIN_TYPE_UNPROTECTED_WEB |
91 BrowsingDataRemover::ORIGIN_TYPE_PROTECTED_WEB;
92
93 if (cookies) {
94 int data_type_mask =
95 BrowsingDataRemover::DATA_TYPE_COOKIES |
96 BrowsingDataRemover::DATA_TYPE_AVOID_CLOSING_CONNECTIONS;
97
98 BrowsingDataFilterBuilderImpl filter_builder(
99 BrowsingDataFilterBuilder::WHITELIST);
100 filter_builder.AddRegisterableDomain(origin.host());
101 ExpectCall(base::Time(), base::Time::Max(), data_type_mask,
102 kOriginTypeMask, &filter_builder);
103 }
104 if (storage || cache) {
105 int data_type_mask =
106 (storage ? BrowsingDataRemover::DATA_TYPE_DOM_STORAGE : 0) |
107 (cache ? BrowsingDataRemover::DATA_TYPE_CACHE : 0);
108
109 BrowsingDataFilterBuilderImpl filter_builder(
110 BrowsingDataFilterBuilder::WHITELIST);
111 filter_builder.AddOrigin(origin);
112 ExpectCall(base::Time(), base::Time::Max(), data_type_mask,
113 kOriginTypeMask, &filter_builder);
114 }
115 }
116
117 // A shortcut for the above method, but with only cookies deleted. This is
118 // useful for most tests that use |kClearCookiesHeader|.
ExpectClearSiteDataCookiesCall(const url::Origin & origin)119 void ExpectClearSiteDataCookiesCall(const url::Origin& origin) {
120 ExpectClearSiteDataCall(origin, true, false, false);
121 }
122 };
123
124 } // namespace
125
126 class ClearSiteDataHandlerBrowserTest : public ContentBrowserTest {
127 public:
SetUpCommandLine(base::CommandLine * command_line)128 void SetUpCommandLine(base::CommandLine* command_line) override {
129 ContentBrowserTest::SetUpCommandLine(command_line);
130 browsing_data_browsertest_utils::SetIgnoreCertificateErrors(command_line);
131 }
132
SetUpOnMainThread()133 void SetUpOnMainThread() override {
134 ContentBrowserTest::SetUpOnMainThread();
135
136 BrowserContext::GetBrowsingDataRemover(browser_context())
137 ->SetEmbedderDelegate(&embedder_delegate_);
138
139 // Set up HTTP and HTTPS test servers that handle all hosts.
140 host_resolver()->AddRule("*", "127.0.0.1");
141
142 if (IsOutOfProcessNetworkService())
143 browsing_data_browsertest_utils::SetUpMockCertVerifier(net::OK);
144
145 embedded_test_server()->RegisterRequestHandler(
146 base::BindRepeating(&ClearSiteDataHandlerBrowserTest::HandleRequest,
147 base::Unretained(this)));
148 ASSERT_TRUE(embedded_test_server()->Start());
149
150 // Set up HTTPS server.
151 https_server_.reset(new net::EmbeddedTestServer(
152 net::test_server::EmbeddedTestServer::TYPE_HTTPS));
153 https_server_->SetSSLConfig(net::EmbeddedTestServer::CERT_OK);
154 https_server_->RegisterRequestHandler(
155 base::BindRepeating(&ClearSiteDataHandlerBrowserTest::HandleRequest,
156 base::Unretained(this)));
157 ASSERT_TRUE(https_server_->Start());
158 }
159
browser_context()160 BrowserContext* browser_context() {
161 return shell()->web_contents()->GetBrowserContext();
162 }
163
storage_partition()164 StoragePartition* storage_partition() {
165 return BrowserContext::GetDefaultStoragePartition(browser_context());
166 }
167
168 // Adds a cookie for the |url|. Used in the cookie integration tests.
AddCookie(const GURL & url)169 void AddCookie(const GURL& url) {
170 DCHECK_CURRENTLY_ON(BrowserThread::UI);
171 network::mojom::CookieManager* cookie_manager =
172 storage_partition()->GetCookieManagerForBrowserProcess();
173
174 std::unique_ptr<net::CanonicalCookie> cookie(net::CanonicalCookie::Create(
175 url, "A=1", base::Time::Now(), base::nullopt /* server_time */));
176
177 base::RunLoop run_loop;
178 cookie_manager->SetCanonicalCookie(
179 *cookie, url.scheme(), net::CookieOptions::MakeAllInclusive(),
180 base::BindOnce(&ClearSiteDataHandlerBrowserTest::AddCookieCallback,
181 run_loop.QuitClosure()));
182 run_loop.Run();
183 }
184
185 // Retrieves the list of all cookies. Used in the cookie integration tests.
GetCookies()186 net::CookieList GetCookies() {
187 DCHECK_CURRENTLY_ON(BrowserThread::UI);
188 network::mojom::CookieManager* cookie_manager =
189 storage_partition()->GetCookieManagerForBrowserProcess();
190 base::RunLoop run_loop;
191 net::CookieList cookie_list;
192 cookie_manager->GetAllCookies(base::BindRepeating(
193 &ClearSiteDataHandlerBrowserTest::GetCookiesCallback,
194 run_loop.QuitClosure(), base::Unretained(&cookie_list)));
195 run_loop.Run();
196 return cookie_list;
197 }
198
CreateCacheEntry(const GURL & url)199 void CreateCacheEntry(const GURL& url) {
200 ASSERT_EQ(net::OK,
201 LoadBasicRequest(storage_partition()->GetNetworkContext(), url));
202 }
203
TestCacheEntry(const GURL & url)204 bool TestCacheEntry(const GURL& url) {
205 return LoadBasicRequest(storage_partition()->GetNetworkContext(), url,
206 0 /* process_id */, 0 /* render_frame_id */,
207 net::LOAD_ONLY_FROM_CACHE) == net::OK;
208 }
209
GetURLForHTTPSHost1(const std::string & relative_url)210 GURL GetURLForHTTPSHost1(const std::string& relative_url) {
211 return https_server_->GetURL("origin1.com", relative_url);
212 }
213
GetURLForHTTPSHost2(const std::string & relative_url)214 GURL GetURLForHTTPSHost2(const std::string& relative_url) {
215 return https_server_->GetURL("origin2.com", relative_url);
216 }
217
delegate()218 TestBrowsingDataRemoverDelegate* delegate() { return &embedder_delegate_; }
219
https_server()220 net::EmbeddedTestServer* https_server() { return https_server_.get(); }
221
222 // Set a Clear-Site-Data header that |HandleRequest| will use for every
223 // following request.
SetClearSiteDataHeader(const std::string & header)224 void SetClearSiteDataHeader(const std::string& header) {
225 base::AutoLock lock(clear_site_data_header_lock_);
226 clear_site_data_header_ = header;
227 }
228
RunScriptAndGetBool(const std::string & script)229 bool RunScriptAndGetBool(const std::string& script) {
230 bool data;
231 EXPECT_TRUE(content::ExecuteScriptAndExtractBool(shell()->web_contents(),
232 script, &data));
233 return data;
234 }
235
236 private:
237 // Handles all requests.
238 //
239 // Supports the following <key>=<value> query parameters in the url:
240 // <key>="header" responds with the header "Clear-Site-Data: <value>"
241 // <key>="redirect" responds with a redirect to the url <value>
242 // <key>="html" responds with a text/html content <value>
243 // <key>="file" responds with the content of file <value>
244 //
245 // Example: "https://localhost/?header={}&redirect=example.com" will respond
246 // with headers
247 // Clear-Site-Data: {}
248 // Location: example.com
249 //
250 // Example: "https://localhost/?html=<html><head></head><body></body></html>"
251 // will respond with the header
252 // Content-Type: text/html
253 // and content
254 // <html><head></head><body></body></html>
255 //
256 // Example: "https://localhost/?file=file.html"
257 // will respond with the header
258 // Content-Type: text/html
259 // and content from the file content/test/data/file.html
HandleRequest(const net::test_server::HttpRequest & request)260 std::unique_ptr<net::test_server::HttpResponse> HandleRequest(
261 const net::test_server::HttpRequest& request) {
262 std::unique_ptr<net::test_server::BasicHttpResponse> response(
263 new net::test_server::BasicHttpResponse());
264
265 {
266 base::AutoLock lock(clear_site_data_header_lock_);
267 if (!clear_site_data_header_.empty())
268 response->AddCustomHeader("Clear-Site-Data", clear_site_data_header_);
269 }
270
271 std::string value;
272 if (net::GetValueForKeyInQuery(request.GetURL(), "header", &value))
273 response->AddCustomHeader("Clear-Site-Data", value);
274
275 if (net::GetValueForKeyInQuery(request.GetURL(), "redirect", &value)) {
276 response->set_code(net::HTTP_FOUND);
277 response->AddCustomHeader("Location", value);
278 } else {
279 response->set_code(net::HTTP_OK);
280 }
281
282 if (net::GetValueForKeyInQuery(request.GetURL(), "html", &value)) {
283 response->set_content_type("text/html");
284 response->set_content(value);
285
286 // The "html" parameter is telling the server what to serve, and the XSS
287 // auditor will complain if its |value| contains JS code. Disable that
288 // protection.
289 response->AddCustomHeader("X-XSS-Protection", "0");
290 }
291
292 browsing_data_browsertest_utils::SetResponseContent(request.GetURL(),
293 &value, response.get());
294
295 if (base::StartsWith(request.relative_url, "/cachetime",
296 base::CompareCase::SENSITIVE)) {
297 response->set_content(
298 "<html><head><title>Cache: max-age=60</title></head></html>");
299 response->set_content_type("text/html");
300 response->AddCustomHeader("Cache-Control", "max-age=60");
301 }
302
303 return std::move(response);
304 }
305
306 // Callback handler for AddCookie().
AddCookieCallback(base::OnceClosure callback,net::CanonicalCookie::CookieInclusionStatus status)307 static void AddCookieCallback(
308 base::OnceClosure callback,
309 net::CanonicalCookie::CookieInclusionStatus status) {
310 DCHECK_CURRENTLY_ON(BrowserThread::UI);
311 ASSERT_TRUE(status.IsInclude());
312 std::move(callback).Run();
313 }
314
315 // Callback handler for GetCookies().
GetCookiesCallback(base::OnceClosure callback,net::CookieList * out_cookie_list,const net::CookieList & cookie_list)316 static void GetCookiesCallback(base::OnceClosure callback,
317 net::CookieList* out_cookie_list,
318 const net::CookieList& cookie_list) {
319 DCHECK_CURRENTLY_ON(BrowserThread::UI);
320 *out_cookie_list = cookie_list;
321 std::move(callback).Run();
322 }
323
324 // If this is set, |HandleRequest| will always respond with Clear-Site-Data.
325 base::Lock clear_site_data_header_lock_;
326 std::string clear_site_data_header_ GUARDED_BY(clear_site_data_header_lock_);
327
328 std::unique_ptr<net::EmbeddedTestServer> https_server_;
329 TestBrowsingDataRemoverDelegate embedder_delegate_;
330 };
331
332 // Tests that the header is recognized on the beginning, in the middle, and on
333 // the end of a navigation redirect chain. Each of the three parts of the chain
334 // may or may not send the header, so there are 8 configurations to test.
335
336 // Crashes on Win only. https://crbug.com/741189
337 #if defined(OS_WIN)
338 #define MAYBE_RedirectNavigation DISABLED_RedirectNavigation
339 #else
340 #define MAYBE_RedirectNavigation RedirectNavigation
341 #endif
IN_PROC_BROWSER_TEST_F(ClearSiteDataHandlerBrowserTest,MAYBE_RedirectNavigation)342 IN_PROC_BROWSER_TEST_F(ClearSiteDataHandlerBrowserTest,
343 MAYBE_RedirectNavigation) {
344 GURL page_urls[3] = {
345 https_server()->GetURL("origin1.com", "/"),
346 https_server()->GetURL("origin2.com", "/foo/bar"),
347 https_server()->GetURL("origin3.com", "/index.html"),
348 };
349
350 // Iterate through the configurations. URLs whose index is matched by the mask
351 // will send the header, the others won't.
352 for (int mask = 0; mask < (1 << 3); ++mask) {
353 GURL urls[3];
354
355 // Set up the expectations.
356 for (int i = 0; i < 3; ++i) {
357 urls[i] = page_urls[i];
358 if (mask & (1 << i))
359 AddQuery(&urls[i], "header", kClearCookiesHeader);
360
361 if (mask & (1 << i))
362 delegate()->ExpectClearSiteDataCookiesCall(
363 url::Origin::Create(urls[i]));
364 }
365
366 // Set up redirects between urls 0 --> 1 --> 2.
367 AddQuery(&urls[1], "redirect", urls[2].spec());
368 AddQuery(&urls[0], "redirect", urls[1].spec());
369
370 // Navigate to the first url of the redirect chain.
371 EXPECT_TRUE(
372 NavigateToURL(shell(), urls[0], urls[2] /* expected_commit_url */));
373
374 // We reached the end of the redirect chain.
375 EXPECT_EQ(urls[2], shell()->web_contents()->GetURL());
376
377 delegate()->VerifyAndClearExpectations();
378 }
379 }
380
381 // Tests that the header is recognized on the beginning, in the middle, and on
382 // the end of a resource load redirect chain. Each of the three parts of the
383 // chain may or may not send the header, so there are 8 configurations to test.
384
385 // Crashes on Win only. https://crbug.com/741189
386 #if defined(OS_WIN)
387 #define MAYBE_RedirectResourceLoad DISABLED_RedirectResourceLoad
388 #else
389 #define MAYBE_RedirectResourceLoad RedirectResourceLoad
390 #endif
IN_PROC_BROWSER_TEST_F(ClearSiteDataHandlerBrowserTest,MAYBE_RedirectResourceLoad)391 IN_PROC_BROWSER_TEST_F(ClearSiteDataHandlerBrowserTest,
392 MAYBE_RedirectResourceLoad) {
393 GURL resource_urls[3] = {
394 https_server()->GetURL("origin1.com", "/redirect-start"),
395 https_server()->GetURL("origin2.com", "/redirect-middle"),
396 https_server()->GetURL("origin3.com", "/redirect-end"),
397 };
398
399 // Iterate through the configurations. URLs whose index is matched by the mask
400 // will send the header, the others won't.
401 for (int mask = 0; mask < (1 << 3); ++mask) {
402 GURL urls[3];
403
404 // Set up the expectations.
405 for (int i = 0; i < 3; ++i) {
406 urls[i] = resource_urls[i];
407 if (mask & (1 << i))
408 AddQuery(&urls[i], "header", kClearCookiesHeader);
409
410 if (mask & (1 << i))
411 delegate()->ExpectClearSiteDataCookiesCall(
412 url::Origin::Create(urls[i]));
413 }
414
415 // Set up redirects between urls 0 --> 1 --> 2.
416 AddQuery(&urls[1], "redirect", urls[2].spec());
417 AddQuery(&urls[0], "redirect", urls[1].spec());
418
419 // Navigate to a page that embeds "https://origin1.com/image.png"
420 // and observe the loading of that resource.
421 GURL page_with_image = https_server()->GetURL("origin4.com", "/index.html");
422 std::string content_with_image =
423 "<html><head></head><body>"
424 "<img src=\"" +
425 urls[0].spec() +
426 "\" />"
427 "</body></html>";
428 AddQuery(&page_with_image, "html", content_with_image);
429 EXPECT_TRUE(NavigateToURL(shell(), page_with_image));
430
431 delegate()->VerifyAndClearExpectations();
432 }
433 }
434
435 // Tests that the Clear-Site-Data header is ignored for insecure origins.
IN_PROC_BROWSER_TEST_F(ClearSiteDataHandlerBrowserTest,InsecureNavigation)436 IN_PROC_BROWSER_TEST_F(ClearSiteDataHandlerBrowserTest, InsecureNavigation) {
437 // ClearSiteData() should not be called on HTTP.
438 GURL url = embedded_test_server()->GetURL("example.com", "/");
439 AddQuery(&url, "header", kClearCookiesHeader);
440 ASSERT_FALSE(url.SchemeIsCryptographic());
441
442 EXPECT_TRUE(NavigateToURL(shell(), url));
443
444 // We do not expect any calls to have been made.
445 delegate()->VerifyAndClearExpectations();
446 }
447
448 class ClearSiteDataHandlerBrowserTestWithAutoupgradesDisabled
449 : public ClearSiteDataHandlerBrowserTest {
450 public:
SetUpCommandLine(base::CommandLine * command_line)451 void SetUpCommandLine(base::CommandLine* command_line) override {
452 ClearSiteDataHandlerBrowserTest::SetUpCommandLine(command_line);
453 feature_list.InitAndDisableFeature(
454 blink::features::kMixedContentAutoupgrade);
455 }
456
457 private:
458 base::test::ScopedFeatureList feature_list;
459 };
460
461 // Tests that the Clear-Site-Data header is honored for secure resource loads
462 // and ignored for insecure ones.
IN_PROC_BROWSER_TEST_F(ClearSiteDataHandlerBrowserTestWithAutoupgradesDisabled,SecureAndInsecureResourceLoad)463 IN_PROC_BROWSER_TEST_F(ClearSiteDataHandlerBrowserTestWithAutoupgradesDisabled,
464 SecureAndInsecureResourceLoad) {
465 GURL insecure_image =
466 embedded_test_server()->GetURL("example.com", "/image.png");
467 GURL secure_image = https_server()->GetURL("example.com", "/image.png");
468
469 ASSERT_TRUE(secure_image.SchemeIsCryptographic());
470 ASSERT_FALSE(insecure_image.SchemeIsCryptographic());
471
472 AddQuery(&secure_image, "header", kClearCookiesHeader);
473 AddQuery(&insecure_image, "header", kClearCookiesHeader);
474
475 std::string content_with_insecure_image =
476 "<html><head></head><body>"
477 "<img src=\"" +
478 insecure_image.spec() +
479 "\" />"
480 "</body></html>";
481
482 std::string content_with_secure_image =
483 "<html><head></head><body>"
484 "<img src=\"" +
485 secure_image.spec() +
486 "\" />"
487 "</body></html>";
488
489 // Test insecure resources.
490 GURL insecure_page = embedded_test_server()->GetURL("example.com", "/");
491 GURL secure_page = https_server()->GetURL("example.com", "/");
492
493 AddQuery(&insecure_page, "html", content_with_insecure_image);
494 AddQuery(&secure_page, "html", content_with_insecure_image);
495
496 // Insecure resource on an insecure page does not execute Clear-Site-Data.
497 EXPECT_TRUE(NavigateToURL(shell(), insecure_page));
498
499 // Insecure resource on a secure page does not execute Clear-Site-Data.
500 EXPECT_TRUE(NavigateToURL(shell(), secure_page));
501
502 // We do not expect any calls to have been made.
503 delegate()->VerifyAndClearExpectations();
504
505 // Test secure resources.
506 insecure_page = embedded_test_server()->GetURL("example.com", "/");
507 secure_page = https_server()->GetURL("example.com", "/");
508
509 AddQuery(&insecure_page, "html", content_with_secure_image);
510 AddQuery(&secure_page, "html", content_with_secure_image);
511
512 // Secure resource on an insecure page does execute Clear-Site-Data.
513 delegate()->ExpectClearSiteDataCookiesCall(url::Origin::Create(secure_image));
514
515 EXPECT_TRUE(NavigateToURL(shell(), secure_page));
516 delegate()->VerifyAndClearExpectations();
517
518 // Secure resource on a secure page does execute Clear-Site-Data.
519 delegate()->ExpectClearSiteDataCookiesCall(url::Origin::Create(secure_image));
520
521 EXPECT_TRUE(NavigateToURL(shell(), secure_page));
522 delegate()->VerifyAndClearExpectations();
523 }
524
525 // Tests that the Clear-Site-Data header is ignored for service worker resource
526 // loads.
IN_PROC_BROWSER_TEST_F(ClearSiteDataHandlerBrowserTest,ServiceWorker)527 IN_PROC_BROWSER_TEST_F(ClearSiteDataHandlerBrowserTest, ServiceWorker) {
528 GURL origin1 = https_server()->GetURL("origin1.com", "/");
529 GURL origin2 = https_server()->GetURL("origin2.com", "/");
530 GURL origin3 = https_server()->GetURL("origin3.com", "/");
531 GURL origin4 = https_server()->GetURL("origin4.com", "/");
532
533 // Navigation to worker_setup.html will install a service worker. Since
534 // the installation is asynchronous, the JS side will inform us about it in
535 // the page title.
536 GURL url = origin1;
537 AddQuery(&url, "file", "worker_setup.html");
538 EXPECT_TRUE(NavigateToURL(shell(), url));
539 WaitForTitle(shell(), "service worker is ready");
540
541 // The service worker will now serve a page containing several images, which
542 // the browser will try to fetch. The service worker will be instructed
543 // to handle some of the fetches itself, while others will be handled by
544 // the testing server. The setup is the following:
545 //
546 // origin1.com/resource (-> server; should respect header)
547 // origin2.com/resource_from_sw (-> service worker; should not respect header)
548 // origin3.com/resource_from_sw (-> service worker; should not respect header)
549 // origin4.com/resource (-> server; should respect header)
550 // origin1.com/resource_from_sw (-> service worker; should not respect header)
551 // origin2.com/resource (-> server; should respect header)
552 // origin3.com/resource_from_sw (-> service worker; should not respect header)
553 // origin4.com/resource (-> server; should respect header)
554 //
555 // |origin1| and |origin2| are used to test that there is no difference
556 // between same-origin and third-party fetches. Clear-Site-Data should be
557 // called once for each of these origins - caused by the "/resource" fetch,
558 // but not by the "/resource_from_sw" fetch. |origin3| and |origin4| prove
559 // that the number of calls is dependent on the number of network responses,
560 // i.e. that it isn't always 1 as in the case of |origin1| and |origin2|.
561 delegate()->ExpectClearSiteDataCookiesCall(url::Origin::Create(origin1));
562 delegate()->ExpectClearSiteDataCookiesCall(url::Origin::Create(origin4));
563 delegate()->ExpectClearSiteDataCookiesCall(url::Origin::Create(origin2));
564 delegate()->ExpectClearSiteDataCookiesCall(url::Origin::Create(origin4));
565
566 url = https_server()->GetURL("origin1.com", "/anything-in-workers-scope");
567 AddQuery(&url, "origin1", origin1.spec());
568 AddQuery(&url, "origin2", origin2.spec());
569 AddQuery(&url, "origin3", origin3.spec());
570 AddQuery(&url, "origin4", origin4.spec());
571 EXPECT_TRUE(NavigateToURL(shell(), url));
572 WaitForTitle(shell(), "done");
573 delegate()->VerifyAndClearExpectations();
574 }
575
576 // Tests that Clear-Site-Data is only executed on a resource fetch
577 // if credentials are allowed in that fetch.
578
579 // Crashes on Win only. https://crbug.com/741189
580 #if defined(OS_WIN)
581 #define MAYBE_Credentials DISABLED_Credentials
582 #else
583 #define MAYBE_Credentials Credentials
584 #endif
IN_PROC_BROWSER_TEST_F(ClearSiteDataHandlerBrowserTest,MAYBE_Credentials)585 IN_PROC_BROWSER_TEST_F(ClearSiteDataHandlerBrowserTest, MAYBE_Credentials) {
586 GURL page_template = https_server()->GetURL("origin1.com", "/");
587 GURL same_origin_resource =
588 https_server()->GetURL("origin1.com", "/resource");
589 GURL different_origin_resource =
590 https_server()->GetURL("origin2.com", "/resource");
591
592 AddQuery(&same_origin_resource, "header", kClearCookiesHeader);
593 AddQuery(&different_origin_resource, "header", kClearCookiesHeader);
594
595 const struct TestCase {
596 bool same_origin;
597 std::string credentials;
598 bool should_run;
599 } kTestCases[] = {
600 {true, "", true},
601 {true, "omit", false},
602 {true, "same-origin", true},
603 {true, "include", true},
604 {false, "", false},
605 {false, "omit", false},
606 {false, "same-origin", false},
607 {false, "include", true},
608 };
609
610 for (const TestCase& test_case : kTestCases) {
611 const GURL& resource = test_case.same_origin ? same_origin_resource
612 : different_origin_resource;
613 std::string credentials =
614 test_case.credentials.empty()
615 ? ""
616 : "credentials: '" + test_case.credentials + "'";
617
618 // Fetch a resource. Note that the script also handles fetch() error which
619 // might be thrown for third-party fetches because of missing
620 // Access-Control-Allow-Origin. However, that only affects the visibility
621 // of the response; the header will still be processed.
622 std::string content = base::StringPrintf(
623 "<html><head></head><body><script>"
624 "fetch('%s', {%s})"
625 ".then(function() { document.title = 'done'; })"
626 ".catch(function() { document.title = 'done'; })"
627 "</script></body></html>",
628 resource.spec().c_str(), credentials.c_str());
629
630 GURL page = page_template;
631 AddQuery(&page, "html", content);
632
633 if (test_case.should_run)
634 delegate()->ExpectClearSiteDataCookiesCall(url::Origin::Create(resource));
635
636 EXPECT_TRUE(NavigateToURL(shell(), page));
637 WaitForTitle(shell(), "done");
638 delegate()->VerifyAndClearExpectations();
639 }
640 }
641
642 // Tests that the credentials flag is correctly taken into account when it
643 // interpretation changes after redirect.
IN_PROC_BROWSER_TEST_F(ClearSiteDataHandlerBrowserTest,CredentialsOnRedirect)644 IN_PROC_BROWSER_TEST_F(ClearSiteDataHandlerBrowserTest, CredentialsOnRedirect) {
645 GURL urls[2] = {
646 https_server()->GetURL("origin1.com", "/image.png"),
647 https_server()->GetURL("origin2.com", "/image.png"),
648 };
649
650 AddQuery(&urls[0], "header", kClearCookiesHeader);
651 AddQuery(&urls[1], "header", kClearCookiesHeader);
652
653 AddQuery(&urls[0], "redirect", urls[1].spec());
654
655 // Fetch a resource on origin1.com, which will redirect to origin2.com.
656 // Both URLs will respond with Clear-Site-Data. Since the credentials mode is
657 // 'same-origin', the LOAD_DO_NOT_SAVE_COOKIES flag will be set while
658 // processing the response from origin1.com, but not while processing the
659 // response from origin2.com. Therefore, the deletion will only be executed
660 // for origin1.com.
661 //
662 // Note that the script also handles fetch() error which might be thrown for
663 // third-party fetches because of missing Access-Control-Allow-Origin.
664 // However, that only affects the visibility of the response; the header will
665 // still be processed.
666 std::string content = base::StringPrintf(
667 "<html><head></head><body><script>"
668 "fetch('%s', {'credentials': 'same-origin'})"
669 ".then(function() { document.title = 'done'; })"
670 ".catch(function() { document.title = 'done'; })"
671 "</script></body></html>",
672 urls[0].spec().c_str());
673
674 delegate()->ExpectClearSiteDataCookiesCall(url::Origin::Create(urls[0]));
675
676 GURL page = https_server()->GetURL("origin1.com", "/");
677 AddQuery(&page, "html", content);
678
679 EXPECT_TRUE(NavigateToURL(shell(), page));
680 WaitForTitle(shell(), "done");
681 delegate()->VerifyAndClearExpectations();
682 }
683
684 // Tests that ClearSiteData() is called for the correct data types.
IN_PROC_BROWSER_TEST_F(ClearSiteDataHandlerBrowserTest,Types)685 IN_PROC_BROWSER_TEST_F(ClearSiteDataHandlerBrowserTest, Types) {
686 GURL base_url = https_server()->GetURL("example.com", "/");
687
688 const struct TestCase {
689 const char* value;
690 bool remove_cookies;
691 bool remove_storage;
692 bool remove_cache;
693 } kTestCases[] = {
694 {"\"cookies\"", true, false, false},
695 {"\"storage\"", false, true, false},
696 {"\"cache\"", false, false, true},
697 {"\"cookies\", \"storage\"", true, true, false},
698 {"\"cookies\", \"cache\"", true, false, true},
699 {"\"storage\", \"cache\"", false, true, true},
700 {"\"cookies\", \"storage\", \"cache\"", true, true, true},
701 };
702
703 for (const TestCase& test_case : kTestCases) {
704 GURL url = base_url;
705 AddQuery(&url, "header", test_case.value);
706
707 delegate()->ExpectClearSiteDataCall(
708 url::Origin::Create(url), test_case.remove_cookies,
709 test_case.remove_storage, test_case.remove_cache);
710
711 EXPECT_TRUE(NavigateToURL(shell(), url));
712
713 delegate()->VerifyAndClearExpectations();
714 }
715 }
716
717 // Integration test for the deletion of cookies.
IN_PROC_BROWSER_TEST_F(ClearSiteDataHandlerBrowserTest,CookiesIntegrationTest)718 IN_PROC_BROWSER_TEST_F(ClearSiteDataHandlerBrowserTest,
719 CookiesIntegrationTest) {
720 AddCookie(https_server()->GetURL("origin1.com", "/abc"));
721 AddCookie(https_server()->GetURL("subdomain.origin1.com", "/"));
722 AddCookie(https_server()->GetURL("origin2.com", "/def"));
723 AddCookie(https_server()->GetURL("subdomain.origin2.com", "/"));
724
725 // There are four cookies on two eTLD+1s.
726 net::CookieList cookies = GetCookies();
727 EXPECT_EQ(4u, cookies.size());
728
729 // Let Clear-Site-Data delete the "cookies" of "origin1.com".
730 GURL url = https_server()->GetURL("origin1.com", "/clear-site-data");
731 AddQuery(&url, "header", kClearCookiesHeader);
732 EXPECT_TRUE(NavigateToURL(shell(), url));
733
734 // Only the "origin2.com" eTLD now has cookies.
735 cookies = GetCookies();
736 ASSERT_EQ(2u, cookies.size());
737 EXPECT_EQ(cookies[0].Domain(), "origin2.com");
738 EXPECT_EQ(cookies[1].Domain(), "subdomain.origin2.com");
739 }
740
741 // Integration test for the unregistering of service workers.
IN_PROC_BROWSER_TEST_F(ClearSiteDataHandlerBrowserTest,StorageServiceWorkersIntegrationTest)742 IN_PROC_BROWSER_TEST_F(ClearSiteDataHandlerBrowserTest,
743 StorageServiceWorkersIntegrationTest) {
744 StoragePartition* partition = storage_partition();
745 net::EmbeddedTestServer* server = https_server();
746
747 browsing_data_browsertest_utils::AddServiceWorker("origin1.com", partition,
748 server);
749 browsing_data_browsertest_utils::AddServiceWorker("origin2.com", partition,
750 server);
751
752 // There are two service workers installed on two origins.
753 std::vector<StorageUsageInfo> service_workers =
754 browsing_data_browsertest_utils::GetServiceWorkers(partition);
755 EXPECT_EQ(2u, service_workers.size());
756
757 // Navigate to a URL within the scope of "origin1.com" which responds with
758 // a Clear-Site-Data header. Verify that this did NOT remove the service
759 // worker for "origin1.com", as the header would not be respected outside
760 // of the scope.
761 GURL url = server->GetURL("origin1.com", "/anything-in-the-scope");
762 AddQuery(&url, "header", "\"storage\"");
763 EXPECT_TRUE(NavigateToURL(shell(), url));
764 service_workers =
765 browsing_data_browsertest_utils::GetServiceWorkers(partition);
766 EXPECT_EQ(2u, service_workers.size());
767
768 // This time, we will navigate to a URL on "origin1.com" that is not handled
769 // by the serice worker, but results in a network request. One such resource
770 // not handled by "worker.js" is the path "resource".
771 // The header will be respected and the worker deleted.
772 url = server->GetURL("origin1.com", "/resource");
773 AddQuery(&url, "header", "\"storage\"");
774 EXPECT_TRUE(NavigateToURL(shell(), url));
775
776 // Only "origin2.com" now has a service worker.
777 service_workers =
778 browsing_data_browsertest_utils::GetServiceWorkers(partition);
779 ASSERT_EQ(1u, service_workers.size());
780 EXPECT_EQ(service_workers[0].origin.GetURL(),
781 server->GetURL("origin2.com", "/"));
782
783 // TODO(msramek): Test that the service worker update ping also deletes
784 // the service worker.
785 }
786
787 // TODO(msramek): Add integration tests for other storage data types, such as
788 // local storage, indexed DB, etc.
789
790 // Disabled due to flakiness. See https://crbug.com/894572.
791 // Integration test for the deletion of cache entries.
IN_PROC_BROWSER_TEST_F(ClearSiteDataHandlerBrowserTest,DISABLED_CacheIntegrationTest)792 IN_PROC_BROWSER_TEST_F(ClearSiteDataHandlerBrowserTest,
793 DISABLED_CacheIntegrationTest) {
794 GURL url1 = GetURLForHTTPSHost1("/cachetime/foo");
795 GURL url2 = GetURLForHTTPSHost1("/cachetime/bar");
796 GURL url3 = GetURLForHTTPSHost2("/cachetime/foo");
797 GURL url4 = GetURLForHTTPSHost2("/cachetime/bar");
798
799 // Load the url to create cache entries.
800 CreateCacheEntry(url1);
801 CreateCacheEntry(url2);
802 CreateCacheEntry(url3);
803 CreateCacheEntry(url4);
804
805 // There are four cache entries on two origins.
806 EXPECT_TRUE(TestCacheEntry(url1));
807 EXPECT_TRUE(TestCacheEntry(url2));
808 EXPECT_TRUE(TestCacheEntry(url3));
809 EXPECT_TRUE(TestCacheEntry(url4));
810
811 // Let Clear-Site-Data delete the "cache" of HTTPS host 2.
812 GURL url = GetURLForHTTPSHost2("/clear-site-data");
813 AddQuery(&url, "header", "\"cache\"");
814 EXPECT_TRUE(NavigateToURL(shell(), url));
815
816 // Only HTTPS host 1 now has cache entries.
817 EXPECT_TRUE(TestCacheEntry(url1));
818 EXPECT_TRUE(TestCacheEntry(url2));
819 EXPECT_FALSE(TestCacheEntry(url3));
820 EXPECT_FALSE(TestCacheEntry(url4));
821 }
822
823 // Tests that closing the tab right after executing Clear-Site-Data does
824 // not crash.
IN_PROC_BROWSER_TEST_F(ClearSiteDataHandlerBrowserTest,ClosedTab)825 IN_PROC_BROWSER_TEST_F(ClearSiteDataHandlerBrowserTest, ClosedTab) {
826 GURL url = https_server()->GetURL("example.com", "/");
827 AddQuery(&url, "header", kClearCookiesHeader);
828 shell()->LoadURL(url);
829 shell()->Close();
830 }
831
832 // Tests that sending Clear-Site-Data during a service worker installation
833 // doesn't fail. (see crbug.com/898465)
IN_PROC_BROWSER_TEST_F(ClearSiteDataHandlerBrowserTest,ClearSiteDataDuringServiceWorkerInstall)834 IN_PROC_BROWSER_TEST_F(ClearSiteDataHandlerBrowserTest,
835 ClearSiteDataDuringServiceWorkerInstall) {
836 GURL url = embedded_test_server()->GetURL("127.0.0.1", "/");
837 AddQuery(&url, "file", "worker_test.html");
838 EXPECT_TRUE(NavigateToURL(shell(), url));
839 delegate()->ExpectClearSiteDataCall(url::Origin::Create(url), false, true,
840 false);
841 SetClearSiteDataHeader("\"storage\"");
842 EXPECT_TRUE(RunScriptAndGetBool("installServiceWorker()"));
843 delegate()->VerifyAndClearExpectations();
844 EXPECT_TRUE(RunScriptAndGetBool("hasServiceWorker()"));
845 }
846
847 // Tests that sending Clear-Site-Data during a service worker update
848 // removes the service worker. (see crbug.com/898465)
IN_PROC_BROWSER_TEST_F(ClearSiteDataHandlerBrowserTest,ClearSiteDataDuringServiceWorkerUpdate)849 IN_PROC_BROWSER_TEST_F(ClearSiteDataHandlerBrowserTest,
850 ClearSiteDataDuringServiceWorkerUpdate) {
851 GURL url = embedded_test_server()->GetURL("127.0.0.1", "/");
852 AddQuery(&url, "file", "worker_test.html");
853 EXPECT_TRUE(NavigateToURL(shell(), url));
854 // Install a service worker.
855 EXPECT_TRUE(RunScriptAndGetBool("installServiceWorker()"));
856 delegate()->VerifyAndClearExpectations();
857 // Update the service worker and send C-S-D during update.
858 delegate()->ExpectClearSiteDataCall(url::Origin::Create(url), false, true,
859 false);
860
861 base::RunLoop loop;
862 auto* remover = BrowserContext::GetBrowsingDataRemover(browser_context());
863 remover->SetWouldCompleteCallbackForTesting(
864 base::BindLambdaForTesting([&](base::OnceClosure callback) {
865 std::move(callback).Run();
866 loop.Quit();
867 }));
868
869 SetClearSiteDataHeader("\"storage\"");
870 // Expect the update to fail and the service worker to be removed.
871 EXPECT_FALSE(RunScriptAndGetBool("updateServiceWorker()"));
872 delegate()->VerifyAndClearExpectations();
873 loop.Run();
874
875 // Notify crbug.com/912313 if the test fails here again.
876 EXPECT_FALSE(RunScriptAndGetBool("hasServiceWorker()"));
877 }
878
879 } // namespace content
880