| // Copyright 2019 The Chromium Authors | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | // This file tests that Web Workers (a Content feature) work in the Chromium | 
 | // embedder. | 
 |  | 
 | #include "base/containers/contains.h" | 
 | #include "base/feature_list.h" | 
 | #include "base/functional/bind.h" | 
 | #include "base/functional/callback.h" | 
 | #include "base/run_loop.h" | 
 | #include "base/strings/strcat.h" | 
 | #include "base/strings/string_util.h" | 
 | #include "base/test/bind.h" | 
 | #include "chrome/browser/profiles/profile.h" | 
 | #include "chrome/browser/ui/browser.h" | 
 | #include "chrome/test/base/in_process_browser_test.h" | 
 | #include "chrome/test/base/ui_test_utils.h" | 
 | #include "components/content_settings/core/browser/cookie_settings.h" | 
 | #include "components/content_settings/core/common/pref_names.h" | 
 | #include "components/prefs/pref_service.h" | 
 | #include "content/public/browser/browser_context.h" | 
 | #include "content/public/test/browser_test.h" | 
 | #include "content/public/test/browser_test_utils.h" | 
 | #include "content/public/test/url_loader_interceptor.h" | 
 | #include "net/test/embedded_test_server/embedded_test_server.h" | 
 | #include "net/test/embedded_test_server/http_request.h" | 
 | #include "net/test/embedded_test_server/http_response.h" | 
 | #include "third_party/blink/public/common/features.h" | 
 | #include "third_party/re2/src/re2/re2.h" | 
 |  | 
 | // A simple fixture used for testing dedicated workers and shared workers. The | 
 | // fixture stashes the HTTP request to the worker script for inspecting the | 
 | // headers. | 
 | // | 
 | // This is in //chrome instead of //content since the tests exercise the | 
 | // |kBlockThirdPartyCookies| preference which is not a //content concept. | 
 | class ChromeWorkerBrowserTest : public InProcessBrowserTest { | 
 |  public: | 
 |   void SetUp() override { | 
 |     embedded_test_server()->RegisterRequestHandler( | 
 |         base::BindRepeating(&ChromeWorkerBrowserTest::CaptureHeaderHandler, | 
 |                             base::Unretained(this), "/capture")); | 
 |     ASSERT_TRUE(embedded_test_server()->InitializeAndListen()); | 
 |     InProcessBrowserTest::SetUp(); | 
 |   } | 
 |  | 
 |   void SetUpOnMainThread() override { | 
 |     embedded_test_server()->StartAcceptingConnections(); | 
 |   } | 
 |  | 
 |  protected: | 
 |   // Tests worker script fetch (always same-origin) is not affected by the | 
 |   // third-party cookie blocking configuration. | 
 |   // This is the regression test for https://crbug.com/933287. | 
 |   void TestWorkerScriptFetchWithThirdPartyCookieBlocking( | 
 |       content_settings::CookieControlsMode cookie_controls_mode, | 
 |       const std::string& test_url) { | 
 |     const std::string kCookie = "foo=bar"; | 
 |  | 
 |     // Set up third-party cookie blocking. | 
 |     browser()->profile()->GetPrefs()->SetInteger( | 
 |         prefs::kCookieControlsMode, static_cast<int>(cookie_controls_mode)); | 
 |  | 
 |     // Make sure cookies are not set. | 
 |     ASSERT_TRUE( | 
 |         GetCookies(browser()->profile(), embedded_test_server()->base_url()) | 
 |             .empty()); | 
 |  | 
 |     // Request for the worker script should not send cookies. | 
 |     { | 
 |       base::RunLoop run_loop; | 
 |       quit_closure_ = run_loop.QuitClosure(); | 
 |       ASSERT_TRUE(ui_test_utils::NavigateToURL( | 
 |           browser(), embedded_test_server()->GetURL(test_url))); | 
 |       run_loop.Run(); | 
 |       EXPECT_FALSE(base::Contains(header_map_, "Cookie")); | 
 |     } | 
 |  | 
 |     // Set a cookie. | 
 |     ASSERT_TRUE(SetCookie(browser()->profile(), | 
 |                           embedded_test_server()->base_url(), kCookie)); | 
 |  | 
 |     // Request for the worker script should send the cookie regardless of the | 
 |     // third-party cookie blocking configuration. | 
 |     { | 
 |       base::RunLoop run_loop; | 
 |       quit_closure_ = run_loop.QuitClosure(); | 
 |       ASSERT_TRUE(ui_test_utils::NavigateToURL( | 
 |           browser(), embedded_test_server()->GetURL(test_url))); | 
 |       run_loop.Run(); | 
 |       EXPECT_TRUE(base::Contains(header_map_, "Cookie")); | 
 |       EXPECT_EQ(kCookie, header_map_["Cookie"]); | 
 |     } | 
 |   } | 
 |  | 
 |   // TODO(nhiroki): Add tests for creating workers from third-party iframes | 
 |   // while third-party cookie blocking is enabled. This expects that cookies are | 
 |   // not blocked. | 
 |  | 
 |  private: | 
 |   std::unique_ptr<net::test_server::HttpResponse> CaptureHeaderHandler( | 
 |       const std::string& path, | 
 |       const net::test_server::HttpRequest& request) { | 
 |     if (request.GetURL().GetPath() != path) { | 
 |       return nullptr; | 
 |     } | 
 |     // Stash the HTTP request headers. | 
 |     header_map_ = request.headers; | 
 |     std::move(quit_closure_).Run(); | 
 |     return std::make_unique<net::test_server::BasicHttpResponse>(); | 
 |   } | 
 |  | 
 |   net::test_server::HttpRequest::HeaderMap header_map_; | 
 |   base::OnceClosure quit_closure_; | 
 | }; | 
 |  | 
 | IN_PROC_BROWSER_TEST_F(ChromeWorkerBrowserTest, | 
 |                        DedicatedWorkerScriptFetchWithThirdPartyBlocking) { | 
 |   TestWorkerScriptFetchWithThirdPartyCookieBlocking( | 
 |       content_settings::CookieControlsMode::kBlockThirdParty, | 
 |       "/workers/create_dedicated_worker.html?worker_url=/capture"); | 
 | } | 
 |  | 
 | IN_PROC_BROWSER_TEST_F(ChromeWorkerBrowserTest, | 
 |                        DedicatedWorkerScriptFetchWithoutThirdPartyBlocking) { | 
 |   TestWorkerScriptFetchWithThirdPartyCookieBlocking( | 
 |       content_settings::CookieControlsMode::kOff, | 
 |       "/workers/create_dedicated_worker.html?worker_url=/capture"); | 
 | } | 
 |  | 
 | IN_PROC_BROWSER_TEST_F(ChromeWorkerBrowserTest, | 
 |                        SharedWorkerScriptFetchWithThirdPartyBlocking) { | 
 |   TestWorkerScriptFetchWithThirdPartyCookieBlocking( | 
 |       content_settings::CookieControlsMode::kBlockThirdParty, | 
 |       "/workers/create_shared_worker.html?worker_url=/capture"); | 
 | } | 
 |  | 
 | IN_PROC_BROWSER_TEST_F(ChromeWorkerBrowserTest, | 
 |                        SharedWorkerScriptFetchWithoutThirdPartyBlocking) { | 
 |   TestWorkerScriptFetchWithThirdPartyCookieBlocking( | 
 |       content_settings::CookieControlsMode::kOff, | 
 |       "/workers/create_shared_worker.html?worker_url=/capture"); | 
 | } | 
 |  | 
 | // A test fixture used for testing that dedicated and shared workers have the | 
 | // correct user agent value, it should always be the reduced user agent string. | 
 | class ChromeWorkerUserAgentBrowserTest : public InProcessBrowserTest { | 
 |  public: | 
 |   ChromeWorkerUserAgentBrowserTest() = default; | 
 |  | 
 |   // The URL that was used to test user-agent. | 
 |   static constexpr char kOriginUrl[] = "https://127.0.0.1:44444"; | 
 |  | 
 |   // We use a URLLoaderInterceptor, rather than the EmbeddedTestServer, since | 
 |   // EmbeddedTestServer serves content on a random port. | 
 |   std::unique_ptr<content::URLLoaderInterceptor> CreateUrlLoaderInterceptor() { | 
 |     return std::make_unique<content::URLLoaderInterceptor>( | 
 |         base::BindLambdaForTesting( | 
 |             [&](content::URLLoaderInterceptor::RequestParams* params) { | 
 |               if (expected_request_urls_.find(params->url_request.url) == | 
 |                   expected_request_urls_.end()) | 
 |                 return false; | 
 |  | 
 |               std::string path = "chrome/test/data/workers"; | 
 |               path.append(std::string(params->url_request.url.path())); | 
 |  | 
 |               std::string headers = "HTTP/1.1 200 OK\n"; | 
 |               base::StrAppend( | 
 |                   &headers, | 
 |                   {"Content-Type: text/", | 
 |                    base::EndsWith(params->url_request.url.path(), ".js") | 
 |                        ? "javascript" | 
 |                        : "html", | 
 |                    "\n"}); | 
 |               content::URLLoaderInterceptor::WriteResponse( | 
 |                   path, params->client.get(), &headers); | 
 |  | 
 |               return true; | 
 |             })); | 
 |   } | 
 |  | 
 |   void SetExpectedRequestURLs(const std::set<GURL>& expected_request_urls) { | 
 |     expected_request_urls_ = expected_request_urls; | 
 |   } | 
 |  | 
 |   // Check whether the user agent minor version matches "0.0.0" and we expect | 
 |   // the user agent always to be reduced. | 
 |   void CheckUserAgentString(const std::string& user_agent_value) { | 
 |     // A regular expression that matches Chrome/{major_version}.{minor_version} | 
 |     // in the User-Agent string, where the {minor_version} is captured. | 
 |     static constexpr char kChromeVersionRegex[] = | 
 |         "Chrome/[0-9]+\\.([0-9]+\\.[0-9]+\\.[0-9]+)"; | 
 |     // The minor version in the reduced UA string is always "0.0.0". | 
 |     static constexpr char kReducedMinorVersion[] = "0.0.0"; | 
 |  | 
 |     std::string minor_version; | 
 |     EXPECT_TRUE(re2::RE2::PartialMatch(user_agent_value, kChromeVersionRegex, | 
 |                                        &minor_version)); | 
 |  | 
 |     EXPECT_EQ(minor_version, kReducedMinorVersion); | 
 |   } | 
 |  | 
 |  private: | 
 |   std::set<GURL> expected_request_urls_; | 
 | }; | 
 |  | 
 | constexpr char ChromeWorkerUserAgentBrowserTest::kOriginUrl[]; | 
 |  | 
 | IN_PROC_BROWSER_TEST_F(ChromeWorkerUserAgentBrowserTest, SharedWorker) { | 
 |   const GURL main_page_url = GURL(base::StrCat( | 
 |       {kOriginUrl, | 
 |        "/create_shared_worker.html?worker_url=onconnect_user_agent.js"})); | 
 |   const GURL worker_url = | 
 |       GURL(base::StrCat({kOriginUrl, "/onconnect_user_agent.js"})); | 
 |   SetExpectedRequestURLs({main_page_url, worker_url}); | 
 |  | 
 |   std::unique_ptr<content::URLLoaderInterceptor> interceptor = | 
 |       CreateUrlLoaderInterceptor(); | 
 |  | 
 |   // Navigate to the page that has the scripts for registering the worker. | 
 |   ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), main_page_url)); | 
 |  | 
 |   // Check the result of navigator.userAgent called from the worker. | 
 |   CheckUserAgentString( | 
 |       EvalJs(browser()->tab_strip_model()->GetActiveWebContents(), | 
 |              "waitForMessage()") | 
 |           .ExtractString()); | 
 | } | 
 |  | 
 | IN_PROC_BROWSER_TEST_F(ChromeWorkerUserAgentBrowserTest, | 
 |                        DedicatedWorkerCreatedFromFrame) { | 
 |   const GURL main_page_url = GURL(base::StrCat( | 
 |       {kOriginUrl, "/create_dedicated_worker.html?worker_url=user_agent.js"})); | 
 |   const GURL worker_url = GURL(base::StrCat({kOriginUrl, "/user_agent.js"})); | 
 |   SetExpectedRequestURLs({main_page_url, worker_url}); | 
 |  | 
 |   std::unique_ptr<content::URLLoaderInterceptor> interceptor = | 
 |       CreateUrlLoaderInterceptor(); | 
 |  | 
 |   // Navigate to the page that has the scripts for registering the worker. | 
 |   ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), main_page_url)); | 
 |  | 
 |   // Check the result of navigator.userAgent called from the worker. | 
 |   CheckUserAgentString( | 
 |       EvalJs(browser()->tab_strip_model()->GetActiveWebContents(), | 
 |              "waitForMessage()") | 
 |           .ExtractString()); | 
 | } | 
 |  | 
 | IN_PROC_BROWSER_TEST_F(ChromeWorkerUserAgentBrowserTest, | 
 |                        DedicatedWorkerCreatedFromDedicatedWorker) { | 
 |   const GURL main_page_url = | 
 |       GURL(base::StrCat({kOriginUrl, | 
 |                          "/create_dedicated_worker.html?worker_url=parent_" | 
 |                          "worker_user_agent.js"})); | 
 |   const GURL worker_url = | 
 |       GURL(base::StrCat({kOriginUrl, "/parent_worker_user_agent.js"})); | 
 |   const GURL user_agent_url = | 
 |       GURL(base::StrCat({kOriginUrl, "/user_agent.js"})); | 
 |   SetExpectedRequestURLs({main_page_url, worker_url, user_agent_url}); | 
 |  | 
 |   std::unique_ptr<content::URLLoaderInterceptor> interceptor = | 
 |       CreateUrlLoaderInterceptor(); | 
 |  | 
 |   // Navigate to the page that has the scripts for registering the worker. | 
 |   ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), main_page_url)); | 
 |  | 
 |   // Check the result of navigator.userAgent called from the worker. | 
 |   CheckUserAgentString( | 
 |       EvalJs(browser()->tab_strip_model()->GetActiveWebContents(), | 
 |              "waitForMessage()") | 
 |           .ExtractString()); | 
 | } |