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