| 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 f8ae1b6db1f48516e81e28b98767c1fe76cdf109..a58561492c3cd622081b9af299e2f85643f81211 100644
|
| --- a/content/browser/browsing_data/clear_site_data_throttle_browsertest.cc
|
| +++ b/content/browser/browsing_data/clear_site_data_throttle_browsertest.cc
|
| @@ -9,18 +9,33 @@
|
| #include "base/bind.h"
|
| #include "base/callback.h"
|
| #include "base/command_line.h"
|
| +#include "base/scoped_observer.h"
|
| +#include "base/strings/stringprintf.h"
|
| +#include "base/strings/utf_string_conversions.h"
|
| +#include "content/browser/browsing_data/browsing_data_filter_builder_impl.h"
|
| +#include "content/browser/service_worker/service_worker_context_observer.h"
|
| +#include "content/browser/service_worker/service_worker_context_wrapper.h"
|
| +#include "content/public/browser/browser_context.h"
|
| +#include "content/public/browser/browsing_data_remover.h"
|
| #include "content/public/browser/content_browser_client.h"
|
| +#include "content/public/browser/storage_partition.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/cache_test_util.h"
|
| #include "content/public/test/content_browser_test.h"
|
| #include "content/public/test/content_browser_test_utils.h"
|
| +#include "content/public/test/mock_browsing_data_remover_delegate.h"
|
| #include "content/public/test/test_navigation_observer.h"
|
| #include "content/shell/browser/shell.h"
|
| #include "net/base/escape.h"
|
| #include "net/base/url_util.h"
|
| +#include "net/cookies/cookie_store.h"
|
| #include "net/dns/mock_host_resolver.h"
|
| #include "net/test/embedded_test_server/http_request.h"
|
| #include "net/test/embedded_test_server/http_response.h"
|
| +#include "net/url_request/url_request_context.h"
|
| +#include "net/url_request/url_request_context_getter.h"
|
| #include "storage/browser/quota/quota_settings.h"
|
| #include "testing/gmock/include/gmock/gmock.h"
|
| #include "url/origin.h"
|
| @@ -32,50 +47,104 @@ namespace content {
|
|
|
| namespace {
|
|
|
| -class MockContentBrowserClient : public ContentBrowserClient {
|
| - public:
|
| - MOCK_METHOD6(ClearSiteData,
|
| - void(content::BrowserContext* browser_context,
|
| - const url::Origin& origin,
|
| - bool remove_cookies,
|
| - bool remove_storage,
|
| - bool remove_cache,
|
| - const base::Closure& callback));
|
| -
|
| - void GetQuotaSettings(
|
| - content::BrowserContext* context,
|
| - content::StoragePartition* partition,
|
| - const storage::OptionalQuotaSettingsCallback& callback) override {
|
| - callback.Run(storage::GetHardCodedSettings(100 * 1024 * 1024));
|
| - }
|
| -};
|
| -
|
| -class TestContentBrowserClient : public MockContentBrowserClient {
|
| - public:
|
| - void ClearSiteData(content::BrowserContext* browser_context,
|
| - const url::Origin& origin,
|
| - bool remove_cookies,
|
| - bool remove_storage,
|
| - bool remove_cache,
|
| - const base::Closure& callback) override {
|
| - // Record the method call and run the |callback|.
|
| - MockContentBrowserClient::ClearSiteData(browser_context, origin,
|
| - remove_cookies, remove_storage,
|
| - remove_cache, callback);
|
| - callback.Run();
|
| - }
|
| -};
|
| -
|
| // Adds a key=value pair to the url's query.
|
| void AddQuery(GURL* url, const std::string& key, const std::string& value) {
|
| *url = GURL(url->spec() + (url->has_query() ? "&" : "?") + key + "=" +
|
| 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\" ] }";
|
|
|
| +// A helper class to observe BrowsingDataRemover deletion tasks coming from
|
| +// ClearSiteData.
|
| +class TestBrowsingDataRemoverDelegate : public MockBrowsingDataRemoverDelegate {
|
| + public:
|
| + // Sets a test expectation that a Clear-Site-Data header call from |origin|,
|
| + // instructing to delete |cookies|, |storage|, and |cache|, will schedule
|
| + // the corresponding BrowsingDataRemover deletion tasks.
|
| + void ExpectClearSiteDataCall(const url::Origin& origin,
|
| + bool cookies,
|
| + bool storage,
|
| + bool cache) {
|
| + const int kOriginTypeMask =
|
| + BrowsingDataRemover::ORIGIN_TYPE_UNPROTECTED_WEB |
|
| + BrowsingDataRemover::ORIGIN_TYPE_PROTECTED_WEB;
|
| +
|
| + if (cookies) {
|
| + int data_type_mask = BrowsingDataRemover::DATA_TYPE_COOKIES |
|
| + BrowsingDataRemover::DATA_TYPE_CHANNEL_IDS;
|
| +
|
| + BrowsingDataFilterBuilderImpl filter_builder(
|
| + BrowsingDataFilterBuilder::WHITELIST);
|
| + filter_builder.AddRegisterableDomain(origin.host());
|
| + ExpectCall(base::Time(), base::Time::Max(), data_type_mask,
|
| + kOriginTypeMask, std::move(filter_builder));
|
| + }
|
| + if (storage || cache) {
|
| + int data_type_mask =
|
| + (storage ? BrowsingDataRemover::DATA_TYPE_DOM_STORAGE : 0) |
|
| + (cache ? BrowsingDataRemover::DATA_TYPE_CACHE : 0);
|
| +
|
| + BrowsingDataFilterBuilderImpl filter_builder(
|
| + BrowsingDataFilterBuilder::WHITELIST);
|
| + filter_builder.AddOrigin(origin);
|
| + ExpectCall(base::Time(), base::Time::Max(), data_type_mask,
|
| + kOriginTypeMask, std::move(filter_builder));
|
| + }
|
| + }
|
| +
|
| + // A shortcut for the above method, but with only cookies deleted. This is
|
| + // useful for most tests that use |kClearCookiesHeader|.
|
| + void ExpectClearSiteDataCookiesCall(const url::Origin& origin) {
|
| + ExpectClearSiteDataCall(origin, true, false, false);
|
| + }
|
| +};
|
| +
|
| +// TODO(msramek): A class like this already exists in ServiceWorkerBrowserTest.
|
| +// Consider extracting it to a test utils file.
|
| +class ServiceWorkerActivationObserver : public ServiceWorkerContextObserver {
|
| + public:
|
| + static void SignalActivation(ServiceWorkerContextWrapper* context,
|
| + const base::Closure& callback) {
|
| + new ServiceWorkerActivationObserver(context, callback);
|
| + }
|
| +
|
| + private:
|
| + ServiceWorkerActivationObserver(ServiceWorkerContextWrapper* context,
|
| + const base::Closure& callback)
|
| + : context_(context), scoped_observer_(this), callback_(callback) {
|
| + scoped_observer_.Add(context);
|
| + }
|
| +
|
| + ~ServiceWorkerActivationObserver() override {}
|
| +
|
| + // ServiceWorkerContextObserver overrides.
|
| + void OnVersionStateChanged(int64_t version_id,
|
| + ServiceWorkerVersion::Status) override {
|
| + if (context_->GetLiveVersion(version_id)->status() ==
|
| + ServiceWorkerVersion::ACTIVATED) {
|
| + callback_.Run();
|
| + delete this;
|
| + }
|
| + }
|
| +
|
| + ServiceWorkerContextWrapper* context_;
|
| + ScopedObserver<ServiceWorkerContextWrapper, ServiceWorkerContextObserver>
|
| + scoped_observer_;
|
| + base::Closure callback_;
|
| +};
|
| +
|
| } // namespace
|
|
|
| class ClearSiteDataThrottleBrowserTest : public ContentBrowserTest {
|
| @@ -93,7 +162,8 @@ class ClearSiteDataThrottleBrowserTest : public ContentBrowserTest {
|
| void SetUpOnMainThread() override {
|
| ContentBrowserTest::SetUpOnMainThread();
|
|
|
| - SetBrowserClientForTesting(&test_client_);
|
| + BrowserContext::GetBrowsingDataRemover(browser_context())
|
| + ->SetEmbedderDelegate(&embedder_delegate_);
|
|
|
| // Set up HTTP and HTTPS test servers that handle all hosts.
|
| host_resolver()->AddRule("*", "127.0.0.1");
|
| @@ -110,22 +180,149 @@ class ClearSiteDataThrottleBrowserTest : public ContentBrowserTest {
|
| base::Bind(&ClearSiteDataThrottleBrowserTest::HandleRequest,
|
| base::Unretained(this)));
|
| ASSERT_TRUE(https_server_->Start());
|
| +
|
| + // Initialize the cookie store pointer on the IO thread.
|
| + base::RunLoop run_loop;
|
| + BrowserThread::PostTask(
|
| + BrowserThread::IO, FROM_HERE,
|
| + base::BindOnce(
|
| + &ClearSiteDataThrottleBrowserTest::InitializeCookieStore,
|
| + base::Unretained(this),
|
| + base::Unretained(
|
| + BrowserContext::GetDefaultStoragePartition(browser_context())
|
| + ->GetURLRequestContext()),
|
| + run_loop.QuitClosure()));
|
| + run_loop.Run();
|
| + }
|
| +
|
| + BrowserContext* browser_context() {
|
| + return shell()->web_contents()->GetBrowserContext();
|
| + }
|
| +
|
| + void InitializeCookieStore(
|
| + net::URLRequestContextGetter* request_context_getter,
|
| + base::Closure callback) {
|
| + cookie_store_ =
|
| + request_context_getter->GetURLRequestContext()->cookie_store();
|
| + callback.Run();
|
| + }
|
| +
|
| + // Adds a cookie for the |url|. Used in the cookie integration tests.
|
| + void AddCookie(const GURL& url) {
|
| + DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
| + base::RunLoop run_loop;
|
| + BrowserThread::PostTask(
|
| + BrowserThread::IO, FROM_HERE,
|
| + base::BindOnce(
|
| + &net::CookieStore::SetCookieWithOptionsAsync,
|
| + base::Unretained(cookie_store_), url, "A=1", net::CookieOptions(),
|
| + base::Bind(&ClearSiteDataThrottleBrowserTest::AddCookieCallback,
|
| + run_loop.QuitClosure())));
|
| + run_loop.Run();
|
| + }
|
| +
|
| + // Retrieves the list of all cookies. Used in the cookie integration tests.
|
| + net::CookieList GetCookies() {
|
| + DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
| + base::RunLoop run_loop;
|
| + net::CookieList cookie_list;
|
| + BrowserThread::PostTask(
|
| + BrowserThread::IO, FROM_HERE,
|
| + base::BindOnce(
|
| + &net::CookieStore::GetAllCookiesAsync,
|
| + base::Unretained(cookie_store_),
|
| + base::Bind(&ClearSiteDataThrottleBrowserTest::GetCookiesCallback,
|
| + run_loop.QuitClosure(),
|
| + base::Unretained(&cookie_list))));
|
| + run_loop.Run();
|
| + return cookie_list;
|
| + }
|
| +
|
| + // Adds a service worker. Used in the storage integration tests.
|
| + void AddServiceWorker(const std::string& origin) {
|
| + DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
| + ServiceWorkerContextWrapper* service_worker_context =
|
| + static_cast<ServiceWorkerContextWrapper*>(
|
| + BrowserContext::GetDefaultStoragePartition(browser_context())
|
| + ->GetServiceWorkerContext());
|
| +
|
| + GURL scope_url = https_server()->GetURL(origin, "/");
|
| + GURL js_url = https_server()->GetURL(origin, "/?file=worker.js");
|
| +
|
| + // Register the worker.
|
| + BrowserThread::PostTask(
|
| + BrowserThread::IO, FROM_HERE,
|
| + base::BindOnce(
|
| + &ServiceWorkerContextWrapper::RegisterServiceWorker,
|
| + base::Unretained(service_worker_context), scope_url, js_url,
|
| + base::Bind(
|
| + &ClearSiteDataThrottleBrowserTest::AddServiceWorkerCallback,
|
| + base::Unretained(this))));
|
| +
|
| + // Wait for its activation.
|
| + base::RunLoop run_loop;
|
| + BrowserThread::PostTask(
|
| + BrowserThread::IO, FROM_HERE,
|
| + base::Bind(&ServiceWorkerActivationObserver::SignalActivation,
|
| + base::Unretained(service_worker_context),
|
| + run_loop.QuitClosure()));
|
| + run_loop.Run();
|
| + }
|
| +
|
| + // Retrieves the list of all service workers. Used in the storage integration
|
| + // tests.
|
| + std::vector<ServiceWorkerUsageInfo> GetServiceWorkers() {
|
| + DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
| + ServiceWorkerContextWrapper* service_worker_context =
|
| + static_cast<ServiceWorkerContextWrapper*>(
|
| + BrowserContext::GetDefaultStoragePartition(browser_context())
|
| + ->GetServiceWorkerContext());
|
| +
|
| + std::vector<ServiceWorkerUsageInfo> service_workers;
|
| + base::RunLoop run_loop;
|
| +
|
| + BrowserThread::PostTask(
|
| + BrowserThread::IO, FROM_HERE,
|
| + base::BindOnce(
|
| + &ServiceWorkerContextWrapper::GetAllOriginsInfo,
|
| + base::Unretained(service_worker_context),
|
| + base::Bind(
|
| + &ClearSiteDataThrottleBrowserTest::GetServiceWorkersCallback,
|
| + base::Unretained(this), run_loop.QuitClosure(),
|
| + base::Unretained(&service_workers))));
|
| + run_loop.Run();
|
| +
|
| + return service_workers;
|
| }
|
|
|
| - TestContentBrowserClient* GetContentBrowserClient() { return &test_client_; }
|
| + TestBrowsingDataRemoverDelegate* delegate() { return &embedder_delegate_; }
|
|
|
| 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(
|
| @@ -142,18 +339,78 @@ 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);
|
| }
|
|
|
| - TestContentBrowserClient test_client_;
|
| + // Callback handler for AddCookie().
|
| + static void AddCookieCallback(const base::Closure& callback, bool success) {
|
| + DCHECK_CURRENTLY_ON(BrowserThread::IO);
|
| + ASSERT_TRUE(success);
|
| + callback.Run();
|
| + }
|
| +
|
| + // Callback handler for GetCookies().
|
| + static void GetCookiesCallback(const base::Closure& callback,
|
| + net::CookieList* out_cookie_list,
|
| + const net::CookieList& cookie_list) {
|
| + DCHECK_CURRENTLY_ON(BrowserThread::IO);
|
| + *out_cookie_list = cookie_list;
|
| + callback.Run();
|
| + }
|
| +
|
| + // Callback handler for AddServiceWorker().
|
| + void AddServiceWorkerCallback(bool success) { ASSERT_TRUE(success); }
|
| +
|
| + // Callback handler for GetServiceWorkers().
|
| + void GetServiceWorkersCallback(
|
| + const base::Closure& callback,
|
| + std::vector<ServiceWorkerUsageInfo>* out_service_workers,
|
| + const std::vector<ServiceWorkerUsageInfo>& service_workers) {
|
| + *out_service_workers = service_workers;
|
| + callback.Run();
|
| + }
|
| +
|
| std::unique_ptr<net::EmbeddedTestServer> https_server_;
|
| + TestBrowsingDataRemoverDelegate embedder_delegate_;
|
| +
|
| + net::CookieStore* cookie_store_;
|
| };
|
|
|
| // 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) {
|
| - GURL base_urls[3] = {
|
| +// 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 page_urls[3] = {
|
| https_server()->GetURL("origin1.com", "/"),
|
| https_server()->GetURL("origin2.com", "/foo/bar"),
|
| https_server()->GetURL("origin3.com", "/index.html"),
|
| @@ -166,14 +423,12 @@ IN_PROC_BROWSER_TEST_F(ClearSiteDataThrottleBrowserTest, Redirect) {
|
|
|
| // Set up the expectations.
|
| for (int i = 0; i < 3; ++i) {
|
| - urls[i] = base_urls[i];
|
| + urls[i] = page_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);
|
| + if (mask & (1 << i))
|
| + delegate()->ExpectClearSiteDataCookiesCall(url::Origin(urls[i]));
|
| }
|
|
|
| // Set up redirects between urls 0 --> 1 --> 2.
|
| @@ -186,24 +441,286 @@ IN_PROC_BROWSER_TEST_F(ClearSiteDataThrottleBrowserTest, Redirect) {
|
| // We reached the end of the redirect chain.
|
| EXPECT_EQ(urls[2], shell()->web_contents()->GetURL());
|
|
|
| - testing::Mock::VerifyAndClearExpectations(GetContentBrowserClient());
|
| + delegate()->VerifyAndClearExpectations();
|
| + }
|
| +}
|
| +
|
| +// 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 resource_urls[3] = {
|
| + https_server()->GetURL("origin1.com", "/redirect-start"),
|
| + https_server()->GetURL("origin2.com", "/redirect-middle"),
|
| + https_server()->GetURL("origin3.com", "/redirect-end"),
|
| + };
|
| +
|
| + // 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] = resource_urls[i];
|
| + if (mask & (1 << i))
|
| + AddQuery(&urls[i], "header", kClearCookiesHeader);
|
| +
|
| + if (mask & (1 << i))
|
| + delegate()->ExpectClearSiteDataCookiesCall(url::Origin(urls[i]));
|
| + }
|
| +
|
| + // 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);
|
| +
|
| + delegate()->VerifyAndClearExpectations();
|
| }
|
| }
|
|
|
| // 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);
|
| ASSERT_FALSE(url.SchemeIsCryptographic());
|
|
|
| - EXPECT_CALL(*GetContentBrowserClient(), ClearSiteData(_, _, _, _, _, _))
|
| - .Times(0);
|
| + NavigateToURL(shell(), url);
|
| +
|
| + // We do not expect any calls to have been made.
|
| + delegate()->VerifyAndClearExpectations();
|
| +}
|
| +
|
| +// 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);
|
| +
|
| + // 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);
|
| +
|
| + // We do not expect any calls to have been made.
|
| + delegate()->VerifyAndClearExpectations();
|
| +
|
| + // 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.
|
| + delegate()->ExpectClearSiteDataCookiesCall(url::Origin(secure_image));
|
| +
|
| + NavigateToURL(shell(), secure_page);
|
| + delegate()->VerifyAndClearExpectations();
|
| +
|
| + // Secure resource on a secure page does execute Clear-Site-Data.
|
| + delegate()->ExpectClearSiteDataCookiesCall(url::Origin(secure_image));
|
| +
|
| + NavigateToURL(shell(), secure_page);
|
| + delegate()->VerifyAndClearExpectations();
|
| +}
|
|
|
| +// 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)
|
| + // origin2.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)
|
| + // origin1.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)
|
| + // 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|.
|
| + delegate()->ExpectClearSiteDataCookiesCall(url::Origin(origin1));
|
| + delegate()->ExpectClearSiteDataCookiesCall(url::Origin(origin4));
|
| + delegate()->ExpectClearSiteDataCookiesCall(url::Origin(origin2));
|
| + delegate()->ExpectClearSiteDataCookiesCall(url::Origin(origin4));
|
| +
|
| + 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");
|
| + delegate()->VerifyAndClearExpectations();
|
| +}
|
| +
|
| +// 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);
|
| +
|
| + if (test_case.should_run)
|
| + delegate()->ExpectClearSiteDataCookiesCall(url::Origin(resource));
|
| +
|
| + NavigateToURL(shell(), page);
|
| + WaitForTitle(shell(), "done");
|
| + delegate()->VerifyAndClearExpectations();
|
| + }
|
| }
|
|
|
| -// Tests that ClearSiteData() is called for the correct datatypes.
|
| +// Tests that the credentials flag is correctly taken into account when it
|
| +// interpretation changes after redirect.
|
| +IN_PROC_BROWSER_TEST_F(ClearSiteDataThrottleBrowserTest,
|
| + CredentialsOnRedirect) {
|
| + GURL urls[2] = {
|
| + https_server()->GetURL("origin1.com", "/image.png"),
|
| + https_server()->GetURL("origin2.com", "/image.png"),
|
| + };
|
| +
|
| + AddQuery(&urls[0], "header", kClearCookiesHeader);
|
| + AddQuery(&urls[1], "header", kClearCookiesHeader);
|
| +
|
| + AddQuery(&urls[0], "redirect", urls[1].spec());
|
| +
|
| + // Fetch a resource on origin1.com, which will redirect to origin2.com.
|
| + // Both URLs will respond with Clear-Site-Data. Since the credentials mode is
|
| + // 'same-origin', the LOAD_DO_NOT_SAVE_COOKIES flag will be set while
|
| + // processing the response from origin1.com, but not while processing the
|
| + // response from origin2.com. Therefore, the deletion will only be executed
|
| + // for origin1.com.
|
| + //
|
| + // 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', {'credentials': 'same-origin'})"
|
| + ".then(function() { document.title = 'done'; })"
|
| + ".catch(function() { document.title = 'done'; })"
|
| + "</script></body></html>",
|
| + urls[0].spec().c_str());
|
| +
|
| + delegate()->ExpectClearSiteDataCookiesCall(url::Origin(urls[0]));
|
| +
|
| + GURL page = https_server()->GetURL("origin1.com", "/");
|
| + AddQuery(&page, "html", content);
|
| +
|
| + NavigateToURL(shell(), page);
|
| + WaitForTitle(shell(), "done");
|
| + delegate()->VerifyAndClearExpectations();
|
| +}
|
| +
|
| +// Tests that ClearSiteData() is called for the correct data types.
|
| IN_PROC_BROWSER_TEST_F(ClearSiteDataThrottleBrowserTest, Types) {
|
| GURL base_url = https_server()->GetURL("example.com", "/");
|
|
|
| @@ -227,16 +744,125 @@ IN_PROC_BROWSER_TEST_F(ClearSiteDataThrottleBrowserTest, Types) {
|
| GURL url = base_url;
|
| AddQuery(&url, "header", test_case.value);
|
|
|
| - EXPECT_CALL(
|
| - *GetContentBrowserClient(),
|
| - ClearSiteData(shell()->web_contents()->GetBrowserContext(),
|
| - url::Origin(url), test_case.remove_cookies,
|
| - test_case.remove_storage, test_case.remove_cache, _));
|
| + delegate()->ExpectClearSiteDataCall(
|
| + url::Origin(url), test_case.remove_cookies, test_case.remove_storage,
|
| + test_case.remove_cache);
|
|
|
| NavigateToURL(shell(), url);
|
|
|
| - testing::Mock::VerifyAndClearExpectations(GetContentBrowserClient());
|
| + delegate()->VerifyAndClearExpectations();
|
| }
|
| }
|
|
|
| +// Integration test for the deletion of cookies.
|
| +IN_PROC_BROWSER_TEST_F(ClearSiteDataThrottleBrowserTest,
|
| + CookiesIntegrationTest) {
|
| + AddCookie(https_server()->GetURL("origin1.com", "/abc"));
|
| + AddCookie(https_server()->GetURL("subdomain.origin1.com", "/"));
|
| + AddCookie(https_server()->GetURL("origin2.com", "/def"));
|
| + AddCookie(https_server()->GetURL("subdomain.origin2.com", "/"));
|
| +
|
| + // There are four cookies on two eTLD+1s.
|
| + net::CookieList cookies = GetCookies();
|
| + EXPECT_EQ(4u, cookies.size());
|
| +
|
| + // Let Clear-Site-Data delete the "cookies" of "origin1.com".
|
| + GURL url = https_server()->GetURL("origin1.com", "/clear-site-data");
|
| + AddQuery(&url, "header", kClearCookiesHeader);
|
| + NavigateToURL(shell(), url);
|
| +
|
| + // Only the "origin2.com" eTLD now has cookies.
|
| + cookies = GetCookies();
|
| + ASSERT_EQ(2u, cookies.size());
|
| + EXPECT_EQ(cookies[0].Domain(), "origin2.com");
|
| + EXPECT_EQ(cookies[1].Domain(), "subdomain.origin2.com");
|
| +}
|
| +
|
| +// Integration test for the unregistering of service workers.
|
| +IN_PROC_BROWSER_TEST_F(ClearSiteDataThrottleBrowserTest,
|
| + StorageServiceWorkersIntegrationTest) {
|
| + AddServiceWorker("origin1.com");
|
| + AddServiceWorker("origin2.com");
|
| +
|
| + // There are two service workers installed on two origins.
|
| + std::vector<ServiceWorkerUsageInfo> service_workers = GetServiceWorkers();
|
| + EXPECT_EQ(2u, service_workers.size());
|
| +
|
| + // Navigate to a URL within the scope of "origin1.com" which responds with
|
| + // a Clear-Site-Data header. Verify that this did NOT remove the service
|
| + // worker for "origin1.com", as the header would not be respected outside
|
| + // of the scope.
|
| + GURL url = https_server()->GetURL("origin1.com", "/anything-in-the-scope");
|
| + AddQuery(&url, "header", "{ \"types\": [ \"storage\" ] }");
|
| + NavigateToURL(shell(), url);
|
| + service_workers = GetServiceWorkers();
|
| + EXPECT_EQ(2u, service_workers.size());
|
| +
|
| + // This time, we will navigate to a URL on "origin1.com" that is not handled
|
| + // by the serice worker, but results in a network request. One such resource
|
| + // not handled by "worker.js" is the path "resource".
|
| + // The header will be respected and the worker deleted.
|
| + url = https_server()->GetURL("origin1.com", "/resource");
|
| + AddQuery(&url, "header", "{ \"types\": [ \"storage\" ] }");
|
| + NavigateToURL(shell(), url);
|
| +
|
| + // Only "origin2.com" now has a service worker.
|
| + service_workers = GetServiceWorkers();
|
| + ASSERT_EQ(1u, service_workers.size());
|
| + EXPECT_EQ(service_workers[0].origin,
|
| + https_server()->GetURL("origin2.com", "/"));
|
| +
|
| + // TODO(msramek): Test that the service worker update ping also deletes
|
| + // the service worker.
|
| +}
|
| +
|
| +// TODO(msramek): Add integration tests for other storage data types, such as
|
| +// local storage, indexed DB, etc.
|
| +
|
| +// Integration test for the deletion of cache entries.
|
| +// NOTE: This test might be flaky. disk_cache::Backend calls back before cache
|
| +// entries are actually written to the disk. Other tests using CacheTestUtil
|
| +// show that a timeout of around 1s between cache operations is necessary to
|
| +// avoid flakiness.
|
| +IN_PROC_BROWSER_TEST_F(ClearSiteDataThrottleBrowserTest, CacheIntegrationTest) {
|
| + const int kTimeoutMs = 1000;
|
| +
|
| + CacheTestUtil util(
|
| + BrowserContext::GetDefaultStoragePartition(browser_context()));
|
| + std::string url1 = https_server()->GetURL("origin1.com", "/foo").spec();
|
| + std::string url2 = https_server()->GetURL("origin1.com", "/bar").spec();
|
| + std::string url3 = https_server()->GetURL("origin2.com", "/foo").spec();
|
| + std::string url4 = https_server()->GetURL("origin2.com", "/bar").spec();
|
| +
|
| + std::set<std::string> entries_to_create = {url1, url2, url3, url4};
|
| + util.CreateCacheEntries(entries_to_create);
|
| + base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(kTimeoutMs));
|
| +
|
| + // There are four cache entries on two origins.
|
| + std::vector<std::string> cache_keys = util.GetEntryKeys();
|
| + EXPECT_EQ(4u, cache_keys.size());
|
| +
|
| + // Let Clear-Site-Data delete the "cache" of "origin1.com".
|
| + GURL url = https_server()->GetURL("origin1.com", "/clear-site-data");
|
| + AddQuery(&url, "header", "{ \"types\": [ \"cache\" ] }");
|
| + NavigateToURL(shell(), url);
|
| + base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(kTimeoutMs));
|
| +
|
| + // Only "origin2.com" now has cache entries.
|
| + cache_keys = util.GetEntryKeys();
|
| + ASSERT_EQ(2u, cache_keys.size());
|
| + std::sort(cache_keys.begin(), cache_keys.end());
|
| + EXPECT_EQ(url4, cache_keys[0]);
|
| + EXPECT_EQ(url3, cache_keys[1]);
|
| +}
|
| +
|
| +// Tests that closing the tab right after executing Clear-Site-Data does
|
| +// not crash.
|
| +IN_PROC_BROWSER_TEST_F(ClearSiteDataThrottleBrowserTest, ClosedTab) {
|
| + GURL url = https_server()->GetURL("example.com", "/");
|
| + AddQuery(&url, "header", kClearCookiesHeader);
|
| + shell()->LoadURL(url);
|
| + shell()->Close();
|
| +}
|
| +
|
| } // namespace content
|
|
|