| Index: content/browser/browsing_data/clear_site_data_throttle_browsertest.cc
|
| diff --git a/content/browser/browsing_data/clear_site_data_throttle_browsertest.cc b/content/browser/browsing_data/clear_site_data_throttle_browsertest.cc
|
| index f5e43643bc67802e5fd264bf70fd51cd243394c3..212b0ad8fa53128ce196b2948a36f1e781056d0f 100644
|
| --- a/content/browser/browsing_data/clear_site_data_throttle_browsertest.cc
|
| +++ b/content/browser/browsing_data/clear_site_data_throttle_browsertest.cc
|
| @@ -9,9 +9,12 @@
|
| #include "base/bind.h"
|
| #include "base/callback.h"
|
| #include "base/command_line.h"
|
| +#include "base/strings/stringprintf.h"
|
| +#include "base/strings/utf_string_conversions.h"
|
| #include "content/public/browser/content_browser_client.h"
|
| #include "content/public/browser/web_contents.h"
|
| #include "content/public/common/content_switches.h"
|
| +#include "content/public/test/browser_test_utils.h"
|
| #include "content/public/test/content_browser_test.h"
|
| #include "content/public/test/content_browser_test_utils.h"
|
| #include "content/public/test/test_navigation_observer.h"
|
| @@ -64,6 +67,15 @@ void AddQuery(GURL* url, const std::string& key, const std::string& value) {
|
| net::EscapeQueryParamValue(value, false));
|
| }
|
|
|
| +// A helper function to synchronize with JS side of the tests. JS can append
|
| +// information to the loaded website's title and C++ will wait until that
|
| +// happens.
|
| +void WaitForTitle(const Shell* shell, const char* expected_title) {
|
| + base::string16 expected_title_16 = base::ASCIIToUTF16(expected_title);
|
| + TitleWatcher title_watcher(shell->web_contents(), expected_title_16);
|
| + ASSERT_EQ(expected_title_16, title_watcher.WaitAndGetTitle());
|
| +}
|
| +
|
| // A value of the Clear-Site-Data header that requests cookie deletion. Reused
|
| // in tests that need a valid header but do not depend on its value.
|
| static const char* kClearCookiesHeader = "{ \"types\": [ \"cookies\" ] }";
|
| @@ -109,15 +121,29 @@ class ClearSiteDataThrottleBrowserTest : public ContentBrowserTest {
|
| net::EmbeddedTestServer* https_server() { return https_server_.get(); }
|
|
|
| private:
|
| - // Handles all requests. If the request url query contains a "header" key,
|
| - // responds with the "Clear-Site-Data" header of the corresponding value.
|
| - // If the query contains a "redirect" key, responds with a redirect to a url
|
| - // given by the corresponding value.
|
| + // Handles all requests.
|
| + //
|
| + // Supports the following <key>=<value> query parameters in the url:
|
| + // <key>="header" responds with the header "Clear-Site-Data: <value>"
|
| + // <key>="redirect" responds with a redirect to the url <value>
|
| + // <key>="html" responds with a text/html content <value>
|
| + // <key>="file" responds with the content of file <value>
|
| //
|
| // Example: "https://localhost/?header={}&redirect=example.com" will respond
|
| // with headers
|
| // Clear-Site-Data: {}
|
| // Location: example.com
|
| + //
|
| + // Example: "https://localhost/?html=<html><head></head><body></body></html>"
|
| + // will respond with the header
|
| + // Content-Type: text/html
|
| + // and content
|
| + // <html><head></head><body></body></html>
|
| + //
|
| + // Example: "https://localhost/?file=file.html
|
| + // will respond with the header
|
| + // Content-Type: text/html
|
| + // and content from the file content/test/data/file.html
|
| std::unique_ptr<net::test_server::HttpResponse> HandleRequest(
|
| const net::test_server::HttpRequest& request) {
|
| std::unique_ptr<net::test_server::BasicHttpResponse> response(
|
| @@ -134,6 +160,36 @@ class ClearSiteDataThrottleBrowserTest : public ContentBrowserTest {
|
| response->set_code(net::HTTP_OK);
|
| }
|
|
|
| + if (net::GetValueForKeyInQuery(request.GetURL(), "html", &value)) {
|
| + response->set_content_type("text/html");
|
| + response->set_content(value);
|
| +
|
| + // The "html" parameter is telling the server what to serve, and the XSS
|
| + // auditor will complain if its |value| contains JS code. Disable that
|
| + // protection.
|
| + response->AddCustomHeader("X-XSS-Protection", "0");
|
| + }
|
| +
|
| + if (net::GetValueForKeyInQuery(request.GetURL(), "file", &value)) {
|
| + base::FilePath path(GetTestFilePath("browsing_data", value.c_str()));
|
| + base::File file(path, base::File::FLAG_OPEN | base::File::FLAG_READ);
|
| + EXPECT_TRUE(file.IsValid());
|
| + int64_t length = file.GetLength();
|
| + EXPECT_GE(length, 0);
|
| + std::unique_ptr<char[]> buffer(new char[length + 1]);
|
| + file.Read(0, buffer.get(), length);
|
| + buffer[length] = '\0';
|
| +
|
| + if (path.Extension() == FILE_PATH_LITERAL(".js"))
|
| + response->set_content_type("application/javascript");
|
| + else if (path.Extension() == FILE_PATH_LITERAL(".html"))
|
| + response->set_content_type("text/html");
|
| + else
|
| + NOTREACHED();
|
| +
|
| + response->set_content(buffer.get());
|
| + }
|
| +
|
| return std::move(response);
|
| }
|
|
|
| @@ -142,9 +198,9 @@ class ClearSiteDataThrottleBrowserTest : public ContentBrowserTest {
|
| };
|
|
|
| // Tests that the header is recognized on the beginning, in the middle, and on
|
| -// the end of a redirect chain. Each of the three parts of the chain may or
|
| -// may not send the header, so there are 8 configurations to test.
|
| -IN_PROC_BROWSER_TEST_F(ClearSiteDataThrottleBrowserTest, Redirect) {
|
| +// the end of a navigation redirect chain. Each of the three parts of the chain
|
| +// may or may not send the header, so there are 8 configurations to test.
|
| +IN_PROC_BROWSER_TEST_F(ClearSiteDataThrottleBrowserTest, RedirectNavigation) {
|
| GURL base_urls[3] = {
|
| https_server()->GetURL("origin1.com", "/"),
|
| https_server()->GetURL("origin2.com", "/foo/bar"),
|
| @@ -182,8 +238,55 @@ IN_PROC_BROWSER_TEST_F(ClearSiteDataThrottleBrowserTest, Redirect) {
|
| }
|
| }
|
|
|
| +// Tests that the header is recognized on the beginning, in the middle, and on
|
| +// the end of a resource load redirect chain. Each of the three parts of the
|
| +// chain may or may not send the header, so there are 8 configurations to test.
|
| +IN_PROC_BROWSER_TEST_F(ClearSiteDataThrottleBrowserTest, RedirectResourceLoad) {
|
| + GURL base_urls[3] = {
|
| + https_server()->GetURL("origin1.com", "/image.png"),
|
| + https_server()->GetURL("origin2.com", "/redirected-image.png"),
|
| + https_server()->GetURL("origin3.com", "/actual-image.png"),
|
| + };
|
| +
|
| + // Iterate through the configurations. URLs whose index is matched by the mask
|
| + // will send the header, the others won't.
|
| + for (int mask = 0; mask < (1 << 3); ++mask) {
|
| + GURL urls[3];
|
| +
|
| + // Set up the expectations.
|
| + for (int i = 0; i < 3; ++i) {
|
| + urls[i] = base_urls[i];
|
| + if (mask & (1 << i))
|
| + AddQuery(&urls[i], "header", kClearCookiesHeader);
|
| +
|
| + EXPECT_CALL(*GetContentBrowserClient(),
|
| + ClearSiteData(shell()->web_contents()->GetBrowserContext(),
|
| + url::Origin(urls[i]), _, _, _, _))
|
| + .Times((mask & (1 << i)) ? 1 : 0);
|
| + }
|
| +
|
| + // Set up redirects between urls 0 --> 1 --> 2.
|
| + AddQuery(&urls[1], "redirect", urls[2].spec());
|
| + AddQuery(&urls[0], "redirect", urls[1].spec());
|
| +
|
| + // Navigate to a page that embeds "https://origin1.com/image.png"
|
| + // and observe the loading of that resource.
|
| + GURL page_with_image = https_server()->GetURL("origin4.com", "/index.html");
|
| + std::string content_with_image =
|
| + "<html><head></head><body>"
|
| + "<img src=\"" +
|
| + urls[0].spec() +
|
| + "\" />"
|
| + "</body></html>";
|
| + AddQuery(&page_with_image, "html", content_with_image);
|
| + NavigateToURL(shell(), page_with_image);
|
| +
|
| + testing::Mock::VerifyAndClearExpectations(GetContentBrowserClient());
|
| + }
|
| +}
|
| +
|
| // Tests that the Clear-Site-Data header is ignored for insecure origins.
|
| -IN_PROC_BROWSER_TEST_F(ClearSiteDataThrottleBrowserTest, Insecure) {
|
| +IN_PROC_BROWSER_TEST_F(ClearSiteDataThrottleBrowserTest, InsecureNavigation) {
|
| // ClearSiteData() should not be called on HTTP.
|
| GURL url = embedded_test_server()->GetURL("example.com", "/");
|
| AddQuery(&url, "header", kClearCookiesHeader);
|
| @@ -195,6 +298,184 @@ IN_PROC_BROWSER_TEST_F(ClearSiteDataThrottleBrowserTest, Insecure) {
|
| NavigateToURL(shell(), url);
|
| }
|
|
|
| +// Tests that the Clear-Site-Data header is honored for secure resource loads
|
| +// and ignored for insecure ones.
|
| +IN_PROC_BROWSER_TEST_F(ClearSiteDataThrottleBrowserTest,
|
| + SecureAndInsecureResourceLoad) {
|
| + GURL insecure_image =
|
| + embedded_test_server()->GetURL("example.com", "/image.png");
|
| + GURL secure_image = https_server()->GetURL("example.com", "/image.png");
|
| +
|
| + ASSERT_TRUE(secure_image.SchemeIsCryptographic());
|
| + ASSERT_FALSE(insecure_image.SchemeIsCryptographic());
|
| +
|
| + AddQuery(&secure_image, "header", kClearCookiesHeader);
|
| + AddQuery(&insecure_image, "header", kClearCookiesHeader);
|
| +
|
| + std::string content_with_insecure_image =
|
| + "<html><head></head><body>"
|
| + "<img src=\"" +
|
| + insecure_image.spec() +
|
| + "\" />"
|
| + "</body></html>";
|
| +
|
| + std::string content_with_secure_image =
|
| + "<html><head></head><body>"
|
| + "<img src=\"" +
|
| + secure_image.spec() +
|
| + "\" />"
|
| + "</body></html>";
|
| +
|
| + // Test insecure resources.
|
| + GURL insecure_page = embedded_test_server()->GetURL("example.com", "/");
|
| + GURL secure_page = https_server()->GetURL("example.com", "/");
|
| +
|
| + AddQuery(&insecure_page, "html", content_with_insecure_image);
|
| + AddQuery(&secure_page, "html", content_with_insecure_image);
|
| +
|
| + EXPECT_CALL(*GetContentBrowserClient(), ClearSiteData(_, _, _, _, _, _))
|
| + .Times(0);
|
| +
|
| + // Insecure resource on an insecure page does not execute Clear-Site-Data.
|
| + NavigateToURL(shell(), insecure_page);
|
| +
|
| + // Insecure resource on a secure page does not execute Clear-Site-Data.
|
| + NavigateToURL(shell(), secure_page);
|
| +
|
| + testing::Mock::VerifyAndClearExpectations(GetContentBrowserClient());
|
| +
|
| + // Test secure resources.
|
| + insecure_page = embedded_test_server()->GetURL("example.com", "/");
|
| + secure_page = https_server()->GetURL("example.com", "/");
|
| +
|
| + AddQuery(&insecure_page, "html", content_with_secure_image);
|
| + AddQuery(&secure_page, "html", content_with_secure_image);
|
| +
|
| + // Secure resource on an insecure page does execute Clear-Site-Data.
|
| + EXPECT_CALL(*GetContentBrowserClient(), ClearSiteData(_, _, _, _, _, _))
|
| + .Times(1);
|
| +
|
| + NavigateToURL(shell(), secure_page);
|
| + testing::Mock::VerifyAndClearExpectations(GetContentBrowserClient());
|
| +
|
| + // Secure resource on a secure page does execute Clear-Site-Data.
|
| + EXPECT_CALL(*GetContentBrowserClient(), ClearSiteData(_, _, _, _, _, _))
|
| + .Times(1);
|
| +
|
| + NavigateToURL(shell(), secure_page);
|
| +}
|
| +
|
| +// Tests that the Clear-Site-Data header is ignored for service worker resource
|
| +// loads.
|
| +IN_PROC_BROWSER_TEST_F(ClearSiteDataThrottleBrowserTest, ServiceWorker) {
|
| + GURL origin1 = https_server()->GetURL("origin1.com", "/");
|
| + GURL origin2 = https_server()->GetURL("origin2.com", "/");
|
| + GURL origin3 = https_server()->GetURL("origin3.com", "/");
|
| + GURL origin4 = https_server()->GetURL("origin4.com", "/");
|
| +
|
| + // Navigation to worker_setup.html will install a service worker. Since
|
| + // the installation is asynchronous, the JS side will inform us about it in
|
| + // the page title.
|
| + GURL url = origin1;
|
| + AddQuery(&url, "file", "worker_setup.html");
|
| + NavigateToURL(shell(), url);
|
| + WaitForTitle(shell(), "service worker is ready");
|
| +
|
| + // The service worker will now serve a page containing several images, which
|
| + // the browser will try to fetch. The service worker will be instructed
|
| + // to handle some of the fetches itself, while others will be handled by
|
| + // the testing server. The setup is the following:
|
| + //
|
| + // origin1.com/resource (-> server; should respect header)
|
| + // origin1.com/resource_from_sw (-> service worker; should not respect header)
|
| + // origin2.com/resource_from_sw (-> service worker; should not respect header)
|
| + // origin2.com/resource (-> server; should respect header)
|
| + // origin3.com/resource_from_sw (-> service worker; should not respect header)
|
| + // origin3.com/resource_from_sw (-> service worker; should not respect header)
|
| + // origin4.com/resource (-> server; should respect header)
|
| + // origin4.com/resource (-> server; should respect header)
|
| + //
|
| + // |origin1| and |origin2| are used to test that there is no difference
|
| + // between same-origin and third-party fetches. Clear-Site-Data should be
|
| + // called once for each of these origins - caused by the "/resource" fetch,
|
| + // but not by the "/resource_from_sw" fetch. |origin3| and |origin4| prove
|
| + // that the number of calls is dependent on the number of network responses,
|
| + // i.e. that it isn't always 1 as in the case of |origin1| and |origin2|.
|
| + EXPECT_CALL(*GetContentBrowserClient(),
|
| + ClearSiteData(_, url::Origin(origin1), _, _, _, _)).Times(1);
|
| + EXPECT_CALL(*GetContentBrowserClient(),
|
| + ClearSiteData(_, url::Origin(origin2), _, _, _, _)).Times(1);
|
| + EXPECT_CALL(*GetContentBrowserClient(),
|
| + ClearSiteData(_, url::Origin(origin3), _, _, _, _)).Times(0);
|
| + EXPECT_CALL(*GetContentBrowserClient(),
|
| + ClearSiteData(_, url::Origin(origin4), _, _, _, _)).Times(2);
|
| +
|
| + url = https_server()->GetURL("origin1.com", "/anything-in-workers-scope");
|
| + AddQuery(&url, "origin1", origin1.spec());
|
| + AddQuery(&url, "origin2", origin2.spec());
|
| + AddQuery(&url, "origin3", origin3.spec());
|
| + AddQuery(&url, "origin4", origin4.spec());
|
| + NavigateToURL(shell(), url);
|
| + WaitForTitle(shell(), "done");
|
| +}
|
| +
|
| +// Tests that Clear-Site-Data is only executed on a resource fetch
|
| +// if credentials are allowed in that fetch.
|
| +IN_PROC_BROWSER_TEST_F(ClearSiteDataThrottleBrowserTest, Credentials) {
|
| + GURL page_template = https_server()->GetURL("origin1.com", "/");
|
| + GURL same_origin_resource =
|
| + https_server()->GetURL("origin1.com", "/resource");
|
| + GURL different_origin_resource =
|
| + https_server()->GetURL("origin2.com", "/resource");
|
| +
|
| + AddQuery(&same_origin_resource, "header", kClearCookiesHeader);
|
| + AddQuery(&different_origin_resource, "header", kClearCookiesHeader);
|
| +
|
| + const struct TestCase {
|
| + bool same_origin;
|
| + std::string credentials;
|
| + bool should_run;
|
| + } kTestCases[] = {
|
| + { true, "", false },
|
| + { true, "omit", false },
|
| + { true, "same-origin", true },
|
| + { true, "include", true },
|
| + { false, "", false },
|
| + { false, "omit", false },
|
| + { false, "same-origin", false },
|
| + { false, "include", true },
|
| + };
|
| +
|
| + for (const TestCase& test_case : kTestCases) {
|
| + const GURL& resource = test_case.same_origin
|
| + ? same_origin_resource : different_origin_resource;
|
| + std::string credentials = test_case.credentials.empty()
|
| + ? "" : "credentials: '" + test_case.credentials + "'";
|
| +
|
| + // Fetch a resource. Note that the script also handles fetch() error which
|
| + // might be thrown for third-party fetches because of missing
|
| + // Access-Control-Allow-Origin. However, that only affects the visibility
|
| + // of the response; the header will still be processed.
|
| + std::string content = base::StringPrintf(
|
| + "<html><head></head><body><script>"
|
| + "fetch('%s', {%s})"
|
| + ".then(function() { document.title = 'done'; })"
|
| + ".catch(function() { document.title = 'done'; })"
|
| + "</script></body></html>",
|
| + resource.spec().c_str(),
|
| + credentials.c_str());
|
| +
|
| + GURL page = page_template;
|
| + AddQuery(&page, "html", content);
|
| +
|
| + EXPECT_CALL(*GetContentBrowserClient(), ClearSiteData(_, _, _, _, _, _))
|
| + .Times(test_case.should_run ? 1 : 0);
|
| + NavigateToURL(shell(), page);
|
| + WaitForTitle(shell(), "done");
|
| + testing::Mock::VerifyAndClearExpectations(GetContentBrowserClient());
|
| + }
|
| +}
|
| +
|
| // Tests that ClearSiteData() is called for the correct datatypes.
|
| IN_PROC_BROWSER_TEST_F(ClearSiteDataThrottleBrowserTest, Types) {
|
| GURL base_url = https_server()->GetURL("example.com", "/");
|
|
|