blob: e43459379906a8e0ebc2660dc7aba86da1b74f6a [file] [log] [blame]
annekao38685502015-07-14 17:46:391// Copyright 2015 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
avia2f4804a2015-12-24 23:11:135#include <stdint.h>
6
kalman6f984ae2015-09-18 17:21:587#include "base/bind_helpers.h"
avia2f4804a2015-12-24 23:11:138#include "base/macros.h"
kalman6f984ae2015-09-18 17:21:589#include "base/strings/stringprintf.h"
horo1eeddde2015-11-19 05:59:2510#include "base/strings/utf_string_conversions.h"
annekao38685502015-07-14 17:46:3911#include "chrome/browser/extensions/extension_apitest.h"
rdevlin.croninf5863da2015-09-10 19:21:4512#include "chrome/browser/extensions/extension_service.h"
lazyboy561b7de2015-11-19 19:27:3013#include "chrome/browser/notifications/desktop_notification_profile_util.h"
lshang106c1772016-06-06 01:43:2314#include "chrome/browser/permissions/permission_manager.h"
lazyboy561b7de2015-11-19 19:27:3015#include "chrome/browser/push_messaging/push_messaging_app_identifier.h"
16#include "chrome/browser/push_messaging/push_messaging_service_factory.h"
17#include "chrome/browser/push_messaging/push_messaging_service_impl.h"
18#include "chrome/browser/services/gcm/fake_gcm_profile_service.h"
19#include "chrome/browser/services/gcm/gcm_profile_service_factory.h"
annekao1db36fd2015-07-29 17:09:1620#include "chrome/browser/ui/tabs/tab_strip_model.h"
rdevlin.croninf5863da2015-09-10 19:21:4521#include "chrome/test/base/ui_test_utils.h"
sdefresne9fb67692015-08-03 18:48:2222#include "components/version_info/version_info.h"
kalman6f984ae2015-09-18 17:21:5823#include "content/public/browser/navigation_controller.h"
rdevlin.croninf5863da2015-09-10 19:21:4524#include "content/public/browser/navigation_entry.h"
lshang106c1772016-06-06 01:43:2325#include "content/public/browser/permission_type.h"
kalman6f984ae2015-09-18 17:21:5826#include "content/public/browser/web_contents.h"
lazyboybd325ae2015-11-18 21:35:2627#include "content/public/common/content_switches.h"
kalman6f984ae2015-09-18 17:21:5828#include "content/public/common/page_type.h"
lazyboybd325ae2015-11-18 21:35:2629#include "content/public/test/background_sync_test_util.h"
annekao1db36fd2015-07-29 17:09:1630#include "content/public/test/browser_test_utils.h"
kalman6f984ae2015-09-18 17:21:5831#include "extensions/browser/extension_host.h"
lazyboyc3e763a2015-12-09 23:09:5832#include "extensions/browser/extension_registry.h"
kalman6f984ae2015-09-18 17:21:5833#include "extensions/browser/process_manager.h"
34#include "extensions/test/background_page_watcher.h"
annekao38685502015-07-14 17:46:3935#include "extensions/test/extension_test_message_listener.h"
horo1eeddde2015-11-19 05:59:2536#include "net/test/embedded_test_server/embedded_test_server.h"
annekao38685502015-07-14 17:46:3937
38namespace extensions {
39
kalman6f984ae2015-09-18 17:21:5840namespace {
41
42// Pass into ServiceWorkerTest::StartTestFromBackgroundPage to indicate that
43// registration is expected to succeed.
44std::string* const kExpectSuccess = nullptr;
45
46void DoNothingWithBool(bool b) {}
47
lazyboy22eddc712015-12-10 21:16:2648// Returns the newly added WebContents.
49content::WebContents* AddTab(Browser* browser, const GURL& url) {
50 int starting_tab_count = browser->tab_strip_model()->count();
51 ui_test_utils::NavigateToURLWithDisposition(
52 browser, url, NEW_FOREGROUND_TAB,
53 ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
54 int tab_count = browser->tab_strip_model()->count();
55 EXPECT_EQ(starting_tab_count + 1, tab_count);
56 return browser->tab_strip_model()->GetActiveWebContents();
57}
58
59class WebContentsLoadStopObserver : content::WebContentsObserver {
60 public:
61 explicit WebContentsLoadStopObserver(content::WebContents* web_contents)
62 : content::WebContentsObserver(web_contents),
63 load_stop_observed_(false) {}
64
65 void WaitForLoadStop() {
66 if (load_stop_observed_)
67 return;
68 message_loop_runner_ = new content::MessageLoopRunner;
69 message_loop_runner_->Run();
70 }
71
72 private:
73 void DidStopLoading() override {
74 load_stop_observed_ = true;
75 if (message_loop_runner_)
76 message_loop_runner_->Quit();
77 }
78
79 bool load_stop_observed_;
80 scoped_refptr<content::MessageLoopRunner> message_loop_runner_;
81
82 DISALLOW_COPY_AND_ASSIGN(WebContentsLoadStopObserver);
83};
84
kalman6f984ae2015-09-18 17:21:5885} // namespace
86
annekao38685502015-07-14 17:46:3987class ServiceWorkerTest : public ExtensionApiTest {
88 public:
lazyboy20167c22016-05-18 00:59:3089 ServiceWorkerTest() : current_channel_(version_info::Channel::STABLE) {}
annekao38685502015-07-14 17:46:3990
91 ~ServiceWorkerTest() override {}
92
kalman6f984ae2015-09-18 17:21:5893 protected:
94 // Returns the ProcessManager for the test's profile.
95 ProcessManager* process_manager() { return ProcessManager::Get(profile()); }
96
97 // Starts running a test from the background page test extension.
98 //
99 // This registers a service worker with |script_name|, and fetches the
100 // registration result.
101 //
102 // If |error_or_null| is null (kExpectSuccess), success is expected and this
103 // will fail if there is an error.
104 // If |error_or_null| is not null, nothing is assumed, and the error (which
105 // may be empty) is written to it.
106 const Extension* StartTestFromBackgroundPage(const char* script_name,
107 std::string* error_or_null) {
108 const Extension* extension =
109 LoadExtension(test_data_dir_.AppendASCII("service_worker/background"));
110 CHECK(extension);
111 ExtensionHost* background_host =
112 process_manager()->GetBackgroundHostForExtension(extension->id());
113 CHECK(background_host);
114 std::string error;
115 CHECK(content::ExecuteScriptAndExtractString(
116 background_host->host_contents(),
117 base::StringPrintf("test.registerServiceWorker('%s')", script_name),
118 &error));
119 if (error_or_null)
120 *error_or_null = error;
121 else if (!error.empty())
122 ADD_FAILURE() << "Got unexpected error " << error;
123 return extension;
124 }
125
126 // Navigates the browser to a new tab at |url|, waits for it to load, then
127 // returns it.
128 content::WebContents* Navigate(const GURL& url) {
129 ui_test_utils::NavigateToURLWithDisposition(
130 browser(), url, NEW_FOREGROUND_TAB,
131 ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
132 content::WebContents* web_contents =
133 browser()->tab_strip_model()->GetActiveWebContents();
134 content::WaitForLoadStop(web_contents);
135 return web_contents;
136 }
137
138 // Navigates the browser to |url| and returns the new tab's page type.
139 content::PageType NavigateAndGetPageType(const GURL& url) {
140 return Navigate(url)->GetController().GetActiveEntry()->GetPageType();
141 }
142
143 // Extracts the innerText from |contents|.
144 std::string ExtractInnerText(content::WebContents* contents) {
145 std::string inner_text;
146 if (!content::ExecuteScriptAndExtractString(
147 contents,
148 "window.domAutomationController.send(document.body.innerText)",
149 &inner_text)) {
150 ADD_FAILURE() << "Failed to get inner text for "
151 << contents->GetVisibleURL();
152 }
153 return inner_text;
154 }
155
156 // Navigates the browser to |url|, then returns the innerText of the new
157 // tab's WebContents' main frame.
158 std::string NavigateAndExtractInnerText(const GURL& url) {
159 return ExtractInnerText(Navigate(url));
160 }
161
annekao38685502015-07-14 17:46:39162 private:
lazyboy20167c22016-05-18 00:59:30163 // Sets the channel to "stable".
164 // Not useful after we've opened extension Service Workers to stable
165 // channel.
166 // TODO(lazyboy): Remove this when ExtensionServiceWorkersEnabled() is
167 // removed.
annekao38685502015-07-14 17:46:39168 ScopedCurrentChannel current_channel_;
kalman6f984ae2015-09-18 17:21:58169
annekao38685502015-07-14 17:46:39170 DISALLOW_COPY_AND_ASSIGN(ServiceWorkerTest);
171};
172
lazyboybd325ae2015-11-18 21:35:26173class ServiceWorkerBackgroundSyncTest : public ServiceWorkerTest {
174 public:
175 ServiceWorkerBackgroundSyncTest() {}
176 ~ServiceWorkerBackgroundSyncTest() override {}
177
178 void SetUpCommandLine(base::CommandLine* command_line) override {
179 // ServiceWorkerRegistration.sync requires experimental flag.
180 command_line->AppendSwitch(
181 switches::kEnableExperimentalWebPlatformFeatures);
182 ServiceWorkerTest::SetUpCommandLine(command_line);
183 }
184
185 void SetUp() override {
186 content::background_sync_test_util::SetIgnoreNetworkChangeNotifier(true);
187 ServiceWorkerTest::SetUp();
188 }
189
190 private:
191 DISALLOW_COPY_AND_ASSIGN(ServiceWorkerBackgroundSyncTest);
192};
193
lazyboy561b7de2015-11-19 19:27:30194class ServiceWorkerPushMessagingTest : public ServiceWorkerTest {
195 public:
196 ServiceWorkerPushMessagingTest()
197 : gcm_service_(nullptr), push_service_(nullptr) {}
198 ~ServiceWorkerPushMessagingTest() override {}
199
200 void GrantNotificationPermissionForTest(const GURL& url) {
201 GURL origin = url.GetOrigin();
202 DesktopNotificationProfileUtil::GrantPermission(profile(), origin);
lshang106c1772016-06-06 01:43:23203 ASSERT_EQ(blink::mojom::PermissionStatus::GRANTED,
204 PermissionManager::Get(profile())->GetPermissionStatus(
205 content::PermissionType::NOTIFICATIONS, origin, origin));
lazyboy561b7de2015-11-19 19:27:30206 }
207
208 PushMessagingAppIdentifier GetAppIdentifierForServiceWorkerRegistration(
avia2f4804a2015-12-24 23:11:13209 int64_t service_worker_registration_id,
lazyboy561b7de2015-11-19 19:27:30210 const GURL& origin) {
211 PushMessagingAppIdentifier app_identifier =
212 PushMessagingAppIdentifier::FindByServiceWorker(
213 profile(), origin, service_worker_registration_id);
214
215 EXPECT_FALSE(app_identifier.is_null());
216 return app_identifier;
217 }
218
219 // ExtensionApiTest overrides.
220 void SetUpCommandLine(base::CommandLine* command_line) override {
peter9de96272015-12-04 15:23:27221 command_line->AppendSwitch(
222 switches::kEnableExperimentalWebPlatformFeatures);
lazyboy561b7de2015-11-19 19:27:30223 ServiceWorkerTest::SetUpCommandLine(command_line);
224 }
225 void SetUpOnMainThread() override {
226 gcm_service_ = static_cast<gcm::FakeGCMProfileService*>(
227 gcm::GCMProfileServiceFactory::GetInstance()->SetTestingFactoryAndUse(
228 profile(), &gcm::FakeGCMProfileService::Build));
229 gcm_service_->set_collect(true);
230 push_service_ = PushMessagingServiceFactory::GetForProfile(profile());
231
232 ServiceWorkerTest::SetUpOnMainThread();
233 }
234
235 gcm::FakeGCMProfileService* gcm_service() const { return gcm_service_; }
236 PushMessagingServiceImpl* push_service() const { return push_service_; }
237
238 private:
239 gcm::FakeGCMProfileService* gcm_service_;
240 PushMessagingServiceImpl* push_service_;
241
242 DISALLOW_COPY_AND_ASSIGN(ServiceWorkerPushMessagingTest);
243};
244
lazyboy20167c22016-05-18 00:59:30245IN_PROC_BROWSER_TEST_F(ServiceWorkerTest, RegisterSucceeds) {
kalman6f984ae2015-09-18 17:21:58246 StartTestFromBackgroundPage("register.js", kExpectSuccess);
annekao38685502015-07-14 17:46:39247}
248
lazyboyc3e763a2015-12-09 23:09:58249IN_PROC_BROWSER_TEST_F(ServiceWorkerTest, UpdateRefreshesServiceWorker) {
250 base::ScopedTempDir scoped_temp_dir;
251 ASSERT_TRUE(scoped_temp_dir.CreateUniqueTempDir());
252 base::FilePath pem_path = test_data_dir_.AppendASCII("service_worker")
253 .AppendASCII("update")
254 .AppendASCII("service_worker.pem");
255 base::FilePath path_v1 = PackExtensionWithOptions(
256 test_data_dir_.AppendASCII("service_worker")
257 .AppendASCII("update")
258 .AppendASCII("v1"),
259 scoped_temp_dir.path().AppendASCII("v1.crx"), pem_path, base::FilePath());
260 base::FilePath path_v2 = PackExtensionWithOptions(
261 test_data_dir_.AppendASCII("service_worker")
262 .AppendASCII("update")
263 .AppendASCII("v2"),
264 scoped_temp_dir.path().AppendASCII("v2.crx"), pem_path, base::FilePath());
265 const char* kId = "hfaanndiiilofhfokeanhddpkfffchdi";
266
267 ExtensionTestMessageListener listener_v1("Pong from version 1", false);
268 listener_v1.set_failure_message("FAILURE_V1");
269 // Install version 1.0 of the extension.
270 ASSERT_TRUE(InstallExtension(path_v1, 1));
271 EXPECT_TRUE(extensions::ExtensionRegistry::Get(profile())
272 ->enabled_extensions()
273 .GetByID(kId));
274 EXPECT_TRUE(listener_v1.WaitUntilSatisfied());
275
276 ExtensionTestMessageListener listener_v2("Pong from version 2", false);
277 listener_v2.set_failure_message("FAILURE_V2");
278
279 // Update to version 2.0.
280 EXPECT_TRUE(UpdateExtension(kId, path_v2, 0));
281 EXPECT_TRUE(extensions::ExtensionRegistry::Get(profile())
282 ->enabled_extensions()
283 .GetByID(kId));
284 EXPECT_TRUE(listener_v2.WaitUntilSatisfied());
285}
286
lazyboy22eddc712015-12-10 21:16:26287IN_PROC_BROWSER_TEST_F(ServiceWorkerTest, UpdateWithoutSkipWaiting) {
288 base::ScopedTempDir scoped_temp_dir;
289 ASSERT_TRUE(scoped_temp_dir.CreateUniqueTempDir());
290 base::FilePath pem_path = test_data_dir_.AppendASCII("service_worker")
291 .AppendASCII("update_without_skip_waiting")
292 .AppendASCII("update_without_skip_waiting.pem");
293 base::FilePath path_v1 = PackExtensionWithOptions(
294 test_data_dir_.AppendASCII("service_worker")
295 .AppendASCII("update_without_skip_waiting")
296 .AppendASCII("v1"),
297 scoped_temp_dir.path().AppendASCII("v1.crx"), pem_path, base::FilePath());
298 base::FilePath path_v2 = PackExtensionWithOptions(
299 test_data_dir_.AppendASCII("service_worker")
300 .AppendASCII("update_without_skip_waiting")
301 .AppendASCII("v2"),
302 scoped_temp_dir.path().AppendASCII("v2.crx"), pem_path, base::FilePath());
303 const char* kId = "mhnnnflgagdakldgjpfcofkiocpdmogl";
304
305 // Install version 1.0 of the extension.
306 ASSERT_TRUE(InstallExtension(path_v1, 1));
307 EXPECT_TRUE(extensions::ExtensionRegistry::Get(profile())
308 ->enabled_extensions()
309 .GetByID(kId));
310 const Extension* extension = extensions::ExtensionRegistry::Get(profile())
311 ->enabled_extensions()
312 .GetByID(kId);
313
314 ExtensionTestMessageListener listener1("Pong from version 1", false);
315 listener1.set_failure_message("FAILURE");
316 content::WebContents* web_contents =
317 AddTab(browser(), extension->GetResourceURL("page.html"));
318 EXPECT_TRUE(listener1.WaitUntilSatisfied());
319
320 // Update to version 2.0.
321 EXPECT_TRUE(UpdateExtension(kId, path_v2, 0));
322 EXPECT_TRUE(extensions::ExtensionRegistry::Get(profile())
323 ->enabled_extensions()
324 .GetByID(kId));
325 const Extension* extension_after_update =
326 extensions::ExtensionRegistry::Get(profile())
327 ->enabled_extensions()
328 .GetByID(kId);
329
330 // Service worker version 2 would be installed but it won't be controlling
331 // the extension page yet.
332 ExtensionTestMessageListener listener2("Pong from version 1", false);
333 listener2.set_failure_message("FAILURE");
334 web_contents =
335 AddTab(browser(), extension_after_update->GetResourceURL("page.html"));
336 EXPECT_TRUE(listener2.WaitUntilSatisfied());
337
338 // Navigate the tab away from the extension page so that no clients are
339 // using the service worker.
340 // Note that just closing the tab with WebContentsDestroyedWatcher doesn't
341 // seem to be enough because it returns too early.
342 WebContentsLoadStopObserver navigate_away_observer(web_contents);
343 web_contents->GetController().LoadURL(
344 GURL(url::kAboutBlankURL), content::Referrer(), ui::PAGE_TRANSITION_TYPED,
345 std::string());
346 navigate_away_observer.WaitForLoadStop();
347
348 // Now expect service worker version 2 to control the extension page.
349 ExtensionTestMessageListener listener3("Pong from version 2", false);
350 listener3.set_failure_message("FAILURE");
351 web_contents =
352 AddTab(browser(), extension_after_update->GetResourceURL("page.html"));
353 EXPECT_TRUE(listener3.WaitUntilSatisfied());
354}
355
kalman6f984ae2015-09-18 17:21:58356IN_PROC_BROWSER_TEST_F(ServiceWorkerTest, FetchArbitraryPaths) {
357 const Extension* extension =
358 StartTestFromBackgroundPage("fetch.js", kExpectSuccess);
annekao1db36fd2015-07-29 17:09:16359
kalman6f984ae2015-09-18 17:21:58360 // Open some arbirary paths. Their contents should be what the service worker
361 // responds with, which in this case is the path of the fetch.
362 EXPECT_EQ(
363 "Caught a fetch for /index.html",
364 NavigateAndExtractInnerText(extension->GetResourceURL("index.html")));
365 EXPECT_EQ("Caught a fetch for /path/to/other.html",
366 NavigateAndExtractInnerText(
367 extension->GetResourceURL("path/to/other.html")));
368 EXPECT_EQ("Caught a fetch for /some/text/file.txt",
369 NavigateAndExtractInnerText(
370 extension->GetResourceURL("some/text/file.txt")));
371 EXPECT_EQ("Caught a fetch for /no/file/extension",
372 NavigateAndExtractInnerText(
373 extension->GetResourceURL("no/file/extension")));
374 EXPECT_EQ("Caught a fetch for /",
375 NavigateAndExtractInnerText(extension->GetResourceURL("")));
annekao1db36fd2015-07-29 17:09:16376}
377
lazyboy52c3bcf2016-01-08 00:11:29378IN_PROC_BROWSER_TEST_F(ServiceWorkerTest, SWServedBackgroundPageReceivesEvent) {
379 const Extension* extension =
380 StartTestFromBackgroundPage("replace_background.js", kExpectSuccess);
381 ExtensionHost* background_page =
382 process_manager()->GetBackgroundHostForExtension(extension->id());
383 ASSERT_TRUE(background_page);
384
385 // Close the background page and start it again so that the service worker
386 // will start controlling pages.
387 background_page->Close();
388 BackgroundPageWatcher(process_manager(), extension).WaitForClose();
389 background_page = nullptr;
390 process_manager()->WakeEventPage(extension->id(),
391 base::Bind(&DoNothingWithBool));
392 BackgroundPageWatcher(process_manager(), extension).WaitForOpen();
393
394 // Since the SW is now controlling the extension, the SW serves the background
395 // script. page.html sends a message to the background script and we verify
396 // that the SW served background script correctly receives the message/event.
397 ExtensionTestMessageListener listener("onMessage/SW BG.", false);
398 listener.set_failure_message("onMessage/original BG.");
399 content::WebContents* web_contents =
400 AddTab(browser(), extension->GetResourceURL("page.html"));
401 ASSERT_TRUE(web_contents);
402 EXPECT_TRUE(listener.WaitUntilSatisfied());
403}
404
kalman6f984ae2015-09-18 17:21:58405IN_PROC_BROWSER_TEST_F(ServiceWorkerTest,
406 LoadingBackgroundPageBypassesServiceWorker) {
407 const Extension* extension =
408 StartTestFromBackgroundPage("fetch.js", kExpectSuccess);
annekao49241182015-08-18 17:14:01409
kalman6f984ae2015-09-18 17:21:58410 std::string kExpectedInnerText = "background.html contents for testing.";
annekao49241182015-08-18 17:14:01411
kalman6f984ae2015-09-18 17:21:58412 // Sanity check that the background page has the expected content.
413 ExtensionHost* background_page =
414 process_manager()->GetBackgroundHostForExtension(extension->id());
415 ASSERT_TRUE(background_page);
416 EXPECT_EQ(kExpectedInnerText,
417 ExtractInnerText(background_page->host_contents()));
annekao49241182015-08-18 17:14:01418
kalman6f984ae2015-09-18 17:21:58419 // Close the background page.
420 background_page->Close();
421 BackgroundPageWatcher(process_manager(), extension).WaitForClose();
422 background_page = nullptr;
423
424 // Start it again.
425 process_manager()->WakeEventPage(extension->id(),
426 base::Bind(&DoNothingWithBool));
427 BackgroundPageWatcher(process_manager(), extension).WaitForOpen();
428
429 // Content should not have been affected by the fetch, which would otherwise
430 // be "Caught fetch for...".
431 background_page =
432 process_manager()->GetBackgroundHostForExtension(extension->id());
433 ASSERT_TRUE(background_page);
434 content::WaitForLoadStop(background_page->host_contents());
435
436 // TODO(kalman): Everything you've read has been a LIE! It should be:
437 //
438 // EXPECT_EQ(kExpectedInnerText,
439 // ExtractInnerText(background_page->host_contents()));
440 //
441 // but there is a bug, and we're actually *not* bypassing the service worker
442 // for background page loads! For now, let it pass (assert wrong behavior)
443 // because it's not a regression, but this must be fixed eventually.
444 //
445 // Tracked in crbug.com/532720.
446 EXPECT_EQ("Caught a fetch for /background.html",
447 ExtractInnerText(background_page->host_contents()));
annekao49241182015-08-18 17:14:01448}
449
kalman6f984ae2015-09-18 17:21:58450IN_PROC_BROWSER_TEST_F(ServiceWorkerTest,
451 ServiceWorkerPostsMessageToBackgroundClient) {
452 const Extension* extension = StartTestFromBackgroundPage(
453 "post_message_to_background_client.js", kExpectSuccess);
annekao533482222015-08-21 23:23:53454
kalman6f984ae2015-09-18 17:21:58455 // The service worker in this test simply posts a message to the background
456 // client it receives from getBackgroundClient().
457 const char* kScript =
458 "var messagePromise = null;\n"
459 "if (test.lastMessageFromServiceWorker) {\n"
460 " messagePromise = Promise.resolve(test.lastMessageFromServiceWorker);\n"
461 "} else {\n"
462 " messagePromise = test.waitForMessage(navigator.serviceWorker);\n"
463 "}\n"
464 "messagePromise.then(function(message) {\n"
465 " window.domAutomationController.send(String(message == 'success'));\n"
466 "})\n";
467 EXPECT_EQ("true", ExecuteScriptInBackgroundPage(extension->id(), kScript));
annekao533482222015-08-21 23:23:53468}
469
kalman6f984ae2015-09-18 17:21:58470IN_PROC_BROWSER_TEST_F(ServiceWorkerTest,
471 BackgroundPagePostsMessageToServiceWorker) {
472 const Extension* extension =
473 StartTestFromBackgroundPage("post_message_to_sw.js", kExpectSuccess);
annekao533482222015-08-21 23:23:53474
kalman6f984ae2015-09-18 17:21:58475 // The service worker in this test waits for a message, then echoes it back
476 // by posting a message to the background page via getBackgroundClient().
477 const char* kScript =
478 "var mc = new MessageChannel();\n"
479 "test.waitForMessage(mc.port1).then(function(message) {\n"
480 " window.domAutomationController.send(String(message == 'hello'));\n"
481 "});\n"
482 "test.registeredServiceWorker.postMessage(\n"
483 " {message: 'hello', port: mc.port2}, [mc.port2])\n";
484 EXPECT_EQ("true", ExecuteScriptInBackgroundPage(extension->id(), kScript));
annekao533482222015-08-21 23:23:53485}
486
rdevlin.croninf5863da2015-09-10 19:21:45487IN_PROC_BROWSER_TEST_F(ServiceWorkerTest,
488 ServiceWorkerSuspensionOnExtensionUnload) {
kalman6f984ae2015-09-18 17:21:58489 // For this test, only hold onto the extension's ID and URL + a function to
490 // get a resource URL, because we're going to be disabling and uninstalling
491 // it, which will invalidate the pointer.
492 std::string extension_id;
493 GURL extension_url;
494 {
495 const Extension* extension =
496 StartTestFromBackgroundPage("fetch.js", kExpectSuccess);
497 extension_id = extension->id();
498 extension_url = extension->url();
499 }
500 auto get_resource_url = [&extension_url](const std::string& path) {
501 return Extension::GetResourceURL(extension_url, path);
502 };
rdevlin.croninf5863da2015-09-10 19:21:45503
kalman6f984ae2015-09-18 17:21:58504 // Fetch should route to the service worker.
505 EXPECT_EQ("Caught a fetch for /index.html",
506 NavigateAndExtractInnerText(get_resource_url("index.html")));
rdevlin.croninf5863da2015-09-10 19:21:45507
kalman6f984ae2015-09-18 17:21:58508 // Disable the extension. Opening the page should fail.
509 extension_service()->DisableExtension(extension_id,
rdevlin.croninf5863da2015-09-10 19:21:45510 Extension::DISABLE_USER_ACTION);
511 base::RunLoop().RunUntilIdle();
rdevlin.croninf5863da2015-09-10 19:21:45512
kalman6f984ae2015-09-18 17:21:58513 EXPECT_EQ(content::PAGE_TYPE_ERROR,
514 NavigateAndGetPageType(get_resource_url("index.html")));
515 EXPECT_EQ(content::PAGE_TYPE_ERROR,
516 NavigateAndGetPageType(get_resource_url("other.html")));
517
518 // Re-enable the extension. Opening pages should immediately start to succeed
519 // again.
rdevlin.croninf5863da2015-09-10 19:21:45520 extension_service()->EnableExtension(extension_id);
521 base::RunLoop().RunUntilIdle();
522
kalman6f984ae2015-09-18 17:21:58523 EXPECT_EQ("Caught a fetch for /index.html",
524 NavigateAndExtractInnerText(get_resource_url("index.html")));
525 EXPECT_EQ("Caught a fetch for /other.html",
526 NavigateAndExtractInnerText(get_resource_url("other.html")));
527 EXPECT_EQ("Caught a fetch for /another.html",
528 NavigateAndExtractInnerText(get_resource_url("another.html")));
rdevlin.croninf5863da2015-09-10 19:21:45529
kalman6f984ae2015-09-18 17:21:58530 // Uninstall the extension. Opening pages should fail again.
531 base::string16 error;
532 extension_service()->UninstallExtension(
533 extension_id, UninstallReason::UNINSTALL_REASON_FOR_TESTING,
534 base::Bind(&base::DoNothing), &error);
535 base::RunLoop().RunUntilIdle();
536
537 EXPECT_EQ(content::PAGE_TYPE_ERROR,
538 NavigateAndGetPageType(get_resource_url("index.html")));
539 EXPECT_EQ(content::PAGE_TYPE_ERROR,
540 NavigateAndGetPageType(get_resource_url("other.html")));
541 EXPECT_EQ(content::PAGE_TYPE_ERROR,
542 NavigateAndGetPageType(get_resource_url("anotherother.html")));
543 EXPECT_EQ(content::PAGE_TYPE_ERROR,
544 NavigateAndGetPageType(get_resource_url("final.html")));
545}
546
547IN_PROC_BROWSER_TEST_F(ServiceWorkerTest, BackgroundPageIsWokenIfAsleep) {
548 const Extension* extension =
549 StartTestFromBackgroundPage("wake_on_fetch.js", kExpectSuccess);
550
551 // Navigate to special URLs that this test's service worker recognises, each
552 // making a check then populating the response with either "true" or "false".
553 EXPECT_EQ("true", NavigateAndExtractInnerText(extension->GetResourceURL(
554 "background-client-is-awake")));
555 EXPECT_EQ("true", NavigateAndExtractInnerText(
556 extension->GetResourceURL("ping-background-client")));
557 // Ping more than once for good measure.
558 EXPECT_EQ("true", NavigateAndExtractInnerText(
559 extension->GetResourceURL("ping-background-client")));
560
561 // Shut down the event page. The SW should detect that it's closed, but still
562 // be able to ping it.
563 ExtensionHost* background_page =
564 process_manager()->GetBackgroundHostForExtension(extension->id());
565 ASSERT_TRUE(background_page);
566 background_page->Close();
567 BackgroundPageWatcher(process_manager(), extension).WaitForClose();
568
569 EXPECT_EQ("false", NavigateAndExtractInnerText(extension->GetResourceURL(
570 "background-client-is-awake")));
571 EXPECT_EQ("true", NavigateAndExtractInnerText(
572 extension->GetResourceURL("ping-background-client")));
573 EXPECT_EQ("true", NavigateAndExtractInnerText(
574 extension->GetResourceURL("ping-background-client")));
575 EXPECT_EQ("true", NavigateAndExtractInnerText(extension->GetResourceURL(
576 "background-client-is-awake")));
577}
578
579IN_PROC_BROWSER_TEST_F(ServiceWorkerTest,
580 GetBackgroundClientFailsWithNoBackgroundPage) {
581 // This extension doesn't have a background page, only a tab at page.html.
582 // The service worker it registers tries to call getBackgroundClient() and
583 // should fail.
584 // Note that this also tests that service workers can be registered from tabs.
585 EXPECT_TRUE(RunExtensionSubtest("service_worker/no_background", "page.html"));
rdevlin.croninf5863da2015-09-10 19:21:45586}
587
lazyboy6ddb7d62015-11-10 23:15:27588IN_PROC_BROWSER_TEST_F(ServiceWorkerTest, NotificationAPI) {
589 EXPECT_TRUE(RunExtensionSubtest("service_worker/notifications/has_permission",
590 "page.html"));
591}
592
lazyboyaea32c22016-01-04 21:37:07593IN_PROC_BROWSER_TEST_F(ServiceWorkerTest, WebAccessibleResourcesFetch) {
594 EXPECT_TRUE(RunExtensionSubtest(
595 "service_worker/web_accessible_resources/fetch/", "page.html"));
596}
597
lazyboyee4adef2016-05-24 00:55:16598IN_PROC_BROWSER_TEST_F(ServiceWorkerTest, TabsCreate) {
599 // Extensions APIs from SW are only enabled on trunk.
600 ScopedCurrentChannel current_channel_override(version_info::Channel::UNKNOWN);
601 const Extension* extension = LoadExtensionWithFlags(
602 test_data_dir_.AppendASCII("service_worker/tabs_create"), kFlagNone);
603 ASSERT_TRUE(extension);
604 ui_test_utils::NavigateToURL(browser(),
605 extension->GetResourceURL("page.html"));
606 content::WebContents* web_contents =
607 browser()->tab_strip_model()->GetActiveWebContents();
608
609 int starting_tab_count = browser()->tab_strip_model()->count();
610 std::string result;
611 ASSERT_TRUE(content::ExecuteScriptAndExtractString(
612 web_contents, "window.runServiceWorker()", &result));
613 ASSERT_EQ("chrome.tabs.create callback", result);
614 EXPECT_EQ(starting_tab_count + 1, browser()->tab_strip_model()->count());
615
616 // Check extension shutdown path.
617 UnloadExtension(extension->id());
618 EXPECT_EQ(starting_tab_count, browser()->tab_strip_model()->count());
619}
620
lazyboyaea32c22016-01-04 21:37:07621// This test loads a web page that has an iframe pointing to a
622// chrome-extension:// URL. The URL is listed in the extension's
623// web_accessible_resources. Initially the iframe is served from the extension's
624// resource file. After verifying that, we register a Service Worker that
625// controls the extension. Further requests to the same resource as before
626// should now be served by the Service Worker.
627// This test also verifies that if the requested resource exists in the manifest
628// but is not present in the extension directory, the Service Worker can still
629// serve the resource file.
630IN_PROC_BROWSER_TEST_F(ServiceWorkerTest, WebAccessibleResourcesIframeSrc) {
631 const Extension* extension = LoadExtensionWithFlags(
632 test_data_dir_.AppendASCII(
633 "service_worker/web_accessible_resources/iframe_src"),
634 kFlagNone);
635 ASSERT_TRUE(extension);
636 ASSERT_TRUE(StartEmbeddedTestServer());
dchengc363d41a2016-06-08 20:29:39637 GURL page_url = embedded_test_server()->GetURL(
638 "/extensions/api_test/service_worker/web_accessible_resources/"
639 "webpage.html");
lazyboyaea32c22016-01-04 21:37:07640
641 content::WebContents* web_contents = AddTab(browser(), page_url);
642 std::string result;
643 // webpage.html will create an iframe pointing to a resource from |extension|.
644 // Expect the resource to be served by the extension.
645 EXPECT_TRUE(content::ExecuteScriptAndExtractString(
646 web_contents, base::StringPrintf("window.testIframe('%s', 'iframe.html')",
647 extension->id().c_str()),
648 &result));
649 EXPECT_EQ("FROM_EXTENSION_RESOURCE", result);
650
651 ExtensionTestMessageListener service_worker_ready_listener("SW_READY", false);
652 EXPECT_TRUE(ExecuteScriptInBackgroundPageNoWait(
653 extension->id(), "window.registerServiceWorker()"));
654 EXPECT_TRUE(service_worker_ready_listener.WaitUntilSatisfied());
655
656 result.clear();
657 // webpage.html will create another iframe pointing to a resource from
658 // |extension| as before. But this time, the resource should be be served
659 // from the Service Worker.
660 EXPECT_TRUE(content::ExecuteScriptAndExtractString(
661 web_contents, base::StringPrintf("window.testIframe('%s', 'iframe.html')",
662 extension->id().c_str()),
663 &result));
664 EXPECT_EQ("FROM_SW_RESOURCE", result);
665
666 result.clear();
667 // webpage.html will create yet another iframe pointing to a resource that
668 // exists in the extension manifest's web_accessible_resources, but is not
669 // present in the extension directory. Expect the resources of the iframe to
670 // be served by the Service Worker.
671 EXPECT_TRUE(content::ExecuteScriptAndExtractString(
672 web_contents,
673 base::StringPrintf("window.testIframe('%s', 'iframe_non_existent.html')",
674 extension->id().c_str()),
675 &result));
676 EXPECT_EQ("FROM_SW_RESOURCE", result);
677}
678
lazyboybd325ae2015-11-18 21:35:26679IN_PROC_BROWSER_TEST_F(ServiceWorkerBackgroundSyncTest, Sync) {
680 const Extension* extension = LoadExtensionWithFlags(
681 test_data_dir_.AppendASCII("service_worker/sync"), kFlagNone);
682 ASSERT_TRUE(extension);
683 ui_test_utils::NavigateToURL(browser(),
684 extension->GetResourceURL("page.html"));
685 content::WebContents* web_contents =
686 browser()->tab_strip_model()->GetActiveWebContents();
687
688 // Prevent firing by going offline.
689 content::background_sync_test_util::SetOnline(web_contents, false);
690
691 ExtensionTestMessageListener sync_listener("SYNC: send-chats", false);
692 sync_listener.set_failure_message("FAIL");
693
694 std::string result;
695 ASSERT_TRUE(content::ExecuteScriptAndExtractString(
696 web_contents, "window.runServiceWorker()", &result));
697 ASSERT_EQ("SERVICE_WORKER_READY", result);
698
699 EXPECT_FALSE(sync_listener.was_satisfied());
700 // Resume firing by going online.
701 content::background_sync_test_util::SetOnline(web_contents, true);
702 EXPECT_TRUE(sync_listener.WaitUntilSatisfied());
703}
704
horo1eeddde2015-11-19 05:59:25705IN_PROC_BROWSER_TEST_F(ServiceWorkerTest,
706 FetchFromContentScriptShouldNotGoToServiceWorkerOfPage) {
707 ASSERT_TRUE(StartEmbeddedTestServer());
708 GURL page_url = embedded_test_server()->GetURL(
709 "/extensions/api_test/service_worker/content_script_fetch/"
710 "controlled_page/index.html");
711 content::WebContents* tab =
712 browser()->tab_strip_model()->GetActiveWebContents();
713 ui_test_utils::NavigateToURL(browser(), page_url);
714 content::WaitForLoadStop(tab);
715
716 std::string value;
717 ASSERT_TRUE(
718 content::ExecuteScriptAndExtractString(tab, "register();", &value));
719 EXPECT_EQ("SW controlled", value);
720
721 ASSERT_TRUE(RunExtensionTest("service_worker/content_script_fetch"))
722 << message_;
723}
724
lazyboyd429e2582016-05-20 20:18:52725IN_PROC_BROWSER_TEST_F(ServiceWorkerPushMessagingTest, OnPush) {
lazyboy561b7de2015-11-19 19:27:30726 const Extension* extension = LoadExtensionWithFlags(
727 test_data_dir_.AppendASCII("service_worker/push_messaging"), kFlagNone);
728 ASSERT_TRUE(extension);
729 GURL extension_url = extension->url();
730
731 ASSERT_NO_FATAL_FAILURE(GrantNotificationPermissionForTest(extension_url));
732
733 GURL url = extension->GetResourceURL("page.html");
734 ui_test_utils::NavigateToURL(browser(), url);
735
736 content::WebContents* web_contents =
737 browser()->tab_strip_model()->GetActiveWebContents();
738
739 // Start the ServiceWorker.
740 ExtensionTestMessageListener ready_listener("SERVICE_WORKER_READY", false);
741 ready_listener.set_failure_message("SERVICE_WORKER_FAILURE");
742 const char* kScript = "window.runServiceWorker()";
743 EXPECT_TRUE(content::ExecuteScript(web_contents->GetMainFrame(), kScript));
744 EXPECT_TRUE(ready_listener.WaitUntilSatisfied());
745
746 PushMessagingAppIdentifier app_identifier =
747 GetAppIdentifierForServiceWorkerRegistration(0LL, extension_url);
748 ASSERT_EQ(app_identifier.app_id(), gcm_service()->last_registered_app_id());
749 EXPECT_EQ("1234567890", gcm_service()->last_registered_sender_ids()[0]);
750
lazyboyd429e2582016-05-20 20:18:52751 base::RunLoop run_loop;
lazyboy561b7de2015-11-19 19:27:30752 // Send a push message via gcm and expect the ServiceWorker to receive it.
753 ExtensionTestMessageListener push_message_listener("OK", false);
754 push_message_listener.set_failure_message("FAIL");
755 gcm::IncomingMessage message;
756 message.sender_id = "1234567890";
757 message.raw_data = "testdata";
758 message.decrypted = true;
lazyboyd429e2582016-05-20 20:18:52759 push_service()->SetMessageCallbackForTesting(run_loop.QuitClosure());
lazyboy561b7de2015-11-19 19:27:30760 push_service()->OnMessage(app_identifier.app_id(), message);
761 EXPECT_TRUE(push_message_listener.WaitUntilSatisfied());
lazyboyd429e2582016-05-20 20:18:52762 run_loop.Run(); // Wait until the message is handled by push service.
lazyboy561b7de2015-11-19 19:27:30763}
764
annekao38685502015-07-14 17:46:39765} // namespace extensions