[M85 Dev][EFL] Fix errors to generate ninja files
[platform/framework/web/chromium-efl.git] / chrome / browser / chrome_service_worker_browsertest.cc
1 // Copyright 2014 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 // This file tests that Service Workers (a Content feature) work in the Chromium
6 // embedder.
7
8 #include "base/bind.h"
9 #include "base/command_line.h"
10 #include "base/files/scoped_temp_dir.h"
11 #include "base/numerics/safe_conversions.h"
12 #include "base/path_service.h"
13 #include "base/run_loop.h"
14 #include "base/strings/stringprintf.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "base/test/metrics/histogram_tester.h"
17 #include "base/test/scoped_feature_list.h"
18 #include "base/threading/thread_restrictions.h"
19 #include "build/build_config.h"
20 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
21 #include "chrome/browser/profiles/profile.h"
22 #include "chrome/browser/ui/browser.h"
23 #include "chrome/browser/ui/browser_window.h"
24 #include "chrome/browser/ui/tabs/tab_strip_model.h"
25 #include "chrome/common/chrome_paths.h"
26 #include "chrome/common/chrome_switches.h"
27 #include "chrome/test/base/in_process_browser_test.h"
28 #include "chrome/test/base/test_chrome_web_ui_controller_factory.h"
29 #include "chrome/test/base/ui_test_utils.h"
30 #include "components/content_settings/browser/tab_specific_content_settings.h"
31 #include "components/content_settings/core/browser/host_content_settings_map.h"
32 #include "components/content_settings/core/common/pref_names.h"
33 #include "components/favicon/content/content_favicon_driver.h"
34 #include "components/favicon/core/favicon_driver_observer.h"
35 #include "components/nacl/common/buildflags.h"
36 #include "content/public/browser/browser_context.h"
37 #include "content/public/browser/browser_task_traits.h"
38 #include "content/public/browser/browser_thread.h"
39 #include "content/public/browser/render_frame_host.h"
40 #include "content/public/browser/service_worker_context.h"
41 #include "content/public/browser/storage_partition.h"
42 #include "content/public/browser/url_data_source.h"
43 #include "content/public/browser/web_contents.h"
44 #include "content/public/browser/web_ui_controller.h"
45 #include "content/public/test/browser_test.h"
46 #include "content/public/test/browser_test_utils.h"
47 #include "net/dns/mock_host_resolver.h"
48 #include "net/test/embedded_test_server/embedded_test_server.h"
49 #include "net/test/embedded_test_server/http_request.h"
50 #include "net/test/embedded_test_server/http_response.h"
51 #include "ppapi/shared_impl/ppapi_switches.h"
52 #include "third_party/blink/public/common/messaging/string_message_codec.h"
53 #include "third_party/blink/public/mojom/service_worker/service_worker_registration.mojom.h"
54
55 namespace chrome_service_worker_browser_test {
56
57 const char kInstallAndWaitForActivatedPage[] =
58     "<script>"
59     "navigator.serviceWorker.register('./sw.js', {scope: './scope/'})"
60     "  .then(function(reg) {"
61     "      reg.addEventListener('updatefound', function() {"
62     "          var worker = reg.installing;"
63     "          worker.addEventListener('statechange', function() {"
64     "              if (worker.state == 'activated')"
65     "                document.title = 'READY';"
66     "            });"
67     "        });"
68     "    });"
69     "</script>";
70
71 class ChromeServiceWorkerTest : public InProcessBrowserTest {
72  protected:
73   ChromeServiceWorkerTest() {
74     EXPECT_TRUE(service_worker_dir_.CreateUniqueTempDir());
75     EXPECT_TRUE(base::CreateDirectoryAndGetError(
76         service_worker_dir_.GetPath().Append(
77             FILE_PATH_LITERAL("scope")), nullptr));
78   }
79   ~ChromeServiceWorkerTest() override {}
80
81   void WriteFile(const base::FilePath::StringType& filename,
82                  base::StringPiece contents) {
83     base::ScopedAllowBlockingForTesting allow_blocking;
84     EXPECT_EQ(base::checked_cast<int>(contents.size()),
85               base::WriteFile(service_worker_dir_.GetPath().Append(filename),
86                               contents.data(), contents.size()));
87   }
88
89   void NavigateToPageAndWaitForReadyTitle(const std::string path) {
90     const base::string16 expected_title1 = base::ASCIIToUTF16("READY");
91     content::TitleWatcher title_watcher1(
92         browser()->tab_strip_model()->GetActiveWebContents(), expected_title1);
93     ui_test_utils::NavigateToURL(browser(),
94                                  embedded_test_server()->GetURL(path));
95     EXPECT_EQ(expected_title1, title_watcher1.WaitAndGetTitle());
96   }
97
98   void InitializeServer() {
99     embedded_test_server()->ServeFilesFromDirectory(
100         service_worker_dir_.GetPath());
101     ASSERT_TRUE(embedded_test_server()->Start());
102   }
103
104   content::ServiceWorkerContext* GetServiceWorkerContext() {
105     return content::BrowserContext::GetDefaultStoragePartition(
106                browser()->profile())
107         ->GetServiceWorkerContext();
108   }
109
110   base::ScopedTempDir service_worker_dir_;
111
112  private:
113   DISALLOW_COPY_AND_ASSIGN(ChromeServiceWorkerTest);
114 };
115
116 template <typename T>
117 static void ExpectResultAndRun(T expected,
118                                const base::Closure& continuation,
119                                T actual) {
120   EXPECT_EQ(expected, actual);
121   continuation.Run();
122 }
123
124 // http://crbug.com/368570
125 IN_PROC_BROWSER_TEST_F(ChromeServiceWorkerTest,
126                        CanShutDownWithRegisteredServiceWorker) {
127   WriteFile(FILE_PATH_LITERAL("service_worker.js"), "");
128
129   embedded_test_server()->ServeFilesFromDirectory(
130       service_worker_dir_.GetPath());
131   ASSERT_TRUE(embedded_test_server()->Start());
132
133   base::RunLoop run_loop;
134   blink::mojom::ServiceWorkerRegistrationOptions options(
135       embedded_test_server()->GetURL("/"), blink::mojom::ScriptType::kClassic,
136       blink::mojom::ServiceWorkerUpdateViaCache::kImports);
137   GetServiceWorkerContext()->RegisterServiceWorker(
138       embedded_test_server()->GetURL("/service_worker.js"), options,
139       base::BindOnce(&ExpectResultAndRun<bool>, true, run_loop.QuitClosure()));
140   run_loop.Run();
141
142   // Leave the Service Worker registered, and make sure that the browser can
143   // shut down without DCHECK'ing. It'd be nice to check here that the SW is
144   // actually occupying a process, but we don't yet have the public interface to
145   // do that.
146 }
147
148 // http://crbug.com/419290
149 IN_PROC_BROWSER_TEST_F(ChromeServiceWorkerTest,
150                        CanCloseIncognitoWindowWithServiceWorkerController) {
151   WriteFile(FILE_PATH_LITERAL("service_worker.js"), "");
152   WriteFile(FILE_PATH_LITERAL("service_worker.js.mock-http-headers"),
153             "HTTP/1.1 200 OK\nContent-Type: text/javascript");
154   WriteFile(FILE_PATH_LITERAL("test.html"), "");
155   InitializeServer();
156
157   Browser* incognito = CreateIncognitoBrowser();
158
159   base::RunLoop run_loop;
160   blink::mojom::ServiceWorkerRegistrationOptions options(
161       embedded_test_server()->GetURL("/"), blink::mojom::ScriptType::kClassic,
162       blink::mojom::ServiceWorkerUpdateViaCache::kImports);
163   GetServiceWorkerContext()->RegisterServiceWorker(
164       embedded_test_server()->GetURL("/service_worker.js"), options,
165       base::BindOnce(&ExpectResultAndRun<bool>, true, run_loop.QuitClosure()));
166   run_loop.Run();
167
168   ui_test_utils::NavigateToURL(incognito,
169                                embedded_test_server()->GetURL("/test.html"));
170
171   CloseBrowserSynchronously(incognito);
172
173   // Test passes if we don't crash.
174 }
175
176 IN_PROC_BROWSER_TEST_F(ChromeServiceWorkerTest,
177                        FailRegisterServiceWorkerWhenJSDisabled) {
178   WriteFile(FILE_PATH_LITERAL("service_worker.js"), "");
179   InitializeServer();
180
181   HostContentSettingsMapFactory::GetForProfile(browser()->profile())
182       ->SetDefaultContentSetting(ContentSettingsType::JAVASCRIPT,
183                                  CONTENT_SETTING_BLOCK);
184
185   base::RunLoop run_loop;
186   blink::mojom::ServiceWorkerRegistrationOptions options(
187       embedded_test_server()->GetURL("/"), blink::mojom::ScriptType::kClassic,
188       blink::mojom::ServiceWorkerUpdateViaCache::kImports);
189   GetServiceWorkerContext()->RegisterServiceWorker(
190       embedded_test_server()->GetURL("/service_worker.js"), options,
191       base::BindOnce(&ExpectResultAndRun<bool>, false, run_loop.QuitClosure()));
192   run_loop.Run();
193 }
194
195 IN_PROC_BROWSER_TEST_F(ChromeServiceWorkerTest,
196                        FallbackMainResourceRequestWhenJSDisabled) {
197   WriteFile(
198       FILE_PATH_LITERAL("sw.js"),
199       "self.onfetch = function(e) {"
200       "  e.respondWith(new Response('<title>Fail</title>',"
201       "                             {headers: {'Content-Type': 'text/html'}}));"
202       "};");
203   WriteFile(FILE_PATH_LITERAL("scope/done.html"), "<title>Done</title>");
204   WriteFile(FILE_PATH_LITERAL("test.html"), kInstallAndWaitForActivatedPage);
205   InitializeServer();
206   NavigateToPageAndWaitForReadyTitle("/test.html");
207
208   GetServiceWorkerContext()->StopAllServiceWorkersForOrigin(
209       embedded_test_server()->base_url());
210   HostContentSettingsMapFactory::GetForProfile(browser()->profile())
211       ->SetDefaultContentSetting(ContentSettingsType::JAVASCRIPT,
212                                  CONTENT_SETTING_BLOCK);
213
214   const base::string16 expected_title2 = base::ASCIIToUTF16("Done");
215   content::TitleWatcher title_watcher2(
216       browser()->tab_strip_model()->GetActiveWebContents(), expected_title2);
217   ui_test_utils::NavigateToURL(
218       browser(),
219       embedded_test_server()->GetURL("/scope/done.html"));
220   EXPECT_EQ(expected_title2, title_watcher2.WaitAndGetTitle());
221
222   content::WebContents* web_contents =
223       browser()->tab_strip_model()->GetActiveWebContents();
224   EXPECT_TRUE(content_settings::TabSpecificContentSettings::FromWebContents(
225                   web_contents)
226                   ->IsContentBlocked(ContentSettingsType::JAVASCRIPT));
227 }
228
229 IN_PROC_BROWSER_TEST_F(ChromeServiceWorkerTest,
230                        StartServiceWorkerAndDispatchMessage) {
231   base::RunLoop run_loop;
232   blink::TransferableMessage msg;
233   const base::string16 message_data = base::UTF8ToUTF16("testMessage");
234
235   WriteFile(FILE_PATH_LITERAL("sw.js"), "self.onfetch = function(e) {};");
236   WriteFile(FILE_PATH_LITERAL("test.html"), kInstallAndWaitForActivatedPage);
237   InitializeServer();
238   NavigateToPageAndWaitForReadyTitle("/test.html");
239   msg.owned_encoded_message = blink::EncodeStringMessage(message_data);
240   msg.encoded_message = msg.owned_encoded_message;
241
242   content::GetIOThreadTaskRunner({})->PostTask(
243       FROM_HERE,
244       base::BindOnce(
245           &content::ServiceWorkerContext::StartServiceWorkerAndDispatchMessage,
246           base::Unretained(GetServiceWorkerContext()),
247           embedded_test_server()->GetURL("/scope/"), std::move(msg),
248           base::BindRepeating(&ExpectResultAndRun<bool>, true,
249                               run_loop.QuitClosure())));
250
251   run_loop.Run();
252 }
253
254 class ChromeServiceWorkerFetchTest : public ChromeServiceWorkerTest {
255  protected:
256   ChromeServiceWorkerFetchTest() {}
257   ~ChromeServiceWorkerFetchTest() override {}
258
259   void SetUpOnMainThread() override {
260     WriteServiceWorkerFetchTestFiles();
261     embedded_test_server()->ServeFilesFromDirectory(
262         service_worker_dir_.GetPath());
263     ASSERT_TRUE(embedded_test_server()->Start());
264     InitializeServiceWorkerFetchTestPage();
265   }
266
267   std::string ExecuteScriptAndExtractString(const std::string& js) {
268     std::string result;
269     EXPECT_TRUE(content::ExecuteScriptAndExtractString(
270         browser()->tab_strip_model()->GetActiveWebContents(), js, &result));
271     return result;
272   }
273
274   std::string RequestString(const std::string& url,
275                             const std::string& mode,
276                             const std::string& credentials) const {
277     return base::StringPrintf("url:%s, mode:%s, credentials:%s\n", url.c_str(),
278                               mode.c_str(), credentials.c_str());
279   }
280
281   std::string GetURL(const std::string& relative_url) const {
282     return embedded_test_server()->GetURL(relative_url).spec();
283   }
284
285  private:
286   void WriteServiceWorkerFetchTestFiles() {
287     WriteFile(FILE_PATH_LITERAL("sw.js"),
288               "this.onactivate = function(event) {"
289               "  event.waitUntil(self.clients.claim());"
290               "};"
291               "this.onfetch = function(event) {"
292               // Ignore the default favicon request. The default favicon request
293               // is sent after the page loading is finished, and we can't
294               // control the timing of the request. If the request is sent after
295               // clients.claim() is called, fetch event for the default favicon
296               // request is triggered and the tests become flaky. See
297               // https://crbug.com/912543.
298               "  if (event.request.url.endsWith('/favicon.ico')) {"
299               "    return;"
300               "  }"
301               "  event.respondWith("
302               "      self.clients.matchAll().then(function(clients) {"
303               "          clients.forEach(function(client) {"
304               "              client.postMessage("
305               "                'url:' + event.request.url + ', ' +"
306               "                'mode:' + event.request.mode + ', ' +"
307               "                'credentials:' + event.request.credentials"
308               "              );"
309               "            });"
310               "          return fetch(event.request);"
311               "        }));"
312               "};");
313     WriteFile(FILE_PATH_LITERAL("test.html"),
314               "<script>"
315               "navigator.serviceWorker.register('./sw.js', {scope: './'})"
316               "  .then(function(reg) {"
317               "      reg.addEventListener('updatefound', function() {"
318               "          var worker = reg.installing;"
319               "          worker.addEventListener('statechange', function() {"
320               "              if (worker.state == 'activated')"
321               "                document.title = 'READY';"
322               "            });"
323               "        });"
324               "    });"
325               "var reportOnFetch = true;"
326               "var issuedRequests = [];"
327               "function reportRequests() {"
328               "  var str = '';"
329               "  issuedRequests.forEach(function(data) {"
330               "      str += data + '\\n';"
331               "    });"
332               "  window.domAutomationController.send(str);"
333               "}"
334               "navigator.serviceWorker.addEventListener("
335               "    'message',"
336               "    function(event) {"
337               "      issuedRequests.push(event.data);"
338               "      if (reportOnFetch) {"
339               "        reportRequests();"
340               "      }"
341               "    }, false);"
342               "</script>");
343   }
344
345   void InitializeServiceWorkerFetchTestPage() {
346     // The message "READY" will be sent when the service worker is activated.
347     const base::string16 expected_title = base::ASCIIToUTF16("READY");
348     content::TitleWatcher title_watcher(
349         browser()->tab_strip_model()->GetActiveWebContents(), expected_title);
350     ui_test_utils::NavigateToURL(browser(),
351                                  embedded_test_server()->GetURL("/test.html"));
352     EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
353   }
354
355   DISALLOW_COPY_AND_ASSIGN(ChromeServiceWorkerFetchTest);
356 };
357
358 class FaviconUpdateWaiter : public favicon::FaviconDriverObserver {
359  public:
360   explicit FaviconUpdateWaiter(content::WebContents* web_contents) {
361     scoped_observer_.Add(
362         favicon::ContentFaviconDriver::FromWebContents(web_contents));
363   }
364   ~FaviconUpdateWaiter() override = default;
365
366   void Wait() {
367     if (updated_)
368       return;
369
370     base::RunLoop run_loop;
371     quit_closure_ = run_loop.QuitClosure();
372     run_loop.Run();
373   }
374
375  private:
376   void OnFaviconUpdated(favicon::FaviconDriver* favicon_driver,
377                         NotificationIconType notification_icon_type,
378                         const GURL& icon_url,
379                         bool icon_url_changed,
380                         const gfx::Image& image) override {
381     updated_ = true;
382     if (!quit_closure_.is_null())
383       std::move(quit_closure_).Run();
384   }
385
386   bool updated_ = false;
387   ScopedObserver<favicon::FaviconDriver, favicon::FaviconDriverObserver>
388       scoped_observer_{this};
389   base::OnceClosure quit_closure_;
390
391   DISALLOW_COPY_AND_ASSIGN(FaviconUpdateWaiter);
392 };
393
394 class ChromeServiceWorkerLinkFetchTest : public ChromeServiceWorkerFetchTest {
395  protected:
396   ChromeServiceWorkerLinkFetchTest() {}
397   ~ChromeServiceWorkerLinkFetchTest() override {}
398   void SetUpOnMainThread() override {
399     // Map all hosts to localhost and setup the EmbeddedTestServer for
400     // redirects.
401     host_resolver()->AddRule("*", "127.0.0.1");
402     ChromeServiceWorkerFetchTest::SetUpOnMainThread();
403   }
404   std::string ExecuteManifestFetchTest(const std::string& url,
405                                        const std::string& cross_origin) {
406     std::string js(
407         base::StringPrintf("reportOnFetch = false;"
408                            "var link = document.createElement('link');"
409                            "link.rel = 'manifest';"
410                            "link.href = '%s';",
411                            url.c_str()));
412     if (!cross_origin.empty()) {
413       js +=
414           base::StringPrintf("link.crossOrigin = '%s';", cross_origin.c_str());
415     }
416     js += "document.head.appendChild(link);";
417     ExecuteJavaScriptForTests(js);
418     return GetManifestAndIssuedRequests();
419   }
420
421   std::string ExecuteFaviconFetchTest(const std::string& url) {
422     FaviconUpdateWaiter waiter(
423         browser()->tab_strip_model()->GetActiveWebContents());
424     std::string js(
425         base::StringPrintf("reportOnFetch = false;"
426                            "var link = document.createElement('link');"
427                            "link.rel = 'icon';"
428                            "link.href = '%s';"
429                            "document.head.appendChild(link);",
430                            url.c_str()));
431     ExecuteJavaScriptForTests(js);
432     waiter.Wait();
433     return ExecuteScriptAndExtractString("reportRequests();");
434   }
435
436   void CopyTestFile(const std::string& src, const std::string& dst) {
437     base::ScopedAllowBlockingForTesting allow_blocking;
438     base::FilePath test_data_dir;
439     base::PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir);
440     EXPECT_TRUE(base::CopyFile(test_data_dir.AppendASCII(src),
441                                service_worker_dir_.GetPath().AppendASCII(dst)));
442   }
443
444  private:
445   void ExecuteJavaScriptForTests(const std::string& js) {
446     base::RunLoop run_loop;
447     browser()
448         ->tab_strip_model()
449         ->GetActiveWebContents()
450         ->GetMainFrame()
451         ->ExecuteJavaScriptForTests(
452             base::ASCIIToUTF16(js),
453             base::BindOnce([](const base::Closure& quit_callback,
454                               base::Value result) { quit_callback.Run(); },
455                            run_loop.QuitClosure()));
456     run_loop.Run();
457   }
458
459   std::string GetManifestAndIssuedRequests() {
460     base::RunLoop run_loop;
461     browser()->tab_strip_model()->GetActiveWebContents()->GetManifest(
462         base::BindOnce(&ManifestCallbackAndRun, run_loop.QuitClosure()));
463     run_loop.Run();
464     return ExecuteScriptAndExtractString(
465         "if (issuedRequests.length != 0) reportRequests();"
466         "else reportOnFetch = true;");
467   }
468
469   static void ManifestCallbackAndRun(const base::Closure& continuation,
470                                      const GURL&,
471                                      const blink::Manifest&) {
472     continuation.Run();
473   }
474
475   DISALLOW_COPY_AND_ASSIGN(ChromeServiceWorkerLinkFetchTest);
476 };
477
478 IN_PROC_BROWSER_TEST_F(ChromeServiceWorkerLinkFetchTest, ManifestSameOrigin) {
479   // <link rel="manifest" href="manifest.json">
480   EXPECT_EQ(RequestString(GetURL("/manifest.json"), "cors", "omit"),
481             ExecuteManifestFetchTest("manifest.json", ""));
482 }
483
484 IN_PROC_BROWSER_TEST_F(ChromeServiceWorkerLinkFetchTest,
485                        ManifestSameOriginUseCredentials) {
486   // <link rel="manifest" href="manifest.json" crossorigin="use-credentials">
487   EXPECT_EQ(RequestString(GetURL("/manifest.json"), "cors", "include"),
488             ExecuteManifestFetchTest("manifest.json", "use-credentials"));
489 }
490
491 IN_PROC_BROWSER_TEST_F(ChromeServiceWorkerLinkFetchTest, ManifestOtherOrigin) {
492   // <link rel="manifest" href="http://www.example.com:PORT/manifest.json">
493   const std::string url = embedded_test_server()
494                               ->GetURL("www.example.com", "/manifest.json")
495                               .spec();
496   EXPECT_EQ(RequestString(url, "cors", "omit"),
497             ExecuteManifestFetchTest(url, ""));
498 }
499
500 IN_PROC_BROWSER_TEST_F(ChromeServiceWorkerLinkFetchTest,
501                        ManifestOtherOriginUseCredentials) {
502   // <link rel="manifest" href="http://www.example.com:PORT/manifest.json"
503   //  crossorigin="use-credentials">
504   const std::string url = embedded_test_server()
505                               ->GetURL("www.example.com", "/manifest.json")
506                               .spec();
507   EXPECT_EQ(RequestString(url, "cors", "include"),
508             ExecuteManifestFetchTest(url, "use-credentials"));
509 }
510
511 IN_PROC_BROWSER_TEST_F(ChromeServiceWorkerLinkFetchTest, FaviconSameOrigin) {
512   // <link rel="favicon" href="fav.png">
513   CopyTestFile("favicon/icon.png", "fav.png");
514   EXPECT_EQ(RequestString(GetURL("/fav.png"), "no-cors", "include"),
515             ExecuteFaviconFetchTest("/fav.png"));
516 }
517
518 IN_PROC_BROWSER_TEST_F(ChromeServiceWorkerLinkFetchTest, FaviconOtherOrigin) {
519   // <link rel="favicon" href="http://www.example.com:PORT/fav.png">
520   CopyTestFile("favicon/icon.png", "fav.png");
521   const std::string url =
522       embedded_test_server()->GetURL("www.example.com", "/fav.png").spec();
523   EXPECT_EQ("", ExecuteFaviconFetchTest(url));
524 }
525
526 #if BUILDFLAG(ENABLE_NACL)
527 // This test registers a service worker and then loads a controlled iframe that
528 // creates a PNaCl plugin in an <embed> element. Once loaded, the PNaCl plugin
529 // is ordered to do a resource request for "/echo". The service worker records
530 // all the fetch events it sees. Since requests for plug-ins and requests
531 // initiated by plug-ins should not be interecepted by service workers, we
532 // expect that the the service worker only see the navigation request for the
533 // iframe.
534 class ChromeServiceWorkerFetchPPAPITest : public ChromeServiceWorkerFetchTest {
535  protected:
536   ChromeServiceWorkerFetchPPAPITest() {}
537   ~ChromeServiceWorkerFetchPPAPITest() override {}
538
539   void SetUpCommandLine(base::CommandLine* command_line) override {
540     ChromeServiceWorkerFetchTest::SetUpCommandLine(command_line);
541     // Use --enable-nacl flag to ensure the PNaCl module can load (without
542     // needing to use an OT token)
543     command_line->AppendSwitch(switches::kEnableNaCl);
544   }
545
546   void SetUpOnMainThread() override {
547     base::FilePath document_root;
548     ASSERT_TRUE(ui_test_utils::GetRelativeBuildDirectory(&document_root));
549     embedded_test_server()->AddDefaultHandlers(
550         document_root.Append(FILE_PATH_LITERAL("nacl_test_data"))
551             .Append(FILE_PATH_LITERAL("pnacl")));
552     ChromeServiceWorkerFetchTest::SetUpOnMainThread();
553     test_page_url_ = GetURL("/pnacl_url_loader.html");
554   }
555
556   std::string GetNavigationRequestString(const std::string& fragment) const {
557     return RequestString(test_page_url_ + fragment, "navigate", "include");
558   }
559
560   std::string ExecutePNACLUrlLoaderTest(const std::string& mode) {
561     std::string result(ExecuteScriptAndExtractString(
562         base::StringPrintf("reportOnFetch = false;"
563                            "var iframe = document.createElement('iframe');"
564                            "iframe.src='%s#%s';"
565                            "document.body.appendChild(iframe);",
566                            test_page_url_.c_str(), mode.c_str())));
567     EXPECT_EQ(base::StringPrintf("OnOpen%s", mode.c_str()), result);
568     return ExecuteScriptAndExtractString("reportRequests();");
569   }
570
571  private:
572   std::string test_page_url_;
573
574   DISALLOW_COPY_AND_ASSIGN(ChromeServiceWorkerFetchPPAPITest);
575 };
576
577 IN_PROC_BROWSER_TEST_F(ChromeServiceWorkerFetchPPAPITest,
578                        NotInterceptedByServiceWorker) {
579   // Only the navigation to the iframe should be intercepted by the service
580   // worker. The request for the PNaCl manifest ("/pnacl_url_loader.nmf"),
581   // the request for the compiled code ("/pnacl_url_loader_newlib_pnacl.pexe"),
582   // and any other requests initiated by the plug-in ("/echo") should not be
583   // seen by the service worker.
584   const std::string fragment =
585       "NotIntercepted";  // this string is not important.
586   EXPECT_EQ(GetNavigationRequestString("#" + fragment),
587             ExecutePNACLUrlLoaderTest(fragment));
588 }
589 #endif  // BUILDFLAG(ENABLE_NACL)
590
591 class ChromeServiceWorkerNavigationHintTest : public ChromeServiceWorkerTest {
592  protected:
593   void RunNavigationHintTest(
594       const char* scope,
595       content::StartServiceWorkerForNavigationHintResult expected_result,
596       bool expected_started) {
597     base::RunLoop run_loop;
598     GetServiceWorkerContext()->StartServiceWorkerForNavigationHint(
599         embedded_test_server()->GetURL(scope),
600         base::BindOnce(&ExpectResultAndRun<
601                            content::StartServiceWorkerForNavigationHintResult>,
602                        expected_result, run_loop.QuitClosure()));
603     run_loop.Run();
604     if (expected_started) {
605       histogram_tester_.ExpectBucketCount(
606           "ServiceWorker.StartWorker.Purpose",
607           27 /* ServiceWorkerMetrics::EventType::NAVIGATION_HINT  */, 1);
608       histogram_tester_.ExpectBucketCount(
609           "ServiceWorker.StartWorker.StatusByPurpose_NAVIGATION_HINT",
610           0 /* SERVICE_WORKER_OK  */, 1);
611     } else {
612       histogram_tester_.ExpectTotalCount("ServiceWorker.StartWorker.Purpose",
613                                          0);
614       histogram_tester_.ExpectTotalCount(
615           "ServiceWorker.StartWorker.StatusByPurpose_NAVIGATION_HINT", 0);
616     }
617     histogram_tester_.ExpectBucketCount(
618         "ServiceWorker.StartForNavigationHint.Result",
619         static_cast<int>(expected_result), 1);
620   }
621
622   base::HistogramTester histogram_tester_;
623 };
624
625 IN_PROC_BROWSER_TEST_F(ChromeServiceWorkerNavigationHintTest, Started) {
626   WriteFile(FILE_PATH_LITERAL("sw.js"), "self.onfetch = function(e) {};");
627   WriteFile(FILE_PATH_LITERAL("test.html"), kInstallAndWaitForActivatedPage);
628   InitializeServer();
629   NavigateToPageAndWaitForReadyTitle("/test.html");
630   GetServiceWorkerContext()->StopAllServiceWorkersForOrigin(
631       embedded_test_server()->base_url());
632   RunNavigationHintTest(
633       "/scope/", content::StartServiceWorkerForNavigationHintResult::STARTED,
634       true);
635 }
636
637 IN_PROC_BROWSER_TEST_F(ChromeServiceWorkerNavigationHintTest, AlreadyRunning) {
638   WriteFile(FILE_PATH_LITERAL("sw.js"), "self.onfetch = function(e) {};");
639   WriteFile(FILE_PATH_LITERAL("test.html"), kInstallAndWaitForActivatedPage);
640   InitializeServer();
641   NavigateToPageAndWaitForReadyTitle("/test.html");
642   RunNavigationHintTest(
643       "/scope/",
644       content::StartServiceWorkerForNavigationHintResult::ALREADY_RUNNING,
645       false);
646 }
647
648 IN_PROC_BROWSER_TEST_F(ChromeServiceWorkerNavigationHintTest,
649                        NoServiceWorkerRegistration) {
650   InitializeServer();
651   RunNavigationHintTest("/scope/",
652                         content::StartServiceWorkerForNavigationHintResult::
653                             NO_SERVICE_WORKER_REGISTRATION,
654                         false);
655 }
656
657 IN_PROC_BROWSER_TEST_F(ChromeServiceWorkerNavigationHintTest,
658                        NoActiveServiceWorkerVersion) {
659   WriteFile(FILE_PATH_LITERAL("sw.js"),
660             "self.oninstall = function(e) {\n"
661             "    e.waitUntil(new Promise(r => { /* never resolve */ }));\n"
662             "  };\n"
663             "self.onfetch = function(e) {};");
664   InitializeServer();
665   base::RunLoop run_loop;
666   blink::mojom::ServiceWorkerRegistrationOptions options(
667       embedded_test_server()->GetURL("/scope/"),
668       blink::mojom::ScriptType::kClassic,
669       blink::mojom::ServiceWorkerUpdateViaCache::kImports);
670   GetServiceWorkerContext()->RegisterServiceWorker(
671       embedded_test_server()->GetURL("/sw.js"), options,
672       base::BindOnce(&ExpectResultAndRun<bool>, true, run_loop.QuitClosure()));
673   run_loop.Run();
674   RunNavigationHintTest("/scope/",
675                         content::StartServiceWorkerForNavigationHintResult::
676                             NO_ACTIVE_SERVICE_WORKER_VERSION,
677                         false);
678 }
679
680 IN_PROC_BROWSER_TEST_F(ChromeServiceWorkerNavigationHintTest, NoFetchHandler) {
681   WriteFile(FILE_PATH_LITERAL("sw.js"), "/* empty */");
682   WriteFile(FILE_PATH_LITERAL("test.html"), kInstallAndWaitForActivatedPage);
683   InitializeServer();
684   NavigateToPageAndWaitForReadyTitle("/test.html");
685   GetServiceWorkerContext()->StopAllServiceWorkersForOrigin(
686       embedded_test_server()->base_url());
687   RunNavigationHintTest(
688       "/scope/",
689       content::StartServiceWorkerForNavigationHintResult::NO_FETCH_HANDLER,
690       false);
691 }
692
693 // Copied from devtools_sanity_browsertest.cc.
694 class StaticURLDataSource : public content::URLDataSource {
695  public:
696   StaticURLDataSource(const std::string& source, const std::string& content)
697       : source_(source), content_(content) {}
698   ~StaticURLDataSource() override = default;
699
700   // content::URLDataSource:
701   std::string GetSource() override { return source_; }
702   void StartDataRequest(const GURL& url,
703                         const content::WebContents::Getter& wc_getter,
704                         GotDataCallback callback) override {
705     std::string data(content_);
706     std::move(callback).Run(base::RefCountedString::TakeString(&data));
707   }
708   std::string GetMimeType(const std::string& path) override {
709     return "application/javascript";
710   }
711   bool ShouldAddContentSecurityPolicy() override { return false; }
712
713  private:
714   const std::string source_;
715   const std::string content_;
716
717   DISALLOW_COPY_AND_ASSIGN(StaticURLDataSource);
718 };
719
720 // Copied from devtools_sanity_browsertest.cc.
721 class MockWebUIProvider
722     : public TestChromeWebUIControllerFactory::WebUIProvider {
723  public:
724   MockWebUIProvider(const std::string& source, const std::string& content)
725       : source_(source), content_(content) {}
726   ~MockWebUIProvider() override = default;
727
728   std::unique_ptr<content::WebUIController> NewWebUI(content::WebUI* web_ui,
729                                                      const GURL& url) override {
730     content::URLDataSource::Add(
731         Profile::FromWebUI(web_ui),
732         std::make_unique<StaticURLDataSource>(source_, content_));
733     return std::make_unique<content::WebUIController>(web_ui);
734   }
735
736  private:
737   const std::string source_;
738   const std::string content_;
739   DISALLOW_COPY_AND_ASSIGN(MockWebUIProvider);
740 };
741
742 // Tests that registering a service worker with a chrome:// URL fails.
743 IN_PROC_BROWSER_TEST_F(ChromeServiceWorkerTest, DisallowChromeScheme) {
744   const GURL kScript("chrome://dummyurl/sw.js");
745   const GURL kScope("chrome://dummyurl");
746
747   // Make chrome://dummyurl/sw.js serve a service worker script.
748   TestChromeWebUIControllerFactory test_factory;
749   MockWebUIProvider mock_provider("serviceworker", "// empty service worker");
750   test_factory.AddFactoryOverride(kScript.host(), &mock_provider);
751   content::WebUIControllerFactory::RegisterFactory(&test_factory);
752
753   // Try to register the service worker.
754   base::RunLoop run_loop;
755   bool result = true;
756   blink::mojom::ServiceWorkerRegistrationOptions options(
757       kScope, blink::mojom::ScriptType::kClassic,
758       blink::mojom::ServiceWorkerUpdateViaCache::kImports);
759   GetServiceWorkerContext()->RegisterServiceWorker(
760       kScript, options,
761       base::BindOnce(
762           [](base::OnceClosure quit_closure, bool* out_result, bool result) {
763             *out_result = result;
764             std::move(quit_closure).Run();
765           },
766           run_loop.QuitClosure(), &result));
767   run_loop.Run();
768
769   // Registration should fail. This is the desired behavior. At the time of this
770   // writing, there are a few reasons the registration fails:
771   // * OriginCanAccessServiceWorkers() returns false for the "chrome" scheme.
772   // * Even if that returned true, the URL loader factory bundle used to make
773   //   the resource request in ServiceWorkerNewScriptLoader doesn't support
774   //   the "chrome" scheme. This is because:
775   //     * The call to RegisterNonNetworkSubresourceURLLoaderFactories() from
776   //       CreateFactoryBundle() in embedded_worker_instance.cc doesn't register
777   //       the "chrome" scheme, because there is no frame/web_contents.
778   //     * Even if that registered a factory, CreateFactoryBundle() would
779   //       skip it because GetServiceWorkerSchemes() doesn't include "chrome".
780   //
781   // It's difficult to change all these, so the test author hasn't actually
782   // changed Chrome in a way that makes this test fail, to prove that the test
783   // would be effective at catching a regression.
784   EXPECT_FALSE(result);
785 }
786
787 enum class ServicifiedFeatures { kNone, kServiceWorker, kNetwork };
788
789 // A simple fixture used for navigation preload tests so far. The fixture
790 // stashes the HttpRequest to a certain URL, useful for inspecting the headers
791 // to see if it was a navigation preload request and if it contained cookies.
792 //
793 // This is in //chrome instead of //content since the tests exercise the
794 // kBlockThirdPartyCookies preference which is not a //content concept.
795 class ChromeServiceWorkerNavigationPreloadTest : public InProcessBrowserTest {
796  public:
797   ChromeServiceWorkerNavigationPreloadTest() = default;
798
799   void SetUp() override {
800     embedded_test_server()->RegisterRequestHandler(base::BindRepeating(
801         &ChromeServiceWorkerNavigationPreloadTest::HandleRequest,
802         base::Unretained(this)));
803     ASSERT_TRUE(embedded_test_server()->InitializeAndListen());
804
805     InProcessBrowserTest::SetUp();
806   }
807
808   void SetUpOnMainThread() override {
809     // Make all hosts resolve to 127.0.0.1 so the same embedded test server can
810     // be used for cross-origin URLs.
811     host_resolver()->AddRule("*", "127.0.0.1");
812
813     embedded_test_server()->StartAcceptingConnections();
814   }
815
816   std::unique_ptr<net::test_server::HttpResponse> HandleRequest(
817       const net::test_server::HttpRequest& request) {
818     // Intercept requests to the "test" endpoint.
819     GURL url = request.base_url;
820     url = url.Resolve(request.relative_url);
821     if (url.path() != "/service_worker/test")
822       return nullptr;
823
824     // Stash the request for testing. We'd typically prefer to echo back the
825     // request and test the resulting page contents, but that becomes
826     // cumbersome if the test involves cross-origin frames.
827     EXPECT_FALSE(received_request_);
828     received_request_ = request;
829
830     // Respond with OK.
831     std::unique_ptr<net::test_server::BasicHttpResponse> http_response(
832         new net::test_server::BasicHttpResponse());
833     http_response->set_code(net::HTTP_OK);
834     http_response->set_content("OK");
835     http_response->set_content_type("text/plain");
836     return http_response;
837   }
838
839   bool HasHeader(const net::test_server::HttpRequest& request,
840                  const std::string& name) const {
841     return request.headers.find(name) != request.headers.end();
842   }
843
844   std::string GetHeader(const net::test_server::HttpRequest& request,
845                         const std::string& name) const {
846     const auto& iter = request.headers.find(name);
847     EXPECT_TRUE(iter != request.headers.end());
848     if (iter == request.headers.end())
849       return std::string();
850     return iter->second;
851   }
852
853   bool has_received_request() const { return received_request_.has_value(); }
854
855   const net::test_server::HttpRequest& received_request() const {
856     return *received_request_;
857   }
858
859  private:
860   base::test::ScopedFeatureList scoped_feature_list_;
861
862   // The request that hit the "test" endpoint.
863   base::Optional<net::test_server::HttpRequest> received_request_;
864
865   DISALLOW_COPY_AND_ASSIGN(ChromeServiceWorkerNavigationPreloadTest);
866 };
867
868 // Tests navigation preload during a navigation in the top-level frame
869 // when third-party cookies are blocked. The navigation preload request
870 // should be sent with cookies as normal. Regression test for
871 // https://crbug.com/913220.
872 IN_PROC_BROWSER_TEST_F(ChromeServiceWorkerNavigationPreloadTest,
873                        TopFrameWithThirdPartyBlocking) {
874   // Enable third-party cookie blocking.
875   browser()->profile()->GetPrefs()->SetBoolean(prefs::kBlockThirdPartyCookies,
876                                                true);
877
878   // Load a page that registers a service worker.
879   ui_test_utils::NavigateToURL(
880       browser(), embedded_test_server()->GetURL(
881                      "/service_worker/create_service_worker.html"));
882   EXPECT_EQ("DONE", EvalJs(browser()->tab_strip_model()->GetActiveWebContents(),
883                            "register('navigation_preload_worker.js');"));
884
885   // Also set cookies.
886   EXPECT_EQ("foo=bar",
887             EvalJs(browser()->tab_strip_model()->GetActiveWebContents(),
888                    "document.cookie = 'foo=bar'; document.cookie;"));
889
890   // Load the test page.
891   ui_test_utils::NavigateToURL(
892       browser(), embedded_test_server()->GetURL("/service_worker/test"));
893
894   // The navigation preload request should have occurred and included cookies.
895   ASSERT_TRUE(has_received_request());
896   EXPECT_EQ("true",
897             GetHeader(received_request(), "Service-Worker-Navigation-Preload"));
898   EXPECT_EQ("foo=bar", GetHeader(received_request(), "Cookie"));
899 }
900
901 // Tests navigation preload during a navigation in a third-party iframe
902 // when third-party cookies are blocked. This blocks service worker as well,
903 // so the navigation preload request should not be sent. And the navigation
904 // request should not include cookies.
905 IN_PROC_BROWSER_TEST_F(ChromeServiceWorkerNavigationPreloadTest,
906                        SubFrameWithThirdPartyBlocking) {
907   // Enable third-party cookie blocking.
908   browser()->profile()->GetPrefs()->SetBoolean(prefs::kBlockThirdPartyCookies,
909                                                true);
910
911   // Load a page that registers a service worker.
912   ui_test_utils::NavigateToURL(
913       browser(), embedded_test_server()->GetURL(
914                      "/service_worker/create_service_worker.html"));
915   EXPECT_EQ("DONE", EvalJs(browser()->tab_strip_model()->GetActiveWebContents(),
916                            "register('navigation_preload_worker.js');"));
917
918   // Also set cookies.
919   EXPECT_EQ("foo=bar",
920             EvalJs(browser()->tab_strip_model()->GetActiveWebContents(),
921                    "document.cookie = 'foo=bar'; document.cookie;"));
922
923   // Generate a cross-origin URL.
924   GURL top_frame_url = embedded_test_server()->GetURL(
925       "/service_worker/page_with_third_party_iframe.html");
926   GURL::Replacements replacements;
927   replacements.SetHostStr("cross-origin.example.com");
928   top_frame_url = top_frame_url.ReplaceComponents(replacements);
929
930   // Navigate to the page and embed a third-party iframe to the test
931   // page.
932   ui_test_utils::NavigateToURL(browser(), top_frame_url);
933   GURL iframe_url = embedded_test_server()->GetURL("/service_worker/test");
934   EXPECT_EQ(true, EvalJs(browser()->tab_strip_model()->GetActiveWebContents(),
935                          "addIframe('" + iframe_url.spec() + "');"));
936
937   // The request should have been received. Because the navigation was for a
938   // third-party iframe with cookies blocked, the service worker should not have
939   // handled the request so navigation preload should not have occurred.
940   // Likewise, the cookies should not have been sent.
941   ASSERT_TRUE(has_received_request());
942   EXPECT_FALSE(
943       HasHeader(received_request(), "Service-Worker-Navigation-Preload"));
944   EXPECT_FALSE(HasHeader(received_request(), "Cookie"));
945 }
946
947 }  // namespace chrome_service_worker_browser_test